napi_rs_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::{Ident, Literal};
5use quote::{format_ident, quote};
6use syn::fold::{fold_fn_arg, fold_signature, Fold};
7use syn::parse::{Parse, ParseStream, Result};
8use syn::punctuated::Punctuated;
9use syn::{parse_macro_input, Block, FnArg, ItemFn, Signature, Token};
10
11struct ArgLength {
12  length: Option<Literal>,
13}
14
15impl Parse for ArgLength {
16  fn parse(input: ParseStream) -> Result<Self> {
17    let vars = Punctuated::<Literal, Token![,]>::parse_terminated(input)?;
18    Ok(ArgLength {
19      length: vars.first().map(|i| i.clone()),
20    })
21  }
22}
23
24struct JsFunction {
25  args: Vec<FnArg>,
26  name: Option<Ident>,
27  signature: Option<Signature>,
28  block: Vec<Block>,
29}
30
31impl JsFunction {
32  pub fn new() -> Self {
33    JsFunction {
34      args: vec![],
35      name: None,
36      signature: None,
37      block: vec![],
38    }
39  }
40}
41
42impl Fold for JsFunction {
43  fn fold_fn_arg(&mut self, arg: FnArg) -> FnArg {
44    self.args.push(arg.clone());
45    fold_fn_arg(self, arg)
46  }
47
48  fn fold_signature(&mut self, signature: Signature) -> Signature {
49    self.name = Some(format_ident!("{}", signature.ident));
50    let mut new_signature = signature.clone();
51    new_signature.ident = format_ident!("_{}", signature.ident);
52    self.signature = Some(new_signature);
53    fold_signature(self, signature)
54  }
55
56  fn fold_block(&mut self, node: Block) -> Block {
57    self.block.push(node.clone());
58    node
59  }
60}
61
62#[proc_macro_attribute]
63pub fn js_function(attr: TokenStream, input: TokenStream) -> TokenStream {
64  let arg_len = parse_macro_input!(attr as ArgLength);
65  let arg_len_span = arg_len.length.unwrap_or(Literal::usize_unsuffixed(0));
66  let input = parse_macro_input!(input as ItemFn);
67  let mut js_fn = JsFunction::new();
68  js_fn.fold_item_fn(input);
69  let fn_name = js_fn.name.unwrap();
70  let fn_block = js_fn.block;
71  let signature = js_fn.signature.unwrap();
72  let new_fn_name = signature.ident.clone();
73  let expanded = quote! {
74    #signature #(#fn_block)*
75
76    extern "C" fn #fn_name(
77      raw_env: napi_rs::sys::napi_env,
78      cb_info: napi_rs::sys::napi_callback_info,
79    ) -> napi_rs::sys::napi_value {
80      use std::io::Write;
81      use std::mem;
82      use std::os::raw::c_char;
83      use std::ptr;
84      use napi_rs::{Any, Env, Status, Value, CallContext};
85      let mut argc = #arg_len_span as usize;
86      let mut raw_args =
87      unsafe { mem::MaybeUninit::<[napi_rs::sys::napi_value; 8]>::uninit().assume_init() };
88      let mut raw_this = ptr::null_mut();
89
90      let mut has_error = false;
91
92      unsafe {
93        let status = napi_rs::sys::napi_get_cb_info(
94          raw_env,
95          cb_info,
96          &mut argc as *mut usize as *mut u64,
97          &mut raw_args[0],
98          &mut raw_this,
99          ptr::null_mut(),
100        );
101        has_error = has_error && (Status::from(status) == Status::Ok);
102      }
103
104      let mut env = Env::from_raw(raw_env);
105      let call_ctx = CallContext::new(&mut env, raw_this, raw_args, #arg_len_span);
106      let result = call_ctx.and_then(|ctx| #new_fn_name(ctx));
107      has_error = has_error && result.is_err();
108
109      match result {
110        Ok(result) => result.into_raw(),
111        Err(e) => {
112          let message = format!("{:?}", e);
113          unsafe {
114            napi_rs::sys::napi_throw_error(raw_env, ptr::null(), message.as_ptr() as *const c_char);
115          }
116          let mut undefined = ptr::null_mut();
117          unsafe { napi_rs::sys::napi_get_undefined(raw_env, &mut undefined) };
118          undefined
119        }
120      }
121    }
122  };
123  // Hand the output tokens back to the compiler
124  TokenStream::from(expanded)
125}