Skip to content

Commit 1989162

Browse files
committed
Use newtypes for cached method/field IDs
1 parent 48f6a00 commit 1989162

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

java-spaghetti-gen/src/emit_rust/fields.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ impl<'a> Field<'a> {
159159
)?;
160160
writeln!(
161161
out,
162-
"{indent} static __FIELD: ::std::sync::OnceLock<usize> = ::std::sync::OnceLock::new();"
162+
"{indent} static __FIELD: ::std::sync::OnceLock<::java_spaghetti::JFieldID> \
163+
= ::std::sync::OnceLock::new();"
163164
)?;
164165
writeln!(out, "{indent} unsafe {{")?;
165166
if !self.java.is_static() {
@@ -172,9 +173,9 @@ impl<'a> Field<'a> {
172173
writeln!(
173174
out,
174175
"{indent} \
175-
let __jni_field = *__FIELD.get_or_init(|| \
176-
__jni_env.require_{}field(__jni_class, {}, {}).addr()\
177-
) as ::java_spaghetti::sys::jfieldID;",
176+
let __jni_field = __FIELD.get_or_init(|| \
177+
::java_spaghetti::JFieldID::from_raw(__jni_env.require_{}field(__jni_class, {}, {}))\
178+
).as_raw();",
178179
if self.java.is_static() { "static_" } else { "" },
179180
StrEmitter(self.java.name()),
180181
StrEmitter(FieldSigWriter(self.java.descriptor()))
@@ -213,7 +214,8 @@ impl<'a> Field<'a> {
213214
)?;
214215
writeln!(
215216
out,
216-
"{indent} static __FIELD: ::std::sync::OnceLock<usize> = ::std::sync::OnceLock::new();"
217+
"{indent} static __FIELD: ::std::sync::OnceLock<::java_spaghetti::JFieldID> \
218+
= ::std::sync::OnceLock::new();"
217219
)?;
218220
writeln!(out, "{indent} unsafe {{")?;
219221
if !self.java.is_static() {
@@ -226,9 +228,9 @@ impl<'a> Field<'a> {
226228
writeln!(
227229
out,
228230
"{indent} \
229-
let __jni_field = *__FIELD.get_or_init(|| \
230-
__jni_env.require_{}field(__jni_class, {}, {}).addr()\
231-
) as ::java_spaghetti::sys::jfieldID;",
231+
let __jni_field = __FIELD.get_or_init(|| \
232+
::java_spaghetti::JFieldID::from_raw(__jni_env.require_{}field(__jni_class, {}, {}))\
233+
).as_raw();",
232234
if self.java.is_static() { "static_" } else { "" },
233235
StrEmitter(self.java.name()),
234236
StrEmitter(FieldSigWriter(self.java.descriptor()))

java-spaghetti-gen/src/emit_rust/methods.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ impl<'a> Method<'a> {
209209

210210
writeln!(
211211
out,
212-
"{indent} static __METHOD: ::std::sync::OnceLock<usize> = ::std::sync::OnceLock::new();"
212+
"{indent} static __METHOD: ::std::sync::OnceLock<::java_spaghetti::JMethodID> \
213+
= ::std::sync::OnceLock::new();"
213214
)?;
214215
writeln!(out, "{indent} unsafe {{")?;
215216
writeln!(out, "{indent} let __jni_args = [{params_array}];")?;
@@ -223,9 +224,9 @@ impl<'a> Method<'a> {
223224
writeln!(
224225
out,
225226
"{indent} \
226-
let __jni_method = *__METHOD.get_or_init(|| \
227-
__jni_env.require_{}method(__jni_class, {}, {}).addr()\
228-
) as ::java_spaghetti::sys::jmethodID;",
227+
let __jni_method = __METHOD.get_or_init(|| \
228+
::java_spaghetti::JMethodID::from_raw(__jni_env.require_{}method(__jni_class, {}, {}))\
229+
).as_raw();",
229230
if self.java.is_static() { "static_" } else { "" },
230231
StrEmitter(self.java.name()),
231232
StrEmitter(MethodSigWriter(self.java.descriptor()))

java-spaghetti/src/id_cache.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! New types for `jfieldID` and `jmethodID` that implement `Send` and `Sync`.
2+
//!
3+
//! Inspired by: <https://docs.rs/jni/0.21.1/jni/objects/struct.JMethodID.html>.
4+
//!
5+
//! According to the JNI spec field IDs may be invalidated when the corresponding class is unloaded:
6+
//! <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#accessing_fields_and_methods>
7+
//!
8+
//! You should generally not be interacting with these types directly, but it must be public for codegen.
9+
10+
use crate::sys::{jfieldID, jmethodID};
11+
12+
#[doc(hidden)]
13+
#[repr(transparent)]
14+
pub struct JFieldID {
15+
internal: jfieldID,
16+
}
17+
18+
// Field IDs are valid across threads (not tied to a JNIEnv)
19+
unsafe impl Send for JFieldID {}
20+
unsafe impl Sync for JFieldID {}
21+
22+
impl JFieldID {
23+
/// Creates a [`JFieldID`] that wraps the given `raw` [`jfieldID`].
24+
///
25+
/// # Safety
26+
///
27+
/// Expects a valid, non-`null` ID.
28+
pub unsafe fn from_raw(raw: jfieldID) -> Self {
29+
debug_assert!(!raw.is_null(), "from_raw fieldID argument");
30+
Self { internal: raw }
31+
}
32+
33+
pub fn as_raw(&self) -> jfieldID {
34+
self.internal
35+
}
36+
}
37+
38+
#[doc(hidden)]
39+
#[repr(transparent)]
40+
pub struct JMethodID {
41+
internal: jmethodID,
42+
}
43+
44+
// Method IDs are valid across threads (not tied to a JNIEnv)
45+
unsafe impl Send for JMethodID {}
46+
unsafe impl Sync for JMethodID {}
47+
48+
impl JMethodID {
49+
/// Creates a [`JMethodID`] that wraps the given `raw` [`jmethodID`].
50+
///
51+
/// # Safety
52+
///
53+
/// Expects a valid, non-`null` ID.
54+
pub unsafe fn from_raw(raw: jmethodID) -> Self {
55+
debug_assert!(!raw.is_null(), "from_raw methodID argument");
56+
Self { internal: raw }
57+
}
58+
59+
pub fn as_raw(&self) -> jmethodID {
60+
self.internal
61+
}
62+
}

java-spaghetti/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ mod refs {
2929
mod array;
3030
mod as_jvalue;
3131
mod env;
32+
mod id_cache;
3233
mod jni_type;
3334
mod string_chars;
3435
mod vm;
3536

3637
pub use array::*;
3738
pub use as_jvalue::*;
3839
pub use env::*;
40+
pub use id_cache::*;
3941
pub use jni_type::JniType;
4042
pub use refs::*;
4143
pub use string_chars::*;

0 commit comments

Comments
 (0)