use std::{
convert::TryInto,
marker::PhantomData,
os::raw::{c_char, c_void},
panic::{AssertUnwindSafe, catch_unwind, resume_unwind},
ptr,
sync::{Mutex, MutexGuard},
};
use jni_sys::jobject;
use crate::{
DEFAULT_LOCAL_FRAME_CAPACITY, JNIVersion, JavaVM,
descriptors::Desc,
errors::*,
jni_sig,
objects::{
Auto, AutoElements, AutoElementsCritical, Global, IntoAuto, JByteBuffer, JClass,
JClassLoader, JFieldID, JList, JMap, JMethodID, JObject, JStaticFieldID, JStaticMethodID,
JString, JThrowable, JValue, JValueOwned, ReleaseMode, TypeArray, Weak,
},
signature::{FieldSignature, JavaType, MethodSignature, Primitive},
strings::{JNIStr, MUTF8Chars},
sys::{
self, JNINativeMethod, jboolean, jbyte, jchar, jdouble, jfloat, jint, jlong, jshort, jsize,
jvalue,
},
};
use crate::{
errors::ErrorPolicy,
objects::{
Cast, JBooleanArray, JByteArray, JCharArray, JDoubleArray, JFloatArray, JIntArray,
JLongArray, JObjectArray, JPrimitiveArray, JShortArray, LoaderContext,
type_array_sealed::TypeArraySealed,
},
};
use crate::{objects::AsJArrayRaw, signature::ReturnType};
use super::{AttachGuard, objects::Reference};
#[cfg(doc)]
use crate::{jni_str, objects::JThread, strings::JNIString};
#[derive(Debug)]
pub struct Env<'local> {
pub(crate) raw: *mut sys::JNIEnv,
pub(crate) level: usize,
owns_attachment: bool,
_lifetime: PhantomData<&'local ()>,
}
impl Drop for Env<'_> {
fn drop(&mut self) {
}
}
#[cfg(test)]
mod test {
use crate::Env;
static_assertions::assert_not_impl_any!(Env: Send);
static_assertions::assert_not_impl_any!(Env: Sync);
}
impl<'local> Env<'local> {
#[allow(unused)]
fn ensure_version(&self, version: JNIVersion) -> Result<()> {
if self.version()? < version {
Err(Error::UnsupportedVersion)
} else {
Ok(())
}
}
pub(crate) unsafe fn new(raw: *mut sys::JNIEnv, level: usize, owns_attachment: bool) -> Self {
let env = Env {
raw,
level,
owns_attachment,
_lifetime: PhantomData,
};
env.assert_top();
env
}
#[inline]
pub fn assert_top(&self) {
assert_eq!(
self.level,
JavaVM::thread_attach_guard_level(),
r#" jni runtime check failure: attempt to create a new local reference using an Env<'not_top> that is not associated with the top JNI stack frame.
This implies that there is more than one mutable Env in scope.
This should only be possible by misusing APIs like JavaVM::attach_current_thread or Env::with_local_frame to materialize a mutable Env when there is already one in scope.
To fix this, ensure that you only ever have one mutable Env in scope at a time, which is associated with the top JNI stack frame.
See the jni-rs Env documentation for more details.
"#
);
}
pub fn owns_attachment(&self) -> bool {
self.owns_attachment
}
pub fn get_raw(&self) -> *mut sys::JNIEnv {
self.raw
}
pub fn version(&self) -> Result<JNIVersion> {
Ok(JNIVersion::from(unsafe {
jni_call_no_post_check_ex!(self, v1_1, GetVersion)?
}))
}
#[doc(hidden)]
#[deprecated(since = "0.22.0", note = "Renamed to `version` instead")]
pub fn get_version(&self) -> Result<JNIVersion> {
self.version()
}
unsafe fn define_class_impl(
&mut self,
name: *const c_char,
loader: &JClassLoader,
buf: *const jbyte,
buf_len: usize,
) -> Result<JClass<'local>> {
if buf_len > jsize::MAX as usize {
return Err(Error::JniCall(JniError::InvalidArguments));
}
self.assert_top();
unsafe {
jni_call_with_catch_and_null_check!(
catch |env| {
crate::exceptions::JClassFormatError =>
Err(Error::ClassFormatError),
crate::exceptions::JClassCircularityError =>
Err(Error::ClassCircularityError),
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
crate::exceptions::JSecurityException =>
Err(Error::SecurityViolation),
else => Err(Error::NullPtr("Unexpected Exception")),
},
self,
v1_1,
DefineClass,
name,
loader.as_raw(),
buf,
buf_len as jsize
)
.map(|class| JClass::from_raw(self, class))
}
}
pub fn define_class<'any_loader, N, L>(
&mut self,
name: Option<N>,
loader: L,
buf: &[u8],
) -> Result<JClass<'local>>
where
N: AsRef<JNIStr>,
L: AsRef<JClassLoader<'any_loader>>,
{
let name: Option<&JNIStr> = name.as_ref().map(|n| n.as_ref());
let name = name.map_or(ptr::null(), |n| n.as_ptr());
unsafe {
self.define_class_impl(
name,
loader.as_ref(),
buf.as_ptr() as *const jbyte,
buf.len(),
)
}
}
pub fn define_class_jbyte<'any_loader, N, L>(
&mut self,
name: Option<N>,
loader: L,
buf: &[jbyte],
) -> Result<JClass<'local>>
where
N: AsRef<JNIStr>,
L: AsRef<JClassLoader<'any_loader>>,
{
let name: Option<&JNIStr> = name.as_ref().map(|n| n.as_ref());
let name = name.as_ref().map_or(ptr::null(), |n| n.as_ptr());
unsafe {
self.define_class_impl(
name,
loader.as_ref(),
buf.as_ptr() as *const jbyte,
buf.len(),
)
}
}
pub fn find_class<S>(&mut self, name: S) -> Result<JClass<'local>>
where
S: AsRef<JNIStr>,
{
self.assert_top();
let name = name.as_ref();
unsafe {
jni_call_with_catch_and_null_check!(
catch |env| {
e: crate::exceptions::JNoClassDefFoundError => {
let cause: &JThrowable = &e.as_throwable();
let cause = env.new_global_ref(cause).ok();
Err(Error::NoClassDefFound {
requested: name.to_string(),
cause
})
},
e: crate::exceptions::JClassNotFoundException => {
let cause: &JThrowable = &e.as_throwable();
let cause = env.new_global_ref(cause).ok();
Err(Error::NoClassDefFound {
requested: name.to_string(),
cause
})
},
crate::exceptions::JClassFormatError =>
Err(Error::ClassFormatError),
crate::exceptions::JClassCircularityError =>
Err(Error::ClassCircularityError),
e: crate::exceptions::JLinkageError => {
let cause: &JThrowable = &e.as_throwable();
let cause = env.new_global_ref(cause).ok();
Err(Error::LinkageError {
requested: name.to_string(),
cause
})
},
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::NullPtr("Unexpected exception")),
},
self, v1_1, FindClass, name.as_ptr())
.map(|class| JClass::from_raw(self, class))
}
}
pub fn load_class<S>(&mut self, name: S) -> Result<JClass<'local>>
where
S: AsRef<JNIStr>,
{
let name = name.as_ref();
LoaderContext::None.load_class(self, name, false)
}
pub fn get_superclass<'other_local, T>(&mut self, class: T) -> Result<Option<JClass<'local>>>
where
T: Desc<'local, JClass<'other_local>>,
{
self.assert_top();
let class = class.lookup(self)?;
let superclass = unsafe {
JClass::from_raw(
self,
jni_call_no_post_check_ex!(self, v1_1, GetSuperclass, class.as_ref().as_raw())?,
)
};
Ok((!superclass.is_null()).then_some(superclass))
}
fn is_assignable_from_class(&self, class1: &JClass, class2: &JClass) -> Result<bool> {
let class1 = null_check!(class1, "is_assignable_from class1")?;
let class2 = null_check!(class2, "is_assignable_from class2")?;
unsafe {
Ok(jni_call_no_post_check_ex!(
self,
v1_1,
IsAssignableFrom,
class1.as_raw(), class2.as_raw() )?)
}
}
pub fn is_assignable_from<'other_local_1, 'other_local_2, T, U>(
&mut self,
class1: T,
class2: U,
) -> Result<bool>
where
T: Desc<'local, JClass<'other_local_1>>,
U: Desc<'local, JClass<'other_local_2>>,
{
let class1 = class1.lookup(self)?;
let class1 = null_check!(class1.as_ref(), "is_assignable_from class1")?;
let class2 = class2.lookup(self)?;
let class2 = null_check!(class2.as_ref(), "is_assignable_from class2")?;
self.is_assignable_from_class(class1, class2)
}
pub(crate) fn is_instance_of_cast_type<To: Reference>(&self, obj: &JObject) -> Result<bool> {
let class = match To::lookup_class(self, &LoaderContext::FromObject(obj)) {
Ok(class) => class,
Err(Error::ClassNotFound { name: _ }) => return Ok(false),
Err(e) => return Err(e),
};
let class: &JClass = &class;
self.is_instance_of_class(obj, class)
}
pub(crate) fn is_instance_of_class<'other_local_1, 'other_local_2, O, C>(
&self,
object: O,
class: C,
) -> Result<bool>
where
O: AsRef<JObject<'other_local_1>>,
C: AsRef<JClass<'other_local_2>>,
{
let class = null_check!(class.as_ref(), "is_instance_of class")?;
unsafe {
Ok(jni_call_no_post_check_ex!(
self,
v1_1,
IsInstanceOf,
object.as_ref().as_raw(), class.as_raw() )?)
}
}
pub fn is_instance_of<'other_local_1, 'other_local_2, O, T>(
&mut self,
object: O,
class: T,
) -> Result<bool>
where
O: AsRef<JObject<'other_local_1>>,
T: Desc<'local, JClass<'other_local_2>>,
{
let class = class.lookup(self)?;
self.is_instance_of_class(object, class)
}
pub fn is_same_object<'other_local_1, 'other_local_2, O, T>(
&self,
ref1: O,
ref2: T,
) -> Result<bool>
where
O: AsRef<JObject<'other_local_1>>,
T: AsRef<JObject<'other_local_2>>,
{
unsafe {
jni_call_no_post_check_ex!(
self,
v1_1,
IsSameObject,
ref1.as_ref().as_raw(), ref2.as_ref().as_raw() )
}
}
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 =
unsafe { jni_call_no_post_check_ex!(self, v1_1, Throw, throwable.as_ref().as_raw())? };
drop(throwable);
if res == 0 {
Err(Error::JavaException)
} else {
Err(Error::ThrowFailed(res))
}
}
fn throw_new_optional(&self, class: &JClass, msg: Option<&JNIStr>) -> Result<()> {
let throwable_class = JThrowable::lookup_class(self, &LoaderContext::None)?;
let throwable_class: &JClass = &throwable_class;
if !self.is_assignable_from_class(class.as_ref(), throwable_class)? {
return Err(Error::WrongObjectType);
}
let msg = msg.as_ref().map(|m| m.as_ref());
let res: i32 = unsafe {
jni_call_no_post_check_ex!(
self,
v1_1,
ThrowNew,
class.as_raw(),
msg.map(|m| m.as_ptr()).unwrap_or(std::ptr::null())
)?
};
if res == 0 {
Err(Error::JavaException)
} else {
Err(Error::ThrowFailed(res))
}
}
pub fn throw_new<'other_local, S, T>(&mut self, class: T, msg: S) -> Result<()>
where
S: AsRef<JNIStr>,
T: Desc<'local, JClass<'other_local>>,
{
let class = class.lookup(self)?;
let msg: &JNIStr = msg.as_ref();
self.throw_new_optional(class.as_ref(), Some(msg))
}
pub fn throw_new_void<'other_local, T>(&mut self, class: T) -> Result<()>
where
T: Desc<'local, JClass<'other_local>>,
{
let class = class.lookup(self)?;
self.throw_new_optional(class.as_ref(), None)
}
#[inline]
pub fn exception_check(&self) -> bool {
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_2, ExceptionCheck) }
}
pub fn exception_occurred(&mut self) -> Option<JThrowable<'local>> {
self.assert_top();
let throwable =
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_1, ExceptionOccurred) };
if throwable.is_null() {
None
} else {
Some(unsafe { JThrowable::from_raw(self, throwable) })
}
}
pub fn exception_describe(&self) {
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_1, ExceptionDescribe) };
}
pub fn exception_clear(&self) {
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_1, ExceptionClear) };
}
pub fn exception_catch(&self) -> Result<()> {
fn try_get_stack_trace(
env: &mut jni::Env<'_>,
throwable: &jni::objects::JThrowable,
) -> jni::errors::Result<String> {
let stack_trace = throwable.get_stack_trace(env)?;
let len = stack_trace.len(env)?;
let mut trace = String::new();
for i in 0..len {
let element = stack_trace.get_element(env, i)?;
let element_jstr = element.try_to_string(env)?;
trace.push_str(&format!("{i}: {element_jstr}\n"));
}
Ok(trace)
}
if self.exception_check() {
let result = self.with_local_frame(
DEFAULT_LOCAL_FRAME_CAPACITY,
|env| -> jni::errors::Result<jni::errors::Error> {
let Some(e) = env.exception_occurred() else {
unreachable!(
"ExceptionOccurred returned None after ExceptionCheck returned true"
);
};
env.exception_clear();
assert!(!e.is_null(), "ExceptionOccurred returned a null throwable");
let e = env.new_global_ref(e)?;
let name = env.get_object_class(&e)?.get_name(env)?.to_string();
let msg = e.get_message(env)?.to_string();
let stack = match try_get_stack_trace(env, &e) {
Ok(stack_trace) => stack_trace,
Err(err) => format!("none: {err:?}"),
};
Ok(jni::errors::Error::CaughtJavaException {
exception: e,
name,
msg,
stack,
})
},
);
match result {
Ok(exception) => Err(exception),
Err(err) => Err(jni::errors::Error::CaughtJavaException {
exception: Default::default(),
name: "UNKNOWN".to_string(),
msg: format!("Failed to query JThrowable: {err:?})"),
stack: "none".to_string(),
}),
}
} else {
Ok(())
}
}
pub fn fatal_error(&self, msg: &JNIStr) -> ! {
self.exception_clear();
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_1, FatalError, msg.as_ptr()) }
}
pub unsafe fn new_direct_byte_buffer(
&mut self,
data: *mut u8,
len: usize,
) -> Result<JByteBuffer<'local>> {
self.assert_top();
let data = null_check!(data, "new_direct_byte_buffer data argument")?;
unsafe {
let obj = jni_call_with_catch_and_null_check!(
catch |env| {
crate::exceptions::JIllegalArgumentException =>
Err(Error::JniCall(JniError::InvalidArguments)),
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::NullPtr("Unexpected Exception")),
},
self,
v1_4,
NewDirectByteBuffer,
data as *mut c_void,
len as jlong
)?;
Ok(JByteBuffer::from_raw(self, obj))
}
}
pub fn get_direct_buffer_address(&self, buf: &JByteBuffer) -> Result<*mut u8> {
let buf = null_check!(buf, "get_direct_buffer_address argument")?;
unsafe {
let ptr =
jni_call_only_check_null_ret!(self, v1_4, GetDirectBufferAddress, buf.as_raw())?;
Ok(ptr as _)
}
}
pub fn get_direct_buffer_capacity(&self, buf: &JByteBuffer) -> Result<usize> {
let buf = null_check!(buf, "get_direct_buffer_capacity argument")?;
unsafe {
let capacity =
jni_call_no_post_check_ex!(self, v1_4, GetDirectBufferCapacity, buf.as_raw())?;
match capacity {
-1 => Err(Error::JniCall(JniError::Unknown)),
_ => Ok(capacity as usize),
}
}
}
pub fn new_global_ref<'any_local, O>(&self, obj: O) -> Result<Global<O::GlobalKind>>
where
O: Reference + AsRef<JObject<'any_local>>,
{
if obj.is_null() {
return Ok(Global::null());
}
let global_ref = unsafe {
let global_ref = O::global_kind_from_raw(jni_call_no_post_check_ex!(
self,
v1_1,
NewGlobalRef,
obj.as_raw()
)?);
Global::new(self, global_ref)
};
if global_ref.is_null() {
if self.is_same_object(obj, JObject::null())? {
Err(Error::ObjectFreed)
} else {
Err(Error::JniCall(JniError::NoMemory))
}
} else {
Ok(global_ref)
}
}
pub fn new_cast_global_ref<'any_local, To>(
&self,
obj: impl Reference + AsRef<JObject<'any_local>>,
) -> Result<Global<To::GlobalKind>>
where
To: Reference,
{
if obj.is_null() {
return Ok(Default::default());
}
if self.is_instance_of_cast_type::<To>(obj.as_ref())? {
let new = self.new_global_ref(obj)?;
unsafe {
let cast = To::global_kind_from_raw(new.into_raw());
Ok(Global::new(self, cast))
}
} else {
Err(Error::WrongObjectType)
}
}
pub unsafe fn global_from_raw<T: Reference>(&self, raw: sys::jobject) -> Global<T::GlobalKind> {
unsafe { Global::new(self, T::global_kind_from_raw(raw)) }
}
pub fn cast_global<To>(
&self,
obj: Global<
impl Into<JObject<'static>>
+ AsRef<JObject<'static>>
+ Default
+ Reference
+ Send
+ Sync
+ 'static,
>,
) -> Result<Global<To::GlobalKind>>
where
To: Reference,
{
if obj.is_null() {
return Ok(Default::default());
}
if self.is_instance_of_cast_type::<To>(obj.as_ref())? {
unsafe {
let cast = To::global_kind_from_raw(obj.into_raw());
Ok(Global::new(self, cast))
}
} else {
Err(Error::WrongObjectType)
}
}
pub fn new_weak_ref<O>(&self, obj: O) -> Result<Weak<O::GlobalKind>>
where
O: Reference,
{
if obj.is_null() {
return Err(Error::ObjectFreed);
}
let weak_ref = unsafe {
let weak = O::global_kind_from_raw(jni_call_with_catch!(
catch |env| {
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_2,
NewWeakGlobalRef,
obj.as_raw()
)?);
Weak::new(self, weak)
};
if weak_ref.is_null() {
Err(Error::ObjectFreed)
} else {
Ok(weak_ref)
}
}
pub unsafe fn weak_from_raw<T: Reference>(&self, raw: sys::jobject) -> Weak<T::GlobalKind> {
unsafe { Weak::new(self, T::global_kind_from_raw(raw)) }
}
pub fn new_local_ref<'any_local, O>(&mut self, obj: O) -> Result<O::Kind<'local>>
where
O: Reference + AsRef<JObject<'any_local>>,
{
self.assert_top();
if obj.is_null() {
return Ok(O::null());
}
let local = unsafe {
O::kind_from_raw(jni_call_no_post_check_ex!(
self,
v1_2,
NewLocalRef,
obj.as_raw()
)?)
};
if local.is_null() {
if self.is_same_object(obj, JObject::null())? {
Err(Error::ObjectFreed)
} else {
Err(Error::JniCall(JniError::NoMemory))
}
} else {
Ok(local)
}
}
pub fn new_cast_local_ref<'any_local, To>(
&mut self,
obj: impl Reference + AsRef<JObject<'any_local>>,
) -> Result<To::Kind<'local>>
where
To: Reference,
{
if obj.is_null() {
return Ok(To::null());
}
if self.is_instance_of_cast_type::<To>(obj.as_ref())? {
let new = self.new_local_ref(obj.as_ref())?;
unsafe { Ok(To::kind_from_raw::<'local>(new.into_raw())) }
} else {
Err(Error::WrongObjectType)
}
}
pub fn cast_local<'any_local, To>(
&self,
obj: impl Reference + Into<JObject<'any_local>> + AsRef<JObject<'any_local>>,
) -> Result<To::Kind<'any_local>>
where
To: Reference,
{
if obj.is_null() {
return Ok(To::null::<'any_local>());
}
if self.is_instance_of_cast_type::<To>(obj.as_ref())? {
let obj: JObject = obj.into();
unsafe { Ok(To::kind_from_raw::<'any_local>(obj.into_raw())) }
} else {
Err(Error::WrongObjectType)
}
}
pub fn as_cast<'from, 'any, To>(
&self,
obj: &'from (impl Reference + AsRef<JObject<'any>>),
) -> Result<Cast<'from, 'any, To>>
where
To: Reference,
'any: 'from,
{
Cast::new(self, obj)
}
pub unsafe fn as_cast_unchecked<'from, 'any, To>(
&self,
obj: &'from (impl Reference + AsRef<JObject<'any>>),
) -> Cast<'from, 'any, To>
where
To: Reference,
'any: 'from,
{
unsafe { Cast::new_unchecked(obj) }
}
pub unsafe fn as_cast_raw<'from, To>(
&self,
from: &'from jobject,
) -> Result<Cast<'from, 'from, To>>
where
To: Reference,
{
unsafe { Cast::from_raw(self, from) }
}
#[deprecated = "Use '.auto()' from IntoAuto trait"]
pub fn auto_local<O>(&self, obj: O) -> Auto<'local, O>
where
O: Into<JObject<'local>>,
{
Auto::new(obj)
}
pub fn delete_local_ref<'other_local, O>(&self, obj: O)
where
O: Into<JObject<'other_local>>,
{
let obj = obj.into();
let raw = obj.into_raw();
unsafe {
ex_safe_jni_call_no_post_check_ex!(self, v1_1, DeleteLocalRef, raw);
}
}
unsafe fn push_local_frame(&self, capacity: i32) -> Result<()> {
let res =
unsafe { ex_safe_jni_call_no_post_check_ex!(self, v1_2, PushLocalFrame, capacity) };
jni_error_code_to_result(res)
}
unsafe fn pop_local_frame<'frame_local, T: Reference>(
&self,
result: T::Kind<'frame_local>,
) -> Result<T::Kind<'local>> {
let result: JObject<'frame_local> = result.into();
unsafe {
let raw =
ex_safe_jni_call_no_post_check_ex!(self, v1_2, PopLocalFrame, result.into_raw());
Ok(T::kind_from_raw(raw))
}
}
pub fn with_local_frame<F, T, E>(&self, capacity: usize, f: F) -> std::result::Result<T, E>
where
F: FnOnce(&mut Env) -> std::result::Result<T, E>,
E: From<Error>,
{
let capacity: jni_sys::jint = capacity
.try_into()
.map_err(|_| Error::JniCall(JniError::InvalidArguments))?;
unsafe {
let mut guard = AttachGuard::from_unowned(self.get_raw());
let env = guard.borrow_env_mut();
self.push_local_frame(capacity)?;
let ret = catch_unwind(AssertUnwindSafe(|| f(env)));
self.pop_local_frame::<JObject>(JObject::null())?;
drop(guard);
match ret {
Ok(ret) => ret,
Err(payload) => {
resume_unwind(payload);
}
}
}
}
pub fn with_local_frame_returning_local<F, T, E>(
&mut self,
capacity: usize,
f: F,
) -> std::result::Result<T::Kind<'local>, E>
where
F: for<'new_local> FnOnce(
&mut Env<'new_local>,
) -> std::result::Result<T::Kind<'new_local>, E>,
T: Reference,
E: From<Error>,
{
self.assert_top();
let capacity: jni_sys::jint = capacity
.try_into()
.map_err(|_| Error::JniCall(JniError::InvalidArguments))?;
unsafe {
let panic_payload = {
let mut guard = AttachGuard::from_unowned(self.get_raw());
let env = guard.borrow_env_mut();
self.push_local_frame(capacity)?;
match catch_unwind(AssertUnwindSafe(|| f(env))) {
Ok(Ok(obj)) => {
let obj = self.pop_local_frame::<T>(obj)?;
return Ok(obj);
}
Ok(Err(err)) => {
self.pop_local_frame::<T>(T::null())?;
return Err(err);
}
Err(payload) => {
self.pop_local_frame::<T>(T::null())?;
payload
}
}
};
resume_unwind(panic_payload);
}
}
pub fn with_top_local_frame<F, T, E>(&self, f: F) -> std::result::Result<T, E>
where
F: FnOnce(&mut Env) -> std::result::Result<T, E>,
E: From<Error>,
{
unsafe {
let mut guard = AttachGuard::from_unowned(self.get_raw());
f(guard.borrow_env_mut())
}
}
pub fn alloc_object<'other_local, T>(&mut self, class: T) -> Result<JObject<'local>>
where
T: Desc<'local, JClass<'other_local>>,
{
self.assert_top();
let class = class.lookup(self)?;
let obj = unsafe {
jni_call_with_catch_and_null_check!(
catch |env| {
crate::exceptions::JInstantiationException =>
Err(Error::Instantiation),
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::NullPtr("Unexpected Exception")),
},
self, v1_1, AllocObject, class.as_ref().as_raw())?
};
drop(class);
Ok(unsafe { JObject::from_raw(self, obj) })
}
pub fn get_method_id<'other_local, 'sig, 'sig_args, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
) -> Result<JMethodID>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
let class = class.lookup(self)?;
let ffi_name = name.as_ref();
let sig = sig.as_ref();
let method_id = unsafe {
jni_call_with_catch!(
catch |env| {
crate::exceptions::JNoSuchMethodError =>
Err(Error::MethodNotFound {
name: ffi_name.to_string(),
sig: sig.sig().to_string(),
}),
e: crate::exceptions::JExceptionInInitializerError => {
let exception = e.get_exception(env);
let exception = if let Ok(exception) = exception {
env.new_global_ref(exception).ok()
} else {
None
};
Err(Error::ExceptionInInitializer { exception })
},
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
GetMethodID,
class.as_ref().as_raw(),
ffi_name.as_ptr(),
sig.as_ref().sig().as_ptr()
)?
};
drop(class);
assert!(
!method_id.is_null(),
"Spurious null method ID returned, without a pending exception"
);
Ok(unsafe { JMethodID::from_raw(method_id) })
}
pub fn get_static_method_id<'other_local, 'sig, 'sig_args, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
) -> Result<JStaticMethodID>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
let class = class.lookup(self)?;
let ffi_name = name.as_ref();
let sig = sig.as_ref();
let method_id = unsafe {
jni_call_with_catch!(
catch |env| {
crate::exceptions::JNoSuchMethodError =>
Err(Error::MethodNotFound {
name: ffi_name.to_string(),
sig: sig.sig().to_string(),
}),
e: crate::exceptions::JExceptionInInitializerError => {
let exception = e.get_exception(env);
let exception = if let Ok(exception) = exception {
env.new_global_ref(exception).ok()
} else {
None
};
Err(Error::ExceptionInInitializer { exception })
},
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
GetStaticMethodID,
class.as_ref().as_raw(),
ffi_name.as_ptr(),
sig.as_ref().sig().as_ptr()
)?
};
drop(class);
assert!(
!method_id.is_null(),
"Spurious null method ID returned, without a pending exception"
);
Ok(unsafe { JStaticMethodID::from_raw(method_id) })
}
pub fn get_field_id<'other_local, 'sig, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
) -> Result<JFieldID>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<FieldSignature<'sig>>,
{
let class = class.lookup(self)?;
let ffi_name = name.as_ref();
let ffi_sig = sig.as_ref();
let field_id = unsafe {
jni_call_with_catch!(
catch |env| {
crate::exceptions::JNoSuchFieldError =>
Err(Error::FieldNotFound {
name: ffi_name.to_string(),
sig: ffi_sig.sig().to_string(),
}),
e: crate::exceptions::JExceptionInInitializerError => {
let exception = e.get_exception(env);
let exception = if let Ok(exception) = exception {
env.new_global_ref(exception).ok()
} else {
None
};
Err(Error::ExceptionInInitializer { exception })
},
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
GetFieldID,
class.as_ref().as_raw(),
ffi_name.as_ptr(),
ffi_sig.sig().as_ptr()
)?
};
drop(class);
assert!(
!field_id.is_null(),
"Spurious null field ID returned with no exception"
);
Ok(unsafe { JFieldID::from_raw(field_id) })
}
pub fn get_static_field_id<'other_local, 'sig, T, U, V>(
&mut self,
class: T,
name: U,
sig: V,
) -> Result<JStaticFieldID>
where
T: Desc<'local, JClass<'other_local>>,
U: AsRef<JNIStr>,
V: AsRef<FieldSignature<'sig>>,
{
let class = class.lookup(self)?;
let ffi_name = name.as_ref();
let ffi_sig = sig.as_ref();
let field_id = unsafe {
jni_call_with_catch!(
catch |env| {
crate::exceptions::JNoSuchFieldError =>
Err(Error::FieldNotFound {
name: ffi_name.to_string(),
sig: ffi_sig.sig().to_string(),
}),
e: crate::exceptions::JExceptionInInitializerError => {
let exception = e.get_exception(env);
let exception = if let Ok(exception) = exception {
env.new_global_ref(exception).ok()
} else {
None
};
Err(Error::ExceptionInInitializer { exception })
},
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
GetStaticFieldID,
class.as_ref().as_raw(),
ffi_name.as_ptr(),
ffi_sig.sig().as_ptr()
)?
};
drop(class);
assert!(
!field_id.is_null(),
"Spurious null field ID returned with no exception"
);
Ok(unsafe { JStaticFieldID::from_raw(field_id) })
}
pub fn get_object_class<'other_local, O>(&mut self, obj: O) -> Result<JClass<'local>>
where
O: AsRef<JObject<'other_local>>,
{
self.assert_top();
let obj = obj.as_ref();
let obj = null_check!(obj, "get_object_class")?;
unsafe {
Ok(JClass::from_raw(
self,
jni_call_no_post_check_ex!(self, v1_1, GetObjectClass, obj.as_raw())?,
))
}
}
pub unsafe fn call_static_method_unchecked<'other_local, T, U>(
&mut self,
class: T,
method_id: U,
ret: ReturnType,
args: &[jvalue],
) -> Result<JValueOwned<'local>>
where
T: Desc<'local, JClass<'other_local>>,
U: Desc<'local, JStaticMethodID>,
{
self.assert_top();
use super::signature::Primitive::{
Boolean, Byte, Char, Double, Float, Int, Long, Short, Void,
};
use JavaType::{Array, Object, Primitive};
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();
macro_rules! invoke {
($call:ident -> $ret:ty) => {{
let o: $ret =
jni_call_post_check_ex!(self, v1_1, $call, class_raw, method_id, jni_args)?;
o
}};
}
let ret = unsafe {
match ret {
Object | Array => {
let obj = invoke!(CallStaticObjectMethodA -> jobject);
let obj = JObject::from_raw(self, obj);
JValueOwned::from(obj)
}
Primitive(Boolean) => invoke!(CallStaticBooleanMethodA -> bool).into(),
Primitive(Char) => invoke!(CallStaticCharMethodA -> u16).into(),
Primitive(Byte) => invoke!(CallStaticByteMethodA -> i8).into(),
Primitive(Short) => invoke!(CallStaticShortMethodA -> i16).into(),
Primitive(Int) => invoke!(CallStaticIntMethodA -> i32).into(),
Primitive(Long) => invoke!(CallStaticLongMethodA -> i64).into(),
Primitive(Float) => invoke!(CallStaticFloatMethodA -> f32).into(),
Primitive(Double) => invoke!(CallStaticDoubleMethodA -> f64).into(),
Primitive(Void) => {
jni_call_post_check_ex!(
self,
v1_1,
CallStaticVoidMethodA,
class_raw,
method_id,
jni_args
)?;
JValueOwned::Void
}
}
};
drop(class);
Ok(ret)
}
pub unsafe fn call_method_unchecked<'other_local, O, T>(
&mut self,
obj: O,
method_id: T,
ret_ty: ReturnType,
args: &[jvalue],
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
T: Desc<'local, JMethodID>,
{
self.assert_top();
use super::signature::Primitive::{
Boolean, Byte, Char, Double, Float, Int, Long, Short, Void,
};
use JavaType::{Array, Object, Primitive};
let method_id = method_id.lookup(self)?.as_ref().into_raw();
let obj = obj.as_ref().as_raw();
let jni_args = args.as_ptr();
macro_rules! invoke {
($call:ident -> $ret:ty) => {{
let o: $ret = jni_call_post_check_ex!(self, v1_1, $call, obj, method_id, jni_args)?;
o
}};
}
let ret = unsafe {
match ret_ty {
Object | Array => {
let obj = invoke!(CallObjectMethodA -> jobject);
let obj = JObject::from_raw(self, obj);
JValueOwned::from(obj)
}
Primitive(Boolean) => invoke!(CallBooleanMethodA -> bool).into(),
Primitive(Char) => invoke!(CallCharMethodA -> u16).into(),
Primitive(Byte) => invoke!(CallByteMethodA -> i8).into(),
Primitive(Short) => invoke!(CallShortMethodA -> i16).into(),
Primitive(Int) => invoke!(CallIntMethodA -> i32).into(),
Primitive(Long) => invoke!(CallLongMethodA -> i64).into(),
Primitive(Float) => invoke!(CallFloatMethodA -> f32).into(),
Primitive(Double) => invoke!(CallDoubleMethodA -> f64).into(),
Primitive(Void) => {
jni_call_post_check_ex!(self, v1_1, CallVoidMethodA, obj, method_id, jni_args)?;
JValueOwned::Void
}
}
};
Ok(ret)
}
pub unsafe fn call_nonvirtual_method_unchecked<'other_local, O, T, U>(
&mut self,
obj: O,
class: T,
method_id: U,
ret_ty: ReturnType,
args: &[jvalue],
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
T: Desc<'local, JClass<'other_local>>,
U: Desc<'local, JMethodID>,
{
self.assert_top();
use super::signature::Primitive::{
Boolean, Byte, Char, Double, Float, Int, Long, Short, Void,
};
use JavaType::{Array, Object, Primitive};
let method_id = method_id.lookup(self)?.as_ref().into_raw();
let class = class.lookup(self)?;
let obj = obj.as_ref().as_raw();
let class_raw = class.as_ref().as_raw();
let jni_args = args.as_ptr();
macro_rules! invoke {
($call:ident -> $ret:ty) => {{
let o: $ret = jni_call_post_check_ex!(
self, v1_1, $call, obj, class_raw, method_id, jni_args
)?;
o
}};
}
let ret = unsafe {
match ret_ty {
Object | Array => {
let obj = invoke!(CallNonvirtualObjectMethodA -> jobject);
let obj = JObject::from_raw(self, obj);
JValueOwned::from(obj)
}
Primitive(Boolean) => invoke!(CallNonvirtualBooleanMethodA -> bool).into(),
Primitive(Char) => invoke!(CallNonvirtualCharMethodA -> u16).into(),
Primitive(Byte) => invoke!(CallNonvirtualByteMethodA -> i8).into(),
Primitive(Short) => invoke!(CallNonvirtualShortMethodA -> i16).into(),
Primitive(Int) => invoke!(CallNonvirtualIntMethodA -> i32).into(),
Primitive(Long) => invoke!(CallNonvirtualLongMethodA -> i64).into(),
Primitive(Float) => invoke!(CallNonvirtualFloatMethodA -> f32).into(),
Primitive(Double) => invoke!(CallNonvirtualDoubleMethodA -> f64).into(),
Primitive(Void) => {
jni_call_post_check_ex!(
self,
v1_1,
CallNonvirtualVoidMethodA,
obj,
class_raw,
method_id,
jni_args
)?;
JValueOwned::Void
}
}
};
Ok(ret)
}
pub fn call_method<'other_local, 'sig, 'sig_args, O, N, S>(
&mut self,
obj: O,
name: N,
sig: S,
args: &[JValue],
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
self.assert_top();
let obj = obj.as_ref();
let obj = null_check!(obj, "call_method obj argument")?;
let sig = sig.as_ref();
if sig.args().len() != args.len() {
return Err(Error::InvalidArgList(sig.into()));
}
let base_types_match = sig
.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(),
});
if !base_types_match {
return Err(Error::InvalidArgList(sig.into()));
}
let class = self.get_object_class(obj)?.auto();
let args: Vec<jvalue> = args.iter().map(|v| v.as_jni()).collect();
unsafe { self.call_method_unchecked(obj, (&class, name, sig), sig.ret(), &args) }
}
pub fn call_static_method<'other_local, 'sig, 'sig_args, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
args: &[JValue],
) -> Result<JValueOwned<'local>>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
self.assert_top();
let sig = sig.as_ref();
if sig.args().len() != args.len() {
return Err(Error::InvalidArgList(sig.into()));
}
let base_types_match = sig
.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(),
});
if !base_types_match {
return Err(Error::InvalidArgList(sig.into()));
}
let class = class.lookup(self)?;
let class = class.as_ref();
let args: Vec<jvalue> = args.iter().map(|v| v.as_jni()).collect();
unsafe { self.call_static_method_unchecked(class, (class, name, sig), sig.ret(), &args) }
}
pub fn call_nonvirtual_method<'other_local, 'sig, 'sig_args, O, C, N, S>(
&mut self,
obj: O,
class: C,
name: N,
sig: S,
args: &[JValue],
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
self.assert_top();
let obj = obj.as_ref();
let obj = null_check!(obj, "call_method obj argument")?;
let sig = sig.as_ref();
if sig.args().len() != args.len() {
return Err(Error::InvalidArgList(sig.into()));
}
let base_types_match = sig
.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(),
});
if !base_types_match {
return Err(Error::InvalidArgList(sig.into()));
}
let class = class.lookup(self)?;
let class = class.as_ref();
let args: Vec<jvalue> = args.iter().map(|v| v.as_jni()).collect();
unsafe {
self.call_nonvirtual_method_unchecked(obj, class, (class, name, sig), sig.ret(), &args)
}
}
pub fn new_object<'other_local, 'sig, 'sig_args, C, S>(
&mut self,
class: C,
ctor_sig: S,
ctor_args: &[JValue],
) -> Result<JObject<'local>>
where
C: Desc<'local, JClass<'other_local>>,
S: AsRef<MethodSignature<'sig, 'sig_args>>,
{
self.assert_top();
let ctor_sig = ctor_sig.as_ref();
if ctor_sig.args().len() != ctor_args.len() {
return Err(Error::InvalidArgList(ctor_sig.into()));
}
let base_types_match = ctor_sig
.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(),
});
if !base_types_match {
return Err(Error::InvalidArgList(ctor_sig.into()));
}
if ctor_sig.ret() != ReturnType::Primitive(Primitive::Void) {
return Err(Error::InvalidCtorReturn);
}
let class = class.lookup(self)?;
let class = class.as_ref();
let method_id: JMethodID = Desc::<JMethodID>::lookup((class, ctor_sig), self)?;
let ctor_args: Vec<jvalue> = ctor_args.iter().map(|v| v.as_jni()).collect();
unsafe { self.new_object_unchecked(class, method_id, &ctor_args) }
}
pub unsafe fn new_object_unchecked<'other_local, C, M>(
&mut self,
class: C,
ctor_id: M,
ctor_args: &[jvalue],
) -> Result<JObject<'local>>
where
C: Desc<'local, JClass<'other_local>>,
M: Desc<'local, JMethodID>,
{
self.assert_top();
let class = class.lookup(self)?;
let ctor_id: JMethodID = *ctor_id.lookup(self)?.as_ref();
let jni_args = ctor_args.as_ptr();
let obj = unsafe {
jni_call_post_check_ex_and_null_ret!(
self,
v1_1,
NewObjectA,
class.as_ref().as_raw(),
ctor_id.into_raw(),
jni_args
)
.map(|obj| JObject::from_raw(self, obj))
}?;
drop(class);
Ok(obj)
}
#[deprecated(
since = "0.22.0",
note = "use JList::cast_local instead or Env::new_cast_local_ref/cast_local/as_cast_local or Env::new_cast_global_ref/cast_global/as_cast_global"
)]
pub fn get_list<'any_local>(
&mut self,
obj: impl Reference + Into<JObject<'any_local>> + AsRef<JObject<'any_local>>,
) -> Result<JList<'any_local>> {
JList::cast_local(self, obj)
}
#[deprecated(
since = "0.22.0",
note = "use JMap::cast_local instead or Env::new_cast_local_ref/cast_local/as_cast_local or Env::new_cast_global_ref/cast_global/as_cast_global"
)]
pub fn get_map<'any_local>(
&mut self,
obj: impl Reference + Into<JObject<'any_local>> + AsRef<JObject<'any_local>>,
) -> Result<JMap<'any_local>> {
JMap::cast_local(self, obj)
}
#[deprecated(
since = "0.22.0",
note = "use JString::mutf8_chars or JString::to_string instead; this method is redundant and does not perform unsafe operations"
)]
pub fn get_string_unchecked<'any_local, StringRef>(
&mut self,
obj: StringRef,
) -> Result<MUTF8Chars<'any_local, StringRef>>
where
StringRef: AsRef<JString<'any_local>> + Reference,
{
MUTF8Chars::from_get_string_utf_chars(self, obj)
}
#[deprecated(
since = "0.22.0",
note = "use JString::mutf8_chars or JString::to_string instead"
)]
pub fn get_string<'any_local, StringRef>(
&self,
obj: StringRef,
) -> Result<MUTF8Chars<'any_local, StringRef>>
where
StringRef: AsRef<JString<'any_local>> + Reference,
{
MUTF8Chars::from_get_string_utf_chars(self, obj)
}
pub fn new_string(&mut self, from: impl AsRef<str>) -> Result<JString<'local>> {
JString::new(self, from)
}
#[deprecated(
since = "0.22.0",
note = "use JPrimitiveArray::len or JObjectArray::len instead. This method will be removed in a future version"
)]
pub fn get_array_length<'other_local, 'array>(
&self,
array: &'array impl AsJArrayRaw<'other_local>,
) -> Result<jsize> {
let array = null_check!(array.as_jarray_raw(), "get_array_length array argument")?;
let len: jsize = unsafe { jni_call_no_post_check_ex!(self, v1_1, GetArrayLength, array)? };
Ok(len)
}
pub fn new_object_array<'other_local_1, 'other_local_2, T, U>(
&mut self,
length: jsize,
element_class: T,
initial_element: U,
) -> Result<JObjectArray<'local>>
where
T: Desc<'local, JClass<'other_local_2>>,
U: AsRef<JObject<'other_local_1>>,
{
self.assert_top();
let class = element_class.lookup(self)?;
let array = unsafe {
jni_call_post_check_ex_and_null_ret!(
self,
v1_1,
NewObjectArray,
length,
class.as_ref().as_raw(),
initial_element.as_ref().as_raw()
)
.map(|array| JObjectArray::<JObject>::from_raw(self, array))?
};
drop(class);
Ok(array)
}
pub fn new_object_type_array<'any_local, E>(
&mut self,
length: usize,
initial_element: impl AsRef<E::Kind<'any_local>>,
) -> Result<JObjectArray<'local, E::Kind<'local>>>
where
E: Reference,
{
JObjectArray::<E>::new(self, length, initial_element)
}
#[deprecated(
since = "0.22.0",
note = "use JObjectArray::get_element instead. This method will be removed in a future version"
)]
pub fn get_object_array_element<'other_local, E: Reference + 'other_local>(
&mut self,
array: impl AsRef<JObjectArray<'other_local, E>>,
index: usize,
) -> Result<E::Kind<'local>> {
array.as_ref().get_element(self, index)
}
#[deprecated(
since = "0.22.0",
note = "use JObjectArray::set_element instead. This method will be removed in a future version"
)]
pub fn set_object_array_element<'any_local_1, 'any_local_2, E: Reference + 'any_local_1>(
&self,
array: impl AsRef<JObjectArray<'any_local_1, E>>,
index: usize,
value: impl AsRef<E::Kind<'any_local_2>>,
) -> Result<()> {
array.as_ref().set_element(self, index, value)
}
pub fn byte_array_from_slice(&mut self, buf: &[u8]) -> Result<JByteArray<'local>> {
let bytes = JByteArray::new(self, buf.len())?;
let buf: &[i8] = unsafe { std::mem::transmute(buf) };
bytes.set_region(self, 0, buf)?;
Ok(bytes)
}
pub fn convert_byte_array<'other_local>(
&self,
array: impl AsRef<JByteArray<'other_local>>,
) -> Result<Vec<u8>> {
let array = array.as_ref().as_raw();
let array = null_check!(array, "convert_byte_array array argument")?;
let length = unsafe { jni_call_no_post_check_ex!(self, v1_1, GetArrayLength, array)? };
let mut vec = vec![0u8; length as usize];
unsafe {
jni_call_with_catch!(
catch |env| {
crate::exceptions::JArrayIndexOutOfBoundsException =>
Err(Error::IndexOutOfBounds),
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
GetByteArrayRegion,
array,
0,
length,
vec.as_mut_ptr() as *mut i8
)?;
}
Ok(vec)
}
pub fn new_boolean_array(&mut self, length: usize) -> Result<JBooleanArray<'local>> {
JBooleanArray::new(self, length)
}
pub fn new_byte_array(&mut self, length: usize) -> Result<JByteArray<'local>> {
JByteArray::new(self, length)
}
pub fn new_char_array(&mut self, length: usize) -> Result<JCharArray<'local>> {
JCharArray::new(self, length)
}
pub fn new_short_array(&mut self, length: usize) -> Result<JShortArray<'local>> {
JShortArray::new(self, length)
}
pub fn new_int_array(&mut self, length: usize) -> Result<JIntArray<'local>> {
JIntArray::new(self, length)
}
pub fn new_long_array(&mut self, length: usize) -> Result<JLongArray<'local>> {
JLongArray::new(self, length)
}
pub fn new_float_array(&mut self, length: usize) -> Result<JFloatArray<'local>> {
JFloatArray::new(self, length)
}
pub fn new_double_array(&mut self, length: usize) -> Result<JDoubleArray<'local>> {
JDoubleArray::new(self, length)
}
#[deprecated(
since = "0.22.0",
note = "use JBooleanArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_boolean_array_region<'other_local>(
&self,
array: impl AsRef<JBooleanArray<'other_local>>,
start: jsize,
buf: &mut [jboolean],
) -> Result<()> {
unsafe {
<jboolean as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JByteArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_byte_array_region<'other_local>(
&self,
array: impl AsRef<JByteArray<'other_local>>,
start: jsize,
buf: &mut [jbyte],
) -> Result<()> {
unsafe { <jbyte as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JCharArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_char_array_region<'other_local>(
&self,
array: impl AsRef<JCharArray<'other_local>>,
start: jsize,
buf: &mut [jchar],
) -> Result<()> {
unsafe { <jchar as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JShortArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_short_array_region<'other_local>(
&self,
array: impl AsRef<JShortArray<'other_local>>,
start: jsize,
buf: &mut [jshort],
) -> Result<()> {
unsafe {
<jshort as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JIntArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_int_array_region<'other_local>(
&self,
array: impl AsRef<JIntArray<'other_local>>,
start: jsize,
buf: &mut [jint],
) -> Result<()> {
unsafe { <jint as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JLongArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_long_array_region<'other_local>(
&self,
array: impl AsRef<JLongArray<'other_local>>,
start: jsize,
buf: &mut [jlong],
) -> Result<()> {
unsafe { <jlong as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JFloatArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_float_array_region<'other_local>(
&self,
array: impl AsRef<JFloatArray<'other_local>>,
start: jsize,
buf: &mut [jfloat],
) -> Result<()> {
unsafe {
<jfloat as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JDoubleArray::get_region instead; this method will be removed in a future version"
)]
pub fn get_double_array_region<'other_local>(
&self,
array: impl AsRef<JDoubleArray<'other_local>>,
start: jsize,
buf: &mut [jdouble],
) -> Result<()> {
unsafe {
<jdouble as TypeArraySealed>::get_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JBooleanArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_boolean_array_region<'other_local>(
&self,
array: impl AsRef<JBooleanArray<'other_local>>,
start: jsize,
buf: &[jboolean],
) -> Result<()> {
unsafe {
<jboolean as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JByteArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_byte_array_region<'other_local>(
&self,
array: impl AsRef<JByteArray<'other_local>>,
start: jsize,
buf: &[jbyte],
) -> Result<()> {
unsafe { <jbyte as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JCharArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_char_array_region<'other_local>(
&self,
array: impl AsRef<JCharArray<'other_local>>,
start: jsize,
buf: &[jchar],
) -> Result<()> {
unsafe { <jchar as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JShortArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_short_array_region<'other_local>(
&self,
array: impl AsRef<JShortArray<'other_local>>,
start: jsize,
buf: &[jshort],
) -> Result<()> {
unsafe {
<jshort as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JIntArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_int_array_region<'other_local>(
&self,
array: impl AsRef<JIntArray<'other_local>>,
start: jsize,
buf: &[jint],
) -> Result<()> {
unsafe { <jint as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JLongArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_long_array_region<'other_local>(
&self,
array: impl AsRef<JLongArray<'other_local>>,
start: jsize,
buf: &[jlong],
) -> Result<()> {
unsafe { <jlong as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf) }
}
#[deprecated(
since = "0.22.0",
note = "use JFloatArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_float_array_region<'other_local>(
&self,
array: impl AsRef<JFloatArray<'other_local>>,
start: jsize,
buf: &[jfloat],
) -> Result<()> {
unsafe {
<jfloat as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf)
}
}
#[deprecated(
since = "0.22.0",
note = "use JDoubleArray::set_region instead; this method will be removed in a future version"
)]
pub fn set_double_array_region<'other_local>(
&self,
array: impl AsRef<JDoubleArray<'other_local>>,
start: jsize,
buf: &[jdouble],
) -> Result<()> {
unsafe {
<jdouble as TypeArraySealed>::set_region(self, array.as_ref().as_raw(), start, buf)
}
}
pub fn to_reflected_method<'other_local>(
&mut self,
class: impl Desc<'local, JClass<'other_local>>,
method_id: impl Desc<'local, JMethodID>,
) -> Result<JObject<'local>> {
unsafe { self.to_reflected_method_base(class, method_id, JMethodID::into_raw, false) }
}
pub fn to_reflected_static_method<'other_local>(
&mut self,
class: impl Desc<'local, JClass<'other_local>>,
method_id: impl Desc<'local, JStaticMethodID>,
) -> Result<JObject<'local>> {
unsafe { self.to_reflected_method_base(class, method_id, JStaticMethodID::into_raw, true) }
}
#[allow(clippy::wrong_self_convention)]
unsafe fn to_reflected_method_base<'other_local, M>(
&mut self,
class: impl Desc<'local, JClass<'other_local>>,
method_id: impl Desc<'local, M>,
to_jmethodid: impl FnOnce(M) -> crate::sys::jmethodID,
is_static: bool,
) -> Result<JObject<'local>>
where
M: Copy,
{
self.assert_top();
let class = class.lookup(self)?;
let method_id = to_jmethodid(*method_id.lookup(self)?.as_ref());
unsafe {
jni_call_post_check_ex_and_null_ret!(
self,
v1_2,
ToReflectedMethod,
class.as_ref().as_raw(),
method_id,
is_static
)
.map(|jobject| JObject::from_raw(self, jobject))
}
}
pub unsafe fn get_field_unchecked<'other_local, O, F>(
&mut self,
obj: O,
field: F,
ty: JavaType,
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
F: Desc<'local, JFieldID>,
{
self.assert_top();
use super::signature::Primitive::{
Boolean, Byte, Char, Double, Float, Int, Long, Short, Void,
};
use JavaType::{Array, Object, Primitive};
let obj = obj.as_ref();
let obj = null_check!(obj, "get_field_typed obj argument")?;
let field = field.lookup(self)?.as_ref().into_raw();
let obj = obj.as_raw();
macro_rules! field {
($get_field:ident) => {{
unsafe {
JValueOwned::from(jni_call_no_post_check_ex!(
self, v1_1, $get_field, obj, field
)?)
}
}};
}
match ty {
Object | Array => {
let obj = unsafe {
jni_call_post_check_ex!(self, v1_1, GetObjectField, obj, field)
.map(|obj| JObject::from_raw(self, obj))?
};
Ok(obj.into())
}
Primitive(Char) => Ok(field!(GetCharField)),
Primitive(Boolean) => Ok(field!(GetBooleanField)),
Primitive(Short) => Ok(field!(GetShortField)),
Primitive(Int) => Ok(field!(GetIntField)),
Primitive(Long) => Ok(field!(GetLongField)),
Primitive(Float) => Ok(field!(GetFloatField)),
Primitive(Double) => Ok(field!(GetDoubleField)),
Primitive(Byte) => Ok(field!(GetByteField)),
Primitive(Void) => Err(Error::WrongJValueType("void", "see java field")),
}
}
pub unsafe fn set_field_unchecked<'other_local, O, F>(
&mut self,
obj: O,
field: F,
value: JValue,
) -> Result<()>
where
O: AsRef<JObject<'other_local>>,
F: Desc<'local, JFieldID>,
{
if let JValue::Void = value {
return Err(Error::WrongJValueType("void", "see java field"));
}
let obj = obj.as_ref();
let field = field.lookup(self)?.as_ref().into_raw();
let obj = obj.as_raw();
macro_rules! set_field {
($set_field:ident($val:expr)) => {{ unsafe { jni_call_no_post_check_ex!(self, v1_1, $set_field, obj, field, $val) } }};
}
match value {
JValue::Object(o) => set_field!(SetObjectField(o.as_raw()))?,
JValue::Bool(b) => set_field!(SetBooleanField(b))?,
JValue::Char(c) => set_field!(SetCharField(c))?,
JValue::Short(s) => set_field!(SetShortField(s))?,
JValue::Int(i) => set_field!(SetIntField(i))?,
JValue::Long(l) => set_field!(SetLongField(l))?,
JValue::Float(f) => set_field!(SetFloatField(f))?,
JValue::Double(d) => set_field!(SetDoubleField(d))?,
JValue::Byte(b) => set_field!(SetByteField(b))?,
_ => (),
};
Ok(())
}
pub fn get_field<'other_local, 'sig, O, N, S>(
&mut self,
obj: O,
name: N,
sig: S,
) -> Result<JValueOwned<'local>>
where
O: AsRef<JObject<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<FieldSignature<'sig>>,
{
self.assert_top();
let obj = obj.as_ref();
let obj = null_check!(obj, "get_field obj argument")?;
let class = self.get_object_class(obj)?.auto();
let sig = sig.as_ref();
let field_ty = sig.ty();
let field_id: JFieldID = Desc::<JFieldID>::lookup((&class, name, sig), self)?;
unsafe { self.get_field_unchecked(obj, field_id, field_ty) }
}
pub fn set_field<'other_local, 'sig, O, N, S>(
&mut self,
obj: O,
name: N,
sig: S,
value: JValue,
) -> Result<()>
where
O: AsRef<JObject<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<FieldSignature<'sig>>,
{
let obj = obj.as_ref();
let obj = null_check!(obj, "set_field obj argument")?;
let sig = sig.as_ref();
let field_ty = sig.ty();
if value.java_type() != field_ty {
return Err(Error::WrongJValueType(value.type_name(), "see java field"));
}
let class = self.get_object_class(obj)?.auto();
unsafe { self.set_field_unchecked(obj, (&class, name, sig), value) }
}
pub unsafe fn get_static_field_unchecked<'other_local, C, F>(
&mut self,
class: C,
field: F,
ty: JavaType,
) -> Result<JValueOwned<'local>>
where
C: Desc<'local, JClass<'other_local>>,
F: Desc<'local, JStaticFieldID>,
{
self.assert_top();
use super::signature::Primitive::{
Boolean, Byte, Char, Double, Float, Int, Long, Short, Void,
};
use JavaType::{Array, Object, Primitive};
let class = class.lookup(self)?;
let field = field.lookup(self)?;
macro_rules! field {
($get_field:ident) => {{
unsafe {
jni_call_post_check_ex!(
self,
v1_1,
$get_field,
class.as_ref().as_raw(),
field.as_ref().into_raw()
)?
}
}};
}
let ret = match ty {
Primitive(Void) => Err(Error::WrongJValueType("void", "see java field")),
Object | Array => {
let obj = field!(GetStaticObjectField);
let obj = unsafe { JObject::from_raw(self, obj) };
Ok(JValueOwned::from(obj))
}
Primitive(Boolean) => Ok(field!(GetStaticBooleanField).into()),
Primitive(Char) => Ok(field!(GetStaticCharField).into()),
Primitive(Short) => Ok(field!(GetStaticShortField).into()),
Primitive(Int) => Ok(field!(GetStaticIntField).into()),
Primitive(Long) => Ok(field!(GetStaticLongField).into()),
Primitive(Float) => Ok(field!(GetStaticFloatField).into()),
Primitive(Double) => Ok(field!(GetStaticDoubleField).into()),
Primitive(Byte) => Ok(field!(GetStaticByteField).into()),
};
drop(class);
ret
}
pub unsafe fn set_static_field_unchecked<'other_local, C, F>(
&mut self,
class: C,
field: F,
value: JValue,
) -> Result<()>
where
C: Desc<'local, JClass<'other_local>>,
F: Desc<'local, JStaticFieldID>,
{
let class = class.lookup(self)?;
let field = field.lookup(self)?;
macro_rules! set_field {
($set_field:ident($val:expr)) => {{
unsafe {
jni_call_no_post_check_ex!(
self,
v1_1,
$set_field,
class.as_ref().as_raw(),
field.as_ref().into_raw(),
$val
)
}
}};
}
match value {
JValue::Object(v) => set_field!(SetStaticObjectField(v.as_raw()))?,
JValue::Byte(v) => set_field!(SetStaticByteField(v))?,
JValue::Char(v) => set_field!(SetStaticCharField(v))?,
JValue::Short(v) => set_field!(SetStaticShortField(v))?,
JValue::Int(v) => set_field!(SetStaticIntField(v))?,
JValue::Long(v) => set_field!(SetStaticLongField(v))?,
JValue::Bool(v) => set_field!(SetStaticBooleanField(v))?,
JValue::Float(v) => set_field!(SetStaticFloatField(v))?,
JValue::Double(v) => set_field!(SetStaticDoubleField(v))?,
_ => (),
}
drop(class);
Ok(())
}
pub fn get_static_field<'other_local, 'sig, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
) -> Result<JValueOwned<'local>>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<FieldSignature<'sig>>,
{
self.assert_top();
let sig = sig.as_ref();
let field_ty = sig.ty();
let class = class.lookup(self)?;
unsafe {
self.get_static_field_unchecked(class.as_ref(), (class.as_ref(), name, sig), field_ty)
}
}
pub fn set_static_field<'other_local, 'sig, C, N, S>(
&mut self,
class: C,
name: N,
sig: S,
value: JValue,
) -> Result<()>
where
C: Desc<'local, JClass<'other_local>>,
N: AsRef<JNIStr>,
S: AsRef<FieldSignature<'sig>>,
{
let sig = sig.as_ref();
let field_ty = sig.ty();
if value.java_type() != field_ty {
return Err(Error::WrongJValueType(value.type_name(), "see java field"));
}
let class = class.lookup(self)?;
unsafe {
self.set_static_field_unchecked(class.as_ref(), (class.as_ref(), name, sig), value)
}
}
fn lock_rust_field<'other_local, O, S>(
&self,
obj: O,
field: S,
) -> Result<(MonitorGuard<'local>, JFieldID)>
where
O: AsRef<JObject<'other_local>>,
S: AsRef<JNIStr>,
{
self.with_local_frame(DEFAULT_LOCAL_FRAME_CAPACITY, |env| {
let obj = obj.as_ref();
let class = env.get_object_class(obj)?;
let field_id: JFieldID =
Desc::<JFieldID>::lookup((&class, &field, jni_sig!("J")), env)?;
let guard = self.lock_obj(obj)?;
Ok((guard, field_id))
})
}
pub unsafe fn set_rust_field<'other_local, O, S, T>(
&self,
obj: O,
field: S,
rust_object: T,
) -> Result<()>
where
O: AsRef<JObject<'other_local>>,
S: AsRef<JNIStr>,
T: Send + 'static,
{
let (_guard, field_id) = self.lock_rust_field(&obj, &field)?;
self.with_top_local_frame(|env| {
unsafe {
let field_ptr = env
.get_field_unchecked(&obj, field_id, ReturnType::Primitive(Primitive::Long))?
.j()? as *mut Mutex<T>;
if !field_ptr.is_null() {
return Err(Error::FieldAlreadySet(field.as_ref().to_str().into()));
}
}
let mbox = Box::new(::std::sync::Mutex::new(rust_object));
let ptr: *mut Mutex<T> = Box::into_raw(mbox);
unsafe { env.set_field_unchecked(obj, field_id, (ptr as crate::sys::jlong).into()) }
})
}
pub unsafe fn get_rust_field<'other_local, O, S, T>(
&self,
obj: O,
field: S,
) -> Result<MutexGuard<'local, T>>
where
O: AsRef<JObject<'other_local>>,
S: AsRef<JNIStr>,
T: Send + 'static,
{
let (_guard, field_id) = self.lock_rust_field(&obj, &field)?;
self.with_top_local_frame(|env| {
unsafe {
let field_ptr = env
.get_field_unchecked(obj, field_id, ReturnType::Primitive(Primitive::Long))?
.j()? as *mut Mutex<T>;
null_check!(field_ptr, "rust value from Java")?;
Ok((*field_ptr).lock().unwrap())
}
})
}
pub unsafe fn take_rust_field<'other_local, O, S, T>(&self, obj: O, field: S) -> Result<T>
where
O: AsRef<JObject<'other_local>>,
S: AsRef<JNIStr>,
T: Send + 'static,
{
let (_guard, field_id) = self.lock_rust_field(&obj, &field)?;
self.with_top_local_frame(|env| {
let mbox = unsafe {
let ptr = env
.get_field_unchecked(&obj, field_id, ReturnType::Primitive(Primitive::Long))?
.j()? as *mut Mutex<T>;
null_check!(ptr, "rust value from Java")?;
Box::from_raw(ptr)
};
drop(mbox.try_lock()?);
unsafe {
env.set_field_unchecked(obj, field_id, (0 as sys::jlong).into())?;
}
Ok(mbox.into_inner().unwrap())
})
}
pub fn lock_obj<'other_local, O>(&self, obj: O) -> Result<MonitorGuard<'local>>
where
O: AsRef<JObject<'other_local>>,
{
let inner = obj.as_ref().as_raw();
let res = unsafe { jni_call_no_post_check_ex!(self, v1_1, MonitorEnter, inner)? };
jni_error_code_to_result(res)?;
Ok(MonitorGuard {
obj: inner,
life: Default::default(),
})
}
pub fn get_java_vm(&self) -> Result<JavaVM> {
JavaVM::from_env(self)
}
pub fn ensure_local_capacity(&self, capacity: usize) -> Result<()> {
let capacity: jint = capacity
.try_into()
.map_err(|_| Error::JniCall(JniError::InvalidArguments))?;
let res = unsafe { jni_call_no_post_check_ex!(self, v1_2, EnsureLocalCapacity, capacity)? };
jni_error_code_to_result(res)?;
Ok(())
}
pub unsafe 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 res = unsafe {
jni_call_with_catch!(
catch |env| {
e: crate::exceptions::JNoSuchMethodError => {
let msg = e.as_throwable().get_message(env).ok();
let msg = if let Some(msg) = msg { msg.to_string() } else { String::new() };
Err(Error::NoSuchMethod(msg))
},
else => Err(Error::JniCall(JniError::Unknown)),
},
self,
v1_1,
RegisterNatives,
class.as_ref().as_raw(),
methods.as_ptr() as *mut sys::JNINativeMethod,
methods.len() as jint
)?
};
drop(class);
jni_error_code_to_result(res)
}
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 = unsafe {
jni_call_post_check_ex!(self, v1_1, UnregisterNatives, class.as_ref().as_raw())?
};
drop(class);
jni_error_code_to_result(res)
}
#[deprecated(
since = "0.22.0",
note = "use JPrimitiveArray::get_elements instead. This API will be removed in a future version"
)]
pub unsafe fn get_array_elements<'array_local, T, TArrayRef>(
&self,
array: TArrayRef,
mode: ReleaseMode,
) -> Result<AutoElements<'array_local, T, TArrayRef>>
where
T: TypeArray,
TArrayRef: AsRef<JPrimitiveArray<'array_local, T>> + Reference,
{
AutoElements::new(self, array, mode)
}
#[deprecated(
since = "0.22.0",
note = "use JPrimitiveArray::get_elements_critical instead. This API will be removed in a future version"
)]
pub unsafe fn get_array_elements_critical<'array_local, T, TArrayRef>(
&self,
array: TArrayRef,
mode: ReleaseMode,
) -> Result<AutoElementsCritical<'array_local, T, TArrayRef>>
where
T: TypeArray,
TArrayRef: AsRef<JPrimitiveArray<'array_local, T>> + Reference,
{
AutoElementsCritical::new(self, array, mode)
}
}
#[derive(Debug)]
pub enum Outcome<T, E> {
Ok(T),
Err(E),
Panic(Box<dyn std::any::Any + Send + 'static>),
}
#[must_use = "The outcome must be resolved to a value that can be returned to Java. See ::resolve or ::resolve_with"]
#[derive(Debug)]
pub struct EnvOutcome<'local, T, E> {
raw_env: *mut crate::sys::JNIEnv,
outcome: Outcome<T, E>,
_invariant: std::marker::PhantomData<&'local mut ()>, }
impl<'local, T, E> EnvOutcome<'local, T, E> {
pub(crate) fn new(raw_env: *mut crate::sys::JNIEnv, outcome: Outcome<T, E>) -> Self {
Self {
raw_env,
outcome,
_invariant: Default::default(),
}
}
pub fn resolve<'native_method, P>(self) -> T
where
P: ErrorPolicy<T, E, Captures<'local, 'native_method> = ()>,
T: Default + 'native_method,
'local: 'native_method,
{
self.resolve_with::<P, _>(|| ())
}
pub fn resolve_with<'native_method, P, F>(self, capture: F) -> T
where
P: ErrorPolicy<T, E>,
T: Default + 'native_method,
F: FnOnce() -> <P as ErrorPolicy<T, E>>::Captures<'local, 'native_method>,
'local: 'native_method,
{
self.resolve_inner::<P, F>(capture)
}
fn resolve_inner<'native_method, P, F>(self, capture: F) -> T
where
P: ErrorPolicy<T, E>,
T: Default + 'native_method,
F: FnOnce() -> <P as ErrorPolicy<T, E>>::Captures<'local, 'native_method>,
'local: 'native_method,
{
unsafe {
let mut guard: AttachGuard<'local> = AttachGuard::from_unowned(self.raw_env);
let env = guard.borrow_env_mut();
match self.outcome {
Outcome::Ok(t) => t,
Outcome::Err(e) => {
let mut cap = capture();
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
match P::on_error(env, &mut cap, e) {
Ok(t) => t,
Err(err) => P::on_internal_jni_error(&mut cap, err),
}
}))
.unwrap_or_else(|payload| P::on_internal_panic(&mut cap, payload))
}
Outcome::Panic(p) => {
let mut cap = capture();
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
match P::on_panic(env, &mut cap, p) {
Ok(t) => t,
Err(err) => P::on_internal_jni_error(&mut cap, err),
}
}))
.unwrap_or_else(|payload| P::on_internal_panic(&mut cap, payload))
}
}
}
}
pub fn into_outcome(self) -> Outcome<T, E> {
self.outcome
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct EnvUnowned<'local> {
ptr: *mut jni_sys::JNIEnv,
_lifetime: std::marker::PhantomData<&'local ()>,
}
impl<'local> EnvUnowned<'local> {
pub fn with_env<F, T, E>(&mut self, f: F) -> EnvOutcome<'local, T, E>
where
F: FnOnce(&mut Env<'local>) -> std::result::Result<T, E>,
E: From<Error>,
{
let mut guard: AttachGuard<'local> = unsafe { AttachGuard::from_unowned(self.ptr) };
let env = guard.borrow_env_mut();
let result = catch_unwind(AssertUnwindSafe(|| f(env)));
let outcome = match result {
Ok(ret) => match ret {
Ok(t) => Outcome::Ok(t),
Err(e) => Outcome::Err(e),
},
Err(payload) => Outcome::Panic(payload),
};
EnvOutcome::new(self.ptr, outcome)
}
pub fn with_env_no_catch<F, T, E>(&mut self, f: F) -> EnvOutcome<'local, T, E>
where
F: FnOnce(&mut Env<'local>) -> std::result::Result<T, E>,
E: From<Error>,
{
let mut guard: AttachGuard<'local> = unsafe { AttachGuard::from_unowned(self.ptr) };
let result = f(guard.borrow_env_mut());
match result {
Ok(t) => EnvOutcome::new(self.ptr, Outcome::Ok(t)),
Err(e) => EnvOutcome::new(self.ptr, Outcome::Err(e)),
}
}
pub unsafe fn from_raw(ptr: *mut jni_sys::JNIEnv) -> Self {
assert!(!ptr.is_null(), "EnvUnowned pointer must not be null");
Self {
ptr,
_lifetime: std::marker::PhantomData,
}
}
pub fn as_raw(&self) -> *mut jni_sys::JNIEnv {
self.ptr
}
pub fn into_raw(self) -> *mut jni_sys::JNIEnv {
self.ptr
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct NativeMethod<'desc> {
desc: JNINativeMethod,
_phantom: PhantomData<&'desc JNIStr>,
}
impl<'desc> NativeMethod<'desc> {
pub const unsafe fn from_raw_parts(
name: &'desc JNIStr,
sig: &'desc JNIStr,
fn_ptr: *mut c_void,
) -> NativeMethod<'desc> {
NativeMethod {
desc: JNINativeMethod {
name: name.as_ptr() as *mut c_char,
signature: sig.as_ptr() as *mut c_char,
fnPtr: fn_ptr as *mut c_void,
},
_phantom: PhantomData,
}
}
}
#[derive(Debug)]
#[must_use]
pub struct MonitorGuard<'local> {
obj: sys::jobject,
life: PhantomData<&'local ()>,
}
impl Drop for MonitorGuard<'_> {
fn drop(&mut self) {
JavaVM::singleton()
.expect("JavaVM singleton must be initialized")
.with_top_local_frame(|env| -> crate::errors::Result<()> {
let res =
unsafe { ex_safe_jni_call_no_post_check_ex!(env, v1_1, MonitorExit, self.obj) };
if let Err(err) = jni_error_code_to_result(res) {
log::error!("error releasing java monitor: {err}");
}
Ok(())
})
.expect("MonitorGuard dropped on detached thread");
}
}
#[cfg(test)]
static_assertions::assert_not_impl_any!(MonitorGuard: Send);