diff --git a/macro/src/args.rs b/macro/src/args.rs index 870446f..8c7c1a0 100644 --- a/macro/src/args.rs +++ b/macro/src/args.rs @@ -1,5 +1,5 @@ use proc_macro2::{Span, TokenStream}; -use quote::TokenStreamExt; +use quote::{ToTokens, TokenStreamExt}; use syn::Ident; pub(crate) struct ArgumentOptions { @@ -109,7 +109,7 @@ impl ArgumentOptions { }, }; }); - incoming.append_all(quote::quote!( mut #pat: <#ty as jni_toolbox::FromJava<'local>>::T,)); + incoming.append_all(quote::quote!( #pat: <#ty as jni_toolbox::FromJava<'local>>::From,)); forwarding.append_all(quote::quote!( #new_pat,)); } @@ -121,3 +121,25 @@ struct SingleArgument { pat: syn::Ident, ty: Box, } + +#[allow(unused)] +fn bare_type(t: syn::Type) -> TokenStream { + match t { + syn::Type::Array(x) => bare_type(*x.elem), + syn::Type::BareFn(f) => f.to_token_stream(), + syn::Type::Group(x) => bare_type(*x.elem), + syn::Type::ImplTrait(t) => t.to_token_stream(), + syn::Type::Infer(x) => x.to_token_stream(), + syn::Type::Macro(x) => x.to_token_stream(), + syn::Type::Never(x) => x.to_token_stream(), + syn::Type::Paren(p) => bare_type(*p.elem), + syn::Type::Path(p) => p.to_token_stream(), + syn::Type::Ptr(x) => bare_type(*x.elem), + syn::Type::Reference(r) => bare_type(*r.elem), + syn::Type::Slice(s) => bare_type(*s.elem), + syn::Type::TraitObject(t) => t.to_token_stream(), + syn::Type::Tuple(x) => x.to_token_stream(), + syn::Type::Verbatim(x) => x.to_token_stream(), + _ => todo!(), + } +} diff --git a/macro/src/attrs.rs b/macro/src/attrs.rs index 3872693..12ac108 100644 --- a/macro/src/attrs.rs +++ b/macro/src/attrs.rs @@ -4,7 +4,6 @@ pub(crate) struct AttrsOptions { pub(crate) package: String, pub(crate) class: String, pub(crate) exception: Option, - pub(crate) return_pointer: bool, } impl AttrsOptions { @@ -14,7 +13,6 @@ impl AttrsOptions { let mut package = None; let mut class = None; let mut exception = None; - let mut return_pointer = false; for attr in attrs { match what_next { @@ -24,7 +22,7 @@ impl AttrsOptions { "package" => what_next = WhatNext::Package, "class" => what_next = WhatNext::Class, "exception" => what_next = WhatNext::Exception, - "ptr" => return_pointer = true, + "ptr" => {}, // accepted for backwards compatibility _ => return Err(syn::Error::new(Span::call_site(), "unexpected attribute on macro: {attr}")), } } @@ -53,7 +51,7 @@ impl AttrsOptions { let Some(package) = package else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'package'")) }; let Some(class) = class else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'class'")) }; - Ok(Self { package, class, exception, return_pointer }) + Ok(Self { package, class, exception }) } } diff --git a/macro/src/ret.rs b/macro/src/ret.rs index 254fcda..5934e30 100644 --- a/macro/src/ret.rs +++ b/macro/src/ret.rs @@ -6,13 +6,16 @@ use syn::{ReturnType, Type}; pub(crate) struct ReturnOptions { pub(crate) ty: Option>, pub(crate) result: bool, + pub(crate) pointer: bool, pub(crate) void: bool, } +const PRIMITIVE_TYPES: [&str; 7] = ["i8", "i16", "i32", "i64", "f32", "f64", "bool"]; + impl ReturnOptions { pub(crate) fn parse_signature(ret: &ReturnType) -> Result { match ret { - syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true }), + syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true, pointer: false }), syn::ReturnType::Type(_tok, ty) => match *ty.clone() { syn::Type::Path(path) => { let Some(last) = path.path.segments.last() else { @@ -26,14 +29,19 @@ impl ReturnOptions { syn::PathArguments::AngleBracketed(ref generics) => for generic in generics.args.iter() { match generic { syn::GenericArgument::Lifetime(_) => continue, - syn::GenericArgument::Type(ty) => return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty) }), - _ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result")), + syn::GenericArgument::Type(ty) => { + // TODO checking by making ty a token stream and then string equals is not exactly great! + let pointer = !PRIMITIVE_TYPES.iter().any(|t| ty.to_token_stream().to_string() == *t); + return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty), pointer }); + }, + _ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result")) } } } } - Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false }) + let pointer = !PRIMITIVE_TYPES.iter().any(|t| last.ident == t); + Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false, pointer }) }, _ => Err(syn::Error::new(Span::call_site(), "unsupported return type")), }, @@ -43,13 +51,7 @@ impl ReturnOptions { pub(crate) fn tokens(&self) -> TokenStream { match &self.ty { // TODO why do we need to invoke syn::Token! macro ??? None => ReturnType::Default.to_token_stream(), - Some(t) => { - if t.to_token_stream().to_string() == "bool" {// TODO: there DEFINITELY is a better way - quote::quote!( -> jni::sys::jboolean ) - } else { - quote::quote!( -> <#t as jni_toolbox::IntoJavaRaw>::T ) - } - } + Some(t) => quote::quote!( -> <#t as jni_toolbox::IntoJava<'local>>::Ret ) } } } diff --git a/macro/src/wrapper.rs b/macro/src/wrapper.rs index b774e33..50872c4 100644 --- a/macro/src/wrapper.rs +++ b/macro/src/wrapper.rs @@ -15,7 +15,7 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?; let return_expr = if ret.void { quote::quote!( () ) - } else if attrs.return_pointer { + } else if ret.pointer { quote::quote!( std::ptr::null_mut() ) } else { quote::quote!( 0 ) @@ -25,11 +25,6 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re let args = ArgumentOptions::parse_args(&fn_item, return_expr.clone())?; let return_type = ret.tokens(); - let return_type_import = if attrs.return_pointer { - syn::Ident::new("IntoJavaObject", Span::call_site()) - } else { - syn::Ident::new("IntoJavaPrimitive", Span::call_site()) - }; let name = fn_item.sig.ident.to_string(); let name_jni = name.replace("_", "_1"); @@ -38,100 +33,88 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re let incoming = args.incoming; // V----------------------------------V - let header = if attrs.return_pointer { - quote::quote! { - #[no_mangle] - #[allow(unused_mut)] - pub extern "system" fn #fn_name<'local>(#incoming) -> jni::sys::jobject - } - } else { - quote::quote! { - #[no_mangle] - #[allow(unused_mut)] - pub extern "system" fn #fn_name<'local>(#incoming) #return_type - } + let header = quote::quote! { + #[no_mangle] + #[allow(unused_unit)] + pub extern "system" fn #fn_name<'local>(#incoming) #return_type }; - // ^----------------------------------^ + let transforming = args.transforming; + let transformations = quote::quote! { + use jni_toolbox::{JniToolboxError, FromJava, IntoJava}; + #transforming + }; + let env_ident = args.env; let forwarding = args.forwarding; - let transforming = args.transforming; - let body = if ret.result { // wrap errors + let invocation = quote::quote! { + let mut env_copy = unsafe { #env_ident.unsafe_clone() }; + let result = #fn_name_inner(#forwarding); + }; + + + let error_handling = if ret.result { if let Some(exception) = attrs.exception { - // V----------------------------------V quote::quote! { - { - use jni_toolbox::#return_type_import; - use jni_toolbox::{JniToolboxError, FromJava}; - #transforming - match #fn_name_inner(#forwarding) { - Ok(ret) => ret, - Err(e) => match #env_ident.throw_new(#exception, format!("{e:?}")) { - Ok(_) => return #return_expr, - Err(e) => panic!("error throwing java exception: {e}"), - } + let ret = match result { + Ok(x) => x, + Err(e) => match env_copy.throw_new(#exception, format!("{e:?}")) { + Ok(_) => return #return_expr, + Err(e) => panic!("error throwing java exception: {e}"), } - } + }; } - // ^----------------------------------^ } else { - // V----------------------------------V quote::quote! { - { - use jni_toolbox::#return_type_import; - use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw}; - // NOTE: this should be SAFE! the cloned env reference lives less than the actual one, we just lack a - // way to get it back from the called function and thus resort to unsafe cloning - let mut env_copy = unsafe { #env_ident.unsafe_clone() }; - #transforming - match #fn_name_inner(#forwarding) { - Err(e) => match env_copy.find_class(e.jclass()) { - Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"), - Ok(class) => match env_copy.new_string(format!("{e:?}")) { - Err(e) => panic!("error throwing Java exception -- failed creating error string: {e}"), - Ok(msg) => match env_copy.new_object(class, "(Ljava/lang/String;)V", &[jni::objects::JValueGen::Object(&msg)]) { - Err(e) => panic!("error throwing Java exception -- failed creating object: {e}"), - Ok(obj) => match env_copy.throw(jni::objects::JThrowable::from(obj)) { - Err(e) => panic!("error throwing Java exception -- failed throwing: {e}"), - Ok(_) => return #return_expr, - }, + let ret = match result { + Ok(x) => x, + Err(e) => match env_copy.find_class(e.jclass()) { + Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"), + Ok(class) => match env_copy.new_string(format!("{e:?}")) { + Err(e) => panic!("error throwing Java exception -- failed creating error string: {e}"), + Ok(msg) => match env_copy.new_object(class, "(Ljava/lang/String;)V", &[jni::objects::JValueGen::Object(&msg)]) { + Err(e) => panic!("error throwing Java exception -- failed creating object: {e}"), + Ok(obj) => match env_copy.throw(jni::objects::JThrowable::from(obj)) { + Err(e) => panic!("error throwing Java exception -- failed throwing: {e}"), + Ok(_) => return #return_expr, }, }, - } - Ok(ret) => match ret.into_java(&mut env_copy) { - Ok(fin) => return fin.into_java_raw(), - Err(e) => { - // TODO should we panic instead? - let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}")); - return #return_expr; - } }, } - } + }; } } } else { - // V----------------------------------V - quote::quote! { - { - use jni_toolbox::#return_type_import; - use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw}; - #transforming - match #fn_name_inner(#forwarding).into_java(&mut #env_ident) { - Ok(res) => return res.into_java_raw(), - Err(e) => { - // TODO should we panic instead? - let _ = #env_ident.throw_new("java/lang/RuntimeException", format!("{e:?}")); - return #return_expr; - }, - } + quote::quote!( let ret = result; ) + }; + + + let reverse_transformations = quote::quote! { + match ret.into_java(&mut env_copy) { + Ok(fin) => fin, + Err(e) => { + // TODO should we panic instead? + let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}")); + #return_expr } } - // ^----------------------------------^ }; + let body = quote::quote! { + { + #transformations + + #invocation + + #error_handling + + #reverse_transformations + } + }; + + out.append_all(input); out.append_all(header); out.append_all(body); diff --git a/src/from_java.rs b/src/from_java.rs index e6af7fa..ff20c10 100644 --- a/src/from_java.rs +++ b/src/from_java.rs @@ -1,27 +1,27 @@ -use jni::objects::{JObject, JString}; +use jni::objects::{JObject, JObjectArray, JPrimitiveArray, JString, TypeArray}; /// Used in the generated code to have proper type bindings. You probably didn't want /// to call this directly. -pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::T) -> Result { +pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::From) -> Result { T::from_java(env, val) } /// Specifies how a Java type should be converted before being fed to Rust. pub trait FromJava<'j> : Sized { /// The JNI type representing the input. - type T : Sized; + type From : Sized; /// Attempts to convert this Java object into its Rust counterpart. - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result; + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result; } macro_rules! auto_from_java { ($t: ty, $j: ty) => { impl<'j> FromJava<'j> for $t { - type T = $j; + type From = $j; #[inline] - fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result { Ok(value) } } @@ -35,38 +35,75 @@ auto_from_java!(i8, jni::sys::jbyte); auto_from_java!(f32, jni::sys::jfloat); auto_from_java!(f64, jni::sys::jdouble); auto_from_java!(JObject<'j>, JObject<'j>); +auto_from_java!(JString<'j>, JString<'j>); +auto_from_java!(JObjectArray<'j>, JObjectArray<'j>); -impl<'j> FromJava<'j> for bool { - type T = jni::sys::jboolean; +impl<'j, T: TypeArray> FromJava<'j> for JPrimitiveArray<'j, T> { + type From = JPrimitiveArray<'j, T>; #[inline] - fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for char { + type From = jni::sys::jchar; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result { + char::from_u32(value.into()).ok_or_else(|| jni::errors::Error::WrongJValueType("char", "invalid u16")) + } +} + +impl<'j> FromJava<'j> for bool { + type From = jni::sys::jboolean; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result { Ok(value == 1) } } impl<'j> FromJava<'j> for String { - type T = JString<'j>; + type From = JString<'j>; - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result { if value.is_null() { return Err(jni::errors::Error::NullPtr("string can't be null")) }; Ok(env.get_string(&value)?.into()) } } -impl<'j, T: FromJava<'j, T: std::convert::AsRef>>> FromJava<'j> for Option { - type T = T::T; +impl<'j, T> FromJava<'j> for Option +where + T: FromJava<'j, From: AsRef>>, +{ + type From = T::From; - fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result { if value.as_ref().is_null() { return Ok(None) }; Ok(Some(T::from_java(env, value)?)) } } +impl<'j, T: FromJava<'j, From = JObject<'j>>> FromJava<'j> for Vec { + type From = JObjectArray<'j>; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result { + let len = env.get_array_length(&value)?; + let mut out = Vec::new(); + for i in 0..len { + let el = env.get_object_array_element(&value, i)?; + out.push(T::from_java(env, el)?); + } + Ok(out) + } +} + #[cfg(feature = "uuid")] impl<'j> FromJava<'j> for uuid::Uuid { - type T = JObject<'j>; - fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::T) -> Result { + type From = JObject<'j>; + fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::From) -> Result { let lsb = u64::from_ne_bytes( env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])? .j()? diff --git a/src/into_java.rs b/src/into_java.rs index abc5721..5860da6 100644 --- a/src/into_java.rs +++ b/src/into_java.rs @@ -1,22 +1,20 @@ -use jni::objects::{JObject, JObjectArray, JString}; - -use crate::raw_java::FromJavaRaw; +use jni::objects::JObject; /// Specifies how a Rust type should be converted into a Java primitive. -pub trait IntoJavaPrimitive<'j> { +pub trait IntoJava<'j> { /// The JNI type representing the output. - type T; + type Ret; /// Attempts to convert this Rust object into a Java primitive. - fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result; + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result; } macro_rules! auto_into_java { ($t: ty, $j: ty) => { - impl<'j> IntoJavaPrimitive<'j> for $t { - type T = $j; + impl<'j> IntoJava<'j> for $t { + type Ret = $j; - fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result { + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result { Ok(self) } } @@ -33,69 +31,71 @@ auto_into_java!(f32, jni::sys::jfloat); auto_into_java!(f64, jni::sys::jdouble); auto_into_java!((), ()); -impl<'j> IntoJavaPrimitive<'j> for bool { - type T = jni::sys::jboolean; +impl<'j> IntoJava<'j> for bool { + type Ret = jni::sys::jboolean; #[inline] - fn into_java(self, _: &mut jni::JNIEnv) -> Result { + fn into_java(self, _: &mut jni::JNIEnv) -> Result { Ok(if self { 1 } else { 0 }) } } +impl<'j, X: IntoJavaObject<'j>> IntoJava<'j> for X { + type Ret = jni::sys::jobject; + + fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + Ok(self.into_java_object(env)?.as_raw()) + } +} + /// Specifies how a Rust type should be converted into a Java object. pub trait IntoJavaObject<'j> { - type T: std::convert::AsRef>; /// The Java class associated with this type. const CLASS: &'static str; /// Attempts to convert this Rust object into a Java object. - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result; + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error>; } impl<'j> IntoJavaObject<'j> for &str { - type T = JString<'j>; const CLASS: &'static str = "java/lang/String"; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { - env.new_string(self) + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error> { + Ok(env.new_string(self)?.into()) } } impl<'j> IntoJavaObject<'j> for String { - type T = JString<'j>; const CLASS: &'static str = "java/lang/String"; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { - self.as_str().into_java(env) + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error> { + self.as_str().into_java_object(env) } } -impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec { - type T = JObjectArray<'j>; - const CLASS: &'static str = E::CLASS; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { - let mut array = env.new_object_array(self.len() as i32, E::CLASS, JObject::null())?; +impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec { + const CLASS: &'static str = T::CLASS; // TODO shouldnt it be 'Object[]' ? + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error> { + let mut array = env.new_object_array(self.len() as i32, T::CLASS, JObject::null())?; for (n, el) in self.into_iter().enumerate() { - let el = el.into_java(env)?; + let el = el.into_java_object(env)?; env.set_object_array_element(&mut array, n as i32, &el)?; } - Ok(array) + Ok(array.into()) } } -impl<'j, E: std::convert::AsRef> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option { - type T = E; +impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Option { const CLASS: &'static str = T::CLASS; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error> { match self { - Some(x) => x.into_java(env), - None => Ok(unsafe { E::from_java_raw(std::ptr::null_mut()) }) // safe, that's what JObject::null does + Some(x) => x.into_java_object(env), + None => Ok(JObject::null()) } } } #[cfg(feature = "uuid")] impl<'j> IntoJavaObject<'j> for uuid::Uuid { - type T = jni::objects::JObject<'j>; const CLASS: &'static str = "java/util/UUID"; - fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result, jni::errors::Error> { let class = env.find_class(Self::CLASS)?; let (msb, lsb) = self.as_u64_pair(); let msb = i64::from_ne_bytes(msb.to_ne_bytes()); diff --git a/src/lib.rs b/src/lib.rs index 065043e..ef25319 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,9 @@ pub mod into_java; pub mod from_java; -pub mod raw_java; pub use jni_toolbox_macro::jni; -pub use into_java::{IntoJavaObject, IntoJavaPrimitive}; +pub use into_java::{IntoJavaObject, IntoJava}; pub use from_java::{FromJava, from_java_static}; -pub use raw_java::IntoJavaRaw; /// An error that is meant to be used with jni-toolbox. diff --git a/src/raw_java.rs b/src/raw_java.rs deleted file mode 100644 index f0d13e3..0000000 --- a/src/raw_java.rs +++ /dev/null @@ -1,63 +0,0 @@ -use jni::objects::{JObject, JObjectArray, JString}; - - -/// Needed internally to perform some operations. -pub trait FromJavaRaw { - unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self; -} - -macro_rules! auto_from_raw { - ($type: ty) => { - impl FromJavaRaw for $type { - #[inline] - unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self { - Self::from_raw(raw) - } - } - }; -} - -auto_from_raw!(JObject<'_>); -auto_from_raw!(JString<'_>); -auto_from_raw!(JObjectArray<'_>); - -/// Intermediate trait used to guess the JNI return type. -/// Usually doesn't need to be manually implemented. -pub trait IntoJavaRaw { - type T; - fn into_java_raw(self) -> Self::T; -} - -macro_rules! auto_into_raw_primitive { - ($type: ty) => { - impl IntoJavaRaw for $type { - type T = $type; - fn into_java_raw(self) -> Self::T { - self - } - } - } -} - -auto_into_raw_primitive!(jni::sys::jlong); -auto_into_raw_primitive!(jni::sys::jint); -auto_into_raw_primitive!(jni::sys::jshort); -auto_into_raw_primitive!(jni::sys::jbyte); -auto_into_raw_primitive!(jni::sys::jdouble); -auto_into_raw_primitive!(jni::sys::jfloat); -auto_into_raw_primitive!(()); - -macro_rules! auto_into_raw_object { - ($lt: lifetime, $type: ty) => { - impl<'j> IntoJavaRaw for $type { - type T = jni::sys::jobject; - fn into_java_raw(self) -> Self::T { - self.as_raw() - } - } - }; -} - -auto_into_raw_object!('j, JObject<'j>); -auto_into_raw_object!('j, JString<'j>); -auto_into_raw_object!('j, JObjectArray<'j>);