use std::panic::{AssertUnwindSafe, catch_unwind};
use crate::Env;
#[cfg(doc)]
use crate::{EnvOutcome, Outcome, errors::Error};
pub trait ErrorPolicy<T, E> {
type Captures<'unowned_env_local: 'native_method, 'native_method>;
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: E,
) -> crate::errors::Result<T>;
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
captures: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> crate::errors::Result<T>;
fn on_internal_jni_error<'unowned_env_local: 'native_method, 'native_method>(
_captures: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: crate::errors::Error,
) -> T
where
T: Default,
{
log::error!(
"Secondary failure while handling error or panic in native method: {:?}",
err
);
T::default()
}
fn on_internal_panic<'unowned_env_local: 'native_method, 'native_method>(
_captures: &mut Self::Captures<'unowned_env_local, 'native_method>,
_payload: Box<dyn std::any::Any + Send + 'static>,
) -> T
where
T: Default,
{
log::error!("Last resort: panic while handling error or panic in native method");
T::default()
}
}
#[derive(Debug, Default)]
pub struct ThrowRuntimeExAndDefault;
impl<T: Default, E: std::error::Error> ErrorPolicy<T, E> for ThrowRuntimeExAndDefault {
type Captures<'unowned_env_local: 'native_method, 'native_method> = ();
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: E,
) -> crate::errors::Result<T> {
if env.exception_check() {
return Ok(T::default()); }
let err_string = format!("Rust error: {}", err);
let _ = env.throw(err_string);
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> crate::errors::Result<T> {
let panic_string = match payload.downcast::<&'static str>() {
Ok(s) => (*s).to_string(),
Err(payload) => {
if let Err(drop_panic) = catch_unwind(AssertUnwindSafe(|| drop(payload))) {
log::error!("Panic while dropping panic payload: {:?}", drop_panic);
std::mem::forget(drop_panic);
}
"non-string panic payload".to_string()
}
};
let _ = env.throw(format!("Rust panic: {}", panic_string));
Ok(T::default())
}
}
#[derive(Debug, Default)]
pub struct LogErrorAndDefault;
impl<T: Default, E: std::error::Error> ErrorPolicy<T, E> for LogErrorAndDefault {
type Captures<'unowned_env_local: 'native_method, 'native_method> = ();
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: E,
) -> crate::errors::Result<T> {
log::error!("Rust error: {}", err);
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
_cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> crate::errors::Result<T> {
let panic_string = match payload.downcast::<&'static str>() {
Ok(s) => (*s).to_string(),
Err(payload) => {
if let Err(drop_panic) = catch_unwind(AssertUnwindSafe(|| drop(payload))) {
log::error!("Panic while dropping panic payload: {:?}", drop_panic);
std::mem::forget(drop_panic);
}
"non-string panic payload".to_string()
}
};
log::error!("Rust panic: {}", panic_string);
Ok(T::default())
}
}
#[derive(Debug, Default)]
pub struct LogContextErrorAndDefault;
impl<T: Default, E: std::error::Error> ErrorPolicy<T, E> for LogContextErrorAndDefault {
type Captures<'unowned_env_local: 'native_method, 'native_method> = String;
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
err: E,
) -> crate::errors::Result<T> {
log::error!("{cap}: {err}");
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> crate::errors::Result<T> {
let panic_string = match payload.downcast::<&'static str>() {
Ok(s) => (*s).to_string(),
Err(payload) => {
if let Err(drop_panic) = catch_unwind(AssertUnwindSafe(|| drop(payload))) {
log::error!("Panic while dropping panic payload: {:?}", drop_panic);
std::mem::forget(drop_panic);
}
"non-string panic payload".to_string()
}
};
log::error!("{cap}: {panic_string}");
Ok(T::default())
}
}
#[cfg(test)]
mod tests {
use crate::{
EnvUnowned,
objects::{JClass, JObject, JString},
};
use super::*;
struct TestCustomPolicyCaptures<'local, 'native_method>
where
'local: 'native_method,
{
context: &'native_method JObject<'local>, }
struct TestCustomPolicy;
impl<T: Default, E: std::error::Error> ErrorPolicy<T, E> for TestCustomPolicy {
type Captures<'unowned_env_local: 'native_method, 'native_method> =
TestCustomPolicyCaptures<'unowned_env_local, 'native_method>;
fn on_error<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
cap: &mut TestCustomPolicyCaptures<'unowned_env_local, 'native_method>,
err: E,
) -> crate::errors::Result<T> {
eprintln!("Error: {:?}, context: {:?}", err, cap.context);
Ok(T::default())
}
fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
_env: &mut Env<'unowned_env_local>,
_cap: &mut TestCustomPolicyCaptures<'unowned_env_local, 'native_method>,
payload: Box<dyn std::any::Any + Send + 'static>,
) -> crate::errors::Result<T> {
eprintln!("Panic: {:?}", payload);
Ok(T::default())
}
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_HelloWorld_test<'local>(
mut unowned_env: EnvUnowned<'local>,
_this: JObject<'local>,
context: JObject<'local>,
) -> JClass<'local> {
unowned_env
.with_env(|env| -> crate::errors::Result<_> {
let class = env.get_object_class(&context)?;
Ok(class)
})
.resolve_with::<TestCustomPolicy, _>(|| {
TestCustomPolicyCaptures::<'local, '_> {
context: &context, }
})
}
#[unsafe(no_mangle)]
pub extern "system" fn Java_HelloWorld_logErrorWithContextString<'local>(
mut unowned_env: EnvUnowned<'local>,
_this: JObject<'local>,
context: JObject<'local>,
arg: JString<'local>,
) -> JClass<'local> {
unowned_env
.with_env(|env| -> crate::errors::Result<_> {
let class = env.get_object_class(&context)?;
Ok(class)
})
.resolve_with::<LogContextErrorAndDefault, _>(|| {
format!("In 'logErrorWithContextString' with arg: {arg}")
})
}
}