wasm-bindgen-backend-0.2.58/Cargo.toml.orig010066400017500001750000000012521360514047700166670ustar0000000000000000[package] name = "wasm-bindgen-backend" version = "0.2.58" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend" homepage = "https://rustwasm.github.io/wasm-bindgen/" documentation = "https://docs.rs/wasm-bindgen-backend" description = """ Backend code generation of the wasm-bindgen tool """ edition = "2018" [features] spans = [] extra-traits = ["syn/extra-traits"] [dependencies] bumpalo = "3.0.0" lazy_static = "1.0.0" log = "0.4" proc-macro2 = "1.0" quote = '1.0' syn = { version = '1.0', features = ['full'] } wasm-bindgen-shared = { path = "../shared", version = "=0.2.58" } wasm-bindgen-backend-0.2.58/Cargo.toml0000644000000024311360516023000131600ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "wasm-bindgen-backend" version = "0.2.58" authors = ["The wasm-bindgen Developers"] description = "Backend code generation of the wasm-bindgen tool\n" homepage = "https://rustwasm.github.io/wasm-bindgen/" documentation = "https://docs.rs/wasm-bindgen-backend" license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend" [dependencies.bumpalo] version = "3.0.0" [dependencies.lazy_static] version = "1.0.0" [dependencies.log] version = "0.4" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" features = ["full"] [dependencies.wasm-bindgen-shared] version = "=0.2.58" [features] extra-traits = ["syn/extra-traits"] spans = [] wasm-bindgen-backend-0.2.58/src/ast.rs010066400017500001750000000246361357151432700157400ustar0000000000000000use crate::Diagnostic; use proc_macro2::{Ident, Span}; use std::hash::{Hash, Hasher}; use syn; use wasm_bindgen_shared as shared; /// An abstract syntax tree representing a rust program. Contains /// extra information for joining up this rust code with javascript. #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Default, Clone)] pub struct Program { /// rust -> js interfaces pub exports: Vec, /// js -> rust interfaces pub imports: Vec, /// rust enums pub enums: Vec, /// rust structs pub structs: Vec, /// rust consts pub consts: Vec, /// "dictionaries", generated for WebIDL, which are basically just "typed /// objects" in the sense that they represent a JS object with a particular /// shape in JIT parlance. pub dictionaries: Vec, /// custom typescript sections to be included in the definition file pub typescript_custom_sections: Vec, /// Inline JS snippets pub inline_js: Vec, } /// A rust to js interface. Allows interaction with rust objects/functions /// from javascript. #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Export { /// Comments extracted from the rust source. pub comments: Vec, /// The rust function pub function: Function, /// The class name in JS this is attached to pub js_class: Option, /// The kind (static, named, regular) pub method_kind: MethodKind, /// The type of `self` (either `self`, `&self`, or `&mut self`) pub method_self: Option, /// The struct name, in Rust, this is attached to pub rust_class: Option, /// The name of the rust function/method on the rust side. pub rust_name: Ident, /// Whether or not this function should be flagged as the wasm start /// function. pub start: bool, } /// The 3 types variations of `self`. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum MethodSelf { /// `self` ByValue, /// `&mut self` RefMutable, /// `&self` RefShared, } #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Import { pub module: ImportModule, pub js_namespace: Option, pub kind: ImportKind, } #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub enum ImportModule { None, Named(String, Span), RawNamed(String, Span), Inline(usize, Span), } impl Hash for ImportModule { fn hash(&self, h: &mut H) { match self { ImportModule::None => { 0u8.hash(h); } ImportModule::Named(name, _) => { 1u8.hash(h); name.hash(h); } ImportModule::Inline(idx, _) => { 2u8.hash(h); idx.hash(h); } ImportModule::RawNamed(name, _) => { 3u8.hash(h); name.hash(h); } } } } #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub enum ImportKind { Function(ImportFunction), Static(ImportStatic), Type(ImportType), Enum(ImportEnum), } #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct ImportFunction { pub function: Function, pub rust_name: Ident, pub js_ret: Option, pub catch: bool, pub variadic: bool, pub structural: bool, pub assert_no_shim: bool, pub kind: ImportFunctionKind, pub shim: Ident, pub doc_comment: Option, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum ImportFunctionKind { Method { class: String, ty: syn::Type, kind: MethodKind, }, Normal, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum MethodKind { Constructor, Operation(Operation), } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Operation { pub is_static: bool, pub kind: OperationKind, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub enum OperationKind { Regular, Getter(Option), Setter(Option), IndexingGetter, IndexingSetter, IndexingDeleter, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportStatic { pub vis: syn::Visibility, pub ty: syn::Type, pub shim: Ident, pub rust_name: Ident, pub js_name: String, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportType { pub vis: syn::Visibility, pub rust_name: Ident, pub js_name: String, pub attrs: Vec, pub doc_comment: Option, pub instanceof_shim: String, pub is_type_of: Option, pub extends: Vec, pub vendor_prefixes: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct ImportEnum { /// The Rust enum's visibility pub vis: syn::Visibility, /// The Rust enum's identifiers pub name: Ident, /// The Rust identifiers for the variants pub variants: Vec, /// The JS string values of the variants pub variant_values: Vec, /// Attributes to apply to the Rust enum pub rust_attrs: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug))] #[derive(Clone)] pub struct Function { pub name: String, pub name_span: Span, pub renamed_via_js_name: bool, pub arguments: Vec, pub ret: Option, pub rust_attrs: Vec, pub rust_vis: syn::Visibility, pub r#async: bool, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Struct { pub rust_name: Ident, pub js_name: String, pub fields: Vec, pub comments: Vec, pub is_inspectable: bool, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct StructField { pub name: syn::Member, pub struct_name: Ident, pub readonly: bool, pub ty: syn::Type, pub getter: Ident, pub setter: Ident, pub comments: Vec, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Enum { pub name: Ident, pub variants: Vec, pub comments: Vec, pub hole: u32, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Variant { pub name: Ident, pub value: u32, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypeKind { ByRef, ByMutRef, ByValue, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypeLocation { ImportArgument, ImportRet, ExportArgument, ExportRet, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] #[derive(Clone)] pub struct Const { pub vis: syn::Visibility, pub name: Ident, pub class: Option, pub ty: syn::Type, pub value: ConstValue, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] #[derive(Clone)] /// same as webidl::ast::ConstValue pub enum ConstValue { BooleanLiteral(bool), FloatLiteral(f64), SignedIntegerLiteral(i64), UnsignedIntegerLiteral(u64), Null, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct Dictionary { pub name: Ident, pub fields: Vec, pub ctor: bool, pub doc_comment: Option, pub ctor_doc_comment: Option, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[derive(Clone)] pub struct DictionaryField { pub rust_name: Ident, pub js_name: String, pub required: bool, pub ty: syn::Type, pub doc_comment: Option, } impl Export { /// Mangles a rust -> javascript export, so that the created Ident will be unique over function /// name and class name, if the function belongs to a javascript class. pub(crate) fn rust_symbol(&self) -> Ident { let mut generated_name = String::from("__wasm_bindgen_generated"); if let Some(class) = &self.js_class { generated_name.push_str("_"); generated_name.push_str(class); } generated_name.push_str("_"); generated_name.push_str(&self.function.name.to_string()); Ident::new(&generated_name, Span::call_site()) } /// This is the name of the shim function that gets exported and takes the raw /// ABI form of its arguments and converts them back into their normal, /// "high level" form before calling the actual function. pub(crate) fn export_name(&self) -> String { let fn_name = self.function.name.to_string(); match &self.js_class { Some(class) => shared::struct_function_export_name(class, &fn_name), None => shared::free_function_export_name(&fn_name), } } } impl ImportKind { /// Whether this type can be inside an `impl` block. pub fn fits_on_impl(&self) -> bool { match *self { ImportKind::Function(_) => true, ImportKind::Static(_) => false, ImportKind::Type(_) => false, ImportKind::Enum(_) => false, } } } impl Function { /// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in /// javascript (in this case `xxx`, so you can write `val = obj.xxx`) pub fn infer_getter_property(&self) -> &str { &self.name } /// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`) pub fn infer_setter_property(&self) -> Result { let name = self.name.to_string(); // if `#[wasm_bindgen(js_name = "...")]` is used then that explicitly // because it was hand-written anyway. if self.renamed_via_js_name { return Ok(name); } // Otherwise we infer names based on the Rust function name. if !name.starts_with("set_") { bail_span!( syn::token::Pub(self.name_span), "setters must start with `set_`, found: {}", name, ); } Ok(name[4..].to_string()) } } wasm-bindgen-backend-0.2.58/src/codegen.rs010066400017500001750000001512571360512735500165540ustar0000000000000000use crate::ast; use crate::encode; use crate::util::ShortHash; use crate::Diagnostic; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{quote, ToTokens}; use std::collections::HashSet; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; use syn; use wasm_bindgen_shared as shared; pub trait TryToTokens { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>; fn try_to_token_stream(&self) -> Result { let mut tokens = TokenStream::new(); self.try_to_tokens(&mut tokens)?; Ok(tokens) } } impl TryToTokens for ast::Program { // Generate wrappers for all the items that we've found fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut errors = Vec::new(); for export in self.exports.iter() { if let Err(e) = export.try_to_tokens(tokens) { errors.push(e); } } 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(t) = &i.kind { types.insert(t.rust_name.clone()); } } for i in self.imports.iter() { DescribeImport(&i.kind).to_tokens(tokens); // If there is a js namespace, check that name isn't a type. If it is, // this import might be a method on that type. if let Some(ns) = &i.js_namespace { if types.contains(ns) && i.kind.fits_on_impl() { let kind = match i.kind.try_to_token_stream() { Ok(kind) => kind, Err(e) => { errors.push(e); continue; } }; (quote! { impl #ns { #kind } }).to_tokens(tokens); continue; } } if let Err(e) = i.kind.try_to_tokens(tokens) { errors.push(e); } } for e in self.enums.iter() { e.to_tokens(tokens); } for c in self.consts.iter() { c.to_tokens(tokens); } for d in self.dictionaries.iter() { d.to_tokens(tokens); } Diagnostic::from_vec(errors)?; // 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 = AtomicUsize::new(0); let generated_static_name = format!( "__WASM_BINDGEN_GENERATED_{}", ShortHash(CNT.fetch_add(1, Ordering::SeqCst)), ); let generated_static_name = Ident::new(&generated_static_name, Span::call_site()); // See comments in `crates/cli-support/src/lib.rs` about what this // `schema_version` is. let prefix_json = format!( r#"{{"schema_version":"{}","version":"{}"}}"#, shared::SCHEMA_VERSION, shared::version() ); let encoded = encode::encode(self)?; let mut bytes = Vec::new(); bytes.push((prefix_json.len() >> 0) as u8); bytes.push((prefix_json.len() >> 8) as u8); bytes.push((prefix_json.len() >> 16) as u8); bytes.push((prefix_json.len() >> 24) as u8); bytes.extend_from_slice(prefix_json.as_bytes()); bytes.extend_from_slice(&encoded.custom_section); let generated_static_length = bytes.len(); let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site()); // We already consumed the contents of included files when generating // the custom section, but we want to make sure that updates to the // generated files will cause this macro to rerun incrementally. To do // that we use `include_str!` to force rustc to think it has a // dependency on these files. That way when the file changes Cargo will // automatically rerun rustc which will rerun this macro. Other than // this we don't actually need the results of the `include_str!`, so // it's just shoved into an anonymous static. let file_dependencies = encoded.included_files.iter().map(|file| { let file = file.to_str().unwrap(); quote! { include_str!(#file) } }); (quote! { #[allow(non_upper_case_globals)] #[cfg(target_arch = "wasm32")] #[link_section = "__wasm_bindgen_unstable"] #[doc(hidden)] #[allow(clippy::all)] pub static #generated_static_name: [u8; #generated_static_length] = { static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*]; *#generated_static_value }; }) .to_tokens(tokens); Ok(()) } } impl ToTokens for ast::Struct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.rust_name; let name_str = self.js_name.to_string(); let name_len = name_str.len() as u32; let name_chars = name_str.chars().map(|c| c as u32); let new_fn = Ident::new(&shared::new_function(&name_str), Span::call_site()); let free_fn = Ident::new(&shared::free_function(&name_str), Span::call_site()); (quote! { #[allow(clippy::all)] impl wasm_bindgen::describe::WasmDescribe for #name { fn describe() { use wasm_bindgen::__wbindgen_if_not_std; __wbindgen_if_not_std! { compile_error! { "exporting a class to JS requires the `std` feature to \ be enabled in the `wasm-bindgen` crate" } } use wasm_bindgen::describe::*; inform(RUST_STRUCT); inform(#name_len); #(inform(#name_chars);)* } } #[allow(clippy::all)] impl wasm_bindgen::convert::IntoWasmAbi for #name { type Abi = u32; fn into_abi(self) -> u32 { use wasm_bindgen::__rt::std::boxed::Box; use wasm_bindgen::__rt::WasmRefCell; Box::into_raw(Box::new(WasmRefCell::new(self))) as u32 } } #[allow(clippy::all)] impl wasm_bindgen::convert::FromWasmAbi for #name { type Abi = u32; unsafe fn from_abi(js: u32) -> Self { use wasm_bindgen::__rt::std::boxed::Box; use wasm_bindgen::__rt::{assert_not_null, WasmRefCell}; let ptr = js as *mut WasmRefCell<#name>; assert_not_null(ptr); let js = Box::from_raw(ptr); (*js).borrow_mut(); // make sure no one's borrowing js.into_inner() } } #[allow(clippy::all)] impl wasm_bindgen::__rt::core::convert::From<#name> for wasm_bindgen::JsValue { fn from(value: #name) -> Self { let ptr = wasm_bindgen::convert::IntoWasmAbi::into_abi(value); #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] extern "C" { fn #new_fn(ptr: u32) -> u32; } #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn #new_fn(_: u32) -> u32 { panic!("cannot convert to JsValue outside of the wasm target") } unsafe { ::from_abi(#new_fn(ptr)) } } } #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] #[no_mangle] #[doc(hidden)] #[allow(clippy::all)] pub unsafe extern "C" fn #free_fn(ptr: u32) { <#name as wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr); } #[allow(clippy::all)] impl wasm_bindgen::convert::RefFromWasmAbi for #name { type Abi = u32; type Anchor = wasm_bindgen::__rt::Ref<'static, #name>; unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { let js = js as *mut wasm_bindgen::__rt::WasmRefCell<#name>; wasm_bindgen::__rt::assert_not_null(js); (*js).borrow() } } #[allow(clippy::all)] impl wasm_bindgen::convert::RefMutFromWasmAbi for #name { type Abi = u32; type Anchor = wasm_bindgen::__rt::RefMut<'static, #name>; unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor { let js = js as *mut wasm_bindgen::__rt::WasmRefCell<#name>; wasm_bindgen::__rt::assert_not_null(js); (*js).borrow_mut() } } impl wasm_bindgen::convert::OptionIntoWasmAbi for #name { #[inline] fn none() -> Self::Abi { 0 } } impl wasm_bindgen::convert::OptionFromWasmAbi for #name { #[inline] fn is_none(abi: &Self::Abi) -> bool { *abi == 0 } } }) .to_tokens(tokens); for field in self.fields.iter() { field.to_tokens(tokens); } } } impl ToTokens for ast::StructField { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.name; let struct_name = &self.struct_name; let ty = &self.ty; let getter = &self.getter; let setter = &self.setter; let assert_copy = quote! { assert_copy::<#ty>() }; let assert_copy = respan(assert_copy, ty); (quote! { #[doc(hidden)] #[allow(clippy::all)] #[cfg_attr(all(target_arch = "wasm32", not(target_os = "emscripten")), no_mangle)] pub unsafe extern "C" fn #getter(js: u32) -> <#ty as wasm_bindgen::convert::IntoWasmAbi>::Abi { use wasm_bindgen::__rt::{WasmRefCell, assert_not_null}; use wasm_bindgen::convert::IntoWasmAbi; fn assert_copy(){} #assert_copy; let js = js as *mut WasmRefCell<#struct_name>; assert_not_null(js); let val = (*js).borrow().#name; <#ty as IntoWasmAbi>::into_abi(val) } }) .to_tokens(tokens); Descriptor( &getter, quote! { <#ty as WasmDescribe>::describe(); }, ) .to_tokens(tokens); if self.readonly { return; } (quote! { #[no_mangle] #[doc(hidden)] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] #[allow(clippy::all)] pub unsafe extern "C" fn #setter( js: u32, val: <#ty as wasm_bindgen::convert::FromWasmAbi>::Abi, ) { use wasm_bindgen::__rt::{WasmRefCell, assert_not_null}; use wasm_bindgen::convert::FromWasmAbi; let js = js as *mut WasmRefCell<#struct_name>; assert_not_null(js); let val = <#ty as FromWasmAbi>::from_abi(val); (*js).borrow_mut().#name = val; } }) .to_tokens(tokens); } } impl TryToTokens for ast::Export { fn try_to_tokens(self: &ast::Export, into: &mut TokenStream) -> Result<(), Diagnostic> { 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 = Ident::new("_ret", Span::call_site()); let offset = if self.method_self.is_some() { args.push(quote! { me: u32 }); 1 } else { 0 }; let name = &self.rust_name; let receiver = match self.method_self { Some(ast::MethodSelf::ByValue) => { let class = self.rust_class.as_ref().unwrap(); arg_conversions.push(quote! { let me = unsafe { <#class as wasm_bindgen::convert::FromWasmAbi>::from_abi(me) }; }); quote! { me.#name } } Some(ast::MethodSelf::RefMutable) => { let class = self.rust_class.as_ref().unwrap(); arg_conversions.push(quote! { let mut me = unsafe { <#class as wasm_bindgen::convert::RefMutFromWasmAbi> ::ref_mut_from_abi(me) }; let me = &mut *me; }); quote! { me.#name } } Some(ast::MethodSelf::RefShared) => { let class = self.rust_class.as_ref().unwrap(); arg_conversions.push(quote! { let me = unsafe { <#class as wasm_bindgen::convert::RefFromWasmAbi> ::ref_from_abi(me) }; let me = &*me; }); quote! { me.#name } } None => match &self.rust_class { Some(class) => quote! { #class::#name }, None => quote! { #name }, }, }; let mut argtys = Vec::new(); for (i, arg) in self.function.arguments.iter().enumerate() { argtys.push(&arg.ty); let i = i + offset; let ident = Ident::new(&format!("arg{}", i), Span::call_site()); let ty = &arg.ty; match &*arg.ty { syn::Type::Reference(syn::TypeReference { mutability: Some(_), elem, .. }) => { args.push(quote! { #ident: <#elem as wasm_bindgen::convert::RefMutFromWasmAbi>::Abi }); arg_conversions.push(quote! { let mut #ident = unsafe { <#elem as wasm_bindgen::convert::RefMutFromWasmAbi> ::ref_mut_from_abi(#ident) }; let #ident = &mut *#ident; }); } syn::Type::Reference(syn::TypeReference { elem, .. }) => { args.push(quote! { #ident: <#elem as wasm_bindgen::convert::RefFromWasmAbi>::Abi }); arg_conversions.push(quote! { let #ident = unsafe { <#elem as wasm_bindgen::convert::RefFromWasmAbi> ::ref_from_abi(#ident) }; let #ident = &*#ident; }); } _ => { args.push(quote! { #ident: <#ty as wasm_bindgen::convert::FromWasmAbi>::Abi }); arg_conversions.push(quote! { let #ident = unsafe { <#ty as wasm_bindgen::convert::FromWasmAbi> ::from_abi(#ident) }; }); } } converted_arguments.push(quote! { #ident }); } let syn_unit = syn::Type::Tuple(syn::TypeTuple { elems: Default::default(), paren_token: Default::default(), }); let syn_ret = self.function.ret.as_ref().unwrap_or(&syn_unit); if let syn::Type::Reference(_) = syn_ret { bail_span!(syn_ret, "cannot return a borrowed ref with #[wasm_bindgen]",) } // For an `async` function we always run it through `future_to_promise` // since we're returning a promise to JS, and this will implicitly // require that the function returns a `Future>` let (ret_ty, ret_expr) = if self.function.r#async { if self.start { ( quote! { () }, quote! { wasm_bindgen_futures::spawn_local(async move { <#syn_ret as wasm_bindgen::__rt::Start>::start(#ret.await); }) }, ) } else { ( quote! { wasm_bindgen::JsValue }, quote! { wasm_bindgen_futures::future_to_promise(async move { <#syn_ret as wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await) }).into() }, ) } } else if self.start { ( quote! { () }, quote! { <#syn_ret as wasm_bindgen::__rt::Start>::start(#ret) }, ) } else { (quote! { #syn_ret }, quote! { #ret }) }; let projection = quote! { <#ret_ty as wasm_bindgen::convert::ReturnWasmAbi> }; let convert_ret = quote! { #projection::return_abi(#ret_expr) }; let describe_ret = quote! { <#ret_ty as WasmDescribe>::describe(); }; let nargs = self.function.arguments.len() as u32; let attrs = &self.function.rust_attrs; let start_check = if self.start { quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; } } else { quote! {} }; (quote! { #(#attrs)* #[allow(non_snake_case)] #[cfg_attr( all(target_arch = "wasm32", not(target_os = "emscripten")), export_name = #export_name, )] #[allow(clippy::all)] pub extern "C" fn #generated_name(#(#args),*) -> #projection::Abi { #start_check // Scope all local variables to be destroyed after we call the // function to ensure that `#convert_ret`, if it panics, doesn't // leak anything. let #ret = { #(#arg_conversions)* #receiver(#(#converted_arguments),*) }; #convert_ret } }) .to_tokens(into); // In addition to generating the shim function above which is what // our generated JS will invoke, we *also* generate a "descriptor" // shim. This descriptor shim uses the `WasmDescribe` trait to // programmatically describe the type signature of the generated // shim above. This in turn is then used to inform the // `wasm-bindgen` CLI tool exactly what types and such it should be // using in JS. // // Note that this descriptor function is a purely an internal detail // of `#[wasm_bindgen]` and isn't intended to be exported to anyone // or actually part of the final was binary. Additionally, this is // literally executed when the `wasm-bindgen` tool executes. // // In any case, there's complications in `wasm-bindgen` to handle // this, but the tl;dr; is that this is stripped from the final wasm // binary along with anything it references. let export = Ident::new(&export_name, Span::call_site()); Descriptor( &export, quote! { inform(FUNCTION); inform(0); inform(#nargs); #(<#argtys as WasmDescribe>::describe();)* #describe_ret }, ) .to_tokens(into); Ok(()) } } impl TryToTokens for ast::ImportKind { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { match *self { ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?, ast::ImportKind::Static(ref s) => s.to_tokens(tokens), ast::ImportKind::Type(ref t) => t.to_tokens(tokens), ast::ImportKind::Enum(ref e) => e.to_tokens(tokens), } Ok(()) } } impl ToTokens for ast::ImportType { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; let rust_name = &self.rust_name; let attrs = &self.attrs; let doc_comment = match &self.doc_comment { None => "", Some(comment) => comment, }; let const_name = format!("__wbg_generated_const_{}", rust_name); let const_name = Ident::new(&const_name, Span::call_site()); let instanceof_shim = Ident::new(&self.instanceof_shim, Span::call_site()); let internal_obj = match self.extends.first() { Some(target) => { quote! { #target } } None => { quote! { wasm_bindgen::JsValue } } }; let is_type_of = self.is_type_of.as_ref().map(|is_type_of| { quote! { #[inline] fn is_type_of(val: &JsValue) -> bool { let is_type_of: fn(&JsValue) -> bool = #is_type_of; is_type_of(val) } } }); (quote! { #[allow(bad_style)] #(#attrs)* #[doc = #doc_comment] #[repr(transparent)] #[allow(clippy::all)] #vis struct #rust_name { obj: #internal_obj } #[allow(bad_style)] #[allow(clippy::all)] const #const_name: () = { use wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi}; use wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi}; use wasm_bindgen::convert::RefFromWasmAbi; use wasm_bindgen::describe::WasmDescribe; use wasm_bindgen::{JsValue, JsCast}; use wasm_bindgen::__rt::core; impl WasmDescribe for #rust_name { fn describe() { JsValue::describe(); } } impl core::ops::Deref for #rust_name { type Target = #internal_obj; #[inline] fn deref(&self) -> &#internal_obj { &self.obj } } impl IntoWasmAbi for #rust_name { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { self.obj.into_abi() } } impl OptionIntoWasmAbi for #rust_name { #[inline] fn none() -> Self::Abi { 0 } } impl<'a> OptionIntoWasmAbi for &'a #rust_name { #[inline] fn none() -> Self::Abi { 0 } } impl FromWasmAbi for #rust_name { type Abi = ::Abi; #[inline] unsafe fn from_abi(js: Self::Abi) -> Self { #rust_name { obj: JsValue::from_abi(js).into(), } } } impl OptionFromWasmAbi for #rust_name { #[inline] fn is_none(abi: &Self::Abi) -> bool { *abi == 0 } } impl<'a> IntoWasmAbi for &'a #rust_name { type Abi = <&'a JsValue as IntoWasmAbi>::Abi; #[inline] fn into_abi(self) -> Self::Abi { (&self.obj).into_abi() } } impl RefFromWasmAbi for #rust_name { type Abi = ::Abi; type Anchor = core::mem::ManuallyDrop<#rust_name>; #[inline] unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { let tmp = ::ref_from_abi(js); core::mem::ManuallyDrop::new(#rust_name { obj: core::mem::ManuallyDrop::into_inner(tmp).into(), }) } } // TODO: remove this on the next major version impl From for #rust_name { #[inline] fn from(obj: JsValue) -> #rust_name { #rust_name { obj: obj.into() } } } impl AsRef for #rust_name { #[inline] fn as_ref(&self) -> &JsValue { self.obj.as_ref() } } impl AsRef<#rust_name> for #rust_name { #[inline] fn as_ref(&self) -> &#rust_name { self } } impl From<#rust_name> for JsValue { #[inline] fn from(obj: #rust_name) -> JsValue { obj.obj.into() } } impl JsCast for #rust_name { fn instanceof(val: &JsValue) -> bool { #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] extern "C" { fn #instanceof_shim(val: u32) -> u32; } #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn #instanceof_shim(_: u32) -> u32 { panic!("cannot check instanceof on non-wasm targets"); } unsafe { let idx = val.into_abi(); #instanceof_shim(idx) != 0 } } #is_type_of #[inline] fn unchecked_from_js(val: JsValue) -> Self { #rust_name { obj: val.into() } } #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { // Should be safe because `#rust_name` is a transparent // wrapper around `val` unsafe { &*(val as *const JsValue as *const #rust_name) } } } () }; }) .to_tokens(tokens); for superclass in self.extends.iter() { (quote! { #[allow(clippy::all)] impl From<#rust_name> for #superclass { #[inline] fn from(obj: #rust_name) -> #superclass { use wasm_bindgen::JsCast; #superclass::unchecked_from_js(obj.into()) } } #[allow(clippy::all)] impl AsRef<#superclass> for #rust_name { #[inline] fn as_ref(&self) -> &#superclass { use wasm_bindgen::JsCast; #superclass::unchecked_from_js_ref(self.as_ref()) } } }) .to_tokens(tokens); } } } impl ToTokens for ast::ImportEnum { fn to_tokens(&self, tokens: &mut TokenStream) { let vis = &self.vis; let name = &self.name; let expect_string = format!("attempted to convert invalid {} into JSValue", name); let variants = &self.variants; let variant_strings = &self.variant_values; let attrs = &self.rust_attrs; let mut current_idx: usize = 0; let variant_indexes: Vec = variants .iter() .map(|_| { let this_index = current_idx; current_idx += 1; Literal::usize_unsuffixed(this_index) }) .collect(); // Borrow variant_indexes because we need to use it multiple times inside the quote! macro let variant_indexes_ref = &variant_indexes; // A vector of EnumName::VariantName tokens for this enum let variant_paths: Vec = self .variants .iter() .map(|v| quote!(#name::#v).into_token_stream()) .collect(); // Borrow variant_paths because we need to use it multiple times inside the quote! macro let variant_paths_ref = &variant_paths; (quote! { #[allow(bad_style)] #(#attrs)* #[allow(clippy::all)] #vis enum #name { #(#variants = #variant_indexes_ref,)* #[doc(hidden)] __Nonexhaustive, } #[allow(clippy::all)] impl #name { #vis fn from_js_value(obj: &wasm_bindgen::JsValue) -> Option<#name> { obj.as_string().and_then(|obj_str| match obj_str.as_str() { #(#variant_strings => Some(#variant_paths_ref),)* _ => None, }) } } #[allow(clippy::all)] impl wasm_bindgen::describe::WasmDescribe for #name { fn describe() { wasm_bindgen::JsValue::describe() } } #[allow(clippy::all)] impl wasm_bindgen::convert::IntoWasmAbi for #name { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { wasm_bindgen::JsValue::from(self).into_abi() } } #[allow(clippy::all)] impl wasm_bindgen::convert::FromWasmAbi for #name { type Abi = ::Abi; unsafe fn from_abi(js: Self::Abi) -> Self { #name::from_js_value(&wasm_bindgen::JsValue::from_abi(js)).unwrap_or(#name::__Nonexhaustive) } } #[allow(clippy::all)] impl wasm_bindgen::convert::OptionIntoWasmAbi for #name { #[inline] fn none() -> Self::Abi { Object::none() } } #[allow(clippy::all)] impl wasm_bindgen::convert::OptionFromWasmAbi for #name { #[inline] fn is_none(abi: &Self::Abi) -> bool { Object::is_none(abi) } } #[allow(clippy::all)] impl From<#name> for wasm_bindgen::JsValue { fn from(obj: #name) -> wasm_bindgen::JsValue { match obj { #(#variant_paths_ref => wasm_bindgen::JsValue::from_str(#variant_strings),)* #name::__Nonexhaustive => panic!(#expect_string), } } } }).to_tokens(tokens); } } impl TryToTokens for ast::ImportFunction { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { let mut class_ty = None; let mut is_method = false; match self.kind { ast::ImportFunctionKind::Method { ref ty, ref kind, .. } => { if let ast::MethodKind::Operation(ast::Operation { is_static: false, .. }) = kind { is_method = true; } class_ty = Some(ty); } ast::ImportFunctionKind::Normal => {} } let vis = &self.function.rust_vis; let ret = match &self.function.ret { Some(ty) => quote! { -> #ty }, None => quote!(), }; let mut abi_argument_names = Vec::new(); let mut abi_arguments = Vec::new(); let mut arg_conversions = Vec::new(); let mut arguments = Vec::new(); let ret_ident = Ident::new("_ret", Span::call_site()); for (i, arg) in self.function.arguments.iter().enumerate() { let ty = &arg.ty; let name = match &*arg.pat { syn::Pat::Ident(syn::PatIdent { by_ref: None, ident, subpat: None, .. }) => ident.clone(), syn::Pat::Wild(_) => syn::Ident::new(&format!("__genarg_{}", i), Span::call_site()), _ => bail_span!( arg.pat, "unsupported pattern in #[wasm_bindgen] imported function", ), }; abi_argument_names.push(name.clone()); abi_arguments.push(quote! { #name: <#ty as wasm_bindgen::convert::IntoWasmAbi>::Abi }); let var = if i == 0 && is_method { quote! { self } } else { arguments.push(quote! { #name: #ty }); quote! { #name } }; arg_conversions.push(quote! { let #name = <#ty as wasm_bindgen::convert::IntoWasmAbi> ::into_abi(#var); }); } let abi_ret; let mut convert_ret; match &self.js_ret { Some(syn::Type::Reference(_)) => { bail_span!( self.js_ret, "cannot return references in #[wasm_bindgen] imports yet" ); } Some(ref ty) => { abi_ret = quote! { <#ty as wasm_bindgen::convert::FromWasmAbi>::Abi }; convert_ret = quote! { <#ty as wasm_bindgen::convert::FromWasmAbi> ::from_abi(#ret_ident) }; } None => { abi_ret = quote! { () }; convert_ret = quote! { () }; } } let mut exceptional_ret = quote!(); if self.catch { convert_ret = quote! { Ok(#convert_ret) }; exceptional_ret = quote! { wasm_bindgen::__rt::take_last_exception()?; }; } let rust_name = &self.rust_name; let import_name = &self.shim; let attrs = &self.function.rust_attrs; let arguments = &arguments; let abi_arguments = &abi_arguments; let abi_argument_names = &abi_argument_names; let doc_comment = match &self.doc_comment { None => "", Some(doc_string) => doc_string, }; let me = if is_method { quote! { &self, } } else { quote!() }; // Route any errors pointing to this imported function to the identifier // of the function we're imported from so we at least know what function // is causing issues. // // Note that this is where type errors like "doesn't implement // FromWasmAbi" or "doesn't implement IntoWasmAbi" currently get routed. // I suspect that's because they show up in the signature via trait // projections as types of arguments, and all that needs to typecheck // before the body can be typechecked. Due to rust-lang/rust#60980 (and // probably related issues) we can't really get a precise span. // // Ideally what we want is to point errors for particular types back to // the specific argument/type that generated the error, but it looks // like rustc itself doesn't do great in that regard so let's just do // the best we can in the meantime. let extern_fn = respan( quote! { #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] extern "C" { fn #import_name(#(#abi_arguments),*) -> #abi_ret; } #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret { #( drop(#abi_argument_names); )* panic!("cannot call wasm-bindgen imported functions on \ non-wasm targets"); } }, &self.rust_name, ); let invocation = quote! { #(#attrs)* #[allow(bad_style)] #[doc = #doc_comment] #[allow(clippy::all)] #vis fn #rust_name(#me #(#arguments),*) #ret { #extern_fn unsafe { let #ret_ident = { #(#arg_conversions)* #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); } Ok(()) } } // See comment above in ast::Export for what's going on here. struct DescribeImport<'a>(&'a ast::ImportKind); impl<'a> ToTokens for DescribeImport<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { let f = match *self.0 { ast::ImportKind::Function(ref f) => f, ast::ImportKind::Static(_) => return, ast::ImportKind::Type(_) => return, ast::ImportKind::Enum(_) => return, }; let argtys = f.function.arguments.iter().map(|arg| &arg.ty); let nargs = f.function.arguments.len() as u32; let inform_ret = match &f.js_ret { Some(ref t) => quote! { <#t as WasmDescribe>::describe(); }, None => quote! { <() as WasmDescribe>::describe(); }, }; Descriptor( &f.shim, quote! { inform(FUNCTION); inform(0); inform(#nargs); #(<#argtys as WasmDescribe>::describe();)* #inform_ret }, ) .to_tokens(tokens); } } impl ToTokens for ast::Enum { fn to_tokens(&self, into: &mut TokenStream) { let enum_name = &self.name; let hole = &self.hole; let cast_clauses = self.variants.iter().map(|variant| { let variant_name = &variant.name; quote! { if js == #enum_name::#variant_name as u32 { #enum_name::#variant_name } } }); (quote! { #[allow(clippy::all)] impl wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = u32; #[inline] fn into_abi(self) -> u32 { self as u32 } } #[allow(clippy::all)] impl wasm_bindgen::convert::FromWasmAbi for #enum_name { type Abi = u32; #[inline] unsafe fn from_abi(js: u32) -> Self { #(#cast_clauses else)* { wasm_bindgen::throw_str("invalid enum value passed") } } } #[allow(clippy::all)] impl wasm_bindgen::convert::OptionFromWasmAbi for #enum_name { #[inline] fn is_none(val: &u32) -> bool { *val == #hole } } #[allow(clippy::all)] impl wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name { #[inline] fn none() -> Self::Abi { #hole } } #[allow(clippy::all)] impl wasm_bindgen::describe::WasmDescribe for #enum_name { fn describe() { use wasm_bindgen::describe::*; inform(ENUM); inform(#hole); } } }) .to_tokens(into); } } impl ToTokens for ast::ImportStatic { fn to_tokens(&self, into: &mut TokenStream) { let name = &self.rust_name; let ty = &self.ty; let shim_name = &self.shim; let vis = &self.vis; (quote! { #[allow(bad_style)] #[allow(clippy::all)] #vis static #name: wasm_bindgen::JsStatic<#ty> = { fn init() -> #ty { #[link(wasm_import_module = "__wbindgen_placeholder__")] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] extern "C" { fn #shim_name() -> <#ty as wasm_bindgen::convert::FromWasmAbi>::Abi; } #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn #shim_name() -> <#ty as wasm_bindgen::convert::FromWasmAbi>::Abi { panic!("cannot access imported statics on non-wasm targets") } unsafe { <#ty as wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name()) } } thread_local!(static _VAL: #ty = init();); wasm_bindgen::JsStatic { __inner: &_VAL, } }; }) .to_tokens(into); Descriptor( &shim_name, quote! { <#ty as WasmDescribe>::describe(); }, ) .to_tokens(into); } } impl ToTokens for ast::Const { fn to_tokens(&self, tokens: &mut TokenStream) { use crate::ast::ConstValue::*; let vis = &self.vis; let name = &self.name; let ty = &self.ty; let value: TokenStream = match self.value { BooleanLiteral(false) => quote!(false), BooleanLiteral(true) => quote!(true), // the actual type is unknown because of typedefs // so we cannot use std::fxx::INFINITY // but we can use type inference FloatLiteral(f) if f.is_infinite() && f.is_sign_positive() => quote!(1.0 / 0.0), FloatLiteral(f) if f.is_infinite() && f.is_sign_negative() => quote!(-1.0 / 0.0), FloatLiteral(f) if f.is_nan() => quote!(0.0 / 0.0), // again no suffix // panics on +-inf, nan FloatLiteral(f) => { let f = Literal::f64_suffixed(f); quote!(#f) } SignedIntegerLiteral(i) => { let i = Literal::i64_suffixed(i); quote!(#i) } UnsignedIntegerLiteral(i) => { let i = Literal::u64_suffixed(i); quote!(#i) } Null => unimplemented!(), }; let declaration = quote!(#vis const #name: #ty = #value as #ty;); if let Some(class) = &self.class { (quote! { impl #class { #declaration } }) .to_tokens(tokens); } else { declaration.to_tokens(tokens); } } } impl ToTokens for ast::Dictionary { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.name; let mut methods = TokenStream::new(); for field in self.fields.iter() { field.to_tokens(&mut methods); } let required_names = &self .fields .iter() .filter(|f| f.required) .map(|f| &f.rust_name) .collect::>(); let required_types = &self .fields .iter() .filter(|f| f.required) .map(|f| &f.ty) .collect::>(); let required_names2 = required_names; let required_names3 = required_names; let doc_comment = match &self.doc_comment { None => "", Some(doc_string) => doc_string, }; let ctor = if self.ctor { let doc_comment = match &self.ctor_doc_comment { None => "", Some(doc_string) => doc_string, }; quote! { #[doc = #doc_comment] pub fn new(#(#required_names: #required_types),*) -> #name { let mut _ret = #name { obj: ::js_sys::Object::new() }; #(_ret.#required_names2(#required_names3);)* return _ret } } } else { quote! {} }; let const_name = Ident::new(&format!("_CONST_{}", name), Span::call_site()); (quote! { #[derive(Clone, Debug)] #[repr(transparent)] #[allow(clippy::all)] #[doc = #doc_comment] pub struct #name { obj: ::js_sys::Object, } #[allow(clippy::all)] impl #name { #ctor #methods } #[allow(bad_style)] #[allow(clippy::all)] const #const_name: () = { use js_sys::Object; use wasm_bindgen::describe::WasmDescribe; use wasm_bindgen::convert::*; use wasm_bindgen::{JsValue, JsCast}; use wasm_bindgen::__rt::core::mem::ManuallyDrop; // interop w/ JsValue impl From<#name> for JsValue { #[inline] fn from(val: #name) -> JsValue { val.obj.into() } } impl AsRef for #name { #[inline] fn as_ref(&self) -> &JsValue { self.obj.as_ref() } } // Boundary conversion impls impl WasmDescribe for #name { fn describe() { Object::describe(); } } impl IntoWasmAbi for #name { type Abi = ::Abi; #[inline] fn into_abi(self) -> Self::Abi { self.obj.into_abi() } } impl<'a> IntoWasmAbi for &'a #name { type Abi = <&'a Object as IntoWasmAbi>::Abi; #[inline] fn into_abi(self) -> Self::Abi { (&self.obj).into_abi() } } impl FromWasmAbi for #name { type Abi = ::Abi; #[inline] unsafe fn from_abi(abi: Self::Abi) -> Self { #name { obj: Object::from_abi(abi) } } } impl OptionIntoWasmAbi for #name { #[inline] fn none() -> Self::Abi { Object::none() } } impl<'a> OptionIntoWasmAbi for &'a #name { #[inline] fn none() -> Self::Abi { <&'a Object>::none() } } impl OptionFromWasmAbi for #name { #[inline] fn is_none(abi: &Self::Abi) -> bool { Object::is_none(abi) } } impl RefFromWasmAbi for #name { type Abi = ::Abi; type Anchor = ManuallyDrop<#name>; #[inline] unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor { let tmp = ::ref_from_abi(js); ManuallyDrop::new(#name { obj: ManuallyDrop::into_inner(tmp), }) } } impl JsCast for #name { #[inline] fn instanceof(val: &JsValue) -> bool { Object::instanceof(val) } #[inline] fn unchecked_from_js(val: JsValue) -> Self { #name { obj: Object::unchecked_from_js(val) } } #[inline] fn unchecked_from_js_ref(val: &JsValue) -> &Self { unsafe { &*(val as *const JsValue as *const #name) } } } }; }) .to_tokens(tokens); } } impl ToTokens for ast::DictionaryField { fn to_tokens(&self, tokens: &mut TokenStream) { let rust_name = &self.rust_name; let js_name = &self.js_name; let ty = &self.ty; let doc_comment = match &self.doc_comment { None => "", Some(doc_string) => doc_string, }; (quote! { #[allow(clippy::all)] #[doc = #doc_comment] pub fn #rust_name(&mut self, val: #ty) -> &mut Self { use wasm_bindgen::JsValue; let r = ::js_sys::Reflect::set( self.obj.as_ref(), &JsValue::from(#js_name), &JsValue::from(val), ); debug_assert!(r.is_ok(), "setting properties should never fail on our dictionary objects"); let _ = r; self } }).to_tokens(tokens); } } /// Emits the necessary glue tokens for "descriptor", generating an appropriate /// symbol name as well as attributes around the descriptor function itself. struct Descriptor<'a, T>(&'a Ident, T); impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> { fn to_tokens(&self, tokens: &mut TokenStream) { // It's possible for the same descriptor to be emitted in two different // modules (aka a value imported twice in a crate, each in a separate // module). In this case no need to emit duplicate descriptors (which // leads to duplicate symbol errors), instead just emit one. // // It's up to the descriptors themselves to ensure they have unique // names for unique items imported, currently done via `ShortHash` and // hashing appropriate data into the symbol name. lazy_static::lazy_static! { static ref DESCRIPTORS_EMITTED: Mutex> = Default::default(); } if !DESCRIPTORS_EMITTED .lock() .unwrap() .insert(self.0.to_string()) { return; } let name = Ident::new(&format!("__wbindgen_describe_{}", self.0), self.0.span()); let inner = &self.1; (quote! { #[no_mangle] #[allow(non_snake_case)] #[doc(hidden)] #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] #[allow(clippy::all)] pub extern "C" fn #name() { use wasm_bindgen::describe::*; // See definition of `link_mem_intrinsics` for what this is doing wasm_bindgen::__rt::link_mem_intrinsics(); #inner } }) .to_tokens(tokens); } } /// Converts `span` into a stream of tokens, and attempts to ensure that `input` /// has all the appropriate span information so errors in it point to `span`. fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream { let mut first_span = Span::call_site(); let mut last_span = Span::call_site(); let mut spans = TokenStream::new(); span.to_tokens(&mut spans); for (i, token) in spans.into_iter().enumerate() { if i == 0 { first_span = token.span(); } last_span = token.span(); } let mut new_tokens = Vec::new(); for (i, mut token) in input.into_iter().enumerate() { if i == 0 { token.set_span(first_span); } else { token.set_span(last_span); } new_tokens.push(token); } new_tokens.into_iter().collect() } wasm-bindgen-backend-0.2.58/src/defined.rs010066400017500001750000000235431352460564600165460ustar0000000000000000use crate::ast; use proc_macro2::Ident; use syn; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ImportedTypeKind { /// The definition of an imported type. Definition, /// A reference to an imported type. Reference, } impl ImportedTypes for Option where T: ImportedTypes, { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { if let Some(inner) = self { inner.imported_types(f); } } } /// Iterate over definitions of and references to imported types in the AST. pub trait ImportedTypes { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind); } /// Iterate over definitions of imported types in the AST. pub trait ImportedTypeDefinitions { fn imported_type_definitions(&self, f: &mut F) where F: FnMut(&Ident); } impl ImportedTypeDefinitions for T where T: ImportedTypes, { fn imported_type_definitions(&self, f: &mut F) where F: FnMut(&Ident), { self.imported_types(&mut |id, kind| { if let ImportedTypeKind::Definition = kind { f(id); } }); } } /// Iterate over references to imported types in the AST. pub trait ImportedTypeReferences { fn imported_type_references(&self, f: &mut F) where F: FnMut(&Ident); } impl ImportedTypeReferences for T where T: ImportedTypes, { fn imported_type_references(&self, f: &mut F) where F: FnMut(&Ident), { self.imported_types(&mut |id, kind| { if let ImportedTypeKind::Reference = kind { f(id); } }); } } impl<'a, T: ImportedTypes> ImportedTypes for &'a T { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { (*self).imported_types(f) } } impl ImportedTypes for ast::Program { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.imports.imported_types(f); self.consts.imported_types(f); self.dictionaries.imported_types(f); } } impl ImportedTypes for Vec where T: ImportedTypes, { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { for x in self { x.imported_types(f); } } } impl ImportedTypes for ast::Import { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.kind.imported_types(f) } } impl ImportedTypes for ast::ImportKind { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { ast::ImportKind::Static(s) => s.imported_types(f), ast::ImportKind::Function(fun) => fun.imported_types(f), ast::ImportKind::Type(ty) => ty.imported_types(f), ast::ImportKind::Enum(enm) => enm.imported_types(f), } } } impl ImportedTypes for ast::ImportStatic { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.ty.imported_types(f); } } impl ImportedTypes for syn::Type { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { syn::Type::Reference(ref r) => r.imported_types(f), syn::Type::Path(ref p) => p.imported_types(f), _ => {} } } } impl ImportedTypes for syn::TypeReference { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.elem.imported_types(f); } } impl ImportedTypes for syn::TypePath { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.qself.imported_types(f); self.path.imported_types(f); } } impl ImportedTypes for syn::QSelf { fn imported_types(&self, _: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { // TODO } } impl ImportedTypes for syn::Path { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { for seg in self.segments.iter() { seg.arguments.imported_types(f); } f( &self.segments.last().unwrap().ident, ImportedTypeKind::Reference, ); } } impl ImportedTypes for syn::PathArguments { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { syn::PathArguments::AngleBracketed(data) => { for arg in data.args.iter() { arg.imported_types(f); } } //TOCHECK syn::PathArguments::Parenthesized(data) => { for input in data.inputs.iter() { input.imported_types(f); } // TODO do we need to handle output here? // https://docs.rs/syn/0.14.0/syn/struct.ParenthesizedGenericArguments.html } syn::PathArguments::None => {} } } } impl ImportedTypes for syn::GenericArgument { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { syn::GenericArgument::Lifetime(_) => {} syn::GenericArgument::Type(ty) => ty.imported_types(f), syn::GenericArgument::Binding(_) => {} // TODO syn::GenericArgument::Const(_) => {} // TODO syn::GenericArgument::Constraint(_) => {} // TODO } } } impl ImportedTypes for ast::ImportFunction { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.function.imported_types(f); self.kind.imported_types(f); } } impl ImportedTypes for ast::ImportFunctionKind { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { ast::ImportFunctionKind::Method { ty, .. } => ty.imported_types(f), ast::ImportFunctionKind::Normal => {} } } } impl ImportedTypes for ast::Function { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.arguments.imported_types(f); if let Some(ref r) = self.ret { r.imported_types(f); } } } impl ImportedTypes for syn::FnArg { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { match self { syn::FnArg::Receiver(_) => {} syn::FnArg::Typed(p) => p.imported_types(f), } } } impl ImportedTypes for syn::PatType { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.ty.imported_types(f) } } impl ImportedTypes for ast::ImportType { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { f(&self.rust_name, ImportedTypeKind::Definition); for class in self.extends.iter() { class.imported_types(f); } } } impl ImportedTypes for ast::ImportEnum { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { f(&self.name, ImportedTypeKind::Definition); } } impl ImportedTypes for ast::Const { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.ty.imported_types(f); } } impl ImportedTypes for ast::Dictionary { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { f(&self.name, ImportedTypeKind::Definition); for field in self.fields.iter() { field.imported_types(f); } } } impl ImportedTypes for ast::DictionaryField { fn imported_types(&self, f: &mut F) where F: FnMut(&Ident, ImportedTypeKind), { self.ty.imported_types(f); } } /// Remove any methods, statics, &c, that reference types that are *not* /// defined. pub trait RemoveUndefinedImports { fn remove_undefined_imports(&mut self, is_defined: &F) -> bool where F: Fn(&Ident) -> bool; } impl RemoveUndefinedImports for ast::Program { fn remove_undefined_imports(&mut self, is_defined: &F) -> bool where F: Fn(&Ident) -> bool, { let mut changed = self.imports.remove_undefined_imports(is_defined); changed = self.consts.remove_undefined_imports(is_defined) || changed; for dictionary in self.dictionaries.iter_mut() { let num_required = |dict: &ast::Dictionary| dict.fields.iter().filter(|f| f.required).count(); let before = num_required(dictionary); changed = dictionary.fields.remove_undefined_imports(is_defined) || changed; // If a required field was removed we can no longer construct this // dictionary so disable the constructor. if before != num_required(dictionary) { dictionary.ctor = false; } } changed } } impl RemoveUndefinedImports for Vec where T: ImportedTypeReferences, { fn remove_undefined_imports(&mut self, is_defined: &F) -> bool where F: Fn(&Ident) -> bool, { let before = self.len(); self.retain(|x| { let mut all_defined = true; x.imported_type_references(&mut |id| { if all_defined { if !is_defined(id) { log::info!("removing due to {} not being defined", id); all_defined = false; } } }); all_defined }); before != self.len() } } wasm-bindgen-backend-0.2.58/src/encode.rs010066400017500001750000000364421360470371600164030ustar0000000000000000use crate::util::ShortHash; use proc_macro2::{Ident, Span}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::env; use std::fs; use std::path::PathBuf; use crate::ast; use crate::Diagnostic; pub struct EncodeResult { pub custom_section: Vec, pub included_files: Vec, } pub fn encode(program: &ast::Program) -> Result { let mut e = Encoder::new(); let i = Interner::new(); shared_program(program, &i)?.encode(&mut e); let custom_section = e.finish(); let included_files = i .files .borrow() .values() .map(|p| &p.path) .cloned() .collect(); Ok(EncodeResult { custom_section, included_files, }) } struct Interner { bump: bumpalo::Bump, files: RefCell>, root: PathBuf, crate_name: String, has_package_json: Cell, } struct LocalFile { path: PathBuf, definition: Span, new_identifier: String, } impl Interner { fn new() -> Interner { Interner { bump: bumpalo::Bump::new(), files: RefCell::new(HashMap::new()), root: env::var_os("CARGO_MANIFEST_DIR").unwrap().into(), crate_name: env::var("CARGO_PKG_NAME").unwrap(), has_package_json: Cell::new(false), } } fn intern(&self, s: &Ident) -> &str { self.intern_str(&s.to_string()) } fn intern_str(&self, s: &str) -> &str { // NB: eventually this could be used to intern `s` to only allocate one // copy, but for now let's just "transmute" `s` to have the same // lifetime as this struct itself (which is our main goal here) self.bump.alloc_str(s) } /// Given an import to a local module `id` this generates a unique module id /// to assign to the contents of `id`. /// /// Note that repeated invocations of this function will be memoized, so the /// same `id` will always return the same resulting unique `id`. fn resolve_import_module(&self, id: &str, span: Span) -> Result<&str, Diagnostic> { let mut files = self.files.borrow_mut(); if let Some(file) = files.get(id) { return Ok(self.intern_str(&file.new_identifier)); } self.check_for_package_json(); let path = if id.starts_with("/") { self.root.join(&id[1..]) } else if id.starts_with("./") || id.starts_with("../") { let msg = "relative module paths aren't supported yet"; return Err(Diagnostic::span_error(span, msg)); } else { return Ok(self.intern_str(&id)); }; // Generate a unique ID which is somewhat readable as well, so mix in // the crate name, hash to make it unique, and then the original path. let new_identifier = format!("{}{}", self.unique_crate_identifier(), id); let file = LocalFile { path, definition: span, new_identifier, }; files.insert(id.to_string(), file); drop(files); self.resolve_import_module(id, span) } fn unique_crate_identifier(&self) -> String { format!("{}-{}", self.crate_name, ShortHash(0)) } fn check_for_package_json(&self) { if self.has_package_json.get() { return; } let path = self.root.join("package.json"); if path.exists() { self.has_package_json.set(true); } } } fn shared_program<'a>( prog: &'a ast::Program, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(Program { exports: prog .exports .iter() .map(|a| shared_export(a, intern)) .collect::, _>>()?, structs: prog .structs .iter() .map(|a| shared_struct(a, intern)) .collect(), enums: prog.enums.iter().map(|a| shared_enum(a, intern)).collect(), imports: prog .imports .iter() .map(|a| shared_import(a, intern)) .collect::, _>>()?, typescript_custom_sections: prog .typescript_custom_sections .iter() .map(|x| -> &'a str { &x }) .collect(), local_modules: intern .files .borrow() .values() .map(|file| { fs::read_to_string(&file.path) .map(|s| LocalModule { identifier: intern.intern_str(&file.new_identifier), contents: intern.intern_str(&s), }) .map_err(|e| { let msg = format!("failed to read file `{}`: {}", file.path.display(), e); Diagnostic::span_error(file.definition, msg) }) }) .collect::, _>>()?, inline_js: prog .inline_js .iter() .map(|js| intern.intern_str(js)) .collect(), unique_crate_identifier: intern.intern_str(&intern.unique_crate_identifier()), package_json: if intern.has_package_json.get() { Some(intern.intern_str(intern.root.join("package.json").to_str().unwrap())) } else { None }, }) } fn shared_export<'a>( export: &'a ast::Export, intern: &'a Interner, ) -> Result, Diagnostic> { let consumed = match export.method_self { Some(ast::MethodSelf::ByValue) => true, _ => false, }; let method_kind = from_ast_method_kind(&export.function, intern, &export.method_kind)?; Ok(Export { class: export.js_class.as_ref().map(|s| &**s), comments: export.comments.iter().map(|s| &**s).collect(), consumed, function: shared_function(&export.function, intern), method_kind, start: export.start, }) } fn shared_function<'a>(func: &'a ast::Function, _intern: &'a Interner) -> Function<'a> { let arg_names = func .arguments .iter() .enumerate() .map(|(idx, arg)| { if let syn::Pat::Ident(x) = &*arg.pat { return x.ident.to_string(); } format!("arg{}", idx) }) .collect::>(); Function { arg_names, name: &func.name, } } fn shared_enum<'a>(e: &'a ast::Enum, intern: &'a Interner) -> Enum<'a> { Enum { name: intern.intern(&e.name), variants: e .variants .iter() .map(|v| shared_variant(v, intern)) .collect(), comments: e.comments.iter().map(|s| &**s).collect(), } } fn shared_variant<'a>(v: &'a ast::Variant, intern: &'a Interner) -> EnumVariant<'a> { EnumVariant { name: intern.intern(&v.name), value: v.value, } } fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result, Diagnostic> { Ok(Import { module: match &i.module { ast::ImportModule::Named(m, span) => { ImportModule::Named(intern.resolve_import_module(m, *span)?) } ast::ImportModule::RawNamed(m, _span) => ImportModule::RawNamed(intern.intern_str(m)), ast::ImportModule::Inline(idx, _) => ImportModule::Inline(*idx as u32), ast::ImportModule::None => ImportModule::None, }, js_namespace: i.js_namespace.as_ref().map(|s| intern.intern(s)), kind: shared_import_kind(&i.kind, intern)?, }) } fn shared_import_kind<'a>( i: &'a ast::ImportKind, intern: &'a Interner, ) -> Result, Diagnostic> { Ok(match i { ast::ImportKind::Function(f) => ImportKind::Function(shared_import_function(f, intern)?), ast::ImportKind::Static(f) => ImportKind::Static(shared_import_static(f, intern)), ast::ImportKind::Type(f) => ImportKind::Type(shared_import_type(f, intern)), ast::ImportKind::Enum(f) => ImportKind::Enum(shared_import_enum(f, intern)), }) } fn shared_import_function<'a>( i: &'a ast::ImportFunction, intern: &'a Interner, ) -> Result, Diagnostic> { let method = match &i.kind { ast::ImportFunctionKind::Method { class, kind, .. } => { let kind = from_ast_method_kind(&i.function, intern, kind)?; Some(MethodData { class, kind }) } ast::ImportFunctionKind::Normal => None, }; Ok(ImportFunction { shim: intern.intern(&i.shim), catch: i.catch, method, assert_no_shim: i.assert_no_shim, structural: i.structural, function: shared_function(&i.function, intern), variadic: i.variadic, }) } fn shared_import_static<'a>(i: &'a ast::ImportStatic, intern: &'a Interner) -> ImportStatic<'a> { ImportStatic { name: &i.js_name, shim: intern.intern(&i.shim), } } fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> ImportType<'a> { ImportType { name: &i.js_name, instanceof_shim: &i.instanceof_shim, vendor_prefixes: i.vendor_prefixes.iter().map(|x| intern.intern(x)).collect(), } } fn shared_import_enum<'a>(_i: &'a ast::ImportEnum, _intern: &'a Interner) -> ImportEnum { ImportEnum {} } fn shared_struct<'a>(s: &'a ast::Struct, intern: &'a Interner) -> Struct<'a> { Struct { name: &s.js_name, fields: s .fields .iter() .map(|s| shared_struct_field(s, intern)) .collect(), comments: s.comments.iter().map(|s| &**s).collect(), is_inspectable: s.is_inspectable, } } fn shared_struct_field<'a>(s: &'a ast::StructField, intern: &'a Interner) -> StructField<'a> { StructField { name: match &s.name { syn::Member::Named(ident) => intern.intern(ident), syn::Member::Unnamed(index) => intern.intern_str(&index.index.to_string()), }, readonly: s.readonly, comments: s.comments.iter().map(|s| &**s).collect(), } } trait Encode { fn encode(&self, dst: &mut Encoder); } struct Encoder { dst: Vec, } impl Encoder { fn new() -> Encoder { Encoder { dst: vec![0, 0, 0, 0], } } fn finish(mut self) -> Vec { let len = self.dst.len() - 4; self.dst[0] = (len >> 0) as u8; self.dst[1] = (len >> 8) as u8; self.dst[2] = (len >> 16) as u8; self.dst[3] = (len >> 24) as u8; self.dst } fn byte(&mut self, byte: u8) { self.dst.push(byte); } } impl Encode for bool { fn encode(&self, dst: &mut Encoder) { dst.byte(*self as u8); } } impl Encode for u32 { fn encode(&self, dst: &mut Encoder) { let mut val = *self; while (val >> 7) != 0 { dst.byte((val as u8) | 0x80); val >>= 7; } assert_eq!(val >> 7, 0); dst.byte(val as u8); } } impl Encode for usize { fn encode(&self, dst: &mut Encoder) { assert!(*self <= u32::max_value() as usize); (*self as u32).encode(dst); } } impl<'a> Encode for &'a [u8] { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); dst.dst.extend_from_slice(*self); } } impl<'a> Encode for &'a str { fn encode(&self, dst: &mut Encoder) { self.as_bytes().encode(dst); } } impl<'a> Encode for String { fn encode(&self, dst: &mut Encoder) { self.as_bytes().encode(dst); } } impl Encode for Vec { fn encode(&self, dst: &mut Encoder) { self.len().encode(dst); for item in self { item.encode(dst); } } } impl Encode for Option { fn encode(&self, dst: &mut Encoder) { match self { None => dst.byte(0), Some(val) => { dst.byte(1); val.encode(dst) } } } } macro_rules! encode_struct { ($name:ident ($($lt:tt)*) $($field:ident: $ty:ty,)*) => { struct $name $($lt)* { $($field: $ty,)* } impl $($lt)* Encode for $name $($lt)* { fn encode(&self, _dst: &mut Encoder) { $(self.$field.encode(_dst);)* } } } } macro_rules! encode_enum { ($name:ident ($($lt:tt)*) $($fields:tt)*) => ( enum $name $($lt)* { $($fields)* } impl$($lt)* Encode for $name $($lt)* { fn encode(&self, dst: &mut Encoder) { use self::$name::*; encode_enum!(@arms self dst (0) () $($fields)*) } } ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*)) => ( encode_enum!(@expr match $me { $($arms)* }) ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident, $($rest:tt)*) => ( encode_enum!( @arms $me $dst ($cnt+1) ($($arms)* $name => $dst.byte($cnt),) $($rest)* ) ); (@arms $me:ident $dst:ident ($cnt:expr) ($($arms:tt)*) $name:ident($t:ty), $($rest:tt)*) => ( encode_enum!( @arms $me $dst ($cnt+1) ($($arms)* $name(val) => { $dst.byte($cnt); val.encode($dst) }) $($rest)* ) ); (@expr $e:expr) => ($e); } macro_rules! encode_api { () => (); (struct $name:ident<'a> { $($fields:tt)* } $($rest:tt)*) => ( encode_struct!($name (<'a>) $($fields)*); encode_api!($($rest)*); ); (struct $name:ident { $($fields:tt)* } $($rest:tt)*) => ( encode_struct!($name () $($fields)*); encode_api!($($rest)*); ); (enum $name:ident<'a> { $($variants:tt)* } $($rest:tt)*) => ( encode_enum!($name (<'a>) $($variants)*); encode_api!($($rest)*); ); (enum $name:ident { $($variants:tt)* } $($rest:tt)*) => ( encode_enum!($name () $($variants)*); encode_api!($($rest)*); ); } wasm_bindgen_shared::shared_api!(encode_api); fn from_ast_method_kind<'a>( function: &'a ast::Function, intern: &'a Interner, method_kind: &'a ast::MethodKind, ) -> Result, Diagnostic> { Ok(match method_kind { ast::MethodKind::Constructor => MethodKind::Constructor, ast::MethodKind::Operation(ast::Operation { is_static, kind }) => { let is_static = *is_static; let kind = match kind { ast::OperationKind::Getter(g) => { let g = g.as_ref().map(|g| intern.intern(g)); OperationKind::Getter(g.unwrap_or_else(|| function.infer_getter_property())) } ast::OperationKind::Regular => OperationKind::Regular, ast::OperationKind::Setter(s) => { let s = s.as_ref().map(|s| intern.intern(s)); OperationKind::Setter(match s { Some(s) => s, None => intern.intern_str(&function.infer_setter_property()?), }) } ast::OperationKind::IndexingGetter => OperationKind::IndexingGetter, ast::OperationKind::IndexingSetter => OperationKind::IndexingSetter, ast::OperationKind::IndexingDeleter => OperationKind::IndexingDeleter, }; MethodKind::Operation(Operation { is_static, kind }) } }) } wasm-bindgen-backend-0.2.58/src/error.rs010066400017500001750000000063661354215717600163050ustar0000000000000000use proc_macro2::*; use quote::{ToTokens, TokenStreamExt}; use syn::parse::Error; #[macro_export] macro_rules! err_span { ($span:expr, $($msg:tt)*) => ( $crate::Diagnostic::spanned_error(&$span, format!($($msg)*)) ) } #[macro_export] macro_rules! bail_span { ($($t:tt)*) => ( return Err(err_span!($($t)*).into()) ) } #[derive(Debug)] pub struct Diagnostic { inner: Repr, } #[derive(Debug)] enum Repr { Single { text: String, span: Option<(Span, Span)>, }, SynError(Error), Multi { diagnostics: Vec, }, } impl Diagnostic { pub fn error>(text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: None, }, } } pub fn span_error>(span: Span, text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: Some((span, span)), }, } } pub fn spanned_error>(node: &dyn ToTokens, text: T) -> Diagnostic { Diagnostic { inner: Repr::Single { text: text.into(), span: extract_spans(node), }, } } pub fn from_vec(diagnostics: Vec) -> Result<(), Diagnostic> { if diagnostics.len() == 0 { Ok(()) } else { Err(Diagnostic { inner: Repr::Multi { diagnostics }, }) } } #[allow(unconditional_recursion)] pub fn panic(&self) -> ! { match &self.inner { Repr::Single { text, .. } => panic!("{}", text), Repr::SynError(error) => panic!("{}", error), Repr::Multi { diagnostics } => diagnostics[0].panic(), } } } impl From for Diagnostic { fn from(err: Error) -> Diagnostic { Diagnostic { inner: Repr::SynError(err), } } } fn extract_spans(node: &dyn ToTokens) -> Option<(Span, Span)> { let mut t = TokenStream::new(); node.to_tokens(&mut t); let mut tokens = t.into_iter(); let start = tokens.next().map(|t| t.span()); let end = tokens.last().map(|t| t.span()); start.map(|start| (start, end.unwrap_or(start))) } impl ToTokens for Diagnostic { fn to_tokens(&self, dst: &mut TokenStream) { match &self.inner { Repr::Single { text, span } => { let cs2 = (Span::call_site(), Span::call_site()); let (start, end) = span.unwrap_or(cs2); dst.append(Ident::new("compile_error", start)); dst.append(Punct::new('!', Spacing::Alone)); let mut message = TokenStream::new(); message.append(Literal::string(text)); let mut group = Group::new(Delimiter::Brace, message); group.set_span(end); dst.append(group); } Repr::Multi { diagnostics } => { for diagnostic in diagnostics { diagnostic.to_tokens(dst); } } Repr::SynError(err) => { err.to_compile_error().to_tokens(dst); } } } } wasm-bindgen-backend-0.2.58/src/lib.rs010066400017500001750000000005231354317317300157030ustar0000000000000000#![recursion_limit = "256"] #![cfg_attr(feature = "extra-traits", deny(missing_debug_implementations))] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-backend/0.2")] pub use crate::codegen::TryToTokens; pub use crate::error::Diagnostic; #[macro_use] mod error; pub mod ast; mod codegen; pub mod defined; mod encode; pub mod util; wasm-bindgen-backend-0.2.58/src/util.rs010066400017500001750000000122301347603543600161140ustar0000000000000000use std::collections::hash_map::DefaultHasher; use std::env; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use crate::ast; use proc_macro2::{self, Ident}; use syn; fn is_rust_keyword(name: &str) -> bool { match name { "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" | "while" | "yield" | "bool" | "_" => true, _ => false, } } // Create an `Ident`, possibly mangling it if it conflicts with a Rust keyword. pub fn rust_ident(name: &str) -> Ident { if name == "" { panic!("tried to create empty Ident (from \"\")"); } else if is_rust_keyword(name) { Ident::new(&format!("{}_", name), proc_macro2::Span::call_site()) // we didn't historically have `async` in the `is_rust_keyword` list above, // so for backwards compatibility reasons we need to generate an `async` // identifier as well, but we'll be sure to use a raw identifier to ease // compatibility with the 2018 edition. // // Note, though, that `proc-macro` doesn't support a normal way to create a // raw identifier. To get around that we do some wonky parsing to // roundaboutly create one. } else if name == "async" { let ident = "r#async" .parse::() .unwrap() .into_iter() .next() .unwrap(); match ident { proc_macro2::TokenTree::Ident(i) => i, _ => unreachable!(), } } else if name.chars().next().unwrap().is_ascii_digit() { Ident::new(&format!("N{}", name), proc_macro2::Span::call_site()) } else { raw_ident(name) } } // Create an `Ident` without checking to see if it conflicts with a Rust // keyword. pub fn raw_ident(name: &str) -> Ident { Ident::new(name, proc_macro2::Span::call_site()) } /// Create a path type from the given segments. For example an iterator yielding /// the idents `[foo, bar, baz]` will result in the path type `foo::bar::baz`. pub fn simple_path_ty(segments: I) -> syn::Type where I: IntoIterator, { path_ty(false, segments) } /// Create a global path type from the given segments. For example an iterator /// yielding the idents `[foo, bar, baz]` will result in the path type /// `::foo::bar::baz`. pub fn leading_colon_path_ty(segments: I) -> syn::Type where I: IntoIterator, { path_ty(true, segments) } fn path_ty(leading_colon: bool, segments: I) -> syn::Type where I: IntoIterator, { let segments: Vec<_> = segments .into_iter() .map(|i| syn::PathSegment { ident: i, arguments: syn::PathArguments::None, }) .collect(); syn::TypePath { qself: None, path: syn::Path { leading_colon: if leading_colon { Some(Default::default()) } else { None }, segments: syn::punctuated::Punctuated::from_iter(segments), }, } .into() } pub fn ident_ty(ident: Ident) -> syn::Type { simple_path_ty(Some(ident)) } pub fn wrap_import_function(function: ast::ImportFunction) -> ast::Import { ast::Import { module: ast::ImportModule::None, js_namespace: None, kind: ast::ImportKind::Function(function), } } /// Small utility used when generating symbol names. /// /// Hashes the public field here along with a few cargo-set env vars to /// distinguish between runs of the procedural macro. #[derive(Debug)] pub struct ShortHash(pub T); impl fmt::Display for ShortHash { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { static HASHED: AtomicBool = AtomicBool::new(false); static HASH: AtomicUsize = AtomicUsize::new(0); // Try to amortize the cost of loading env vars a lot as we're gonna be // hashing for a lot of symbols. if !HASHED.load(SeqCst) { let mut h = DefaultHasher::new(); env::var("CARGO_PKG_NAME") .expect("should have CARGO_PKG_NAME env var") .hash(&mut h); env::var("CARGO_PKG_VERSION") .expect("should have CARGO_PKG_VERSION env var") .hash(&mut h); // This may chop off 32 bits on 32-bit platforms, but that's ok, we // just want something to mix in below anyway. HASH.store(h.finish() as usize, SeqCst); HASHED.store(true, SeqCst); } let mut h = DefaultHasher::new(); HASH.load(SeqCst).hash(&mut h); self.0.hash(&mut h); write!(f, "{:016x}", h.finish()) } }