feat(jni): implement synthetic JavaVM and JNIEnv dispatch tables
This commit is contained in:
parent
0e980a3dc0
commit
3923f7aa28
6 changed files with 270 additions and 0 deletions
102
crates/molotov-core/src/jni/env.rs
Normal file
102
crates/molotov-core/src/jni/env.rs
Normal 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
|
||||
}
|
||||
}
|
||||
4
crates/molotov-core/src/jni/mod.rs
Normal file
4
crates/molotov-core/src/jni/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub mod env;
|
||||
pub mod types;
|
||||
pub mod vm;
|
||||
86
crates/molotov-core/src/jni/types.rs
Normal file
86
crates/molotov-core/src/jni/types.rs
Normal 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)>,
|
||||
}
|
||||
59
crates/molotov-core/src/jni/vm.rs
Normal file
59
crates/molotov-core/src/jni/vm.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
pub mod audio;
|
||||
pub mod dynamic;
|
||||
pub mod elf;
|
||||
pub mod jni;
|
||||
pub mod linker;
|
||||
pub mod mmap;
|
||||
pub mod reloc;
|
||||
|
|
|
|||
18
crates/molotov-core/tests/jni_tables_test.rs
Normal file
18
crates/molotov-core/tests/jni_tables_test.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in a new issue