1use quote::quote;
2use syn::spanned::Spanned;
3
4#[proc_macro]
5pub fn init(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
6 init_impl(input.into())
7 .unwrap_or_else(|e| e.to_compile_error())
8 .into()
9}
10
11fn init_impl(input: proc_macro2::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
12 let input: syn::Ident = syn::parse2(input)?;
13 let code = quote! {
14 #[no_mangle]
15 pub unsafe extern "C" fn napi_register_module_v1(env: node_api::sys::napi_env, exports: node_api::sys::napi_value) -> node_api::sys::napi_value {
16 let env = node_api::Env::from_raw(env);
17 let exports = node_api::Value::from_raw(env, exports);
18 let result = std::panic::catch_unwind(|| #input(env, exports));
19 let result = match result {
20 Ok(result) => result,
21 Err(panic_info) => {
22 env.throw_error("A panic occurred.");
23 return std::ptr::null_mut();
24 },
25 };
26 let exports = match result {
27 Ok(exports) => exports,
28 Err(error) => {
29 if !env.is_exception_pending() {
30 env.throw_error(&format!("{}", error));
31 }
32 return std::ptr::null_mut();
33 }
34 };
35 exports.raw()
36 }
37 };
38 Ok(code)
39}
40
41#[proc_macro_attribute]
42pub fn function(
43 _attr: proc_macro::TokenStream,
44 input: proc_macro::TokenStream,
45) -> proc_macro::TokenStream {
46 function_impl(input.into())
47 .unwrap_or_else(|e| e.to_compile_error())
48 .into()
49}
50
51fn function_impl(input: proc_macro2::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
52 let input: syn::ItemFn = syn::parse2(input)?;
53 let visibility = &input.vis;
54 let ident = &input.sig.ident;
55 let impl_inputs = input.sig.inputs.iter().skip(1);
56 let impl_output = &input.sig.output;
57 let impl_block = &input.block;
58 let args = input
59 .sig
60 .inputs
61 .iter()
62 .skip(1)
63 .map(|input| {
64 let input = match input {
65 syn::FnArg::Typed(arg) => arg,
66 syn::FnArg::Receiver(_) => {
67 return Err(syn::Error::new(
68 input.span(),
69 "receiver arg is not allowed here",
70 ))
71 }
72 };
73 let ident = match &*input.pat {
74 syn::Pat::Ident(pat_ident) => &pat_ident.ident,
75 _ => return Err(syn::Error::new(input.pat.span(), "invalid pattern")),
76 };
77 Ok(ident)
78 })
79 .collect::<syn::Result<Vec<_>>>()?;
80 let args_count = args.len();
81 let from_node_api_statements = args
82 .iter()
83 .enumerate()
84 .map(|(i, ident)| {
85 let code = quote! {
86 let #ident = argv[#i];
87 let #ident = node_api::Value::from_raw(env, #ident);
88 let #ident = node_api::FromNodeAPI::from_node_api(#ident)?;
89 };
90 Ok(code)
91 })
92 .collect::<syn::Result<Vec<_>>>()?;
93 let code = quote! {
94 #visibility unsafe extern "C" fn #ident(env: node_api::sys::napi_env, info: node_api::sys::napi_callback_info) -> node_api::sys::napi_value {
95 fn function_impl<'a>(env: node_api::Env<'a>, #(#impl_inputs),*) #impl_output #impl_block
96 let env = node_api::Env::from_raw(env);
97 let result = std::panic::catch_unwind(|| -> node_api::Result<_> {
98 let mut argc = #args_count;
99 let mut argv: [node_api::sys::napi_value; #args_count] = [std::ptr::null_mut(); #args_count];
100 let status = node_api::sys::napi_get_cb_info(
101 env.raw(),
102 info,
103 &mut argc as *mut usize,
104 argv.as_mut_ptr() as *mut node_api::sys::napi_value,
105 std::ptr::null_mut(),
106 std::ptr::null_mut()
107 );
108 if status != node_api::sys::napi_status::napi_ok {
109 return Err(node_api::Error::from_last_node_api_error(env.raw(), status).into());
110 }
111 if argc != #args_count {
112 return Err(node_api::Error::message("invalid number of arguments").into());
113 }
114 #(#from_node_api_statements)*
115 let output = function_impl(env, #(#args),*).map_err(|error| node_api::Error::message(error.to_string()))?;
116 let output = node_api::IntoNodeApi::into_node_api(output, env)?;
117 Ok(output)
118 });
119 let result = match result {
120 Ok(result) => result,
121 Err(_) => {
122 env.throw_error("A panic occurred.");
123 return std::ptr::null_mut();
124 },
125 };
126 let output = match result {
127 Ok(output) => output,
128 Err(error) => {
129 if !env.is_exception_pending() {
130 env.throw_error(&format!("{}", error));
131 }
132 return std::ptr::null_mut();
133 }
134 };
135 output.raw()
136 }
137 };
138 Ok(code)
139}