From c2e9a4b71e350cf3fffcb9a156ffd67340206d6e Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 7 Mar 2018 14:48:18 -0800 Subject: [PATCH] Implement quote::ToTokens for AST types They were already implemented, just without the formalization ;) --- crates/wasm-bindgen-macro/src/ast.rs | 12 +- crates/wasm-bindgen-macro/src/lib.rs | 975 ++++++++++++++------------- 2 files changed, 503 insertions(+), 484 deletions(-) diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index b6b82be6..aa5d692a 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -10,7 +10,7 @@ pub struct Program { pub exports: Vec, pub imports: Vec, pub enums: Vec, - pub imported_types: Vec<(syn::Visibility, syn::Ident)>, + pub imported_types: Vec, pub structs: Vec, } @@ -53,6 +53,11 @@ pub struct Enum { pub variants: Vec<(syn::Ident, u32)> } +pub struct ImportedType { + pub vis: syn::Visibility, + pub name: syn::Ident, +} + pub enum Type { // special Vector(VectorType, bool), @@ -323,7 +328,10 @@ impl Program { pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType, _module_opts: &BindgenAttrs) { - self.imported_types.push((f.vis, f.ident)); + self.imported_types.push(ImportedType { + vis: f.vis, + name: f.ident + }); } pub fn wbg_literal(&self, dst: &mut Tokens) -> usize { diff --git a/crates/wasm-bindgen-macro/src/lib.rs b/crates/wasm-bindgen-macro/src/lib.rs index b0fe93d9..1a8bd01e 100755 --- a/crates/wasm-bindgen-macro/src/lib.rs +++ b/crates/wasm-bindgen-macro/src/lib.rs @@ -34,7 +34,7 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream { let mut ret = Tokens::new(); let mut program = ast::Program::default(); program.push_item(item, Some(opts), &mut ret); - generate_wrappers(program, &mut ret); + program.to_tokens(&mut ret); // println!("{}", ret); @@ -59,523 +59,534 @@ fn to_ident_name(s: &str) -> Cow { ) } -// 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); +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); + } + for i in self.imports.iter() { + i.to_tokens(tokens); + } + for e in self.enums.iter() { + e.to_tokens(tokens); + } + for it in self.imported_types.iter() { + it.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.wbg_literal(&mut generated_static_value); + + (my_quote! { + #[no_mangle] + #[allow(non_upper_case_globals)] + pub static #generated_static_name: [u32; #generated_static_length] = + [#generated_static_value]; + }).to_tokens(tokens); } - for s in program.structs.iter() { - bindgen_struct(s, tokens); - } - for i in program.imports.iter() { - bindgen_import(i, tokens); - } - for e in program.enums.iter() { - bindgen_enum(e, tokens); - } - for &(ref vis, ref t) in program.imported_types.iter() { - bindgen_imported_type(vis, t, 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 = program.wbg_literal(&mut generated_static_value); - - (my_quote! { - #[no_mangle] - #[allow(non_upper_case_globals)] - pub static #generated_static_name: [u32; #generated_static_length] = - [#generated_static_value]; - }).to_tokens(tokens); } -fn bindgen_struct(s: &ast::Struct, into: &mut Tokens) { - let name = &s.name; - 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 { - type Js = u32; - const DESCRIPTOR: u32 = #c; +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(into); -} - -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 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); - let me = unsafe { &*me }; - }); - offset = 1; - } - - for (i, ty) in export.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) - }; - }); + fn into_js(self) -> u32 { + Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 } - if let ast::VectorType::String = *ty { + + 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::string::String::from_utf8_unchecked(#ident) + ::std::vec::Vec::from_raw_parts(#ptr, #len, #len) }; }); } else { arg_conversions.push(my_quote! { let #ident = unsafe { - ::std::str::from_utf8_unchecked(#ident) + ::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::ImportedType { + fn to_tokens(&self, tokens: &mut Tokens) { + let vis = &self.vis; + let name = &self.name; + (my_quote! { + #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() + } + } + }).to_tokens(tokens); + } +} + +impl ToTokens for ast::Import { + fn to_tokens(&self, tokens: &mut Tokens) { + let mut class_ty = None; + let mut is_method = false; + let mut class_name = None; + match self.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), + self.function.name.as_ref(), + ); + 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); + }); + } } } - ast::Type::ByValue(ref t) => { - args.push(my_quote! { - #ident: <#t as ::wasm_bindgen::convert::WasmBoundary >::Js - }); + } + 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 #ident = unsafe { - <#t as ::wasm_bindgen::convert::WasmBoundary> - ::from_js(#ident) - }; + let mut #name = 0; + let mut #name_ptr = &mut #name as *mut usize; }); - } - 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 export.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 = 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)] - 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); -} - -fn bindgen_imported_type(vis: &syn::Visibility, - name: &syn::Ident, - tokens: &mut Tokens) { - (my_quote! { - #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() - } - } - }).to_tokens(tokens); -} - - -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 names = import.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 import.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); - }); + 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 { - 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 import.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( + convert_ret = my_quote! { 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! { () }; } } - 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 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); - 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 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 name = self.function.name; + let import_name = syn::Ident::from(import_name); + 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)* + #vis extern #fn_token #name(#me #(#arguments),*) #ret { + ::wasm_bindgen::__rt::link_this_library(); + extern { + fn #import_name(#(#abi_arguments),*) -> #abi_ret; + } + unsafe { + #(#arg_conversions)* + let #ret_ident = #import_name(#(#abi_argument_names),*); + #exceptional_ret + #convert_ret + } } }; - } - let name = import.function.name; - let import_name = syn::Ident::from(import_name); - let attrs = &import.function.rust_attrs; - - let arguments = import.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)* - #vis extern #fn_token #name(#me #(#arguments),*) #ret { - ::wasm_bindgen::__rt::link_this_library(); - 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); } - }; - - if let Some(class) = class_ty { - (quote! { - impl #class { - #invocation - } - }).to_tokens(tokens); - } else { - invocation.to_tokens(tokens); } } -fn bindgen_enum(e: &ast::Enum, into: &mut Tokens) { - let enum_name = &e.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 = e.variants.iter().map(|variant| { - let &(variant_name, _) = variant; - 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 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; + quote! { + if #incoming_u32 == #enum_name::#variant_name as u32 { + #enum_name::#variant_name } } - } - - impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { - type Js = u32; - const DESCRIPTOR: u32 = #c; - - fn into_js(self) -> u32 { - self as u32 + }); + (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)); + } + } } - unsafe fn from_js(js: u32) -> Self { - #enum_name::from_u32(js) + 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); + }).to_tokens(into); + } }