From d6c0aa2f9eaa35be6f85e1e81549767a9fb9c4ee Mon Sep 17 00:00:00 2001 From: zaaarf Date: Mon, 23 Sep 2024 00:23:42 +0200 Subject: [PATCH] feat: working but questionable intermediate trait implementation --- macro/src/ret.rs | 8 +++- macro/src/wrapper.rs | 39 +++++++++++++----- src/lib.rs | 95 ++++++++++++++++++++++++++++---------------- 3 files changed, 95 insertions(+), 47 deletions(-) diff --git a/macro/src/ret.rs b/macro/src/ret.rs index f642fe2..254fcda 100644 --- a/macro/src/ret.rs +++ b/macro/src/ret.rs @@ -42,8 +42,14 @@ impl ReturnOptions { pub(crate) fn tokens(&self) -> TokenStream { match &self.ty { // TODO why do we need to invoke syn::Token! macro ??? - Some(t) => quote::quote!( -> <#t as jni_toolbox::IntoJava<'local>>::T ), 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 ) + } + } } } } diff --git a/macro/src/wrapper.rs b/macro/src/wrapper.rs index 5bdaa89..b774e33 100644 --- a/macro/src/wrapper.rs +++ b/macro/src/wrapper.rs @@ -24,21 +24,35 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // TODO a bit ugly passing the return expr down... we should probably manage returns here 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"); let fn_name_inner = syn::Ident::new(&name, Span::call_site()); let fn_name = syn::Ident::new(&format!("Java_{}_{}_{name_jni}", attrs.package, attrs.class), Span::call_site()); - let incoming = args.incoming; // V----------------------------------V - let header = quote::quote! { - #[no_mangle] - #[allow(unused_mut)] - pub extern "system" fn #fn_name<'local>(#incoming) #return_type + 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 env_ident = args.env; @@ -49,7 +63,8 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // V----------------------------------V quote::quote! { { - use jni_toolbox::{JniToolboxError, FromJava, IntoJava}; + use jni_toolbox::#return_type_import; + use jni_toolbox::{JniToolboxError, FromJava}; #transforming match #fn_name_inner(#forwarding) { Ok(ret) => ret, @@ -65,7 +80,8 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // V----------------------------------V quote::quote! { { - use jni_toolbox::{JniToolboxError, FromJava, IntoJava}; + 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() }; @@ -85,7 +101,7 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re }, } Ok(ret) => match ret.into_java(&mut env_copy) { - Ok(fin) => return fin, + Ok(fin) => return fin.into_java_raw(), Err(e) => { // TODO should we panic instead? let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}")); @@ -100,10 +116,11 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // V----------------------------------V quote::quote! { { - use jni_toolbox::{JniToolboxError, FromJava, IntoJava}; + 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, + Ok(res) => return res.into_java_raw(), Err(e) => { // TODO should we panic instead? let _ = #env_ident.throw_new("java/lang/RuntimeException", format!("{e:?}")); diff --git a/src/lib.rs b/src/lib.rs index 07ca45b..a3895eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub use jni_toolbox_macro::jni; -use jni::objects::{JObject, JString, JObjectArray}; +use jni::objects::{JObject, JObjectArray, JString}; pub trait JniToolboxError: std::error::Error { fn jclass(&self) -> String; @@ -42,8 +42,10 @@ macro_rules! auto_from_java { auto_from_java!(i64, jni::sys::jlong); auto_from_java!(i32, jni::sys::jint); auto_from_java!(i16, jni::sys::jshort); +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>); impl<'j> FromJava<'j> for bool { type T = jni::sys::jboolean; @@ -63,11 +65,11 @@ impl<'j> FromJava<'j> for String { } } -impl<'j, T: FromJava<'j, T = JObject<'j>>> FromJava<'j> for Option { - type T = JObject<'j>; +impl<'j, T: FromJava<'j, T: std::convert::AsRef>>> FromJava<'j> for Option { + type T = T::T; fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { - if value.is_null() { return Ok(None) }; + if value.as_ref().is_null() { return Ok(None) }; Ok(Some(T::from_java(env, value)?)) } } @@ -92,30 +94,9 @@ impl<'j> FromJava<'j> for uuid::Uuid { } } -trait JavaType {} - -/// Intermediate trait used to guess the JNI return type. -/// Usually doesn't need to be manually implemented. -pub trait IntoJavaRaw<'j, T> { - fn into_java_raw(self, env: &mut jni::JNIEnv<'j>) -> Result; -} - -impl <'j, E, T: IntoJavaPrimitive<'j, T = E>> IntoJavaRaw<'j, E> for T { - fn into_java_raw(self, env: &mut jni::JNIEnv<'j>) -> Result { - self.into_java_primitive(env) - } -} - -impl<'j, T: IntoJavaObject<'j>> IntoJavaRaw<'j, jni::sys::jobject> for T { - fn into_java_raw(self, env: &mut jni::JNIEnv<'j>) -> Result { - self.into_java(env) - .map(|j| j.as_ref().as_raw()) - } -} - pub trait IntoJavaPrimitive<'j> { type T; - fn into_java_primitive(self, _: &mut jni::JNIEnv<'j>) -> Result; + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result; } macro_rules! auto_into_java { @@ -123,16 +104,19 @@ macro_rules! auto_into_java { impl<'j> IntoJavaPrimitive<'j> for $t { type T = $j; - fn into_java_primitive(self, _: &mut jni::JNIEnv<'j>) -> Result { + fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result { Ok(self) } } }; } +// TODO: primitive arrays! + auto_into_java!(i64, jni::sys::jlong); auto_into_java!(i32, jni::sys::jint); auto_into_java!(i16, jni::sys::jshort); +auto_into_java!(i8, jni::sys::jbyte); auto_into_java!(f32, jni::sys::jfloat); auto_into_java!(f64, jni::sys::jdouble); auto_into_java!((), ()); @@ -141,7 +125,7 @@ impl<'j> IntoJavaPrimitive<'j> for bool { type T = jni::sys::jboolean; #[inline] - fn into_java_primitive(self, _: &mut jni::JNIEnv) -> Result { + fn into_java(self, _: &mut jni::JNIEnv) -> Result { Ok(if self { 1 } else { 0 }) } } @@ -171,7 +155,6 @@ impl<'j> IntoJavaObject<'j> for String { 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())?; for (n, el) in self.into_iter().enumerate() { @@ -182,13 +165,13 @@ impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec { } } -impl<'j, E: std::convert::AsRef> + JavaFromRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option { +impl<'j, E: std::convert::AsRef> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option { type T = E; const CLASS: &'static str = T::CLASS; fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { match self { Some(x) => x.into_java(env), - None => Ok(unsafe { E::from_raw(std::ptr::null_mut()) }) // safe, that's what JObject::null does + None => Ok(unsafe { E::from_java_raw(std::ptr::null_mut()) }) // safe, that's what JObject::null does } } } @@ -207,15 +190,15 @@ impl<'j> IntoJavaObject<'j> for uuid::Uuid { } /// Needed internally to perform some operations. -trait JavaFromRaw { - unsafe fn from_raw(raw: jni::sys::jobject) -> Self; +trait FromJavaRaw { + unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self; } macro_rules! auto_from_raw { ($type: ty) => { - impl JavaFromRaw for $type { + impl FromJavaRaw for $type { #[inline] - unsafe fn from_raw(raw: jni::sys::jobject) -> Self { + unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self { Self::from_raw(raw) } } @@ -225,3 +208,45 @@ macro_rules! auto_from_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!(jni::sys::jboolean); +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>);