extern crate proc_macro;
extern crate proc_macro2;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use syn::{parse_macro_input, Expr, FnArg, ItemFn, ReturnType, LitStr};
use quote::quote;
#[proc_macro_attribute]
pub fn call_from_java(macro_args: TokenStream, user_function: TokenStream) -> TokenStream {
let cloned_user_function = user_function.clone();
let macro_arg = parse_macro_input!(macro_args as LitStr);
let user_function = parse_macro_input!(user_function as ItemFn);
let mut generated = impl_call_from_java_macro(&user_function, macro_arg);
generated.extend(cloned_user_function.into_iter());
generated
}
fn impl_call_from_java_macro(user_function: &ItemFn, macro_arg: LitStr) -> TokenStream {
let jni_ident_string = format!("Java_{}", macro_arg.value().replace(".", "_"));
let ref jni_ident = Ident::new(jni_ident_string.as_ref(), Span::call_site());
let user_function_signature = &user_function.sig;
let user_function_name = &user_function_signature.ident;
let user_function_args = &user_function_signature.inputs;
let user_function_arg_names: Vec<String> = user_function_args
.iter()
.map(|arg| {
let a = arg.clone();
let q = quote!(#a).to_string();
let v: Vec<&str> = q.split(' ').collect();
v.get(0)
.expect(&format!("Could not locate the argument name for: {}", q))
.to_string()
})
.collect();
let jni_function_args: Vec<FnArg> = user_function_arg_names
.iter()
.map(|arg| {
let a: FnArg = syn::parse_str(&format!("{}: jobject", arg)).unwrap();
a
})
.collect();
let ref jni_function_output = match &user_function_signature.output {
ReturnType::Default => ReturnType::Default,
_ => {
let ret_type: ReturnType = syn::parse_str("-> jobject").unwrap();
ret_type
}
};
let return_value = match &user_function_signature.output {
ReturnType::Default => {
let ret_value: Expr = syn::parse_str("()").unwrap();
ret_value
}
_ => {
let ret_value: Expr = syn::parse_str(
r#"match instance_to_return {
Ok(i) => {
i.java_object()
// i.as_java_ptr_with_local_ref(jni_env).unwrap()
},
Err(error) => {
let message = format!("{}", error);
let _ = jvm.throw_invocation_exception(&message);
ptr::null_mut()
},
}"#,
).unwrap();
ret_value
}
};
let instance_args_to_pass_to_user_function: Vec<Expr> = user_function_arg_names.iter()
.map(|jobj_arg_name| {
let expression: Expr = syn::parse_str(&format!("Instance::from_jobject_with_global_ref({}).expect(\"Could not create Instance from jobject\")", jobj_arg_name)).unwrap();
expression
})
.collect();
let gen = quote! {
#[no_mangle]
pub fn #jni_ident(jni_env: *mut JNIEnv, _class: *const c_void, #(#jni_function_args),*) #jni_function_output {
match unsafe {Jvm::try_from(jni_env)} {
Ok(mut jvm) => {
jvm.detach_thread_on_drop(false);
let instance_to_return = #user_function_name(#(#instance_args_to_pass_to_user_function),*);
#return_value
},
Err(error) => {
let message = format!("Could not attach to the JVM thread: {}", error);
println!("{}", message);
panic!("{}", message);
},
}
}
};
gen.into()
}