ffi #jni_main slice 1: Java source emitter (pure fn + unit tests)
`src/ir/jni_java_emit.zig`'s `emitJavaSource` takes a
`ForeignClassDecl` with `is_main = true` and returns the `.java`
source text. AOT pipeline integration (javac + d8 + APK bundling +
manifest synthesis + RegisterNatives) lands in subsequent slices.
Emission shape per bodied method:
@Override
public <ret> <name>(<params>) {
super.<name>(<args>);
sx_<name>(<args>);
}
private native <ret> sx_<name>(<params>);
Declaration-only methods (no body — references inherited Java
methods that sx wants to *call*) are skipped — no override, no
native delegate.
`#extends Alias` resolves through the supplied class registry to
the parent's foreign Java path. Default parent is
`android.app.NativeActivity` when `#extends` is absent.
Type matrix: primitives (void/bool/s8..s64/u8/u16/f32/f64), `*Self`
elided as the receiver (Java's implicit `this`), `*void` as
`Object`, `*Foo` cross-class refs resolved through the class
registry.
Six unit tests cover: non-main rejection, full void onCreate(Bundle)
shape with @Override delegate, primitive params, declaration-only
skipping, `#extends Alias` resolution, default-package classes.
130/130 examples still green; zig test clean.
This commit is contained in:
@@ -40,6 +40,7 @@ pub const resolveAstType = type_bridge.resolveAstType;
|
||||
pub const bridgeType = type_bridge.bridgeType;
|
||||
|
||||
pub const jni_descriptor = @import("jni_descriptor.zig");
|
||||
pub const jni_java_emit = @import("jni_java_emit.zig");
|
||||
|
||||
pub const types_tests = @import("types.test.zig");
|
||||
pub const inst_tests = @import("inst.test.zig");
|
||||
@@ -50,6 +51,7 @@ pub const lower_tests = @import("lower.test.zig");
|
||||
pub const type_bridge_tests = @import("type_bridge.test.zig");
|
||||
pub const emit_llvm_tests = @import("emit_llvm.test.zig");
|
||||
pub const jni_descriptor_tests = @import("jni_descriptor.test.zig");
|
||||
pub const jni_java_emit_tests = @import("jni_java_emit.test.zig");
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
||||
237
src/ir/jni_java_emit.test.zig
Normal file
237
src/ir/jni_java_emit.test.zig
Normal file
@@ -0,0 +1,237 @@
|
||||
// Tests for jni_java_emit.zig — #jni_main pipeline slice 1.
|
||||
// Locks in the Java source emitted from `ForeignClassDecl` AST nodes:
|
||||
// package split, class header, @Override delegate pattern, primitive
|
||||
// type mapping, cross-class refs through the foreign_class registry.
|
||||
|
||||
const std = @import("std");
|
||||
const ast = @import("../ast.zig");
|
||||
const emit = @import("jni_java_emit.zig");
|
||||
|
||||
const Node = ast.Node;
|
||||
|
||||
fn makeTypeExpr(allocator: std.mem.Allocator, name: []const u8) !*Node {
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{
|
||||
.span = .{ .start = 0, .end = 0 },
|
||||
.data = .{ .type_expr = .{ .name = name } },
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
fn makePointer(allocator: std.mem.Allocator, pointee: *Node) !*Node {
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{
|
||||
.span = .{ .start = 0, .end = 0 },
|
||||
.data = .{ .pointer_type_expr = .{ .pointee_type = pointee } },
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
/// Marker for "method has a body" — emitJavaSource only checks
|
||||
/// `body != null`. The actual node contents are unused.
|
||||
fn makeBodyMarker(allocator: std.mem.Allocator) !*Node {
|
||||
const node = try allocator.create(Node);
|
||||
node.* = .{
|
||||
.span = .{ .start = 0, .end = 0 },
|
||||
.data = .{ .block = .{ .stmts = &.{} } },
|
||||
};
|
||||
return node;
|
||||
}
|
||||
|
||||
test "rejects non-main decl" {
|
||||
const a = std.testing.allocator;
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "Foo",
|
||||
.foreign_path = "co/example/Foo",
|
||||
.runtime = .jni_class,
|
||||
.is_main = false, // ← not main
|
||||
};
|
||||
const result = emit.emitJavaSource(a, &fcd, .{});
|
||||
try std.testing.expectError(emit.EmitError.NotAJniMainClass, result);
|
||||
}
|
||||
|
||||
test "void onCreate(Bundle) with #extends NativeActivity default" {
|
||||
const a = std.testing.allocator;
|
||||
var arena = std.heap.ArenaAllocator.init(a);
|
||||
defer arena.deinit();
|
||||
const aa = arena.allocator();
|
||||
|
||||
const self_ty = try makePointer(aa, try makeTypeExpr(aa, "Self"));
|
||||
const bundle_ty = try makePointer(aa, try makeTypeExpr(aa, "Bundle"));
|
||||
const body = try makeBodyMarker(aa);
|
||||
|
||||
var registry = std.StringHashMap([]const u8).init(a);
|
||||
defer registry.deinit();
|
||||
try registry.put("Bundle", "android/os/Bundle");
|
||||
|
||||
const member: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "onCreate",
|
||||
.params = &.{ self_ty, bundle_ty },
|
||||
.param_names = &.{ "self", "b" },
|
||||
.return_type = null,
|
||||
.body = body,
|
||||
} };
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "SxApp",
|
||||
.foreign_path = "co/swipelab/sx_runtime/SxNativeActivity",
|
||||
.runtime = .jni_class,
|
||||
.is_main = true,
|
||||
.members = &.{member},
|
||||
};
|
||||
|
||||
const out = try emit.emitJavaSource(a, &fcd, .{ .classes = ®istry });
|
||||
defer a.free(out);
|
||||
|
||||
const expected =
|
||||
\\package co.swipelab.sx_runtime;
|
||||
\\
|
||||
\\public class SxNativeActivity extends android.app.NativeActivity {
|
||||
\\ @Override
|
||||
\\ public void onCreate(android.os.Bundle b) {
|
||||
\\ super.onCreate(b);
|
||||
\\ sx_onCreate(b);
|
||||
\\ }
|
||||
\\ private native void sx_onCreate(android.os.Bundle b);
|
||||
\\}
|
||||
\\
|
||||
;
|
||||
try std.testing.expectEqualStrings(expected, out);
|
||||
}
|
||||
|
||||
test "primitive params" {
|
||||
const a = std.testing.allocator;
|
||||
var arena = std.heap.ArenaAllocator.init(a);
|
||||
defer arena.deinit();
|
||||
const aa = arena.allocator();
|
||||
|
||||
const self_ty = try makePointer(aa, try makeTypeExpr(aa, "Self"));
|
||||
const bool_ty = try makeTypeExpr(aa, "bool");
|
||||
const body = try makeBodyMarker(aa);
|
||||
|
||||
const member: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "onWindowFocusChanged",
|
||||
.params = &.{ self_ty, bool_ty },
|
||||
.param_names = &.{ "self", "hasFocus" },
|
||||
.return_type = null,
|
||||
.body = body,
|
||||
} };
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "Sx",
|
||||
.foreign_path = "co/sample/Sx",
|
||||
.runtime = .jni_class,
|
||||
.is_main = true,
|
||||
.members = &.{member},
|
||||
};
|
||||
const out = try emit.emitJavaSource(a, &fcd, .{});
|
||||
defer a.free(out);
|
||||
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "public void onWindowFocusChanged(boolean hasFocus)") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "super.onWindowFocusChanged(hasFocus);") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "sx_onWindowFocusChanged(hasFocus);") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "private native void sx_onWindowFocusChanged(boolean hasFocus);") != null);
|
||||
}
|
||||
|
||||
test "declaration-only methods are skipped" {
|
||||
const a = std.testing.allocator;
|
||||
var arena = std.heap.ArenaAllocator.init(a);
|
||||
defer arena.deinit();
|
||||
const aa = arena.allocator();
|
||||
|
||||
const self_ty = try makePointer(aa, try makeTypeExpr(aa, "Self"));
|
||||
const body = try makeBodyMarker(aa);
|
||||
|
||||
// One bodied (override), one declaration-only (calls inherited).
|
||||
const bodied: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "onCreate",
|
||||
.params = &.{self_ty},
|
||||
.param_names = &.{"self"},
|
||||
.return_type = null,
|
||||
.body = body,
|
||||
} };
|
||||
const decl_only: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "finish",
|
||||
.params = &.{self_ty},
|
||||
.param_names = &.{"self"},
|
||||
.return_type = null,
|
||||
.body = null, // sx-side just *calls* this; Java's NativeActivity.finish() provides it
|
||||
} };
|
||||
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "Sx",
|
||||
.foreign_path = "co/example/Sx",
|
||||
.runtime = .jni_class,
|
||||
.is_main = true,
|
||||
.members = &.{ bodied, decl_only },
|
||||
};
|
||||
const out = try emit.emitJavaSource(a, &fcd, .{});
|
||||
defer a.free(out);
|
||||
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "sx_onCreate") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "sx_finish") == null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "void finish(") == null);
|
||||
}
|
||||
|
||||
test "#extends Alias resolves through class registry" {
|
||||
const a = std.testing.allocator;
|
||||
var arena = std.heap.ArenaAllocator.init(a);
|
||||
defer arena.deinit();
|
||||
const aa = arena.allocator();
|
||||
|
||||
const self_ty = try makePointer(aa, try makeTypeExpr(aa, "Self"));
|
||||
const body = try makeBodyMarker(aa);
|
||||
|
||||
const extends_member: ast.ForeignClassMember = .{ .extends = "MyParent" };
|
||||
const method_member: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "onCreate",
|
||||
.params = &.{self_ty},
|
||||
.param_names = &.{"self"},
|
||||
.return_type = null,
|
||||
.body = body,
|
||||
} };
|
||||
|
||||
var registry = std.StringHashMap([]const u8).init(a);
|
||||
defer registry.deinit();
|
||||
try registry.put("MyParent", "co/example/MyParentActivity");
|
||||
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "Sx",
|
||||
.foreign_path = "co/example/Sx",
|
||||
.runtime = .jni_class,
|
||||
.is_main = true,
|
||||
.members = &.{ extends_member, method_member },
|
||||
};
|
||||
const out = try emit.emitJavaSource(a, &fcd, .{ .classes = ®istry });
|
||||
defer a.free(out);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "extends co.example.MyParentActivity") != null);
|
||||
}
|
||||
|
||||
test "default-package class (no slash in foreign_path)" {
|
||||
const a = std.testing.allocator;
|
||||
var arena = std.heap.ArenaAllocator.init(a);
|
||||
defer arena.deinit();
|
||||
const aa = arena.allocator();
|
||||
|
||||
const self_ty = try makePointer(aa, try makeTypeExpr(aa, "Self"));
|
||||
const body = try makeBodyMarker(aa);
|
||||
|
||||
const member: ast.ForeignClassMember = .{ .method = .{
|
||||
.name = "onCreate",
|
||||
.params = &.{self_ty},
|
||||
.param_names = &.{"self"},
|
||||
.return_type = null,
|
||||
.body = body,
|
||||
} };
|
||||
const fcd: ast.ForeignClassDecl = .{
|
||||
.name = "Sx",
|
||||
.foreign_path = "SxNoPackage",
|
||||
.runtime = .jni_class,
|
||||
.is_main = true,
|
||||
.members = &.{member},
|
||||
};
|
||||
const out = try emit.emitJavaSource(a, &fcd, .{});
|
||||
defer a.free(out);
|
||||
|
||||
// No `package ...;` line when the foreign path has no slashes.
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "package ") == null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, out, "public class SxNoPackage") != null);
|
||||
}
|
||||
267
src/ir/jni_java_emit.zig
Normal file
267
src/ir/jni_java_emit.zig
Normal file
@@ -0,0 +1,267 @@
|
||||
// Java source emission for `#jni_main #jni_class("...") { ... }` decls
|
||||
// (FFI plan, #jni_main pipeline slice 1).
|
||||
//
|
||||
// Given a `ForeignClassDecl` whose `is_main` flag is set, emit a `.java`
|
||||
// source file that:
|
||||
//
|
||||
// - declares a `public class` at the foreign_path's package + simple
|
||||
// name (e.g. `co/swipelab/Test/SxTestActivity` →
|
||||
// `package co.swipelab.Test; public class SxTestActivity`);
|
||||
// - extends the parent specified by `#extends Alias` (or
|
||||
// `android.app.NativeActivity` by default for a #jni_main class);
|
||||
// - for each method with a body, emits an `@Override` Java method
|
||||
// that calls `super` then a private native delegate `sx_<method>`;
|
||||
// - emits the matching `private native ... sx_<method>(...)` decls.
|
||||
//
|
||||
// The downstream pipeline (slice 2+) feeds this through `javac` + `d8`
|
||||
// and bundles the resulting `.dex` into the APK. Slice 3 wires the
|
||||
// manifest's `<activity android:name="...">` to point at this class.
|
||||
// Slice 4 emits a synthetic `JNI_OnLoad` that calls `RegisterNatives`
|
||||
// to bind the `sx_<method>` symbols.
|
||||
//
|
||||
// Type matrix covered today:
|
||||
// - void return + primitive returns (s8/s16/s32/s64, u8/u16, bool,
|
||||
// f32/f64)
|
||||
// - `(self: *Self)` plus primitive params
|
||||
// - cross-class refs (`*Foo` where Foo is another declared
|
||||
// `#foreign #jni_class`) lower to Foo's foreign path → Java
|
||||
// fully-qualified type
|
||||
// - `*void` → `Object` (opaque jobject)
|
||||
|
||||
const std = @import("std");
|
||||
const ast = @import("../ast.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const EmitError = error{
|
||||
OutOfMemory,
|
||||
UnsupportedType,
|
||||
NotAJniMainClass,
|
||||
};
|
||||
|
||||
pub const Options = struct {
|
||||
/// Map from sx alias → foreign path of declared `#jni_class` decls.
|
||||
/// Used to resolve `*Foo` cross-class refs in method signatures.
|
||||
classes: ?*const std.StringHashMap([]const u8) = null,
|
||||
/// Default superclass when the user doesn't write `#extends ...;`.
|
||||
default_extends: []const u8 = "android.app.NativeActivity",
|
||||
};
|
||||
|
||||
/// Emit a `.java` source for the given foreign-class decl. Result is
|
||||
/// heap-allocated through `allocator`; caller owns it.
|
||||
pub fn emitJavaSource(
|
||||
allocator: Allocator,
|
||||
fcd: *const ast.ForeignClassDecl,
|
||||
opts: Options,
|
||||
) EmitError![]u8 {
|
||||
if (!fcd.is_main) return EmitError.NotAJniMainClass;
|
||||
|
||||
var buf: std.ArrayList(u8) = .empty;
|
||||
errdefer buf.deinit(allocator);
|
||||
|
||||
const parts = splitForeignPath(fcd.foreign_path);
|
||||
if (parts.pkg.len > 0) {
|
||||
try buf.appendSlice(allocator, "package ");
|
||||
try appendDotted(allocator, &buf, parts.pkg);
|
||||
try buf.appendSlice(allocator, ";\n\n");
|
||||
}
|
||||
|
||||
var parent: []const u8 = opts.default_extends;
|
||||
var parent_owned = false;
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.extends => |alias| {
|
||||
if (opts.classes) |reg| {
|
||||
if (reg.get(alias)) |path| {
|
||||
parent = try foreignPathToJavaName(allocator, path);
|
||||
parent_owned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent = alias;
|
||||
break;
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
defer if (parent_owned) allocator.free(parent);
|
||||
|
||||
try buf.appendSlice(allocator, "public class ");
|
||||
try buf.appendSlice(allocator, parts.cls);
|
||||
try buf.appendSlice(allocator, " extends ");
|
||||
try buf.appendSlice(allocator, parent);
|
||||
try buf.appendSlice(allocator, " {\n");
|
||||
|
||||
// Two passes: @Override stubs that call super + native delegate,
|
||||
// then the native declarations.
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.method => |md| {
|
||||
if (md.body == null) continue;
|
||||
if (md.is_static) continue; // TODO: static native handling
|
||||
try emitOverride(allocator, &buf, md, opts);
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.method => |md| {
|
||||
if (md.body == null) continue;
|
||||
if (md.is_static) continue;
|
||||
try emitNativeDecl(allocator, &buf, md, opts);
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
|
||||
try buf.appendSlice(allocator, "}\n");
|
||||
return buf.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
const PathParts = struct { pkg: []const u8, cls: []const u8 };
|
||||
|
||||
fn splitForeignPath(foreign_path: []const u8) PathParts {
|
||||
const last_slash = std.mem.lastIndexOfScalar(u8, foreign_path, '/') orelse {
|
||||
return .{ .pkg = "", .cls = foreign_path };
|
||||
};
|
||||
return .{
|
||||
.pkg = foreign_path[0..last_slash],
|
||||
.cls = foreign_path[last_slash + 1 ..],
|
||||
};
|
||||
}
|
||||
|
||||
fn appendDotted(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
slash_path: []const u8,
|
||||
) EmitError!void {
|
||||
for (slash_path) |c| {
|
||||
try buf.append(allocator, if (c == '/') '.' else c);
|
||||
}
|
||||
}
|
||||
|
||||
fn foreignPathToJavaName(allocator: Allocator, slash_path: []const u8) EmitError![]u8 {
|
||||
var buf: std.ArrayList(u8) = .empty;
|
||||
try appendDotted(allocator, &buf, slash_path);
|
||||
return buf.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn emitOverride(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
md: ast.ForeignMethodDecl,
|
||||
opts: Options,
|
||||
) EmitError!void {
|
||||
try buf.appendSlice(allocator, " @Override\n public ");
|
||||
try emitJavaReturnType(allocator, buf, md.return_type, opts);
|
||||
try buf.append(allocator, ' ');
|
||||
try buf.appendSlice(allocator, md.name);
|
||||
try buf.append(allocator, '(');
|
||||
try emitJavaParamList(allocator, buf, md, opts);
|
||||
try buf.appendSlice(allocator, ") {\n super.");
|
||||
try buf.appendSlice(allocator, md.name);
|
||||
try buf.append(allocator, '(');
|
||||
try emitJavaArgList(allocator, buf, md);
|
||||
try buf.appendSlice(allocator, ");\n sx_");
|
||||
try buf.appendSlice(allocator, md.name);
|
||||
try buf.append(allocator, '(');
|
||||
try emitJavaArgList(allocator, buf, md);
|
||||
try buf.appendSlice(allocator, ");\n }\n");
|
||||
}
|
||||
|
||||
fn emitNativeDecl(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
md: ast.ForeignMethodDecl,
|
||||
opts: Options,
|
||||
) EmitError!void {
|
||||
try buf.appendSlice(allocator, " private native ");
|
||||
try emitJavaReturnType(allocator, buf, md.return_type, opts);
|
||||
try buf.appendSlice(allocator, " sx_");
|
||||
try buf.appendSlice(allocator, md.name);
|
||||
try buf.append(allocator, '(');
|
||||
try emitJavaParamList(allocator, buf, md, opts);
|
||||
try buf.appendSlice(allocator, ");\n");
|
||||
}
|
||||
|
||||
fn emitJavaReturnType(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
ret: ?*ast.Node,
|
||||
opts: Options,
|
||||
) EmitError!void {
|
||||
if (ret == null) {
|
||||
try buf.appendSlice(allocator, "void");
|
||||
return;
|
||||
}
|
||||
try emitJavaType(allocator, buf, ret.?, opts);
|
||||
}
|
||||
|
||||
fn emitJavaParamList(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
md: ast.ForeignMethodDecl,
|
||||
opts: Options,
|
||||
) EmitError!void {
|
||||
const start: usize = if (md.is_static) 0 else 1; // skip self
|
||||
for (md.params[start..], 0..) |p, i| {
|
||||
if (i > 0) try buf.appendSlice(allocator, ", ");
|
||||
try emitJavaType(allocator, buf, p, opts);
|
||||
try buf.append(allocator, ' ');
|
||||
try buf.appendSlice(allocator, md.param_names[start + i]);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitJavaArgList(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
md: ast.ForeignMethodDecl,
|
||||
) EmitError!void {
|
||||
const start: usize = if (md.is_static) 0 else 1;
|
||||
for (md.param_names[start..], 0..) |name, i| {
|
||||
if (i > 0) try buf.appendSlice(allocator, ", ");
|
||||
try buf.appendSlice(allocator, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn emitJavaType(
|
||||
allocator: Allocator,
|
||||
buf: *std.ArrayList(u8),
|
||||
type_node: *ast.Node,
|
||||
opts: Options,
|
||||
) EmitError!void {
|
||||
switch (type_node.data) {
|
||||
.type_expr => |te| {
|
||||
const name = javaPrimitiveName(te.name) orelse return EmitError.UnsupportedType;
|
||||
try buf.appendSlice(allocator, name);
|
||||
},
|
||||
.pointer_type_expr => |ptr| {
|
||||
const inner = ptr.pointee_type;
|
||||
if (inner.data != .type_expr) return EmitError.UnsupportedType;
|
||||
const target_name = inner.data.type_expr.name;
|
||||
if (std.mem.eql(u8, target_name, "void")) {
|
||||
try buf.appendSlice(allocator, "Object");
|
||||
return;
|
||||
}
|
||||
if (opts.classes) |reg| {
|
||||
if (reg.get(target_name)) |path| {
|
||||
try appendDotted(allocator, buf, path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Unknown alias — pass through dotted as best-effort. Sema
|
||||
// should catch this earlier; tolerate for now.
|
||||
try appendDotted(allocator, buf, target_name);
|
||||
},
|
||||
else => return EmitError.UnsupportedType,
|
||||
}
|
||||
}
|
||||
|
||||
fn javaPrimitiveName(name: []const u8) ?[]const u8 {
|
||||
if (std.mem.eql(u8, name, "void")) return "void";
|
||||
if (std.mem.eql(u8, name, "bool")) return "boolean";
|
||||
if (std.mem.eql(u8, name, "s8")) return "byte";
|
||||
if (std.mem.eql(u8, name, "u8")) return "byte";
|
||||
if (std.mem.eql(u8, name, "s16")) return "short";
|
||||
if (std.mem.eql(u8, name, "u16")) return "char";
|
||||
if (std.mem.eql(u8, name, "s32")) return "int";
|
||||
if (std.mem.eql(u8, name, "s64")) return "long";
|
||||
if (std.mem.eql(u8, name, "f32")) return "float";
|
||||
if (std.mem.eql(u8, name, "f64")) return "double";
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user