deno_bindgen_macro/
lib.rs

1// Copyright 2020-2021 the Deno authors. All rights reserved. MIT license.
2
3use proc_macro::TokenStream;
4use quote::format_ident;
5use quote::quote;
6use std::env;
7use std::fs::OpenOptions;
8use std::io::Read;
9use std::io::Write;
10use std::path::Path;
11use syn::parse_macro_input;
12use syn::parse_quote;
13use syn::ItemFn;
14
15mod attrs;
16mod derive_fn;
17mod derive_struct;
18mod docs;
19mod meta;
20
21use crate::derive_fn::process_function;
22use crate::derive_struct::process_struct;
23use crate::meta::Glue;
24use crate::meta::Type;
25
26#[cfg(target_endian = "little")]
27const ENDIANNESS: bool = true;
28
29#[cfg(target_endian = "big")]
30const ENDIANNESS: bool = false;
31
32#[proc_macro_attribute]
33pub fn deno_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
34  let metafile_path: String = match env::var("OUT_DIR") {
35    Ok(out_dir) => Path::new(&out_dir)
36      .join("bindings.json")
37      .into_os_string()
38      .into_string()
39      .unwrap(),
40    Err(_e) => String::from("bindings.json"),
41  };
42
43  let mut metadata: Glue =
44    match OpenOptions::new().read(true).open(metafile_path.as_str()) {
45      Ok(mut fd) => {
46        let mut meta = String::new();
47        fd.read_to_string(&mut meta)
48          .expect("Error reading meta file");
49
50        serde_json::from_str(&meta).unwrap_or_default()
51      }
52      Err(_) => Glue {
53        little_endian: ENDIANNESS,
54        name: env::var("CARGO_CRATE_NAME").unwrap_or_default(),
55        ..Default::default()
56      },
57    };
58
59  let mut metafile = OpenOptions::new()
60    .write(true)
61    .create(true)
62    .open(metafile_path.as_str())
63    .expect("Error opening meta file");
64
65  match syn::parse::<ItemFn>(input.clone()) {
66    Ok(func) => {
67      let attr = parse_macro_input!(attr as syn::AttributeArgs);
68      let symbol = process_function(func.clone(), attr, &mut metadata).unwrap();
69
70      let mut params = vec![];
71      let mut overrides = vec![];
72      let mut input_idents = vec![];
73      let mut c_index = 0;
74
75      for parameter in symbol.parameters {
76        match parameter {
77          Type::StructEnum { .. } => {
78            let ident = format_ident!("arg{}", c_index.to_string());
79            params.push(quote! { #ident: *const u8 });
80
81            c_index += 1;
82            let len_ident = format_ident!("arg{}", c_index.to_string());
83            params.push(quote! { #len_ident: usize });
84
85            overrides.push(quote! {
86              let buf = unsafe {
87                ::std::slice::from_raw_parts(#ident, #len_ident)
88              };
89              let #ident = deno_bindgen::serde_json::from_slice(buf).unwrap();
90            });
91
92            input_idents.push(ident);
93          }
94          Type::Str | Type::Buffer | Type::BufferMut => {
95            let ident = format_ident!("arg{}", c_index.to_string());
96            match parameter {
97              Type::Str | Type::Buffer => {
98                params.push(quote! { #ident: *const u8 })
99              }
100              Type::BufferMut => params.push(quote! { #ident: *mut u8 }),
101              _ => unreachable!(),
102            };
103
104            c_index += 1;
105            let len_ident = format_ident!("arg{}", c_index.to_string());
106            params.push(quote! { #len_ident: usize });
107
108            let return_type = match parameter {
109              Type::Str => quote! { ::std::str::from_utf8(buf).unwrap() },
110              Type::Buffer | Type::BufferMut => quote! { buf },
111              _ => unreachable!(),
112            };
113
114            let buf_expr = match parameter {
115              Type::Str | Type::Buffer => {
116                quote! { let buf = ::std::slice::from_raw_parts(#ident, #len_ident); }
117              }
118              Type::BufferMut => {
119                // https://github.com/littledivy/deno_bindgen/issues/26
120                // *mut u8 should never outlive the symbol call. This can lead to UB.
121                quote! { let mut buf: &'sym mut [u8] = ::std::slice::from_raw_parts_mut(#ident, #len_ident);
122                }
123              }
124              _ => unreachable!(),
125            };
126
127            overrides.push(quote! {
128              let #ident = unsafe {
129                #buf_expr
130                #return_type
131              };
132            });
133
134            input_idents.push(ident);
135          }
136          // TODO
137          _ => {
138            let ident = format_ident!("arg{}", c_index.to_string());
139            let ty = syn::Type::from(parameter);
140            params.push(quote! { #ident: #ty });
141            input_idents.push(ident);
142          }
143        };
144
145        c_index += 1;
146      }
147
148      let (result, transformer) = match symbol.result {
149        Type::Buffer
150        // Note that this refers to an owned String
151        // and not a `&str`
152        | Type::Str => {
153          let ty = parse_quote! { *const u8 };
154          let slice = match symbol.result {
155            Type::Str => quote! {
156              result.as_bytes()
157            },
158            _ => quote! { result }
159          };
160          let transformer = quote! {
161            let length = (result.len() as u32).to_be_bytes();
162            let mut v = length.to_vec();
163            v.extend_from_slice(&#slice);
164
165            ::std::mem::forget(result);
166            let result = v.as_ptr();
167            // Leak the result to JS land.
168            ::std::mem::forget(v);
169            result
170          };
171
172          (ty, transformer)
173        }
174        Type::StructEnum { .. } => {
175          let ty = parse_quote! { *const u8 };
176          let transformer = quote! {
177            let json = deno_bindgen::serde_json::to_string(&result).expect("Failed to serialize as JSON");
178            let encoded = json.into_bytes();
179            let length = (encoded.len() as u32).to_be_bytes();
180            let mut v = length.to_vec();
181            v.extend(encoded.clone());
182
183            let ret = v.as_ptr();
184            // Leak the result to JS land.
185            ::std::mem::forget(v);
186            ret
187          };
188
189          (ty, transformer)
190        }
191        Type::Ptr => (parse_quote! { *const u8 }, quote! { result }),
192        _ => (syn::Type::from(symbol.result), quote! { result }),
193      };
194
195      let name = &func.sig.ident;
196      let fn_inputs = &func.sig.inputs;
197      let fn_output = &func.sig.output;
198      let fn_generics = &func.sig.generics;
199      let fn_block = &func.block;
200
201      let overrides = overrides
202        .iter()
203        .fold(quote! {}, |acc, new| quote! { #acc #new });
204
205      metafile
206        .write_all(&serde_json::to_vec(&metadata).unwrap())
207        .unwrap();
208
209      TokenStream::from(quote! {
210        #[no_mangle]
211        pub extern "C" fn #name <'sym> (#(#params,) *) -> #result {
212          fn __inner_impl #fn_generics (#fn_inputs) #fn_output #fn_block
213          #overrides
214          let result = __inner_impl(#(#input_idents, ) *);
215          #transformer
216        }
217      })
218    }
219    Err(_) => {
220      let input = syn::parse_macro_input!(input as syn::DeriveInput);
221      process_struct(&mut metadata, input.clone()).unwrap();
222
223      metafile
224        .write_all(&serde_json::to_vec(&metadata).unwrap())
225        .unwrap();
226
227      TokenStream::from(quote! {
228        #[derive(::serde::Deserialize,::serde::Serialize)]
229        #input
230      })
231    }
232  }
233}