From 134c197dd4e6dac1fcee81405c5584b3ca4e9dbe Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 19 May 2026 21:25:42 +0300 Subject: [PATCH] =?UTF-8?q?ffi=201.15:=20xfail=20=E2=80=94=20Android=20cro?= =?UTF-8?q?ss-compile=20of=20#jni=5Fcall(void)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `examples/ffi-jni-call-02-void.sx` exercising `#jni_call(void) (env, target, "name", "sig")` inside an `inline if OS == .android` arm, plus a new tuple in `tests/cross_compile.sh`. Host run_examples passes (the inline-if strips the JNI body, leaving "skipped"); the Android cross-compile FAILs because `lowerFfiIntrinsicCall` still emits the placeholder diagnostic for any `fic.kind != .objc_call`. Per the FFI cadence rule this is a test-add (xfail); the next commit makes the Android cross-compile green by adding the `.jni_msg_send` opcode and its emit_llvm expansion. --- examples/ffi-jni-call-02-void.sx | 49 ++++++++++++++++++++++++ tests/cross_compile.sh | 1 + tests/expected/ffi-jni-call-02-void.exit | 1 + tests/expected/ffi-jni-call-02-void.txt | 1 + 4 files changed, 52 insertions(+) create mode 100644 examples/ffi-jni-call-02-void.sx create mode 100644 tests/expected/ffi-jni-call-02-void.exit create mode 100644 tests/expected/ffi-jni-call-02-void.txt diff --git a/examples/ffi-jni-call-02-void.sx b/examples/ffi-jni-call-02-void.sx new file mode 100644 index 0000000..73bb5d4 --- /dev/null +++ b/examples/ffi-jni-call-02-void.sx @@ -0,0 +1,49 @@ +// Phase 1 step 1.15 (PLAN-FFI.md): `#jni_call(void)` codegen. +// +// `#jni_call(T)(env, target, "method_name", "(Sig)RetSig", args...)` +// dispatches a JNI instance-method call. The lowering hand-emits the +// vtable indirection: +// +// ifs = *env // JNINativeInterface* +// cls = ifs->GetObjectClass(env, target) +// mid = ifs->GetMethodID(env, cls, name, sig) +// ifs->CallMethod(env, target, mid, args...) +// +// Phase 1.17 introduces method-ID caching via static slots populated +// at module-init; this step keeps the per-call-site lookup. +// +// Host can't dlopen libjvm via the JIT, so the JNI body is gated +// behind `inline if OS == .android`. The macOS test path strips the +// body and prints "skipped"; the cross_compile.sh Android target +// verifies that the gated body actually compiles + links against +// libjvm in the Android sysroot. + +#import "modules/std.sx"; +#import "modules/compiler.sx"; + +main :: () -> s32 { + inline if OS == .android { + // Real Android entry passes env + target via android_main / + // ANativeActivity (modules/platform/android.sx). For the cross- + // compile-only test we just need the lowering to emit valid + // IR; runtime correctness is exercised by the chess + // sx_android_query_safe_insets path once Phase 1D for + // sx_android_jni.c lands. + env : *void = null; + target : *void = null; + #jni_call(void)(env, target, "noop", "()V"); + } + inline if OS != .android { + print("skipped (not android)\n"); + } + 0; +} + +// Android target requires `android_main` as the NDK entry — kept as +// a 3-line trampoline so this example can pass through +// `--target android` builds in `tests/cross_compile.sh`. +android_main :: (app: *void) { + inline if OS == .android { + main(); + } +} diff --git a/tests/cross_compile.sh b/tests/cross_compile.sh index 559a07f..fee1f59 100755 --- a/tests/cross_compile.sh +++ b/tests/cross_compile.sh @@ -27,6 +27,7 @@ mkdir -p "$TMP_DIR" # host to actually run it. TUPLES=( "android|examples/ffi-objc-call-10-os-gate.sx" + "android|examples/ffi-jni-call-02-void.sx" ) PASS=0 diff --git a/tests/expected/ffi-jni-call-02-void.exit b/tests/expected/ffi-jni-call-02-void.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/ffi-jni-call-02-void.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/ffi-jni-call-02-void.txt b/tests/expected/ffi-jni-call-02-void.txt new file mode 100644 index 0000000..d63e354 --- /dev/null +++ b/tests/expected/ffi-jni-call-02-void.txt @@ -0,0 +1 @@ +skipped (not android)