From 0bd54480c6bd68126e64ee10ed5f67f16990cca4 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 29 Mar 2018 09:14:32 -0700 Subject: [PATCH] Split out the AST and codegen into the wasm-bindgen-backend crate --- crates/backend/Cargo.toml | 10 + crates/{macro => backend}/src/ast.rs | 396 +++++++------- crates/backend/src/codegen.rs | 621 ++++++++++++++++++++++ crates/backend/src/lib.rs | 25 + crates/{macro => backend}/src/literal.rs | 14 +- crates/macro/Cargo.toml | 1 + crates/macro/src/lib.rs | 639 +---------------------- 7 files changed, 860 insertions(+), 846 deletions(-) create mode 100644 crates/backend/Cargo.toml rename crates/{macro => backend}/src/ast.rs (73%) create mode 100644 crates/backend/src/codegen.rs create mode 100755 crates/backend/src/lib.rs rename crates/{macro => backend}/src/literal.rs (97%) diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml new file mode 100644 index 00000000..a81f7eaf --- /dev/null +++ b/crates/backend/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "wasm-bindgen-backend" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[dependencies] +quote = '0.4' +proc-macro2 = { version = "0.2", features = ["nightly"] } +wasm-bindgen-shared = { path = "../shared", version = "0.1.0" } +syn = { version = '0.12', features = ['full'] } diff --git a/crates/macro/src/ast.rs b/crates/backend/src/ast.rs similarity index 73% rename from crates/macro/src/ast.rs rename to crates/backend/src/ast.rs index a38094de..008884a4 100644 --- a/crates/macro/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -1,6 +1,6 @@ use literal::{self, Literal}; use proc_macro2::Span; -use quote::{Tokens, ToTokens}; +use quote::{ToTokens, Tokens}; use shared; use syn; @@ -39,14 +39,8 @@ pub struct ImportFunction { } pub enum ImportFunctionKind { - Method { - class: String, - ty: syn::Type, - }, - JsConstructor { - class: String, - ty: syn::Type, - }, + Method { class: String, ty: syn::Type }, + JsConstructor { class: String, ty: syn::Type }, Normal, } @@ -79,7 +73,7 @@ pub struct Struct { pub struct Enum { pub name: syn::Ident, - pub variants: Vec + pub variants: Vec, } pub struct Variant { @@ -110,20 +104,20 @@ pub enum VectorType { } impl Program { - pub fn push_item(&mut self, - item: syn::Item, - opts: Option, - tokens: &mut Tokens) { + pub fn push_item(&mut self, item: syn::Item, opts: Option, 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() + 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); } + Some((i, _)) => { + f.attrs.remove(i); + } _ => {} } f.to_tokens(tokens); @@ -153,8 +147,10 @@ impl Program { e.to_tokens(tokens); self.push_enum(e, opts); } - _ => panic!("#[wasm_bindgen] can only be applied to a function, \ - struct, enum, impl, or extern block"), + _ => panic!( + "#[wasm_bindgen] can only be applied to a function, \ + struct, enum, impl, or extern block" + ), } } @@ -172,12 +168,13 @@ impl Program { panic!("generic impls aren't supported"); } let name = match *item.self_ty { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => { - match extract_path_ident(path) { - Some(ident) => ident, - None => panic!("unsupported self type in impl"), - } - } + syn::Type::Path(syn::TypePath { + qself: None, + ref path, + }) => match extract_path_ident(path) { + Some(ident) => ident, + None => panic!("unsupported self type in impl"), + }, _ => panic!("unsupported self type in impl"), }; for item in item.items.into_iter() { @@ -209,12 +206,14 @@ impl Program { 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); + 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(), @@ -229,30 +228,40 @@ impl Program { _ => panic!("only public enums are allowed"), } - let variants = item.variants.iter().enumerate().map(|(i, v)| { - match v.fields { - syn::Fields::Unit => (), - _ => panic!("Only C-Style enums allowed") - } - let value = match v.discriminant { - Some((_, syn::Expr::Lit(syn::ExprLit {attrs: _, lit: syn::Lit::Int(ref int_lit)}))) => { - if int_lit.value() > ::max_value() as u64 { - panic!("Enums can only support numbers that can be represented as u32"); + let variants = item.variants + .iter() + .enumerate() + .map(|(i, v)| { + match v.fields { + syn::Fields::Unit => (), + _ => panic!("Only C-Style enums allowed"), + } + let value = match v.discriminant { + Some(( + _, + syn::Expr::Lit(syn::ExprLit { + attrs: _, + lit: syn::Lit::Int(ref int_lit), + }), + )) => { + if int_lit.value() > ::max_value() as u64 { + panic!("Enums can only support numbers that can be represented as u32"); + } + int_lit.value() as u32 } - int_lit.value() as u32 - }, - None => i as u32, - _ => panic!("Enums may only have number literal values") - }; + None => i as u32, + _ => panic!("Enums may only have number literal values"), + }; - Variant { - name: v.ident, - value, - } - }).collect(); + Variant { + name: v.ident, + value, + } + }) + .collect(); self.enums.push(Enum { name: item.ident, - variants + variants, }); } @@ -281,20 +290,17 @@ impl Program { _ => panic!("only foreign functions/types allowed for now"), }; - self.imports.push(Import { module, js_namespace, kind }); + self.imports.push(Import { + module, + js_namespace, + kind, + }); } } - pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs) - -> ImportKind - { + pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs) -> ImportKind { let js_name = opts.js_name().unwrap_or(f.ident); - let mut wasm = Function::from_decl(js_name, - f.decl, - f.attrs, - opts, - f.vis, - false).0; + let mut wasm = Function::from_decl(js_name, f.decl, f.attrs, opts, f.vis, false).0; if wasm.opts.catch() { // TODO: this assumes a whole bunch: // @@ -308,20 +314,19 @@ impl Program { } let kind = if wasm.opts.method() { - let class = wasm.arguments.get(0) + 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::Vector(..) => { - panic!("method receivers cannot be vectors") - } + Type::ByRef(ref t) | Type::ByValue(ref t) => t, + Type::ByMutRef(_) => panic!("first method argument cannot be mutable ref"), + Type::Vector(..) => panic!("method receivers cannot be vectors"), }; let class_name = match *class { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, + 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) @@ -337,7 +342,10 @@ impl Program { _ => panic!("constructor returns must be bare types"), }; let class_name = match *class { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, + 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) @@ -367,20 +375,18 @@ impl Program { }) } - pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType) - -> ImportKind - { + pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType) -> ImportKind { ImportKind::Type(ImportType { vis: f.vis, - name: f.ident + name: f.ident, }) } - pub fn push_foreign_static(&mut self, - f: syn::ForeignItemStatic, - opts: BindgenAttrs) - -> ImportKind - { + pub fn push_foreign_static( + &mut self, + f: syn::ForeignItemStatic, + opts: BindgenAttrs, + ) -> ImportKind { if f.mutability.is_some() { panic!("cannot import mutable globals yet") } @@ -427,20 +433,17 @@ impl Function { panic!("can only bindgen safe functions"); } - Function::from_decl(input.ident, - input.decl, - input.attrs, - opts, - input.vis, - false).0 + Function::from_decl(input.ident, input.decl, input.attrs, opts, input.vis, false).0 } - pub fn from_decl(name: syn::Ident, - decl: Box, - attrs: Vec, - opts: BindgenAttrs, - vis: syn::Visibility, - allow_self: bool) -> (Function, Option) { + pub fn from_decl( + name: syn::Ident, + decl: Box, + attrs: Vec, + opts: BindgenAttrs, + vis: syn::Visibility, + allow_self: bool, + ) -> (Function, Option) { if decl.variadic.is_some() { panic!("can't bindgen variadic functions") } @@ -449,20 +452,19 @@ impl Function { } let mut mutable = None; - let arguments = decl.inputs.iter() - .filter_map(|arg| { - match *arg { - syn::FnArg::Captured(ref c) => Some(c), - syn::FnArg::SelfValue(_) => { - panic!("by-value `self` not yet supported"); - } - syn::FnArg::SelfRef(ref a) if allow_self => { - assert!(mutable.is_none()); - mutable = Some(a.mutability.is_some()); - None - } - _ => panic!("arguments cannot be `self` or ignored"), + let arguments = decl.inputs + .iter() + .filter_map(|arg| match *arg { + syn::FnArg::Captured(ref c) => Some(c), + syn::FnArg::SelfValue(_) => { + panic!("by-value `self` not yet supported"); } + syn::FnArg::SelfRef(ref a) if allow_self => { + assert!(mutable.is_none()); + mutable = Some(a.mutability.is_some()); + None + } + _ => panic!("arguments cannot be `self` or ignored"), }) .map(|arg| Type::from(&arg.ty)) .collect::>(); @@ -472,24 +474,27 @@ impl Function { syn::ReturnType::Type(_, ref t) => Some(Type::from(t)), }; - (Function { - name, - arguments, - ret, - opts, - rust_vis: vis, - rust_decl: decl, - rust_attrs: attrs, - }, mutable) + ( + Function { + name, + arguments, + ret, + opts, + rust_vis: vis, + rust_decl: decl, + rust_attrs: attrs, + }, + mutable, + ) } } pub fn extract_path_ident(path: &syn::Path) -> Option { if path.leading_colon.is_some() { - return None + return None; } if path.segments.len() != 1 { - return None + return None; } match path.segments.first().unwrap().value().arguments { syn::PathArguments::None => {} @@ -503,7 +508,10 @@ impl Type { match *ty { syn::Type::Reference(ref r) => { match *r.elem { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => { + syn::Type::Path(syn::TypePath { + qself: None, + ref path, + }) => { let ident = extract_path_ident(path); match ident.as_ref().map(|s| s.as_ref()) { Some("str") => return Type::Vector(VectorType::String, false), @@ -512,7 +520,7 @@ impl Type { } syn::Type::Slice(ref slice) => { if let Some(ty) = VectorType::from(&slice.elem) { - return Type::Vector(ty, false) + return Type::Vector(ty, false); } } _ => {} @@ -521,26 +529,26 @@ impl Type { Type::ByMutRef((*r.elem).clone()) } else { Type::ByRef((*r.elem).clone()) - } + }; } - syn::Type::Path(syn::TypePath { qself: None, ref path }) - if path.leading_colon.is_none() && path.segments.len() == 1 => + syn::Type::Path(syn::TypePath { + qself: None, + ref path, + }) if path.leading_colon.is_none() && path.segments.len() == 1 => { let seg = path.segments.first().unwrap().into_value(); match seg.arguments { - syn::PathArguments::None => { - match seg.ident.as_ref() { - "String" => return Type::Vector(VectorType::String, true), - _ => {} - } - } + syn::PathArguments::None => match seg.ident.as_ref() { + "String" => return Type::Vector(VectorType::String, true), + _ => {} + }, syn::PathArguments::AngleBracketed(ref t) if seg.ident == "Vec" && t.args.len() == 1 => { match **t.args.first().unwrap().value() { syn::GenericArgument::Type(ref t) => { if let Some(ty) = VectorType::from(t) { - return Type::Vector(ty, true) + return Type::Vector(ty, true); } } _ => {} @@ -571,14 +579,9 @@ impl Export { 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 => { - shared::free_function_export_name(self.function.name.as_ref()) + shared::struct_function_export_name(class.as_ref(), self.function.name.as_ref()) } + None => shared::free_function_export_name(self.function.name.as_ref()), }; syn::LitStr::new(&name, Span::def_site()) } @@ -609,7 +612,8 @@ pub struct BindgenAttrs { impl BindgenAttrs { pub fn find(attrs: &mut Vec) -> BindgenAttrs { - let pos = attrs.iter() + let pos = attrs + .iter() .enumerate() .find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen") .map(|a| a.0); @@ -617,101 +621,83 @@ impl BindgenAttrs { Some(i) => i, None => return BindgenAttrs::default(), }; - syn::parse(attrs.remove(pos).tts.into()) - .expect("malformed #[wasm_bindgen] attribute") + 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, - } + self.attrs + .iter() + .filter_map(|a| match *a { + BindgenAttr::Module(ref s) => Some(&s[..]), + _ => None, }) .next() } pub fn catch(&self) -> bool { - self.attrs.iter() - .any(|a| { - match *a { - BindgenAttr::Catch => true, - _ => false, - } - }) + 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, - } - }) + 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, - } - }) + self.attrs.iter().any(|a| match *a { + BindgenAttr::Method => true, + _ => false, + }) } fn js_namespace(&self) -> Option { - self.attrs.iter() - .filter_map(|a| { - match *a { - BindgenAttr::JsNamespace(s) => Some(s), - _ => None, - } + self.attrs + .iter() + .filter_map(|a| match *a { + BindgenAttr::JsNamespace(s) => Some(s), + _ => None, }) .next() } pub fn getter(&self) -> Option> { - self.attrs.iter() - .filter_map(|a| { - match *a { - BindgenAttr::Getter(s) => Some(s), - _ => None, - } + self.attrs + .iter() + .filter_map(|a| match *a { + BindgenAttr::Getter(s) => Some(s), + _ => None, }) .next() } pub fn setter(&self) -> Option> { - self.attrs.iter() - .filter_map(|a| { - match *a { - BindgenAttr::Setter(s) => Some(s), - _ => None, - } + self.attrs + .iter() + .filter_map(|a| match *a { + BindgenAttr::Setter(s) => Some(s), + _ => None, }) .next() } pub fn structural(&self) -> bool { - self.attrs.iter() - .any(|a| { - match *a { - BindgenAttr::Structural => true, - _ => false, - } - }) + self.attrs.iter().any(|a| match *a { + BindgenAttr::Structural => true, + _ => false, + }) } pub fn js_name(&self) -> Option { - self.attrs.iter() - .filter_map(|a| { - match *a { - BindgenAttr::JsName(s) => Some(s), - _ => None, - } + self.attrs + .iter() + .filter_map(|a| match *a { + BindgenAttr::JsName(s) => Some(s), + _ => None, }) .next() } @@ -800,14 +786,17 @@ impl syn::synom::Synom for BindgenAttr { fn extract_first_ty_param(ty: Option<&Type>) -> Option> { let ty = match ty { Some(t) => t, - None => return Some(None) + None => return Some(None), }; let ty = match *ty { Type::ByValue(ref t) => t, _ => return None, }; let path = match *ty { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, + syn::Type::Path(syn::TypePath { + qself: None, + ref path, + }) => path, _ => return None, }; let seg = path.segments.last()?.into_value(); @@ -826,12 +815,10 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option> { Some(Some(Type::from(ty))) } -fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) - -> syn::synom::PResult<'a, ()> -{ +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)) + return Ok(((), next)); } } syn::parse_error() @@ -847,7 +834,10 @@ fn ungroup(input: &syn::Type) -> &syn::Type { impl VectorType { fn from(ty: &syn::Type) -> Option { let path = match *ungroup(ty) { - syn::Type::Path(syn::TypePath { qself: None, ref path }) => path, + syn::Type::Path(syn::TypePath { + qself: None, + ref path, + }) => path, _ => return None, }; match extract_path_ident(path)?.as_ref() { diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs new file mode 100644 index 00000000..bb162d42 --- /dev/null +++ b/crates/backend/src/codegen.rs @@ -0,0 +1,621 @@ +use ast; +use quote::{ToTokens, Tokens}; +use proc_macro2::Span; +use shared; +use std::borrow::Cow; +use std::collections::HashSet; +use std::env; +use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering}; +use syn; + +fn to_ident_name(s: &str) -> Cow { + if s.chars().all(|c| match c { + 'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => true, + _ => false, + }) { + return Cow::from(s); + } + + Cow::from( + s.chars() + .map(|c| match c { + 'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => c, + _ => '_', + }) + .collect::(), + ) +} + +impl ToTokens for ast::Program { + // Generate wrappers for all the items that we've found + fn to_tokens(&self, tokens: &mut Tokens) { + for export in self.exports.iter() { + export.to_tokens(tokens); + } + for s in self.structs.iter() { + s.to_tokens(tokens); + } + let mut types = HashSet::new(); + for i in self.imports.iter() { + if let ast::ImportKind::Type(ref t) = i.kind { + types.insert(t.name); + } + } + for i in self.imports.iter() { + match i.js_namespace { + Some(ns) if types.contains(&ns) => { + let kind = &i.kind; + (quote! { impl #ns { #kind } }).to_tokens(tokens); + } + _ => i.kind.to_tokens(tokens), + } + } + for e in self.enums.iter() { + e.to_tokens(tokens); + } + + // 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 crate_name = env::var("CARGO_PKG_NAME").expect("should have CARGO_PKG_NAME env var"); + let crate_vers = + env::var("CARGO_PKG_VERSION").expect("should have CARGO_PKG_VERSION env var"); + + let generated_static_name = format!( + "__WASM_BINDGEN_GENERATED_{}_{}_{}", + to_ident_name(&crate_name), + to_ident_name(&crate_vers), + CNT.fetch_add(1, Ordering::SeqCst) + ); + let generated_static_name = syn::Ident::from(generated_static_name); + + let mut generated_static_value = Tokens::new(); + let generated_static_length = self.literal(&mut generated_static_value); + + (my_quote! { + #[allow(non_upper_case_globals)] + #[wasm_custom_section = "__wasm_bindgen_unstable"] + const #generated_static_name: [u8; #generated_static_length] = + [#generated_static_value]; + }).to_tokens(tokens); + } +} + +impl ToTokens for ast::Struct { + fn to_tokens(&self, tokens: &mut Tokens) { + let name = &self.name; + let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref())); + let c = shared::name_to_descriptor(name.as_ref()) as u32; + (my_quote! { + impl ::wasm_bindgen::convert::WasmBoundary for #name { + type Js = u32; + const DESCRIPTOR: u32 = #c; + + fn into_js(self) -> u32 { + Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 + } + + unsafe fn from_js(js: u32) -> Self { + let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; + ::wasm_bindgen::__rt::assert_not_null(js); + let js = Box::from_raw(js); + js.borrow_mut(); // make sure no one's borrowing + js.into_inner() + } + } + + impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { + type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>; + unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor { + let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; + ::wasm_bindgen::__rt::assert_not_null(js); + (*js).borrow() + } + } + + impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name { + type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>; + + unsafe fn from_js_ref_mut(js: Self::Js) -> Self::RefAnchor { + let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; + ::wasm_bindgen::__rt::assert_not_null(js); + (*js).borrow_mut() + } + } + + #[no_mangle] + pub unsafe extern fn #free_fn(ptr: u32) { + <#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr); + } + }).to_tokens(tokens); + } +} + +impl ToTokens for ast::Export { + fn to_tokens(self: &ast::Export, into: &mut Tokens) { + let generated_name = self.rust_symbol(); + let export_name = self.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 self.method { + let class = self.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); + let me = unsafe { &*me }; + }); + offset = 1; + } + + for (i, ty) in self.function.arguments.iter().enumerate() { + let i = i + offset; + let ident = syn::Ident::from(format!("arg{}", i)); + match *ty { + ast::Type::Vector(ref ty, owned) => { + let ptr = syn::Ident::from(format!("arg{}_ptr", i)); + let len = syn::Ident::from(format!("arg{}_len", i)); + let abi_ty = ty.abi_element(); + args.push(my_quote! { #ptr: *mut #abi_ty }); + args.push(my_quote! { #len: usize }); + if owned { + arg_conversions.push(my_quote! { + let #ident = unsafe { + ::std::vec::Vec::from_raw_parts(#ptr, #len, #len) + }; + }); + } else { + arg_conversions.push(my_quote! { + let #ident = unsafe { + ::std::slice::from_raw_parts(#ptr as *const #abi_ty, #len) + }; + }); + } + if let ast::VectorType::String = *ty { + if owned { + arg_conversions.push(my_quote! { + let #ident = unsafe { + ::std::string::String::from_utf8_unchecked(#ident) + }; + }); + } else { + arg_conversions.push(my_quote! { + let #ident = unsafe { + ::std::str::from_utf8_unchecked(#ident) + }; + }); + } + } + } + ast::Type::ByValue(ref t) => { + args.push(my_quote! { + #ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js + }); + arg_conversions.push(my_quote! { + let #ident = unsafe { + <#t as ::wasm_bindgen::convert::WasmBoundary> + ::from_js(#ident) + }; + }); + } + ast::Type::ByRef(ref ty) => { + args.push(my_quote! { + #ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js + }); + arg_conversions.push(my_quote! { + let #ident = unsafe { + <#ty as ::wasm_bindgen::convert::FromRefWasmBoundary> + ::from_js_ref(#ident) + }; + let #ident = &*#ident; + }); + } + ast::Type::ByMutRef(ref ty) => { + args.push(my_quote! { + #ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js + }); + arg_conversions.push(my_quote! { + let mut #ident = unsafe { + <#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary> + ::from_js_ref_mut(#ident) + }; + let #ident = &mut *#ident; + }); + } + } + converted_arguments.push(my_quote! { #ident }); + } + let ret_ty; + let convert_ret; + match self.function.ret { + Some(ast::Type::Vector(ref ty, true)) => { + ret_ty = my_quote! { -> *mut #ty }; + convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) }; + } + Some(ast::Type::ByValue(ref t)) => { + ret_ty = my_quote! { + -> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js + }; + convert_ret = my_quote! { + <#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret) + }; + } + Some(ast::Type::Vector(_, false)) + | Some(ast::Type::ByMutRef(_)) + | Some(ast::Type::ByRef(_)) => { + panic!("can't return a borrowed ref"); + } + None => { + ret_ty = my_quote!{}; + convert_ret = my_quote!{}; + } + } + + let name = self.function.name; + let receiver = match self.class { + Some(_) if self.method => { + if self.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)] + pub extern fn #generated_name(#(#args),*) #ret_ty { + ::wasm_bindgen::__rt::link_this_library(); + #(#arg_conversions)* + let #ret = #receiver(#(#converted_arguments),*); + #convert_ret + } + }; + tokens.to_tokens(into); + } +} + +impl ToTokens for ast::ImportType { + fn to_tokens(&self, tokens: &mut Tokens) { + let vis = &self.vis; + let name = &self.name; + (my_quote! { + #[allow(bad_style)] + #vis struct #name { + obj: ::wasm_bindgen::JsValue, + } + + impl ::wasm_bindgen::convert::WasmBoundary for #name { + type Js = <::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::WasmBoundary>::Js; + const DESCRIPTOR: u32 = <::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR; + + fn into_js(self) -> Self::Js { + self.obj.into_js() + } + + unsafe fn from_js(js: Self::Js) -> Self { + #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() + } + } + + impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { + type RefAnchor = ::std::mem::ManuallyDrop<#name>; + unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor { + let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> + ::from_js(js); + ::std::mem::ManuallyDrop::new(#name { obj }) + } + } + + + impl From<::wasm_bindgen::JsValue> for #name { + fn from(obj: ::wasm_bindgen::JsValue) -> #name { + #name { obj } + } + } + + impl From<#name> for ::wasm_bindgen::JsValue { + fn from(obj: #name) -> ::wasm_bindgen::JsValue { + obj.obj + } + } + }).to_tokens(tokens); + } +} + +impl ToTokens for ast::ImportKind { + fn to_tokens(&self, tokens: &mut Tokens) { + match *self { + ast::ImportKind::Function(ref f) => f.to_tokens(tokens), + ast::ImportKind::Static(ref s) => s.to_tokens(tokens), + ast::ImportKind::Type(ref t) => t.to_tokens(tokens), + } + } +} + +impl ToTokens for ast::ImportFunction { + fn to_tokens(&self, tokens: &mut Tokens) { + let mut class_ty = None; + let mut is_method = false; + match self.kind { + ast::ImportFunctionKind::Method { ref ty, .. } => { + is_method = true; + class_ty = Some(ty); + } + ast::ImportFunctionKind::JsConstructor { ref ty, .. } => { + class_ty = Some(ty); + } + ast::ImportFunctionKind::Normal => {} + } + let vis = &self.function.rust_vis; + let ret = &self.function.rust_decl.output; + let fn_token = &self.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 names = self.function + .rust_decl + .inputs + .iter() + .map(|arg| match *arg { + syn::FnArg::Captured(ref c) => c, + _ => panic!("arguments cannot be `self` or ignored"), + }) + .map(|arg| match arg.pat { + syn::Pat::Ident(syn::PatIdent { + by_ref: None, + ident, + subpat: None, + .. + }) => ident, + _ => panic!("unsupported pattern in foreign function"), + }); + + for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() { + match *ty { + ast::Type::Vector(ref ty, owned) => { + let ptr = syn::Ident::from(format!("{}_ptr", name)); + let len = syn::Ident::from(format!("{}_len", name)); + abi_argument_names.push(ptr); + abi_argument_names.push(len); + let abi_ty = ty.abi_element(); + abi_arguments.push(my_quote! { #ptr: *const #abi_ty }); + abi_arguments.push(my_quote! { #len: usize }); + arg_conversions.push(my_quote! { + let #ptr = #name.as_ptr(); + let #len = #name.len(); + }); + if owned { + arg_conversions.push(my_quote! { ::std::mem::forget(#name); }); + } + } + ast::Type::ByValue(ref t) => { + abi_argument_names.push(name); + abi_arguments.push(my_quote! { + #name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js + }); + 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 }); + 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); + }); + } + } + } + } + let abi_ret; + let mut convert_ret; + match self.function.ret { + Some(ast::Type::ByValue(ref t)) => { + abi_ret = my_quote! { + <#t as ::wasm_bindgen::convert::WasmBoundary>::Js + }; + convert_ret = my_quote! { + <#t as ::wasm_bindgen::convert::WasmBoundary>::from_js(#ret_ident) + }; + } + + Some(ast::Type::Vector(ref ty, true)) => { + let name = syn::Ident::from("__ret_len"); + let name_ptr = syn::Ident::from("__ret_len_ptr"); + abi_argument_names.push(name_ptr); + abi_arguments.push(my_quote! { #name_ptr: *mut usize }); + arg_conversions.push(my_quote! { + let mut #name = 0; + let mut #name_ptr = &mut #name as *mut usize; + }); + let abi_ty = ty.abi_element(); + abi_ret = my_quote! { *mut #abi_ty }; + if let ast::VectorType::String = *ty { + convert_ret = my_quote! { + String::from_utf8_unchecked( + Vec::from_raw_parts(#ret_ident, #name, #name) + ) + }; + } else { + convert_ret = my_quote! { + Vec::from_raw_parts(#ret_ident, #name, #name) + }; + } + } + Some(ast::Type::ByRef(_)) + | Some(ast::Type::Vector(_, false)) + | Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"), + None => { + abi_ret = my_quote! { () }; + convert_ret = my_quote! { () }; + } + } + + let mut exceptional_ret = my_quote!{}; + if self.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); + abi_arguments.push(my_quote! { #exn_data_ptr: *mut u32 }); + arg_conversions.push(my_quote! { + let mut #exn_data = [0; 2]; + let #exn_data_ptr = #exn_data.as_mut_ptr(); + }); + convert_ret = my_quote! { Ok(#convert_ret) }; + exceptional_ret = my_quote! { + if #exn_data[0] == 1 { + return Err(<::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::WasmBoundary>::from_js(#exn_data[1])) + } + }; + } + + let rust_name = self.rust_name; + let import_name = self.shim; + let attrs = &self.function.rust_attrs; + + let arguments = self.function + .rust_decl + .inputs + .iter() + .skip(if is_method { 1 } else { 0 }) + .collect::>(); + + let me = if is_method { + my_quote! { &self, } + } else { + quote!() + }; + + let invocation = my_quote! { + #(#attrs)* + #[allow(bad_style)] + #vis extern #fn_token #rust_name(#me #(#arguments),*) #ret { + ::wasm_bindgen::__rt::link_this_library(); + #[wasm_import_module = "__wbindgen_placeholder__"] + extern { + fn #import_name(#(#abi_arguments),*) -> #abi_ret; + } + unsafe { + #(#arg_conversions)* + let #ret_ident = #import_name(#(#abi_argument_names),*); + #exceptional_ret + #convert_ret + } + } + }; + + if let Some(class) = class_ty { + (quote! { + impl #class { + #invocation + } + }).to_tokens(tokens); + } else { + invocation.to_tokens(tokens); + } + } +} + +impl ToTokens for ast::Enum { + fn to_tokens(&self, into: &mut Tokens) { + let enum_name = &self.name; + let c = shared::TYPE_ENUM as u32; + let incoming_u32 = quote! { n }; + let enum_name_as_string = enum_name.to_string(); + let cast_clauses = self.variants.iter().map(|variant| { + let variant_name = &variant.name; + quote! { + if #incoming_u32 == #enum_name::#variant_name as u32 { + #enum_name::#variant_name + } + } + }); + (my_quote! { + impl #enum_name { + fn from_u32(#incoming_u32: u32) -> #enum_name { + #(#cast_clauses else)* { + wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string)); + } + } + } + + impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { + type Js = u32; + const DESCRIPTOR: u32 = #c; + + fn into_js(self) -> u32 { + self as u32 + } + + unsafe fn from_js(js: u32) -> Self { + #enum_name::from_u32(js) + } + } + }).to_tokens(into); + } +} + +impl ToTokens for ast::ImportStatic { + fn to_tokens(&self, into: &mut Tokens) { + let name = self.rust_name; + let ty = &self.ty; + let shim_name = self.shim; + let vis = &self.vis; + (my_quote! { + #[allow(bad_style)] + #vis static #name: ::wasm_bindgen::JsStatic<#ty> = { + fn init() -> #ty { + #[wasm_import_module = "__wbindgen_placeholder__"] + extern { + fn #shim_name() -> u32; + } + unsafe { + ::wasm_bindgen::convert::WasmBoundary::from_js(#shim_name()) + } + } + ::wasm_bindgen::JsStatic { + __inner: ::std::cell::UnsafeCell::new(None), + __init: init, + } + }; + }).to_tokens(into); + } +} diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs new file mode 100755 index 00000000..a217b3ad --- /dev/null +++ b/crates/backend/src/lib.rs @@ -0,0 +1,25 @@ +#![recursion_limit = "128"] + +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +extern crate wasm_bindgen_shared as shared; + +macro_rules! my_quote { + ($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*)) +} + +pub mod ast; +mod codegen; +mod literal; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/crates/macro/src/literal.rs b/crates/backend/src/literal.rs similarity index 97% rename from crates/macro/src/literal.rs rename to crates/backend/src/literal.rs index c44046bf..286fac47 100644 --- a/crates/macro/src/literal.rs +++ b/crates/backend/src/literal.rs @@ -11,10 +11,7 @@ pub struct LiteralBuilder<'a> { impl<'a> LiteralBuilder<'a> { pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> { - LiteralBuilder { - dst, - cnt: 0, - } + LiteralBuilder { dst, cnt: 0 } } pub fn finish(self) -> usize { @@ -95,8 +92,9 @@ impl<'a> LiteralBuilder<'a> { } fn list(&mut self, list: T, mut cb: F) - where F: FnMut(T::Item, &mut Self), - T: IntoIterator, + where + F: FnMut(T::Item, &mut Self), + T: IntoIterator, { self.append("["); for (i, element) in list.into_iter().enumerate() { @@ -316,8 +314,6 @@ impl Literal for ast::ImportStatic { impl Literal for ast::ImportType { fn literal(&self, a: &mut LiteralBuilder) { - a.fields(&[ - ("kind", &|a| a.str("type")), - ]) + a.fields(&[("kind", &|a| a.str("type"))]) } } diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index 3a0dfe87..77f04522 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -19,3 +19,4 @@ quote = '0.4' proc-macro2 = { version = "0.2", features = ["nightly"] } serde_json = "1" wasm-bindgen-shared = { path = "../shared", version = "0.1.0" } +wasm-bindgen-backend = { path = "../backend", version = "0.1.0" } diff --git a/crates/macro/src/lib.rs b/crates/macro/src/lib.rs index 9499772a..7b8eb874 100755 --- a/crates/macro/src/lib.rs +++ b/crates/macro/src/lib.rs @@ -1,40 +1,24 @@ -#![recursion_limit = "128"] #![feature(proc_macro)] -#[macro_use] extern crate syn; -#[macro_use] extern crate quote; extern crate proc_macro; extern crate proc_macro2; extern crate serde_json; +extern crate wasm_bindgen_backend as backend; extern crate wasm_bindgen_shared as shared; -use std::borrow::Cow; -use std::env; -use std::sync::atomic::*; -use std::collections::HashSet; - use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::{Tokens, ToTokens}; - -macro_rules! my_quote { - ($($t:tt)*) => (quote_spanned!(Span::call_site() => $($t)*)) -} - -mod ast; -mod literal; +use quote::{ToTokens, Tokens}; #[proc_macro_attribute] pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { - let item = syn::parse::(input.clone()) - .expect("expected a valid Rust item"); - let opts = syn::parse::(attr) + let item = syn::parse::(input.clone()).expect("expected a valid Rust item"); + let opts = syn::parse::(attr) .expect("invalid arguments to #[wasm_bindgen]"); let mut ret = Tokens::new(); - let mut program = ast::Program::default(); + let mut program = backend::ast::Program::default(); program.push_item(item, Some(opts), &mut ret); program.to_tokens(&mut ret); @@ -42,616 +26,3 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { ret.into() } - -fn to_ident_name(s: &str) -> Cow { - if s.chars().all(|c| match c { - 'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => true, - _ => false, - }) { - return Cow::from(s); - } - - Cow::from( - s.chars() - .map(|c| match c { - 'a'...'z' | 'A'...'Z' | '0'...'9' | '_' => c, - _ => '_', - }) - .collect::(), - ) -} - -impl ToTokens for ast::Program { - // Generate wrappers for all the items that we've found - fn to_tokens(&self, tokens: &mut Tokens) { - for export in self.exports.iter() { - export.to_tokens(tokens); - } - for s in self.structs.iter() { - s.to_tokens(tokens); - } - let mut types = HashSet::new(); - for i in self.imports.iter() { - if let ast::ImportKind::Type(ref t) = i.kind { - types.insert(t.name); - } - } - for i in self.imports.iter() { - match i.js_namespace { - Some(ns) if types.contains(&ns) => { - let kind = &i.kind; - (quote! { impl #ns { #kind } }).to_tokens(tokens); - } - _ => i.kind.to_tokens(tokens), - } - } - for e in self.enums.iter() { - e.to_tokens(tokens); - } - - // 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 crate_name = env::var("CARGO_PKG_NAME").expect("should have CARGO_PKG_NAME env var"); - let crate_vers = env::var("CARGO_PKG_VERSION").expect("should have CARGO_PKG_VERSION env var"); - - let generated_static_name = format!( - "__WASM_BINDGEN_GENERATED_{}_{}_{}", - to_ident_name(&crate_name), - to_ident_name(&crate_vers), - CNT.fetch_add(1, Ordering::SeqCst) - ); - let generated_static_name = syn::Ident::from(generated_static_name); - - let mut generated_static_value = Tokens::new(); - let generated_static_length = self.literal(&mut generated_static_value); - - (my_quote! { - #[allow(non_upper_case_globals)] - #[wasm_custom_section = "__wasm_bindgen_unstable"] - const #generated_static_name: [u8; #generated_static_length] = - [#generated_static_value]; - }).to_tokens(tokens); - } -} - -impl ToTokens for ast::Struct { - fn to_tokens(&self, tokens: &mut Tokens) { - let name = &self.name; - let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref())); - let c = shared::name_to_descriptor(name.as_ref()) as u32; - (my_quote! { - impl ::wasm_bindgen::convert::WasmBoundary for #name { - type Js = u32; - const DESCRIPTOR: u32 = #c; - - fn into_js(self) -> u32 { - Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 - } - - unsafe fn from_js(js: u32) -> Self { - let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - ::wasm_bindgen::__rt::assert_not_null(js); - let js = Box::from_raw(js); - js.borrow_mut(); // make sure no one's borrowing - js.into_inner() - } - } - - impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { - type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>; - unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor { - let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - ::wasm_bindgen::__rt::assert_not_null(js); - (*js).borrow() - } - } - - impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name { - type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>; - - unsafe fn from_js_ref_mut(js: Self::Js) -> Self::RefAnchor { - let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - ::wasm_bindgen::__rt::assert_not_null(js); - (*js).borrow_mut() - } - } - - #[no_mangle] - pub unsafe extern fn #free_fn(ptr: u32) { - <#name as ::wasm_bindgen::convert::WasmBoundary>::from_js(ptr); - } - }).to_tokens(tokens); - } -} - -impl ToTokens for ast::Export { - fn to_tokens(self: &ast::Export, into: &mut Tokens) { - let generated_name = self.rust_symbol(); - let export_name = self.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 self.method { - let class = self.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); - let me = unsafe { &*me }; - }); - offset = 1; - } - - for (i, ty) in self.function.arguments.iter().enumerate() { - let i = i + offset; - let ident = syn::Ident::from(format!("arg{}", i)); - match *ty { - ast::Type::Vector(ref ty, owned) => { - let ptr = syn::Ident::from(format!("arg{}_ptr", i)); - let len = syn::Ident::from(format!("arg{}_len", i)); - let abi_ty = ty.abi_element(); - args.push(my_quote! { #ptr: *mut #abi_ty }); - args.push(my_quote! { #len: usize }); - if owned { - arg_conversions.push(my_quote! { - let #ident = unsafe { - ::std::vec::Vec::from_raw_parts(#ptr, #len, #len) - }; - }); - } else { - arg_conversions.push(my_quote! { - let #ident = unsafe { - ::std::slice::from_raw_parts(#ptr as *const #abi_ty, #len) - }; - }); - } - if let ast::VectorType::String = *ty { - if owned { - arg_conversions.push(my_quote! { - let #ident = unsafe { - ::std::string::String::from_utf8_unchecked(#ident) - }; - }); - } else { - arg_conversions.push(my_quote! { - let #ident = unsafe { - ::std::str::from_utf8_unchecked(#ident) - }; - }); - } - } - } - ast::Type::ByValue(ref t) => { - args.push(my_quote! { - #ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js - }); - arg_conversions.push(my_quote! { - let #ident = unsafe { - <#t as ::wasm_bindgen::convert::WasmBoundary> - ::from_js(#ident) - }; - }); - } - ast::Type::ByRef(ref ty) => { - args.push(my_quote! { - #ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js - }); - arg_conversions.push(my_quote! { - let #ident = unsafe { - <#ty as ::wasm_bindgen::convert::FromRefWasmBoundary> - ::from_js_ref(#ident) - }; - let #ident = &*#ident; - }); - } - ast::Type::ByMutRef(ref ty) => { - args.push(my_quote! { - #ident: <#ty as ::wasm_bindgen::convert::WasmBoundary>::Js - }); - arg_conversions.push(my_quote! { - let mut #ident = unsafe { - <#ty as ::wasm_bindgen::convert::FromRefMutWasmBoundary> - ::from_js_ref_mut(#ident) - }; - let #ident = &mut *#ident; - }); - } - } - converted_arguments.push(my_quote! { #ident }); - } - let ret_ty; - let convert_ret; - match self.function.ret { - Some(ast::Type::Vector(ref ty, true)) => { - ret_ty = my_quote! { -> *mut #ty }; - convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) }; - } - Some(ast::Type::ByValue(ref t)) => { - ret_ty = my_quote! { - -> <#t as ::wasm_bindgen::convert::WasmBoundary>::Js - }; - convert_ret = my_quote! { - <#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret) - }; - } - Some(ast::Type::Vector(_, false)) | - Some(ast::Type::ByMutRef(_)) | - Some(ast::Type::ByRef(_)) => { - panic!("can't return a borrowed ref"); - } - None => { - ret_ty = my_quote! {}; - convert_ret = my_quote! {}; - } - } - - let name = self.function.name; - let receiver = match self.class { - Some(_) if self.method => { - if self.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)] - pub extern fn #generated_name(#(#args),*) #ret_ty { - ::wasm_bindgen::__rt::link_this_library(); - #(#arg_conversions)* - let #ret = #receiver(#(#converted_arguments),*); - #convert_ret - } - }; - tokens.to_tokens(into); - } -} - -impl ToTokens for ast::ImportType { - fn to_tokens(&self, tokens: &mut Tokens) { - let vis = &self.vis; - let name = &self.name; - (my_quote! { - #[allow(bad_style)] - #vis struct #name { - obj: ::wasm_bindgen::JsValue, - } - - impl ::wasm_bindgen::convert::WasmBoundary for #name { - type Js = <::wasm_bindgen::JsValue as - ::wasm_bindgen::convert::WasmBoundary>::Js; - const DESCRIPTOR: u32 = <::wasm_bindgen::JsValue as - ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR; - - fn into_js(self) -> Self::Js { - self.obj.into_js() - } - - unsafe fn from_js(js: Self::Js) -> Self { - #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() - } - } - - impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { - type RefAnchor = ::std::mem::ManuallyDrop<#name>; - unsafe fn from_js_ref(js: Self::Js) -> Self::RefAnchor { - let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> - ::from_js(js); - ::std::mem::ManuallyDrop::new(#name { obj }) - } - } - - - impl From<::wasm_bindgen::JsValue> for #name { - fn from(obj: ::wasm_bindgen::JsValue) -> #name { - #name { obj } - } - } - - impl From<#name> for ::wasm_bindgen::JsValue { - fn from(obj: #name) -> ::wasm_bindgen::JsValue { - obj.obj - } - } - }).to_tokens(tokens); - } -} - -impl ToTokens for ast::ImportKind { - fn to_tokens(&self, tokens: &mut Tokens) { - match *self { - ast::ImportKind::Function(ref f) => f.to_tokens(tokens), - ast::ImportKind::Static(ref s) => s.to_tokens(tokens), - ast::ImportKind::Type(ref t) => t.to_tokens(tokens), - } - } -} - -impl ToTokens for ast::ImportFunction { - fn to_tokens(&self, tokens: &mut Tokens) { - let mut class_ty = None; - let mut is_method = false; - match self.kind { - ast::ImportFunctionKind::Method { ref ty, .. } => { - is_method = true; - class_ty = Some(ty); - } - ast::ImportFunctionKind::JsConstructor { ref ty, .. } => { - class_ty = Some(ty); - } - ast::ImportFunctionKind::Normal => {} - } - let vis = &self.function.rust_vis; - let ret = &self.function.rust_decl.output; - let fn_token = &self.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 names = self.function.rust_decl.inputs - .iter() - .map(|arg| { - match *arg { - syn::FnArg::Captured(ref c) => c, - _ => panic!("arguments cannot be `self` or ignored"), - } - }) - .map(|arg| { - match arg.pat { - syn::Pat::Ident(syn::PatIdent { - by_ref: None, - ident, - subpat: None, - .. - }) => { - ident - } - _ => panic!("unsupported pattern in foreign function"), - } - }); - - for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() { - match *ty { - ast::Type::Vector(ref ty, owned) => { - let ptr = syn::Ident::from(format!("{}_ptr", name)); - let len = syn::Ident::from(format!("{}_len", name)); - abi_argument_names.push(ptr); - abi_argument_names.push(len); - let abi_ty = ty.abi_element(); - abi_arguments.push(my_quote! { #ptr: *const #abi_ty }); - abi_arguments.push(my_quote! { #len: usize }); - arg_conversions.push(my_quote! { - let #ptr = #name.as_ptr(); - let #len = #name.len(); - }); - if owned { - arg_conversions.push(my_quote! { ::std::mem::forget(#name); }); - } - } - ast::Type::ByValue(ref t) => { - abi_argument_names.push(name); - abi_arguments.push(my_quote! { - #name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Js - }); - 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 }); - 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); - }); - } - } - } - } - let abi_ret; - let mut convert_ret; - match self.function.ret { - Some(ast::Type::ByValue(ref t)) => { - abi_ret = my_quote! { - <#t as ::wasm_bindgen::convert::WasmBoundary>::Js - }; - convert_ret = my_quote! { - <#t as ::wasm_bindgen::convert::WasmBoundary>::from_js(#ret_ident) - }; - } - - Some(ast::Type::Vector(ref ty, true)) => { - let name = syn::Ident::from("__ret_len"); - let name_ptr = syn::Ident::from("__ret_len_ptr"); - abi_argument_names.push(name_ptr); - abi_arguments.push(my_quote! { #name_ptr: *mut usize }); - arg_conversions.push(my_quote! { - let mut #name = 0; - let mut #name_ptr = &mut #name as *mut usize; - }); - let abi_ty = ty.abi_element(); - abi_ret = my_quote! { *mut #abi_ty }; - if let ast::VectorType::String = *ty { - convert_ret = my_quote! { - String::from_utf8_unchecked( - Vec::from_raw_parts(#ret_ident, #name, #name) - ) - }; - } else { - convert_ret = my_quote! { - Vec::from_raw_parts(#ret_ident, #name, #name) - }; - } - } - Some(ast::Type::ByRef(_)) | - Some(ast::Type::Vector(_, false)) | - Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"), - None => { - abi_ret = my_quote! { () }; - convert_ret = my_quote! { () }; - } - } - - let mut exceptional_ret = my_quote! {}; - if self.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); - abi_arguments.push(my_quote! { #exn_data_ptr: *mut u32 }); - arg_conversions.push(my_quote! { - let mut #exn_data = [0; 2]; - let #exn_data_ptr = #exn_data.as_mut_ptr(); - }); - convert_ret = my_quote! { Ok(#convert_ret) }; - exceptional_ret = my_quote! { - if #exn_data[0] == 1 { - return Err(<::wasm_bindgen::JsValue as - ::wasm_bindgen::convert::WasmBoundary>::from_js(#exn_data[1])) - } - }; - } - - let rust_name = self.rust_name; - let import_name = self.shim; - let attrs = &self.function.rust_attrs; - - let arguments = self.function.rust_decl.inputs - .iter() - .skip(if is_method { 1 } else { 0 }) - .collect::>(); - - let me = if is_method { - my_quote! { &self, } - } else { - quote!() - }; - - let invocation = my_quote! { - #(#attrs)* - #[allow(bad_style)] - #vis extern #fn_token #rust_name(#me #(#arguments),*) #ret { - ::wasm_bindgen::__rt::link_this_library(); - #[wasm_import_module = "__wbindgen_placeholder__"] - extern { - fn #import_name(#(#abi_arguments),*) -> #abi_ret; - } - unsafe { - #(#arg_conversions)* - let #ret_ident = #import_name(#(#abi_argument_names),*); - #exceptional_ret - #convert_ret - } - } - }; - - if let Some(class) = class_ty { - (quote! { - impl #class { - #invocation - } - }).to_tokens(tokens); - } else { - invocation.to_tokens(tokens); - } - } -} - -impl ToTokens for ast::Enum { - fn to_tokens(&self, into: &mut Tokens) { - let enum_name = &self.name; - let c = shared::TYPE_ENUM as u32; - let incoming_u32 = quote! { n }; - let enum_name_as_string = enum_name.to_string(); - let cast_clauses = self.variants.iter().map(|variant| { - let variant_name = &variant.name; - quote! { - if #incoming_u32 == #enum_name::#variant_name as u32 { - #enum_name::#variant_name - } - } - }); - (my_quote! { - impl #enum_name { - fn from_u32(#incoming_u32: u32) -> #enum_name { - #(#cast_clauses else)* { - wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string)); - } - } - } - - impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { - type Js = u32; - const DESCRIPTOR: u32 = #c; - - fn into_js(self) -> u32 { - self as u32 - } - - unsafe fn from_js(js: u32) -> Self { - #enum_name::from_u32(js) - } - } - }).to_tokens(into); - } -} - -impl ToTokens for ast::ImportStatic { - fn to_tokens(&self, into: &mut Tokens) { - let name = self.rust_name; - let ty = &self.ty; - let shim_name = self.shim; - let vis = &self.vis; - (my_quote! { - #[allow(bad_style)] - #vis static #name: ::wasm_bindgen::JsStatic<#ty> = { - fn init() -> #ty { - #[wasm_import_module = "__wbindgen_placeholder__"] - extern { - fn #shim_name() -> u32; - } - unsafe { - ::wasm_bindgen::convert::WasmBoundary::from_js(#shim_name()) - } - } - ::wasm_bindgen::JsStatic { - __inner: ::std::cell::UnsafeCell::new(None), - __init: init, - } - }; - }).to_tokens(into); - } -}