use proc_macro2::{Ident, Literal, TokenStream}; use quote::{format_ident, quote}; use syn::fold::{fold_fn_arg, fold_signature, Fold}; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{Block, FnArg, Signature, Token, Visibility}; pub struct ArgLength { pub length: Literal, } impl Parse for ArgLength { fn parse(input: ParseStream) -> Result { let vars = Punctuated::::parse_terminated(input)?; Ok(ArgLength { length: vars .first() .cloned() .unwrap_or_else(|| Literal::usize_unsuffixed(0)), }) } } pub struct JsFunction { pub args: Vec, pub name: Option, pub signature: Option, pub signature_raw: Option, pub block: Vec, pub visibility: Visibility, } impl JsFunction { pub fn new() -> Self { JsFunction { args: vec![], name: None, signature: None, signature_raw: None, visibility: Visibility::Inherited, block: vec![], } } } impl Fold for JsFunction { fn fold_fn_arg(&mut self, arg: FnArg) -> FnArg { self.args.push(arg.clone()); fold_fn_arg(self, arg) } fn fold_signature(&mut self, signature: Signature) -> Signature { self.name = Some(format_ident!("{}", signature.ident)); let mut new_signature = signature.clone(); new_signature.ident = format_ident!("_generated_{}_generated_", signature.ident); self.signature = Some(new_signature); self.signature_raw = Some(signature.clone()); fold_signature(self, signature) } fn fold_visibility(&mut self, v: Visibility) -> Visibility { self.visibility = v.clone(); v } fn fold_block(&mut self, node: Block) -> Block { self.block.push(node.clone()); node } } pub enum FunctionKind { Contextless, JsFunction, } pub fn get_execute_js_code(new_fn_name: Ident, function_kind: FunctionKind) -> TokenStream { let return_token_stream = match function_kind { FunctionKind::Contextless => { quote! { Ok(Some(v)) => unsafe { v.raw() }, Ok(None) => ptr::null_mut(), } } FunctionKind::JsFunction => { quote! { Ok(v) => unsafe { v.raw() }, } } }; quote! { match panic::catch_unwind(AssertUnwindSafe(move || #new_fn_name(ctx))).map_err(|e| { let message = { if let Some(string) = e.downcast_ref::() { string.clone() } else if let Some(string) = e.downcast_ref::<&str>() { string.to_string() } else { format!("panic from Rust code: {:?}", e) } }; Error::from_reason(message) }).and_then(|v| v) { #return_token_stream Err(e) => { unsafe { napi::JsError::from(e).throw_into(raw_env) }; ptr::null_mut() } } } }