ffi #jni_main: emit implements clauses from #implements Alias; members
`jni_java_emit` previously dropped `#implements` members on the floor. They now compose into the Java class header — first one prefixed with ` implements `, subsequent ones comma-separated. Aliases resolve through the class registry just like `#extends`: an unmapped alias passes through verbatim (handy for built-in JVM interfaces like `java.lang.Runnable` without declaring a `#jni_class` for them). First building block of the chess-on-Pixel migration: the new Activity needs `implements android.view.SurfaceHolder$Callback` to receive surfaceCreated / surfaceChanged / surfaceDestroyed callbacks from the SurfaceView it hosts. Unit test locks in both the registry-resolved and pass-through paths. 131 host / zig build test green.
This commit is contained in:
@@ -235,3 +235,44 @@ test "default-package class (no slash in foreign_path)" {
|
|||||||
try std.testing.expect(std.mem.indexOf(u8, out, "package ") == null);
|
try std.testing.expect(std.mem.indexOf(u8, out, "package ") == null);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, out, "public class SxNoPackage") != null);
|
try std.testing.expect(std.mem.indexOf(u8, out, "public class SxNoPackage") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "#implements clauses on the class header" {
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Two interfaces: one resolvable via the registry, one passed through verbatim.
|
||||||
|
const impl_a: ast.ForeignClassMember = .{ .implements = "Callback" };
|
||||||
|
const impl_b: ast.ForeignClassMember = .{ .implements = "java.lang.Runnable" };
|
||||||
|
const method: 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("Callback", "android/view/SurfaceHolder$Callback");
|
||||||
|
|
||||||
|
const fcd: ast.ForeignClassDecl = .{
|
||||||
|
.name = "SxApp",
|
||||||
|
.foreign_path = "co/example/SxApp",
|
||||||
|
.runtime = .jni_class,
|
||||||
|
.is_main = true,
|
||||||
|
.members = &.{ impl_a, impl_b, method },
|
||||||
|
};
|
||||||
|
const out = try emit.emitJavaSource(a, &fcd, .{ .classes = ®istry });
|
||||||
|
defer a.free(out);
|
||||||
|
|
||||||
|
try std.testing.expect(std.mem.indexOf(
|
||||||
|
u8,
|
||||||
|
out,
|
||||||
|
"public class SxApp extends android.app.Activity implements android.view.SurfaceHolder$Callback, java.lang.Runnable {",
|
||||||
|
) != null);
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,6 +90,27 @@ pub fn emitJavaSource(
|
|||||||
try buf.appendSlice(allocator, parts.cls);
|
try buf.appendSlice(allocator, parts.cls);
|
||||||
try buf.appendSlice(allocator, " extends ");
|
try buf.appendSlice(allocator, " extends ");
|
||||||
try buf.appendSlice(allocator, parent);
|
try buf.appendSlice(allocator, parent);
|
||||||
|
|
||||||
|
// `#implements Alias;` body items become Java `implements` clauses on the
|
||||||
|
// class header. Aliases resolve through the class registry the same way
|
||||||
|
// `#extends` does — an unmapped alias passes through verbatim (useful for
|
||||||
|
// referring to built-in JVM interfaces without declaring them).
|
||||||
|
var first_iface = true;
|
||||||
|
for (fcd.members) |m| switch (m) {
|
||||||
|
.implements => |alias| {
|
||||||
|
try buf.appendSlice(allocator, if (first_iface) " implements " else ", ");
|
||||||
|
first_iface = false;
|
||||||
|
if (opts.classes) |reg| {
|
||||||
|
if (reg.get(alias)) |path| {
|
||||||
|
try appendDotted(allocator, &buf, path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try buf.appendSlice(allocator, alias);
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
};
|
||||||
|
|
||||||
try buf.appendSlice(allocator, " {\n");
|
try buf.appendSlice(allocator, " {\n");
|
||||||
|
|
||||||
// Two passes: @Override stubs that call super + native delegate,
|
// Two passes: @Override stubs that call super + native delegate,
|
||||||
|
|||||||
Reference in New Issue
Block a user