155 lines
5.0 KiB
Markdown
155 lines
5.0 KiB
Markdown
# JNI Implementation
|
|
|
|
**Location**: `crates/core/src/native/jni.rs`
|
|
|
|
## JNIEnv Structure
|
|
|
|
The JNIEnv is created as a function table (`JNINativeInterface_` from the `jni` crate) with 250+ function pointers:
|
|
|
|
```rust
|
|
pub fn create_jni_function_table(thread: *const VmThread) -> JNIEnv {
|
|
Box::into_raw(Box::new(JNINativeInterface_ {
|
|
reserved0: thread as *mut _, // Stores pointer to VmThread for context
|
|
reserved1: std::ptr::null_mut(),
|
|
reserved2: std::ptr::null_mut(),
|
|
reserved3: std::ptr::null_mut(),
|
|
GetVersion: Some(jni_get_version),
|
|
FindClass: Some(find_class),
|
|
RegisterNatives: Some(register_natives),
|
|
GetMethodID: Some(get_method_id),
|
|
// ... 240+ more function pointers
|
|
}))
|
|
}
|
|
```
|
|
|
|
**Key Feature:** The `reserved0` field stores a pointer to the `VmThread`, allowing each JNI function to access thread
|
|
context via `get_thread(env)`.
|
|
|
|
## VmThread's JNIEnv Storage
|
|
|
|
**Location**: `crates/core/src/thread.rs`
|
|
|
|
Each thread owns its JNIEnv:
|
|
|
|
```rust
|
|
pub struct VmThread {
|
|
pub id: ThreadId,
|
|
pub vm: Arc<Vm>,
|
|
pub loader: Arc<Mutex<ClassLoader>>,
|
|
pub jni_env: JNIEnv, // Stored per-thread
|
|
// ... other fields
|
|
}
|
|
|
|
// Created during VmThread initialization:
|
|
let jni_env = create_jni_function_table(weak_self.as_ptr() as * mut VmThread);
|
|
```
|
|
|
|
## Native Method Invocation
|
|
|
|
**Location**: `crates/core/src/thread.rs` (lines 340-428)
|
|
|
|
The flow is:
|
|
|
|
1. **Method Detection:** When a method has `ACC_NATIVE` flag, `invoke_native()` is called
|
|
2. **Symbol Resolution:** Generates JNI symbol name (e.g., `Java_java_lang_String_intern`)
|
|
3. **Lookup:** Searches registered native methods or loaded native libraries via `find_native_method()`
|
|
4. **FFI Call:** Uses `libffi` to call the native function with constructed arguments
|
|
|
|
```rust
|
|
pub fn invoke_native(&self, method: &MethodRef, args: Vec<Value>) -> MethodCallResult {
|
|
let symbol_name = generate_jni_method_name(method, false);
|
|
|
|
// Find the function pointer
|
|
let p = self.vm.find_native_method(&symbol_name)
|
|
.ok_or(VmError::NativeError(...))?;
|
|
|
|
// Build Call Interface (CIF) for FFI
|
|
let cp = CodePtr::from_ptr(p);
|
|
let built_args = build_args(args, &mut storage, &self.jni_env as *mut JNIEnv);
|
|
let cif = method.build_cif();
|
|
|
|
// Invoke with type-specific call
|
|
match &method.desc.return_type {
|
|
None => {
|
|
cif.call::<()>(cp, built_args.as_ref());
|
|
Ok(None)
|
|
}
|
|
Some(FieldType::Base(BaseType::Int)) => {
|
|
let v = cif.call::<jint>(cp, built_args.as_ref());
|
|
Ok(Some(v.into()))
|
|
}
|
|
// ... handle other return types
|
|
}
|
|
}
|
|
```
|
|
|
|
## Argument Marshalling
|
|
|
|
**Location**: `crates/core/src/thread.rs` (lines 509-548)
|
|
|
|
Native functions receive:
|
|
|
|
1. `JNIEnv*` - pointer to the function table
|
|
2. `jclass` or `jobject` (receiver) - always an ID (u32)
|
|
3. Parameters - converted from VM `Value` types to JNI types
|
|
|
|
```rust
|
|
fn build_args(mut params: VecDeque<Value>, storage: &mut Vec<Box<dyn Any>>,
|
|
jnienv: *mut JNIEnv) -> Vec<Arg> {
|
|
storage.push(Box::new(jnienv)); // Slot 0: JNIEnv*
|
|
let receiver_id = params.pop_front().map(...);
|
|
storage.push(Box::new(receiver_id as jobject)); // Slot 1: this/class
|
|
|
|
for value in params {
|
|
match value {
|
|
Value::Primitive(Primitive::Int(x)) => storage.push(Box::new(x)),
|
|
Value::Reference(Some(ref_kind)) => {
|
|
storage.push(Box::new(ref_kind.id() as jobject)) // References as IDs
|
|
}
|
|
// ... other types
|
|
}
|
|
}
|
|
storage.iter().map(|boxed| arg(&**boxed)).collect()
|
|
}
|
|
```
|
|
|
|
## Native Method Registration
|
|
|
|
**Location**: `crates/core/src/native/jni.rs` (lines 381-442)
|
|
|
|
Java code calls `RegisterNatives()` JNI function, which stores pointers:
|
|
|
|
```rust
|
|
unsafe extern "system" fn register_natives(
|
|
env: *mut JNIEnv,
|
|
clazz: jclass,
|
|
methods: *const JNINativeMethod,
|
|
n_methods: jint,
|
|
) -> jint {
|
|
let thread = &*get_thread(env);
|
|
|
|
for i in 0..n_methods as usize {
|
|
let native_method = &*methods.add(i);
|
|
let full_name = generate_jni_short_name(&class_name, name);
|
|
|
|
thread.vm.native_methods.insert(full_name, native_method.fnPtr);
|
|
}
|
|
JNI_OK
|
|
}
|
|
```
|
|
|
|
## Implemented JNI Functions
|
|
|
|
| Category | Functions |
|
|
|-------------------|--------------------------------------------------------------|
|
|
| Version | `GetVersion` |
|
|
| Class Operations | `FindClass`, `GetSuperclass`, `IsAssignableFrom` |
|
|
| Exceptions | `Throw`, `ThrowNew`, `ExceptionOccurred`, `ExceptionClear` |
|
|
| References | `NewGlobalRef`, `DeleteGlobalRef`, `NewLocalRef` |
|
|
| Object Operations | `AllocObject`, `NewObject`, `GetObjectClass`, `IsInstanceOf` |
|
|
| Field Access | `GetFieldID`, `Get/Set<Type>Field`, `GetStaticFieldID` |
|
|
| Method Invocation | `GetMethodID`, `Call<Type>Method`, `CallStatic<Type>Method` |
|
|
| String Operations | `NewString`, `GetStringLength`, `GetStringChars` |
|
|
| Array Operations | `NewArray`, `GetArrayLength`, `Get/Set<Type>ArrayRegion` |
|
|
| Registration | `RegisterNatives`, `UnregisterNatives` |
|
|
| Monitors | `MonitorEnter`, `MonitorExit` | |