refactor(ffi-linkage): Phase 9.2b — rename runtime-class fns + state → runtime_* / is_reference
The runtime-class object-model identifiers (Decision 5): parse/lower/find/resolve/ register/stamp fns Foreign→Runtime (parseRuntimeClassDecl, lowerRuntimeMethodCall, findRuntimeMethodInChain, resolveRuntimeMethodReturnType, registerRuntimeClassDecl, runtimeClassStructType, runtimeKindForOffset, …); state foreign_class_map→ runtime_class_map, current_foreign_class/_method→current_runtime_*, the foreign_class_decl union variant→runtime_class_decl, foreign_method/static/instance/ class→runtime_*; and the reference-vs-define flag is_foreign→is_reference (+ is_foreign_eff→is_reference_eff) now that it only lives on RuntimeClassDecl. Snapshot-neutral; suite green (646/444). Remaining 9.2: the foreign_path family (coupled .sx hooks: jni_main_foreign_path_at spans build.sx/bundle.sx/compiler_hooks.zig/specs.md) + the extern-ref validators (checkForeignRefs etc. → Extern, linkage not runtime) + bare 'foreign' comments.
This commit is contained in:
@@ -93,7 +93,7 @@ pub const Node = struct {
|
||||
protocol_decl: ProtocolDecl,
|
||||
impl_block: ImplBlock,
|
||||
ffi_intrinsic_call: FfiIntrinsicCall,
|
||||
foreign_class_decl: RuntimeClassDecl,
|
||||
runtime_class_decl: RuntimeClassDecl,
|
||||
jni_env_block: JniEnvBlock,
|
||||
|
||||
pub fn declName(self: Data) ?[]const u8 {
|
||||
@@ -109,7 +109,7 @@ pub const Node = struct {
|
||||
.ufcs_alias => |d| d.name,
|
||||
.c_import_decl => |d| d.name,
|
||||
.protocol_decl => |d| d.name,
|
||||
.foreign_class_decl => |d| d.name,
|
||||
.runtime_class_decl => |d| d.name,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@@ -888,7 +888,7 @@ pub const RuntimeClassDecl = struct {
|
||||
foreign_path: []const u8, // directive arg: "java/path/Foo" / "NSString" / "Foundation.URL"
|
||||
runtime: RuntimeKind,
|
||||
members: []const RuntimeClassMember = &.{},
|
||||
is_foreign: bool = false, // `#foreign #...` prefix — class is provided by the foreign runtime; we only reference it
|
||||
is_reference: bool = false, // `#foreign #...` prefix — class is provided by the foreign runtime; we only reference it
|
||||
is_main: bool = false, // `#jni_main` / `#objc_main` — class is the launchable entry (Activity / UIApplicationDelegate / ...)
|
||||
/// True when the sx-side alias NAME was a backtick raw identifier — exempt
|
||||
/// from the reserved-type-name decl check.
|
||||
|
||||
@@ -295,7 +295,7 @@ pub const FfiCtors = struct {
|
||||
// Parent class — pre-resolved Obj-C runtime name from
|
||||
// lower.zig (M2.3 resolveObjcParentName). Stored on the
|
||||
// cache entry so emit_llvm doesn't re-walk
|
||||
// foreign_class_map here.
|
||||
// runtime_class_map here.
|
||||
const parent_name = entry_kv.parent_objc_name;
|
||||
|
||||
const parent_str_global = self.e.emitPrivateCString(parent_name, "OBJC_CLASS_NAME_");
|
||||
|
||||
10
src/core.zig
10
src/core.zig
@@ -359,12 +359,12 @@ pub const Compilation = struct {
|
||||
return module;
|
||||
}
|
||||
|
||||
/// Walk `lowering.program_index.foreign_class_map` and render Java sources for every
|
||||
/// Walk `lowering.program_index.runtime_class_map` and render Java sources for every
|
||||
/// `#jni_main #jni_class("...")` declaration. Renders happen here so the
|
||||
/// AST + class-registry snapshot stay confined to the lowering pass; the
|
||||
/// downstream APK pipeline only needs `{foreign_path, java_source}` pairs.
|
||||
fn collectJniMainEmissions(self: *Compilation, lowering: *ir.Lowering) !void {
|
||||
// `foreign_class_map` registers each decl under bare + qualified names —
|
||||
// `runtime_class_map` registers each decl under bare + qualified names —
|
||||
// dedupe by foreign_path so a single decl emits one .java.
|
||||
var seen = std.StringHashMap(void).init(self.allocator);
|
||||
defer seen.deinit();
|
||||
@@ -373,7 +373,7 @@ pub const Compilation = struct {
|
||||
// and `#extends Alias` resolution.
|
||||
var registry = std.StringHashMap([]const u8).init(self.allocator);
|
||||
defer registry.deinit();
|
||||
var it_reg = lowering.program_index.foreign_class_map.iterator();
|
||||
var it_reg = lowering.program_index.runtime_class_map.iterator();
|
||||
while (it_reg.next()) |entry| {
|
||||
try registry.put(entry.key_ptr.*, entry.value_ptr.*.foreign_path);
|
||||
}
|
||||
@@ -384,11 +384,11 @@ pub const Compilation = struct {
|
||||
// .so loading via another class.
|
||||
const lib_name = libNameFromOutputPath(self.target_config.output_path);
|
||||
|
||||
var it = lowering.program_index.foreign_class_map.iterator();
|
||||
var it = lowering.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const fcd = entry.value_ptr.*;
|
||||
if (!fcd.is_main) continue;
|
||||
if (fcd.is_foreign) continue;
|
||||
if (fcd.is_reference) continue;
|
||||
if (fcd.runtime != .jni_class) continue;
|
||||
if (seen.contains(fcd.foreign_path)) continue;
|
||||
try seen.put(fcd.foreign_path, {});
|
||||
|
||||
@@ -371,7 +371,7 @@ pub const ResolvedModule = struct {
|
||||
/// symbol, not split into a duplicate `__stdinp.1`.
|
||||
fn isPerSourceDecl(decl: *const Node) bool {
|
||||
return switch (decl.data) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .protocol_decl, .foreign_class_decl => true,
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .protocol_decl, .runtime_class_decl => true,
|
||||
.const_decl => |cd| cd.value.data != .fn_decl,
|
||||
else => false,
|
||||
};
|
||||
@@ -463,7 +463,7 @@ pub const RawDeclRef = union(enum) {
|
||||
union_decl: *const ast.UnionDecl,
|
||||
error_set_decl: *const ast.ErrorSetDecl,
|
||||
protocol_decl: *const ast.ProtocolDecl,
|
||||
foreign_class_decl: *const ast.RuntimeClassDecl,
|
||||
runtime_class_decl: *const ast.RuntimeClassDecl,
|
||||
namespace_decl: *const ast.NamespaceDecl,
|
||||
};
|
||||
|
||||
@@ -513,7 +513,7 @@ pub fn rawDeclRefOf(decl: *const Node) ?RawDeclRef {
|
||||
.union_decl => |*d| .{ .union_decl = d },
|
||||
.error_set_decl => |*d| .{ .error_set_decl = d },
|
||||
.protocol_decl => |*d| .{ .protocol_decl = d },
|
||||
.foreign_class_decl => |*d| .{ .foreign_class_decl = d },
|
||||
.runtime_class_decl => |*d| .{ .runtime_class_decl = d },
|
||||
.namespace_decl => |*d| .{ .namespace_decl = d },
|
||||
else => null,
|
||||
};
|
||||
@@ -592,7 +592,7 @@ pub const DeclKind = enum {
|
||||
@"union",
|
||||
error_set,
|
||||
protocol,
|
||||
foreign_class,
|
||||
runtime_class,
|
||||
namespace,
|
||||
};
|
||||
|
||||
@@ -606,7 +606,7 @@ fn declKindOf(ref: RawDeclRef) DeclKind {
|
||||
.union_decl => .@"union",
|
||||
.error_set_decl => .error_set,
|
||||
.protocol_decl => .protocol,
|
||||
.foreign_class_decl => .foreign_class,
|
||||
.runtime_class_decl => .runtime_class,
|
||||
.namespace_decl => .namespace,
|
||||
};
|
||||
}
|
||||
@@ -815,9 +815,9 @@ fn stampFnBodySource(decl: *Node, file_path: []const u8) void {
|
||||
// An sx-defined `#objc_class` / `#jni_class`: its IMP trampolines are
|
||||
// emitted at lowering time (possibly from another module's context), so
|
||||
// record the defining path AND stamp each method body (E4).
|
||||
.foreign_class_decl => {
|
||||
decl.data.foreign_class_decl.source_file = file_path;
|
||||
stampForeignClassMethodSources(decl.data.foreign_class_decl, file_path);
|
||||
.runtime_class_decl => {
|
||||
decl.data.runtime_class_decl.source_file = file_path;
|
||||
stampRuntimeClassMethodSources(decl.data.runtime_class_decl, file_path);
|
||||
},
|
||||
.const_decl => |cd| switch (cd.value.data) {
|
||||
.fn_decl => |fd| fd.body.source_file = file_path,
|
||||
@@ -827,9 +827,9 @@ fn stampFnBodySource(decl: *Node, file_path: []const u8) void {
|
||||
// bodies need the defining path stamped just like a top-level fn.
|
||||
.struct_decl => |sd| stampStructMethodSources(sd, file_path),
|
||||
.protocol_decl => cd.value.data.protocol_decl.source_file = file_path,
|
||||
.foreign_class_decl => {
|
||||
cd.value.data.foreign_class_decl.source_file = file_path;
|
||||
stampForeignClassMethodSources(cd.value.data.foreign_class_decl, file_path);
|
||||
.runtime_class_decl => {
|
||||
cd.value.data.runtime_class_decl.source_file = file_path;
|
||||
stampRuntimeClassMethodSources(cd.value.data.runtime_class_decl, file_path);
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
@@ -853,7 +853,7 @@ fn stampStructMethodSources(sd: ast.StructDecl, file_path: []const u8) void {
|
||||
|
||||
/// Stamp the defining module path onto every bodied method of an sx-defined
|
||||
/// foreign class, so the method's sx body lowers in the class's own module.
|
||||
fn stampForeignClassMethodSources(fcd: ast.RuntimeClassDecl, file_path: []const u8) void {
|
||||
fn stampRuntimeClassMethodSources(fcd: ast.RuntimeClassDecl, file_path: []const u8) void {
|
||||
for (fcd.members) |m| {
|
||||
if (m == .method) {
|
||||
if (m.method.body) |b| b.source_file = file_path;
|
||||
|
||||
@@ -360,7 +360,7 @@ test "plan: foreign-class instance vs static dispatch" {
|
||||
.{ .method = .{ .name = "stringWithUTF8String", .params = &.{}, .param_names = &.{}, .return_type = typeExpr(alloc, "i64"), .is_static = true } },
|
||||
};
|
||||
var fcd = ast.RuntimeClassDecl{ .name = "NSString", .foreign_path = "NSString", .runtime = .objc_class, .members = &members };
|
||||
l.program_index.foreign_class_map.put("NSString", &fcd) catch unreachable;
|
||||
l.program_index.runtime_class_map.put("NSString", &fcd) catch unreachable;
|
||||
_ = module.types.intern(.{ .@"struct" = .{ .name = module.types.internString("NSString"), .fields = &.{} } });
|
||||
|
||||
// Instance: `cast(NSString, _).length` — receiver prepended.
|
||||
@@ -368,9 +368,9 @@ test "plan: foreign-class instance vs static dispatch" {
|
||||
const recv = callNode(alloc, ident(alloc, "cast"), &[_]*Node{ typeExpr(alloc, "NSString"), intLit(alloc, 0) });
|
||||
const call = callNode(alloc, fieldAccess(alloc, recv, "length"), &.{});
|
||||
const p = cr.plan(&call.data.call);
|
||||
try std.testing.expectEqual(CallPlan.Kind.foreign_instance, p.kind);
|
||||
try std.testing.expectEqualStrings("length", p.target.foreign_method.name);
|
||||
try std.testing.expect(!p.target.foreign_method.is_static);
|
||||
try std.testing.expectEqual(CallPlan.Kind.runtime_instance, p.kind);
|
||||
try std.testing.expectEqualStrings("length", p.target.runtime_method.name);
|
||||
try std.testing.expect(!p.target.runtime_method.is_static);
|
||||
try std.testing.expectEqual(TypeId.i64, p.return_type);
|
||||
try std.testing.expect(p.prepends_receiver);
|
||||
}
|
||||
@@ -378,9 +378,9 @@ test "plan: foreign-class instance vs static dispatch" {
|
||||
{
|
||||
const call = callNode(alloc, fieldAccess(alloc, ident(alloc, "NSString"), "stringWithUTF8String"), &.{});
|
||||
const p = cr.plan(&call.data.call);
|
||||
try std.testing.expectEqual(CallPlan.Kind.foreign_static, p.kind);
|
||||
try std.testing.expectEqualStrings("stringWithUTF8String", p.target.foreign_method.name);
|
||||
try std.testing.expect(p.target.foreign_method.is_static);
|
||||
try std.testing.expectEqual(CallPlan.Kind.runtime_static, p.kind);
|
||||
try std.testing.expectEqualStrings("stringWithUTF8String", p.target.runtime_method.name);
|
||||
try std.testing.expect(p.target.runtime_method.is_static);
|
||||
try std.testing.expect(!p.prepends_receiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ pub const CallPlan = struct {
|
||||
/// type prefix). Distinct from `namespace_fn` precisely because the
|
||||
/// receiver IS prepended (`prepends_receiver`).
|
||||
free_fn_ufcs,
|
||||
foreign_instance,
|
||||
foreign_static,
|
||||
runtime_instance,
|
||||
runtime_static,
|
||||
/// `pkg.fn(args)` — the receiver is a namespace / module prefix, NOT a
|
||||
/// value, so nothing is prepended.
|
||||
namespace_fn,
|
||||
@@ -82,7 +82,7 @@ pub const CallPlan = struct {
|
||||
/// Protocol method, by index in the protocol's method table.
|
||||
protocol_method: u32,
|
||||
/// Foreign-class method (Obj-C / JNI), with its static-ness.
|
||||
foreign_method: struct { name: []const u8, is_static: bool },
|
||||
runtime_method: struct { name: []const u8, is_static: bool },
|
||||
/// Enum / tagged-union type under construction.
|
||||
constructed: TypeId,
|
||||
};
|
||||
@@ -254,13 +254,13 @@ pub const CallResolver = struct {
|
||||
const inner_info = self.l.module.types.get(recv_inner);
|
||||
if (inner_info == .@"struct") {
|
||||
const sn = self.l.module.types.getString(inner_info.@"struct".name);
|
||||
if (self.l.program_index.foreign_class_map.get(sn)) |fcd| {
|
||||
if (self.l.program_index.runtime_class_map.get(sn)) |fcd| {
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.method => |md| if (!md.is_static and std.mem.eql(u8, md.name, cfa.field)) {
|
||||
return .{
|
||||
.kind = .foreign_instance,
|
||||
.return_type = self.l.resolveForeignMethodReturnType(fcd, md),
|
||||
.target = .{ .foreign_method = .{ .name = md.name, .is_static = false } },
|
||||
.kind = .runtime_instance,
|
||||
.return_type = self.l.resolveRuntimeMethodReturnType(fcd, md),
|
||||
.target = .{ .runtime_method = .{ .name = md.name, .is_static = false } },
|
||||
.prepends_receiver = true,
|
||||
};
|
||||
},
|
||||
@@ -396,13 +396,13 @@ pub const CallResolver = struct {
|
||||
};
|
||||
if (type_name) |tn| {
|
||||
// Foreign-class static method: `Alias.static_method(args)`.
|
||||
if (self.l.program_index.foreign_class_map.get(tn)) |fcd| {
|
||||
if (self.l.program_index.runtime_class_map.get(tn)) |fcd| {
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.method => |md| if (md.is_static and std.mem.eql(u8, md.name, cfa.field)) {
|
||||
return .{
|
||||
.kind = .foreign_static,
|
||||
.return_type = self.l.resolveForeignMethodReturnType(fcd, md),
|
||||
.target = .{ .foreign_method = .{ .name = md.name, .is_static = true } },
|
||||
.kind = .runtime_static,
|
||||
.return_type = self.l.resolveRuntimeMethodReturnType(fcd, md),
|
||||
.target = .{ .runtime_method = .{ .name = md.name, .is_static = true } },
|
||||
};
|
||||
},
|
||||
else => {},
|
||||
|
||||
@@ -179,7 +179,7 @@ pub const ObjcLowering = struct {
|
||||
const is_objc_obj = blk: {
|
||||
if (pointee_info != .@"struct") break :blk false;
|
||||
const name = self.l.module.types.getString(pointee_info.@"struct".name);
|
||||
break :blk self.l.program_index.foreign_class_map.get(name) != null;
|
||||
break :blk self.l.program_index.runtime_class_map.get(name) != null;
|
||||
};
|
||||
if (is_objc_obj) {
|
||||
try out.append(self.l.alloc, '@');
|
||||
@@ -319,7 +319,7 @@ pub const ObjcLowering = struct {
|
||||
const pointee_info = self.l.module.types.get(ptr_info.pointer.pointee);
|
||||
if (pointee_info != .@"struct") return false;
|
||||
const struct_name = self.l.module.types.getString(pointee_info.@"struct".name);
|
||||
const fcd = self.l.program_index.foreign_class_map.get(struct_name) orelse return false;
|
||||
const fcd = self.l.program_index.runtime_class_map.get(struct_name) orelse return false;
|
||||
return fcd.runtime == .objc_class or fcd.runtime == .objc_protocol;
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ pub const ObjcLowering = struct {
|
||||
const pointee_info = self.l.module.types.get(pointee);
|
||||
if (pointee_info != .@"struct") break :blk false;
|
||||
const struct_name = self.l.module.types.getString(pointee_info.@"struct".name);
|
||||
const fcd = self.l.program_index.foreign_class_map.get(struct_name) orelse break :blk false;
|
||||
const fcd = self.l.program_index.runtime_class_map.get(struct_name) orelse break :blk false;
|
||||
break :blk fcd.runtime == .objc_class or fcd.runtime == .objc_protocol;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Tests for jni_java_emit.zig — #jni_main pipeline slice 1.
|
||||
// Locks in the Java source emitted from `RuntimeClassDecl` AST nodes:
|
||||
// package split, class header, @Override delegate pattern, primitive
|
||||
// type mapping, cross-class refs through the foreign_class registry.
|
||||
// type mapping, cross-class refs through the runtime_class registry.
|
||||
|
||||
const std = @import("std");
|
||||
const ast = @import("../ast.zig");
|
||||
|
||||
@@ -389,7 +389,7 @@ test "lower: objcDefinedStateStructType collects user-declared fields" {
|
||||
.foreign_path = "SxFoo",
|
||||
.runtime = .objc_class,
|
||||
.members = &members,
|
||||
.is_foreign = false,
|
||||
.is_reference = false,
|
||||
.is_main = false,
|
||||
};
|
||||
|
||||
@@ -421,7 +421,7 @@ test "lower: objcDefinedStateStructType handles empty field set" {
|
||||
.foreign_path = "SxEmpty",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = false,
|
||||
.is_reference = false,
|
||||
.is_main = false,
|
||||
};
|
||||
|
||||
@@ -453,7 +453,7 @@ test "lower: objcDefinedStateStructType skips non-field members" {
|
||||
.foreign_path = "SxMixed",
|
||||
.runtime = .objc_class,
|
||||
.members = &members,
|
||||
.is_foreign = false,
|
||||
.is_reference = false,
|
||||
.is_main = false,
|
||||
};
|
||||
|
||||
@@ -481,10 +481,10 @@ test "lower: objcTypeEncodingFromSignature emits @ for Obj-C class pointers" {
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSString", &ns_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSString", &ns_fcd);
|
||||
|
||||
// Return *NSString, no args: "@@:"
|
||||
const e1 = try lowering.objc().objcTypeEncodingFromSignature(ns_ptr, &.{}, null);
|
||||
@@ -514,10 +514,10 @@ test "lower: objcTypeEncodingFromSignature unwraps optional to wire type" {
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSString", &ns_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSString", &ns_fcd);
|
||||
|
||||
// `?i64 -> ?*NSString` collapses to `q -> @` at the Obj-C boundary.
|
||||
const opt_i64 = module.types.optionalOf(.i64);
|
||||
@@ -683,10 +683,10 @@ test "lower: isObjcClassPointer recognises pointer-to-foreign-Obj-C-class" {
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSString", &ns_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSString", &ns_fcd);
|
||||
try std.testing.expect(lowering.objc().isObjcClassPointer(ns_ptr));
|
||||
|
||||
// *NSCopying where NSCopying is a registered Obj-C *protocol* → also true
|
||||
@@ -699,10 +699,10 @@ test "lower: isObjcClassPointer recognises pointer-to-foreign-Obj-C-class" {
|
||||
.foreign_path = "NSCopying",
|
||||
.runtime = .objc_protocol,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSCopying", &proto_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSCopying", &proto_fcd);
|
||||
try std.testing.expect(lowering.objc().isObjcClassPointer(proto_ptr));
|
||||
|
||||
// *Plain where Plain is a non-foreign struct → false.
|
||||
@@ -731,10 +731,10 @@ test "lower: objcPropertyKind defaults + explicit ARC modifiers" {
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSString", &ns_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSString", &ns_fcd);
|
||||
|
||||
// Primitive field, no modifiers → assign (the non-object default).
|
||||
const prim = ast.RuntimeFieldDecl{ .name = "count", .field_type = typeKeyword(alloc, "i32"), .is_property = true };
|
||||
@@ -756,10 +756,10 @@ test "lower: objcPropertyKind defaults + explicit ARC modifiers" {
|
||||
.foreign_path = "NSCoding",
|
||||
.runtime = .objc_protocol,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try lowering.program_index.foreign_class_map.put("NSCoding", &proto_fcd);
|
||||
try lowering.program_index.runtime_class_map.put("NSCoding", &proto_fcd);
|
||||
const proto_ty = typeKeyword(alloc, "*NSCoding");
|
||||
defer alloc.destroy(proto_ty);
|
||||
const proto_default = ast.RuntimeFieldDecl{ .name = "coder", .field_type = proto_ty, .is_property = true };
|
||||
|
||||
@@ -275,8 +275,8 @@ pub const Lowering = struct {
|
||||
trace_clear_fid: ?FuncId = null, // extern `sx_trace_clear`
|
||||
needs_trace_runtime: bool = false, // set when lowering emits a trace push/clear; signals Compilation to auto-link sx_trace.c
|
||||
chain_fail_target: ?ChainFailTarget = null, // ERR E2.4: when set, a failable `or` chain routes its TOTAL failure here (an absorbing consumer like `catch`) instead of propagating to the function
|
||||
current_foreign_class: ?*const ast.RuntimeClassDecl = null, // set while lowering a `#jni_main` (or any sx-defined `#jni_class`) bodied method — `super.method(args)` dispatch resolves the parent class against this fcd's `#extends`
|
||||
current_foreign_method: ?ast.RuntimeMethodDecl = null, // the specific method whose body is being lowered; `super.<same_name>(...)` reuses its signature
|
||||
current_runtime_class: ?*const ast.RuntimeClassDecl = null, // set while lowering a `#jni_main` (or any sx-defined `#jni_class`) bodied method — `super.method(args)` dispatch resolves the parent class against this fcd's `#extends`
|
||||
current_runtime_method: ?ast.RuntimeMethodDecl = null, // the specific method whose body is being lowered; `super.<same_name>(...)` reuses its signature
|
||||
type_bindings: ?std.StringHashMap(TypeId) = null, // generic type param bindings ($T → concrete TypeId)
|
||||
current_match_tags: ?[]const u64 = null, // type tags for current match arm (for runtime dispatch)
|
||||
force_block_value: bool = false, // set by lowerBlockValue to extract if-else values
|
||||
@@ -844,9 +844,9 @@ pub const Lowering = struct {
|
||||
// lowerFieldAccess to go through the `__sx_state` ivar
|
||||
// (object_getIvar + struct_gep) when needed — see M1.2 A.3.
|
||||
if (node.data == .type_expr and std.mem.eql(u8, node.data.type_expr.name, "Self")) {
|
||||
if (self.current_foreign_class) |fcd| {
|
||||
if (self.current_runtime_class) |fcd| {
|
||||
if (fcd.runtime == .objc_class or fcd.runtime == .objc_protocol) {
|
||||
return self.foreignClassStructType(fcd);
|
||||
return self.runtimeClassStructType(fcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1770,15 +1770,15 @@ pub const Lowering = struct {
|
||||
pub const getSelRegisterNameFid = lower_ffi.getSelRegisterNameFid;
|
||||
pub const lowerFfiIntrinsicCall = lower_ffi.lowerFfiIntrinsicCall;
|
||||
pub const lowerJniCall = lower_ffi.lowerJniCall;
|
||||
pub const lowerForeignMethodCall = lower_ffi.lowerForeignMethodCall;
|
||||
pub const resolveForeignClassMemberType = lower_ffi.resolveForeignClassMemberType;
|
||||
pub const resolveForeignMethodReturnType = lower_ffi.resolveForeignMethodReturnType;
|
||||
pub const foreignClassStructType = lower_ffi.foreignClassStructType;
|
||||
pub const lowerRuntimeMethodCall = lower_ffi.lowerRuntimeMethodCall;
|
||||
pub const resolveRuntimeClassMemberType = lower_ffi.resolveRuntimeClassMemberType;
|
||||
pub const resolveRuntimeMethodReturnType = lower_ffi.resolveRuntimeMethodReturnType;
|
||||
pub const runtimeClassStructType = lower_ffi.runtimeClassStructType;
|
||||
pub const lowerObjcMethodCall = lower_ffi.lowerObjcMethodCall;
|
||||
pub const lowerObjcStaticCall = lower_ffi.lowerObjcStaticCall;
|
||||
pub const lowerForeignStaticCall = lower_ffi.lowerForeignStaticCall;
|
||||
pub const lowerRuntimeStaticCall = lower_ffi.lowerRuntimeStaticCall;
|
||||
pub const lowerSuperCall = lower_ffi.lowerSuperCall;
|
||||
pub const registerForeignClassDecl = lower_ffi.registerForeignClassDecl;
|
||||
pub const registerRuntimeClassDecl = lower_ffi.registerRuntimeClassDecl;
|
||||
pub const resolveObjcParentName = lower_ffi.resolveObjcParentName;
|
||||
pub const declareObjcDefinedStateIvarGlobal = lower_ffi.declareObjcDefinedStateIvarGlobal;
|
||||
pub const declareObjcDefinedClassGlobal = lower_ffi.declareObjcDefinedClassGlobal;
|
||||
@@ -1786,15 +1786,15 @@ pub const Lowering = struct {
|
||||
pub const synthesizeFnDeclFromObjcMethod = lower_ffi.synthesizeFnDeclFromObjcMethod;
|
||||
pub const lookupObjcDefinedClassForMethod = lower_ffi.lookupObjcDefinedClassForMethod;
|
||||
pub const getJniEnvTlFids = lower_ffi.getJniEnvTlFids;
|
||||
pub const registerNamespacedForeignClasses = lower_ffi.registerNamespacedForeignClasses;
|
||||
pub const registerNamespacedRuntimeClasses = lower_ffi.registerNamespacedRuntimeClasses;
|
||||
pub const synthesizeJniMainStubs = lower_ffi.synthesizeJniMainStubs;
|
||||
pub const synthesizeJniMainStub = lower_ffi.synthesizeJniMainStub;
|
||||
|
||||
// --- moved to lower/objc_class.zig (lower_objc_class) ---
|
||||
pub const lowerObjcDefinedClassMethods = lower_objc_class.lowerObjcDefinedClassMethods;
|
||||
pub const lookupObjcPropertyOnPointer = lower_objc_class.lookupObjcPropertyOnPointer;
|
||||
pub const findForeignMethodInChain = lower_objc_class.findForeignMethodInChain;
|
||||
pub const findForeignPropertyInChain = lower_objc_class.findForeignPropertyInChain;
|
||||
pub const findRuntimeMethodInChain = lower_objc_class.findRuntimeMethodInChain;
|
||||
pub const findRuntimePropertyInChain = lower_objc_class.findRuntimePropertyInChain;
|
||||
pub const lookupObjcDefinedStateFieldOnPointer = lower_objc_class.lookupObjcDefinedStateFieldOnPointer;
|
||||
pub const lowerObjcDefinedStateFieldRead = lower_objc_class.lowerObjcDefinedStateFieldRead;
|
||||
pub const lowerObjcDefinedStateForObj = lower_objc_class.lowerObjcDefinedStateForObj;
|
||||
|
||||
@@ -610,10 +610,10 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// NewObject. Falls through to existing paths when no match.
|
||||
if (fa.object.data == .identifier) {
|
||||
const alias = fa.object.data.identifier.name;
|
||||
if (self.program_index.foreign_class_map.get(alias)) |fcd| {
|
||||
if (self.program_index.runtime_class_map.get(alias)) |fcd| {
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.method => |md| if (md.is_static and std.mem.eql(u8, md.name, fa.field)) {
|
||||
return self.lowerForeignStaticCall(fcd, md, args.items, c.callee.span);
|
||||
return self.lowerRuntimeStaticCall(fcd, md, args.items, c.callee.span);
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
@@ -896,9 +896,9 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// (or its parallel forms). Routes to the JNI dispatch
|
||||
// shape, descriptor derived from the sx signature.
|
||||
const struct_name = self.getStructTypeName(obj_ty);
|
||||
if (struct_name) |sname_for_foreign| {
|
||||
if (self.program_index.foreign_class_map.get(sname_for_foreign)) |fcd| {
|
||||
return self.lowerForeignMethodCall(fcd, fa.field, obj, args.items, c.callee.span);
|
||||
if (struct_name) |sname_for_runtime| {
|
||||
if (self.program_index.runtime_class_map.get(sname_for_runtime)) |fcd| {
|
||||
return self.lowerRuntimeMethodCall(fcd, fa.field, obj, args.items, c.callee.span);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2237,17 +2237,17 @@ pub fn resolveCallParamTypes(self: *Lowering, c: *const ast.Call, sel_author: ?*
|
||||
}
|
||||
if (self.getStructTypeName(obj_ty)) |sname| {
|
||||
// Foreign-class receiver (`#objc_class` / `#jni_class` / etc.):
|
||||
// resolve the method from `foreign_class_map` walking `#extends`.
|
||||
// resolve the method from `runtime_class_map` walking `#extends`.
|
||||
// Without this path, `target_type` for each arg falls back to
|
||||
// whatever `self.target_type` was on entry — typically the
|
||||
// enclosing fn's return type — which silently truncates `xx ptr`
|
||||
// casts inside e.g. a `BOOL`-returning method body.
|
||||
if (self.program_index.foreign_class_map.get(sname)) |fcd| {
|
||||
if (self.findForeignMethodInChain(fcd, fa.field)) |found| {
|
||||
if (self.program_index.runtime_class_map.get(sname)) |fcd| {
|
||||
if (self.findRuntimeMethodInChain(fcd, fa.field)) |found| {
|
||||
const md = found.method;
|
||||
const saved_fc = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
self.current_foreign_class = found.fcd;
|
||||
const saved_fc = self.current_runtime_class;
|
||||
defer self.current_runtime_class = saved_fc;
|
||||
self.current_runtime_class = found.fcd;
|
||||
const user_param_start: usize = if (md.is_static) 0 else 1;
|
||||
if (md.params.len > user_param_start) {
|
||||
var types_list = std.ArrayList(TypeId).empty;
|
||||
|
||||
@@ -204,10 +204,10 @@ pub fn checkRequiredEntryPoints(self: *Lowering) void {
|
||||
const tc = self.target_config orelse return;
|
||||
if (!tc.isAndroid()) return;
|
||||
|
||||
var it = self.program_index.foreign_class_map.iterator();
|
||||
var it = self.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const fcd = entry.value_ptr.*;
|
||||
if (fcd.is_main and !fcd.is_foreign and fcd.runtime == .jni_class) return;
|
||||
if (fcd.is_main and !fcd.is_reference and fcd.runtime == .jni_class) return;
|
||||
}
|
||||
|
||||
if (self.diagnostics) |diags| {
|
||||
@@ -341,11 +341,11 @@ pub fn lowerDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
.impl_block => {
|
||||
self.protocolResolver().registerImplBlock(&decl.data.impl_block, is_imported, decl);
|
||||
},
|
||||
.foreign_class_decl => {
|
||||
self.registerForeignClassDecl(&decl.data.foreign_class_decl);
|
||||
.runtime_class_decl => {
|
||||
self.registerRuntimeClassDecl(&decl.data.runtime_class_decl);
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
self.registerNamespacedForeignClasses(ns);
|
||||
self.registerNamespacedRuntimeClasses(ns);
|
||||
if (self.main_file != null) {
|
||||
self.registerNamespaceQualifiedFns(ns.name, ns.own_decls);
|
||||
self.lowerDecls(ns.decls);
|
||||
@@ -769,11 +769,11 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
.impl_block => {
|
||||
self.protocolResolver().registerImplBlock(&decl.data.impl_block, is_imported, decl);
|
||||
},
|
||||
.foreign_class_decl => {
|
||||
self.registerForeignClassDecl(&decl.data.foreign_class_decl);
|
||||
.runtime_class_decl => {
|
||||
self.registerRuntimeClassDecl(&decl.data.runtime_class_decl);
|
||||
},
|
||||
.namespace_decl => |ns| {
|
||||
self.registerNamespacedForeignClasses(ns);
|
||||
self.registerNamespacedRuntimeClasses(ns);
|
||||
if (self.main_file != null) {
|
||||
self.scanDecls(ns.decls);
|
||||
self.registerNamespaceQualifiedFns(ns.name, ns.own_decls);
|
||||
@@ -1746,7 +1746,7 @@ pub fn selectNominalLeaf(self: *Lowering, name: []const u8, from: []const u8, ra
|
||||
/// it is recognised via `type_aliases_by_source` separately from named types.
|
||||
pub fn isNamedTypeKind(raw: resolver_mod.RawDeclRef) bool {
|
||||
return switch (raw) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .protocol_decl, .foreign_class_decl => true,
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl, .protocol_decl, .runtime_class_decl => true,
|
||||
.fn_decl, .const_decl, .var_decl, .namespace_decl => false,
|
||||
};
|
||||
}
|
||||
@@ -1774,7 +1774,7 @@ pub fn namedRefTid(self: *Lowering, ref: resolver_mod.RawDeclRef, name: []const
|
||||
.struct_decl => |d| (table.type_decl_tids.get(@ptrCast(d)) orelse table.findByName(table.internString(name))),
|
||||
.enum_decl => |d| (table.type_decl_tids.get(@ptrCast(d)) orelse table.findByName(table.internString(name))),
|
||||
.union_decl => |d| (table.type_decl_tids.get(@ptrCast(d)) orelse table.findByName(table.internString(name))),
|
||||
.error_set_decl, .protocol_decl, .foreign_class_decl => table.findByName(table.internString(name)),
|
||||
.error_set_decl, .protocol_decl, .runtime_class_decl => table.findByName(table.internString(name)),
|
||||
.fn_decl, .const_decl, .var_decl, .namespace_decl => null,
|
||||
};
|
||||
}
|
||||
@@ -2288,15 +2288,15 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
// Already lowered?
|
||||
if (self.lowered_functions.contains(name)) return;
|
||||
|
||||
// For sx-defined `#objc_class` methods, pin current_foreign_class
|
||||
// For sx-defined `#objc_class` methods, pin current_runtime_class
|
||||
// so `*Self` substitutions in resolveTypeWithBindings find the
|
||||
// state-struct type (M1.2 A.2b). The inline body-lowering path
|
||||
// below re-resolves param types, so the context must be set
|
||||
// BEFORE any resolveReturnType / resolveParamType call.
|
||||
const saved_fc_lazy = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc_lazy;
|
||||
const saved_fc_lazy = self.current_runtime_class;
|
||||
defer self.current_runtime_class = saved_fc_lazy;
|
||||
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
||||
self.current_foreign_class = fcd;
|
||||
self.current_runtime_class = fcd;
|
||||
}
|
||||
// No AST? (builtins, foreign functions, or imported functions not in this file)
|
||||
const fd = self.program_index.fn_ast_map.get(name) orelse return;
|
||||
@@ -2381,10 +2381,10 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId, name: []const u8) void {
|
||||
// objc-defined-class method context for `*Self` substitution (M1.2 A.2b);
|
||||
// the resolveReturnType / resolveParamType calls below consult it.
|
||||
const saved_fc = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
const saved_fc = self.current_runtime_class;
|
||||
defer self.current_runtime_class = saved_fc;
|
||||
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
||||
self.current_foreign_class = fcd;
|
||||
self.current_runtime_class = fcd;
|
||||
}
|
||||
|
||||
var reentry = FnBodyReentry.enter(self);
|
||||
@@ -2477,13 +2477,13 @@ pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId
|
||||
/// Lower a single function declaration.
|
||||
pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, is_imported: bool) void {
|
||||
// For sx-defined `#objc_class` methods (qualified `<Class>.<method>`),
|
||||
// set `current_foreign_class` so `*Self` substitutions through
|
||||
// set `current_runtime_class` so `*Self` substitutions through
|
||||
// `resolveTypeWithBindings` find the state-struct type (M1.2 A.2b).
|
||||
// Save+restore — function lowering can re-enter.
|
||||
const saved_fc = self.current_foreign_class;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
const saved_fc = self.current_runtime_class;
|
||||
defer self.current_runtime_class = saved_fc;
|
||||
if (self.lookupObjcDefinedClassForMethod(name)) |fcd| {
|
||||
self.current_foreign_class = fcd;
|
||||
self.current_runtime_class = fcd;
|
||||
}
|
||||
|
||||
const name_id = self.module.types.internString(name);
|
||||
|
||||
@@ -233,7 +233,7 @@ pub fn lowerJniCall(self: *Lowering, fic: *const ast.FfiIntrinsicCall) Ref {
|
||||
/// JNI runtimes lower directly to `jni_msg_send` with a descriptor derived
|
||||
/// from the method's sx signature; Obj-C / Swift runtimes are deferred to
|
||||
/// Phase 3/4 and currently surface a clear diagnostic.
|
||||
pub fn lowerForeignMethodCall(
|
||||
pub fn lowerRuntimeMethodCall(
|
||||
self: *Lowering,
|
||||
fcd: *const ast.RuntimeClassDecl,
|
||||
method_name: []const u8,
|
||||
@@ -250,7 +250,7 @@ pub fn lowerForeignMethodCall(
|
||||
// still used for `*Self` substitution at the dispatch site
|
||||
// — the inherited method's *Self should resolve to the
|
||||
// child receiver, not the parent.
|
||||
const found = self.findForeignMethodInChain(fcd, method_name) orelse {
|
||||
const found = self.findRuntimeMethodInChain(fcd, method_name) orelse {
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, span, "no method '{s}' on foreign class '{s}' (or any `#extends` ancestor)", .{ method_name, fcd.name });
|
||||
}
|
||||
@@ -270,7 +270,7 @@ pub fn lowerForeignMethodCall(
|
||||
if (fcd.runtime == .objc_class or fcd.runtime == .objc_protocol) {
|
||||
return self.lowerObjcMethodCall(fcd, method, target, method_args, span);
|
||||
}
|
||||
if (!fcd.is_foreign) {
|
||||
if (!fcd.is_reference) {
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, span, "sx-defined classes on non-Obj-C runtimes can't yet be dispatched into (class '{s}', runtime '{s}')", .{ fcd.name, @tagName(fcd.runtime) });
|
||||
}
|
||||
@@ -295,7 +295,7 @@ pub fn lowerForeignMethodCall(
|
||||
// resolve `*Foo` cross-class refs to their foreign paths.
|
||||
var registry = jni_descriptor.ClassRegistry.init(self.alloc);
|
||||
defer registry.deinit();
|
||||
var it = self.program_index.foreign_class_map.iterator();
|
||||
var it = self.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
registry.put(entry.key_ptr.*, entry.value_ptr.*.foreign_path) catch {};
|
||||
}
|
||||
@@ -357,33 +357,33 @@ pub fn lowerForeignMethodCall(
|
||||
/// with the foreign class's own struct type. Without this substitution
|
||||
/// chained calls like `Cls.alloc().init()` see the inner result as a
|
||||
/// fictitious `Self` struct and the next dispatch lookup fails.
|
||||
pub fn resolveForeignClassMemberType(
|
||||
pub fn resolveRuntimeClassMemberType(
|
||||
self: *Lowering,
|
||||
fcd: *const ast.RuntimeClassDecl,
|
||||
type_node: *const ast.Node,
|
||||
) TypeId {
|
||||
if (type_node.data == .type_expr and std.mem.eql(u8, type_node.data.type_expr.name, "Self")) {
|
||||
return self.foreignClassStructType(fcd);
|
||||
return self.runtimeClassStructType(fcd);
|
||||
}
|
||||
if (type_node.data == .pointer_type_expr) {
|
||||
const pt = type_node.data.pointer_type_expr;
|
||||
if (pt.pointee_type.data == .type_expr and std.mem.eql(u8, pt.pointee_type.data.type_expr.name, "Self")) {
|
||||
return self.module.types.ptrTo(self.foreignClassStructType(fcd));
|
||||
return self.module.types.ptrTo(self.runtimeClassStructType(fcd));
|
||||
}
|
||||
}
|
||||
return self.resolveType(type_node);
|
||||
}
|
||||
|
||||
pub fn resolveForeignMethodReturnType(
|
||||
pub fn resolveRuntimeMethodReturnType(
|
||||
self: *Lowering,
|
||||
fcd: *const ast.RuntimeClassDecl,
|
||||
method: ast.RuntimeMethodDecl,
|
||||
) TypeId {
|
||||
const rt = method.return_type orelse return .void;
|
||||
return self.resolveForeignClassMemberType(fcd, rt);
|
||||
return self.resolveRuntimeClassMemberType(fcd, rt);
|
||||
}
|
||||
|
||||
pub fn foreignClassStructType(self: *Lowering, fcd: *const ast.RuntimeClassDecl) TypeId {
|
||||
pub fn runtimeClassStructType(self: *Lowering, fcd: *const ast.RuntimeClassDecl) TypeId {
|
||||
const name_id = self.module.types.internString(fcd.name);
|
||||
if (self.module.types.findByName(name_id)) |existing| return existing;
|
||||
return self.module.types.intern(.{ .@"struct" = .{ .name = name_id, .fields = &.{} } });
|
||||
@@ -434,7 +434,7 @@ pub fn lowerObjcMethodCall(
|
||||
}
|
||||
}
|
||||
|
||||
const ret_ty = self.resolveForeignMethodReturnType(fcd, method);
|
||||
const ret_ty = self.resolveRuntimeMethodReturnType(fcd, method);
|
||||
|
||||
// Cache the SEL slot per (selector-string, module) like
|
||||
// `#objc_call` does. The mangling produces the literal selector
|
||||
@@ -489,7 +489,7 @@ pub fn lowerObjcStaticCall(
|
||||
}
|
||||
}
|
||||
|
||||
const ret_ty = self.resolveForeignMethodReturnType(fcd, method);
|
||||
const ret_ty = self.resolveRuntimeMethodReturnType(fcd, method);
|
||||
|
||||
const vptr_ty = self.module.types.ptrTo(.void);
|
||||
|
||||
@@ -505,7 +505,7 @@ pub fn lowerObjcStaticCall(
|
||||
// instead of going through `objc_msgSend` (which would land in the
|
||||
// +alloc IMP and use `__sx_default_context.allocator`). This honors
|
||||
// a surrounding `push Context.{ allocator = ... }`.
|
||||
if (!fcd.is_foreign and
|
||||
if (!fcd.is_reference and
|
||||
fcd.runtime == .objc_class and
|
||||
method_args.len == 0 and
|
||||
std.mem.eql(u8, method.name, "alloc"))
|
||||
@@ -568,7 +568,7 @@ pub fn lowerObjcStaticCall(
|
||||
/// user can use `#jni_static_call(T)(class, "name", sig, args...)`
|
||||
/// for those. Constructor is the common case for #jni_main bodies
|
||||
/// that need to instantiate Android classes (SurfaceView, etc.).
|
||||
pub fn lowerForeignStaticCall(
|
||||
pub fn lowerRuntimeStaticCall(
|
||||
self: *Lowering,
|
||||
fcd: *const ast.RuntimeClassDecl,
|
||||
method: ast.RuntimeMethodDecl,
|
||||
@@ -600,7 +600,7 @@ pub fn lowerForeignStaticCall(
|
||||
// Build class registry snapshot for `*Foo` cross-class refs.
|
||||
var registry = jni_descriptor.ClassRegistry.init(self.alloc);
|
||||
defer registry.deinit();
|
||||
var it = self.program_index.foreign_class_map.iterator();
|
||||
var it = self.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
registry.put(entry.key_ptr.*, entry.value_ptr.*.foreign_path) catch {};
|
||||
}
|
||||
@@ -679,7 +679,7 @@ pub fn lowerSuperCall(
|
||||
method_args: []const Ref,
|
||||
span: ast.Span,
|
||||
) Ref {
|
||||
const fcd = self.current_foreign_class orelse {
|
||||
const fcd = self.current_runtime_class orelse {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, span, "'super' is only valid inside a `#jni_class` method body", .{});
|
||||
return Ref.none;
|
||||
};
|
||||
@@ -689,7 +689,7 @@ pub fn lowerSuperCall(
|
||||
var parent_path: []const u8 = "android/app/Activity";
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.extends => |alias| {
|
||||
if (self.program_index.foreign_class_map.get(alias)) |parent_fcd| {
|
||||
if (self.program_index.runtime_class_map.get(alias)) |parent_fcd| {
|
||||
parent_path = parent_fcd.foreign_path;
|
||||
} else {
|
||||
parent_path = alias;
|
||||
@@ -704,14 +704,14 @@ pub fn lowerSuperCall(
|
||||
// the parent class to be declared via `#foreign #jni_class`.
|
||||
var descriptor: []const u8 = "";
|
||||
var resolved_method: ?ast.RuntimeMethodDecl = null;
|
||||
if (self.current_foreign_method) |em| {
|
||||
if (self.current_runtime_method) |em| {
|
||||
if (std.mem.eql(u8, em.name, method_name)) {
|
||||
resolved_method = em;
|
||||
}
|
||||
}
|
||||
if (resolved_method == null) {
|
||||
const parent_fcd = blk: for (fcd.members) |m| switch (m) {
|
||||
.extends => |alias| if (self.program_index.foreign_class_map.get(alias)) |pf| break :blk pf else continue,
|
||||
.extends => |alias| if (self.program_index.runtime_class_map.get(alias)) |pf| break :blk pf else continue,
|
||||
else => {},
|
||||
} else null;
|
||||
if (parent_fcd) |pf| {
|
||||
@@ -733,7 +733,7 @@ pub fn lowerSuperCall(
|
||||
// for `*Self` resolution).
|
||||
var registry = jni_descriptor.ClassRegistry.init(self.alloc);
|
||||
defer registry.deinit();
|
||||
var it = self.program_index.foreign_class_map.iterator();
|
||||
var it = self.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
registry.put(entry.key_ptr.*, entry.value_ptr.*.foreign_path) catch {};
|
||||
}
|
||||
@@ -781,7 +781,7 @@ pub fn lowerSuperCall(
|
||||
// ── Foreign-class registration ──────────────────────────────────
|
||||
|
||||
/// Register a foreign-class declaration. The alias goes into
|
||||
/// `foreign_class_map` for method-dispatch lookup. The underlying
|
||||
/// `runtime_class_map` for method-dispatch lookup. The underlying
|
||||
/// type (e.g. `*Activity`) is resolved via the existing struct
|
||||
/// fallback in `type_bridge.resolveTypeName` (which interns unknown
|
||||
/// named types as 0-field structs).
|
||||
@@ -792,9 +792,9 @@ pub fn lowerSuperCall(
|
||||
/// under qualified names `<ClassName>.<methodName>`. Lazy lowering
|
||||
/// then handles the body via the standard path; `*Self` is
|
||||
/// substituted to `*<ClassName>State` during body lowering (M1.2 A.2b).
|
||||
pub fn registerForeignClassDecl(self: *Lowering, fcd: *const ast.RuntimeClassDecl) void {
|
||||
self.program_index.foreign_class_map.put(fcd.name, fcd) catch {};
|
||||
if (!fcd.is_foreign and fcd.runtime == .objc_class) {
|
||||
pub fn registerRuntimeClassDecl(self: *Lowering, fcd: *const ast.RuntimeClassDecl) void {
|
||||
self.program_index.runtime_class_map.put(fcd.name, fcd) catch {};
|
||||
if (!fcd.is_reference and fcd.runtime == .objc_class) {
|
||||
if (self.module.lookupObjcDefinedClass(fcd.name) == null) {
|
||||
self.module.appendObjcDefinedClass(fcd.name, fcd);
|
||||
// M2.3 — resolve the `#extends` alias to the actual
|
||||
@@ -828,8 +828,8 @@ pub fn registerForeignClassDecl(self: *Lowering, fcd: *const ast.RuntimeClassDec
|
||||
pub fn resolveObjcParentName(self: *Lowering, fcd: *const ast.RuntimeClassDecl) []const u8 {
|
||||
for (fcd.members) |m| switch (m) {
|
||||
.extends => |alias| {
|
||||
if (self.program_index.foreign_class_map.get(alias)) |parent_fcd| {
|
||||
if (parent_fcd.is_foreign) return parent_fcd.foreign_path;
|
||||
if (self.program_index.runtime_class_map.get(alias)) |parent_fcd| {
|
||||
if (parent_fcd.is_reference) return parent_fcd.foreign_path;
|
||||
// Sx-defined parent — its alias IS its Obj-C name.
|
||||
return parent_fcd.name;
|
||||
}
|
||||
@@ -883,11 +883,11 @@ pub fn declareObjcDefinedClassGlobal(self: *Lowering, class_name: []const u8) vo
|
||||
/// (M1.2 A.4b.iii). Bodyless declarations are skipped — they
|
||||
/// reference inherited / external methods, not sx-side bodies.
|
||||
pub fn registerObjcDefinedClassMethods(self: *Lowering, fcd: *const ast.RuntimeClassDecl) void {
|
||||
// Set current_foreign_class so `*Self` substitutions in
|
||||
// Set current_runtime_class so `*Self` substitutions in
|
||||
// declareFunction's type resolution find the state struct.
|
||||
const saved = self.current_foreign_class;
|
||||
self.current_foreign_class = fcd;
|
||||
defer self.current_foreign_class = saved;
|
||||
const saved = self.current_runtime_class;
|
||||
self.current_runtime_class = fcd;
|
||||
defer self.current_runtime_class = saved;
|
||||
|
||||
var method_infos = std.ArrayList(Module.ObjcDefinedMethodEntry).empty;
|
||||
|
||||
@@ -969,7 +969,7 @@ pub fn synthesizeFnDeclFromObjcMethod(self: *Lowering, method: ast.RuntimeMethod
|
||||
/// If `name` matches an sx-defined `#objc_class`'s qualified-method
|
||||
/// pattern (`<ClassName>.<methodName>`), return the class's
|
||||
/// RuntimeClassDecl. Used by `lowerFunction` to set
|
||||
/// `current_foreign_class` so `*Self` resolves to the state struct
|
||||
/// `current_runtime_class` so `*Self` resolves to the state struct
|
||||
/// during body lowering.
|
||||
pub fn lookupObjcDefinedClassForMethod(self: *Lowering, name: []const u8) ?*const ast.RuntimeClassDecl {
|
||||
const dot = std.mem.indexOf(u8, name, ".") orelse return null;
|
||||
@@ -1013,15 +1013,15 @@ pub fn getJniEnvTlFids(self: *Lowering) struct { get: FuncId, set: FuncId } {
|
||||
/// scan/lower already handles bare-name registration; this only adds the
|
||||
/// qualified-name entry, so cross-class refs in method signatures
|
||||
/// (`*View` → bare lookup) still work.
|
||||
pub fn registerNamespacedForeignClasses(self: *Lowering, ns: ast.NamespaceDecl) void {
|
||||
pub fn registerNamespacedRuntimeClasses(self: *Lowering, ns: ast.NamespaceDecl) void {
|
||||
for (ns.decls) |inner| {
|
||||
if (inner.data == .foreign_class_decl) {
|
||||
const fcd = &inner.data.foreign_class_decl;
|
||||
if (inner.data == .runtime_class_decl) {
|
||||
const fcd = &inner.data.runtime_class_decl;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ ns.name, fcd.name }) catch fcd.name;
|
||||
self.program_index.foreign_class_map.put(qualified, fcd) catch {};
|
||||
self.program_index.runtime_class_map.put(qualified, fcd) catch {};
|
||||
} else if (inner.data == .namespace_decl) {
|
||||
// Nested namespaces — qualify with both prefixes.
|
||||
self.registerNamespacedForeignClasses(inner.data.namespace_decl);
|
||||
self.registerNamespacedRuntimeClasses(inner.data.namespace_decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1033,11 +1033,11 @@ pub fn synthesizeJniMainStubs(self: *Lowering) void {
|
||||
var seen = std.StringHashMap(void).init(self.alloc);
|
||||
defer seen.deinit();
|
||||
|
||||
var it = self.program_index.foreign_class_map.iterator();
|
||||
var it = self.program_index.runtime_class_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const fcd = entry.value_ptr.*;
|
||||
if (!fcd.is_main) continue;
|
||||
if (fcd.is_foreign) continue;
|
||||
if (fcd.is_reference) continue;
|
||||
if (fcd.runtime != .jni_class) continue;
|
||||
if (seen.contains(fcd.foreign_path)) continue;
|
||||
seen.put(fcd.foreign_path, {}) catch continue;
|
||||
@@ -1121,13 +1121,13 @@ pub fn synthesizeJniMainStub(self: *Lowering, fcd: *const ast.RuntimeClassDecl,
|
||||
// Record method context so `super.method(args)` inside the body
|
||||
// can find the parent class (via `#extends`) and the method's
|
||||
// signature.
|
||||
const saved_fcd = self.current_foreign_class;
|
||||
const saved_method = self.current_foreign_method;
|
||||
self.current_foreign_class = fcd;
|
||||
self.current_foreign_method = md;
|
||||
const saved_fcd = self.current_runtime_class;
|
||||
const saved_method = self.current_runtime_method;
|
||||
self.current_runtime_class = fcd;
|
||||
self.current_runtime_method = md;
|
||||
defer {
|
||||
self.current_foreign_class = saved_fcd;
|
||||
self.current_foreign_method = saved_method;
|
||||
self.current_runtime_class = saved_fcd;
|
||||
self.current_runtime_method = saved_method;
|
||||
}
|
||||
|
||||
// JNI native methods are C-callable entry points — install the
|
||||
|
||||
@@ -296,7 +296,7 @@ pub fn rawNamedTypePtr(ref: resolver_mod.RawDeclRef) ?*const anyopaque {
|
||||
.union_decl => |d| @ptrCast(d),
|
||||
.error_set_decl => |d| @ptrCast(d),
|
||||
.protocol_decl => |d| @ptrCast(d),
|
||||
.foreign_class_decl => |d| @ptrCast(d),
|
||||
.runtime_class_decl => |d| @ptrCast(d),
|
||||
.fn_decl, .const_decl, .var_decl, .namespace_decl => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const Lowering = lower.Lowering;
|
||||
/// `#objc_class`. The Obj-C runtime invokes these via the IMP
|
||||
/// pointers wired up in M1.2 A.4 — no sx-side call path triggers
|
||||
/// lazy lowering, so we walk the cache and force-lower here.
|
||||
/// `lowerFunction` sets `current_foreign_class` automatically based
|
||||
/// `lowerFunction` sets `current_runtime_class` automatically based
|
||||
/// on the qualified name, so `*Self` substitutions in the body
|
||||
/// resolve correctly (M1.2 A.2b). After the bodies are lowered,
|
||||
/// `emitObjcDefinedClassImps` wraps each with a C-ABI trampoline
|
||||
@@ -65,16 +65,16 @@ pub fn lookupObjcPropertyOnPointer(self: *Lowering, obj_expr: *const ast.Node, f
|
||||
const pointee_info = self.module.types.get(ptr_info.pointer.pointee);
|
||||
if (pointee_info != .@"struct") return null;
|
||||
const struct_name = self.module.types.getString(pointee_info.@"struct".name);
|
||||
const fcd = self.program_index.foreign_class_map.get(struct_name) orelse return null;
|
||||
const fcd = self.program_index.runtime_class_map.get(struct_name) orelse return null;
|
||||
if (fcd.runtime != .objc_class and fcd.runtime != .objc_protocol) return null;
|
||||
return self.findForeignPropertyInChain(fcd, field_name);
|
||||
return self.findRuntimePropertyInChain(fcd, field_name);
|
||||
}
|
||||
|
||||
/// Walk the `#extends` chain looking for a method by name. M2.3.
|
||||
/// Returns the owning fcd + the method decl, or null if no ancestor
|
||||
/// declares it. Depth-capped at 16 to break accidental cycles
|
||||
/// (real Obj-C class chains rarely exceed 6 levels).
|
||||
pub fn findForeignMethodInChain(self: *Lowering, fcd: *const ast.RuntimeClassDecl, method_name: []const u8) ?struct { fcd: *const ast.RuntimeClassDecl, method: ast.RuntimeMethodDecl } {
|
||||
pub fn findRuntimeMethodInChain(self: *Lowering, fcd: *const ast.RuntimeClassDecl, method_name: []const u8) ?struct { fcd: *const ast.RuntimeClassDecl, method: ast.RuntimeMethodDecl } {
|
||||
var current: *const ast.RuntimeClassDecl = fcd;
|
||||
var depth: u32 = 0;
|
||||
while (depth < 16) : (depth += 1) {
|
||||
@@ -90,14 +90,14 @@ pub fn findForeignMethodInChain(self: *Lowering, fcd: *const ast.RuntimeClassDec
|
||||
};
|
||||
break :blk null;
|
||||
} orelse return null;
|
||||
current = self.program_index.foreign_class_map.get(parent) orelse return null;
|
||||
current = self.program_index.runtime_class_map.get(parent) orelse return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Walk the `#extends` chain looking for a `#property` field by
|
||||
/// name. M2.3 companion to findForeignMethodInChain.
|
||||
pub fn findForeignPropertyInChain(self: *Lowering, fcd: *const ast.RuntimeClassDecl, field_name: []const u8) ?ast.RuntimeFieldDecl {
|
||||
/// name. M2.3 companion to findRuntimeMethodInChain.
|
||||
pub fn findRuntimePropertyInChain(self: *Lowering, fcd: *const ast.RuntimeClassDecl, field_name: []const u8) ?ast.RuntimeFieldDecl {
|
||||
var current: *const ast.RuntimeClassDecl = fcd;
|
||||
var depth: u32 = 0;
|
||||
while (depth < 16) : (depth += 1) {
|
||||
@@ -112,7 +112,7 @@ pub fn findForeignPropertyInChain(self: *Lowering, fcd: *const ast.RuntimeClassD
|
||||
};
|
||||
break :blk null;
|
||||
} orelse return null;
|
||||
current = self.program_index.foreign_class_map.get(parent) orelse return null;
|
||||
current = self.program_index.runtime_class_map.get(parent) orelse return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -136,10 +136,10 @@ pub fn lookupObjcDefinedStateFieldOnPointer(self: *Lowering, obj_expr: *const as
|
||||
const pointee_info = self.module.types.get(ptr_info.pointer.pointee);
|
||||
if (pointee_info != .@"struct") return null;
|
||||
const struct_name = self.module.types.getString(pointee_info.@"struct".name);
|
||||
const fcd = self.program_index.foreign_class_map.get(struct_name) orelse return null;
|
||||
const fcd = self.program_index.runtime_class_map.get(struct_name) orelse return null;
|
||||
// Only sx-defined Obj-C classes have a state struct. Foreign
|
||||
// classes' fields are purely declaration metadata (no state).
|
||||
if (fcd.is_foreign or fcd.runtime != .objc_class) return null;
|
||||
if (fcd.is_reference or fcd.runtime != .objc_class) return null;
|
||||
// Skip property fields — those dispatch via the M2.2 getter/setter
|
||||
// path. Plain instance fields take the ivar+gep path.
|
||||
for (fcd.members) |m| switch (m) {
|
||||
@@ -694,11 +694,11 @@ pub fn emitObjcDefinedClassImp(self: *Lowering, fcd: *const ast.RuntimeClassDecl
|
||||
params.append(self.alloc, .{ .name = self.module.types.internString("obj"), .ty = ptr_void }) catch return;
|
||||
params.append(self.alloc, .{ .name = self.module.types.internString("_cmd"), .ty = ptr_void }) catch return;
|
||||
|
||||
// Set current_foreign_class so *Self in user-param resolution
|
||||
// Set current_runtime_class so *Self in user-param resolution
|
||||
// resolves to *<Cls>State (M1.2 A.2b). Save+restore.
|
||||
const saved_fc = self.current_foreign_class;
|
||||
self.current_foreign_class = fcd;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
const saved_fc = self.current_runtime_class;
|
||||
self.current_runtime_class = fcd;
|
||||
defer self.current_runtime_class = saved_fc;
|
||||
|
||||
const param_start: usize = 1;
|
||||
for (md.params[param_start..], 0..) |p_node, i| {
|
||||
@@ -971,12 +971,12 @@ pub fn emitObjcDefinedClassStaticImp(self: *Lowering, fcd: *const ast.RuntimeCla
|
||||
params.append(self.alloc, .{ .name = self.module.types.internString("cls"), .ty = ptr_void }) catch return;
|
||||
params.append(self.alloc, .{ .name = self.module.types.internString("_cmd"), .ty = ptr_void }) catch return;
|
||||
|
||||
// current_foreign_class lets `*Self` (if it appears in
|
||||
// current_runtime_class lets `*Self` (if it appears in
|
||||
// user-arg types — rare for class methods) resolve to the
|
||||
// state-struct type. Save+restore.
|
||||
const saved_fc = self.current_foreign_class;
|
||||
self.current_foreign_class = fcd;
|
||||
defer self.current_foreign_class = saved_fc;
|
||||
const saved_fc = self.current_runtime_class;
|
||||
self.current_runtime_class = fcd;
|
||||
defer self.current_runtime_class = saved_fc;
|
||||
|
||||
for (md.params, 0..) |p_node, i| {
|
||||
const pty = self.resolveType(p_node);
|
||||
|
||||
@@ -79,7 +79,7 @@ pub const Module = struct {
|
||||
/// Pre-resolved Obj-C runtime name of the parent class, so
|
||||
/// emit_llvm can pass it to `objc_getClass(parent)` /
|
||||
/// `objc_allocateClassPair(super, ...)` without walking the
|
||||
/// sx-side foreign_class_map (which lives in lower.zig).
|
||||
/// sx-side runtime_class_map (which lives in lower.zig).
|
||||
/// Defaults to "NSObject" when no `#extends` member is present.
|
||||
parent_objc_name: []const u8 = "NSObject",
|
||||
};
|
||||
|
||||
@@ -67,17 +67,17 @@ test "ProgramIndex declaration maps round-trip (A1.1b)" {
|
||||
try idx.module_const_map.put("AF_INET", .{ .value = &blk, .ty = .i32 });
|
||||
try std.testing.expect(idx.module_const_map.get("AF_INET").?.value == &blk);
|
||||
|
||||
// foreign_class_map: sx alias → RuntimeClassDecl.
|
||||
// runtime_class_map: sx alias → RuntimeClassDecl.
|
||||
const fcd = ast.RuntimeClassDecl{
|
||||
.name = "NSString",
|
||||
.foreign_path = "NSString",
|
||||
.runtime = .objc_class,
|
||||
.members = &.{},
|
||||
.is_foreign = true,
|
||||
.is_reference = true,
|
||||
.is_main = false,
|
||||
};
|
||||
try idx.foreign_class_map.put("NSString", &fcd);
|
||||
try std.testing.expect(idx.foreign_class_map.get("NSString").? == &fcd);
|
||||
try idx.runtime_class_map.put("NSString", &fcd);
|
||||
try std.testing.expect(idx.runtime_class_map.get("NSString").? == &fcd);
|
||||
|
||||
// protocol_decl_map: protocol name → ProtocolDeclInfo.
|
||||
try idx.protocol_decl_map.put("Show", .{ .name = "Show", .is_inline = false, .methods = &.{} });
|
||||
|
||||
@@ -665,7 +665,7 @@ pub const ProgramIndex = struct {
|
||||
/// resolve. Keyed/allocated with the lowering allocator.
|
||||
qualified_fn_source: std.StringHashMap([]const u8),
|
||||
/// sx alias → RuntimeClassDecl (jni_class / objc_class / swift_class / ... — registered in scan pass).
|
||||
foreign_class_map: std.StringHashMap(*const ast.RuntimeClassDecl),
|
||||
runtime_class_map: std.StringHashMap(*const ast.RuntimeClassDecl),
|
||||
/// `#run` global name → GlobalId.
|
||||
global_names: std.StringHashMap(GlobalInfo),
|
||||
/// Type alias name → target TypeId. The single-source alias table; passed
|
||||
@@ -708,7 +708,7 @@ pub const ProgramIndex = struct {
|
||||
.fn_ast_map = std.StringHashMap(*const ast.FnDecl).init(alloc),
|
||||
.qualified_fn_source = std.StringHashMap([]const u8).init(alloc),
|
||||
.global_names = std.StringHashMap(GlobalInfo).init(alloc),
|
||||
.foreign_class_map = std.StringHashMap(*const ast.RuntimeClassDecl).init(alloc),
|
||||
.runtime_class_map = std.StringHashMap(*const ast.RuntimeClassDecl).init(alloc),
|
||||
.type_alias_map = std.StringHashMap(TypeId).init(alloc),
|
||||
.struct_template_map = std.StringHashMap(StructTemplate).init(alloc),
|
||||
.struct_template_by_decl = std.AutoHashMap(imports.DeclId, StructTemplate).init(alloc),
|
||||
@@ -728,7 +728,7 @@ pub const ProgramIndex = struct {
|
||||
self.import_flags.deinit();
|
||||
self.fn_ast_map.deinit();
|
||||
self.qualified_fn_source.deinit();
|
||||
self.foreign_class_map.deinit();
|
||||
self.runtime_class_map.deinit();
|
||||
self.global_names.deinit();
|
||||
self.type_alias_map.deinit();
|
||||
self.struct_template_map.deinit();
|
||||
|
||||
@@ -121,7 +121,7 @@ pub const Domain = enum {
|
||||
generic_struct_head,
|
||||
type_fn_head,
|
||||
protocol_head,
|
||||
foreign_class,
|
||||
runtime_class,
|
||||
struct_const,
|
||||
namespace_member,
|
||||
ufcs,
|
||||
@@ -135,7 +135,7 @@ pub fn eligibleKind(domain: Domain, raw: RawDeclRef, field: ?[]const u8) bool {
|
||||
return switch (domain) {
|
||||
.bare_type => switch (raw) {
|
||||
.struct_decl, .enum_decl, .union_decl, .error_set_decl,
|
||||
.protocol_decl, .foreign_class_decl => true,
|
||||
.protocol_decl, .runtime_class_decl => true,
|
||||
else => false,
|
||||
},
|
||||
.value_const => raw == .const_decl,
|
||||
@@ -143,7 +143,7 @@ pub fn eligibleKind(domain: Domain, raw: RawDeclRef, field: ?[]const u8) bool {
|
||||
.generic_struct_head => if (structDeclOf(raw)) |sd| sd.type_params.len > 0 else false,
|
||||
.type_fn_head => if (fnDeclOf(raw)) |fd| fd.type_params.len > 0 else false,
|
||||
.protocol_head => raw == .protocol_decl,
|
||||
.foreign_class => raw == .foreign_class_decl,
|
||||
.runtime_class => raw == .runtime_class_decl,
|
||||
.struct_const => structHasConstMember(raw, field orelse return false),
|
||||
.namespace_member => true,
|
||||
.ufcs => fnDeclOf(raw) != null,
|
||||
@@ -229,9 +229,9 @@ pub fn classifyHeadKind(raw: RawDeclRef, gs: *bool, tf: *bool, pr: *bool) void {
|
||||
|
||||
/// True when the bare-type verdict selected a foreign-class author
|
||||
/// unambiguously. Used by lowering to route to the foreign-class path.
|
||||
pub fn foreignClassWinsType(set: AuthorSet, verdict: Verdict) bool {
|
||||
pub fn runtimeClassWinsType(set: AuthorSet, verdict: Verdict) bool {
|
||||
return switch (verdict) {
|
||||
.own_wins => if (set.own) |a| std.meta.activeTag(a.raw) == .foreign_class_decl else false,
|
||||
.own_wins => if (set.own) |a| std.meta.activeTag(a.raw) == .runtime_class_decl else false,
|
||||
.single => blk: {
|
||||
var selected: ?RawAuthor = null;
|
||||
for (set.flat) |a| {
|
||||
@@ -240,7 +240,7 @@ pub fn foreignClassWinsType(set: AuthorSet, verdict: Verdict) bool {
|
||||
selected = a;
|
||||
}
|
||||
const a = selected orelse break :blk false;
|
||||
break :blk std.meta.activeTag(a.raw) == .foreign_class_decl;
|
||||
break :blk std.meta.activeTag(a.raw) == .runtime_class_decl;
|
||||
},
|
||||
.ambiguous, .not_visible, .domain_filtered => false,
|
||||
};
|
||||
|
||||
@@ -211,7 +211,7 @@ pub const UnknownTypeChecker = struct {
|
||||
}
|
||||
}
|
||||
},
|
||||
.foreign_class_decl => |fcd| {
|
||||
.runtime_class_decl => |fcd| {
|
||||
// The sx-side alias (left of `::`) is a user-chosen name, so a
|
||||
// reserved spelling is rejected like any other type decl (0089).
|
||||
self.checkDeclName(node, fcd.name, fcd.is_raw);
|
||||
@@ -394,7 +394,7 @@ pub const UnknownTypeChecker = struct {
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
var it_fc = self.index.foreign_class_map.keyIterator();
|
||||
var it_fc = self.index.runtime_class_map.keyIterator();
|
||||
while (it_fc.next()) |k| out.put(k.*, {}) catch {};
|
||||
var it_tmpl = self.index.struct_template_map.keyIterator();
|
||||
while (it_tmpl.next()) |k| out.put(k.*, {}) catch {};
|
||||
|
||||
@@ -258,15 +258,15 @@ pub const Parser = struct {
|
||||
// `#foreign` flips that to "reference an existing class on the foreign side."
|
||||
// `#jni_main` flags the class as the launchable entry (Android Activity).
|
||||
const prefix_loc = self.current.loc;
|
||||
if (self.tryParseForeignClassPrefix()) |prefix| {
|
||||
if (self.tryParseRuntimeClassPrefix()) |prefix| {
|
||||
// Phase 8 cutover: the prefix `#foreign` on a runtime-class directive is
|
||||
// removed — reference an existing class via the postfix `extern` modifier
|
||||
// (`X :: #objc_class("…") extern { … }`) instead. `prefix_loc` pins the
|
||||
// diagnostic to the `#foreign` token (already consumed by the lookahead).
|
||||
if (prefix.is_foreign) {
|
||||
if (prefix.is_reference) {
|
||||
return self.failAt(prefix_loc, "`#foreign` has been removed; use the postfix `extern` (import) / `export` (define) linkage keyword instead");
|
||||
}
|
||||
return self.parseForeignClassDecl(name, start_pos, prefix.runtime, prefix.is_foreign, prefix.is_main, name_is_raw);
|
||||
return self.parseRuntimeClassDecl(name, start_pos, prefix.runtime, prefix.is_reference, prefix.is_main, name_is_raw);
|
||||
}
|
||||
|
||||
// C-style union declaration
|
||||
@@ -1280,7 +1280,7 @@ pub const Parser = struct {
|
||||
} });
|
||||
}
|
||||
|
||||
fn foreignRuntimeForCurrent(self: *Parser) ?ast.RuntimeKind {
|
||||
fn runtimeKindForCurrent(self: *Parser) ?ast.RuntimeKind {
|
||||
return switch (self.current.tag) {
|
||||
.hash_jni_class => .jni_class,
|
||||
.hash_jni_interface => .jni_interface,
|
||||
@@ -1295,7 +1295,7 @@ pub const Parser = struct {
|
||||
|
||||
const RuntimeClassPrefix = struct {
|
||||
runtime: ast.RuntimeKind,
|
||||
is_foreign: bool,
|
||||
is_reference: bool,
|
||||
is_main: bool,
|
||||
};
|
||||
|
||||
@@ -1305,16 +1305,16 @@ pub const Parser = struct {
|
||||
/// directive (possibly after modifiers). Consumes the modifier tokens
|
||||
/// only when a runtime directive follows; otherwise leaves the parser
|
||||
/// state untouched.
|
||||
fn tryParseForeignClassPrefix(self: *Parser) ?RuntimeClassPrefix {
|
||||
fn tryParseRuntimeClassPrefix(self: *Parser) ?RuntimeClassPrefix {
|
||||
// Peek ahead through modifier tokens to confirm a directive follows.
|
||||
var lookahead_idx: usize = 0;
|
||||
var is_foreign = false;
|
||||
var is_reference = false;
|
||||
var is_main = false;
|
||||
while (true) {
|
||||
const tag = self.peekTag(lookahead_idx);
|
||||
switch (tag) {
|
||||
.hash_foreign => {
|
||||
is_foreign = true;
|
||||
is_reference = true;
|
||||
lookahead_idx += 1;
|
||||
},
|
||||
.hash_jni_main => {
|
||||
@@ -1324,11 +1324,11 @@ pub const Parser = struct {
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
const runtime = self.foreignRuntimeForOffset(lookahead_idx) orelse return null;
|
||||
const runtime = self.runtimeKindForOffset(lookahead_idx) orelse return null;
|
||||
// Commit: consume modifier tokens.
|
||||
var i: usize = 0;
|
||||
while (i < lookahead_idx) : (i += 1) self.advance();
|
||||
return .{ .runtime = runtime, .is_foreign = is_foreign, .is_main = is_main };
|
||||
return .{ .runtime = runtime, .is_reference = is_reference, .is_main = is_main };
|
||||
}
|
||||
|
||||
fn peekTag(self: *Parser, offset: usize) Tag {
|
||||
@@ -1349,7 +1349,7 @@ pub const Parser = struct {
|
||||
return self.peekTag(1) == .identifier and self.peekTag(2) == .r_paren;
|
||||
}
|
||||
|
||||
fn foreignRuntimeForOffset(self: *Parser, offset: usize) ?ast.RuntimeKind {
|
||||
fn runtimeKindForOffset(self: *Parser, offset: usize) ?ast.RuntimeKind {
|
||||
const tag = self.peekTag(offset);
|
||||
return switch (tag) {
|
||||
.hash_jni_class => .jni_class,
|
||||
@@ -1363,7 +1363,7 @@ pub const Parser = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn parseForeignClassDecl(self: *Parser, name: []const u8, start_pos: u32, runtime: ast.RuntimeKind, is_foreign: bool, is_main: bool, name_is_raw: bool) anyerror!*Node {
|
||||
fn parseRuntimeClassDecl(self: *Parser, name: []const u8, start_pos: u32, runtime: ast.RuntimeKind, is_reference: bool, is_main: bool, name_is_raw: bool) anyerror!*Node {
|
||||
self.advance(); // skip directive token
|
||||
|
||||
try self.expect(.l_paren);
|
||||
@@ -1380,20 +1380,20 @@ pub const Parser = struct {
|
||||
// `#foreign` modifier (mirrors `struct #compiler` postfix placement).
|
||||
// `… extern { … }` ⇒ reference an existing runtime class (== `#foreign`).
|
||||
// `… export { … }` ⇒ define + register a new sx class (== no `#foreign`).
|
||||
// Maps straight onto the existing `is_foreign` decision so lowering is
|
||||
// Maps straight onto the existing `is_reference` decision so lowering is
|
||||
// unchanged. The legacy prefix `#foreign` form still works via the
|
||||
// `is_foreign` argument; interplay/diagnostics for combining them is Phase 4.
|
||||
var is_foreign_eff = is_foreign;
|
||||
// `is_reference` argument; interplay/diagnostics for combining them is Phase 4.
|
||||
var is_reference_eff = is_reference;
|
||||
if (self.current.tag == .kw_extern or self.current.tag == .kw_export) {
|
||||
// Prefix `#foreign` and the postfix `extern`/`export` keyword are two
|
||||
// spellings of the same linkage axis — combining them is contradictory
|
||||
// (`#foreign`=import vs `export`=define) or redundant (`#foreign … extern`).
|
||||
// Reject at the postfix keyword rather than let it silently override.
|
||||
if (is_foreign) {
|
||||
if (is_reference) {
|
||||
const kw = if (self.current.tag == .kw_export) "export" else "extern";
|
||||
return self.failFmt("conflicting linkage: prefix '#foreign' cannot be combined with postfix '{s}'; use either '#foreign' or postfix 'extern'/'export', not both", .{kw});
|
||||
}
|
||||
is_foreign_eff = self.current.tag == .kw_extern;
|
||||
is_reference_eff = self.current.tag == .kw_extern;
|
||||
self.advance();
|
||||
}
|
||||
|
||||
@@ -1619,12 +1619,12 @@ pub const Parser = struct {
|
||||
}
|
||||
try self.expect(.r_brace);
|
||||
|
||||
return try self.createNode(start_pos, .{ .foreign_class_decl = .{
|
||||
return try self.createNode(start_pos, .{ .runtime_class_decl = .{
|
||||
.name = name,
|
||||
.foreign_path = foreign_path,
|
||||
.runtime = runtime,
|
||||
.members = try members.toOwnedSlice(self.allocator),
|
||||
.is_foreign = is_foreign_eff,
|
||||
.is_reference = is_reference_eff,
|
||||
.is_main = is_main,
|
||||
.is_raw = name_is_raw,
|
||||
} });
|
||||
|
||||
@@ -1332,16 +1332,16 @@ pub const Analyzer = struct {
|
||||
}
|
||||
}
|
||||
},
|
||||
.foreign_class_decl => |fd| {
|
||||
.runtime_class_decl => |fd| {
|
||||
try self.addSymbol(fd.name, .type_alias, null, node.span);
|
||||
if (fd.is_foreign and fd.is_main) {
|
||||
if (fd.is_reference and fd.is_main) {
|
||||
try self.diagnostics.append(self.allocator, .{
|
||||
.level = .err,
|
||||
.message = "'#foreign' and '#jni_main' / '#objc_main' are mutually exclusive — a foreign-referenced class can't be the app's main entry",
|
||||
.span = node.span,
|
||||
});
|
||||
}
|
||||
if (fd.is_foreign) {
|
||||
if (fd.is_reference) {
|
||||
for (fd.members) |m| switch (m) {
|
||||
.method => |md| if (md.body != null) {
|
||||
try self.diagnostics.append(self.allocator, .{
|
||||
@@ -1784,7 +1784,7 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
.tuple_type_expr,
|
||||
.ufcs_alias,
|
||||
.closure_type_expr,
|
||||
.foreign_class_decl,
|
||||
.runtime_class_decl,
|
||||
=> {},
|
||||
.jni_env_block => |eb| {
|
||||
if (findNodeAtOffset(eb.env, offset)) |found| return found;
|
||||
|
||||
Reference in New Issue
Block a user