1 //! This crate contains the part of the implementation of the `#[wasm_bindgen]` optsibute that is
2 //! not in the shared backend crate.
3 
4 #![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro-support/0.2")]
5 
6 extern crate proc_macro2;
7 extern crate quote;
8 #[macro_use]
9 extern crate syn;
10 #[macro_use]
11 extern crate wasm_bindgen_backend as backend;
12 extern crate wasm_bindgen_shared as shared;
13 
14 pub use crate::parser::BindgenAttrs;
15 use crate::parser::MacroParse;
16 use backend::{Diagnostic, TryToTokens};
17 use proc_macro2::TokenStream;
18 use quote::ToTokens;
19 use quote::TokenStreamExt;
20 use syn::parse::{Parse, ParseStream, Result as SynResult};
21 
22 mod parser;
23 
24 /// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic>25 pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
26     parser::reset_attrs_used();
27     let item = syn::parse2::<syn::Item>(input)?;
28     let opts = syn::parse2(attr)?;
29 
30     let mut tokens = proc_macro2::TokenStream::new();
31     let mut program = backend::ast::Program::default();
32     item.macro_parse(&mut program, (Some(opts), &mut tokens))?;
33     program.try_to_tokens(&mut tokens)?;
34 
35     // If we successfully got here then we should have used up all attributes
36     // and considered all of them to see if they were used. If one was forgotten
37     // that's a bug on our end, so sanity check here.
38     parser::assert_all_attrs_checked();
39 
40     Ok(tokens)
41 }
42 
43 /// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
expand_class_marker( attr: TokenStream, input: TokenStream, ) -> Result<TokenStream, Diagnostic>44 pub fn expand_class_marker(
45     attr: TokenStream,
46     input: TokenStream,
47 ) -> Result<TokenStream, Diagnostic> {
48     parser::reset_attrs_used();
49     let mut item = syn::parse2::<syn::ImplItemMethod>(input)?;
50     let opts: ClassMarker = syn::parse2(attr)?;
51 
52     let mut program = backend::ast::Program::default();
53     item.macro_parse(&mut program, (&opts.class, &opts.js_class))?;
54     parser::assert_all_attrs_checked(); // same as above
55 
56     // This is where things are slightly different, we are being expanded in the
57     // context of an impl so we can't inject arbitrary item-like tokens into the
58     // output stream. If we were to do that then it wouldn't parse!
59     //
60     // Instead what we want to do is to generate the tokens for `program` into
61     // the header of the function. This'll inject some no_mangle functions and
62     // statics and such, and they should all be valid in the context of the
63     // start of a function.
64     //
65     // We manually implement `ToTokens for ImplItemMethod` here, injecting our
66     // program's tokens before the actual method's inner body tokens.
67     let mut tokens = proc_macro2::TokenStream::new();
68     tokens.append_all(item.attrs.iter().filter(|attr| match attr.style {
69         syn::AttrStyle::Outer => true,
70         _ => false,
71     }));
72     item.vis.to_tokens(&mut tokens);
73     item.sig.to_tokens(&mut tokens);
74     let mut err = None;
75     item.block.brace_token.surround(&mut tokens, |tokens| {
76         if let Err(e) = program.try_to_tokens(tokens) {
77             err = Some(e);
78         }
79         tokens.append_all(item.attrs.iter().filter(|attr| match attr.style {
80             syn::AttrStyle::Inner(_) => true,
81             _ => false,
82         }));
83         tokens.append_all(&item.block.stmts);
84     });
85 
86     if let Some(err) = err {
87         return Err(err);
88     }
89 
90     Ok(tokens)
91 }
92 
93 struct ClassMarker {
94     class: syn::Ident,
95     js_class: String,
96 }
97 
98 impl Parse for ClassMarker {
parse(input: ParseStream) -> SynResult<Self>99     fn parse(input: ParseStream) -> SynResult<Self> {
100         let class = input.parse::<syn::Ident>()?;
101         input.parse::<Token![=]>()?;
102         let js_class = input.parse::<syn::LitStr>()?.value();
103         Ok(ClassMarker { class, js_class })
104     }
105 }
106