refactor(ir): move pure JNI helpers into jni_descriptor.zig (A6.2 step 2)

Relocate the two pure JNI decision helpers out of lower.zig into
jni_descriptor.zig (already the JNI helper module), alongside the descriptor
derivation. Behavior-preserving move — no facade, since neither takes *Lowering.

- jniMangleNativeName(allocator, foreign_path, method_name) and
  isJniReturnTypeSupported(table, ret_ty) moved verbatim as pub free fns; added a
  types import + TypeId alias to jni_descriptor.zig.
- Rerouted lower.zig's 2 call sites (synthesizeJniMainStub; the JNI return-type
  guard at lower.zig:6000) through jni_descriptor.* — lower.zig already imported
  the module.
- Moved the 2 unit tests lower.test.zig -> jni_descriptor.test.zig (re-pointed to
  desc.*; a standalone TypeTable.init replaces the Module setup). Dropped the
  now-unused lower_mod alias.
- Stayed in lower.zig per PLAN A6.2 step 5/6: jniMapParamType (trivial resolveType
  wrapper), synthesizeJniMainStub(s), lowerJniCall, lowerJniConstructor,
  lowerSuperCall, getJniEnvTlFids. Java rendering stays in jni_java_emit.zig.
  Phase A6 complete.

Gate: zig build, zig build test, bash tests/run_examples.sh -> 361/0
(9 JNI .ir snapshots + 26 14xx examples green, no churn).
This commit is contained in:
agra
2026-06-03 08:28:41 +03:00
parent 0a4a240e31
commit 20c767e336
4 changed files with 96 additions and 96 deletions

View File

@@ -5997,7 +5997,7 @@ pub const Lowering = struct {
// would silently lower to LLVMGetUndef and produce wrong arguments at
// the call site (chess Android touch shipped broken because s32→s32+
// f32 returns hit the undef path before .f32 was wired up).
if (!isJniReturnTypeSupported(&self.module.types, ret_ty)) {
if (!jni_descriptor.isJniReturnTypeSupported(&self.module.types, ret_ty)) {
if (self.diagnostics) |d| {
d.addFmt(.err, span, "JNI method '{s}.{s}' returns '{s}', which isn't supported by the JNI call-method lowering yet — only void/bool/s32/s64/f32/f64 and pointers are wired up", .{ fcd.name, method.name, self.module.types.typeName(ret_ty) });
}
@@ -16445,7 +16445,7 @@ pub const Lowering = struct {
}
fn synthesizeJniMainStub(self: *Lowering, fcd: *const ast.ForeignClassDecl, md: ast.ForeignMethodDecl) void {
const mangled = jniMangleNativeName(self.alloc, fcd.foreign_path, md.name) catch return;
const mangled = jni_descriptor.jniMangleNativeName(self.alloc, fcd.foreign_path, md.name) catch return;
const name_id = self.module.types.internString(mangled);
const ptr_void = self.module.types.ptrTo(.void);
@@ -16568,48 +16568,3 @@ pub const Lowering = struct {
fn jniMapParamType(self: *Lowering, type_node: *ast.Node) TypeId {
return self.resolveType(type_node);
}
/// Whether emit_llvm's `jni_msg_send` lowering can dispatch a Call<T>Method
/// for this return type. Anything outside this set falls into the `else`
/// arm of the switches in `emit_llvm.zig` and would silently produce
/// `LLVMGetUndef` — a footgun that previously shipped (chess Android touch
/// went undef because `MotionEvent.getX() -> f32` wasn't in the switch).
/// Pointer-typed returns route through `CallObjectMethod`.
pub fn isJniReturnTypeSupported(table: *const @import("types.zig").TypeTable, ret_ty: TypeId) bool {
return switch (ret_ty) {
.void, .bool, .s32, .s64, .f32, .f64 => true,
else => blk: {
if (ret_ty.isBuiltin()) break :blk false;
const info = table.get(ret_ty);
break :blk info == .pointer or info == .many_pointer;
},
};
}
/// Encode a (foreign_path, method_name) pair as the JNI-resolved symbol
/// `Java_<pkg-mangled>_<Class>_sx_1<method-mangled>`. JNI mangling:
/// `/` → `_`, `_` → `_1`. The `sx_` prefix matches the Java-side
/// `private native sx_<name>(...)` delegate.
pub fn jniMangleNativeName(allocator: std.mem.Allocator, foreign_path: []const u8, method_name: []const u8) ![]u8 {
var buf = std.ArrayList(u8).empty;
try buf.appendSlice(allocator, "Java_");
for (foreign_path) |ch| {
if (ch == '/') {
try buf.append(allocator, '_');
} else if (ch == '_') {
try buf.appendSlice(allocator, "_1");
} else {
try buf.append(allocator, ch);
}
}
try buf.append(allocator, '_');
try buf.appendSlice(allocator, "sx_1");
for (method_name) |ch| {
if (ch == '_') {
try buf.appendSlice(allocator, "_1");
} else {
try buf.append(allocator, ch);
}
}
return buf.toOwnedSlice(allocator);
}