mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-22 09:11:35 +00:00
Migrate from a macro to an attribute
This commit migrates from `wasm_bindgen!`-the-macro to `#[wasm_bindgen]`-the-attribute. The actual mechanics of the macro are relatively simple in just generating some shims here and there, but wrapping everything in one huge macro invocation can often seem intimidating as it gives off this feeling of "oh dear anything can happen here!" Using an attribute should curb expectations much more greatly of "oh there's just some extra stuff happening behind the scenes". The usage is otherwise relatively straightforward and close to what it was before, but check out the DESIGN.md/README.md changes for more info!
This commit is contained in:
@ -1,50 +1,50 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::{Tokens, ToTokens};
|
||||
use shared;
|
||||
use syn;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Program {
|
||||
pub structs: Vec<Struct>,
|
||||
pub free_functions: Vec<Function>,
|
||||
pub exports: Vec<Export>,
|
||||
pub imports: Vec<Import>,
|
||||
pub imported_structs: Vec<ImportStruct>,
|
||||
pub imported_types: Vec<(syn::Visibility, syn::Ident)>,
|
||||
pub structs: Vec<Struct>,
|
||||
}
|
||||
|
||||
pub struct Export {
|
||||
pub class: Option<syn::Ident>,
|
||||
pub method: bool,
|
||||
pub mutable: bool,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
pub struct Import {
|
||||
pub module: Option<String>,
|
||||
pub kind: ImportKind,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
pub enum ImportKind {
|
||||
Method { class: String, ty: syn::Type },
|
||||
Static { class: String, ty: syn::Type },
|
||||
JsConstructor { class: String, ty: syn::Type },
|
||||
Normal,
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: syn::Ident,
|
||||
pub arguments: Vec<Type>,
|
||||
pub ret: Option<Type>,
|
||||
}
|
||||
|
||||
pub struct Import {
|
||||
pub module: String,
|
||||
pub function: ImportFunction,
|
||||
}
|
||||
|
||||
pub struct ImportFunction {
|
||||
pub catch: bool,
|
||||
pub ident: syn::Ident,
|
||||
pub wasm_function: Function,
|
||||
pub opts: BindgenAttrs,
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
pub rust_decl: Box<syn::FnDecl>,
|
||||
pub rust_vis: syn::Visibility,
|
||||
pub rust_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
pub struct ImportStruct {
|
||||
pub module: Option<String>,
|
||||
pub struct Struct {
|
||||
pub name: syn::Ident,
|
||||
pub functions: Vec<ImportStructFunction>,
|
||||
}
|
||||
|
||||
pub struct ImportStructFunction {
|
||||
pub kind: ImportFunctionKind,
|
||||
pub function: ImportFunction,
|
||||
}
|
||||
|
||||
pub enum ImportFunctionKind {
|
||||
Method,
|
||||
Static,
|
||||
JsConstructor,
|
||||
}
|
||||
|
||||
pub enum Type {
|
||||
@ -57,19 +57,54 @@ pub enum Type {
|
||||
ByValue(syn::Type),
|
||||
}
|
||||
|
||||
pub struct Struct {
|
||||
pub name: syn::Ident,
|
||||
pub methods: Vec<Method>,
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
pub struct Method {
|
||||
pub mutable: bool,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn push_impl(&mut self, item: &syn::ItemImpl) {
|
||||
pub fn push_item(&mut self,
|
||||
item: syn::Item,
|
||||
opts: Option<BindgenAttrs>,
|
||||
tokens: &mut Tokens) {
|
||||
match item {
|
||||
syn::Item::Fn(mut f) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
|
||||
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); }
|
||||
None => {
|
||||
panic!("#[wasm_bindgen] can only be applied to #[no_mangle] \
|
||||
functions, or those that would otherwise be exported")
|
||||
}
|
||||
}
|
||||
f.to_tokens(tokens);
|
||||
self.exports.push(Export {
|
||||
class: None,
|
||||
method: false,
|
||||
mutable: false,
|
||||
function: Function::from(f, opts),
|
||||
});
|
||||
}
|
||||
syn::Item::Struct(mut s) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut s.attrs));
|
||||
s.to_tokens(tokens);
|
||||
self.structs.push(Struct::from(s, opts));
|
||||
}
|
||||
syn::Item::Impl(mut i) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut i.attrs));
|
||||
i.to_tokens(tokens);
|
||||
self.push_impl(i, opts);
|
||||
}
|
||||
syn::Item::ForeignMod(mut f) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
self.push_foreign_mod(f, opts);
|
||||
}
|
||||
_ => panic!("#[wasm_bindgen] can only be applied to a function, \
|
||||
struct, impl, or extern block"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_impl(&mut self, item: syn::ItemImpl, _opts: BindgenAttrs) {
|
||||
if item.defaultness.is_some() {
|
||||
panic!("default impls are not supported");
|
||||
}
|
||||
@ -91,70 +126,76 @@ impl Program {
|
||||
}
|
||||
_ => panic!("unsupported self type in impl"),
|
||||
};
|
||||
let dst = self.structs
|
||||
.iter_mut()
|
||||
.find(|s| s.name == name)
|
||||
.expect(&format!("failed to definition of struct for impl of `{}`", name));
|
||||
for item in item.items.iter() {
|
||||
dst.push_item(item);
|
||||
for item in item.items.into_iter() {
|
||||
self.push_impl_item(name, item);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_foreign_mod(&mut self, f: &syn::ItemForeignMod) {
|
||||
fn push_impl_item(&mut self, class: syn::Ident, item: syn::ImplItem) {
|
||||
let mut method = match item {
|
||||
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
|
||||
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
|
||||
syn::ImplItem::Method(m) => m,
|
||||
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||
};
|
||||
match method.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => return,
|
||||
}
|
||||
if method.defaultness.is_some() {
|
||||
panic!("default methods are not supported");
|
||||
}
|
||||
if method.sig.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if method.sig.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
let opts = BindgenAttrs::find(&mut method.attrs);
|
||||
|
||||
let (function, mutable) = Function::from_decl(method.sig.ident,
|
||||
Box::new(method.sig.decl),
|
||||
method.attrs,
|
||||
opts,
|
||||
method.vis,
|
||||
true);
|
||||
self.exports.push(Export {
|
||||
class: Some(class),
|
||||
method: mutable.is_some(),
|
||||
mutable: mutable.unwrap_or(false),
|
||||
function,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_foreign_mod(&mut self, f: syn::ItemForeignMod, opts: BindgenAttrs) {
|
||||
match f.abi.name {
|
||||
Some(ref l) if l.value() == "JS" => {}
|
||||
_ => panic!("only foreign mods with the `JS` ABI are allowed"),
|
||||
Some(ref l) if l.value() == "C" => {}
|
||||
None => {}
|
||||
_ => panic!("only foreign mods with the `C` ABI are allowed"),
|
||||
}
|
||||
let module = f.attrs.iter()
|
||||
.filter_map(|f| f.interpret_meta())
|
||||
.filter_map(|i| {
|
||||
match i {
|
||||
syn::Meta::NameValue(i) => {
|
||||
if i.ident == "wasm_module" {
|
||||
Some(i.lit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.and_then(|lit| {
|
||||
match lit {
|
||||
syn::Lit::Str(v) => Some(v.value()),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.expect("must specify `#[wasm_module = ...]` for module to import from");
|
||||
for item in f.items.iter() {
|
||||
let import = self.gen_foreign_item(item, false).0;
|
||||
self.imports.push(Import {
|
||||
module: module.clone(),
|
||||
function: import,
|
||||
});
|
||||
for item in f.items.into_iter() {
|
||||
match item {
|
||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &opts),
|
||||
syn::ForeignItem::Type(t) => self.push_foreign_ty(t, &opts),
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_foreign_item(&mut self,
|
||||
f: &syn::ForeignItem,
|
||||
allow_self: bool) -> (ImportFunction, bool) {
|
||||
let f = match *f {
|
||||
syn::ForeignItem::Fn(ref f) => f,
|
||||
_ => panic!("only foreign functions allowed for now, not statics"),
|
||||
};
|
||||
pub fn push_foreign_fn(&mut self,
|
||||
mut f: syn::ForeignItemFn,
|
||||
module_opts: &BindgenAttrs) {
|
||||
let opts = BindgenAttrs::find(&mut f.attrs);
|
||||
|
||||
let (mut wasm, mutable) = Function::from_decl(f.ident, &f.decl, allow_self);
|
||||
let is_method = match mutable {
|
||||
Some(false) => true,
|
||||
None => false,
|
||||
Some(true) => {
|
||||
panic!("mutable self methods not allowed in extern structs");
|
||||
}
|
||||
};
|
||||
let opts = BindgenOpts::from(&f.attrs);
|
||||
|
||||
if opts.catch {
|
||||
let mut wasm = Function::from_decl(f.ident,
|
||||
f.decl,
|
||||
f.attrs,
|
||||
opts,
|
||||
f.vis,
|
||||
false).0;
|
||||
if wasm.opts.catch() {
|
||||
// TODO: this assumes a whole bunch:
|
||||
//
|
||||
// * The outer type is actually a `Result`
|
||||
@ -166,38 +207,73 @@ impl Program {
|
||||
.expect("can't `catch` without returning a Result");
|
||||
}
|
||||
|
||||
(ImportFunction {
|
||||
rust_attrs: f.attrs.clone(),
|
||||
rust_vis: f.vis.clone(),
|
||||
rust_decl: f.decl.clone(),
|
||||
ident: f.ident.clone(),
|
||||
wasm_function: wasm,
|
||||
catch: opts.catch,
|
||||
}, is_method)
|
||||
let kind = if wasm.opts.method() {
|
||||
let class = wasm.arguments.get(0)
|
||||
.expect("methods must have at least one argument");
|
||||
let class = match *class {
|
||||
Type::ByRef(ref t) |
|
||||
Type::ByValue(ref t) => t,
|
||||
Type::ByMutRef(_) => {
|
||||
panic!("first method argument cannot be mutable ref")
|
||||
}
|
||||
Type::String | Type::BorrowedStr => {
|
||||
panic!("method receivers cannot be strings")
|
||||
}
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
|
||||
ImportKind::Method {
|
||||
class: class_name.as_ref().to_string(),
|
||||
ty: class.clone(),
|
||||
}
|
||||
} else if wasm.opts.constructor() {
|
||||
let class = match wasm.ret {
|
||||
Some(Type::ByValue(ref t)) => t,
|
||||
_ => panic!("constructor returns must be bare types"),
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
|
||||
ImportKind::JsConstructor {
|
||||
class: class_name.as_ref().to_string(),
|
||||
ty: class.clone(),
|
||||
}
|
||||
|
||||
} else if let Some(class) = wasm.opts.static_receiver() {
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
ImportKind::Static {
|
||||
class: class_name.to_string(),
|
||||
ty: class.clone(),
|
||||
}
|
||||
} else {
|
||||
ImportKind::Normal
|
||||
};
|
||||
|
||||
self.imports.push(Import {
|
||||
module: module_opts.module().map(|s| s.to_string()),
|
||||
kind,
|
||||
function: wasm,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_extern_class(&mut self, class: &ExternClass) {
|
||||
let functions = class.functions.iter()
|
||||
.map(|f| {
|
||||
let (f, method) = self.gen_foreign_item(f, true);
|
||||
let kind = if method {
|
||||
ImportFunctionKind::Method
|
||||
} else {
|
||||
let opts = BindgenOpts::from(&f.rust_attrs);
|
||||
if opts.constructor {
|
||||
ImportFunctionKind::JsConstructor
|
||||
} else {
|
||||
ImportFunctionKind::Static
|
||||
}
|
||||
};
|
||||
ImportStructFunction { kind, function: f }
|
||||
})
|
||||
.collect();
|
||||
self.imported_structs.push(ImportStruct {
|
||||
module: class.module.as_ref().map(|s| s.value()),
|
||||
name: class.name,
|
||||
functions,
|
||||
});
|
||||
pub fn push_foreign_ty(&mut self,
|
||||
f: syn::ForeignItemType,
|
||||
_module_opts: &BindgenAttrs) {
|
||||
self.imported_types.push((f.vis, f.ident));
|
||||
}
|
||||
|
||||
pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
|
||||
@ -207,16 +283,17 @@ impl Program {
|
||||
};
|
||||
a.append("wbg:");
|
||||
a.fields(&[
|
||||
("structs", &|a| a.list(&self.structs, Struct::wbg_literal)),
|
||||
("free_functions", &|a| a.list(&self.free_functions, Function::wbg_literal)),
|
||||
("exports", &|a| a.list(&self.exports, Export::wbg_literal)),
|
||||
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
|
||||
("imported_structs", &|a| a.list(&self.imported_structs, ImportStruct::wbg_literal)),
|
||||
("custom_type_names", &|a| {
|
||||
a.list(&self.structs, |s, a| {
|
||||
let val = shared::name_to_descriptor(s.name.as_ref());
|
||||
let names = self.exports.iter()
|
||||
.filter_map(|e| e.class)
|
||||
.collect::<BTreeSet<_>>();
|
||||
a.list(&names, |s, a| {
|
||||
let val = shared::name_to_descriptor(s.as_ref());
|
||||
a.fields(&[
|
||||
("descriptor", &|a| a.char(val)),
|
||||
("name", &|a| a.str(s.name.as_ref()))
|
||||
("name", &|a| a.str(s.as_ref()))
|
||||
]);
|
||||
})
|
||||
}),
|
||||
@ -226,7 +303,7 @@ impl Program {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn from(input: &syn::ItemFn) -> Function {
|
||||
pub fn from(input: syn::ItemFn, opts: BindgenAttrs) -> Function {
|
||||
match input.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("can only bindgen public functions"),
|
||||
@ -237,18 +314,24 @@ impl Function {
|
||||
if input.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
if !input.abi.is_none() {
|
||||
panic!("can only bindgen Rust ABI functions")
|
||||
}
|
||||
if !input.abi.is_none() {
|
||||
panic!("can only bindgen Rust ABI functions")
|
||||
if input.abi.is_none() {
|
||||
panic!("can only bindgen `extern` ABI functions, or those that \
|
||||
would otherwise be exported")
|
||||
}
|
||||
|
||||
Function::from_decl(input.ident, &input.decl, false).0
|
||||
Function::from_decl(input.ident,
|
||||
input.decl,
|
||||
input.attrs,
|
||||
opts,
|
||||
input.vis,
|
||||
false).0
|
||||
}
|
||||
|
||||
pub fn from_decl(name: syn::Ident,
|
||||
decl: &syn::FnDecl,
|
||||
decl: Box<syn::FnDecl>,
|
||||
attrs: Vec<syn::Attribute>,
|
||||
opts: BindgenAttrs,
|
||||
vis: syn::Visibility,
|
||||
allow_self: bool) -> (Function, Option<bool>) {
|
||||
if decl.variadic.is_some() {
|
||||
panic!("can't bindgen variadic functions")
|
||||
@ -281,31 +364,15 @@ impl Function {
|
||||
syn::ReturnType::Type(_, ref t) => Some(Type::from(t)),
|
||||
};
|
||||
|
||||
(Function { name, arguments, ret }, mutable)
|
||||
}
|
||||
|
||||
pub fn free_function_export_name(&self) -> syn::LitStr {
|
||||
let name = shared::free_function_export_name(self.name.as_ref());
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
|
||||
pub fn struct_function_export_name(&self, s: syn::Ident) -> syn::LitStr {
|
||||
let name = shared::struct_function_export_name(
|
||||
s.as_ref(),
|
||||
self.name.as_ref(),
|
||||
);
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
|
||||
pub fn rust_symbol(&self, namespace: Option<syn::Ident>) -> syn::Ident {
|
||||
let mut generated_name = format!("__wasm_bindgen_generated");
|
||||
if let Some(ns) = namespace {
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(ns.as_ref());
|
||||
}
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(self.name.as_ref());
|
||||
syn::Ident::from(generated_name)
|
||||
(Function {
|
||||
name,
|
||||
arguments,
|
||||
ret,
|
||||
opts,
|
||||
rust_vis: vis,
|
||||
rust_decl: decl,
|
||||
rust_attrs: attrs,
|
||||
}, mutable)
|
||||
}
|
||||
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
@ -389,74 +456,68 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
pub fn from(s: &syn::ItemStruct) -> Struct {
|
||||
Struct {
|
||||
name: s.ident,
|
||||
methods: Vec::new(),
|
||||
functions: Vec::new(),
|
||||
impl Export {
|
||||
pub fn rust_symbol(&self) -> syn::Ident {
|
||||
let mut generated_name = format!("__wasm_bindgen_generated");
|
||||
if let Some(class) = self.class {
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(class.as_ref());
|
||||
}
|
||||
generated_name.push_str("_");
|
||||
generated_name.push_str(self.function.name.as_ref());
|
||||
syn::Ident::from(generated_name)
|
||||
}
|
||||
|
||||
pub fn free_function(&self) -> syn::Ident {
|
||||
syn::Ident::from(shared::free_function(self.name.as_ref()))
|
||||
}
|
||||
|
||||
pub fn push_item(&mut self, item: &syn::ImplItem) {
|
||||
let method = match *item {
|
||||
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
|
||||
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
|
||||
syn::ImplItem::Method(ref m) => m,
|
||||
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||
};
|
||||
match method.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => return,
|
||||
}
|
||||
if method.defaultness.is_some() {
|
||||
panic!("default methods are not supported");
|
||||
}
|
||||
if method.sig.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if method.sig.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
let (function, mutable) = Function::from_decl(method.sig.ident,
|
||||
&method.sig.decl,
|
||||
true);
|
||||
match mutable {
|
||||
Some(mutable) => {
|
||||
self.methods.push(Method { mutable, function });
|
||||
pub fn export_name(&self) -> syn::LitStr {
|
||||
let name = match self.class {
|
||||
Some(class) => {
|
||||
shared::struct_function_export_name(
|
||||
class.as_ref(),
|
||||
self.function.name.as_ref(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
self.functions.push(function);
|
||||
shared::free_function_export_name(self.function.name.as_ref())
|
||||
}
|
||||
}
|
||||
};
|
||||
syn::LitStr::new(&name, Span::def_site())
|
||||
}
|
||||
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("functions", &|a| a.list(&self.functions, Function::wbg_literal)),
|
||||
("methods", &|a| a.list(&self.methods, Method::wbg_literal)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Method {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("mutable", &|a| a.bool(self.mutable)),
|
||||
("class", &|a| {
|
||||
match self.class {
|
||||
Some(ref s) => a.str(s.as_ref()),
|
||||
None => a.append("null"),
|
||||
}
|
||||
}),
|
||||
("method", &|a| a.bool(self.method)),
|
||||
("function", &|a| self.function.wbg_literal(a)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportStruct {
|
||||
impl Import {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
let mut method = false;
|
||||
let mut js_new = false;
|
||||
let mut statik = false;
|
||||
let mut class_name = None;
|
||||
match self.kind {
|
||||
ImportKind::Method { ref class, .. } => {
|
||||
method = true;
|
||||
class_name = Some(class);
|
||||
}
|
||||
ImportKind::JsConstructor { ref class, .. } => {
|
||||
js_new = true;
|
||||
class_name = Some(class);
|
||||
}
|
||||
ImportKind::Static { ref class, .. } => {
|
||||
statik = true;
|
||||
class_name = Some(class);
|
||||
}
|
||||
ImportKind::Normal => {}
|
||||
}
|
||||
a.fields(&[
|
||||
("module", &|a| {
|
||||
match self.module {
|
||||
@ -464,99 +525,27 @@ impl ImportStruct {
|
||||
None => a.append("null"),
|
||||
}
|
||||
}),
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("functions", &|a| {
|
||||
a.list(&self.functions,
|
||||
|f, a| {
|
||||
let (method, new) = match f.kind {
|
||||
ImportFunctionKind::Method => (true, false),
|
||||
ImportFunctionKind::JsConstructor => (false, true),
|
||||
ImportFunctionKind::Static => (false, false),
|
||||
};
|
||||
a.fields(&[
|
||||
("method", &|a| a.bool(method)),
|
||||
("js_new", &|a| a.bool(new)),
|
||||
("catch", &|a| a.bool(f.function.catch)),
|
||||
("function", &|a| f.function.wasm_function.wbg_literal(a)),
|
||||
]);
|
||||
})
|
||||
("catch", &|a| a.bool(self.function.opts.catch())),
|
||||
("method", &|a| a.bool(method)),
|
||||
("js_new", &|a| a.bool(js_new)),
|
||||
("statik", &|a| a.bool(statik)),
|
||||
("function", &|a| self.function.wbg_literal(a)),
|
||||
("class", &|a| {
|
||||
match class_name {
|
||||
Some(s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
}
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
impl Import {
|
||||
fn wbg_literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("module", &|a| a.str(&self.module)),
|
||||
("catch", &|a| a.bool(self.function.catch)),
|
||||
("function", &|a| self.function.wasm_function.wbg_literal(a)),
|
||||
]);
|
||||
impl Struct {
|
||||
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
|
||||
Struct { name: s.ident }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
pub items: Vec<MyItem>,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for File {
|
||||
named!(parse -> Self, map!(many0!(syn!(MyItem)), |items| File { items }));
|
||||
}
|
||||
|
||||
pub enum MyItem {
|
||||
Normal(syn::Item),
|
||||
ExternClass(ExternClass),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for MyItem {
|
||||
named!(parse -> Self, alt!(
|
||||
syn!(syn::Item) => { MyItem::Normal }
|
||||
|
|
||||
syn!(ExternClass) => { MyItem::ExternClass }
|
||||
));
|
||||
}
|
||||
|
||||
pub struct ExternClass {
|
||||
name: syn::Ident,
|
||||
module: Option<syn::LitStr>,
|
||||
functions: Vec<syn::ForeignItem>,
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for ExternClass {
|
||||
named!(parse -> Self, do_parse!(
|
||||
module: option!(do_parse!(
|
||||
punct!(#) >>
|
||||
name: brackets!(do_parse!(
|
||||
call!(term, "wasm_module") >>
|
||||
punct!(=) >>
|
||||
val: syn!(syn::LitStr) >>
|
||||
(val)
|
||||
)) >>
|
||||
(name.1)
|
||||
)) >>
|
||||
keyword!(extern) >>
|
||||
keyword!(struct) >>
|
||||
name: syn!(syn::Ident) >>
|
||||
items: braces!(many0!(syn!(syn::ForeignItem))) >>
|
||||
(ExternClass {
|
||||
name,
|
||||
module,
|
||||
functions: items.1,
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
||||
-> syn::synom::PResult<'a, ()>
|
||||
{
|
||||
if let Some((_span, term, next)) = cursor.term() {
|
||||
if term.as_str() == name {
|
||||
return Ok(((), next))
|
||||
}
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
||||
struct LiteralBuilder<'a> {
|
||||
dst: &'a mut Tokens,
|
||||
cnt: usize,
|
||||
@ -634,51 +623,124 @@ impl<'a> LiteralBuilder<'a> {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct BindgenOpts {
|
||||
catch: bool,
|
||||
constructor: bool,
|
||||
pub struct BindgenAttrs {
|
||||
attrs: Vec<BindgenAttr>,
|
||||
}
|
||||
|
||||
impl BindgenOpts {
|
||||
fn from(attrs: &[syn::Attribute]) -> BindgenOpts {
|
||||
let mut opts = BindgenOpts::default();
|
||||
let attrs = attrs.iter()
|
||||
.filter_map(|a| a.interpret_meta())
|
||||
.filter_map(|m| {
|
||||
match m {
|
||||
syn::Meta::List(i) => {
|
||||
if i.ident == "wasm_bindgen" {
|
||||
Some(i.nested)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl BindgenAttrs {
|
||||
pub fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
|
||||
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 BindgenAttrs::default(),
|
||||
};
|
||||
syn::parse(attrs.remove(pos).tts.into())
|
||||
.expect("malformed #[wasm_bindgen] attribute")
|
||||
}
|
||||
|
||||
fn module(&self) -> Option<&str> {
|
||||
self.attrs.iter()
|
||||
.filter_map(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Module(ref s) => Some(&s[..]),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.flat_map(|a| a)
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn catch(&self) -> bool {
|
||||
self.attrs.iter()
|
||||
.any(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Catch => true,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn constructor(&self) -> bool {
|
||||
self.attrs.iter()
|
||||
.any(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Constructor => true,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn method(&self) -> bool {
|
||||
self.attrs.iter()
|
||||
.any(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Method => true,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn static_receiver(&self) -> Option<&syn::Type> {
|
||||
self.attrs.iter()
|
||||
.filter_map(|a| {
|
||||
match a {
|
||||
syn::NestedMeta::Meta(a) => Some(a),
|
||||
match *a {
|
||||
BindgenAttr::Static(ref s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
for attr in attrs {
|
||||
match attr {
|
||||
syn::Meta::Word(a) => {
|
||||
if a == "constructor" {
|
||||
opts.constructor = true;
|
||||
} else if a == "catch" {
|
||||
opts.catch = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return opts
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
named!(parse -> Self, alt!(
|
||||
do_parse!(
|
||||
opts: parens!(call!(
|
||||
syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated
|
||||
)) >>
|
||||
(BindgenAttrs {
|
||||
attrs: opts.1.into_iter().collect(),
|
||||
})
|
||||
) => { |s| s }
|
||||
|
|
||||
epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } }
|
||||
));
|
||||
}
|
||||
|
||||
enum BindgenAttr {
|
||||
Catch,
|
||||
Constructor,
|
||||
Method,
|
||||
Static(syn::Type),
|
||||
Module(String),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
named!(parse -> Self, alt!(
|
||||
call!(term, "catch") => { |_| BindgenAttr::Catch }
|
||||
|
|
||||
call!(term, "constructor") => { |_| BindgenAttr::Constructor }
|
||||
|
|
||||
call!(term, "method") => { |_| BindgenAttr::Method }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "static") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::Type) >>
|
||||
(s)
|
||||
)=> { BindgenAttr::Static }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "module") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
));
|
||||
}
|
||||
|
||||
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
||||
let ty = match ty {
|
||||
Some(t) => t,
|
||||
@ -707,3 +769,14 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
|
||||
}
|
||||
Some(Some(Type::from(ty)))
|
||||
}
|
||||
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
|
||||
-> syn::synom::PResult<'a, ()>
|
||||
{
|
||||
if let Some((_span, term, next)) = cursor.term() {
|
||||
if term.as_str() == name {
|
||||
return Ok(((), next))
|
||||
}
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ extern crate wasm_bindgen_shared as shared;
|
||||
use std::sync::atomic::*;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenNode, Delimiter, TokenTree};
|
||||
use proc_macro2::Span;
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
macro_rules! my_quote {
|
||||
@ -22,75 +22,41 @@ macro_rules! my_quote {
|
||||
|
||||
mod ast;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
// Parse the input as a list of Rust items, reusing the `syn::File` parser.
|
||||
let file = syn::parse::<ast::File>(input)
|
||||
.expect("expected a set of valid Rust items");
|
||||
#[proc_macro_attribute]
|
||||
pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let item = syn::parse::<syn::Item>(input.clone())
|
||||
.expect("expected a valid Rust item");
|
||||
let opts = syn::parse::<ast::BindgenAttrs>(attr)
|
||||
.expect("invalid arguments to #[wasm_bindgen]");
|
||||
|
||||
let mut ret = Tokens::new();
|
||||
let mut program = ast::Program::default();
|
||||
program.push_item(item, Some(opts), &mut ret);
|
||||
generate_wrappers(program, &mut ret);
|
||||
|
||||
let mut program = ast::Program {
|
||||
structs: Vec::new(),
|
||||
free_functions: Vec::new(),
|
||||
imports: Vec::new(),
|
||||
imported_structs: Vec::new(),
|
||||
};
|
||||
// println!("{}", ret);
|
||||
|
||||
// Translate all input items into our own internal representation (the `ast`
|
||||
// module). We'll be panicking here on anything that we can't process
|
||||
ret.into()
|
||||
}
|
||||
|
||||
for item in file.items.iter() {
|
||||
let item = match *item {
|
||||
ast::MyItem::ExternClass(ref c) => {
|
||||
program.push_extern_class(c);
|
||||
continue
|
||||
}
|
||||
ast::MyItem::Normal(ref item) => item,
|
||||
};
|
||||
|
||||
match *item {
|
||||
syn::Item::Fn(ref f) => {
|
||||
item.to_tokens(&mut ret);
|
||||
program.free_functions.push(ast::Function::from(f));
|
||||
}
|
||||
syn::Item::Struct(ref s) => {
|
||||
item.to_tokens(&mut ret);
|
||||
let s = ast::Struct::from(s);
|
||||
if program.structs.iter().any(|a| a.name == s.name) {
|
||||
panic!("redefinition of struct: {}", s.name);
|
||||
}
|
||||
program.structs.push(s);
|
||||
}
|
||||
syn::Item::Impl(ref i) => {
|
||||
item.to_tokens(&mut ret);
|
||||
program.push_impl(i);
|
||||
}
|
||||
syn::Item::ForeignMod(ref f) => {
|
||||
program.push_foreign_mod(f);
|
||||
}
|
||||
_ => panic!("unexpected item in bindgen macro"),
|
||||
}
|
||||
}
|
||||
|
||||
// Generate wrappers for all the items that we've found
|
||||
|
||||
for function in program.free_functions.iter() {
|
||||
bindgen_fn(function, &mut ret);
|
||||
// Generate wrappers for all the items that we've found
|
||||
fn generate_wrappers(program: ast::Program, tokens: &mut Tokens) {
|
||||
for export in program.exports.iter() {
|
||||
bindgen_export(export, tokens);
|
||||
}
|
||||
for s in program.structs.iter() {
|
||||
bindgen_struct(s, &mut ret);
|
||||
bindgen_struct(s, tokens);
|
||||
}
|
||||
for i in program.imports.iter() {
|
||||
bindgen_import(i, &mut ret);
|
||||
bindgen_import(i, tokens);
|
||||
}
|
||||
for i in program.imported_structs.iter() {
|
||||
bindgen_imported_struct(i, &mut ret);
|
||||
for &(ref vis, ref t) in program.imported_types.iter() {
|
||||
bindgen_imported_type(vis, t, tokens);
|
||||
}
|
||||
|
||||
// Finally generate a static which will eventually be what lives in a custom
|
||||
// section of the wasm executable. For now it's just a plain old static, but
|
||||
// we'll eventually have it actually in its own section.
|
||||
// Generate a static which will eventually be what lives in a custom section
|
||||
// of the wasm executable. For now it's just a plain old static, but we'll
|
||||
// eventually have it actually in its own section.
|
||||
|
||||
static CNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
let generated_static_name = format!("__WASM_BINDGEN_GENERATED{}",
|
||||
@ -104,32 +70,12 @@ pub fn wasm_bindgen(input: TokenStream) -> TokenStream {
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static #generated_static_name: [u32; #generated_static_length] =
|
||||
[#generated_static_value];
|
||||
}).to_tokens(&mut ret);
|
||||
|
||||
// println!("{}", ret);
|
||||
|
||||
ret.into()
|
||||
}
|
||||
|
||||
fn bindgen_fn(function: &ast::Function, into: &mut Tokens) {
|
||||
bindgen(&function.free_function_export_name(),
|
||||
function.rust_symbol(None),
|
||||
Receiver::FreeFunction(function.name),
|
||||
&function.arguments,
|
||||
function.ret.as_ref(),
|
||||
into)
|
||||
}).to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
||||
for f in s.functions.iter() {
|
||||
bindgen_struct_fn(s, f, into);
|
||||
}
|
||||
for f in s.methods.iter() {
|
||||
bindgen_struct_method(s, f, into);
|
||||
}
|
||||
|
||||
let name = &s.name;
|
||||
let free_fn = s.free_function();
|
||||
let free_fn = syn::Ident::from(shared::free_function(s.name.as_ref()));
|
||||
let c = shared::name_to_descriptor(name.as_ref()) as u32;
|
||||
(my_quote! {
|
||||
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
||||
@ -175,43 +121,17 @@ fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) {
|
||||
}).to_tokens(into);
|
||||
}
|
||||
|
||||
fn bindgen_struct_fn(s: &ast::Struct, f: &ast::Function, into: &mut Tokens) {
|
||||
bindgen(&f.struct_function_export_name(s.name),
|
||||
f.rust_symbol(Some(s.name)),
|
||||
Receiver::StructFunction(s.name, f.name),
|
||||
&f.arguments,
|
||||
f.ret.as_ref(),
|
||||
into)
|
||||
}
|
||||
|
||||
fn bindgen_struct_method(s: &ast::Struct, m: &ast::Method, into: &mut Tokens) {
|
||||
bindgen(&m.function.struct_function_export_name(s.name),
|
||||
m.function.rust_symbol(Some(s.name)),
|
||||
Receiver::StructMethod(s.name, m.mutable, m.function.name),
|
||||
&m.function.arguments,
|
||||
m.function.ret.as_ref(),
|
||||
into)
|
||||
}
|
||||
|
||||
enum Receiver {
|
||||
FreeFunction(syn::Ident),
|
||||
StructFunction(syn::Ident, syn::Ident),
|
||||
StructMethod(syn::Ident, bool, syn::Ident),
|
||||
}
|
||||
|
||||
fn bindgen(export_name: &syn::LitStr,
|
||||
generated_name: syn::Ident,
|
||||
receiver: Receiver,
|
||||
arguments: &[ast::Type],
|
||||
ret_type: Option<&ast::Type>,
|
||||
into: &mut Tokens) {
|
||||
fn bindgen_export(export: &ast::Export, into: &mut Tokens) {
|
||||
let generated_name = export.rust_symbol();
|
||||
let export_name = export.export_name();
|
||||
let mut args = vec![];
|
||||
let mut arg_conversions = vec![];
|
||||
let mut converted_arguments = vec![];
|
||||
let ret = syn::Ident::from("_ret");
|
||||
|
||||
let mut offset = 0;
|
||||
if let Receiver::StructMethod(class, _, _) = receiver {
|
||||
if export.method {
|
||||
let class = export.class.unwrap();
|
||||
args.push(my_quote! { me: *mut ::wasm_bindgen::__rt::WasmRefCell<#class> });
|
||||
arg_conversions.push(my_quote! {
|
||||
::wasm_bindgen::__rt::assert_not_null(me);
|
||||
@ -220,7 +140,7 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
for (i, ty) in arguments.iter().enumerate() {
|
||||
for (i, ty) in export.function.arguments.iter().enumerate() {
|
||||
let i = i + offset;
|
||||
let ident = syn::Ident::from(format!("arg{}", i));
|
||||
match *ty {
|
||||
@ -288,12 +208,12 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
}
|
||||
let ret_ty;
|
||||
let convert_ret;
|
||||
match ret_type {
|
||||
Some(&ast::Type::String) => {
|
||||
match export.function.ret {
|
||||
Some(ast::Type::String) => {
|
||||
ret_ty = my_quote! { -> *mut String };
|
||||
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
|
||||
}
|
||||
Some(&ast::Type::ByValue(ref t)) => {
|
||||
Some(ast::Type::ByValue(ref t)) => {
|
||||
ret_ty = my_quote! {
|
||||
-> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
};
|
||||
@ -301,9 +221,9 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
|
||||
};
|
||||
}
|
||||
Some(&ast::Type::BorrowedStr) |
|
||||
Some(&ast::Type::ByMutRef(_)) |
|
||||
Some(&ast::Type::ByRef(_)) => {
|
||||
Some(ast::Type::BorrowedStr) |
|
||||
Some(ast::Type::ByMutRef(_)) |
|
||||
Some(ast::Type::ByRef(_)) => {
|
||||
panic!("can't return a borrowed ref");
|
||||
}
|
||||
None => {
|
||||
@ -312,6 +232,19 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
}
|
||||
}
|
||||
|
||||
let name = export.function.name;
|
||||
let receiver = match export.class {
|
||||
Some(_) if export.method => {
|
||||
if export.mutable {
|
||||
my_quote! { me.borrow_mut().#name }
|
||||
} else {
|
||||
my_quote! { me.borrow().#name }
|
||||
}
|
||||
}
|
||||
Some(class) => my_quote! { #class::#name },
|
||||
None => my_quote!{ #name },
|
||||
};
|
||||
|
||||
let tokens = my_quote! {
|
||||
#[export_name = #export_name]
|
||||
#[allow(non_snake_case)]
|
||||
@ -324,65 +257,14 @@ fn bindgen(export_name: &syn::LitStr,
|
||||
tokens.to_tokens(into);
|
||||
}
|
||||
|
||||
impl ToTokens for Receiver {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
match *self {
|
||||
Receiver::FreeFunction(name) => name.to_tokens(tokens),
|
||||
Receiver::StructFunction(s, name) => {
|
||||
s.to_tokens(tokens);
|
||||
syn::token::Colon2::default().to_tokens(tokens);
|
||||
name.to_tokens(tokens);
|
||||
}
|
||||
Receiver::StructMethod(_, mutable, name) => {
|
||||
(my_quote! { me }).to_tokens(tokens);
|
||||
syn::token::Dot::default().to_tokens(tokens);
|
||||
if mutable {
|
||||
syn::Ident::from("borrow_mut").to_tokens(tokens);
|
||||
} else {
|
||||
syn::Ident::from("borrow").to_tokens(tokens);
|
||||
}
|
||||
tokens.append(TokenTree {
|
||||
span: Span::def_site(),
|
||||
kind: TokenNode::Group(Delimiter::Parenthesis,
|
||||
proc_macro2::TokenStream::empty()),
|
||||
});
|
||||
syn::token::Dot::default().to_tokens(tokens);
|
||||
name.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
let import_name = shared::mangled_import_name(
|
||||
None,
|
||||
import.function.wasm_function.name.as_ref(),
|
||||
);
|
||||
bindgen_import_function(&import.function, &import_name, tokens);
|
||||
}
|
||||
|
||||
fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
||||
let name = import.name;
|
||||
|
||||
let mut methods = Tokens::new();
|
||||
|
||||
for f in import.functions.iter() {
|
||||
let import_name = shared::mangled_import_name(
|
||||
Some(&import.name.to_string()),
|
||||
f.function.wasm_function.name.as_ref(),
|
||||
);
|
||||
bindgen_import_function(&f.function, &import_name, &mut methods);
|
||||
}
|
||||
|
||||
fn bindgen_imported_type(vis: &syn::Visibility,
|
||||
name: &syn::Ident,
|
||||
tokens: &mut Tokens) {
|
||||
(my_quote! {
|
||||
pub struct #name {
|
||||
#vis struct #name {
|
||||
obj: ::wasm_bindgen::JsValue,
|
||||
}
|
||||
|
||||
impl #name {
|
||||
#methods
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::WasmBoundary for #name {
|
||||
type Js = <::wasm_bindgen::JsValue as
|
||||
::wasm_bindgen::convert::WasmBoundary>::Js;
|
||||
@ -397,42 +279,50 @@ fn bindgen_imported_struct(import: &ast::ImportStruct, tokens: &mut Tokens) {
|
||||
#name { obj: ::wasm_bindgen::JsValue::from_js(js) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name {
|
||||
fn to_js_ref(&self) -> u32 {
|
||||
self.obj.to_js_ref()
|
||||
}
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
}
|
||||
|
||||
fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
import_name: &str,
|
||||
tokens: &mut Tokens) {
|
||||
let vis = &import.rust_vis;
|
||||
let ret = &import.rust_decl.output;
|
||||
let fn_token = &import.rust_decl.fn_token;
|
||||
let arguments = &import.rust_decl.inputs;
|
||||
|
||||
fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
|
||||
let mut class_ty = None;
|
||||
let mut is_method = false;
|
||||
let mut class_name = None;
|
||||
match import.kind {
|
||||
ast::ImportKind::Method { ref ty, ref class } => {
|
||||
is_method = true;
|
||||
class_ty = Some(ty);
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportKind::Static { ref ty, ref class } |
|
||||
ast::ImportKind::JsConstructor { ref ty, ref class } => {
|
||||
class_ty = Some(ty);
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportKind::Normal => {}
|
||||
}
|
||||
let import_name = shared::mangled_import_name(
|
||||
class_name.map(|s| &**s),
|
||||
import.function.name.as_ref(),
|
||||
);
|
||||
let vis = &import.function.rust_vis;
|
||||
let ret = &import.function.rust_decl.output;
|
||||
let fn_token = &import.function.rust_decl.fn_token;
|
||||
|
||||
let mut abi_argument_names = Vec::new();
|
||||
let mut abi_arguments = Vec::new();
|
||||
let mut arg_conversions = Vec::new();
|
||||
let ret_ident = syn::Ident::from("_ret");
|
||||
|
||||
let inputs = import.rust_decl.inputs.iter().collect::<Vec<_>>();
|
||||
let (is_method, inputs) = match inputs.get(0) {
|
||||
Some(&&syn::FnArg::Captured(_)) => (false, &inputs[..]),
|
||||
Some(_) => (true, &inputs[1..]),
|
||||
None => (false, &inputs[..]),
|
||||
};
|
||||
|
||||
if is_method {
|
||||
let ptr = syn::Ident::from("ptr");
|
||||
abi_argument_names.push(ptr);
|
||||
abi_arguments.push(my_quote! { #ptr: u32 });
|
||||
arg_conversions.push(my_quote! {
|
||||
let #ptr = ::wasm_bindgen::convert::ToRefWasmBoundary::to_js_ref(&self.obj);
|
||||
});
|
||||
}
|
||||
|
||||
let names = inputs
|
||||
let names = import.function.rust_decl.inputs
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
match **arg {
|
||||
match *arg {
|
||||
syn::FnArg::Captured(ref c) => c,
|
||||
_ => panic!("arguments cannot be `self` or ignored"),
|
||||
}
|
||||
@ -451,7 +341,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
}
|
||||
});
|
||||
|
||||
for (ty, name) in import.wasm_function.arguments.iter().zip(names) {
|
||||
for (i, (ty, name)) in import.function.arguments.iter().zip(names).enumerate() {
|
||||
match *ty {
|
||||
ast::Type::BorrowedStr => {
|
||||
let ptr = syn::Ident::from(format!("{}_ptr", name));
|
||||
@ -470,19 +360,33 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
abi_arguments.push(my_quote! {
|
||||
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
});
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||
::into_js(#name);
|
||||
});
|
||||
if i == 0 && is_method {
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||
::into_js(self);
|
||||
});
|
||||
} else {
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
|
||||
::into_js(#name);
|
||||
});
|
||||
}
|
||||
}
|
||||
ast::Type::ByMutRef(_) => panic!("urgh mut"),
|
||||
ast::Type::ByRef(ref t) => {
|
||||
abi_argument_names.push(name);
|
||||
abi_arguments.push(my_quote! { #name: u32 });
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||
::to_js_ref(#name);
|
||||
});
|
||||
if i == 0 && is_method {
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||
::to_js_ref(self);
|
||||
});
|
||||
} else {
|
||||
arg_conversions.push(my_quote! {
|
||||
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
|
||||
::to_js_ref(#name);
|
||||
});
|
||||
}
|
||||
}
|
||||
// TODO: need to test this
|
||||
ast::Type::String => {
|
||||
@ -502,7 +406,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
}
|
||||
let abi_ret;
|
||||
let mut convert_ret;
|
||||
match import.wasm_function.ret {
|
||||
match import.function.ret {
|
||||
Some(ast::Type::ByValue(ref t)) => {
|
||||
abi_ret = my_quote! {
|
||||
<#t as ::wasm_bindgen::convert::WasmBoundary>::Js
|
||||
@ -539,7 +443,7 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
}
|
||||
|
||||
let mut exceptional_ret = my_quote! {};
|
||||
if import.catch {
|
||||
if import.function.opts.catch() {
|
||||
let exn_data = syn::Ident::from("exn_data");
|
||||
let exn_data_ptr = syn::Ident::from("exn_data_ptr");
|
||||
abi_argument_names.push(exn_data_ptr);
|
||||
@ -557,10 +461,24 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
};
|
||||
}
|
||||
|
||||
let name = import.ident;
|
||||
let name = import.function.name;
|
||||
let import_name = syn::Ident::from(import_name);
|
||||
(quote! {
|
||||
#vis #fn_token #name(#arguments) #ret {
|
||||
let attrs = &import.function.rust_attrs;
|
||||
|
||||
let arguments = import.function.rust_decl.inputs
|
||||
.iter()
|
||||
.skip(if is_method { 1 } else { 0 })
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let me = if is_method {
|
||||
my_quote! { &self, }
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let invocation = quote! {
|
||||
#(#attrs)*
|
||||
#vis extern #fn_token #name(#me #(#arguments),*) #ret {
|
||||
extern {
|
||||
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
|
||||
}
|
||||
@ -571,5 +489,15 @@ fn bindgen_import_function(import: &ast::ImportFunction,
|
||||
#convert_ret
|
||||
}
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
};
|
||||
|
||||
if let Some(class) = class_ty {
|
||||
(quote! {
|
||||
impl #class {
|
||||
#invocation
|
||||
}
|
||||
}).to_tokens(tokens);
|
||||
} else {
|
||||
invocation.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user