feat(jni): implement synthetic JavaVM and JNIEnv dispatch tables

This commit is contained in:
katR 2026-09-29 22:11:13 +05:30
parent 0e980a3dc0
commit 3923f7aa28
6 changed files with 270 additions and 0 deletions

View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#![allow(dead_code)]
use super::types::{JNIEnvPtr, JNINativeInterface, JNI_VERSION_1_6};
use std::ptr;
unsafe extern "C" fn jni_get_version(_env: JNIEnvPtr) -> i32 {
JNI_VERSION_1_6
}
unsafe extern "C" fn jni_exception_describe(_env: JNIEnvPtr) {
tracing::debug!("JNI: ExceptionDescribe called");
}
unsafe extern "C" fn jni_exception_clear(_env: JNIEnvPtr) {
tracing::debug!("JNI: ExceptionClear called");
}
pub struct JNIEnvWrapper {
table: Box<JNINativeInterface>,
table_ptr: *const JNINativeInterface,
}
impl Default for JNIEnvWrapper {
fn default() -> Self {
Self::new()
}
}
impl JNIEnvWrapper {
pub fn new() -> Self {
let table = Box::new(JNINativeInterface {
reserved0: ptr::null_mut(),
reserved1: ptr::null_mut(),
reserved2: ptr::null_mut(),
reserved3: ptr::null_mut(),
GetVersion: Some(jni_get_version),
DefineClass: ptr::null_mut(),
FindClass: None,
FromReflectedMethod: ptr::null_mut(),
FromReflectedField: ptr::null_mut(),
ToReflectedMethod: ptr::null_mut(),
GetSuperclass: ptr::null_mut(),
IsAssignableFrom: ptr::null_mut(),
ToReflectedField: ptr::null_mut(),
Throw: ptr::null_mut(),
ThrowNew: ptr::null_mut(),
ExceptionOccurred: ptr::null_mut(),
ExceptionDescribe: Some(jni_exception_describe),
ExceptionClear: Some(jni_exception_clear),
FatalError: ptr::null_mut(),
PushLocalFrame: ptr::null_mut(),
PopLocalFrame: ptr::null_mut(),
NewGlobalRef: None,
DeleteGlobalRef: None,
DeleteLocalRef: None,
IsSameObject: ptr::null_mut(),
NewLocalRef: ptr::null_mut(),
EnsureLocalCapacity: ptr::null_mut(),
AllocObject: ptr::null_mut(),
NewObject: ptr::null_mut(),
NewObjectA: ptr::null_mut(),
GetObjectClass: ptr::null_mut(),
IsInstanceOf: ptr::null_mut(),
GetMethodID: None,
CallObjectMethod: ptr::null_mut(),
CallBooleanMethod: ptr::null_mut(),
CallByteMethod: ptr::null_mut(),
CallCharMethod: ptr::null_mut(),
CallShortMethod: ptr::null_mut(),
CallIntMethod: ptr::null_mut(),
CallLongMethod: ptr::null_mut(),
CallFloatMethod: ptr::null_mut(),
CallDoubleMethod: ptr::null_mut(),
CallVoidMethod: ptr::null_mut(),
GetStaticMethodID: None,
CallStaticObjectMethod: ptr::null_mut(),
CallStaticBooleanMethod: ptr::null_mut(),
CallStaticByteMethod: ptr::null_mut(),
CallStaticCharMethod: ptr::null_mut(),
CallStaticShortMethod: ptr::null_mut(),
CallStaticIntMethod: ptr::null_mut(),
CallStaticLongMethod: ptr::null_mut(),
CallStaticFloatMethod: ptr::null_mut(),
CallStaticDoubleMethod: ptr::null_mut(),
CallStaticVoidMethod: ptr::null_mut(),
GetFieldID: ptr::null_mut(),
GetStaticFieldID: ptr::null_mut(),
NewStringUTF: None,
GetStringUTFLength: None,
GetStringUTFChars: None,
ReleaseStringUTFChars: None,
});
let table_ptr = &*table as *const JNINativeInterface;
Self { table, table_ptr }
}
pub fn as_raw_ptr(&self) -> JNIEnvPtr {
&self.table_ptr as *const *const JNINativeInterface as JNIEnvPtr
}
}

View file

@ -0,0 +1,4 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod env;
pub mod types;
pub mod vm;

View file

@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#![allow(non_snake_case, dead_code)]
use std::ffi::c_void;
pub const JNI_OK: i32 = 0;
pub const JNI_ERR: i32 = -1;
pub const JNI_VERSION_1_6: i32 = 0x00010006;
pub type JNIEnvPtr = *mut *const JNINativeInterface;
pub type JavaVMPtr = *mut *const JNIInvokeInterface;
#[repr(C)]
pub struct JNIInvokeInterface {
pub reserved0: *mut c_void,
pub reserved1: *mut c_void,
pub reserved2: *mut c_void,
pub DestroyJavaVM: Option<unsafe extern "C" fn(vm: JavaVMPtr) -> i32>,
pub AttachCurrentThread: Option<unsafe extern "C" fn(vm: JavaVMPtr, penv: *mut *mut c_void, args: *mut c_void) -> i32>,
pub DetachCurrentThread: Option<unsafe extern "C" fn(vm: JavaVMPtr) -> i32>,
pub GetEnv: Option<unsafe extern "C" fn(vm: JavaVMPtr, penv: *mut *mut c_void, version: i32) -> i32>,
pub AttachCurrentThreadAsDaemon: Option<unsafe extern "C" fn(vm: JavaVMPtr, penv: *mut *mut c_void, args: *mut c_void) -> i32>,
}
#[repr(C)]
pub struct JNINativeInterface {
pub reserved0: *mut c_void,
pub reserved1: *mut c_void,
pub reserved2: *mut c_void,
pub reserved3: *mut c_void,
pub GetVersion: Option<unsafe extern "C" fn(env: JNIEnvPtr) -> i32>,
pub DefineClass: *mut c_void,
pub FindClass: Option<unsafe extern "C" fn(env: JNIEnvPtr, name: *const libc::c_char) -> *mut c_void>,
pub FromReflectedMethod: *mut c_void,
pub FromReflectedField: *mut c_void,
pub ToReflectedMethod: *mut c_void,
pub GetSuperclass: *mut c_void,
pub IsAssignableFrom: *mut c_void,
pub ToReflectedField: *mut c_void,
pub Throw: *mut c_void,
pub ThrowNew: *mut c_void,
pub ExceptionOccurred: *mut c_void,
pub ExceptionDescribe: Option<unsafe extern "C" fn(env: JNIEnvPtr)>,
pub ExceptionClear: Option<unsafe extern "C" fn(env: JNIEnvPtr)>,
pub FatalError: *mut c_void,
pub PushLocalFrame: *mut c_void,
pub PopLocalFrame: *mut c_void,
pub NewGlobalRef: Option<unsafe extern "C" fn(env: JNIEnvPtr, obj: *mut c_void) -> *mut c_void>,
pub DeleteGlobalRef: Option<unsafe extern "C" fn(env: JNIEnvPtr, obj: *mut c_void)>,
pub DeleteLocalRef: Option<unsafe extern "C" fn(env: JNIEnvPtr, obj: *mut c_void)>,
pub IsSameObject: *mut c_void,
pub NewLocalRef: *mut c_void,
pub EnsureLocalCapacity: *mut c_void,
pub AllocObject: *mut c_void,
pub NewObject: *mut c_void,
pub NewObjectA: *mut c_void,
pub GetObjectClass: *mut c_void,
pub IsInstanceOf: *mut c_void,
pub GetMethodID: Option<unsafe extern "C" fn(env: JNIEnvPtr, clazz: *mut c_void, name: *const libc::c_char, sig: *const libc::c_char) -> *mut c_void>,
pub CallObjectMethod: *mut c_void,
pub CallBooleanMethod: *mut c_void,
pub CallByteMethod: *mut c_void,
pub CallCharMethod: *mut c_void,
pub CallShortMethod: *mut c_void,
pub CallIntMethod: *mut c_void,
pub CallLongMethod: *mut c_void,
pub CallFloatMethod: *mut c_void,
pub CallDoubleMethod: *mut c_void,
pub CallVoidMethod: *mut c_void,
pub GetStaticMethodID: Option<unsafe extern "C" fn(env: JNIEnvPtr, clazz: *mut c_void, name: *const libc::c_char, sig: *const libc::c_char) -> *mut c_void>,
pub CallStaticObjectMethod: *mut c_void,
pub CallStaticBooleanMethod: *mut c_void,
pub CallStaticByteMethod: *mut c_void,
pub CallStaticCharMethod: *mut c_void,
pub CallStaticShortMethod: *mut c_void,
pub CallStaticIntMethod: *mut c_void,
pub CallStaticLongMethod: *mut c_void,
pub CallStaticFloatMethod: *mut c_void,
pub CallStaticDoubleMethod: *mut c_void,
pub CallStaticVoidMethod: *mut c_void,
pub GetFieldID: *mut c_void,
pub GetStaticFieldID: *mut c_void,
pub NewStringUTF: Option<unsafe extern "C" fn(env: JNIEnvPtr, bytes: *const libc::c_char) -> *mut c_void>,
pub GetStringUTFLength: Option<unsafe extern "C" fn(env: JNIEnvPtr, string: *mut c_void) -> i32>,
pub GetStringUTFChars: Option<unsafe extern "C" fn(env: JNIEnvPtr, string: *mut c_void, isCopy: *mut u8) -> *const libc::c_char>,
pub ReleaseStringUTFChars: Option<unsafe extern "C" fn(env: JNIEnvPtr, string: *mut c_void, utf: *const libc::c_char)>,
}

View file

@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#![allow(dead_code)]
use super::env::JNIEnvWrapper;
use super::types::{JNIInvokeInterface, JavaVMPtr, JNI_OK, JNI_VERSION_1_6};
use std::ffi::c_void;
use std::ptr;
unsafe extern "C" fn jni_get_env(_vm: JavaVMPtr, penv: *mut *mut c_void, _version: i32) -> i32 {
if penv.is_null() {
return super::types::JNI_ERR;
}
// Return standard JNIEnv
let env = Box::new(JNIEnvWrapper::new());
*penv = env.as_raw_ptr() as *mut c_void;
std::mem::forget(env);
JNI_OK
}
unsafe extern "C" fn jni_attach_current_thread(_vm: JavaVMPtr, penv: *mut *mut c_void, _args: *mut c_void) -> i32 {
jni_get_env(_vm, penv, JNI_VERSION_1_6)
}
unsafe extern "C" fn jni_detach_current_thread(_vm: JavaVMPtr) -> i32 {
JNI_OK
}
pub struct JavaVMWrapper {
table: Box<JNIInvokeInterface>,
table_ptr: *const JNIInvokeInterface,
}
impl Default for JavaVMWrapper {
fn default() -> Self {
Self::new()
}
}
impl JavaVMWrapper {
pub fn new() -> Self {
let table = Box::new(JNIInvokeInterface {
reserved0: ptr::null_mut(),
reserved1: ptr::null_mut(),
reserved2: ptr::null_mut(),
DestroyJavaVM: None,
AttachCurrentThread: Some(jni_attach_current_thread),
DetachCurrentThread: Some(jni_detach_current_thread),
GetEnv: Some(jni_get_env),
AttachCurrentThreadAsDaemon: Some(jni_attach_current_thread),
});
let table_ptr = &*table as *const JNIInvokeInterface;
Self { table, table_ptr }
}
pub fn as_raw_ptr(&self) -> JavaVMPtr {
&self.table_ptr as *const *const JNIInvokeInterface as JavaVMPtr
}
}

View file

@ -2,6 +2,7 @@
pub mod audio; pub mod audio;
pub mod dynamic; pub mod dynamic;
pub mod elf; pub mod elf;
pub mod jni;
pub mod linker; pub mod linker;
pub mod mmap; pub mod mmap;
pub mod reloc; pub mod reloc;

View file

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use molotov_core::jni::env::JNIEnvWrapper;
use molotov_core::jni::types::JNI_VERSION_1_6;
use molotov_core::jni::vm::JavaVMWrapper;
#[test]
fn test_synthetic_javavm_and_jnienv_version() {
let _vm = JavaVMWrapper::new();
let env = JNIEnvWrapper::new();
unsafe {
let env_ptr = env.as_raw_ptr();
let get_version = (**env_ptr).GetVersion;
assert!(get_version.is_some());
let ver = get_version.unwrap()(env_ptr);
assert_eq!(ver, JNI_VERSION_1_6);
}
}