ffi 2.9: cross-class *Foo resolves via ForeignClassDecl registry

`Context` gains an optional `classes: ?*const ClassRegistry` lookup
(sx alias → foreign path). `writeType`'s pointer-type arm now treats
`*Self` and `*Foo` uniformly: `Self` resolves to `enclosing_path`,
any other named target is looked up in the registry. Missing
registry or missing key both surface as `UnknownClassAlias`.

`DeriveError.CrossClassRefNotYetSupported` retired in favour of
`UnknownClassAlias` — the new error fires for both
"no-registry-provided" and "alias-not-in-registry", giving the
caller (later, sema with a real diagnostic) one error variant to
handle.

Four new unit tests: cross-class resolves with registry, errors
without registry, errors with empty registry, and end-to-end
`deriveMethod` with chained `*Self`/`*Foo` (`getDecorView ::
(self: *Self) -> *View → ()Landroid/view/View;`).

13 jni_descriptor tests pass; 126/126 examples still green.
This commit is contained in:
agra
2026-05-20 10:26:03 +03:00
parent 21c49066e5
commit 51882656a5
2 changed files with 103 additions and 12 deletions

View File

@@ -29,15 +29,23 @@ const Node = ast.Node;
pub const DeriveError = error{
UnknownPrimitive,
UnknownClassAlias, // *Foo where Foo isn't a declared #jni_class
UnsupportedType,
CrossClassRefNotYetSupported, // *Foo for non-Self — lands in step 2.9
OutOfMemory,
};
/// Map from sx-side alias → foreign path of declared `#jni_class` /
/// `#jni_interface` decls. Used to resolve `*Foo` into `L<path>;` in
/// the descriptor. Built during lowering's scan pass.
pub const ClassRegistry = std.StringHashMap([]const u8);
pub const Context = struct {
/// Foreign path of the enclosing #jni_class — used to resolve `*Self`.
/// e.g. "android/view/View".
enclosing_path: []const u8,
/// Lookup for sibling/forward-declared `#jni_class` aliases. When null,
/// only `*Self` resolves; any other pointer-to-named-type errors.
classes: ?*const ClassRegistry = null,
};
/// Appends a single JNI type-descriptor to `buf` for `type_node`.
@@ -71,15 +79,19 @@ pub fn writeType(
try writeType(allocator, buf, ctx, arr.element_type);
},
.pointer_type_expr => |ptr| {
// *Self → L<enclosing-foreign-path>;
// *Self → L<enclosing>;, *Foo → L<Foo's foreign path>;
const inner = ptr.pointee_type;
if (inner.data == .type_expr and std.mem.eql(u8, inner.data.type_expr.name, "Self")) {
try buf.append(allocator, 'L');
try buf.appendSlice(allocator, ctx.enclosing_path);
try buf.append(allocator, ';');
return;
}
return DeriveError.CrossClassRefNotYetSupported;
if (inner.data != .type_expr) return DeriveError.UnsupportedType;
const target_name = inner.data.type_expr.name;
const target_path: []const u8 = if (std.mem.eql(u8, target_name, "Self"))
ctx.enclosing_path
else if (ctx.classes) |reg|
reg.get(target_name) orelse return DeriveError.UnknownClassAlias
else
return DeriveError.UnknownClassAlias;
try buf.append(allocator, 'L');
try buf.appendSlice(allocator, target_path);
try buf.append(allocator, ';');
},
else => return DeriveError.UnsupportedType,
}