mirror of
https://github.com/hexedtech/jni-toolbox.git
synced 2024-11-22 15:34:56 +01:00
alemi
5e69eca9d6
basically it's all IntoJava, but there's another trait layer, IntoJavaObject, which downstream library users should implement. Every IntoJavaObject automatically implements IntoJava too
122 lines
3.4 KiB
Rust
122 lines
3.4 KiB
Rust
use proc_macro2::{Span, TokenStream};
|
|
use quote::TokenStreamExt;
|
|
use syn::Item;
|
|
|
|
use crate::{args::ArgumentOptions, attrs::AttrsOptions, ret::ReturnOptions};
|
|
|
|
pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Result<TokenStream, syn::Error> {
|
|
let mut out = TokenStream::new();
|
|
|
|
let Item::Fn(fn_item) = syn::parse2(input.clone())? else {
|
|
return Err(syn::Error::new(Span::call_site(), "#[jni] is only supported on functions"));
|
|
};
|
|
|
|
let attrs = AttrsOptions::parse_attr(attrs)?;
|
|
let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?;
|
|
let return_expr = if ret.void {
|
|
quote::quote!( () )
|
|
} else if ret.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();
|
|
|
|
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_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 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 {
|
|
quote::quote! {
|
|
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 {
|
|
quote::quote! {
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
};
|
|
}
|
|
}
|
|
} else {
|
|
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);
|
|
Ok(out)
|
|
}
|