Skip to content

Commit ac6dd6f

Browse files
committed
jni: implement RegisterNatives
1 parent 1a457a7 commit ac6dd6f

File tree

3 files changed

+237
-3
lines changed

3 files changed

+237
-3
lines changed

jni/src/env/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod object;
99
mod references;
1010
mod reflection;
1111
mod register;
12+
pub use register::*;
1213
mod string;
1314
mod version;
1415
mod vm;

jni/src/env/register.rs

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,177 @@
1+
use crate::env::JniEnv;
2+
use crate::error::{JniError, Result};
3+
use crate::objects::{
4+
JArray, JBooleanArray, JByteArray, JCharArray, JClass, JDoubleArray, JFloatArray, JIntArray,
5+
JLongArray, JObject, JObjectArray, JShortArray, JString, JThrowable, JWeak,
6+
};
7+
use crate::string::JniString;
8+
9+
use std::ffi::c_void;
10+
11+
use jni_sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
12+
13+
pub struct JniNativeMethod {
14+
name: JniString,
15+
signature: JniString,
16+
fn_ptr: *mut c_void,
17+
}
18+
19+
impl JniNativeMethod {
20+
pub fn from<F, N, S>(name: N, signature: S, fn_ptr: F) -> Self
21+
where
22+
F: JniMethod,
23+
N: Into<JniString>,
24+
S: Into<JniString>,
25+
{
26+
Self {
27+
name: name.into(),
28+
signature: signature.into(),
29+
fn_ptr: fn_ptr.raw(),
30+
}
31+
}
32+
33+
fn raw(&self) -> jni_sys::JNINativeMethod {
34+
jni_sys::JNINativeMethod {
35+
name: self.name.as_cstr().as_ptr().cast_mut(),
36+
signature: self.signature.as_cstr().as_ptr().cast_mut(),
37+
fnPtr: self.fn_ptr.cast(),
38+
}
39+
}
40+
}
41+
42+
pub trait JniMethod {
43+
fn raw(self) -> *mut c_void;
44+
}
45+
46+
macro_rules! impl_jni_method {
47+
(
48+
$(($($params:ident),*)),* $(,)?
49+
) => {
50+
$(
51+
impl<R, $($params),*> JniMethod for fn(JniEnv, JObject, $($params),*) -> R
52+
where
53+
R: JniMethodReturnType,
54+
$($params: JniMethodParameter),*
55+
{
56+
fn raw(self) -> *mut c_void {
57+
self as _
58+
}
59+
}
60+
61+
impl<R, $($params),*> JniMethod for fn(JniEnv, JClass, $($params),*) -> R
62+
where
63+
R: JniMethodReturnType,
64+
$($params: JniMethodParameter),*
65+
{
66+
fn raw(self) -> *mut c_void {
67+
self as _
68+
}
69+
}
70+
)*
71+
}
72+
}
73+
74+
impl_jni_method!(
75+
(),
76+
(T),
77+
(T, U),
78+
(T, U, V),
79+
(T, U, V, W),
80+
(T, U, V, W, X),
81+
(T, U, V, W, X, Y),
82+
(T, U, V, W, X, Y, Z),
83+
(T, U, V, W, X, Y, Z, A),
84+
(T, U, V, W, X, Y, Z, A, B),
85+
(T, U, V, W, X, Y, Z, A, B, C),
86+
(T, U, V, W, X, Y, Z, A, B, C, D),
87+
(T, U, V, W, X, Y, Z, A, B, C, D, E),
88+
(T, U, V, W, X, Y, Z, A, B, C, D, E, F),
89+
(T, U, V, W, X, Y, Z, A, B, C, D, E, F, G),
90+
);
91+
92+
pub trait JniMethodParameter: sealed::Sealed {}
93+
macro_rules! params {
94+
($($ty:ident),* $(,)*) => {
95+
$(
96+
impl JniMethodParameter for $ty {}
97+
impl sealed::Sealed for $ty {}
98+
)*
99+
}
100+
}
101+
102+
params!(
103+
jbyte,
104+
jchar,
105+
jdouble,
106+
jfloat,
107+
jint,
108+
jlong,
109+
jshort,
110+
jboolean,
111+
JObject,
112+
JClass,
113+
JThrowable,
114+
JString,
115+
JWeak,
116+
JArray,
117+
JBooleanArray,
118+
JByteArray,
119+
JCharArray,
120+
JShortArray,
121+
JIntArray,
122+
JLongArray,
123+
JFloatArray,
124+
JDoubleArray,
125+
JObjectArray,
126+
);
127+
128+
pub trait JniMethodReturnType: sealed::Sealed {}
129+
130+
impl<T> JniMethodReturnType for T where T: JniMethodParameter {}
131+
132+
impl JniMethodReturnType for () {}
133+
134+
mod sealed {
135+
pub(super) trait Sealed {}
136+
impl Sealed for () {}
137+
}
138+
1139
impl super::JniEnv {
2-
// TODO: RegisterNatives
140+
/// Registers native methods with the class specified by the `clazz` argument.
141+
///
142+
/// ## PARAMETERS
143+
///
144+
/// `clazz`: a Java class object.
145+
/// `methods`: the native methods in the class.
146+
///
147+
/// ## THROWS
148+
///
149+
/// `NoSuchMethodError`: if a specified method cannot be found or if the method is not native.
150+
pub fn register_natives(&self, clazz: JClass, methods: &[JniNativeMethod]) -> Result<()> {
151+
assert!(!self.raw().is_null());
152+
153+
let methods = methods.iter().map(JniNativeMethod::raw).collect::<Vec<_>>();
154+
155+
let ret;
156+
unsafe {
157+
let invoke_interface = self.as_native_interface();
158+
ret = ((*invoke_interface).RegisterNatives)(
159+
self.0.cast::<jni_sys::JNIEnv>(),
160+
clazz.raw(),
161+
methods.as_ptr(),
162+
methods.len() as jint,
163+
);
164+
}
165+
166+
if self.exception_check() {
167+
return Err(JniError::ExceptionThrown);
168+
}
169+
170+
if ret != 0 {
171+
return Err(JniError::Unknown);
172+
}
173+
174+
Ok(())
175+
}
3176
// TODO: UnregisterNatives
4177
}

runtime/src/native/jni/register.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
use jni::sys::{JNIEnv, JNINativeMethod, jclass, jint};
1+
use crate::native::jni::reference_from_jobject;
2+
use crate::native::method::NativeMethodPtr;
3+
use crate::objects::method::MethodEntryPoint;
4+
use crate::symbols::Symbol;
5+
use crate::thread::JavaThread;
6+
use crate::thread::exceptions::{Throws, throw_with_ret};
7+
8+
use std::ffi::CStr;
9+
10+
use ::jni::sys::{JNI_ERR, JNI_OK, JNIEnv, JNINativeMethod, jclass, jint};
11+
use common::unicode;
212

313
#[unsafe(no_mangle)]
414
pub extern "system" fn RegisterNatives(
@@ -7,7 +17,57 @@ pub extern "system" fn RegisterNatives(
717
methods: *const JNINativeMethod,
818
nMethods: jint,
919
) -> jint {
10-
unimplemented!("jni::RegisterNatives")
20+
let thread = JavaThread::current();
21+
assert_eq!(thread.env().raw(), env);
22+
23+
let Some(mirror) = (unsafe { reference_from_jobject(clazz) }) else {
24+
panic!("Invalid arguments to `GetSuperclass`");
25+
};
26+
27+
let class = mirror.extract_target_class();
28+
if class.is_array() || class.is_interface() {
29+
return JNI_ERR;
30+
}
31+
32+
// SAFETY: It's up to the caller to provide a valid array
33+
let methods = unsafe { std::slice::from_raw_parts(methods, nMethods as usize) };
34+
35+
for raw_method in methods {
36+
// SAFETY: It's up to the caller to provide valid strings
37+
let name_c = unsafe { CStr::from_ptr(raw_method.name) };
38+
let signature_c = unsafe { CStr::from_ptr(raw_method.signature) };
39+
40+
let (Ok(name), Ok(signature)) = (
41+
unicode::decode(name_c.to_bytes()),
42+
unicode::decode(signature_c.to_bytes()),
43+
) else {
44+
return JNI_ERR;
45+
};
46+
47+
match class.resolve_method(Symbol::intern(name), Symbol::intern(signature)) {
48+
Throws::Ok(method) => {
49+
if !method.is_native() {
50+
throw_with_ret!(
51+
0,
52+
thread,
53+
NoSuchMethodError,
54+
"Method '{}' is not declared as native",
55+
method.external_name()
56+
);
57+
}
58+
59+
method.set_entry_point(MethodEntryPoint::NativeMethod(NativeMethodPtr::External(
60+
raw_method.fnPtr,
61+
)));
62+
},
63+
Throws::Exception(e) => {
64+
e.throw(thread);
65+
return 0;
66+
},
67+
}
68+
}
69+
70+
JNI_OK
1171
}
1272

1373
#[unsafe(no_mangle)]

0 commit comments

Comments
 (0)