use std::{ marker::PhantomData, os::raw::{c_char, c_void}, ptr, str, str::FromStr, sync::{Mutex, MutexGuard}, }; use log::warn; use crate::{ descriptors::Desc, errors::*, objects::{ AutoElements, AutoElementsCritical, AutoLocal, GlobalRef, JByteBuffer, JClass, JFieldID, JList, JMap, JMethodID, JObject, JStaticFieldID, JStaticMethodID, JString, JThrowable, JValue, JValueOwned, ReleaseMode, TypeArray, WeakRef, }, signature::{JavaType, Primitive, TypeSignature}, strings::{JNIString, JavaStr}, sys::{ self, jarray, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort, jsize, jvalue, JNINativeMethod, }, JNIVersion, JavaVM, }; use crate::{ errors::Error::JniCall, objects::{ JBooleanArray, JByteArray, JCharArray, JDoubleArray, JFloatArray, JIntArray, JLongArray, JObjectArray, JPrimitiveArray, JShortArray, }, }; use crate::{objects::AsJArrayRaw, signature::ReturnType}; /// FFI-compatible JNIEnv struct. You can safely use this as the JNIEnv argument /// to exported methods that will be called by java. This is where most of the /// magic happens. All methods on this object are wrappers around JNI functions, /// so the documentation on their behavior is still pretty applicable. /// /// # Exception handling /// /// Since we're calling into the JVM with this, many methods also have the /// potential to cause an exception to get thrown. If this is the case, an `Err` /// result will be returned with the error kind `JavaException`. Note that this /// will _not_ clear the exception - it's up to the caller to decide whether to /// do so or to let it continue being thrown. /// /// # References and Lifetimes /// /// As in C JNI, interactions with Java objects happen through references, either local /// or global, represented by [`JObject`] and [`GlobalRef`] respectively. So long as there is at /// least one such reference to a Java object, the JVM garbage collector will not reclaim it. /// /// Global references exist until deleted. Deletion occurs when the `GlobalRef` is /// dropped. /// /// Local references belong to a local reference frame, and exist until /// [deleted][JNIEnv::delete_local_ref] or until the local reference frame is exited. A local /// reference frame is entered when a native method is called from Java, or when Rust code /// does so explicitly using [`JNIEnv::with_local_frame`]. That local reference frame is exited /// when the native method or `with_local_frame` returns. When a local reference frame is exited, /// all local references created inside it are deleted. /// /// Unlike C JNI, this crate creates a separate `JNIEnv` for each local reference frame. The /// associated Rust lifetime `'local` represents that local reference frame. Rust's borrow checker /// will ensure that local references are not used after their local reference frame exits (which /// would cause undefined behavior). /// /// Unlike global references, local references are not deleted when dropped by default. This is for /// performance: it is faster for the JVM to delete all of the local references in a frame all at /// once, than to delete each local reference one at a time. However, this can cause a memory leak /// if the local reference frame remains entered for a long time, such as a long-lasting loop, in /// which case local references should be deleted explicitly. Local references can be deleted when /// dropped if desired; use [`JNIEnv::auto_local`] to arrange that. /// /// ## Lifetime Names /// /// This crate uses the following convention for lifetime names: /// /// * `'local` is the lifetime of a local reference frame, as described above. /// /// * `'other_local`, `'other_local_1`, and `'other_local_2` are the lifetimes of some other local /// reference frame, which may be but doesn't have to be the same as `'local`. For example, /// [`JNIEnv::new_local_ref`] accepts a local reference in any local reference frame /// `'other_local` and creates a new local reference to the same object in `'local`. /// /// * `'obj_ref` is the lifetime of a borrow of a JNI reference, like &[JObject] /// or &[GlobalRef]. For example, [`JNIEnv::get_list`] constructs a new /// [`JList`] that borrows a `&'obj_ref JObject`. /// /// ## `null` Java references /// `null` Java references are handled by the following rules: /// - If a `null` Java reference is passed to a method that expects a non-`null` /// argument, an `Err` result with the kind `NullPtr` is returned. /// - If a JNI function returns `null` to indicate an error (e.g. `new_int_array`), /// it is converted to `Err`/`NullPtr` or, where possible, to a more applicable /// error type, such as `MethodNotFound`. If the JNI function also throws /// an exception, the `JavaException` error kind will be preferred. /// - If a JNI function may return `null` Java reference as one of possible reference /// values (e.g., `get_object_array_element` or `get_field_unchecked`), /// it is converted to `JObject::null()`. /// /// # `&self` and `&mut self` /// /// Most of the methods on this type take a `&mut self` reference, specifically all methods that /// can enter a new local reference frame. This includes anything that might invoke user-defined /// Java code, which can indirectly enter a new local reference frame by calling a native method. /// /// The reason for this restriction is to ensure that a `JNIEnv` instance can only be used in the /// local reference frame that it belongs to. This, in turn, ensures that it is not possible to /// create [`JObject`]s with the lifetime of a different local reference frame, which would lead to /// undefined behavior. (See [issue #392] for background discussion.) /// /// [issue #392]: https://github.com/jni-rs/jni-rs/issues/392 /// /// ## `cannot borrow as mutable` /// /// If a function takes two or more parameters, one of them is `JNIEnv`, and another is something /// returned by a `JNIEnv` method (like [`JObject`]), then calls to that function may not compile: /// /// ```rust,compile_fail /// # use jni::{errors::Result, JNIEnv, objects::*}; /// # /// # fn f(env: &mut JNIEnv) -> Result<()> { /// fn example_function( /// env: &mut JNIEnv, /// obj: &JObject, /// ) { /// // … /// } /// /// example_function( /// env, /// // ERROR: cannot borrow `*env` as mutable more than once at a time /// &env.new_object( /// "com/example/SomeClass", /// "()V", /// &[], /// )?, /// ) /// # ; Ok(()) /// # } /// ``` /// /// To fix this, the `JNIEnv` parameter needs to come *last*: /// /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::*}; /// # /// # fn f(env: &mut JNIEnv) -> Result<()> { /// fn example_function( /// obj: &JObject, /// env: &mut JNIEnv, /// ) { /// // … /// } /// /// example_function( /// &env.new_object( /// "com/example/SomeClass", /// "()V", /// &[], /// )?, /// env, /// ) /// # ; Ok(()) /// # } /// ``` /// /// # Checked and unchecked methods /// /// Some of the methods come in two versions: checked (e.g. `call_method`) and /// unchecked (e.g. `call_method_unchecked`). Under the hood, checked methods /// perform some checks to ensure the validity of provided signatures, names /// and arguments, and then call the corresponding unchecked method. /// /// Checked methods are more flexible as they allow passing class names /// and method/field descriptors as strings and may perform lookups /// of class objects and method/field ids for you, also performing /// all the needed precondition checks. However, these lookup operations /// are expensive, so if you need to call the same method (or access /// the same field) multiple times, it is /// [recommended](https://docs.oracle.com/en/java/javase/11/docs/specs/jni/design.html#accessing-fields-and-methods) /// to cache the instance of the class and the method/field id, e.g. /// - in loops /// - when calling the same Java callback repeatedly. /// /// If you do not cache references to classes and method/field ids, /// you will *not* benefit from the unchecked methods. /// /// Calling unchecked methods with invalid arguments and/or invalid class and /// method descriptors may lead to segmentation fault. #[repr(transparent)] #[derive(Debug)] pub struct JNIEnv<'local> { internal: *mut sys::JNIEnv, lifetime: PhantomData<&'local ()>, } impl<'local> JNIEnv<'local> { /// Create a JNIEnv from a raw pointer. /// /// # Safety /// /// Expects a valid pointer retrieved from the `GetEnv` JNI function or [Self::get_raw] function. Only does a null check. pub unsafe fn from_raw(ptr: *mut sys::JNIEnv) -> Result { non_null!(ptr, "from_raw ptr argument"); Ok(JNIEnv { internal: ptr, lifetime: PhantomData, }) } /// Get the raw JNIEnv pointer pub fn get_raw(&self) -> *mut sys::JNIEnv { self.internal } /// Duplicates this `JNIEnv`. /// /// # Safety /// /// The duplicate `JNIEnv` must not be used to create any local references, unless they are /// discarded before the current [local reference frame] is exited. Otherwise, they may have a /// lifetime longer than they are actually valid for, resulting in a use-after-free bug and /// undefined behavior. /// /// See [issue #392] for background. /// /// [local reference frame]: JNIEnv::with_local_frame /// [issue #392]: https://github.com/jni-rs/jni-rs/issues/392 pub unsafe fn unsafe_clone(&self) -> Self { Self { internal: self.internal, lifetime: self.lifetime, } } /// Get the java version that we're being executed from. pub fn get_version(&self) -> Result { Ok(jni_unchecked!(self.internal, GetVersion).into()) } /// Load a class from a buffer of raw class data. The name of the class must match the name /// encoded within the class file data. pub fn define_class( &mut self, name: S, loader: &JObject, buf: &[u8], ) -> Result> where S: Into, { let name = name.into(); self.define_class_impl(name.as_ptr(), loader, buf) } /// Load a class from a buffer of raw class data. The name of the class is inferred from the /// buffer. pub fn define_unnamed_class(&mut self, loader: &JObject, buf: &[u8]) -> Result> { self.define_class_impl(ptr::null(), loader, buf) } // Note: This requires `&mut` because it might invoke a method on a user-defined `ClassLoader`. fn define_class_impl( &mut self, name: *const c_char, loader: &JObject, buf: &[u8], ) -> Result> { let class = jni_non_null_call!( self.internal, DefineClass, name, loader.as_raw(), buf.as_ptr() as *const jbyte, buf.len() as jsize ); Ok(unsafe { JClass::from_raw(class) }) } /// Load a class from a buffer of raw class data. The name of the class must match the name /// encoded within the class file data. pub fn define_class_bytearray( &mut self, name: S, loader: &JObject, buf: &AutoElements<'_, '_, '_, jbyte>, ) -> Result> where S: Into, { let name = name.into(); let class = jni_non_null_call!( self.internal, DefineClass, name.as_ptr(), loader.as_raw(), buf.as_ptr(), buf.len() as _ ); Ok(unsafe { JClass::from_raw(class) }) } /// Look up a class by name. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::JClass}; /// # /// # fn example<'local>(env: &mut JNIEnv<'local>) -> Result<()> { /// let class: JClass<'local> = env.find_class("java/lang/String")?; /// # Ok(()) /// # } /// ``` pub fn find_class(&mut self, name: S) -> Result> where S: Into, { let name = name.into(); let class = jni_non_null_call!(self.internal, FindClass, name.as_ptr()); Ok(unsafe { JClass::from_raw(class) }) } /// Returns the superclass for a particular class. Returns None for `java.lang.Object` or /// an interface. As with [Self::find_class], takes a descriptor /// /// # Errors /// /// If a JNI call fails pub fn get_superclass<'other_local, T>(&mut self, class: T) -> Result>> where T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let superclass = unsafe { JClass::from_raw(jni_unchecked!( self.internal, GetSuperclass, class.as_ref().as_raw() )) }; Ok((!superclass.is_null()).then_some(superclass)) } /// Tests whether class1 is assignable from class2. pub fn is_assignable_from<'other_local_1, 'other_local_2, T, U>( &mut self, class1: T, class2: U, ) -> Result where T: Desc<'local, JClass<'other_local_1>>, U: Desc<'local, JClass<'other_local_2>>, { let class1 = class1.lookup(self)?; let class2 = class2.lookup(self)?; Ok(jni_unchecked!( self.internal, IsAssignableFrom, class1.as_ref().as_raw(), class2.as_ref().as_raw() ) == sys::JNI_TRUE) } /// Returns true if the object reference can be cast to the given type. /// /// _NB: Unlike the operator `instanceof`, function `IsInstanceOf` *returns `true`* /// for all classes *if `object` is `null`.*_ /// /// See [JNI documentation](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#IsInstanceOf) /// for details. pub fn is_instance_of<'other_local_1, 'other_local_2, O, T>( &mut self, object: O, class: T, ) -> Result where O: AsRef>, T: Desc<'local, JClass<'other_local_2>>, { let class = class.lookup(self)?; Ok(jni_unchecked!( self.internal, IsInstanceOf, object.as_ref().as_raw(), class.as_ref().as_raw() ) == sys::JNI_TRUE) } /// Returns true if ref1 and ref2 refer to the same Java object, or are both `NULL`. Otherwise, /// returns false. pub fn is_same_object<'other_local_1, 'other_local_2, O, T>( &self, ref1: O, ref2: T, ) -> Result where O: AsRef>, T: AsRef>, { Ok(jni_unchecked!( self.internal, IsSameObject, ref1.as_ref().as_raw(), ref2.as_ref().as_raw() ) == sys::JNI_TRUE) } /// Raise an exception from an existing object. This will continue being /// thrown in java unless `exception_clear` is called. /// /// # Examples /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// env.throw(("java/lang/Exception", "something bad happened"))?; /// # Ok(()) /// # } /// ``` /// /// Defaulting to "java/lang/Exception": /// /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// env.throw("something bad happened")?; /// # Ok(()) /// # } /// ``` pub fn throw<'other_local, E>(&mut self, obj: E) -> Result<()> where E: Desc<'local, JThrowable<'other_local>>, { let throwable = obj.lookup(self)?; let res: i32 = jni_unchecked!(self.internal, Throw, throwable.as_ref().as_raw()); // Ensure that `throwable` isn't dropped before the JNI call returns. drop(throwable); if res == 0 { Ok(()) } else { Err(Error::ThrowFailed(res)) } } /// Create and throw a new exception from a class descriptor and an error /// message. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// env.throw_new("java/lang/Exception", "something bad happened")?; /// # Ok(()) /// # } /// ``` pub fn throw_new<'other_local, S, T>(&mut self, class: T, msg: S) -> Result<()> where S: Into, T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let msg = msg.into(); let res: i32 = jni_unchecked!( self.internal, ThrowNew, class.as_ref().as_raw(), msg.as_ptr() ); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); if res == 0 { Ok(()) } else { Err(Error::ThrowFailed(res)) } } /// Check whether or not an exception is currently in the process of being /// thrown. An exception is in this state from the time it gets thrown and /// not caught in a java function until `exception_clear` is called. pub fn exception_occurred(&mut self) -> Result> { let throwable = jni_unchecked!(self.internal, ExceptionOccurred); Ok(unsafe { JThrowable::from_raw(throwable) }) } /// Print exception information to the console. pub fn exception_describe(&self) -> Result<()> { jni_unchecked!(self.internal, ExceptionDescribe); Ok(()) } /// Clear an exception in the process of being thrown. If this is never /// called, the exception will continue being thrown when control is /// returned to java. pub fn exception_clear(&self) -> Result<()> { jni_unchecked!(self.internal, ExceptionClear); Ok(()) } /// Abort the JVM with an error message. #[allow(unused_variables, unreachable_code)] pub fn fatal_error>(&self, msg: S) -> ! { let msg = msg.into(); let res: Result<()> = catch!({ jni_unchecked!(self.internal, FatalError, msg.as_ptr()); unreachable!() }); panic!("{:?}", res.unwrap_err()); } /// Check to see if an exception is being thrown. This only differs from /// `exception_occurred` in that it doesn't return the actual thrown /// exception. pub fn exception_check(&self) -> Result { let check = jni_unchecked!(self.internal, ExceptionCheck) == sys::JNI_TRUE; Ok(check) } /// Create a new instance of a direct java.nio.ByteBuffer /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// let buf = vec![0; 1024 * 1024]; /// let (addr, len) = { // (use buf.into_raw_parts() on nightly) /// let buf = buf.leak(); /// (buf.as_mut_ptr(), buf.len()) /// }; /// let direct_buffer = unsafe { env.new_direct_byte_buffer(addr, len) }?; /// # Ok(()) /// # } /// ``` /// /// # Safety /// /// Expects a valid (non-null) pointer and length /// /// Caller must ensure the lifetime of `data` extends to all uses of the returned /// `ByteBuffer`. The JVM may maintain references to the `ByteBuffer` beyond the lifetime /// of this `JNIEnv`. pub unsafe fn new_direct_byte_buffer( &mut self, data: *mut u8, len: usize, ) -> Result> { non_null!(data, "new_direct_byte_buffer data argument"); let obj = jni_non_null_call!( self.internal, NewDirectByteBuffer, data as *mut c_void, len as jlong ); Ok(JByteBuffer::from_raw(obj)) } /// Returns the starting address of the memory of the direct /// java.nio.ByteBuffer. pub fn get_direct_buffer_address(&self, buf: &JByteBuffer) -> Result<*mut u8> { non_null!(buf, "get_direct_buffer_address argument"); let ptr = jni_unchecked!(self.internal, GetDirectBufferAddress, buf.as_raw()); non_null!(ptr, "get_direct_buffer_address return value"); Ok(ptr as _) } /// Returns the capacity (length) of the direct java.nio.ByteBuffer. /// /// # Terminology /// /// "capacity" here means the length that was passed to [`Self::new_direct_byte_buffer()`] /// which does not reflect the (potentially) larger size of the underlying allocation (unlike the `Vec` /// API). /// /// The terminology is simply kept from the original JNI API (`GetDirectBufferCapacity`). pub fn get_direct_buffer_capacity(&self, buf: &JByteBuffer) -> Result { non_null!(buf, "get_direct_buffer_capacity argument"); let capacity = jni_unchecked!(self.internal, GetDirectBufferCapacity, buf.as_raw()); match capacity { -1 => Err(Error::JniCall(JniError::Unknown)), _ => Ok(capacity as usize), } } /// Turns an object into a global ref. This has the benefit of removing the /// lifetime bounds since it's guaranteed to not get GC'd by java. It /// releases the GC pin upon being dropped. pub fn new_global_ref<'other_local, O>(&self, obj: O) -> Result where O: AsRef>, { let jvm = self.get_java_vm()?; let new_ref = jni_unchecked!(self.internal, NewGlobalRef, obj.as_ref().as_raw()); let global = unsafe { GlobalRef::from_raw(jvm, new_ref) }; Ok(global) } /// Creates a new [weak global reference][WeakRef]. /// /// If the provided object is null, this method returns `None`. Otherwise, it returns `Some` /// containing the new weak global reference. pub fn new_weak_ref<'other_local, O>(&self, obj: O) -> Result> where O: AsRef>, { // We need the `JavaVM` in order to construct a `WeakRef` below. But because `get_java_vm` // is fallible, we need to call it before doing anything else, so that we don't leak // memory if it fails. let vm = self.get_java_vm()?; let obj = obj.as_ref().as_raw(); // Check if the pointer is null *before* calling `NewWeakGlobalRef`. // // This avoids a bug in some JVM implementations which, contrary to the JNI specification, // will throw `java.lang.OutOfMemoryError: C heap space` from `NewWeakGlobalRef` if it is // passed a null pointer. (The specification says it will return a null pointer in that // situation, not throw an exception.) if obj.is_null() { return Ok(None); } let weak: sys::jweak = jni_non_void_call!(self.internal, NewWeakGlobalRef, obj); // Check if the pointer returned by `NewWeakGlobalRef` is null. This can happen if `obj` is // itself a weak reference that was already garbage collected. if weak.is_null() { return Ok(None); } let weak = unsafe { WeakRef::from_raw(vm, weak) }; Ok(Some(weak)) } /// Create a new local reference to an object. /// /// Specifically, this calls the JNI function [`NewLocalRef`], which creates a reference in the /// current local reference frame, regardless of whether the original reference belongs to the /// same local reference frame, a different one, or is a [global reference][GlobalRef]. In Rust /// terms, this method accepts a JNI reference with any valid lifetime and produces a clone of /// that reference with the lifetime of this `JNIEnv`. The returned reference can outlive the /// original. /// /// This method is useful when you have a strong global reference and you can't prevent it from /// being dropped before you're finished with it. In that case, you can use this method to /// create a new local reference that's guaranteed to remain valid for the duration of the /// current local reference frame, regardless of what later happens to the original global /// reference. /// /// # Lifetimes /// /// `'local` is the lifetime of the local reference frame that this `JNIEnv` belongs to. This /// method creates a new local reference in that frame, with lifetime `'local`. /// /// `'other_local` is the lifetime of the original reference's frame. It can be any valid /// lifetime, even one that `'local` outlives or vice versa. /// /// Think of `'local` as meaning `'new` and `'other_local` as meaning `'original`. (It is /// unfortunately not possible to actually give these names to the two lifetimes because /// `'local` is a parameter to the `JNIEnv` type, not a parameter to this method.) /// /// # Example /// /// In the following example, the `ExampleError::extract_throwable` method uses /// `JNIEnv::new_local_ref` to create a new local reference that outlives the original global /// reference: /// /// ```no_run /// # use jni::{JNIEnv, objects::*}; /// # use std::fmt::Display; /// # /// # type SomeOtherErrorType = Box; /// # /// /// An error that may be caused by either a Java exception or something going wrong in Rust /// /// code. /// enum ExampleError { /// /// This variant represents a Java exception. /// /// /// /// The enclosed `GlobalRef` points to a Java object of class `java.lang.Throwable` /// /// (or one of its many subclasses). /// Exception(GlobalRef), /// /// /// This variant represents an error in Rust code, not a Java exception. /// Other(SomeOtherErrorType), /// } /// /// impl ExampleError { /// /// Consumes this `ExampleError` and produces a `JThrowable`, suitable for throwing /// /// back to Java code. /// /// /// /// If this is an `ExampleError::Exception`, then this extracts the enclosed Java /// /// exception object. Otherwise, a new exception object is created to represent this /// /// error. /// fn extract_throwable<'local>(self, env: &mut JNIEnv<'local>) -> jni::errors::Result> { /// let throwable: JObject = match self { /// ExampleError::Exception(exception) => { /// // The error was caused by a Java exception. /// /// // Here, `exception` is a `GlobalRef` pointing to a Java `Throwable`. It /// // will be dropped at the end of this `match` arm. We'll use /// // `new_local_ref` to create a local reference that will outlive the /// // `GlobalRef`. /// /// env.new_local_ref(&exception)? /// } /// /// ExampleError::Other(error) => { /// // The error was caused by something that happened in Rust code. Create a /// // new `java.lang.Error` to represent it. /// /// let error_string = env.new_string(error.to_string())?; /// /// env.new_object( /// "java/lang/Error", /// "(Ljava/lang/String;)V", /// &[ /// (&error_string).into(), /// ], /// )? /// } /// }; /// /// Ok(JThrowable::from(throwable)) /// } /// } /// ``` /// /// [`NewLocalRef`]: https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#newlocalref pub fn new_local_ref<'other_local, O>(&self, obj: O) -> Result> where O: AsRef>, { let local = jni_unchecked!(self.internal, NewLocalRef, obj.as_ref().as_raw()); Ok(unsafe { JObject::from_raw(local) }) } /// Creates a new auto-deleted local reference. /// /// See also [`with_local_frame`](struct.JNIEnv.html#method.with_local_frame) method that /// can be more convenient when you create a _bounded_ number of local references /// but cannot rely on automatic de-allocation (e.g., in case of recursion, deep call stacks, /// [permanently-attached](struct.JavaVM.html#attaching-native-threads) native threads, etc.). pub fn auto_local(&self, obj: O) -> AutoLocal<'local, O> where O: Into>, { AutoLocal::new(obj, self) } /// Deletes the local reference. /// /// Local references are valid for the duration of a native method call. /// They are /// freed automatically after the native method returns. Each local /// reference costs /// some amount of Java Virtual Machine resource. Programmers need to make /// sure that /// native methods do not excessively allocate local references. Although /// local /// references are automatically freed after the native method returns to /// Java, /// excessive allocation of local references may cause the VM to run out of /// memory /// during the execution of a native method. /// /// In most cases it is better to use `AutoLocal` (see `auto_local` method) /// or `with_local_frame` instead of direct `delete_local_ref` calls. /// /// `obj` can be a mutable borrow of a local reference (such as /// `&mut JObject`) instead of the local reference itself (such as /// `JObject`). In this case, the local reference will still exist after /// this method returns, but it will be null. pub fn delete_local_ref<'other_local, O>(&self, obj: O) -> Result<()> where O: Into>, { let raw = obj.into().into_raw(); jni_unchecked!(self.internal, DeleteLocalRef, raw); Ok(()) } /// Creates a new local reference frame, in which at least a given number /// of local references can be created. /// /// Returns `Err` on failure, with a pending `OutOfMemoryError`. /// /// Prefer to use /// [`with_local_frame`](struct.JNIEnv.html#method.with_local_frame) /// instead of direct `push_local_frame`/`pop_local_frame` calls. /// /// See also [`auto_local`](struct.JNIEnv.html#method.auto_local) method /// and `AutoLocal` type — that approach can be more convenient in loops. pub fn push_local_frame(&self, capacity: i32) -> Result<()> { // This method is safe to call in case of pending exceptions (see chapter 2 of the spec) let res = jni_unchecked!(self.internal, PushLocalFrame, capacity); jni_error_code_to_result(res) } /// Pops off the current local reference frame, frees all the local /// references allocated on the current stack frame, except the `result`, /// which is returned from this function and remains valid. /// /// The resulting `JObject` will be `NULL` iff `result` is `NULL`. /// /// This method allows direct control of local frames, but it can cause /// undefined behavior and is therefore unsafe. Prefer /// [`JNIEnv::with_local_frame`] instead. /// /// # Safety /// /// Any local references created after the most recent call to /// [`JNIEnv::push_local_frame`] (or the underlying JNI function) must not /// be used after calling this method. pub unsafe fn pop_local_frame(&self, result: &JObject) -> Result> { // This method is safe to call in case of pending exceptions (see chapter 2 of the spec) Ok(JObject::from_raw(jni_unchecked!( self.internal, PopLocalFrame, result.as_raw() ))) } /// Executes the given function in a new local reference frame, in which at least a given number /// of references can be created. Once this method returns, all references allocated /// in the frame are freed. /// /// If a frame can't be allocated with the requested capacity for local /// references, returns `Err` with a pending `OutOfMemoryError`. /// /// Since local references created within this frame won't be accessible to the calling /// frame then if you need to pass an object back to the caller then you can do that via a /// [`GlobalRef`] / [`Self::make_global`]. pub fn with_local_frame(&mut self, capacity: i32, f: F) -> std::result::Result where F: FnOnce(&mut JNIEnv) -> std::result::Result, E: From, { unsafe { self.push_local_frame(capacity)?; let ret = f(self); self.pop_local_frame(&JObject::null())?; ret } } /// Executes the given function in a new local reference frame, in which at least a given number /// of references can be created. Once this method returns, all references allocated /// in the frame are freed, except the one that the function returns, which remains valid. /// /// If a frame can't be allocated with the requested capacity for local /// references, returns `Err` with a pending `OutOfMemoryError`. /// /// Since the low-level JNI interface has support for passing back a single local reference /// from a local frame as special-case optimization, this alternative to `with_local_frame` /// exposes that capability to return a local reference without needing to create a /// temporary [`GlobalRef`]. pub fn with_local_frame_returning_local( &mut self, capacity: i32, f: F, ) -> std::result::Result, E> where F: for<'new_local> FnOnce( &mut JNIEnv<'new_local>, ) -> std::result::Result, E>, E: From, { unsafe { self.push_local_frame(capacity)?; match f(self) { Ok(obj) => { let obj = self.pop_local_frame(&obj)?; Ok(obj) } Err(err) => { self.pop_local_frame(&JObject::null())?; Err(err) } } } } /// Allocates a new object from a class descriptor without running a /// constructor. pub fn alloc_object<'other_local, T>(&mut self, class: T) -> Result> where T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let obj = jni_non_null_call!(self.internal, AllocObject, class.as_ref().as_raw()); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); Ok(unsafe { JObject::from_raw(obj) }) } /// Common functionality for finding methods. #[allow(clippy::redundant_closure_call)] fn get_method_id_base<'other_local_1, T, U, V, C, R>( &mut self, class: T, name: U, sig: V, get_method: C, ) -> Result where T: Desc<'local, JClass<'other_local_1>>, U: Into, V: Into, C: for<'other_local_2> Fn( &mut Self, &JClass<'other_local_2>, &JNIString, &JNIString, ) -> Result, { let class = class.lookup(self)?; let ffi_name = name.into(); let sig = sig.into(); let res: Result = catch!({ get_method(self, class.as_ref(), &ffi_name, &sig) }); match res { Ok(m) => Ok(m), Err(e) => match e { Error::NullPtr(_) => { let name: String = ffi_name.into(); let sig: String = sig.into(); Err(Error::MethodNotFound { name, sig }) } _ => Err(e), }, } } /// Look up a method by class descriptor, name, and /// signature. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::JMethodID}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// let method_id: JMethodID = /// env.get_method_id("java/lang/String", "substring", "(II)Ljava/lang/String;")?; /// # Ok(()) /// # } /// ``` pub fn get_method_id<'other_local, T, U, V>( &mut self, class: T, name: U, sig: V, ) -> Result where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into, { self.get_method_id_base(class, name, sig, |env, class, name, sig| { let method_id = jni_non_null_call!( env.internal, GetMethodID, class.as_raw(), name.as_ptr(), sig.as_ptr() ); Ok(unsafe { JMethodID::from_raw(method_id) }) }) } /// Look up a static method by class descriptor, name, and /// signature. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::JStaticMethodID}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// let method_id: JStaticMethodID = /// env.get_static_method_id("java/lang/String", "valueOf", "(I)Ljava/lang/String;")?; /// # Ok(()) /// # } /// ``` pub fn get_static_method_id<'other_local, T, U, V>( &mut self, class: T, name: U, sig: V, ) -> Result where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into, { self.get_method_id_base(class, name, sig, |env, class, name, sig| { let method_id = jni_non_null_call!( env.internal, GetStaticMethodID, class.as_raw(), name.as_ptr(), sig.as_ptr() ); Ok(unsafe { JStaticMethodID::from_raw(method_id) }) }) } /// Look up the field ID for a class/name/type combination. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::JFieldID}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// let field_id: JFieldID = env.get_field_id("com/my/Class", "intField", "I")?; /// # Ok(()) /// # } /// ``` pub fn get_field_id<'other_local, T, U, V>( &mut self, class: T, name: U, sig: V, ) -> Result where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into, { let class = class.lookup(self)?; let ffi_name = name.into(); let ffi_sig = sig.into(); let res: Result = catch!({ let field_id = jni_non_null_call!( self.internal, GetFieldID, class.as_ref().as_raw(), ffi_name.as_ptr(), ffi_sig.as_ptr() ); Ok(unsafe { JFieldID::from_raw(field_id) }) }); match res { Ok(m) => Ok(m), Err(e) => match e { Error::NullPtr(_) => { let name: String = ffi_name.into(); let sig: String = ffi_sig.into(); Err(Error::FieldNotFound { name, sig }) } _ => Err(e), }, } } /// Look up the static field ID for a class/name/type combination. /// /// # Example /// ```rust,no_run /// # use jni::{errors::Result, JNIEnv, objects::JStaticFieldID}; /// # /// # fn example(env: &mut JNIEnv) -> Result<()> { /// let field_id: JStaticFieldID = env.get_static_field_id("com/my/Class", "intField", "I")?; /// # Ok(()) /// # } /// ``` pub fn get_static_field_id<'other_local, T, U, V>( &mut self, class: T, name: U, sig: V, ) -> Result where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into, { let class = class.lookup(self)?; let ffi_name = name.into(); let ffi_sig = sig.into(); let res: Result = catch!({ let field_id = jni_non_null_call!( self.internal, GetStaticFieldID, class.as_ref().as_raw(), ffi_name.as_ptr(), ffi_sig.as_ptr() ); Ok(unsafe { JStaticFieldID::from_raw(field_id) }) }); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); match res { Ok(m) => Ok(m), Err(e) => match e { Error::NullPtr(_) => { let name: String = ffi_name.into(); let sig: String = ffi_sig.into(); Err(Error::FieldNotFound { name, sig }) } _ => Err(e), }, } } /// Get the class for an object. pub fn get_object_class<'other_local, O>(&self, obj: O) -> Result> where O: AsRef>, { let obj = obj.as_ref(); non_null!(obj, "get_object_class"); unsafe { Ok(JClass::from_raw(jni_unchecked!( self.internal, GetObjectClass, obj.as_raw() ))) } } /// Call a static method in an unsafe manner. This does nothing to check /// whether the method is valid to call on the class, whether the return /// type is correct, or whether the number of args is valid for the method. /// /// Under the hood, this simply calls the `CallStaticMethodA` method /// with the provided arguments. /// /// # Safety /// /// The provided JMethodID must be valid, and match the types and number of arguments, and return type. /// If these are incorrect, the JVM may crash. The JMethodID must also match the passed type. pub unsafe fn call_static_method_unchecked<'other_local, T, U>( &mut self, class: T, method_id: U, ret: ReturnType, args: &[jvalue], ) -> Result> where T: Desc<'local, JClass<'other_local>>, U: Desc<'local, JStaticMethodID>, { let class = class.lookup(self)?; let method_id = method_id.lookup(self)?.as_ref().into_raw(); let class_raw = class.as_ref().as_raw(); let jni_args = args.as_ptr(); // TODO clean this up let ret = Ok(match ret { ReturnType::Object | ReturnType::Array => { let obj = jni_non_void_call!( self.internal, CallStaticObjectMethodA, class_raw, method_id, jni_args ); let obj = unsafe { JObject::from_raw(obj) }; obj.into() } ReturnType::Primitive(p) => match p { Primitive::Boolean => jni_non_void_call!( self.internal, CallStaticBooleanMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Char => jni_non_void_call!( self.internal, CallStaticCharMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Short => jni_non_void_call!( self.internal, CallStaticShortMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Int => jni_non_void_call!( self.internal, CallStaticIntMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Long => jni_non_void_call!( self.internal, CallStaticLongMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Float => jni_non_void_call!( self.internal, CallStaticFloatMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Double => jni_non_void_call!( self.internal, CallStaticDoubleMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Byte => jni_non_void_call!( self.internal, CallStaticByteMethodA, class_raw, method_id, jni_args ) .into(), Primitive::Void => { jni_void_call!( self.internal, CallStaticVoidMethodA, class_raw, method_id, jni_args ); return Ok(JValueOwned::Void); } }, // JavaType::Primitive }); // match parsed.ret // Ensure that `class` isn't dropped before the JNI call returns. drop(class); ret } /// Call an object method in an unsafe manner. This does nothing to check /// whether the method is valid to call on the object, whether the return /// type is correct, or whether the number of args is valid for the method. /// /// Under the hood, this simply calls the `CallMethodA` method with /// the provided arguments. /// /// # Safety /// /// The provided JMethodID must be valid, and match the types and number of arguments, and return type. /// If these are incorrect, the JVM may crash. The JMethodID must also match the passed type. pub unsafe fn call_method_unchecked<'other_local, O, T>( &mut self, obj: O, method_id: T, ret: ReturnType, args: &[jvalue], ) -> Result> where O: AsRef>, T: Desc<'local, JMethodID>, { let method_id = method_id.lookup(self)?.as_ref().into_raw(); let obj = obj.as_ref().as_raw(); let jni_args = args.as_ptr(); // TODO clean this up Ok(match ret { ReturnType::Object | ReturnType::Array => { let obj = jni_non_void_call!(self.internal, CallObjectMethodA, obj, method_id, jni_args); let obj = unsafe { JObject::from_raw(obj) }; obj.into() } ReturnType::Primitive(p) => match p { Primitive::Boolean => { jni_non_void_call!(self.internal, CallBooleanMethodA, obj, method_id, jni_args) .into() } Primitive::Char => { jni_non_void_call!(self.internal, CallCharMethodA, obj, method_id, jni_args) .into() } Primitive::Short => { jni_non_void_call!(self.internal, CallShortMethodA, obj, method_id, jni_args) .into() } Primitive::Int => { jni_non_void_call!(self.internal, CallIntMethodA, obj, method_id, jni_args) .into() } Primitive::Long => { jni_non_void_call!(self.internal, CallLongMethodA, obj, method_id, jni_args) .into() } Primitive::Float => { jni_non_void_call!(self.internal, CallFloatMethodA, obj, method_id, jni_args) .into() } Primitive::Double => { jni_non_void_call!(self.internal, CallDoubleMethodA, obj, method_id, jni_args) .into() } Primitive::Byte => { jni_non_void_call!(self.internal, CallByteMethodA, obj, method_id, jni_args) .into() } Primitive::Void => { jni_void_call!(self.internal, CallVoidMethodA, obj, method_id, jni_args); return Ok(JValueOwned::Void); } }, // JavaType::Primitive }) // match parsed.ret } /// Calls an object method safely. This comes with a number of /// lookups/checks. It /// /// * Parses the type signature to find the number of arguments and return /// type /// * Looks up the JClass for the given object. /// * Looks up the JMethodID for the class/name/signature combination /// * Ensures that the number/types of args matches the signature /// * Cannot check an object's type - but primitive types are matched against each other (including Object) /// * Calls `call_method_unchecked` with the verified safe arguments. /// /// Note: this may cause a Java exception if the arguments are the wrong /// type, in addition to if the method itself throws. pub fn call_method<'other_local, O, S, T>( &mut self, obj: O, name: S, sig: T, args: &[JValue], ) -> Result> where O: AsRef>, S: Into, T: Into + AsRef, { let obj = obj.as_ref(); non_null!(obj, "call_method obj argument"); // parse the signature let parsed = TypeSignature::from_str(sig.as_ref())?; if parsed.args.len() != args.len() { return Err(Error::InvalidArgList(parsed)); } // check arguments types let base_types_match = parsed .args .iter() .zip(args.iter()) .all(|(exp, act)| match exp { JavaType::Primitive(p) => act.primitive_type() == Some(*p), JavaType::Object(_) | JavaType::Array(_) => act.primitive_type().is_none(), JavaType::Method(_) => { unreachable!("JavaType::Method(_) should not come from parsing a method sig") } }); if !base_types_match { return Err(Error::InvalidArgList(parsed)); } let class = self.auto_local(self.get_object_class(obj)?); let args: Vec = args.iter().map(|v| v.as_jni()).collect(); // SAFETY: We've obtained the method_id above, so it is valid for this class. // We've also validated the argument counts and types using the same type signature // we fetched the original method ID from. unsafe { self.call_method_unchecked(obj, (&class, name, sig), parsed.ret, &args) } } /// Calls a static method safely. This comes with a number of /// lookups/checks. It /// /// * Parses the type signature to find the number of arguments and return /// type /// * Looks up the JMethodID for the class/name/signature combination /// * Ensures that the number/types of args matches the signature /// * Cannot check an object's type - but primitive types are matched against each other (including Object) /// * Calls `call_method_unchecked` with the verified safe arguments. /// /// Note: this may cause a Java exception if the arguments are the wrong /// type, in addition to if the method itself throws. pub fn call_static_method<'other_local, T, U, V>( &mut self, class: T, name: U, sig: V, args: &[JValue], ) -> Result> where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into + AsRef, { let parsed = TypeSignature::from_str(&sig)?; if parsed.args.len() != args.len() { return Err(Error::InvalidArgList(parsed)); } // check arguments types let base_types_match = parsed .args .iter() .zip(args.iter()) .all(|(exp, act)| match exp { JavaType::Primitive(p) => act.primitive_type() == Some(*p), JavaType::Object(_) | JavaType::Array(_) => act.primitive_type().is_none(), JavaType::Method(_) => { unreachable!("JavaType::Method(_) should not come from parsing a method sig") } }); if !base_types_match { return Err(Error::InvalidArgList(parsed)); } // go ahead and look up the class since we'll need that for the next call. let class = class.lookup(self)?; let class = class.as_ref(); let args: Vec = args.iter().map(|v| v.as_jni()).collect(); // SAFETY: We've obtained the method_id above, so it is valid for this class. // We've also validated the argument counts and types using the same type signature // we fetched the original method ID from. unsafe { self.call_static_method_unchecked(class, (class, name, sig), parsed.ret, &args) } } /// Create a new object using a constructor. This is done safely using /// checks similar to those in `call_static_method`. pub fn new_object<'other_local, T, U>( &mut self, class: T, ctor_sig: U, ctor_args: &[JValue], ) -> Result> where T: Desc<'local, JClass<'other_local>>, U: Into + AsRef, { // parse the signature let parsed = TypeSignature::from_str(&ctor_sig)?; // check arguments length if parsed.args.len() != ctor_args.len() { return Err(Error::InvalidArgList(parsed)); } // check arguments types let base_types_match = parsed .args .iter() .zip(ctor_args.iter()) .all(|(exp, act)| match exp { JavaType::Primitive(p) => act.primitive_type() == Some(*p), JavaType::Object(_) | JavaType::Array(_) => act.primitive_type().is_none(), JavaType::Method(_) => { unreachable!("JavaType::Method(_) should not come from parsing a ctor sig") } }); if !base_types_match { return Err(Error::InvalidArgList(parsed)); } // check return value if parsed.ret != ReturnType::Primitive(Primitive::Void) { return Err(Error::InvalidCtorReturn); } // build strings let class = class.lookup(self)?; let class = class.as_ref(); let method_id: JMethodID = Desc::::lookup((class, ctor_sig), self)?; let ctor_args: Vec = ctor_args.iter().map(|v| v.as_jni()).collect(); // SAFETY: We've obtained the method_id above, so it is valid for this class. // We've also validated the argument counts and types using the same type signature // we fetched the original method ID from. unsafe { self.new_object_unchecked(class, method_id, &ctor_args) } } /// Create a new object using a constructor. Arguments aren't checked /// because of the `JMethodID` usage. /// /// # Safety /// /// The provided JMethodID must be valid, and match the types and number of arguments, as well as return type /// (always an Object for a constructor). If these are incorrect, the JVM may crash. The JMethodID must also match /// the passed type. pub unsafe fn new_object_unchecked<'other_local, T>( &mut self, class: T, ctor_id: JMethodID, ctor_args: &[jvalue], ) -> Result> where T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let jni_args = ctor_args.as_ptr(); let obj = jni_non_null_call!( self.internal, NewObjectA, class.as_ref().as_raw(), ctor_id.into_raw(), jni_args ); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); Ok(unsafe { JObject::from_raw(obj) }) } /// Cast a JObject to a `JList`. This won't throw exceptions or return errors /// in the event that the object isn't actually a list, but the methods on /// the resulting map object will. pub fn get_list<'other_local_1, 'obj_ref>( &mut self, obj: &'obj_ref JObject<'other_local_1>, ) -> Result> where 'other_local_1: 'obj_ref, { non_null!(obj, "get_list obj argument"); JList::from_env(self, obj) } /// Cast a JObject to a JMap. This won't throw exceptions or return errors /// in the event that the object isn't actually a map, but the methods on /// the resulting map object will. pub fn get_map<'other_local_1, 'obj_ref>( &mut self, obj: &'obj_ref JObject<'other_local_1>, ) -> Result> where 'other_local_1: 'obj_ref, { non_null!(obj, "get_map obj argument"); JMap::from_env(self, obj) } /// Get a [`JavaStr`] from a [`JString`]. This allows conversions from java string /// objects to rust strings. /// /// This only entails calling `GetStringUTFChars`, which will return a [`JavaStr`] in Java's /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /// format. /// /// This doesn't automatically decode Java's modified UTF-8 format but you /// can use `.into()` to convert the returned [`JavaStr`] into a Rust [`String`]. /// /// # Safety /// /// The caller must guarantee that the Object passed in is an instance of `java.lang.String`, /// passing in anything else will lead to undefined behaviour (The JNI implementation /// is likely to crash or abort the process). /// /// # Errors /// /// Returns an error if `obj` is `null` pub unsafe fn get_string_unchecked<'other_local: 'obj_ref, 'obj_ref>( &self, obj: &'obj_ref JString<'other_local>, ) -> Result> { non_null!(obj, "get_string obj argument"); JavaStr::from_env(self, obj) } /// Get a [`JavaStr`] from a [`JString`]. This allows conversions from java string /// objects to rust strings. /// /// This entails checking that the given object is a `java.lang.String` and /// calling `GetStringUTFChars`, which will return a [`JavaStr`] in Java's /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /// format. /// /// This doesn't automatically decode Java's modified UTF-8 format but you /// can use `.into()` to convert the returned [`JavaStr`] into a Rust [`String`]. /// /// # Performance /// /// This function has a large relative performance impact compared to /// [Self::get_string_unchecked]. For example it may be about five times /// slower than `get_string_unchecked` for very short string. This /// performance penalty comes from the extra validation performed by this /// function. If and only if you can guarantee that your `obj` is of /// `java.lang.String`, use [Self::get_string_unchecked]. /// /// # Errors /// /// Returns an error if `obj` is `null` or is not an instance of `java.lang.String` pub fn get_string<'other_local: 'obj_ref, 'obj_ref>( &mut self, obj: &'obj_ref JString<'other_local>, ) -> Result> { let string_class = self.find_class("java/lang/String")?; if !self.is_assignable_from(string_class, self.get_object_class(obj)?)? { return Err(JniCall(JniError::InvalidArguments)); } // SAFETY: We check that the passed in Object is actually a java.lang.String unsafe { self.get_string_unchecked(obj) } } /// Create a new java string object from a rust string. This requires a /// re-encoding of rusts *real* UTF-8 strings to java's modified UTF-8 /// format. pub fn new_string>(&self, from: S) -> Result> { let ffi_str = from.into(); let s = jni_non_null_call!(self.internal, NewStringUTF, ffi_str.as_ptr()); Ok(unsafe { JString::from_raw(s) }) } /// Get the length of a [`JPrimitiveArray`] or [`JObjectArray`]. pub fn get_array_length<'other_local, 'array>( &self, array: &'array impl AsJArrayRaw<'other_local>, ) -> Result { non_null!(array.as_jarray_raw(), "get_array_length array argument"); let len: jsize = jni_unchecked!(self.internal, GetArrayLength, array.as_jarray_raw()); Ok(len) } /// Construct a new array holding objects in class `element_class`. /// All elements are initially set to `initial_element`. /// /// This function returns a local reference, that must not be allocated /// excessively. /// See [Java documentation][1] for details. /// /// [1]: https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references pub fn new_object_array<'other_local_1, 'other_local_2, T, U>( &mut self, length: jsize, element_class: T, initial_element: U, ) -> Result> where T: Desc<'local, JClass<'other_local_2>>, U: AsRef>, { let class = element_class.lookup(self)?; let array: jarray = jni_non_null_call!( self.internal, NewObjectArray, length, class.as_ref().as_raw(), initial_element.as_ref().as_raw() ); let array = unsafe { JObjectArray::from_raw(array) }; // Ensure that `class` isn't dropped before the JNI call returns. drop(class); Ok(array) } /// Returns a local reference to an element of the [`JObjectArray`] `array`. pub fn get_object_array_element<'other_local>( &mut self, array: impl AsRef>, index: jsize, ) -> Result> { non_null!(array.as_ref(), "get_object_array_element array argument"); Ok(unsafe { JObject::from_raw(jni_non_void_call!( self.internal, GetObjectArrayElement, array.as_ref().as_raw(), index )) }) } /// Sets an element of the [`JObjectArray`] `array`. pub fn set_object_array_element<'other_local_1, 'other_local_2>( &self, array: impl AsRef>, index: jsize, value: impl AsRef>, ) -> Result<()> { non_null!(array.as_ref(), "set_object_array_element array argument"); jni_void_call!( self.internal, SetObjectArrayElement, array.as_ref().as_raw(), index, value.as_ref().as_raw() ); Ok(()) } /// Create a new java byte array from a rust byte slice. pub fn byte_array_from_slice(&self, buf: &[u8]) -> Result> { let length = buf.len() as i32; let bytes = self.new_byte_array(length)?; jni_unchecked!( self.internal, SetByteArrayRegion, bytes.as_raw(), 0, length, buf.as_ptr() as *const i8 ); Ok(bytes) } /// Converts a java byte array to a rust vector of bytes. pub fn convert_byte_array<'other_local>( &self, array: impl AsRef>, ) -> Result> { let array = array.as_ref().as_raw(); non_null!(array, "convert_byte_array array argument"); let length = jni_non_void_call!(self.internal, GetArrayLength, array); let mut vec = vec![0u8; length as usize]; jni_unchecked!( self.internal, GetByteArrayRegion, array, 0, length, vec.as_mut_ptr() as *mut i8 ); Ok(vec) } /// Create a new java boolean array of supplied length. pub fn new_boolean_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewBooleanArray, length); let array = unsafe { JBooleanArray::from_raw(array) }; Ok(array) } /// Create a new java byte array of supplied length. pub fn new_byte_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewByteArray, length); let array = unsafe { JByteArray::from_raw(array) }; Ok(array) } /// Create a new java char array of supplied length. pub fn new_char_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewCharArray, length); let array = unsafe { JCharArray::from_raw(array) }; Ok(array) } /// Create a new java short array of supplied length. pub fn new_short_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewShortArray, length); let array = unsafe { JShortArray::from_raw(array) }; Ok(array) } /// Create a new java int array of supplied length. pub fn new_int_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewIntArray, length); let array = unsafe { JIntArray::from_raw(array) }; Ok(array) } /// Create a new java long array of supplied length. pub fn new_long_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewLongArray, length); let array = unsafe { JLongArray::from_raw(array) }; Ok(array) } /// Create a new java float array of supplied length. pub fn new_float_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewFloatArray, length); let array = unsafe { JFloatArray::from_raw(array) }; Ok(array) } /// Create a new java double array of supplied length. pub fn new_double_array(&self, length: jsize) -> Result> { let array: jarray = jni_non_null_call!(self.internal, NewDoubleArray, length); let array = unsafe { JDoubleArray::from_raw(array) }; Ok(array) } /// Copy elements of the java boolean array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_boolean_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jboolean], ) -> Result<()> { non_null!(array.as_ref(), "get_boolean_array_region array argument"); jni_void_call!( self.internal, GetBooleanArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java byte array from the `start` index to the `buf` /// slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_byte_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jbyte], ) -> Result<()> { non_null!(array.as_ref(), "get_byte_array_region array argument"); jni_void_call!( self.internal, GetByteArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java char array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_char_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jchar], ) -> Result<()> { non_null!(array.as_ref(), "get_char_array_region array argument"); jni_void_call!( self.internal, GetCharArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java short array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_short_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jshort], ) -> Result<()> { non_null!(array.as_ref(), "get_short_array_region array argument"); jni_void_call!( self.internal, GetShortArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java int array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_int_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jint], ) -> Result<()> { non_null!(array.as_ref(), "get_int_array_region array argument"); jni_void_call!( self.internal, GetIntArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java long array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_long_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jlong], ) -> Result<()> { non_null!(array.as_ref(), "get_long_array_region array argument"); jni_void_call!( self.internal, GetLongArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java float array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_float_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jfloat], ) -> Result<()> { non_null!(array.as_ref(), "get_float_array_region array argument"); jni_void_call!( self.internal, GetFloatArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy elements of the java double array from the `start` index to the /// `buf` slice. The number of copied elements is equal to the `buf` length. /// /// # Errors /// If `start` is negative _or_ `start + buf.len()` is greater than [`array.length`] /// then no elements are copied, an `ArrayIndexOutOfBoundsException` is thrown, /// and `Err` is returned. /// /// [`array.length`]: struct.JNIEnv.html#method.get_array_length pub fn get_double_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &mut [jdouble], ) -> Result<()> { non_null!(array.as_ref(), "get_double_array_region array argument"); jni_void_call!( self.internal, GetDoubleArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_mut_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java boolean array at the /// `start` index. pub fn set_boolean_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jboolean], ) -> Result<()> { non_null!(array.as_ref(), "set_boolean_array_region array argument"); jni_void_call!( self.internal, SetBooleanArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java byte array at the /// `start` index. pub fn set_byte_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jbyte], ) -> Result<()> { non_null!(array.as_ref(), "set_byte_array_region array argument"); jni_void_call!( self.internal, SetByteArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java char array at the /// `start` index. pub fn set_char_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jchar], ) -> Result<()> { non_null!(array.as_ref(), "set_char_array_region array argument"); jni_void_call!( self.internal, SetCharArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java short array at the /// `start` index. pub fn set_short_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jshort], ) -> Result<()> { non_null!(array.as_ref(), "set_short_array_region array argument"); jni_void_call!( self.internal, SetShortArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java int array at the /// `start` index. pub fn set_int_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jint], ) -> Result<()> { non_null!(array.as_ref(), "set_int_array_region array argument"); jni_void_call!( self.internal, SetIntArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java long array at the /// `start` index. pub fn set_long_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jlong], ) -> Result<()> { non_null!(array.as_ref(), "set_long_array_region array argument"); jni_void_call!( self.internal, SetLongArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java float array at the /// `start` index. pub fn set_float_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jfloat], ) -> Result<()> { non_null!(array.as_ref(), "set_float_array_region array argument"); jni_void_call!( self.internal, SetFloatArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Copy the contents of the `buf` slice to the java double array at the /// `start` index. pub fn set_double_array_region<'other_local>( &self, array: impl AsRef>, start: jsize, buf: &[jdouble], ) -> Result<()> { non_null!(array.as_ref(), "set_double_array_region array argument"); jni_void_call!( self.internal, SetDoubleArrayRegion, array.as_ref().as_raw(), start, buf.len() as jsize, buf.as_ptr() ); Ok(()) } /// Get a field without checking the provided type against the actual field. pub fn get_field_unchecked<'other_local, O, T>( &mut self, obj: O, field: T, ty: ReturnType, ) -> Result> where O: AsRef>, T: Desc<'local, JFieldID>, { let obj = obj.as_ref(); non_null!(obj, "get_field_typed obj argument"); let field = field.lookup(self)?.as_ref().into_raw(); let obj = obj.as_raw(); // TODO clean this up Ok(match ty { ReturnType::Object | ReturnType::Array => { let obj = jni_non_void_call!(self.internal, GetObjectField, obj, field); let obj = unsafe { JObject::from_raw(obj) }; obj.into() } ReturnType::Primitive(p) => match p { Primitive::Boolean => { jni_unchecked!(self.internal, GetBooleanField, obj, field).into() } Primitive::Char => jni_unchecked!(self.internal, GetCharField, obj, field).into(), Primitive::Short => jni_unchecked!(self.internal, GetShortField, obj, field).into(), Primitive::Int => jni_unchecked!(self.internal, GetIntField, obj, field).into(), Primitive::Long => jni_unchecked!(self.internal, GetLongField, obj, field).into(), Primitive::Float => jni_unchecked!(self.internal, GetFloatField, obj, field).into(), Primitive::Double => { jni_unchecked!(self.internal, GetDoubleField, obj, field).into() } Primitive::Byte => jni_unchecked!(self.internal, GetByteField, obj, field).into(), Primitive::Void => { return Err(Error::WrongJValueType("void", "see java field")); } }, }) } /// Set a field without any type checking. pub fn set_field_unchecked<'other_local, O, T>( &mut self, obj: O, field: T, val: JValue, ) -> Result<()> where O: AsRef>, T: Desc<'local, JFieldID>, { let obj = obj.as_ref(); non_null!(obj, "set_field_typed obj argument"); let field = field.lookup(self)?.as_ref().into_raw(); let obj = obj.as_raw(); // TODO clean this up match val { JValue::Object(o) => { jni_unchecked!(self.internal, SetObjectField, obj, field, o.as_raw()); } // JavaType::Object JValue::Bool(b) => { jni_unchecked!(self.internal, SetBooleanField, obj, field, b); } JValue::Char(c) => { jni_unchecked!(self.internal, SetCharField, obj, field, c); } JValue::Short(s) => { jni_unchecked!(self.internal, SetShortField, obj, field, s); } JValue::Int(i) => { jni_unchecked!(self.internal, SetIntField, obj, field, i); } JValue::Long(l) => { jni_unchecked!(self.internal, SetLongField, obj, field, l); } JValue::Float(f) => { jni_unchecked!(self.internal, SetFloatField, obj, field, f); } JValue::Double(d) => { jni_unchecked!(self.internal, SetDoubleField, obj, field, d); } JValue::Byte(b) => { jni_unchecked!(self.internal, SetByteField, obj, field, b); } JValue::Void => { return Err(Error::WrongJValueType("void", "see java field")); } }; Ok(()) } /// Get a field. Requires an object class lookup and a field id lookup /// internally. pub fn get_field<'other_local, O, S, T>( &mut self, obj: O, name: S, ty: T, ) -> Result> where O: AsRef>, S: Into, T: Into + AsRef, { let obj = obj.as_ref(); let class = self.auto_local(self.get_object_class(obj)?); let parsed = ReturnType::from_str(ty.as_ref())?; let field_id: JFieldID = Desc::::lookup((&class, name, ty), self)?; self.get_field_unchecked(obj, field_id, parsed) } /// Set a field. Does the same lookups as `get_field` and ensures that the /// type matches the given value. pub fn set_field<'other_local, O, S, T>( &mut self, obj: O, name: S, ty: T, val: JValue, ) -> Result<()> where O: AsRef>, S: Into, T: Into + AsRef, { let obj = obj.as_ref(); let parsed = JavaType::from_str(ty.as_ref())?; let in_type = val.primitive_type(); match parsed { JavaType::Object(_) | JavaType::Array(_) => { if in_type.is_some() { return Err(Error::WrongJValueType(val.type_name(), "see java field")); } } JavaType::Primitive(p) => { if let Some(in_p) = in_type { if in_p == p { // good } else { return Err(Error::WrongJValueType(val.type_name(), "see java field")); } } else { return Err(Error::WrongJValueType(val.type_name(), "see java field")); } } JavaType::Method(_) => unimplemented!(), } let class = self.auto_local(self.get_object_class(obj)?); self.set_field_unchecked(obj, (&class, name, ty), val) } /// Get a static field without checking the provided type against the actual /// field. pub fn get_static_field_unchecked<'other_local, T, U>( &mut self, class: T, field: U, ty: JavaType, ) -> Result> where T: Desc<'local, JClass<'other_local>>, U: Desc<'local, JStaticFieldID>, { use JavaType::Primitive as JP; let class = class.lookup(self)?; let field = field.lookup(self)?; let result = match ty { JavaType::Object(_) | JavaType::Array(_) => { let obj = jni_non_void_call!( self.internal, GetStaticObjectField, class.as_ref().as_raw(), field.as_ref().into_raw() ); let obj = unsafe { JObject::from_raw(obj) }; obj.into() } JavaType::Method(_) => return Err(Error::WrongJValueType("Method", "see java field")), JP(Primitive::Boolean) => jni_unchecked!( self.internal, GetStaticBooleanField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Char) => jni_unchecked!( self.internal, GetStaticCharField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Short) => jni_unchecked!( self.internal, GetStaticShortField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Int) => jni_unchecked!( self.internal, GetStaticIntField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Long) => jni_unchecked!( self.internal, GetStaticLongField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Float) => jni_unchecked!( self.internal, GetStaticFloatField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Double) => jni_unchecked!( self.internal, GetStaticDoubleField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Byte) => jni_unchecked!( self.internal, GetStaticByteField, class.as_ref().as_raw(), field.as_ref().into_raw() ) .into(), JP(Primitive::Void) => return Err(Error::WrongJValueType("void", "see java field")), }; // Ensure that `class` isn't dropped before the JNI call returns. drop(class); Ok(result) } /// Get a static field. Requires a class lookup and a field id lookup /// internally. pub fn get_static_field<'other_local, T, U, V>( &mut self, class: T, field: U, sig: V, ) -> Result> where T: Desc<'local, JClass<'other_local>>, U: Into, V: Into + AsRef, { let ty = JavaType::from_str(sig.as_ref())?; // go ahead and look up the class sincewe'll need that for the next // call. let class = class.lookup(self)?; self.get_static_field_unchecked(class.as_ref(), (class.as_ref(), field, sig), ty) } /// Set a static field. Requires a class lookup and a field id lookup internally. pub fn set_static_field<'other_local, T, U>( &mut self, class: T, field: U, value: JValue, ) -> Result<()> where T: Desc<'local, JClass<'other_local>>, U: Desc<'local, JStaticFieldID>, { let class = class.lookup(self)?; let field = field.lookup(self)?; match value { JValue::Object(v) => jni_unchecked!( self.internal, SetStaticObjectField, class.as_ref().as_raw(), field.as_ref().into_raw(), v.as_raw() ), JValue::Byte(v) => jni_unchecked!( self.internal, SetStaticByteField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Char(v) => jni_unchecked!( self.internal, SetStaticCharField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Short(v) => jni_unchecked!( self.internal, SetStaticShortField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Int(v) => jni_unchecked!( self.internal, SetStaticIntField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Long(v) => jni_unchecked!( self.internal, SetStaticLongField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Bool(v) => { jni_unchecked!( self.internal, SetStaticBooleanField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ) } JValue::Float(v) => jni_unchecked!( self.internal, SetStaticFloatField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ), JValue::Double(v) => { jni_unchecked!( self.internal, SetStaticDoubleField, class.as_ref().as_raw(), field.as_ref().into_raw(), v ) } JValue::Void => return Err(Error::WrongJValueType("void", "?")), } // Ensure that `class` isn't dropped before the JNI call returns. drop(class); Ok(()) } /// Surrenders ownership of a Rust value to Java. /// /// This requires an object with a `long` field to store the pointer. /// /// In Java the property may look like: /// ```java /// private long myRustValueHandle = 0; /// ``` /// /// Or, in Kotlin the property may look like: /// ```java /// private var myRustValueHandle: Long = 0 /// ``` /// /// _Note that `private` properties are accessible to JNI which may be /// preferable to avoid exposing the handles to more code than necessary /// (since the handles are usually only meaningful to Rust code)_. /// /// The Rust value will be implicitly wrapped in a `Box>`. /// /// The Java object will be locked while changing the field value. /// /// # Safety /// /// It's important to note that using this API will leak memory if /// [`Self::take_rust_field`] is never called so that the Rust type may be /// dropped. /// /// One suggestion that may help ensure that a set Rust field will be /// cleaned up later is for the Java object to implement `Closeable` and let /// people use a `use` block (Kotlin) or `try-with-resources` (Java). /// /// **DO NOT** make a copy of the handle stored in one of these fields /// since that could lead to a use-after-free error if the Rust type is /// taken and dropped multiple times from Rust. If you need to copy an /// object with one of these fields then the field should be zero /// initialized in the copy. #[allow(unused_variables)] pub unsafe fn set_rust_field<'other_local, O, S, T>( &mut self, obj: O, field: S, rust_object: T, ) -> Result<()> where O: AsRef>, S: AsRef, T: Send + 'static, { let obj = obj.as_ref(); let class = self.auto_local(self.get_object_class(obj)?); let field_id: JFieldID = Desc::::lookup((&class, &field, "J"), self)?; let guard = self.lock_obj(obj)?; // Check to see if we've already set this value. If it's not null, that // means that we're going to leak memory if it gets overwritten. let field_ptr = self .get_field_unchecked(obj, field_id, ReturnType::Primitive(Primitive::Long))? .j()? as *mut Mutex; if !field_ptr.is_null() { return Err(Error::FieldAlreadySet(field.as_ref().to_owned())); } let mbox = Box::new(::std::sync::Mutex::new(rust_object)); let ptr: *mut Mutex = Box::into_raw(mbox); self.set_field_unchecked(obj, field_id, (ptr as crate::sys::jlong).into()) } /// Gets a lock on a Rust value that's been given to a Java object. /// /// Java still retains ownership and [`Self::take_rust_field`] will still /// need to be called at some point. /// /// The Java object will be locked before reading the field value but the /// Java object lock will be released after the Rust `Mutex` lock for the /// field value has been taken (i.e the Java object won't be locked once /// this function returns). /// /// # Safety /// /// Checks for a null pointer, but assumes that the data it points to is valid for T. #[allow(unused_variables)] pub unsafe fn get_rust_field<'other_local, O, S, T>( &mut self, obj: O, field: S, ) -> Result> where O: AsRef>, S: Into, T: Send + 'static, { let obj = obj.as_ref(); let guard = self.lock_obj(obj)?; let ptr = self.get_field(obj, field, "J")?.j()? as *mut Mutex; non_null!(ptr, "rust value from Java"); // dereferencing is safe, because we checked it for null Ok((*ptr).lock().unwrap()) } /// Take a Rust field back from Java. /// /// It sets the field to a null pointer to signal that it's empty. /// /// The Java object will be locked before taking the field value. /// /// # Safety /// /// This will make sure that the pointer is non-null, but still assumes that /// the data it points to is valid for T. #[allow(unused_variables)] pub unsafe fn take_rust_field<'other_local, O, S, T>(&mut self, obj: O, field: S) -> Result where O: AsRef>, S: AsRef, T: Send + 'static, { let obj = obj.as_ref(); let class = self.auto_local(self.get_object_class(obj)?); let field_id: JFieldID = Desc::::lookup((&class, &field, "J"), self)?; let mbox = { let guard = self.lock_obj(obj)?; let ptr = self .get_field_unchecked(obj, field_id, ReturnType::Primitive(Primitive::Long))? .j()? as *mut Mutex; non_null!(ptr, "rust value from Java"); let mbox = Box::from_raw(ptr); // attempt to acquire the lock. This prevents us from consuming the // mutex if there's an outstanding lock. No one else will be able to // get a new one as long as we're in the guarded scope. drop(mbox.try_lock()?); self.set_field_unchecked( obj, field_id, (::std::ptr::null_mut::<()>() as sys::jlong).into(), )?; mbox }; Ok(mbox.into_inner().unwrap()) } /// Lock a Java object. The MonitorGuard that this returns is responsible /// for ensuring that it gets unlocked. pub fn lock_obj<'other_local, O>(&self, obj: O) -> Result> where O: AsRef>, { let inner = obj.as_ref().as_raw(); let _ = jni_unchecked!(self.internal, MonitorEnter, inner); Ok(MonitorGuard { obj: inner, env: self.internal, life: Default::default(), }) } /// Returns underlying `sys::JNIEnv` interface. pub fn get_native_interface(&self) -> *mut sys::JNIEnv { self.internal } /// Returns the Java VM interface. pub fn get_java_vm(&self) -> Result { let mut raw = ptr::null_mut(); let res = jni_unchecked!(self.internal, GetJavaVM, &mut raw); jni_error_code_to_result(res)?; unsafe { JavaVM::from_raw(raw) } } /// Ensures that at least a given number of local references can be created /// in the current thread. pub fn ensure_local_capacity(&self, capacity: jint) -> Result<()> { jni_void_call!(self.internal, EnsureLocalCapacity, capacity); Ok(()) } /// Bind function pointers to native methods of class /// according to method name and signature. /// For details see [documentation](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives). pub fn register_native_methods<'other_local, T>( &mut self, class: T, methods: &[NativeMethod], ) -> Result<()> where T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let jni_native_methods: Vec = methods .iter() .map(|nm| JNINativeMethod { name: nm.name.as_ptr() as *mut c_char, signature: nm.sig.as_ptr() as *mut c_char, fnPtr: nm.fn_ptr, }) .collect(); let res = jni_non_void_call!( self.internal, RegisterNatives, class.as_ref().as_raw(), jni_native_methods.as_ptr(), jni_native_methods.len() as jint ); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); jni_error_code_to_result(res) } /// Unbind all native methods of class. pub fn unregister_native_methods<'other_local, T>(&mut self, class: T) -> Result<()> where T: Desc<'local, JClass<'other_local>>, { let class = class.lookup(self)?; let res = jni_non_void_call!(self.internal, UnregisterNatives, class.as_ref().as_raw()); // Ensure that `class` isn't dropped before the JNI call returns. drop(class); jni_error_code_to_result(res) } /// Returns an [`AutoElements`] to access the elements of the given Java `array`. /// /// The elements are accessible until the returned auto-release guard is dropped. /// /// The returned array may be a copy of the Java array and changes made to /// the returned array will not necessarily be reflected in the original /// array until the [`AutoElements`] guard is dropped. /// /// If you know in advance that you will only be reading from the array then /// pass [`ReleaseMode::NoCopyBack`] so that the JNI implementation knows /// that it's not necessary to copy any data back to the original Java array /// when the [`AutoElements`] guard is dropped. /// /// Since the returned array may be a copy of the Java array, changes made to the /// returned array will not necessarily be reflected in the original array until /// the corresponding `Release*ArrayElements` JNI method is called. /// [`AutoElements`] has a commit() method, to force a copy back of pending /// array changes if needed (and without releasing it). /// /// # Safety /// /// ## No data races /// /// This API has no built-in synchronization that ensures there won't be any data /// races while accessing the array elements. /// /// To avoid undefined behaviour it is the caller's responsibility to ensure there /// will be no data races between other Rust or Java threads trying to access the /// same array. /// /// Acquiring a [`MonitorGuard`] lock for the `array` could be one way of ensuring /// mutual exclusion between Rust and Java threads, so long as the Java threads /// also acquire the same lock via `synchronized(array) {}`. /// /// ## No aliasing /// /// Callers must not create more than one [`AutoElements`] or /// [`AutoElementsCritical`] per Java array at the same time - even if /// there is no risk of a data race. /// /// The reason for this restriction is that [`AutoElements`] and /// [`AutoElementsCritical`] implement `DerefMut` which can provide a /// mutable `&mut [T]` slice reference for the elements and it would /// constitute undefined behaviour to allow there to be more than one /// mutable reference that points to the same memory. /// /// # jboolean elements /// /// Keep in mind that arrays of `jboolean` values should only ever hold /// values of `0` or `1` because any other value could lead to undefined /// behaviour within the JVM. /// /// Also see /// [`get_array_elements_critical`](Self::get_array_elements_critical) which /// imposes additional restrictions that make it less likely to incur the /// cost of copying the array elements. pub unsafe fn get_array_elements<'other_local, 'array, T: TypeArray>( &mut self, array: &'array JPrimitiveArray<'other_local, T>, mode: ReleaseMode, ) -> Result> { non_null!(array, "get_array_elements array argument"); AutoElements::new(self, array, mode) } /// Returns an [`AutoElementsCritical`] to access the elements of the given Java `array`. /// /// The elements are accessible during the critical section that exists until the /// returned auto-release guard is dropped. /// /// This API imposes some strict restrictions that help the JNI implementation /// avoid any need to copy the underlying array elements before making them /// accessible to native code: /// /// 1. No other use of JNI calls are allowed (on the same thread) within the critical /// section that exists while holding the [`AutoElementsCritical`] guard. /// 2. No system calls can be made (Such as `read`) that may depend on a result /// from another Java thread. /// /// The JNI spec does not specify what will happen if these rules aren't adhered to /// but it should be assumed it will lead to undefined behaviour, likely deadlock /// and possible program termination. /// /// Even with these restrictions the returned array may still be a copy of /// the Java array and changes made to the returned array will not /// necessarily be reflected in the original array until the [`AutoElementsCritical`] /// guard is dropped. /// /// If you know in advance that you will only be reading from the array then /// pass [`ReleaseMode::NoCopyBack`] so that the JNI implementation knows /// that it's not necessary to copy any data back to the original Java array /// when the [`AutoElementsCritical`] guard is dropped. /// /// A nested scope or explicit use of `std::mem::drop` can be used to /// control when the returned [`AutoElementsCritical`] is dropped to /// minimize the length of the critical section. /// /// If the given array is `null`, an `Error::NullPtr` is returned. /// /// # Safety /// /// ## Critical Section Restrictions /// /// Although this API takes a mutable reference to a [`JNIEnv`] which should /// ensure that it's not possible to call JNI, this API is still marked as /// `unsafe` due to the complex, far-reaching nature of the critical-section /// restrictions imposed here that can't be guaranteed simply through Rust's /// borrow checker rules. /// /// The rules above about JNI usage and system calls _must_ be adhered to. /// /// Using this API implies: /// /// 1. All garbage collection will likely be paused during the critical section /// 2. Any use of JNI in other threads may block if they need to allocate memory /// (due to the garbage collector being paused) /// 3. Any use of system calls that will wait for a result from another Java thread /// could deadlock if that other thread is blocked by a paused garbage collector. /// /// A failure to adhere to the critical section rules could lead to any /// undefined behaviour, including aborting the program. /// /// ## No data races /// /// This API has no built-in synchronization that ensures there won't be any data /// races while accessing the array elements. /// /// To avoid undefined behaviour it is the caller's responsibility to ensure there /// will be no data races between other Rust or Java threads trying to access the /// same array. /// /// Acquiring a [`MonitorGuard`] lock for the `array` could be one way of ensuring /// mutual exclusion between Rust and Java threads, so long as the Java threads /// also acquire the same lock via `synchronized(array) {}`. /// /// ## No aliasing /// /// Callers must not create more than one [`AutoElements`] or /// [`AutoElementsCritical`] per Java array at the same time - even if /// there is no risk of a data race. /// /// The reason for this restriction is that [`AutoElements`] and /// [`AutoElementsCritical`] implement `DerefMut` which can provide a /// mutable `&mut [T]` slice reference for the elements and it would /// constitute undefined behaviour to allow there to be more than one /// mutable reference that points to the same memory. /// /// ## jboolean elements /// /// Keep in mind that arrays of `jboolean` values should only ever hold /// values of `0` or `1` because any other value could lead to undefined /// behaviour within the JVM. /// /// Also see [`get_array_elements`](Self::get_array_elements) which has fewer /// restrictions, but is is more likely to incur a cost from copying the /// array elements. pub unsafe fn get_array_elements_critical<'other_local, 'array, 'env, T: TypeArray>( &'env mut self, array: &'array JPrimitiveArray<'other_local, T>, mode: ReleaseMode, ) -> Result> { non_null!(array, "get_primitive_array_critical array argument"); AutoElementsCritical::new(self, array, mode) } } /// Native method descriptor. pub struct NativeMethod { /// Name of method. pub name: JNIString, /// Method signature. pub sig: JNIString, /// Pointer to native function with signature /// `fn(env: JNIEnv, class: JClass, ...arguments according to sig) -> RetType` /// for static methods or /// `fn(env: JNIEnv, object: JObject, ...arguments according to sig) -> RetType` /// for instance methods. pub fn_ptr: *mut c_void, } /// Guard for a lock on a java object. This gets returned from the `lock_obj` /// method. pub struct MonitorGuard<'local> { obj: sys::jobject, env: *mut sys::JNIEnv, life: PhantomData<&'local ()>, } impl<'local> Drop for MonitorGuard<'local> { fn drop(&mut self) { let res: Result<()> = catch!({ jni_unchecked!(self.env, MonitorExit, self.obj); Ok(()) }); if let Err(e) = res { warn!("error releasing java monitor: {}", e) } } }