Skip to content

Commit

Permalink
draft: Add lifetime'd jarray wrapper types
Browse files Browse the repository at this point in the history
Closes; #396
Closes: #41
  • Loading branch information
rib committed Jan 18, 2023
1 parent eb82c38 commit b3ce652
Show file tree
Hide file tree
Showing 12 changed files with 516 additions and 210 deletions.
331 changes: 190 additions & 141 deletions src/wrapper/jnienv.rs

Large diffs are not rendered by default.

67 changes: 40 additions & 27 deletions src/wrapper/objects/auto_array.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use crate::sys::jsize;
use jni_sys::jarray;
use log::error;

use std::marker::PhantomData;
use std::ptr::NonNull;

use crate::objects::release_mode::ReleaseMode;
use crate::sys::{jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort};
use crate::{errors::*, objects::JObject, sys, JNIEnv};
use crate::{errors::*, sys, JNIEnv};

use super::JPrimitiveArray;

/// Trait to define type array access/release
///
Expand All @@ -17,9 +21,9 @@ use crate::{errors::*, objects::JObject, sys, JNIEnv};
/// The `get` method must return a valid pointer to the beginning of the JNI array.
///
/// The `release` method must not invalidate the `ptr` if the `mode` is [`sys::JNI_COMMIT`].
pub unsafe trait TypeArray {
pub unsafe trait TypeArray: Copy {
/// getter
fn get(env: &mut JNIEnv, obj: &JObject, is_copy: &mut jboolean) -> Result<*mut Self>;
fn get(env: &mut JNIEnv, array: jarray, is_copy: &mut jboolean) -> Result<*mut Self>;

/// releaser
///
Expand All @@ -29,7 +33,7 @@ pub unsafe trait TypeArray {
///
/// If `mode` is not [`sys::JNI_COMMIT`], `ptr` must not be used again after calling this
/// function.
unsafe fn release(env: &mut JNIEnv, obj: &JObject, ptr: NonNull<Self>, mode: i32)
unsafe fn release(env: &mut JNIEnv, array: jarray, ptr: NonNull<Self>, mode: i32)
-> Result<()>;
}

Expand All @@ -39,33 +43,27 @@ macro_rules! type_array {
/// $jni_type array access/release impl
unsafe impl TypeArray for $jni_type {
/// Get Java $jni_type array
fn get(env: &mut JNIEnv, obj: &JObject, is_copy: &mut jboolean) -> Result<*mut Self> {
fn get(env: &mut JNIEnv, array: jarray, is_copy: &mut jboolean) -> Result<*mut Self> {
let internal = env.get_native_interface();
// Even though this method may throw OoME, use `jni_unchecked`
// instead of `jni_non_null_call` to remove (a slight) overhead
// of exception checking. An error will still be detected as a `null`
// result inside AutoArray ctor. Also, modern Hotspot in case of lack
// of memory will return null and won't throw an exception:
// https://sourcegraph.com/github.com/openjdk/jdk/-/blob/src/hotspot/share/memory/allocation.hpp#L488-489
let res = jni_unchecked!(internal, $jni_get, obj.as_raw(), is_copy);
let res = jni_unchecked!(internal, $jni_get, array, is_copy);
Ok(res)
}

/// Release Java $jni_type array
unsafe fn release(
env: &mut JNIEnv,
obj: &JObject,
array: jarray,
ptr: NonNull<Self>,
mode: i32,
) -> Result<()> {
let internal = env.get_native_interface();
jni_unchecked!(
internal,
$jni_release,
obj.as_raw(),
ptr.as_ptr(),
mode as i32
);
jni_unchecked!(internal, $jni_release, array, ptr.as_ptr(), mode as i32);
Ok(())
}
}
Expand All @@ -92,35 +90,42 @@ type_array!(jdouble, GetDoubleArrayElements, ReleaseDoubleArrayElements);
///
/// AutoArray provides automatic array release through a call to appropriate
/// Release<Type>ArrayElements when it goes out of scope.
pub struct AutoArray<'local, T: TypeArray> {
obj: JObject<'local>,
pub struct AutoArray<'a, 'local: 'a, 'other_local: 'a, T: TypeArray + 'other_local> {
array: jarray,
ptr: NonNull<T>,
mode: ReleaseMode,
is_copy: bool,
env: JNIEnv<'local>,
borrow_array: PhantomData<&'other_local jarray>,
lifetime: PhantomData<&'a T>,
}

impl<'local, T: TypeArray> AutoArray<'local, T> {
impl<'a, 'local: 'a, 'other_local: 'a, T: TypeArray + 'other_local>
AutoArray<'a, 'local, 'other_local, T>
{
pub(crate) fn new(
env: &mut JNIEnv<'local>,
obj: JObject<'local>,
array: impl AsRef<JPrimitiveArray<'other_local, T>>,
mode: ReleaseMode,
) -> Result<Self> {
// Safety: The cloned `JNIEnv` will not be used to create any local references. It will be
// passed to the methods of the `TypeArray` implementation, but that trait is `unsafe` and
// implementations are required to uphold the invariants of `unsafe_clone`.
let mut env = unsafe { env.unsafe_clone() };

let array = array.as_ref().as_raw() as jarray;
let mut is_copy: jboolean = 0xff;
Ok(AutoArray {
ptr: {
let ptr = T::get(&mut env, &obj, &mut is_copy)?;
let ptr = T::get(&mut env, array, &mut is_copy)?;
NonNull::new(ptr).ok_or(Error::NullPtr("Non-null ptr expected"))?
},
obj,
array,
mode,
is_copy: is_copy == sys::JNI_TRUE,
env,
borrow_array: PhantomData,
lifetime: PhantomData,
})
}

Expand All @@ -143,7 +148,7 @@ impl<'local, T: TypeArray> AutoArray<'local, T> {
///
/// If `mode` is not [`sys::JNI_COMMIT`], then the array must not have already been released.
unsafe fn release_array_elements(&mut self, mode: i32) -> Result<()> {
T::release(&mut self.env, &self.obj, self.ptr, mode)
T::release(&mut self.env, self.array, self.ptr, mode)
}

/// Don't commit the changes to the array on release (if it is a copy).
Expand All @@ -161,23 +166,29 @@ impl<'local, T: TypeArray> AutoArray<'local, T> {

/// Returns the array size
pub fn size(&self) -> Result<jsize> {
self.env.get_array_length(*self.obj)
self.env.get_array_length(self.array as crate::sys::jarray)
}
}

impl<'local, T: TypeArray> AsRef<AutoArray<'local, T>> for AutoArray<'local, T> {
fn as_ref(&self) -> &AutoArray<'local, T> {
impl<'a, 'local, 'other_local, T: TypeArray> AsRef<AutoArray<'a, 'local, 'other_local, T>>
for AutoArray<'a, 'local, 'other_local, T>
{
fn as_ref(&self) -> &AutoArray<'a, 'local, 'other_local, T> {
self
}
}

/*
impl<'local, T: TypeArray> AsRef<JObject<'local>> for AutoArray<'local, T> {
fn as_ref(&self) -> &JObject<'local> {
&self.obj
}
}
*/

impl<'local, T: TypeArray> Drop for AutoArray<'local, T> {
impl<'a, 'local: 'a, 'other_local: 'a, T: TypeArray + 'other_local> Drop
for AutoArray<'a, 'local, 'other_local, T>
{
fn drop(&mut self) {
// Safety: `self.mode` is valid and the array has not yet been released.
let res = unsafe { self.release_array_elements(self.mode as i32) };
Expand All @@ -189,8 +200,10 @@ impl<'local, T: TypeArray> Drop for AutoArray<'local, T> {
}
}

impl<'local, T: TypeArray> From<&'local AutoArray<'local, T>> for *mut T {
fn from(other: &'local AutoArray<T>) -> *mut T {
impl<'a, 'local: 'a, 'other_local: 'a, T: TypeArray>
From<&'local AutoArray<'a, 'local, 'other_local, T>> for *mut T
{
fn from(other: &'a AutoArray<T>) -> *mut T {
other.as_ptr()
}
}
15 changes: 9 additions & 6 deletions src/wrapper/objects/auto_primitive_array.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use jni_sys::jarray;
use log::debug;

use crate::sys::jsize;
use crate::wrapper::objects::ReleaseMode;
use crate::{errors::*, objects::JObject, JNIEnv};
use crate::{errors::*, JNIEnv};
use std::os::raw::c_void;
use std::ptr::NonNull;

Expand All @@ -14,7 +15,7 @@ use std::ptr::NonNull;
/// AutoPrimitiveArray provides automatic array release through a call to
/// ReleasePrimitiveArrayCritical when it goes out of scope.
pub struct AutoPrimitiveArray<'local: 'env, 'env> {
obj: JObject<'local>,
array: jarray,
ptr: NonNull<c_void>,
mode: ReleaseMode,
is_copy: bool,
Expand All @@ -24,13 +25,13 @@ pub struct AutoPrimitiveArray<'local: 'env, 'env> {
impl<'local, 'env> AutoPrimitiveArray<'local, 'env> {
pub(crate) fn new(
env: &'env mut JNIEnv<'local>,
obj: JObject<'local>,
array: jarray,
ptr: *mut c_void,
mode: ReleaseMode,
is_copy: bool,
) -> Result<Self> {
Ok(AutoPrimitiveArray {
obj,
array,
ptr: NonNull::new(ptr).ok_or(Error::NullPtr("Non-null ptr expected"))?,
mode,
is_copy,
Expand All @@ -47,7 +48,7 @@ impl<'local, 'env> AutoPrimitiveArray<'local, 'env> {
jni_unchecked!(
self.env.get_native_interface(),
ReleasePrimitiveArrayCritical,
*self.obj,
self.array,
self.ptr.as_mut(),
mode
);
Expand All @@ -69,7 +70,7 @@ impl<'local, 'env> AutoPrimitiveArray<'local, 'env> {

/// Returns the array size
pub fn size(&self) -> Result<jsize> {
self.env.get_array_length(*self.obj)
self.env.get_array_length(self.array)
}
}

Expand All @@ -79,11 +80,13 @@ impl<'local, 'env> AsRef<AutoPrimitiveArray<'local, 'env>> for AutoPrimitiveArra
}
}

/*
impl<'local, 'env> AsRef<JObject<'local>> for AutoPrimitiveArray<'local, 'env> {
fn as_ref(&self) -> &JObject<'local> {
&self.obj
}
}
*/

impl<'local, 'env> Drop for AutoPrimitiveArray<'local, 'env> {
fn drop(&mut self) {
Expand Down
12 changes: 11 additions & 1 deletion src/wrapper/objects/jclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,21 @@ impl<'local> JClass<'local> {
///
/// # Safety
///
/// Expects a valid pointer or `null`
/// `raw` may be a null pointer. If `raw` is not a null pointer, then:
///
/// * `raw` must be a valid raw JNI local reference.
/// * There must not be any other `JObject` representing the same local reference.
/// * The lifetime `'local` must not outlive the local reference frame that the local reference
/// was created in.
pub unsafe fn from_raw(raw: jclass) -> Self {
Self(JObject::from_raw(raw as jobject))
}

/// Returns the raw JNI pointer.
pub fn as_raw(&self) -> jclass {
self.0.as_raw() as jclass
}

/// Unwrap to the raw jni type.
pub fn into_raw(self) -> jclass {
self.0.into_raw() as jclass
Expand Down
11 changes: 6 additions & 5 deletions src/wrapper/objects/jobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use crate::sys::jobject;
#[cfg(doc)]
use crate::{objects::GlobalRef, JNIEnv};

/// Wrapper around `sys::jobject` that adds a lifetime. This prevents it from
/// outliving the context in which it was acquired and getting GC'd out from
/// under us. It matches C's representation of the raw pointer, so it can be
/// used in any of the extern function argument positions that would take a
/// `jobject`.
/// Wrapper around [`sys::jobject`] that adds a lifetime to ensure that
/// the underlying JNI pointer won't be accessible to safe Rust code if the
/// object reference is released.
///
/// It matches C's representation of the raw pointer, so it can be used in any
/// of the extern function argument positions that would take a `jobject`.
///
/// Most other types in the `objects` module deref to this, as they do in the C
/// representation.
Expand Down
82 changes: 82 additions & 0 deletions src/wrapper/objects/jobject_array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use crate::{
objects::JObject,
sys::{jobject, jobjectArray},
};

/// Lifetime'd representation of a [`jobjectArray`] which wraps a [`JObject`] reference
#[repr(transparent)]
#[derive(Debug)]
pub struct JObjectArray<'local>(JObject<'local>);

impl<'local> AsRef<JObjectArray<'local>> for JObjectArray<'local> {
fn as_ref(&self) -> &JObjectArray<'local> {
self
}
}

impl<'local> AsRef<JObject<'local>> for JObjectArray<'local> {
fn as_ref(&self) -> &JObject<'local> {
self
}
}

impl<'local> ::std::ops::Deref for JObjectArray<'local> {
type Target = JObject<'local>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<'local> From<JObjectArray<'local>> for JObject<'local> {
fn from(other: JObjectArray) -> JObject {
other.0
}
}

/// This conversion assumes that the `JObject` is a pointer to a class object.
impl<'local> From<JObject<'local>> for JObjectArray<'local> {
fn from(other: JObject) -> Self {
unsafe { Self::from_raw(other.into_raw()) }
}
}

/// This conversion assumes that the `JObject` is a pointer to a class object.
impl<'local, 'obj_ref> From<&'obj_ref JObject<'local>> for &'obj_ref JObjectArray<'local> {
fn from(other: &'obj_ref JObject<'local>) -> Self {
// Safety: `JObjectArray` is `repr(transparent)` around `JObject`.
unsafe { &*(other as *const JObject<'local> as *const JObjectArray<'local>) }
}
}

impl<'local> std::default::Default for JObjectArray<'local> {
fn default() -> Self {
Self(JObject::null())
}
}

impl<'local> JObjectArray<'local> {
/// Creates a [`JObjectArray`] that wraps the given `raw` [`jclass`]
///
/// # Safety
///
/// `raw` may be a null pointer. If `raw` is not a null pointer, then:
///
/// * `raw` must be a valid raw JNI local reference.
/// * There must not be any other `JObject` representing the same local reference.
/// * The lifetime `'local` must not outlive the local reference frame that the local reference
/// was created in.
pub unsafe fn from_raw(raw: jobjectArray) -> Self {
Self(JObject::from_raw(raw as jobject))
}

/// Returns the raw JNI pointer.
pub fn as_raw(&self) -> jobjectArray {
self.0.as_raw() as jobjectArray
}

/// Unwrap to the raw jni type.
pub fn into_raw(self) -> jobjectArray {
self.0.into_raw() as jobjectArray
}
}
12 changes: 11 additions & 1 deletion src/wrapper/objects/jstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,21 @@ impl<'local> JString<'local> {
///
/// # Safety
///
/// Expects a valid pointer or `null`
/// `raw` may be a null pointer. If `raw` is not a null pointer, then:
///
/// * `raw` must be a valid raw JNI local reference.
/// * There must not be any other `JObject` representing the same local reference.
/// * The lifetime `'local` must not outlive the local reference frame that the local reference
/// was created in.
pub unsafe fn from_raw(raw: jstring) -> Self {
Self(JObject::from_raw(raw as jobject))
}

/// Returns the raw JNI pointer.
pub fn as_raw(&self) -> jstring {
self.0.as_raw() as jstring
}

/// Unwrap to the raw jni type.
pub fn into_raw(self) -> jstring {
self.0.into_raw() as jstring
Expand Down
Loading

0 comments on commit b3ce652

Please sign in to comment.