ffi: lift JNI vtable offsets into a named-constants struct

The numeric slot indices (21, 31, 33, 49, 61) in the `#jni_call`
lowering are JNI-spec constants from `<jni.h>` but appeared as bare
magic numbers — only the trailing comment told you which JNI
function you were loading. Moving them into a private `const Jni`
namespace at file scope makes the call sites self-documenting:

  loadJniFn(ifs, Jni.GetObjectClass, "jni.GetObjectClass")
  loadJniFn(ifs, Jni.NewGlobalRef,   "jni.NewGlobalRef")
  loadJniFn(ifs, Jni.GetMethodID,    "jni.GetMethodID")
  switch (ret_ty_id) {
      .void => Jni.CallVoidMethod,
      .s32  => Jni.CallIntMethod,
      ...
  }

Also pre-loaded the remaining Call<Type>Method slots (Object,
Boolean, Long, Float, Double) so steps 1.19–1.22 just add the
corresponding switch arm — no new magic-number lookups in the diff.

Behavior-preserving refactor: IR snapshots unchanged, all 113 host
tests still pass, both cross-compile tuples still green.
This commit is contained in:
agra
2026-05-19 22:28:51 +03:00
parent ebcfe4c4dc
commit c1877fc00e

View File

@@ -29,6 +29,27 @@ fn isIdentByte(b: u8) bool {
return (b >= 'a' and b <= 'z') or (b >= 'A' and b <= 'Z') or (b >= '0' and b <= '9') or b == '_';
}
/// JNI vtable slot offsets — indices into the `JNINativeInterface`
/// function-pointer array reachable via `*env`. Stable per the JNI
/// spec across versions; locked to the documented order in
/// `<jni.h>`. Slot numbers here MUST match the order of fields in
/// the C `JNINativeInterface_` struct.
const Jni = struct {
const NewGlobalRef: u32 = 21;
const GetObjectClass: u32 = 31;
const GetMethodID: u32 = 33;
// Call<Type>Method (instance, varargs variant). Each numeric type
// has its own slot — distinct ABI per return type, so the JNI
// runtime dispatches the right arg-shuffle for each.
const CallObjectMethod: u32 = 34;
const CallBooleanMethod: u32 = 37;
const CallIntMethod: u32 = 49;
const CallLongMethod: u32 = 52;
const CallFloatMethod: u32 = 55;
const CallDoubleMethod: u32 = 58;
const CallVoidMethod: u32 = 61;
};
// ── LLVMEmitter ─────────────────────────────────────────────────────────
// Emits LLVM IR from an IR Module. This is the Phase 3 replacement for
// the AST-based codegen.
@@ -1219,8 +1240,8 @@ pub const LLVMEmitter = struct {
}
const ret_ty_id = instruction.ty;
const call_method_offset: u32 = switch (ret_ty_id) {
.void => 61, // CallVoidMethod
.s32 => 49, // CallIntMethod
.void => Jni.CallVoidMethod,
.s32 => Jni.CallIntMethod,
else => {
self.mapRef(c.LLVMGetUndef(self.toLLVMType(instruction.ty)));
return;
@@ -1258,18 +1279,18 @@ pub const LLVMEmitter = struct {
// Miss path: GetObjectClass → NewGlobalRef → GetMethodID, then store both.
c.LLVMPositionBuilderAtEnd(self.builder, miss_bb);
const get_obj_cls = self.loadJniFn(ifs, 31, "jni.GetObjectClass");
const get_obj_cls = self.loadJniFn(ifs, Jni.GetObjectClass, "jni.GetObjectClass");
var gocls_params = [_]c.LLVMTypeRef{ self.cached_ptr, self.cached_ptr };
const gocls_ty = c.LLVMFunctionType(self.cached_ptr, &gocls_params, 2, 0);
var gocls_args = [_]c.LLVMValueRef{ env, target };
const local_cls = c.LLVMBuildCall2(self.builder, gocls_ty, get_obj_cls, &gocls_args, 2, "jni.cls");
const new_global_ref = self.loadJniFn(ifs, 21, "jni.NewGlobalRef");
const new_global_ref = self.loadJniFn(ifs, Jni.NewGlobalRef, "jni.NewGlobalRef");
var ngref_params = [_]c.LLVMTypeRef{ self.cached_ptr, self.cached_ptr };
const ngref_ty = c.LLVMFunctionType(self.cached_ptr, &ngref_params, 2, 0);
var ngref_args = [_]c.LLVMValueRef{ env, local_cls };
const global_cls = c.LLVMBuildCall2(self.builder, ngref_ty, new_global_ref, &ngref_args, 2, "jni.global.cls");
_ = c.LLVMBuildStore(self.builder, global_cls, pair.cls_slot);
const get_mid = self.loadJniFn(ifs, 33, "jni.GetMethodID");
const get_mid = self.loadJniFn(ifs, Jni.GetMethodID, "jni.GetMethodID");
var gmid_params = [_]c.LLVMTypeRef{ self.cached_ptr, self.cached_ptr, self.cached_ptr, self.cached_ptr };
const gmid_ty = c.LLVMFunctionType(self.cached_ptr, &gmid_params, 4, 0);
var gmid_args = [_]c.LLVMValueRef{ env, global_cls, name_ptr, sig_ptr };
@@ -1286,12 +1307,12 @@ pub const LLVMEmitter = struct {
c.LLVMAddIncoming(phi, &phi_vals, &phi_blocks, 2);
break :blk phi;
} else blk: {
const get_obj_cls = self.loadJniFn(ifs, 31, "jni.GetObjectClass");
const get_obj_cls = self.loadJniFn(ifs, Jni.GetObjectClass, "jni.GetObjectClass");
var gocls_params = [_]c.LLVMTypeRef{ self.cached_ptr, self.cached_ptr };
const gocls_ty = c.LLVMFunctionType(self.cached_ptr, &gocls_params, 2, 0);
var gocls_args = [_]c.LLVMValueRef{ env, target };
const cls = c.LLVMBuildCall2(self.builder, gocls_ty, get_obj_cls, &gocls_args, 2, "jni.cls");
const get_mid = self.loadJniFn(ifs, 33, "jni.GetMethodID");
const get_mid = self.loadJniFn(ifs, Jni.GetMethodID, "jni.GetMethodID");
var gmid_params = [_]c.LLVMTypeRef{ self.cached_ptr, self.cached_ptr, self.cached_ptr, self.cached_ptr };
const gmid_ty = c.LLVMFunctionType(self.cached_ptr, &gmid_params, 4, 0);
var gmid_args = [_]c.LLVMValueRef{ env, cls, name_ptr, sig_ptr };