diff --git a/macro/src/args.rs b/macro/src/args.rs index 7a926bb..870446f 100644 --- a/macro/src/args.rs +++ b/macro/src/args.rs @@ -4,6 +4,7 @@ use syn::Ident; pub(crate) struct ArgumentOptions { pub(crate) incoming: TokenStream, + pub(crate) transforming: TokenStream, pub(crate) forwarding: TokenStream, pub(crate) env: Ident, } @@ -47,7 +48,7 @@ fn type_equals(ty: Box, search: impl AsRef) -> bool { } impl ArgumentOptions { - pub(crate) fn parse_args(fn_item: &syn::ItemFn) -> Result { + pub(crate) fn parse_args(fn_item: &syn::ItemFn, ret_expr: TokenStream) -> Result { let mut arguments = Vec::new(); let mut pass_env = false; let mut pass_class = false; @@ -62,29 +63,10 @@ impl ArgumentOptions { pat: syn::Ident::new(&pat.to_string(), Span::call_site()), ty: ty.ty.clone(), }); - // if env.is_none() { - // if type_equals(ty.ty.clone(), "JNIEnv") { - // env = Some(syn::Ident::new(&pat.to_string(), Span::call_site())); - // } else { - // let envv = ; - // incoming.append_all(quote::quote!( #envv: jni::JNIEnv<'local>,)); - // env = Some(envv); - // } - // } - // if class.is_none() && !type_equals(ty.ty.clone(), "JNIEnv") { - // if type_equals(ty.ty.clone(), "JClass") { - // class = Some(syn::Ident::new(&pat.to_string(), Span::call_site())); - // } else { - // let classs = syn::Ident::new("class", Span::call_site()); - // incoming.append_all(quote::quote!( #classs: jni::objects::JClass<'local>,)); - // class = Some(classs); - // } - // } - // incoming.append_all(quote::quote!( #ty , )); - // forwarding.append_all(quote::quote! ( #pat, )); } let mut incoming = TokenStream::new(); + let mut transforming = TokenStream::new(); let mut forwarding = TokenStream::new(); let env = if pass_env { @@ -115,12 +97,23 @@ impl ArgumentOptions { for arg in args_iter { let pat = arg.pat; + let new_pat = syn::Ident::new(&format!("{pat}_new"), Span::call_site()); let ty = arg.ty; - incoming.append_all(quote::quote!( mut #pat: #ty,)); - forwarding.append_all(quote::quote!( #pat,)); + transforming.append_all(quote::quote!{ + let #new_pat = match jni_toolbox::from_java_static::<#ty>(&mut #env, #pat) { + Ok(x) => x, + Err(e) => { + // TODO should we panic here instead? + let _ = #env.throw_new("java/lang/RuntimeException", format!("{e:?}")); + return #ret_expr; + }, + }; + }); + incoming.append_all(quote::quote!( mut #pat: <#ty as jni_toolbox::FromJava<'local>>::T,)); + forwarding.append_all(quote::quote!( #new_pat,)); } - Ok(Self { incoming, forwarding, env }) + Ok(Self { incoming, transforming, forwarding, env }) } } diff --git a/macro/src/wrapper.rs b/macro/src/wrapper.rs index 6fb38f6..2b90a56 100644 --- a/macro/src/wrapper.rs +++ b/macro/src/wrapper.rs @@ -12,8 +12,17 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re }; let attrs = AttrsOptions::parse_attr(attrs)?; - let args = ArgumentOptions::parse_args(&fn_item)?; let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?; + let return_expr = if ret.ty.is_none() { + quote::quote!( () ) + } else if attrs.return_pointer { + quote::quote!( std::ptr::null_mut() ) + } else { + quote::quote!( 0 ) + }; + + // 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(); @@ -32,20 +41,16 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re }; // ^----------------------------------^ - let return_expr = if attrs.return_pointer { - quote::quote!( std::ptr::null_mut() ) - } else { - quote::quote!( 0 ) - }; - let env_ident = args.env; let forwarding = args.forwarding; + let transforming = args.transforming; let body = if ret.result { // wrap errors if let Some(exception) = attrs.exception { // V----------------------------------V quote::quote! { { - use jni_toolbox::JniToolboxError; + use jni_toolbox::{JniToolboxError, FromJava}; + #transforming match #fn_name_inner(#forwarding) { Ok(ret) => ret, Err(e) => match #env_ident.throw_new(#exception, format!("{e:?}")) { @@ -60,10 +65,11 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // V----------------------------------V quote::quote! { { - use jni_toolbox::JniToolboxError; + use jni_toolbox::{JniToolboxError, FromJava}; // 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}"), @@ -87,6 +93,8 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re // V----------------------------------V quote::quote! { { + use jni_toolbox::{JniToolboxError, FromJava}; + #transforming #fn_name_inner(#forwarding) } } diff --git a/src/lib.rs b/src/lib.rs index a069c04..4489912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +use jni::objects::JString; pub use jni_toolbox_macro::jni; pub trait JniToolboxError: std::error::Error { @@ -15,3 +16,113 @@ impl JniToolboxError for jni::errors::JniError { "java/lang/RuntimeException".to_string() } } + +pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::T) -> Result { + T::from_java(env, val) +} + +pub trait FromJava<'j> : Sized { + type T : Sized; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result; +} + + + + +impl<'j> FromJava<'j> for bool { + type T = jni::sys::jboolean; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value == 1) + } +} + +impl<'j> FromJava<'j> for i64 { + type T = jni::sys::jlong; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for i32 { + type T = jni::sys::jint; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for i16 { + type T = jni::sys::jshort; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for f32 { + type T = jni::sys::jfloat; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for f64 { + type T = jni::sys::jdouble; + + #[inline] + fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result { + Ok(value) + } +} + +impl<'j> FromJava<'j> for String { + type T = jni::objects::JString<'j>; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + if value.is_null() { return Err(jni::errors::Error::NullPtr("string can't be null")) }; + Ok(env.get_string(&value)?.into()) + } +} + +impl<'j> FromJava<'j> for Option { + type T = jni::objects::JString<'j>; + + fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result { + if value.is_null() { return Ok(None) }; + Ok(Some(String::from_java(env, value)?)) + } +} + + + + + + + + + + + + +pub trait IntoJava<'j> { + type T; + + fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result; +} + +impl<'j> IntoJava<'j> for String { + type T = JString<'j>; + + fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result { + env.new_string(self) + } +}