macro_rules! ex_safe_jni_call_no_post_check_ex {
( $jnienv:expr, $version:tt, $name:ident $(, $args:expr )*) => {{
let env: *mut jni_sys::JNIEnv = $jnienv.get_raw();
let interface: *const jni_sys::JNINativeInterface_ = *env;
((*interface).$version.$name)(env $(, $args)*)
}};
}
macro_rules! jni_call_no_post_check_ex {
( $jnienv:expr, $version:tt, $name:ident $(, $args:expr )*) => {{
$crate::__must_use(if $jnienv.exception_check() {
Err(crate::errors::Error::JavaException)
} else {
let env: *mut jni_sys::JNIEnv = $jnienv.get_raw();
let interface: *const jni_sys::JNINativeInterface_ = *env;
Ok(((*interface).$version.$name)(env $(, $args)*))
})
}};
}
macro_rules! jni_call_post_check_ex {
( $jnienv:expr, $version:tt, $name:ident $(, $args:expr )* ) => ({
jni_call_no_post_check_ex!($jnienv, $version, $name $(, $args)*).and_then(|ret| {
if $jnienv.exception_check() {
Err(crate::errors::Error::JavaException)
} else {
Ok(ret)
}
})
})
}
macro_rules! jni_call_post_check_ex_and_null_ret {
( $jnienv:expr, $version:tt, $name:ident $(, $args:expr )* ) => ({
jni_call_post_check_ex!($jnienv, $version, $name $(, $args)*).and_then(|ret| {
if ret.is_null() {
Err($crate::errors::Error::NullPtr(concat!(stringify!($name), " result")))
} else {
Ok(ret)
}
})
})
}
macro_rules! jni_call_only_check_null_ret {
( $jnienv:expr, $version:tt, $name:ident $(, $args:expr )* ) => ({
jni_call_no_post_check_ex!($jnienv, $version, $name $(, $args)*).and_then(|ret| {
if ret.is_null() {
Err($crate::errors::Error::NullPtr(concat!(stringify!($name), " result")))
} else {
Ok(ret)
}
})
})
}
macro_rules! __jni_match_exceptions {
($env:expr, $ex:expr $(,)?) => {
compile_error!("Missing 'else' expression in catch block")
};
($env:expr, $ex:expr, else => $ex_result:expr $(,)?) => {
$ex_result
};
($env:expr, $ex:expr, $ex_type:path => $ex_result:expr $(,)?) => {
compile_error!("Missing 'else' expression in catch block")
};
($env:expr, $ex:expr, $ex_type:path => $ex_result:expr, $($rest:tt)+) => {
if let Some(_) = <$ex_type>::matches($env, &$ex)? {
$ex_result
} else {
__jni_match_exceptions!($env, $ex, $($rest)+)
}
};
($env:expr, $ex:expr, $ex_name:ident: $ex_type:path => $ex_result:expr, $($rest:tt)+) => {
if let Some($ex_name) = <$ex_type>::matches($env, &$ex)? {
$ex_result
} else {
__jni_match_exceptions!($env, $ex, $($rest)+)
}
};
}
macro_rules! jni_call_with_catch {
( catch |$env_mut:ident| { $($ex_handlers:tt)* }, $jnienv:expr, $version:tt, $name:ident $(, $args:expr )* ) => ({
$crate::__must_use((|| -> $crate::errors::Result<_> {
let ret = jni_call_no_post_check_ex!($jnienv, $version, $name $(, $args)*) ?;
if $jnienv.exception_check() {
$jnienv.with_local_frame(
$crate::DEFAULT_LOCAL_FRAME_CAPACITY,
|$env_mut| -> $crate::errors::Result<_> {
let e = $env_mut
.exception_occurred()
.expect("Expected an exception after ExceptionCheck");
$env_mut.exception_clear();
__jni_match_exceptions!($env_mut, e, $($ex_handlers)*)
},
)
} else {
Ok(ret)
}
})())
});
}
macro_rules! jni_call_with_catch_and_null_check {
( catch |$env_mut:ident| { $($ex_handlers:tt)* }, $jnienv:expr, $version:tt, $name:ident $(, $args:expr )* ) => ({
jni_call_with_catch! {
catch |$env_mut| { $($ex_handlers)* },
$jnienv,
$version,
$name $(, $args)*
}.and_then(|ret| {
if ret.is_null() {
Err($crate::errors::Error::NullPtr(concat!(stringify!($name), " result")))
} else {
Ok(ret)
}
})
});
}
macro_rules! jni_try {
( ($jnienv:expr) -> $ret_ty:ty { $($jni_code:tt)* } catch |$env_mut:ident| { $($ex_handlers:tt)* }) => ({
let ret = (|| -> $ret_ty {$($jni_code)*})();
if $jnienv.exception_check() {
$jnienv.with_local_frame(
$crate::DEFAULT_LOCAL_FRAME_CAPACITY,
|$env_mut| -> $ret_ty {
let e = $env_mut
.exception_occurred()
.expect("Expected an exception after ExceptionCheck");
$env_mut.exception_clear();
__jni_match_exceptions!($env_mut, e, $($ex_handlers)*)
},
)
} else {
ret
}
});
}
macro_rules! null_check {
( $obj:expr, $ctx:expr ) => {
if $obj.is_null() {
Err($crate::errors::Error::NullPtr($ctx))
} else {
Ok($obj)
}
};
}
macro_rules! java_vm_call_unchecked {
( $jvm:expr, $version:tt, $name:ident $(, $args:expr )*) => {{
let jvm: *mut jni_sys::JavaVM = $jvm.get_raw();
((*(*jvm)).$version.$name)(jvm $(, $args)*)
}};
}
#[cfg(test)]
mod tests {
use crate::{
errors::{Error, JniError},
objects::JThrowable,
};
fn _compile_test_jni_call_with_catch_and_null_check(
env: &mut crate::Env<'_>,
name: &crate::strings::JNIStr,
sig: &crate::strings::JNIStr,
) -> crate::errors::Result<crate::sys::jclass> {
unsafe {
jni_call_with_catch_and_null_check!(
catch |env| {
crate::exceptions::JOutOfMemoryError =>
Err(Error::JniCall(JniError::NoMemory)),
crate::exceptions::JClassFormatError =>
Err(Error::ClassFormatError),
crate::exceptions::JClassCircularityError =>
Err(Error::ClassCircularityError),
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
})
},
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::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::JNoSuchMethodError =>
Err(Error::MethodNotFound {
name: name.to_string(),
sig: sig.to_string(),
}),
crate::exceptions::JNoSuchFieldError =>
Err(Error::FieldNotFound {
name: name.to_string(),
sig: sig.to_string(),
}),
crate::exceptions::JArrayStoreException =>
Err(Error::WrongObjectType),
crate::exceptions::JIllegalArgumentException =>
Err(Error::JniCall(JniError::InvalidArguments)),
crate::exceptions::JIllegalMonitorStateException =>
Err(Error::IllegalMonitorState),
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::JSecurityException =>
Err(Error::SecurityViolation),
crate::exceptions::JArrayIndexOutOfBoundsException =>
Err(Error::IndexOutOfBounds),
crate::exceptions::JStringIndexOutOfBoundsException =>
Err(Error::IndexOutOfBounds),
crate::exceptions::JInstantiationException =>
Err(Error::Instantiation),
crate::exceptions::JNumberFormatException =>
Err(Error::ParseFailed(String::new())),
else => Err(Error::NullPtr("Unexpected Exception"))
},
env,
v1_1,
FindClass,
core::ptr::null()
)
}
}
fn _compile_test_jni_try(env: &mut crate::Env<'_>) -> crate::errors::Result<()> {
let _res = jni_try! {
(env) -> crate::errors::Result<crate::objects::JString> {
let s = env.new_string("String")?;
{
let msg = env.new_string("Test")?;
let e = crate::exceptions::JNumberFormatException::new(env, msg)?;
let e: crate::objects::JThrowable = e.into();
env.throw(e)?;
}
Ok(s)
} catch |env| {
crate::exceptions::JNumberFormatException => Err(Error::ParseFailed(String::new())),
else => Err(Error::NullPtr("Test"))
}
}?;
Ok(())
}
}