ffi 1.26: hand-roll JavaVM dispatch in sx for env attach
Adds the JavaVM-side vtable indirection to `library/modules/platform/ android.sx` so the sx caller of `sx_query_safe_insets_jni` (1.25) can obtain a `JNIEnv*` without the C wrapper. `#jni_call` only dispatches through `JNIEnv*`'s vtable (a different table from `JavaVM*`'s), so the JavaVM hop is hand-rolled here. New decls: - `JNI_VERSION_1_6` (0x00010006) and the `ANATIVEACTIVITY_*` byte offsets (8, 24 on 64-bit Android — vm, clazz respectively). - `sx_load_ptr_at(base, offset)` — load a `*void` field at a raw byte offset. Used for both ANativeActivity fields and the JavaVM vtable load. - `sx_load_javavm_fn(vm, slot)` — load function pointer at the given vtable slot. `vm` is `JavaVM*` which points to `JNIInvokeInterface*`; the indirection is `*vm + slot * 8`. - `sx_android_get_env(activity, out_attached)` — calls `GetEnv` (slot 6); on `JNI_EDETACHED` falls through to `AttachCurrentThread` (slot 4), sets `out_attached = true` so caller can balance with `sx_android_detach_env` (slot 5). - `sx_android_activity_clazz(activity)` — reads the jobject at byte offset 24. Chess Android + iOS-sim builds still clean; cross-compile 3/3 green; host 118/119. The new functions dead-strip until step 1.27 wires them into the safe-insets call site in `android.sx::AndroidPlatform.safe_insets`.
This commit is contained in:
@@ -55,6 +55,82 @@ AMotionEvent_getY :: (event: *void, pointer_index: u64) -> f32 #foreig
|
||||
sx_android_query_safe_insets :: (activity: *void, top: *s32, left: *s32, bottom: *s32, right: *s32) -> void #foreign;
|
||||
sx_android_install_input_handler :: (app: *void, handler: (*void, *void) -> s32) -> void #foreign;
|
||||
|
||||
// JavaVM vtable indirection — used to attach the calling thread to
|
||||
// the JVM and recover a `JNIEnv*` for it. `#jni_call` only handles
|
||||
// `JNIEnv*` dispatch (a different vtable), so the JavaVM hop is
|
||||
// hand-rolled here.
|
||||
//
|
||||
// Slot indices match `JNIInvokeInterface_` in `<jni.h>`:
|
||||
// 3 DestroyJavaVM, 4 AttachCurrentThread, 5 DetachCurrentThread,
|
||||
// 6 GetEnv, 7 AttachCurrentThreadAsDaemon.
|
||||
JNI_VERSION_1_6 :: 0x00010006;
|
||||
|
||||
// Byte offsets into `ANativeActivity` on 64-bit Android (the only
|
||||
// Android ABI we target). `vm` is the JavaVM*, `clazz` is the
|
||||
// activity's jobject. The C struct layout in
|
||||
// `<android/native_activity.h>` is the source of truth; these
|
||||
// offsets MUST track that.
|
||||
ANATIVEACTIVITY_VM_OFFSET :: 8;
|
||||
ANATIVEACTIVITY_CLAZZ_OFFSET :: 24;
|
||||
|
||||
// Load a `*void` field at `base + byte_offset`. Plumbing for raw
|
||||
// struct access from foreign pointers.
|
||||
sx_load_ptr_at :: (base: *void, byte_offset: usize) -> *void {
|
||||
addr : usize = xx base;
|
||||
slot : **void = xx (addr + byte_offset);
|
||||
slot.*;
|
||||
}
|
||||
|
||||
// Load a JavaVM function pointer at the given vtable slot index.
|
||||
// `vm` is the `JavaVM*` (which points to `JNIInvokeInterface*`), so
|
||||
// the indirection is `*vm + slot * sizeof(ptr)`.
|
||||
sx_load_javavm_fn :: (vm: *void, slot: usize) -> *void {
|
||||
vtable_pp : **void = xx vm;
|
||||
vtable : *void = vtable_pp.*;
|
||||
addr : usize = xx vtable;
|
||||
fn_slot : **void = xx (addr + slot * 8);
|
||||
fn_slot.*;
|
||||
}
|
||||
|
||||
// Attach the current thread to the JVM if needed, hand back a
|
||||
// `JNIEnv*`. `out_attached` is set to true when this call had to do
|
||||
// the attach (caller should match with `sx_android_detach_env`).
|
||||
// Returns null on failure (no VM, or attach refused).
|
||||
sx_android_get_env :: (activity: *void, out_attached: *bool) -> *void {
|
||||
inline if OS != .android { return null; }
|
||||
out_attached.* = false;
|
||||
if activity == null { return null; }
|
||||
vm := sx_load_ptr_at(activity, ANATIVEACTIVITY_VM_OFFSET);
|
||||
if vm == null { return null; }
|
||||
|
||||
env : *void = null;
|
||||
get_env_fn : (*void, **void, s32) -> s32 = xx sx_load_javavm_fn(vm, 6);
|
||||
if get_env_fn(vm, @env, xx JNI_VERSION_1_6) == 0 { return env; }
|
||||
|
||||
attach_fn : (*void, **void, *void) -> s32 = xx sx_load_javavm_fn(vm, 4);
|
||||
if attach_fn(vm, @env, null) != 0 { return null; }
|
||||
out_attached.* = true;
|
||||
env;
|
||||
}
|
||||
|
||||
sx_android_detach_env :: (activity: *void) {
|
||||
inline if OS != .android { return; }
|
||||
if activity == null { return; }
|
||||
vm := sx_load_ptr_at(activity, ANATIVEACTIVITY_VM_OFFSET);
|
||||
if vm == null { return; }
|
||||
detach_fn : (*void) -> s32 = xx sx_load_javavm_fn(vm, 5);
|
||||
detach_fn(vm);
|
||||
}
|
||||
|
||||
// Read the activity's `clazz` jobject (the Java-side activity
|
||||
// reference). Wrapper around the raw byte-offset load so the call
|
||||
// site reads naturally.
|
||||
sx_android_activity_clazz :: (activity: *void) -> *void {
|
||||
inline if OS != .android { return null; }
|
||||
if activity == null { return null; }
|
||||
sx_load_ptr_at(activity, ANATIVEACTIVITY_CLAZZ_OFFSET);
|
||||
}
|
||||
|
||||
// sx-side reimplementation of the JNI dispatch chain inside
|
||||
// `sx_android_query_safe_insets`. Caller provides an already-attached
|
||||
// `JNIEnv*` and the activity's `clazz` jobject. Outputs physical-pixel
|
||||
|
||||
Reference in New Issue
Block a user