2018-07-29 17:59:46 +02:00
|
|
|
//! This crate contains the part of the implementation of the `#[wasm_bindgen]` optsibute that is
|
|
|
|
//! not in the shared backend crate.
|
|
|
|
|
|
|
|
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro-support/0.2")]
|
|
|
|
|
|
|
|
extern crate proc_macro2;
|
|
|
|
extern crate quote;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate syn;
|
2018-08-01 18:59:59 -05:00
|
|
|
#[macro_use]
|
2018-07-29 17:59:46 +02:00
|
|
|
extern crate wasm_bindgen_backend as backend;
|
|
|
|
extern crate wasm_bindgen_shared as shared;
|
|
|
|
|
2018-09-26 08:26:00 -07:00
|
|
|
use backend::{Diagnostic, TryToTokens};
|
2018-07-29 17:59:46 +02:00
|
|
|
pub use parser::BindgenAttrs;
|
2019-01-25 14:31:50 -08:00
|
|
|
use quote::ToTokens;
|
2018-07-29 17:59:46 +02:00
|
|
|
use parser::MacroParse;
|
2018-08-01 17:15:27 -05:00
|
|
|
use proc_macro2::TokenStream;
|
2019-01-25 14:31:50 -08:00
|
|
|
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
|
|
|
use quote::TokenStreamExt;
|
2018-07-29 17:59:46 +02:00
|
|
|
|
|
|
|
mod parser;
|
|
|
|
|
|
|
|
/// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
|
2018-08-01 17:15:27 -05:00
|
|
|
pub fn expand(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
|
2018-11-27 15:14:59 -08:00
|
|
|
parser::reset_attrs_used();
|
2018-09-04 11:32:09 -07:00
|
|
|
let item = syn::parse2::<syn::Item>(input)?;
|
|
|
|
let opts = syn::parse2(attr)?;
|
2018-08-01 17:15:27 -05:00
|
|
|
|
2018-07-29 17:59:46 +02:00
|
|
|
let mut tokens = proc_macro2::TokenStream::new();
|
|
|
|
let mut program = backend::ast::Program::default();
|
2018-08-01 17:15:27 -05:00
|
|
|
item.macro_parse(&mut program, (Some(opts), &mut tokens))?;
|
|
|
|
program.try_to_tokens(&mut tokens)?;
|
2018-11-27 15:14:59 -08:00
|
|
|
|
|
|
|
// If we successfully got here then we should have used up all attributes
|
|
|
|
// and considered all of them to see if they were used. If one was forgotten
|
|
|
|
// that's a bug on our end, so sanity check here.
|
|
|
|
parser::assert_all_attrs_checked();
|
|
|
|
|
2018-08-01 17:15:27 -05:00
|
|
|
Ok(tokens)
|
|
|
|
}
|
2019-01-25 14:31:50 -08:00
|
|
|
|
|
|
|
/// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
|
|
|
|
pub fn expand_class_marker(attr: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
|
|
|
|
parser::reset_attrs_used();
|
|
|
|
let mut item = syn::parse2::<syn::ImplItemMethod>(input)?;
|
|
|
|
let opts: ClassMarker = syn::parse2(attr)?;
|
|
|
|
|
|
|
|
let mut program = backend::ast::Program::default();
|
|
|
|
item.macro_parse(&mut program, (&opts.class, &opts.js_class))?;
|
|
|
|
parser::assert_all_attrs_checked(); // same as above
|
|
|
|
|
|
|
|
// This is where things are slightly different, we are being expanded in the
|
|
|
|
// context of an impl so we can't inject arbitrary item-like tokens into the
|
|
|
|
// output stream. If we were to do that then it wouldn't parse!
|
|
|
|
//
|
|
|
|
// Instead what we want to do is to generate the tokens for `program` into
|
|
|
|
// the header of the function. This'll inject some no_mangle functions and
|
|
|
|
// statics and such, and they should all be valid in the context of the
|
|
|
|
// start of a function.
|
|
|
|
//
|
|
|
|
// We manually implement `ToTokens for ImplItemMethod` here, injecting our
|
|
|
|
// program's tokens before the actual method's inner body tokens.
|
|
|
|
let mut tokens = proc_macro2::TokenStream::new();
|
|
|
|
tokens.append_all(item.attrs.iter().filter(|attr| {
|
|
|
|
match attr.style {
|
|
|
|
syn::AttrStyle::Outer => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
item.vis.to_tokens(&mut tokens);
|
|
|
|
item.sig.to_tokens(&mut tokens);
|
|
|
|
let mut err = None;
|
|
|
|
item.block.brace_token.surround(&mut tokens, |tokens| {
|
|
|
|
if let Err(e) = program.try_to_tokens(tokens) {
|
|
|
|
err = Some(e);
|
|
|
|
}
|
|
|
|
tokens.append_all(item.attrs.iter().filter(|attr| {
|
|
|
|
match attr.style {
|
|
|
|
syn::AttrStyle::Inner(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
tokens.append_all(&item.block.stmts);
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Some(err) = err {
|
|
|
|
return Err(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(tokens)
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ClassMarker {
|
|
|
|
class: syn::Ident,
|
|
|
|
js_class: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Parse for ClassMarker {
|
|
|
|
fn parse(input: ParseStream) -> SynResult<Self> {
|
|
|
|
let class = input.parse::<syn::Ident>()?;
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
let js_class = input.parse::<syn::LitStr>()?.value();
|
|
|
|
Ok(ClassMarker { class, js_class })
|
|
|
|
}
|
|
|
|
}
|