jvm-rs/docs/jni.md

5.0 KiB

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:

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:

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

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