jvm-rs/docs/jni.md

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