2018-11-27 15:14:59 -08:00
|
|
|
use std::cell::Cell;
|
|
|
|
|
2018-07-17 18:24:48 -05:00
|
|
|
use backend::ast;
|
|
|
|
use backend::util::{ident_ty, ShortHash};
|
2018-08-08 14:42:53 -07:00
|
|
|
use backend::Diagnostic;
|
|
|
|
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
2018-07-07 10:20:31 -07:00
|
|
|
use quote::ToTokens;
|
|
|
|
use shared;
|
|
|
|
use syn;
|
2018-09-04 11:32:09 -07:00
|
|
|
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
thread_local!(static ATTRS: AttributeParseState = Default::default());
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct AttributeParseState {
|
|
|
|
parsed: Cell<usize>,
|
|
|
|
checks: Cell<usize>,
|
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Parsed attributes from a `#[wasm_bindgen(..)]`.
|
2018-07-07 10:20:31 -07:00
|
|
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
|
|
|
pub struct BindgenAttrs {
|
2018-07-25 11:42:01 +01:00
|
|
|
/// List of parsed attributes
|
2018-11-27 15:14:59 -08:00
|
|
|
pub attrs: Vec<(Cell<bool>, BindgenAttr)>,
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
macro_rules! attrgen {
|
2018-11-30 13:04:05 -08:00
|
|
|
($mac:ident) => {
|
2018-11-27 15:14:59 -08:00
|
|
|
$mac! {
|
|
|
|
(catch, Catch(Span)),
|
|
|
|
(constructor, Constructor(Span)),
|
|
|
|
(method, Method(Span)),
|
|
|
|
(static_method_of, StaticMethodOf(Span, Ident)),
|
|
|
|
(js_namespace, JsNamespace(Span, Ident)),
|
|
|
|
(module, Module(Span, String, Span)),
|
2019-03-15 08:04:25 -07:00
|
|
|
(raw_module, RawModule(Span, String, Span)),
|
2019-02-25 11:11:30 -08:00
|
|
|
(inline_js, InlineJs(Span, String, Span)),
|
2018-11-27 15:14:59 -08:00
|
|
|
(getter, Getter(Span, Option<Ident>)),
|
|
|
|
(setter, Setter(Span, Option<Ident>)),
|
|
|
|
(indexing_getter, IndexingGetter(Span)),
|
|
|
|
(indexing_setter, IndexingSetter(Span)),
|
|
|
|
(indexing_deleter, IndexingDeleter(Span)),
|
|
|
|
(structural, Structural(Span)),
|
|
|
|
(final_("final"), Final(Span)),
|
|
|
|
(readonly, Readonly(Span)),
|
|
|
|
(js_name, JsName(Span, String, Span)),
|
|
|
|
(js_class, JsClass(Span, String, Span)),
|
2019-03-26 19:29:46 +00:00
|
|
|
(is_type_of, IsTypeOf(Span, syn::Expr)),
|
2018-11-27 15:14:59 -08:00
|
|
|
(extends, Extends(Span, syn::Path)),
|
|
|
|
(vendor_prefix, VendorPrefix(Span, Ident)),
|
|
|
|
(variadic, Variadic(Span)),
|
|
|
|
(typescript_custom_section, TypescriptCustomSection(Span)),
|
2018-11-28 09:25:51 -08:00
|
|
|
(start, Start(Span)),
|
2019-03-30 05:09:02 +02:00
|
|
|
(skip, Skip(Span)),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-30 13:04:05 -08:00
|
|
|
};
|
2018-11-27 15:14:59 -08:00
|
|
|
}
|
2018-08-05 23:32:31 +03:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
macro_rules! methods {
|
|
|
|
($(($name:ident $(($other:tt))*, $variant:ident($($contents:tt)*)),)*) => {
|
|
|
|
$(methods!(@method $name, $variant($($contents)*));)*
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2019-05-20 20:18:24 -07:00
|
|
|
#[cfg(feature = "strict-macro")]
|
2018-11-27 15:14:59 -08:00
|
|
|
fn check_used(self) -> Result<(), Diagnostic> {
|
|
|
|
// Account for the fact this method was called
|
|
|
|
ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
|
2018-11-08 13:18:58 -08:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
let mut errors = Vec::new();
|
|
|
|
for (used, attr) in self.attrs.iter() {
|
|
|
|
if used.get() {
|
|
|
|
continue
|
|
|
|
}
|
2019-05-20 20:18:24 -07:00
|
|
|
// The check below causes rustc to crash on powerpc64 platforms
|
|
|
|
// with an LLVM error. To avoid this, we instead use #[cfg()]
|
|
|
|
// and duplicate the function below. See #58516 for details.
|
|
|
|
/*if !cfg!(feature = "strict-macro") {
|
2018-11-27 15:14:59 -08:00
|
|
|
continue
|
2019-05-20 20:18:24 -07:00
|
|
|
}*/
|
2018-11-27 15:14:59 -08:00
|
|
|
let span = match attr {
|
|
|
|
$(BindgenAttr::$variant(span, ..) => span,)*
|
|
|
|
};
|
|
|
|
errors.push(Diagnostic::span_error(*span, "unused #[wasm_bindgen] attribute"));
|
|
|
|
}
|
|
|
|
Diagnostic::from_vec(errors)
|
|
|
|
}
|
2019-05-20 20:18:24 -07:00
|
|
|
|
|
|
|
#[cfg(not(feature = "strict-macro"))]
|
|
|
|
fn check_used(self) -> Result<(), Diagnostic> {
|
|
|
|
// Account for the fact this method was called
|
|
|
|
ATTRS.with(|state| state.checks.set(state.checks.get() + 1));
|
2019-05-28 07:03:42 -07:00
|
|
|
Ok(())
|
2019-05-20 20:18:24 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
};
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
(@method $name:ident, $variant:ident(Span, String, Span)) => {
|
|
|
|
fn $name(&self) -> Option<(&str, Span)> {
|
|
|
|
self.attrs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|a| match &a.1 {
|
|
|
|
BindgenAttr::$variant(_, s, span) => {
|
|
|
|
a.0.set(true);
|
|
|
|
Some((&s[..], *span))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
}
|
|
|
|
};
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
(@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
|
|
|
|
#[allow(unused)]
|
|
|
|
fn $name(&self) -> Option<&$($other)*> {
|
|
|
|
self.attrs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|a| match &a.1 {
|
|
|
|
BindgenAttr::$variant(_, s) => {
|
|
|
|
a.0.set(true);
|
|
|
|
Some(s)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
}
|
|
|
|
};
|
2018-08-04 10:00:30 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
(@method $name:ident, $variant:ident($($other:tt)*)) => {
|
|
|
|
#[allow(unused)]
|
|
|
|
fn $name(&self) -> Option<&$($other)*> {
|
|
|
|
self.attrs
|
|
|
|
.iter()
|
|
|
|
.filter_map(|a| match &a.1 {
|
|
|
|
BindgenAttr::$variant(s) => {
|
|
|
|
a.0.set(true);
|
|
|
|
Some(s)
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2018-08-18 22:15:29 +01:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
impl BindgenAttrs {
|
|
|
|
/// Find and parse the wasm_bindgen attributes.
|
|
|
|
fn find(attrs: &mut Vec<syn::Attribute>) -> Result<BindgenAttrs, Diagnostic> {
|
|
|
|
let mut ret = BindgenAttrs::default();
|
|
|
|
loop {
|
|
|
|
let pos = attrs
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
|
|
|
|
.map(|a| a.0);
|
|
|
|
let pos = match pos {
|
|
|
|
Some(i) => i,
|
|
|
|
None => return Ok(ret),
|
|
|
|
};
|
|
|
|
let attr = attrs.remove(pos);
|
|
|
|
let mut tts = attr.tts.clone().into_iter();
|
|
|
|
let group = match tts.next() {
|
|
|
|
Some(TokenTree::Group(d)) => d,
|
|
|
|
Some(_) => bail_span!(attr, "malformed #[wasm_bindgen] attribute"),
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
if tts.next().is_some() {
|
|
|
|
bail_span!(attr, "malformed #[wasm_bindgen] attribute");
|
|
|
|
}
|
|
|
|
if group.delimiter() != Delimiter::Parenthesis {
|
|
|
|
bail_span!(attr, "malformed #[wasm_bindgen] attribute");
|
|
|
|
}
|
|
|
|
let mut attrs: BindgenAttrs = syn::parse2(group.stream())?;
|
|
|
|
ret.attrs.extend(attrs.attrs.drain(..));
|
|
|
|
attrs.check_used()?;
|
|
|
|
}
|
2018-09-28 13:17:37 -07:00
|
|
|
}
|
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
attrgen!(methods);
|
|
|
|
}
|
2018-11-17 23:04:19 -05:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
impl Default for BindgenAttrs {
|
|
|
|
fn default() -> BindgenAttrs {
|
|
|
|
// Add 1 to the list of parsed attribute sets. We'll use this counter to
|
|
|
|
// sanity check that we call `check_used` an appropriate number of
|
|
|
|
// times.
|
|
|
|
ATTRS.with(|state| state.parsed.set(state.parsed.get() + 1));
|
|
|
|
BindgenAttrs { attrs: Vec::new() }
|
2018-11-17 23:04:19 -05:00
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-09-04 11:32:09 -07:00
|
|
|
impl Parse for BindgenAttrs {
|
|
|
|
fn parse(input: ParseStream) -> SynResult<Self> {
|
2018-11-27 15:14:59 -08:00
|
|
|
let mut attrs = BindgenAttrs::default();
|
2018-09-04 11:32:09 -07:00
|
|
|
if input.is_empty() {
|
2018-11-30 13:04:05 -08:00
|
|
|
return Ok(attrs);
|
2018-09-04 11:32:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
|
2018-11-30 13:04:05 -08:00
|
|
|
attrs.attrs = opts.into_iter().map(|c| (Cell::new(false), c)).collect();
|
2018-11-27 15:14:59 -08:00
|
|
|
Ok(attrs)
|
2018-09-04 11:32:09 -07:00
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
macro_rules! gen_bindgen_attr {
|
|
|
|
($(($method:ident $(($other:tt))*, $($variants:tt)*),)*) => {
|
|
|
|
/// The possible attributes in the `#[wasm_bindgen]`.
|
|
|
|
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
|
|
|
pub enum BindgenAttr {
|
|
|
|
$($($variants)*,)*
|
|
|
|
}
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
attrgen!(gen_bindgen_attr);
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-09-04 11:32:09 -07:00
|
|
|
impl Parse for BindgenAttr {
|
|
|
|
fn parse(input: ParseStream) -> SynResult<Self> {
|
|
|
|
let original = input.fork();
|
2018-11-09 07:59:48 -08:00
|
|
|
let attr: AnyIdent = input.parse()?;
|
|
|
|
let attr = attr.0;
|
2018-11-27 15:14:59 -08:00
|
|
|
let attr_span = attr.span();
|
|
|
|
|
|
|
|
macro_rules! parsers {
|
|
|
|
($(($name:ident $(($other:tt))*, $($contents:tt)*),)*) => {
|
|
|
|
$(
|
|
|
|
if attr == parsers!(@attrname $name $($other)*) {
|
|
|
|
parsers!(
|
|
|
|
@parser
|
|
|
|
$($contents)*
|
|
|
|
);
|
|
|
|
}
|
|
|
|
)*
|
2018-11-05 12:29:14 -08:00
|
|
|
};
|
2018-11-27 15:14:59 -08:00
|
|
|
|
|
|
|
(@parser $variant:ident(Span)) => ({
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span));
|
|
|
|
});
|
|
|
|
|
|
|
|
(@parser $variant:ident(Span, Ident)) => ({
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
let ident = input.parse::<AnyIdent>()?.0;
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span, ident))
|
|
|
|
});
|
|
|
|
|
|
|
|
(@parser $variant:ident(Span, Option<Ident>)) => ({
|
|
|
|
if input.parse::<Token![=]>().is_ok() {
|
2018-09-21 17:29:50 -07:00
|
|
|
let ident = input.parse::<AnyIdent>()?.0;
|
2018-11-27 15:14:59 -08:00
|
|
|
return Ok(BindgenAttr::$variant(attr_span, Some(ident)))
|
|
|
|
} else {
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span, None));
|
2018-09-21 17:29:50 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
(@parser $variant:ident(Span, syn::Path)) => ({
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
|
|
|
|
});
|
|
|
|
|
2019-03-26 19:29:46 +00:00
|
|
|
(@parser $variant:ident(Span, syn::Expr)) => ({
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
|
|
|
|
});
|
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
(@parser $variant:ident(Span, String, Span)) => ({
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
let (val, span) = match input.parse::<syn::LitStr>() {
|
|
|
|
Ok(str) => (str.value(), str.span()),
|
|
|
|
Err(_) => {
|
|
|
|
let ident = input.parse::<AnyIdent>()?.0;
|
|
|
|
(ident.to_string(), ident.span())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return Ok(BindgenAttr::$variant(attr_span, val, span))
|
|
|
|
});
|
|
|
|
|
|
|
|
(@attrname $a:ident $b:tt) => ($b);
|
|
|
|
(@attrname $a:ident) => (stringify!($a));
|
2018-11-17 23:04:19 -05:00
|
|
|
}
|
2018-09-04 11:32:09 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
attrgen!(parsers);
|
|
|
|
|
|
|
|
return Err(original.error("unknown attribute"));
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-04 11:32:09 -07:00
|
|
|
struct AnyIdent(Ident);
|
|
|
|
|
|
|
|
impl Parse for AnyIdent {
|
|
|
|
fn parse(input: ParseStream) -> SynResult<Self> {
|
2018-09-26 08:26:00 -07:00
|
|
|
input.step(|cursor| match cursor.ident() {
|
|
|
|
Some((ident, remaining)) => Ok((AnyIdent(ident), remaining)),
|
|
|
|
None => Err(cursor.error("expected an identifier")),
|
2018-09-04 11:32:09 -07:00
|
|
|
})
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Conversion trait with context.
|
|
|
|
///
|
|
|
|
/// Used to convert syn tokens into an AST, that we can then use to generate glue code. The context
|
|
|
|
/// (`Ctx`) is used to pass in the attributes from the `#[wasm_bindgen]`, if needed.
|
2018-07-07 10:20:31 -07:00
|
|
|
trait ConvertToAst<Ctx> {
|
2018-07-25 11:42:01 +01:00
|
|
|
/// What we are converting to.
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target;
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Convert into our target.
|
|
|
|
///
|
|
|
|
/// Since this is used in a procedural macro, use panic to fail.
|
2018-08-01 18:59:59 -05:00
|
|
|
fn convert(self, context: Ctx) -> Result<Self::Target, Diagnostic>;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-11-05 12:29:14 -08:00
|
|
|
impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target = ast::Struct;
|
|
|
|
|
2019-03-30 05:09:02 +02:00
|
|
|
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
if self.generics.params.len() > 0 {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(
|
|
|
|
self.generics,
|
2018-07-07 10:20:31 -07:00
|
|
|
"structs with #[wasm_bindgen] cannot have lifetime or \
|
|
|
|
type parameters currently"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let mut fields = Vec::new();
|
2019-03-30 05:09:02 +02:00
|
|
|
let js_name = attrs
|
2018-11-27 12:07:59 -08:00
|
|
|
.js_name()
|
2018-11-05 12:29:14 -08:00
|
|
|
.map(|s| s.0.to_string())
|
|
|
|
.unwrap_or(self.ident.to_string());
|
2019-04-16 20:31:25 +01:00
|
|
|
for (i, field) in self.fields.iter_mut().enumerate() {
|
|
|
|
match field.vis {
|
|
|
|
syn::Visibility::Public(..) => {}
|
|
|
|
_ => continue,
|
|
|
|
}
|
|
|
|
let (name_str, member) = match &field.ident {
|
|
|
|
Some(ident) => (ident.to_string(), syn::Member::Named(ident.clone())),
|
|
|
|
None => (i.to_string(), syn::Member::Unnamed(i.into())),
|
|
|
|
};
|
2019-03-30 05:09:02 +02:00
|
|
|
|
2019-04-16 20:31:25 +01:00
|
|
|
let attrs = BindgenAttrs::find(&mut field.attrs)?;
|
|
|
|
assert_not_variadic(&attrs)?;
|
|
|
|
if attrs.skip().is_some() {
|
2019-03-30 05:09:02 +02:00
|
|
|
attrs.check_used()?;
|
2019-04-16 20:31:25 +01:00
|
|
|
continue;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2019-04-16 20:31:25 +01:00
|
|
|
|
|
|
|
let comments = extract_doc_comments(&field.attrs);
|
|
|
|
let getter = shared::struct_field_get(&js_name, &name_str);
|
|
|
|
let setter = shared::struct_field_set(&js_name, &name_str);
|
|
|
|
|
|
|
|
fields.push(ast::StructField {
|
|
|
|
name: member,
|
|
|
|
struct_name: self.ident.clone(),
|
|
|
|
readonly: attrs.readonly().is_some(),
|
|
|
|
ty: field.ty.clone(),
|
|
|
|
getter: Ident::new(&getter, Span::call_site()),
|
|
|
|
setter: Ident::new(&setter, Span::call_site()),
|
|
|
|
comments,
|
|
|
|
});
|
|
|
|
attrs.check_used()?;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
let comments: Vec<String> = extract_doc_comments(&self.attrs);
|
2019-03-30 05:09:02 +02:00
|
|
|
attrs.check_used()?;
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok(ast::Struct {
|
2018-11-05 12:29:14 -08:00
|
|
|
rust_name: self.ident.clone(),
|
|
|
|
js_name,
|
2018-07-07 10:20:31 -07:00
|
|
|
fields,
|
|
|
|
comments,
|
2018-08-01 18:59:59 -05:00
|
|
|
})
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 11:11:30 -08:00
|
|
|
impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignItemFn {
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target = ast::ImportKind;
|
|
|
|
|
2018-08-08 14:42:53 -07:00
|
|
|
fn convert(
|
|
|
|
self,
|
2019-02-25 11:11:30 -08:00
|
|
|
(opts, module): (BindgenAttrs, &'a ast::ImportModule),
|
2018-08-08 14:42:53 -07:00
|
|
|
) -> Result<Self::Target, Diagnostic> {
|
2018-08-01 18:59:59 -05:00
|
|
|
let wasm = function_from_decl(
|
2018-09-21 17:29:50 -07:00
|
|
|
&self.ident,
|
|
|
|
&opts,
|
2018-08-01 18:59:59 -05:00
|
|
|
self.decl.clone(),
|
|
|
|
self.attrs.clone(),
|
|
|
|
self.vis.clone(),
|
|
|
|
false,
|
2018-08-03 14:11:44 -05:00
|
|
|
None,
|
2018-11-27 12:07:59 -08:00
|
|
|
)?
|
|
|
|
.0;
|
2018-11-27 15:14:59 -08:00
|
|
|
let catch = opts.catch().is_some();
|
|
|
|
let variadic = opts.variadic().is_some();
|
2018-07-07 10:20:31 -07:00
|
|
|
let js_ret = if catch {
|
|
|
|
// TODO: this assumes a whole bunch:
|
|
|
|
//
|
|
|
|
// * The outer type is actually a `Result`
|
|
|
|
// * The error type is a `JsValue`
|
|
|
|
// * The actual type is the first type parameter
|
|
|
|
//
|
|
|
|
// should probably fix this one day...
|
2018-08-01 18:59:59 -05:00
|
|
|
extract_first_ty_param(wasm.ret.as_ref())?
|
2018-07-07 10:20:31 -07:00
|
|
|
} else {
|
|
|
|
wasm.ret.clone()
|
|
|
|
};
|
|
|
|
|
2019-06-16 22:24:27 -03:00
|
|
|
let operation_kind = operation_kind(&opts);
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
let kind = if opts.method().is_some() {
|
2018-08-08 14:42:53 -07:00
|
|
|
let class = wasm.arguments.get(0).ok_or_else(|| {
|
|
|
|
err_span!(self, "imported methods must have at least one argument")
|
|
|
|
})?;
|
2018-07-07 10:20:31 -07:00
|
|
|
let class = match class.ty {
|
|
|
|
syn::Type::Reference(syn::TypeReference {
|
|
|
|
mutability: None,
|
|
|
|
ref elem,
|
|
|
|
..
|
|
|
|
}) => &**elem,
|
2018-08-08 14:42:53 -07:00
|
|
|
_ => bail_span!(
|
|
|
|
class.ty,
|
|
|
|
"first argument of method must be a shared reference"
|
|
|
|
),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
let class_name = match *class {
|
|
|
|
syn::Type::Path(syn::TypePath {
|
|
|
|
qself: None,
|
|
|
|
ref path,
|
|
|
|
}) => path,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(class, "first argument of method must be a path"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-08-01 18:59:59 -05:00
|
|
|
let class_name = extract_path_ident(class_name)?;
|
2018-07-07 10:20:31 -07:00
|
|
|
let class_name = opts
|
|
|
|
.js_class()
|
2018-11-27 15:14:59 -08:00
|
|
|
.map(|p| p.0.into())
|
2018-07-07 10:20:31 -07:00
|
|
|
.unwrap_or_else(|| class_name.to_string());
|
|
|
|
|
|
|
|
let kind = ast::MethodKind::Operation(ast::Operation {
|
|
|
|
is_static: false,
|
|
|
|
kind: operation_kind,
|
|
|
|
});
|
|
|
|
|
|
|
|
ast::ImportFunctionKind::Method {
|
|
|
|
class: class_name,
|
|
|
|
ty: class.clone(),
|
|
|
|
kind,
|
|
|
|
}
|
|
|
|
} else if let Some(cls) = opts.static_method_of() {
|
2018-08-12 23:16:18 -04:00
|
|
|
let class = opts
|
|
|
|
.js_class()
|
2018-11-27 15:14:59 -08:00
|
|
|
.map(|p| p.0.into())
|
2018-08-12 23:16:18 -04:00
|
|
|
.unwrap_or_else(|| cls.to_string());
|
2018-07-07 10:20:31 -07:00
|
|
|
let ty = ident_ty(cls.clone());
|
|
|
|
|
|
|
|
let kind = ast::MethodKind::Operation(ast::Operation {
|
|
|
|
is_static: true,
|
|
|
|
kind: operation_kind,
|
|
|
|
});
|
|
|
|
|
|
|
|
ast::ImportFunctionKind::Method { class, ty, kind }
|
2018-11-27 15:14:59 -08:00
|
|
|
} else if opts.constructor().is_some() {
|
2018-08-20 10:40:54 -07:00
|
|
|
let class = match js_ret {
|
2018-07-07 10:20:31 -07:00
|
|
|
Some(ref ty) => ty,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(self, "constructor returns must be bare types"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
let class_name = match *class {
|
|
|
|
syn::Type::Path(syn::TypePath {
|
|
|
|
qself: None,
|
|
|
|
ref path,
|
|
|
|
}) => path,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(self, "return value of constructor must be a bare path"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-08-01 18:59:59 -05:00
|
|
|
let class_name = extract_path_ident(class_name)?;
|
2018-08-10 13:36:47 -07:00
|
|
|
let class_name = opts
|
|
|
|
.js_class()
|
2018-11-27 15:14:59 -08:00
|
|
|
.map(|p| p.0.into())
|
2018-08-10 13:36:47 -07:00
|
|
|
.unwrap_or_else(|| class_name.to_string());
|
2018-07-07 10:20:31 -07:00
|
|
|
|
|
|
|
ast::ImportFunctionKind::Method {
|
|
|
|
class: class_name.to_string(),
|
|
|
|
ty: class.clone(),
|
|
|
|
kind: ast::MethodKind::Constructor,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ast::ImportFunctionKind::Normal
|
|
|
|
};
|
|
|
|
|
|
|
|
let shim = {
|
|
|
|
let ns = match kind {
|
2018-07-17 18:24:48 -05:00
|
|
|
ast::ImportFunctionKind::Normal => (0, "n"),
|
|
|
|
ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-07-30 10:50:43 -07:00
|
|
|
let data = (ns, &self.ident, module);
|
2018-08-08 14:42:53 -07:00
|
|
|
format!(
|
|
|
|
"__wbg_{}_{}",
|
2018-09-21 17:29:50 -07:00
|
|
|
wasm.name
|
2018-08-08 14:42:53 -07:00
|
|
|
.chars()
|
|
|
|
.filter(|c| c.is_ascii_alphanumeric())
|
|
|
|
.collect::<String>(),
|
|
|
|
ShortHash(data)
|
|
|
|
)
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-11-27 15:14:59 -08:00
|
|
|
if let Some(span) = opts.final_() {
|
|
|
|
if opts.structural().is_some() {
|
|
|
|
let msg = "cannot specify both `structural` and `final`";
|
2018-11-30 13:04:05 -08:00
|
|
|
return Err(Diagnostic::span_error(*span, msg));
|
2018-11-08 16:47:46 -08:00
|
|
|
}
|
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
let ret = ast::ImportKind::Function(ast::ImportFunction {
|
2018-07-07 10:20:31 -07:00
|
|
|
function: wasm,
|
|
|
|
kind,
|
|
|
|
js_ret,
|
|
|
|
catch,
|
2018-08-18 22:15:29 +01:00
|
|
|
variadic,
|
2018-11-27 15:14:59 -08:00
|
|
|
structural: opts.structural().is_some() || opts.final_().is_none(),
|
2018-07-07 10:20:31 -07:00
|
|
|
rust_name: self.ident.clone(),
|
|
|
|
shim: Ident::new(&shim, Span::call_site()),
|
2018-07-29 17:12:36 +01:00
|
|
|
doc_comment: None,
|
2018-11-27 15:14:59 -08:00
|
|
|
});
|
|
|
|
opts.check_used()?;
|
|
|
|
|
|
|
|
Ok(ret)
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-04 10:00:30 -07:00
|
|
|
impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target = ast::ImportKind;
|
|
|
|
|
2018-08-04 10:00:30 -07:00
|
|
|
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
2018-11-27 15:14:59 -08:00
|
|
|
assert_not_variadic(&attrs)?;
|
2018-08-07 16:18:13 -07:00
|
|
|
let js_name = attrs
|
|
|
|
.js_name()
|
2018-09-21 17:29:50 -07:00
|
|
|
.map(|s| s.0)
|
2018-08-07 16:18:13 -07:00
|
|
|
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
2019-03-26 19:29:46 +00:00
|
|
|
let is_type_of = attrs.is_type_of().cloned();
|
2018-08-04 09:41:59 -07:00
|
|
|
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
|
2018-11-27 15:14:59 -08:00
|
|
|
let mut extends = Vec::new();
|
|
|
|
let mut vendor_prefixes = Vec::new();
|
|
|
|
for (used, attr) in attrs.attrs.iter() {
|
|
|
|
match attr {
|
|
|
|
BindgenAttr::Extends(_, e) => {
|
|
|
|
extends.push(e.clone());
|
|
|
|
used.set(true);
|
|
|
|
}
|
|
|
|
BindgenAttr::VendorPrefix(_, e) => {
|
|
|
|
vendor_prefixes.push(e.clone());
|
|
|
|
used.set(true);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attrs.check_used()?;
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok(ast::ImportKind::Type(ast::ImportType {
|
2018-07-07 10:20:31 -07:00
|
|
|
vis: self.vis,
|
|
|
|
attrs: self.attrs,
|
2018-07-29 17:12:36 +01:00
|
|
|
doc_comment: None,
|
2018-08-04 09:41:59 -07:00
|
|
|
instanceof_shim: shim,
|
2019-03-26 19:29:46 +00:00
|
|
|
is_type_of,
|
2018-08-07 16:09:38 -07:00
|
|
|
rust_name: self.ident,
|
2018-08-07 16:18:13 -07:00
|
|
|
js_name,
|
2018-11-27 15:14:59 -08:00
|
|
|
extends,
|
|
|
|
vendor_prefixes,
|
2018-08-01 18:59:59 -05:00
|
|
|
}))
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 11:11:30 -08:00
|
|
|
impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignItemStatic {
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target = ast::ImportKind;
|
|
|
|
|
2018-09-26 08:26:00 -07:00
|
|
|
fn convert(
|
|
|
|
self,
|
2019-02-25 11:11:30 -08:00
|
|
|
(opts, module): (BindgenAttrs, &'a ast::ImportModule),
|
2018-09-26 08:26:00 -07:00
|
|
|
) -> Result<Self::Target, Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
if self.mutability.is_some() {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(self.mutability, "cannot import mutable globals yet")
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
assert_not_variadic(&opts)?;
|
2018-08-05 00:17:30 +02:00
|
|
|
let default_name = self.ident.to_string();
|
2018-11-30 13:04:05 -08:00
|
|
|
let js_name = opts
|
|
|
|
.js_name()
|
|
|
|
.map(|p| p.0)
|
|
|
|
.unwrap_or(&default_name)
|
|
|
|
.to_string();
|
2018-08-08 14:42:53 -07:00
|
|
|
let shim = format!(
|
|
|
|
"__wbg_static_accessor_{}_{}",
|
2018-08-20 10:52:54 -07:00
|
|
|
self.ident,
|
|
|
|
ShortHash((&js_name, module, &self.ident)),
|
2018-08-08 14:42:53 -07:00
|
|
|
);
|
2018-11-27 15:14:59 -08:00
|
|
|
opts.check_used()?;
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok(ast::ImportKind::Static(ast::ImportStatic {
|
2018-07-07 10:20:31 -07:00
|
|
|
ty: *self.ty,
|
|
|
|
vis: self.vis,
|
|
|
|
rust_name: self.ident.clone(),
|
2018-11-27 15:14:59 -08:00
|
|
|
js_name,
|
2018-07-07 10:20:31 -07:00
|
|
|
shim: Ident::new(&shim, Span::call_site()),
|
2018-08-01 18:59:59 -05:00
|
|
|
}))
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-20 12:01:28 -05:00
|
|
|
impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
|
2018-07-07 10:20:31 -07:00
|
|
|
type Target = ast::Function;
|
|
|
|
|
2018-08-01 18:59:59 -05:00
|
|
|
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
match self.vis {
|
|
|
|
syn::Visibility::Public(_) => {}
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(self, "can only #[wasm_bindgen] public functions"),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if self.constness.is_some() {
|
2018-08-08 14:42:53 -07:00
|
|
|
bail_span!(
|
|
|
|
self.constness,
|
|
|
|
"can only #[wasm_bindgen] non-const functions"
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if self.unsafety.is_some() {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions");
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
assert_not_variadic(&attrs)?;
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
let ret = function_from_decl(
|
2018-09-26 08:26:00 -07:00
|
|
|
&self.ident,
|
|
|
|
&attrs,
|
|
|
|
self.decl,
|
|
|
|
self.attrs,
|
|
|
|
self.vis,
|
|
|
|
false,
|
|
|
|
None,
|
2018-11-27 15:14:59 -08:00
|
|
|
)?;
|
|
|
|
attrs.check_used()?;
|
|
|
|
Ok(ret.0)
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Construct a function (and gets the self type if appropriate) for our AST from a syn function.
|
2018-07-07 10:20:31 -07:00
|
|
|
fn function_from_decl(
|
2018-09-21 17:29:50 -07:00
|
|
|
decl_name: &syn::Ident,
|
|
|
|
opts: &BindgenAttrs,
|
2018-08-03 14:11:44 -05:00
|
|
|
decl: Box<syn::FnDecl>,
|
2018-07-07 10:20:31 -07:00
|
|
|
attrs: Vec<syn::Attribute>,
|
|
|
|
vis: syn::Visibility,
|
|
|
|
allow_self: bool,
|
2018-08-03 14:11:44 -05:00
|
|
|
self_ty: Option<&Ident>,
|
2018-08-01 18:59:59 -05:00
|
|
|
) -> Result<(ast::Function, Option<ast::MethodSelf>), Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
if decl.variadic.is_some() {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(decl.variadic, "can't #[wasm_bindgen] variadic functions");
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if decl.generics.params.len() > 0 {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(
|
|
|
|
decl.generics,
|
|
|
|
"can't #[wasm_bindgen] functions with lifetime or type parameters",
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-08-03 14:11:44 -05:00
|
|
|
assert_no_lifetimes(&decl)?;
|
2018-07-07 10:20:31 -07:00
|
|
|
|
|
|
|
let syn::FnDecl { inputs, output, .. } = { *decl };
|
|
|
|
|
2018-08-03 14:11:44 -05:00
|
|
|
let replace_self = |t: syn::Type| {
|
|
|
|
let self_ty = match self_ty {
|
|
|
|
Some(i) => i,
|
|
|
|
None => return t,
|
|
|
|
};
|
|
|
|
let path = match t {
|
|
|
|
syn::Type::Path(syn::TypePath { qself: None, path }) => path,
|
|
|
|
other => return other,
|
|
|
|
};
|
2018-08-08 14:42:53 -07:00
|
|
|
let new_path = if path.segments.len() == 1 && path.segments[0].ident == "Self" {
|
2018-08-03 14:11:44 -05:00
|
|
|
self_ty.clone().into()
|
|
|
|
} else {
|
|
|
|
path
|
|
|
|
};
|
|
|
|
syn::Type::Path(syn::TypePath {
|
|
|
|
qself: None,
|
|
|
|
path: new_path,
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
2018-07-07 10:20:31 -07:00
|
|
|
let mut method_self = None;
|
|
|
|
let arguments = inputs
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|arg| match arg {
|
2018-08-03 14:11:44 -05:00
|
|
|
syn::FnArg::Captured(mut c) => {
|
|
|
|
c.ty = replace_self(c.ty);
|
|
|
|
Some(c)
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
syn::FnArg::SelfValue(_) => {
|
|
|
|
assert!(method_self.is_none());
|
|
|
|
method_self = Some(ast::MethodSelf::ByValue);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
syn::FnArg::SelfRef(ref a) if allow_self => {
|
|
|
|
assert!(method_self.is_none());
|
|
|
|
if a.mutability.is_some() {
|
|
|
|
method_self = Some(ast::MethodSelf::RefMutable);
|
|
|
|
} else {
|
|
|
|
method_self = Some(ast::MethodSelf::RefShared);
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
_ => panic!("arguments cannot be `self` or ignored"),
|
2018-11-27 12:07:59 -08:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2018-07-07 10:20:31 -07:00
|
|
|
|
|
|
|
let ret = match output {
|
|
|
|
syn::ReturnType::Default => None,
|
2018-08-03 14:11:44 -05:00
|
|
|
syn::ReturnType::Type(_, ty) => Some(replace_self(*ty)),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
|
2019-04-30 10:26:03 -03:00
|
|
|
let (name, name_span, renamed_via_js_name) =
|
|
|
|
if let Some((js_name, js_name_span)) = opts.js_name() {
|
|
|
|
(js_name.to_string(), js_name_span, true)
|
|
|
|
} else {
|
|
|
|
(decl_name.to_string(), decl_name.span(), false)
|
|
|
|
};
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok((
|
2018-07-07 10:20:31 -07:00
|
|
|
ast::Function {
|
|
|
|
arguments,
|
2019-04-30 10:26:03 -03:00
|
|
|
name_span,
|
|
|
|
name,
|
|
|
|
renamed_via_js_name,
|
2018-07-07 10:20:31 -07:00
|
|
|
ret,
|
|
|
|
rust_attrs: attrs,
|
2019-04-30 10:26:03 -03:00
|
|
|
rust_vis: vis,
|
2018-07-07 10:20:31 -07:00
|
|
|
},
|
|
|
|
method_self,
|
2018-08-01 18:59:59 -05:00
|
|
|
))
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) trait MacroParse<Ctx> {
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Parse the contents of an object into our AST, with a context if necessary.
|
|
|
|
///
|
|
|
|
/// The context is used to have access to the attributes on `#[wasm_bindgen]`, and to allow
|
|
|
|
/// writing to the output `TokenStream`.
|
2018-08-08 14:42:53 -07:00
|
|
|
fn macro_parse(self, program: &mut ast::Program, context: Ctx) -> Result<(), Diagnostic>;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
|
|
|
fn macro_parse(
|
|
|
|
self,
|
|
|
|
program: &mut ast::Program,
|
|
|
|
(opts, tokens): (Option<BindgenAttrs>, &'a mut TokenStream),
|
2018-08-01 17:15:27 -05:00
|
|
|
) -> Result<(), Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
match self {
|
|
|
|
syn::Item::Fn(mut f) => {
|
|
|
|
let no_mangle = f
|
|
|
|
.attrs
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m)))
|
|
|
|
.find(|&(_, ref m)| m.name() == "no_mangle");
|
|
|
|
match no_mangle {
|
|
|
|
Some((i, _)) => {
|
|
|
|
f.attrs.remove(i);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
let comments = extract_doc_comments(&f.attrs);
|
|
|
|
f.to_tokens(tokens);
|
2018-09-17 18:26:45 -07:00
|
|
|
let opts = opts.unwrap_or_default();
|
2018-11-28 09:25:51 -08:00
|
|
|
if opts.start().is_some() {
|
|
|
|
if f.decl.generics.params.len() > 0 {
|
2018-11-30 13:04:05 -08:00
|
|
|
bail_span!(&f.decl.generics, "the start function cannot have generics",);
|
2018-11-28 09:25:51 -08:00
|
|
|
}
|
|
|
|
if f.decl.inputs.len() > 0 {
|
2018-11-30 13:04:05 -08:00
|
|
|
bail_span!(&f.decl.inputs, "the start function cannot have arguments",);
|
2018-11-28 09:25:51 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-30 10:26:03 -03:00
|
|
|
let method_kind = ast::MethodKind::Operation(ast::Operation {
|
|
|
|
is_static: true,
|
2019-06-16 22:24:27 -03:00
|
|
|
kind: operation_kind(&opts),
|
2019-04-30 10:26:03 -03:00
|
|
|
});
|
|
|
|
let rust_name = f.ident.clone();
|
|
|
|
let start = opts.start().is_some();
|
2018-07-07 10:20:31 -07:00
|
|
|
program.exports.push(ast::Export {
|
|
|
|
comments,
|
2018-09-17 18:26:45 -07:00
|
|
|
function: f.convert(opts)?,
|
2019-04-30 10:26:03 -03:00
|
|
|
js_class: None,
|
|
|
|
method_kind,
|
|
|
|
method_self: None,
|
|
|
|
rust_class: None,
|
|
|
|
rust_name,
|
|
|
|
start,
|
2018-07-07 10:20:31 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
syn::Item::Struct(mut s) => {
|
2018-11-05 12:29:14 -08:00
|
|
|
let opts = opts.unwrap_or_default();
|
|
|
|
program.structs.push((&mut s).convert(opts)?);
|
2018-07-07 10:20:31 -07:00
|
|
|
s.to_tokens(tokens);
|
|
|
|
}
|
|
|
|
syn::Item::Impl(mut i) => {
|
2018-11-05 12:29:14 -08:00
|
|
|
let opts = opts.unwrap_or_default();
|
|
|
|
(&mut i).macro_parse(program, opts)?;
|
2018-07-07 10:20:31 -07:00
|
|
|
i.to_tokens(tokens);
|
|
|
|
}
|
|
|
|
syn::Item::ForeignMod(mut f) => {
|
2018-08-01 18:59:59 -05:00
|
|
|
let opts = match opts {
|
|
|
|
Some(opts) => opts,
|
|
|
|
None => BindgenAttrs::find(&mut f.attrs)?,
|
|
|
|
};
|
2018-08-01 17:15:27 -05:00
|
|
|
f.macro_parse(program, opts)?;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
syn::Item::Enum(e) => {
|
2018-11-27 15:14:59 -08:00
|
|
|
if let Some(opts) = opts {
|
|
|
|
opts.check_used()?;
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
e.to_tokens(tokens);
|
2018-08-01 17:15:27 -05:00
|
|
|
e.macro_parse(program, ())?;
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-17 23:04:19 -05:00
|
|
|
syn::Item::Const(mut c) => {
|
|
|
|
let opts = match opts {
|
|
|
|
Some(opts) => opts,
|
|
|
|
None => BindgenAttrs::find(&mut c.attrs)?,
|
|
|
|
};
|
|
|
|
c.macro_parse(program, opts)?;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
bail_span!(
|
|
|
|
self,
|
|
|
|
"#[wasm_bindgen] can only be applied to a function, \
|
2018-11-27 12:07:59 -08:00
|
|
|
struct, enum, impl, or extern block",
|
2018-11-17 23:04:19 -05:00
|
|
|
);
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-08-01 17:15:27 -05:00
|
|
|
|
|
|
|
Ok(())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-05 12:29:14 -08:00
|
|
|
impl<'a> MacroParse<BindgenAttrs> for &'a mut syn::ItemImpl {
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
fn macro_parse(
|
|
|
|
self,
|
|
|
|
_program: &mut ast::Program,
|
|
|
|
opts: BindgenAttrs,
|
|
|
|
) -> Result<(), Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
if self.defaultness.is_some() {
|
2018-08-08 14:42:53 -07:00
|
|
|
bail_span!(
|
|
|
|
self.defaultness,
|
|
|
|
"#[wasm_bindgen] default impls are not supported"
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if self.unsafety.is_some() {
|
2018-08-08 14:42:53 -07:00
|
|
|
bail_span!(
|
|
|
|
self.unsafety,
|
|
|
|
"#[wasm_bindgen] unsafe impls are not supported"
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-08-01 18:59:59 -05:00
|
|
|
if let Some((_, path, _)) = &self.trait_ {
|
|
|
|
bail_span!(path, "#[wasm_bindgen] trait impls are not supported");
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if self.generics.params.len() > 0 {
|
2018-08-08 14:42:53 -07:00
|
|
|
bail_span!(
|
|
|
|
self.generics,
|
|
|
|
"#[wasm_bindgen] generic impls aren't supported"
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
let name = match *self.self_ty {
|
|
|
|
syn::Type::Path(syn::TypePath {
|
|
|
|
qself: None,
|
|
|
|
ref path,
|
2018-08-01 18:59:59 -05:00
|
|
|
}) => extract_path_ident(path)?,
|
2018-08-08 14:42:53 -07:00
|
|
|
_ => bail_span!(
|
|
|
|
self.self_ty,
|
|
|
|
"unsupported self type in #[wasm_bindgen] impl"
|
|
|
|
),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-08-01 17:15:27 -05:00
|
|
|
let mut errors = Vec::new();
|
2018-07-07 10:20:31 -07:00
|
|
|
for item in self.items.iter_mut() {
|
2019-01-25 14:31:50 -08:00
|
|
|
if let Err(e) = prepare_for_impl_recursion(item, &name, &opts) {
|
2018-08-01 17:15:27 -05:00
|
|
|
errors.push(e);
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
Diagnostic::from_vec(errors)?;
|
|
|
|
opts.check_used()?;
|
|
|
|
Ok(())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:31:50 -08:00
|
|
|
// Prepare for recursion into an `impl` block. Here we want to attach an
|
|
|
|
// internal attribute, `__wasm_bindgen_class_marker`, with any metadata we need
|
|
|
|
// to pass from the impl to the impl item. Recursive macro expansion will then
|
|
|
|
// expand the `__wasm_bindgen_class_marker` attribute.
|
|
|
|
//
|
|
|
|
// Note that we currently do this because inner items may have things like cfgs
|
|
|
|
// on them, so we want to expand the impl first, let the insides get cfg'd, and
|
|
|
|
// then go for the rest.
|
|
|
|
fn prepare_for_impl_recursion(
|
|
|
|
item: &mut syn::ImplItem,
|
|
|
|
class: &Ident,
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
impl_opts: &BindgenAttrs,
|
2019-01-25 14:31:50 -08:00
|
|
|
) -> Result<(), Diagnostic> {
|
|
|
|
let method = match item {
|
|
|
|
syn::ImplItem::Method(m) => m,
|
|
|
|
syn::ImplItem::Const(_) => {
|
|
|
|
bail_span!(
|
|
|
|
&*item,
|
|
|
|
"const definitions aren't supported with #[wasm_bindgen]"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
syn::ImplItem::Type(_) => bail_span!(
|
|
|
|
&*item,
|
|
|
|
"type definitions in impls aren't supported with #[wasm_bindgen]"
|
|
|
|
),
|
|
|
|
syn::ImplItem::Existential(_) => bail_span!(
|
|
|
|
&*item,
|
|
|
|
"existentials in impls aren't supported with #[wasm_bindgen]"
|
|
|
|
),
|
|
|
|
syn::ImplItem::Macro(_) => {
|
|
|
|
// In theory we want to allow this, but we have no way of expanding
|
|
|
|
// the macro and then placing our magical attributes on the expanded
|
|
|
|
// functions. As a result, just disallow it for now to hopefully
|
|
|
|
// ward off buggy results from this macro.
|
|
|
|
bail_span!(&*item, "macros in impls aren't supported");
|
|
|
|
}
|
|
|
|
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let js_class = impl_opts
|
|
|
|
.js_class()
|
|
|
|
.map(|s| s.0.to_string())
|
|
|
|
.unwrap_or(class.to_string());
|
|
|
|
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
method.attrs.insert(
|
|
|
|
0,
|
|
|
|
syn::Attribute {
|
|
|
|
pound_token: Default::default(),
|
|
|
|
style: syn::AttrStyle::Outer,
|
|
|
|
bracket_token: Default::default(),
|
2019-04-04 09:56:16 -07:00
|
|
|
path: syn::parse_quote! { wasm_bindgen::prelude::__wasm_bindgen_class_marker },
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
tts: quote::quote! { (#class = #js_class) }.into(),
|
|
|
|
},
|
|
|
|
);
|
2019-01-25 14:31:50 -08:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> MacroParse<(&'a Ident, &'a str)> for &'b mut syn::ImplItemMethod {
|
2018-11-27 12:07:59 -08:00
|
|
|
fn macro_parse(
|
|
|
|
self,
|
|
|
|
program: &mut ast::Program,
|
2019-01-25 14:31:50 -08:00
|
|
|
(class, js_class): (&'a Ident, &'a str),
|
2018-11-27 12:07:59 -08:00
|
|
|
) -> Result<(), Diagnostic> {
|
2019-01-25 14:31:50 -08:00
|
|
|
match self.vis {
|
2018-07-07 10:20:31 -07:00
|
|
|
syn::Visibility::Public(_) => {}
|
2018-08-01 17:15:27 -05:00
|
|
|
_ => return Ok(()),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2019-01-25 14:31:50 -08:00
|
|
|
if self.defaultness.is_some() {
|
2018-07-07 10:20:31 -07:00
|
|
|
panic!("default methods are not supported");
|
|
|
|
}
|
2019-01-25 14:31:50 -08:00
|
|
|
if self.sig.constness.is_some() {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(
|
2019-01-25 14:31:50 -08:00
|
|
|
self.sig.constness,
|
2018-08-01 18:59:59 -05:00
|
|
|
"can only #[wasm_bindgen] non-const functions",
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2019-01-25 14:31:50 -08:00
|
|
|
if self.sig.unsafety.is_some() {
|
|
|
|
bail_span!(self.sig.unsafety, "can only bindgen safe functions",);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2019-01-25 14:31:50 -08:00
|
|
|
let opts = BindgenAttrs::find(&mut self.attrs)?;
|
|
|
|
let comments = extract_doc_comments(&self.attrs);
|
2018-07-07 10:20:31 -07:00
|
|
|
let (function, method_self) = function_from_decl(
|
2019-01-25 14:31:50 -08:00
|
|
|
&self.sig.ident,
|
2018-09-21 17:29:50 -07:00
|
|
|
&opts,
|
2019-01-25 14:31:50 -08:00
|
|
|
Box::new(self.sig.decl.clone()),
|
|
|
|
self.attrs.clone(),
|
|
|
|
self.vis.clone(),
|
2018-07-07 10:20:31 -07:00
|
|
|
true,
|
2018-08-03 14:11:44 -05:00
|
|
|
Some(class),
|
2018-08-01 18:59:59 -05:00
|
|
|
)?;
|
2019-04-30 10:26:03 -03:00
|
|
|
let method_kind = if opts.constructor().is_some() {
|
|
|
|
ast::MethodKind::Constructor
|
|
|
|
} else {
|
|
|
|
let is_static = method_self.is_none();
|
2019-06-16 22:24:27 -03:00
|
|
|
let kind = operation_kind(&opts);
|
2019-04-30 10:26:03 -03:00
|
|
|
ast::MethodKind::Operation(ast::Operation { is_static, kind })
|
|
|
|
};
|
2018-07-07 10:20:31 -07:00
|
|
|
program.exports.push(ast::Export {
|
2019-04-30 10:26:03 -03:00
|
|
|
comments,
|
|
|
|
function,
|
2019-01-25 14:31:50 -08:00
|
|
|
js_class: Some(js_class.to_string()),
|
2019-04-30 10:26:03 -03:00
|
|
|
method_kind,
|
2018-07-07 10:20:31 -07:00
|
|
|
method_self,
|
2019-04-30 10:26:03 -03:00
|
|
|
rust_class: Some(class.clone()),
|
2019-01-25 14:31:50 -08:00
|
|
|
rust_name: self.sig.ident.clone(),
|
2019-04-30 10:26:03 -03:00
|
|
|
start: false,
|
2018-07-07 10:20:31 -07:00
|
|
|
});
|
2018-11-27 15:14:59 -08:00
|
|
|
opts.check_used()?;
|
2018-08-01 17:15:27 -05:00
|
|
|
Ok(())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MacroParse<()> for syn::ItemEnum {
|
2018-08-08 14:42:53 -07:00
|
|
|
fn macro_parse(self, program: &mut ast::Program, (): ()) -> Result<(), Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
match self.vis {
|
|
|
|
syn::Visibility::Public(_) => {}
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(self, "only public enums are allowed with #[wasm_bindgen]"),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2019-01-28 14:12:02 -08:00
|
|
|
if self.variants.len() == 0 {
|
|
|
|
bail_span!(self, "cannot export empty enums to JS");
|
|
|
|
}
|
|
|
|
|
|
|
|
let has_discriminant = self.variants[0].discriminant.is_some();
|
|
|
|
|
2018-07-07 10:20:31 -07:00
|
|
|
let variants = self
|
|
|
|
.variants
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, v)| {
|
|
|
|
match v.fields {
|
|
|
|
syn::Fields::Unit => (),
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(v.fields, "only C-Style enums allowed with #[wasm_bindgen]"),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2019-01-28 14:12:02 -08:00
|
|
|
|
|
|
|
// Require that everything either has a discriminant or doesn't.
|
|
|
|
// We don't really want to get in the business of emulating how
|
|
|
|
// rustc assigns values to enums.
|
|
|
|
if v.discriminant.is_some() != has_discriminant {
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
bail_span!(
|
|
|
|
v,
|
|
|
|
"must either annotate discriminant of all variants or none"
|
|
|
|
);
|
2019-01-28 14:12:02 -08:00
|
|
|
}
|
|
|
|
|
2018-07-07 10:20:31 -07:00
|
|
|
let value = match v.discriminant {
|
|
|
|
Some((
|
|
|
|
_,
|
|
|
|
syn::Expr::Lit(syn::ExprLit {
|
|
|
|
attrs: _,
|
|
|
|
lit: syn::Lit::Int(ref int_lit),
|
|
|
|
}),
|
|
|
|
)) => {
|
|
|
|
if int_lit.value() > <u32>::max_value() as u64 {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(
|
|
|
|
int_lit,
|
|
|
|
"enums with #[wasm_bindgen] can only support \
|
|
|
|
numbers that can be represented as u32"
|
|
|
|
);
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
int_lit.value() as u32
|
|
|
|
}
|
|
|
|
None => i as u32,
|
2018-08-08 14:42:53 -07:00
|
|
|
Some((_, ref expr)) => bail_span!(
|
|
|
|
expr,
|
|
|
|
"enums with #[wasm_bidngen] may only have \
|
|
|
|
number literal values",
|
|
|
|
),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok(ast::Variant {
|
2018-07-07 10:20:31 -07:00
|
|
|
name: v.ident.clone(),
|
|
|
|
value,
|
2018-08-01 18:59:59 -05:00
|
|
|
})
|
2018-11-27 12:07:59 -08:00
|
|
|
})
|
2019-01-28 14:12:02 -08:00
|
|
|
.collect::<Result<Vec<_>, Diagnostic>>()?;
|
|
|
|
|
|
|
|
let mut values = variants.iter().map(|v| v.value).collect::<Vec<_>>();
|
|
|
|
values.sort();
|
Migrate `wasm-bindgen` to using `walrus`
This commit moves `wasm-bindgen` the CLI tool from internally using
`parity-wasm` for wasm parsing/serialization to instead use `walrus`.
The `walrus` crate is something we've been working on recently with an
aim to replace the usage of `parity-wasm` in `wasm-bindgen` to make the
current CLI tool more maintainable as well as more future-proof.
The `walrus` crate provides a much nicer AST to work with as well as a
structured `Module`, whereas `parity-wasm` provides a very raw interface
to the wasm module which isn't really appropriate for our use case. The
many transformations and tweaks that wasm-bindgen does have a huge
amount of ad-hoc index management to carefully craft a final wasm
binary, but this is all entirely taken care for us with the `walrus`
crate.
Additionally, `wasm-bindgen` will ingest and rewrite the wasm file,
often changing the binary offsets of functions. Eventually with DWARF
debug information we'll need to be sure to preserve the debug
information throughout the transformations that `wasm-bindgen` does
today. This is practically impossible to do with the `parity-wasm`
architecture, but `walrus` was designed from the get-go to solve this
problem transparently in the `walrus` crate itself. (it doesn't today,
but this is planned work)
It is the intention that this does not end up regressing any
`wasm-bindgen` use cases, neither in functionality or in speed. As a
large change and refactoring, however, it's likely that at least
something will arise! We'll want to continue to remain vigilant to any
issues that come up with this commit.
Note that the `gc` crate has been deleted as part of this change, as the
`gc` crate is no longer necessary since `walrus` does it automatically.
Additionally the `gc` crate was one of the main problems with preserving
debug information as it often deletes wasm items!
Finally, this also starts moving crates to the 2018 edition where
necessary since `walrus` requires the 2018 edition, and in general it's
more pleasant to work within the 2018 edition!
2019-01-31 09:54:23 -08:00
|
|
|
let hole = values
|
|
|
|
.windows(2)
|
2019-01-28 14:12:02 -08:00
|
|
|
.filter_map(|window| {
|
|
|
|
if window[0] + 1 != window[1] {
|
|
|
|
Some(window[0] + 1)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.next()
|
|
|
|
.unwrap_or(*values.last().unwrap() + 1);
|
|
|
|
for value in values {
|
|
|
|
assert!(hole != value);
|
|
|
|
}
|
|
|
|
|
2018-07-07 10:20:31 -07:00
|
|
|
let comments = extract_doc_comments(&self.attrs);
|
|
|
|
program.enums.push(ast::Enum {
|
|
|
|
name: self.ident,
|
|
|
|
variants,
|
|
|
|
comments,
|
2019-01-28 14:12:02 -08:00
|
|
|
hole,
|
2018-07-07 10:20:31 -07:00
|
|
|
});
|
2018-08-01 17:15:27 -05:00
|
|
|
Ok(())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-17 23:04:19 -05:00
|
|
|
impl MacroParse<BindgenAttrs> for syn::ItemConst {
|
|
|
|
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
|
|
|
|
// Shortcut
|
2018-11-27 15:14:59 -08:00
|
|
|
if opts.typescript_custom_section().is_none() {
|
2018-11-17 23:04:19 -05:00
|
|
|
bail_span!(self, "#[wasm_bindgen] will not work on constants unless you are defining a #[wasm_bindgen(typescript_custom_section)].");
|
|
|
|
}
|
|
|
|
|
|
|
|
match *self.expr {
|
|
|
|
syn::Expr::Lit(syn::ExprLit {
|
|
|
|
lit: syn::Lit::Str(litstr),
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
program.typescript_custom_sections.push(litstr.value());
|
2018-11-27 12:07:59 -08:00
|
|
|
}
|
2018-11-17 23:04:19 -05:00
|
|
|
_ => {
|
|
|
|
bail_span!(self, "Expected a string literal to be used with #[wasm_bindgen(typescript_custom_section)].");
|
2018-11-27 12:07:59 -08:00
|
|
|
}
|
2018-11-17 23:04:19 -05:00
|
|
|
}
|
|
|
|
|
2018-11-27 15:14:59 -08:00
|
|
|
opts.check_used()?;
|
|
|
|
|
2018-11-17 23:04:19 -05:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-07 10:20:31 -07:00
|
|
|
impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
|
2018-08-08 14:42:53 -07:00
|
|
|
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) -> Result<(), Diagnostic> {
|
2018-08-01 18:59:59 -05:00
|
|
|
let mut errors = Vec::new();
|
2018-07-07 10:20:31 -07:00
|
|
|
match self.abi.name {
|
|
|
|
Some(ref l) if l.value() == "C" => {}
|
|
|
|
None => {}
|
2018-08-01 18:59:59 -05:00
|
|
|
Some(ref other) => {
|
2018-08-08 14:42:53 -07:00
|
|
|
errors.push(err_span!(
|
|
|
|
other,
|
|
|
|
"only foreign mods with the `C` ABI are allowed"
|
|
|
|
));
|
2018-08-01 18:59:59 -05:00
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2019-03-15 08:04:25 -07:00
|
|
|
let module = if let Some((name, span)) = opts.module() {
|
|
|
|
if opts.inline_js().is_some() {
|
|
|
|
let msg = "cannot specify both `module` and `inline_js`";
|
|
|
|
errors.push(Diagnostic::span_error(span, msg));
|
2019-02-25 11:11:30 -08:00
|
|
|
}
|
2019-03-15 08:04:25 -07:00
|
|
|
if opts.raw_module().is_some() {
|
|
|
|
let msg = "cannot specify both `module` and `raw_module`";
|
|
|
|
errors.push(Diagnostic::span_error(span, msg));
|
2019-02-25 11:11:30 -08:00
|
|
|
}
|
2019-03-15 08:04:25 -07:00
|
|
|
ast::ImportModule::Named(name.to_string(), span)
|
|
|
|
} else if let Some((name, span)) = opts.raw_module() {
|
|
|
|
if opts.inline_js().is_some() {
|
|
|
|
let msg = "cannot specify both `raw_module` and `inline_js`";
|
|
|
|
errors.push(Diagnostic::span_error(span, msg));
|
|
|
|
}
|
|
|
|
ast::ImportModule::RawNamed(name.to_string(), span)
|
|
|
|
} else if let Some((js, span)) = opts.inline_js() {
|
|
|
|
let i = program.inline_js.len();
|
|
|
|
program.inline_js.push(js.to_string());
|
|
|
|
ast::ImportModule::Inline(i, span)
|
|
|
|
} else {
|
|
|
|
ast::ImportModule::None
|
2019-02-25 11:11:30 -08:00
|
|
|
};
|
|
|
|
for item in self.items.into_iter() {
|
|
|
|
if let Err(e) = item.macro_parse(program, module.clone()) {
|
2018-08-01 18:59:59 -05:00
|
|
|
errors.push(e);
|
|
|
|
}
|
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
Diagnostic::from_vec(errors)?;
|
|
|
|
opts.check_used()?;
|
|
|
|
Ok(())
|
2018-08-01 18:59:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 11:11:30 -08:00
|
|
|
impl MacroParse<ast::ImportModule> for syn::ForeignItem {
|
2018-08-08 14:42:53 -07:00
|
|
|
fn macro_parse(
|
|
|
|
mut self,
|
|
|
|
program: &mut ast::Program,
|
2019-02-25 11:11:30 -08:00
|
|
|
module: ast::ImportModule,
|
2018-08-08 14:42:53 -07:00
|
|
|
) -> Result<(), Diagnostic> {
|
2018-08-01 18:59:59 -05:00
|
|
|
let item_opts = {
|
|
|
|
let attrs = match self {
|
|
|
|
syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
|
|
|
|
syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
|
|
|
|
syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
|
2018-07-07 10:20:31 -07:00
|
|
|
_ => panic!("only foreign functions/types allowed for now"),
|
|
|
|
};
|
2018-08-01 18:59:59 -05:00
|
|
|
BindgenAttrs::find(attrs)?
|
|
|
|
};
|
2019-02-25 11:11:30 -08:00
|
|
|
let js_namespace = item_opts.js_namespace().cloned();
|
2018-08-01 18:59:59 -05:00
|
|
|
let kind = match self {
|
|
|
|
syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?,
|
2018-08-04 10:00:30 -07:00
|
|
|
syn::ForeignItem::Type(t) => t.convert(item_opts)?,
|
2018-08-20 10:52:54 -07:00
|
|
|
syn::ForeignItem::Static(s) => s.convert((item_opts, &module))?,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => panic!("only foreign functions/types allowed for now"),
|
|
|
|
};
|
|
|
|
|
|
|
|
program.imports.push(ast::Import {
|
|
|
|
module,
|
|
|
|
js_namespace,
|
|
|
|
kind,
|
|
|
|
});
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-08-01 17:15:27 -05:00
|
|
|
Ok(())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Get the first type parameter of a generic type, errors on incorrect input.
|
2018-08-01 18:59:59 -05:00
|
|
|
fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
let t = match ty {
|
|
|
|
Some(t) => t,
|
2018-07-25 11:42:01 +01:00
|
|
|
None => return Ok(None),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
let path = match *t {
|
|
|
|
syn::Type::Path(syn::TypePath {
|
|
|
|
qself: None,
|
|
|
|
ref path,
|
|
|
|
}) => path,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(t, "must be Result<...>"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-08-08 14:42:53 -07:00
|
|
|
let seg = path
|
|
|
|
.segments
|
|
|
|
.last()
|
2018-08-01 18:59:59 -05:00
|
|
|
.ok_or_else(|| err_span!(t, "must have at least one segment"))?
|
|
|
|
.into_value();
|
2018-07-07 10:20:31 -07:00
|
|
|
let generics = match seg.arguments {
|
|
|
|
syn::PathArguments::AngleBracketed(ref t) => t,
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(t, "must be Result<...>"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
2018-08-08 14:42:53 -07:00
|
|
|
let generic = generics
|
|
|
|
.args
|
|
|
|
.first()
|
2018-08-01 18:59:59 -05:00
|
|
|
.ok_or_else(|| err_span!(t, "must have at least one generic parameter"))?
|
|
|
|
.into_value();
|
|
|
|
let ty = match generic {
|
|
|
|
syn::GenericArgument::Type(t) => t,
|
|
|
|
other => bail_span!(other, "must be a type parameter"),
|
2018-07-07 10:20:31 -07:00
|
|
|
};
|
|
|
|
match *ty {
|
2018-07-25 11:42:01 +01:00
|
|
|
syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Ok(None),
|
2018-07-07 10:20:31 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
2018-07-25 11:42:01 +01:00
|
|
|
Ok(Some(ty.clone()))
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Extract the documentation comments from a Vec of attributes
|
|
|
|
fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
|
|
|
|
attrs
|
2018-09-26 08:26:00 -07:00
|
|
|
.iter()
|
|
|
|
.filter_map(|a| {
|
|
|
|
// if the path segments include an ident of "doc" we know this
|
|
|
|
// this is a doc comment
|
|
|
|
if a.path.segments.iter().any(|s| s.ident.to_string() == "doc") {
|
|
|
|
Some(
|
|
|
|
// We want to filter out any Puncts so just grab the Literals
|
|
|
|
a.tts.clone().into_iter().filter_map(|t| match t {
|
|
|
|
TokenTree::Literal(lit) => {
|
|
|
|
// this will always return the quoted string, we deal with
|
|
|
|
// that in the cli when we read in the comments
|
|
|
|
Some(lit.to_string())
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
//Fold up the [[String]] iter we created into Vec<String>
|
|
|
|
.fold(vec![], |mut acc, a| {
|
|
|
|
acc.extend(a);
|
|
|
|
acc
|
|
|
|
})
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// Check there are no lifetimes on the function.
|
2018-08-03 14:11:44 -05:00
|
|
|
fn assert_no_lifetimes(decl: &syn::FnDecl) -> Result<(), Diagnostic> {
|
2018-08-01 18:59:59 -05:00
|
|
|
struct Walk {
|
|
|
|
diagnostics: Vec<Diagnostic>,
|
|
|
|
}
|
2018-07-07 10:20:31 -07:00
|
|
|
|
2018-08-03 14:11:44 -05:00
|
|
|
impl<'ast> syn::visit::Visit<'ast> for Walk {
|
|
|
|
fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) {
|
2018-08-01 18:59:59 -05:00
|
|
|
self.diagnostics.push(err_span!(
|
|
|
|
&*i,
|
2018-07-07 10:20:31 -07:00
|
|
|
"it is currently not sound to use lifetimes in function \
|
|
|
|
signatures"
|
2018-08-01 18:59:59 -05:00
|
|
|
));
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
}
|
2018-08-08 14:42:53 -07:00
|
|
|
let mut walk = Walk {
|
|
|
|
diagnostics: Vec::new(),
|
|
|
|
};
|
2018-08-03 14:11:44 -05:00
|
|
|
syn::visit::Visit::visit_fn_decl(&mut walk, decl);
|
2018-08-01 18:59:59 -05:00
|
|
|
Diagnostic::from_vec(walk.diagnostics)
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
|
2018-08-18 22:15:29 +01:00
|
|
|
/// This method always fails if the BindgenAttrs contain variadic
|
2018-11-27 15:14:59 -08:00
|
|
|
fn assert_not_variadic(attrs: &BindgenAttrs) -> Result<(), Diagnostic> {
|
|
|
|
if let Some(span) = attrs.variadic() {
|
|
|
|
let msg = "the `variadic` attribute can only be applied to imported \
|
2018-11-30 13:04:05 -08:00
|
|
|
(`extern`) functions";
|
|
|
|
return Err(Diagnostic::span_error(*span, msg));
|
2018-08-18 22:15:29 +01:00
|
|
|
}
|
2018-08-21 12:55:09 +01:00
|
|
|
Ok(())
|
2018-08-18 22:15:29 +01:00
|
|
|
}
|
|
|
|
|
2018-07-25 11:42:01 +01:00
|
|
|
/// If the path is a single ident, return it.
|
2018-08-01 18:59:59 -05:00
|
|
|
fn extract_path_ident(path: &syn::Path) -> Result<Ident, Diagnostic> {
|
2018-07-07 10:20:31 -07:00
|
|
|
if path.leading_colon.is_some() {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(path, "global paths are not supported yet");
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
|
|
|
if path.segments.len() != 1 {
|
2018-08-01 18:59:59 -05:00
|
|
|
bail_span!(path, "multi-segment paths are not supported yet");
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-08-01 18:59:59 -05:00
|
|
|
let value = &path.segments[0];
|
|
|
|
match value.arguments {
|
2018-07-07 10:20:31 -07:00
|
|
|
syn::PathArguments::None => {}
|
2018-08-01 18:59:59 -05:00
|
|
|
_ => bail_span!(path, "paths with type parameters are not supported yet"),
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-08-01 18:59:59 -05:00
|
|
|
Ok(value.ident.clone())
|
2018-07-07 10:20:31 -07:00
|
|
|
}
|
2018-11-27 15:14:59 -08:00
|
|
|
|
|
|
|
pub fn reset_attrs_used() {
|
|
|
|
ATTRS.with(|state| {
|
|
|
|
state.parsed.set(0);
|
|
|
|
state.checks.set(0);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn assert_all_attrs_checked() {
|
|
|
|
ATTRS.with(|state| {
|
|
|
|
assert_eq!(state.parsed.get(), state.checks.get());
|
|
|
|
})
|
|
|
|
}
|
2019-04-30 10:26:03 -03:00
|
|
|
|
2019-06-16 22:24:27 -03:00
|
|
|
fn operation_kind(opts: &BindgenAttrs) -> ast::OperationKind {
|
2019-04-30 10:26:03 -03:00
|
|
|
let mut operation_kind = ast::OperationKind::Regular;
|
|
|
|
if let Some(g) = opts.getter() {
|
|
|
|
operation_kind = ast::OperationKind::Getter(g.clone());
|
|
|
|
}
|
|
|
|
if let Some(s) = opts.setter() {
|
|
|
|
operation_kind = ast::OperationKind::Setter(s.clone());
|
|
|
|
}
|
|
|
|
if opts.indexing_getter().is_some() {
|
|
|
|
operation_kind = ast::OperationKind::IndexingGetter;
|
|
|
|
}
|
|
|
|
if opts.indexing_setter().is_some() {
|
|
|
|
operation_kind = ast::OperationKind::IndexingSetter;
|
|
|
|
}
|
|
|
|
if opts.indexing_deleter().is_some() {
|
|
|
|
operation_kind = ast::OperationKind::IndexingDeleter;
|
|
|
|
}
|
2019-06-16 22:24:27 -03:00
|
|
|
operation_kind
|
2019-04-30 10:26:03 -03:00
|
|
|
}
|