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 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;
|
||||||
|
|
|
||||||
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