typed_jni/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! # JNI Bindings for Rust with Typed References
4//!
5//! This crate provides Rust bindings for the Java Native Interface (JNI) with typed references.
6//!
7//! ## Features
8//!
9//! * `std` - Enables the use standard library. (default)
10//! * `cache` - Enables the use cache for class and member lookups. (default, requires `std`)
11//!
12//! ## Getting Started
13//!
14//! Assume you have Java classes `org.example.Example1` and `org.example.Example2` with following declarations:
15//!
16//! ```java
17//! package org.example;
18//!
19//! public class Example1 {
20//! private final String str;
21//!
22//! public Example1(String str) {
23//! this.str = str;
24//! }
25//!
26//! public void hello() {
27//! System.out.println(str);
28//!
29//! nativeHello(str);
30//! }
31//!
32//! private native void nativeHello(String str);
33//! private native String getStr();
34//! }
35//!
36//! public class Example2 {
37//! private static final Example1 staticExample1 = new Example1("Hello World!!!");
38//!
39//! private final Example1 example1;
40//!
41//! public Example2(Example1 example1) {
42//! this.example1 = example1;
43//! }
44//!
45//! public static void staticHello(int n) {
46//! for (i = 0; i < n; i++) {
47//! staticExample1.hello();
48//! }
49//! }
50//!
51//! public void hello(int n) {
52//! for (i = 0; i < n; i++) {
53//! example1.hello();
54//! }
55//! }
56//! }
57//! ```
58//!
59//! ### Define Java Classes in Rust
60//!
61//! Firstly, add necessary classes defines in your Rust code.
62//!
63//! ```rust
64//! use typed_jni::define_java_class;
65//!
66//! define_java_class!(JavaExample1, "org.example.Example1"); // `Java` prefix of name is not required
67//! define_java_class!(JavaExample2, "org.example.Example2");
68//! ```
69//!
70//! ### Access Java in Rust
71//!
72//! Now you can access Java classes in type-safe way.
73//!
74//! ```rust
75//! use std::string::String;
76//!
77//! use typed_jni::{
78//! LocalClass, LocalObject, TypedCallExt, TypedClassExt, TypedFieldAccessExt, TypedStringExt, builtin::JavaString, core::JNIEnv,
79//! define_java_class,
80//! };
81//!
82//! define_java_class!(JavaExample1, "org.example.Example1");
83//! define_java_class!(JavaExample2, "org.example.Example2");
84//!
85//! fn run_jni<'env>(env: &'env JNIEnv<'static>) {
86//! // create Example object
87//! let example1_cls: LocalClass<JavaExample1> = env.typed_find_class().unwrap();
88//! let example1_obj: LocalObject<JavaExample1> = env
89//! .typed_new_object(&example1_cls, (env.typed_new_string("Hello World!"),))
90//! .unwrap();
91//!
92//! // call hello method
93//! env.typed_call_method::<(), _, _>(&example1_obj, "hello", ()).unwrap();
94//!
95//! // get str field
96//! let str: LocalObject<JavaString> = env.typed_get_field(&example1_obj, "str").unwrap();
97//! let str: String = env.typed_get_string(&str);
98//!
99//! // create Example2 object
100//! let example2_cls: LocalClass<JavaExample2> = env.typed_find_class().unwrap();
101//! let example2_obj: LocalObject<JavaExample2> = env.typed_new_object(&example2_cls, (example1_obj,)).unwrap();
102//!
103//! // call staticHello method
104//! env.typed_call_method::<(), _, _>(&example2_cls, "staticHello", (3i32,))
105//! .unwrap();
106//!
107//! // call hello method
108//! env.typed_call_method::<(), _, _>(&example2_obj, "hello", (3i32,)).unwrap();
109//!
110//! // get staticExample1 field
111//! let static_example1: LocalObject<JavaExample1> = env.typed_get_field(&example2_cls, "staticExample1").unwrap();
112//!
113//! // get example1 field
114//! let example1: LocalObject<JavaExample1> = env.typed_get_field(&example2_obj, "example1").unwrap();
115//! }
116//! ```
117//!
118//! ### Access Rust in Java
119//!
120//! You should define native methods in Rust code with following signature.
121//!
122//! ```rust
123//! use typed_jni::{define_java_class, TrampolineObject, core::JNIEnv, builtin::JavaString, TypedStringExt};
124//!
125//! define_java_class!(JavaExample1, "org.example.Example1");
126//!
127//! #[unsafe(no_mangle)]
128//! pub extern "system" fn Java_org_example_Example1_nativeHello<'env>(env: &'env JNIEnv, obj: TrampolineObject<'env, JavaExample1>, str: TrampolineObject<'env, JavaString>) {
129//! let str: String = env.typed_get_string(&str);
130//!
131//! println!("{}", str);
132//! }
133//!
134//! #[unsafe(no_mangle)]
135//! pub extern "system" fn Java_org_example_Example1_getStr<'env>(env: &'env JNIEnv, obj: TrampolineObject<'env, JavaExample1>) -> TrampolineObject<'env, JavaString> {
136//! env.typed_new_string("native string").into_trampoline()
137//! }
138//! ```
139//!
140//! Then load it in Java code.
141//!
142//! ```java
143//! System.loadLibrary("example1");
144//! ```
145//!
146//! **NOTE**: All object reference in native function **parameters** and **return value** should be `TrampolineObject` or `TrampolineClass`, it is ffi safe.
147//!
148
149extern crate alloc;
150
151mod array;
152pub mod builtin;
153mod call;
154mod class;
155mod field;
156mod object;
157mod reference;
158mod resolver;
159mod string;
160mod throwable;
161
162use ::core::{
163 fmt::{Display, Formatter, Write},
164 marker::PhantomData,
165 ops::Deref,
166};
167pub use typed_jni_core as core;
168use typed_jni_core::{GlobalRef, LocalRef, Ref, TrampolineRef, WeakGlobalRef};
169
170pub use self::{array::*, call::*, class::*, field::*, object::*, reference::*, string::*, throwable::*};
171
172/// A signature of a JNI type.
173#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
174pub enum Signature {
175 Void,
176 Boolean,
177 Byte,
178 Char,
179 Short,
180 Int,
181 Long,
182 Float,
183 Double,
184 Object(&'static str),
185 Array(&'static Signature),
186}
187
188impl Signature {
189 pub const fn size_hint(&self) -> usize {
190 match self {
191 Signature::Void
192 | Signature::Boolean
193 | Signature::Byte
194 | Signature::Char
195 | Signature::Short
196 | Signature::Int
197 | Signature::Long
198 | Signature::Float
199 | Signature::Double => 1, // single char
200 Signature::Object(s) => s.len() + 2, // L<name>;
201 Signature::Array(inner) => inner.size_hint() + 1, // [<inner>
202 }
203 }
204
205 pub fn write_to<W: Write>(&self, w: &mut W) -> ::core::fmt::Result {
206 match self {
207 Signature::Void => w.write_str("V"),
208 Signature::Boolean => w.write_str("Z"),
209 Signature::Byte => w.write_str("B"),
210 Signature::Char => w.write_str("C"),
211 Signature::Short => w.write_str("S"),
212 Signature::Int => w.write_str("I"),
213 Signature::Long => w.write_str("J"),
214 Signature::Float => w.write_str("F"),
215 Signature::Double => w.write_str("D"),
216 Signature::Object(name) => {
217 w.write_str("L")?;
218 w.write_str(name)?;
219 w.write_str(";")
220 }
221 Signature::Array(inner) => {
222 w.write_str("[")?;
223 inner.write_to(w)
224 }
225 }
226 }
227
228 pub fn write_as_class_name_to<W: Write>(&self, w: &mut W) -> ::core::fmt::Result {
229 match self {
230 Signature::Object(name) => w.write_str(name),
231 Signature::Array(inner) => {
232 w.write_str("[")?;
233 inner.write_to(w)
234 }
235 _ => self.write_to(w),
236 }
237 }
238}
239
240impl Display for Signature {
241 fn fmt(&self, f: &mut Formatter<'_>) -> alloc::fmt::Result {
242 self.write_to(f)
243 }
244}
245
246/// A Java type.
247pub trait Type {
248 const SIGNATURE: Signature;
249}
250
251impl<T: Type> Type for &T {
252 const SIGNATURE: Signature = T::SIGNATURE;
253}
254
255impl<T: Type> Type for Option<T> {
256 const SIGNATURE: Signature = T::SIGNATURE;
257}
258
259/// A Java primitive type.
260pub trait PrimitiveType: Type + 'static {}
261
262/// A Java object type.
263pub trait ObjectType: Type + 'static {}
264
265impl Type for () {
266 const SIGNATURE: Signature = Signature::Void;
267}
268
269macro_rules! impl_primitive_type {
270 ($typ:ty, $signature:expr) => {
271 impl Type for $typ {
272 const SIGNATURE: Signature = $signature;
273 }
274
275 impl PrimitiveType for $typ {}
276 };
277}
278
279impl_primitive_type!(bool, Signature::Boolean);
280impl_primitive_type!(i8, Signature::Byte);
281impl_primitive_type!(u16, Signature::Char);
282impl_primitive_type!(i16, Signature::Short);
283impl_primitive_type!(i32, Signature::Int);
284impl_primitive_type!(i64, Signature::Long);
285impl_primitive_type!(f32, Signature::Float);
286impl_primitive_type!(f64, Signature::Double);
287
288/// A reference to an object or class with a specific type.
289pub trait TypedRef: Deref
290where
291 Self::Target: Ref,
292{
293 /// Whether the reference is static.
294 const STATIC: bool;
295
296 /// The type of the object or class.
297 type Type: ObjectType;
298
299 /// Creates a new reference from a raw reference.
300 ///
301 /// # Safety
302 ///
303 /// The reference must match [Self::Type].
304 unsafe fn from_ref(reference: Self::Target) -> Self;
305
306 /// Converts the reference to a raw reference.
307 fn into_ref(self) -> Self::Target;
308}
309
310/// A reference to an object with a specific type.
311#[repr(transparent)]
312pub struct Object<R: Ref, T: ObjectType> {
313 reference: R,
314 _typ: PhantomData<T>,
315}
316
317/// A reference to a class with a specific type.
318#[repr(transparent)]
319pub struct Class<R: Ref, T: ObjectType> {
320 reference: R,
321 _typ: PhantomData<T>,
322}
323
324macro_rules! impl_typed_common {
325 ($typ:ident, $is_static:literal) => {
326 impl<R: Ref, T: ObjectType> Type for $typ<R, T> {
327 const SIGNATURE: Signature = T::SIGNATURE;
328 }
329
330 impl<R: Ref, T: ObjectType> Deref for $typ<R, T> {
331 type Target = R;
332
333 fn deref(&self) -> &Self::Target {
334 &self.reference
335 }
336 }
337
338 impl<R: Ref, T: ObjectType> TypedRef for $typ<R, T> {
339 const STATIC: bool = $is_static;
340
341 type Type = T;
342
343 unsafe fn from_ref(reference: R) -> Self {
344 Self {
345 reference,
346 _typ: PhantomData,
347 }
348 }
349
350 fn into_ref(self) -> R {
351 self.reference
352 }
353 }
354
355 impl<'env, T: ObjectType> $typ<LocalRef<'env>, T> {
356 /// Converts the reference to a trampoline reference.
357 pub fn into_trampoline(self) -> TrampolineObject<'env, T> {
358 unsafe { TrampolineObject::from_ref(self.into_ref().into_trampoline()) }
359 }
360 }
361 };
362}
363
364impl_typed_common!(Object, false);
365impl_typed_common!(Class, true);
366
367/// A local reference to an object with a specific type.
368pub type LocalObject<'env, T> = Object<LocalRef<'env>, T>;
369/// A trampoline reference to an object with a specific type.
370pub type TrampolineObject<'env, T> = Object<TrampolineRef<'env>, T>;
371/// A global reference to an object with a specific type.
372pub type GlobalObject<'vm, T> = Object<GlobalRef<'vm>, T>;
373/// A weak global reference to an object with a specific type.
374pub type WeakGlobalObject<'vm, T> = Object<WeakGlobalRef<'vm>, T>;
375
376/// A local reference to a class with a specific type.
377pub type LocalClass<'env, T> = Class<LocalRef<'env>, T>;
378/// A trampoline reference to a class with a specific type.
379pub type TrampolineClass<'env, T> = Class<TrampolineRef<'env>, T>;
380/// A global reference to a class with a specific type.
381pub type GlobalClass<'vm, T> = Class<GlobalRef<'vm>, T>;
382/// A weak global reference to a class with a specific type.
383pub type WeakGlobalClass<'vm, T> = Class<WeakGlobalRef<'vm>, T>;
384
385/// A util to represent a null argument to be applied to a JNI call or field access.
386///
387/// NOTE: `Option::<Object<R, T>>::None` also can represent null of type T
388pub struct Null<T: ObjectType>(pub PhantomData<T>);
389
390impl<T: ObjectType> Null<T> {
391 pub const NULL: Self = Self(PhantomData);
392}
393
394impl<T: ObjectType> Type for Null<T> {
395 const SIGNATURE: Signature = T::SIGNATURE;
396}
397
398#[doc(hidden)]
399pub const unsafe fn __class_name_to_internal_name_bytes<const N: usize>(s: &'static str) -> [u8; N] {
400 let data = s.as_bytes();
401 let mut ret = [0u8; N];
402
403 let mut index = 0;
404 while index < N {
405 if data[index] == b'.' {
406 ret[index] = b'/';
407 } else {
408 ret[index] = data[index];
409 }
410
411 index += 1;
412 }
413
414 ret
415}
416
417#[doc(hidden)]
418pub const unsafe fn __bytes_to_str(bytes: &'static [u8]) -> &'static str {
419 unsafe { ::core::str::from_utf8_unchecked(bytes) }
420}
421
422/// Defines a Java class as [`Type`] with the given name.
423#[macro_export]
424macro_rules! define_java_class {
425 ($name:ident, $class:literal) => {
426 pub struct $name;
427
428 impl $crate::Type for $name {
429 const SIGNATURE: $crate::Signature = $crate::Signature::Object(unsafe {
430 const REPLACED: [u8; ($class).len()] = unsafe { $crate::__class_name_to_internal_name_bytes($class) };
431
432 $crate::__bytes_to_str(&REPLACED)
433 });
434 }
435
436 impl $crate::ObjectType for $name {}
437 };
438}