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}