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

@@ -352,3 +352,50 @@ test "deriveMethod with slice param" {
defer a.free(out);
try std.testing.expectEqualStrings("([B)I", out);
}
// ── A6.2: native-name mangling + return-type dispatchability ─────────
const types = @import("types.zig");
test "jniMangleNativeName mangles package path + method (/ -> _, _ -> _1)" {
const alloc = std.testing.allocator;
// Plain path + method: `/` separators collapse to `_`, `Java_` prefix,
// `_sx_1` infix before the (mangled) method name.
const m1 = try desc.jniMangleNativeName(alloc, "com/sx/App", "tick");
defer alloc.free(m1);
try std.testing.expectEqualStrings("Java_com_sx_App_sx_1tick", m1);
// Underscores in BOTH the path and the method escape to `_1` (so the JNI
// resolver can round-trip them), distinct from the `/`->`_` separator.
const m2 = try desc.jniMangleNativeName(alloc, "a_b/C", "do_it");
defer alloc.free(m2);
try std.testing.expectEqualStrings("Java_a_1b_C_sx_1do_1it", m2);
}
test "isJniReturnTypeSupported accepts the dispatchable set + pointers only" {
const alloc = std.testing.allocator;
var table = types.TypeTable.init(alloc);
defer table.deinit();
const t = &table;
// The Call<T>Method-dispatchable primitives.
inline for (.{ types.TypeId.void, types.TypeId.bool, types.TypeId.s32, types.TypeId.s64, types.TypeId.f32, types.TypeId.f64 }) |ty| {
try std.testing.expect(desc.isJniReturnTypeSupported(t, ty));
}
// Other primitive widths are NOT dispatchable (would hit emit_llvm's
// undef-producing `else` arm — the footgun this predicate guards).
inline for (.{ types.TypeId.s8, types.TypeId.s16, types.TypeId.u8, types.TypeId.u32, types.TypeId.u64 }) |ty| {
try std.testing.expect(!desc.isJniReturnTypeSupported(t, ty));
}
// Pointer / many-pointer returns route through CallObjectMethod → true.
try std.testing.expect(desc.isJniReturnTypeSupported(t, table.ptrTo(.void)));
try std.testing.expect(desc.isJniReturnTypeSupported(t, table.manyPtrTo(.u8)));
// A pass-by-value struct return is unsupported.
const sname = table.internString("CGRectish");
const sty = table.intern(.{ .@"struct" = .{ .name = sname, .fields = &.{} } });
try std.testing.expect(!desc.isJniReturnTypeSupported(t, sty));
}