diff --git a/.travis.yml b/.travis.yml index ff05fdaf..0173cb89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,6 +52,9 @@ matrix: script: - cargo test --release - cargo test --target wasm32-unknown-unknown + - WASM_BINDGEN_NO_DEBUG=1 cargo test --target wasm32-unknown-unknown + - cargo test --target wasm32-unknown-unknown --features serde-serialize + - cargo test --target wasm32-unknown-unknown -p no-std # Check JS output from all tests against eslint - npm run run-lint-generated-tests # Check Examples against eslint diff --git a/Cargo.toml b/Cargo.toml index a3c8b84f..06eec5d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,9 @@ serde_json = { version = "1.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = { path = 'crates/test', version = '=0.2.15' } +serde_derive = "1.0" +wasm-bindgen-test-crate-a = { path = 'tests/crates/a' } +wasm-bindgen-test-crate-b = { path = 'tests/crates/b' } [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] wasm-bindgen-test-project-builder = { path = "crates/test-project-builder", version = '=0.2.15' } @@ -63,6 +66,7 @@ members = [ "examples/performance", "examples/smorgasboard", "examples/wasm-in-wasm", + "tests/no-std", ] [patch.crates-io] diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index c1cc8a1e..dce50462 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -56,7 +56,6 @@ pub enum MethodSelf { #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] pub struct Import { pub module: Option, - pub version: Option, pub js_namespace: Option, pub kind: ImportKind, } @@ -119,7 +118,7 @@ pub struct ImportStatic { pub ty: syn::Type, pub shim: Ident, pub rust_name: Ident, - pub js_name: Ident, + pub js_name: String, } #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -146,7 +145,7 @@ pub struct ImportEnum { #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] pub struct Function { - pub name: Ident, + pub name: String, pub arguments: Vec, pub ret: Option, pub rust_attrs: Vec, @@ -304,33 +303,8 @@ impl Variant { impl Import { fn shared(&self) -> Result { - match (&self.module, &self.version) { - (&Some(ref m), None) if m.starts_with("./") => {} - (&Some(ref m), &Some(_)) if m.starts_with("./") => { - panic!( - "when a module path starts with `./` that indicates \ - that a local file is being imported so the `version` \ - key cannot also be specified" - ); - } - (&Some(_), &Some(_)) => {} - (&Some(_), &None) => panic!( - "when the `module` directive doesn't start with `./` \ - then it's interpreted as an NPM package which requires \ - a `version` to be specified as well, try using \ - #[wasm_bindgen(module = \"...\", version = \"...\")]" - ), - (&None, &Some(_)) => { - panic!( - "the #[wasm_bindgen(version = \"...\")] attribute can only \ - be used when `module = \"...\"` is also specified" - ); - } - (&None, &None) => {} - } Ok(shared::Import { module: self.module.clone(), - version: self.version.clone(), js_namespace: self.js_namespace.as_ref().map(|s| s.to_string()), kind: self.kind.shared(), }) diff --git a/crates/backend/src/util.rs b/crates/backend/src/util.rs index bab8cb5d..f0bf53e7 100644 --- a/crates/backend/src/util.rs +++ b/crates/backend/src/util.rs @@ -94,7 +94,6 @@ pub fn ident_ty(ident: Ident) -> syn::Type { pub fn wrap_import_function(function: ast::ImportFunction) -> ast::Import { ast::Import { module: None, - version: None, js_namespace: None, kind: ast::ImportKind::Function(function), } diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml index 6dc5a91a..2c16e608 100644 --- a/crates/cli-support/Cargo.toml +++ b/crates/cli-support/Cargo.toml @@ -15,7 +15,6 @@ base64 = "0.9" failure = "0.1.2" parity-wasm = "0.31" serde = "1.0" -serde_derive = "1.0" serde_json = "1.0" tempfile = "3.0" wasm-bindgen-shared = { path = "../shared", version = '=0.2.15' } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 2f5b6a36..ba50995e 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -5,7 +5,6 @@ use std::mem; use failure::{Error, ResultExt}; use parity_wasm; use parity_wasm::elements::*; -use serde_json; use shared; use wasm_gc; @@ -43,7 +42,6 @@ pub struct Context<'a> { pub exported_classes: HashMap, pub function_table_needed: bool, pub run_descriptor: &'a Fn(&str) -> Option>, - pub module_versions: Vec<(String, String)>, } #[derive(Default)] @@ -458,7 +456,6 @@ impl<'a> Context<'a> { self.export_table(); self.gc()?; - self.add_wasm_pack_section(); while js.contains("\n\n\n") { js = js.replace("\n\n\n", "\n\n"); @@ -1629,28 +1626,6 @@ impl<'a> Context<'a> { self.globals.push_str("\n"); } - fn add_wasm_pack_section(&mut self) { - if self.module_versions.len() == 0 { - return; - } - - #[derive(Serialize)] - struct WasmPackSchema<'a> { - version: &'a str, - modules: &'a [(String, String)], - } - - let contents = serde_json::to_string(&WasmPackSchema { - version: "0.0.1", - modules: &self.module_versions, - }).unwrap(); - - let mut section = CustomSection::default(); - *section.name_mut() = "__wasm_pack_unstable".to_string(); - *section.payload_mut() = contents.into_bytes(); - self.module.sections_mut().push(Section::Custom(section)); - } - fn use_node_require(&self) -> bool { self.config.nodejs && !self.config.nodejs_experimental_modules } @@ -1766,7 +1741,6 @@ impl<'a, 'b> SubContext<'a, 'b> { } fn generate_import(&mut self, import: &shared::Import) -> Result<(), Error> { - self.validate_import_module(import)?; match import.kind { shared::ImportKind::Function(ref f) => { self.generate_import_function(import, f).with_context(|_| { @@ -1787,41 +1761,6 @@ impl<'a, 'b> SubContext<'a, 'b> { Ok(()) } - fn validate_import_module(&mut self, import: &shared::Import) -> Result<(), Error> { - let version = match import.version { - Some(ref s) => s, - None => return Ok(()), - }; - let module = match import.module { - Some(ref s) => s, - None => return Ok(()), - }; - if module.starts_with("./") { - return Ok(()); - } - let pkg = if module.starts_with("@") { - // Translate `@foo/bar/baz` to `@foo/bar` and `@foo/bar` to itself - let first_slash = match module.find('/') { - Some(i) => i, - None => bail!( - "packages starting with `@` must be of the form \ - `@foo/bar`, but found: `{}`", - module - ), - }; - match module[first_slash + 1..].find('/') { - Some(i) => &module[..i], - None => module, - } - } else { - module.split('/').next().unwrap() - }; - self.cx - .module_versions - .push((pkg.to_string(), version.clone())); - Ok(()) - } - fn generate_import_static( &mut self, info: &shared::Import, diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index d8cb8fdb..5933a691 100644 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -2,8 +2,6 @@ extern crate parity_wasm; extern crate wasm_bindgen_shared as shared; -#[macro_use] -extern crate serde_derive; extern crate serde_json; extern crate wasm_gc; extern crate wasmi; @@ -203,7 +201,6 @@ impl Bindgen { config: &self, module: &mut module, function_table_needed: false, - module_versions: Default::default(), run_descriptor: &|name| { let mut v = MyExternals(Vec::new()); match instance.invoke_export(name, &[], &mut v) { diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs index 695359f0..e03dc8d7 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs @@ -123,11 +123,12 @@ fn rmain() -> Result<(), Error> { node = !custom.payload().contains(&0x01); } let headless = env::var("NO_HEADLESS").is_err(); + let debug = env::var("WASM_BINDGEN_NO_DEBUG").is_err(); // Make the generated bindings available for the tests to execute against. shell.status("Executing bindgen..."); let mut b = Bindgen::new(); - b.debug(true) + b.debug(debug) .nodejs(node) .input_module(module, wasm, |w| parity_wasm::serialize(w).unwrap()) .keep_debug(false) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index b4c4198b..c637eb41 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -361,6 +361,16 @@ extern "C" { #[wasm_bindgen(constructor)] pub fn new(length: u32) -> ArrayBuffer; + /// The byteLength property of an object which is an instance of type ArrayBuffer + /// it's an accessor property whose set accessor function is undefined, + /// meaning that you can only read this property. + /// The value is established when the array is constructed and cannot be changed. + /// This property returns 0 if this ArrayBuffer has been detached. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/byteLength + #[wasm_bindgen(method, getter, js_name = byteLength)] + pub fn byte_length(this: &ArrayBuffer) -> u32; + /// The `isView()` method returns true if arg is one of the `ArrayBuffer` /// views, such as typed array objects or a DataView; false otherwise. /// @@ -2168,6 +2178,30 @@ extern { #[wasm_bindgen(method, getter)] pub fn multiline(this: &RegExp) -> bool; + /// The non-standard $1, $2, $3, $4, $5, $6, $7, $8, $9 properties + /// are static and read-only properties of regular expressions + /// that contain parenthesized substring matches. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/n + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$1")] + pub fn n1() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$2")] + pub fn n2() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$3")] + pub fn n3() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$4")] + pub fn n4() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$5")] + pub fn n5() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$6")] + pub fn n6() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$7")] + pub fn n7() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$8")] + pub fn n8() -> JsString; + #[wasm_bindgen(static_method_of = RegExp, getter, js_name = "$9")] + pub fn n9() -> JsString; + /// The RegExp constructor creates a regular expression object for matching text with a pattern. /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp diff --git a/crates/js-sys/tests/wasm/ArrayBuffer.rs b/crates/js-sys/tests/wasm/ArrayBuffer.rs index 904221ff..ddfcdeef 100644 --- a/crates/js-sys/tests/wasm/ArrayBuffer.rs +++ b/crates/js-sys/tests/wasm/ArrayBuffer.rs @@ -9,6 +9,12 @@ fn new() { assert!(y.is_object()); } +#[wasm_bindgen_test] +fn byte_length() { + let buf = ArrayBuffer::new(42); + assert_eq!(buf.byte_length(), 42); +} + #[wasm_bindgen_test] fn is_view() { let x = Uint8Array::new(&JsValue::from(42)); diff --git a/crates/js-sys/tests/wasm/Function.rs b/crates/js-sys/tests/wasm/Function.rs index 02e9865f..f2339496 100644 --- a/crates/js-sys/tests/wasm/Function.rs +++ b/crates/js-sys/tests/wasm/Function.rs @@ -29,7 +29,7 @@ fn apply() { assert_eq!(Array::from(&arr).length(), 1); } -#[wasm_bindgen(module = "tests/wasm/Function.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Function.js")] extern { fn get_function_to_bind() -> Function; fn get_value_to_bind_to() -> JsValue; diff --git a/crates/js-sys/tests/wasm/Generator.rs b/crates/js-sys/tests/wasm/Generator.rs index 6d114b9f..5fb738fc 100644 --- a/crates/js-sys/tests/wasm/Generator.rs +++ b/crates/js-sys/tests/wasm/Generator.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; use js_sys::*; -#[wasm_bindgen(module = "tests/wasm/Generator.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Generator.js")] extern { fn one_two_generator() -> Generator; fn dummy_generator() -> Generator; diff --git a/crates/js-sys/tests/wasm/Object.rs b/crates/js-sys/tests/wasm/Object.rs index 80246535..da245a97 100644 --- a/crates/js-sys/tests/wasm/Object.rs +++ b/crates/js-sys/tests/wasm/Object.rs @@ -10,7 +10,7 @@ extern { fn set_foo(this: &Foo42, val: JsValue); } -#[wasm_bindgen(module = "tests/wasm/Object.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Object.js")] extern { fn map_with_symbol_key() -> Object; fn symbol_key() -> JsValue; diff --git a/crates/js-sys/tests/wasm/Proxy.rs b/crates/js-sys/tests/wasm/Proxy.rs index b362b888..30b23418 100644 --- a/crates/js-sys/tests/wasm/Proxy.rs +++ b/crates/js-sys/tests/wasm/Proxy.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; use js_sys::*; -#[wasm_bindgen(module = "tests/wasm/Proxy.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Proxy.js")] extern { fn proxy_target() -> JsValue; fn proxy_handler() -> Object; diff --git a/crates/js-sys/tests/wasm/Reflect.rs b/crates/js-sys/tests/wasm/Reflect.rs index a7c42748..5f6f47f1 100644 --- a/crates/js-sys/tests/wasm/Reflect.rs +++ b/crates/js-sys/tests/wasm/Reflect.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; use js_sys::*; -#[wasm_bindgen(module = "tests/wasm/Reflect.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Reflect.js")] extern { fn get_char_at() -> Function; diff --git a/crates/js-sys/tests/wasm/RegExp.rs b/crates/js-sys/tests/wasm/RegExp.rs index a9b60c70..842a0049 100644 --- a/crates/js-sys/tests/wasm/RegExp.rs +++ b/crates/js-sys/tests/wasm/RegExp.rs @@ -3,7 +3,6 @@ use js_sys::*; #[wasm_bindgen_test] fn exec() { - let re = RegExp::new("quick\\s(brown).+?(jumps)", "ig"); let result = re.exec("The Quick Brown Fox Jumps Over The Lazy Dog"); @@ -76,6 +75,21 @@ fn multiline() { assert!(re.multiline()); } +#[wasm_bindgen_test] +fn n1_to_n9() { + let re = RegExp::new(r"(\w+)\s(\w+)\s(\w+)\s(\w+)\s(\w+)\s(\w+)\s(\w+)\s(\w+)\s(\w+)", ""); + re.test("The Quick Brown Fox Jumps Over The Lazy Dog"); + assert_eq!(RegExp::n1(), "The"); + assert_eq!(RegExp::n2(), "Quick"); + assert_eq!(RegExp::n3(), "Brown"); + assert_eq!(RegExp::n4(), "Fox"); + assert_eq!(RegExp::n5(), "Jumps"); + assert_eq!(RegExp::n6(), "Over"); + assert_eq!(RegExp::n7(), "The"); + assert_eq!(RegExp::n8(), "Lazy"); + assert_eq!(RegExp::n9(), "Dog"); +} + #[wasm_bindgen_test] fn new() { let re = RegExp::new("foo", ""); diff --git a/crates/js-sys/tests/wasm/Symbol.rs b/crates/js-sys/tests/wasm/Symbol.rs index cf455c44..a34a1ca2 100644 --- a/crates/js-sys/tests/wasm/Symbol.rs +++ b/crates/js-sys/tests/wasm/Symbol.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; use js_sys::*; -#[wasm_bindgen(module = "tests/wasm/Symbol.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/Symbol.js")] extern { fn test_has_instance(sym: &Symbol); fn test_is_concat_spreadable(sym: &Symbol); diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 6fa4f974..159bfb94 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -53,17 +53,6 @@ impl BindgenAttrs { .next() } - /// Get the first version attribute - fn version(&self) -> Option<&str> { - self.attrs - .iter() - .filter_map(|a| match a { - BindgenAttr::Version(s) => Some(&s[..]), - _ => None, - }) - .next() - } - /// Whether the catch attribute is present fn catch(&self) -> bool { self.attrs.iter().any(|a| match a { @@ -173,11 +162,11 @@ impl BindgenAttrs { } /// Get the first js_name attribute - fn js_name(&self) -> Option<&Ident> { + fn js_name(&self) -> Option<&str> { self.attrs .iter() .filter_map(|a| match a { - BindgenAttr::JsName(s) => Some(s), + BindgenAttr::JsName(s) => Some(&s[..]), _ => None, }) .next() @@ -219,7 +208,6 @@ pub enum BindgenAttr { StaticMethodOf(Ident), JsNamespace(Ident), Module(String), - Version(String), Getter(Option), Setter(Option), SpecialGetter, @@ -227,7 +215,7 @@ pub enum BindgenAttr { SpecialDeleter, Structural, Readonly, - JsName(Ident), + JsName(String), JsClass(String), } @@ -290,18 +278,15 @@ impl syn::synom::Synom for BindgenAttr { (s.value()) )=> { BindgenAttr::Module } | - do_parse!( - call!(term, "version") >> - punct!(=) >> - s: syn!(syn::LitStr) >> - (s.value()) - )=> { BindgenAttr::Version } - | do_parse!( call!(term, "js_name") >> punct!(=) >> - ns: call!(term2ident) >> - (ns) + name: alt!( + syn!(syn::LitStr) => { |s| s.value() } + | + call!(term2ident) => { |s| s.to_string() } + ) >> + (name) )=> { BindgenAttr::JsName } | do_parse!( @@ -398,9 +383,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn fn convert(self, (opts, module): (BindgenAttrs, &'a Option)) -> Result { - let js_name = opts.js_name().unwrap_or(&self.ident).clone(); + let default_name = self.ident.to_string(); + let js_name = opts.js_name().unwrap_or(&default_name); let wasm = function_from_decl( - &js_name, + js_name, self.decl.clone(), self.attrs.clone(), self.vis.clone(), @@ -517,7 +503,9 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]), }; let data = (ns, &self.ident, module); - format!("__wbg_{}_{}", js_name, ShortHash(data)) + format!("__wbg_{}_{}", + js_name.chars().filter(|c| c.is_ascii_alphanumeric()).collect::(), + ShortHash(data)) }; Ok(ast::ImportKind::Function(ast::ImportFunction { function: wasm, @@ -552,13 +540,16 @@ impl ConvertToAst for syn::ForeignItemStatic { if self.mutability.is_some() { bail_span!(self.mutability, "cannot import mutable globals yet") } - let js_name = opts.js_name().unwrap_or(&self.ident); - let shim = format!("__wbg_static_accessor_{}_{}", js_name, self.ident); + let default_name = self.ident.to_string(); + let js_name = opts.js_name().unwrap_or(&default_name); + let shim = format!("__wbg_static_accessor_{}_{}", + js_name.chars().filter(|c| c.is_ascii_alphanumeric()).collect::(), + self.ident); Ok(ast::ImportKind::Static(ast::ImportStatic { ty: *self.ty, vis: self.vis, rust_name: self.ident.clone(), - js_name: js_name.clone(), + js_name: js_name.to_string(), shim: Ident::new(&shim, Span::call_site()), })) } @@ -579,14 +570,15 @@ impl ConvertToAst for syn::ItemFn { bail_span!(self.unsafety, "can only #[wasm_bindgen] safe functions"); } - let name = attrs.js_name().unwrap_or(&self.ident); + let default_name = self.ident.to_string(); + let name = attrs.js_name().unwrap_or(&default_name); Ok(function_from_decl(name, self.decl, self.attrs, self.vis, false, None)?.0) } } /// Construct a function (and gets the self type if appropriate) for our AST from a syn function. fn function_from_decl( - name: &Ident, + name: &str, decl: Box, attrs: Vec, vis: syn::Visibility, @@ -661,7 +653,7 @@ fn function_from_decl( Ok(( ast::Function { - name: name.clone(), + name: name.to_string(), arguments, ret, rust_vis: vis, @@ -824,7 +816,7 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { }; let (function, method_self) = function_from_decl( - opts.js_name().unwrap_or(&method.sig.ident), + opts.js_name().unwrap_or(&method.sig.ident.to_string()), Box::new(method.sig.decl.clone()), method.attrs.clone(), method.vis.clone(), @@ -940,10 +932,6 @@ impl<'a> MacroParse<&'a BindgenAttrs> for syn::ForeignItem { BindgenAttrs::find(attrs)? }; let module = item_opts.module().or(opts.module()).map(|s| s.to_string()); - let version = item_opts - .version() - .or(opts.version()) - .map(|s| s.to_string()); let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned(); let kind = match self { syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?, @@ -954,7 +942,6 @@ impl<'a> MacroParse<&'a BindgenAttrs> for syn::ForeignItem { program.imports.push(ast::Import { module, - version, js_namespace, kind, }); diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index dc027abe..56a4dafe 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -24,7 +24,6 @@ pub struct Program { #[derive(Deserialize, Serialize)] pub struct Import { pub module: Option, - pub version: Option, pub js_namespace: Option, pub kind: ImportKind, } diff --git a/crates/test-macro/src/lib.rs b/crates/test-macro/src/lib.rs index 4d7aa083..19b77590 100644 --- a/crates/test-macro/src/lib.rs +++ b/crates/test-macro/src/lib.rs @@ -32,9 +32,16 @@ pub fn wasm_bindgen_test( let mut body = TokenStream::from(body).into_iter(); - // Assume the input item is of the form `fn #ident ...`, and extract - // `#ident` - let fn_tok = body.next(); + // Skip over other attributes to `fn #ident ...`, and extract `#ident` + let mut leading_tokens = Vec::new(); + while let Some(token) = body.next() { + leading_tokens.push(token.clone()); + if let TokenTree::Ident(token) = token { + if token == "fn" { + break + } + } + } let ident = match body.next() { Some(TokenTree::Ident(token)) => token, _ => panic!("expected a function name"), @@ -64,7 +71,7 @@ pub fn wasm_bindgen_test( } }).into_iter()); - tokens.extend(fn_tok); + tokens.extend(leading_tokens); tokens.push(ident.into()); tokens.extend(body); diff --git a/crates/web-sys/build.rs b/crates/web-sys/build.rs index 0496a979..8dbb232c 100644 --- a/crates/web-sys/build.rs +++ b/crates/web-sys/build.rs @@ -1,4 +1,5 @@ extern crate env_logger; +#[macro_use] extern crate failure; extern crate wasm_bindgen_webidl; extern crate sourcefile; @@ -8,9 +9,8 @@ use sourcefile::SourceFile; use std::env; use std::ffi::OsStr; use std::fs; -use std::io::Write; use std::path; -use std::process; +use std::process::{self, Command}; fn main() { if let Err(e) = try_main() { @@ -32,12 +32,14 @@ fn try_main() -> Result<(), failure::Error> { let mut source = SourceFile::default(); for entry in entries { let entry = entry.context("getting webidls/enabled/*.webidl entry")?; - if entry.path().extension() == Some(OsStr::new("webidl")) { - println!("cargo:rerun-if-changed={}", entry.path().display()); - source = source.add_file(entry.path()) - .with_context(|_| format!("reading contents of file \"{}\"", - entry.path().display()))?; + let path = entry.path(); + if path.extension() != Some(OsStr::new("webidl")) { + continue } + println!("cargo:rerun-if-changed={}", path.display()); + source = source.add_file(&path) + .with_context(|_| format!("reading contents of file \"{}\"", + path.display()))?; } let bindings = match wasm_bindgen_webidl::compile(&source.contents) { @@ -60,17 +62,19 @@ fn try_main() -> Result<(), failure::Error> { let out_dir = env::var("OUT_DIR").context("reading OUT_DIR environment variable")?; let out_file_path = path::Path::new(&out_dir).join("bindings.rs"); - let mut out_file = fs::File::create(&out_file_path) - .context("creating output bindings file")?; - out_file - .write_all(bindings.as_bytes()) + fs::write(&out_file_path, bindings) .context("writing bindings to output file")?; // run rustfmt on the generated file - really handy for debugging - //if ! process::Command::new("rustfmt").arg(&out_file_path).status() - // .context("running rustfmt")?.success() { - // return Err(format_err!("rustfmt failed to format {}", out_file_path.display())); - //} + if env::var("WEBIDL_RUSTFMT_BINDINGS").is_ok() { + let status = Command::new("rustfmt") + .arg(&out_file_path) + .status() + .context("running rustfmt")?; + if !status.success() { + bail!("rustfmt failed: {}", status) + } + } Ok(()) } diff --git a/crates/webidl-tests/build.rs b/crates/webidl-tests/build.rs index 2f8294b3..536ccf07 100644 --- a/crates/webidl-tests/build.rs +++ b/crates/webidl-tests/build.rs @@ -28,7 +28,7 @@ fn main() { use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; - #[wasm_bindgen(module = "{}", version = "*")] + #[wasm_bindgen(module = "{}")] extern {{ fn not_actually_a_function{1}(); }} diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index e953891e..471b4c73 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -20,4 +20,4 @@ proc-macro2 = "0.4.8" quote = '0.6' syn = { version = '0.14', features = ['full'] } wasm-bindgen-backend = { version = "=0.2.15", path = "../backend" } -webidl = "0.7.0" +weedle = "0.6" diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index e594f95d..574e3d46 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -7,69 +7,61 @@ //! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can //! be partial. -use std::{ - collections::{BTreeMap, BTreeSet}, mem, -}; +use std::collections::{BTreeMap, BTreeSet}; -use webidl; +use weedle::argument::Argument; +use weedle::attribute::ExtendedAttribute; +use weedle::interface::StringifierOrStatic; +use weedle::mixin::MixinMembers; +use weedle; use super::Result; +use util; /// Collection of constructs that may use partial. #[derive(Default)] -pub(crate) struct FirstPassRecord<'a> { - pub(crate) interfaces: BTreeMap, - pub(crate) dictionaries: BTreeSet, - pub(crate) enums: BTreeSet, +pub(crate) struct FirstPassRecord<'src> { + pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>, + pub(crate) dictionaries: BTreeSet<&'src str>, + pub(crate) enums: BTreeSet<&'src str>, /// The mixins, mapping their name to the webidl ast node for the mixin. - pub(crate) mixins: BTreeMap>, - pub(crate) typedefs: BTreeMap, + pub(crate) mixins: BTreeMap<&'src str, Vec<&'src MixinMembers<'src>>>, + pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, } /// We need to collect interface data during the first pass, to be used later. #[derive(Default)] -pub(crate) struct InterfaceData { +pub(crate) struct InterfaceData<'src> { /// Whether only partial interfaces were encountered pub(crate) partial: bool, - pub(crate) operations: BTreeMap, pub(crate) global: bool, + pub(crate) operations: BTreeMap, OperationData<'src>>, } #[derive(PartialEq, Eq, PartialOrd, Ord)] -pub(crate) enum OperationId { +pub(crate) enum OperationId<'src> { Constructor, - Operation(Option), + Operation(Option<&'src str>), SpecialGetter, SpecialSetter, SpecialDeleter, } #[derive(Default)] -pub(crate) struct OperationData { +pub(crate) struct OperationData<'src> { pub(crate) overloaded: bool, /// Map from argument names to whether they are the same for multiple overloads - pub(crate) argument_names_same: BTreeMap, bool>, -} - -/// We need to collect mixin data during the first pass, to be used later. -#[derive(Default)] -pub(crate) struct MixinData<'a> { - /// The non partial mixin, if present. If there is more than one, we are - /// parsing is a malformed WebIDL file, but the parser will recover by - /// using the last parsed mixin. - pub(crate) non_partial: Option<&'a webidl::ast::NonPartialMixin>, - /// 0 or more partial mixins. - pub(crate) partials: Vec<&'a webidl::ast::PartialMixin>, + pub(crate) argument_names_same: BTreeMap, bool>, } /// Implemented on an AST node to populate the `FirstPassRecord` struct. -pub(crate) trait FirstPass { +pub(crate) trait FirstPass<'src, Ctx> { /// Populate `record` with any constructs in `self`. - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, ctx: Ctx) -> Result<()>; + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, ctx: Ctx) -> Result<()>; } -impl FirstPass<()> for [webidl::ast::Definition] { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { +impl<'src> FirstPass<'src, ()> for [weedle::Definition<'src>] { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { for def in self { def.first_pass(record, ())?; } @@ -78,15 +70,17 @@ impl FirstPass<()> for [webidl::ast::Definition] { } } -impl FirstPass<()> for webidl::ast::Definition { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Definition::*; +impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + use weedle::Definition::*; match self { Dictionary(dictionary) => dictionary.first_pass(record, ()), Enum(enum_) => enum_.first_pass(record, ()), Interface(interface) => interface.first_pass(record, ()), - Mixin(mixin) => mixin.first_pass(record, ()), + PartialInterface(interface) => interface.first_pass(record, ()), + InterfaceMixin(mixin) => mixin.first_pass(record, ()), + PartialInterfaceMixin(mixin) => mixin.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), _ => { // Other definitions aren't currently used in the first pass @@ -96,46 +90,38 @@ impl FirstPass<()> for webidl::ast::Definition { } } -impl FirstPass<()> for webidl::ast::Dictionary { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Dictionary::*; - - match self { - NonPartial(dictionary) => dictionary.first_pass(record, ()), - _ => { - // Other dictionaries aren't currently used in the first pass - Ok(()) - } +impl<'src> FirstPass<'src, ()> for weedle::DictionaryDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if !record.dictionaries.insert(self.identifier.0) { + warn!("encountered multiple dictionary declarations of {}", self.identifier.0); } + Ok(()) } } -impl FirstPass<()> for webidl::ast::NonPartialDictionary { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if record.dictionaries.insert(self.name.clone()) { - warn!("Encountered multiple declarations of {}", self.name); +impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if !record.enums.insert(self.identifier.0) { + warn!("Encountered multiple enum declarations of {}", self.identifier.0); } Ok(()) } } -impl FirstPass<()> for webidl::ast::Enum { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if record.enums.insert(self.name.clone()) { - warn!("Encountered multiple declarations of {}", self.name); - } - - Ok(()) - } -} - -fn first_pass_operation<'a>( - record: &mut FirstPassRecord<'a>, - self_name: &str, - id: OperationId, - arguments: &[webidl::ast::Argument], +fn first_pass_operation<'src>( + record: &mut FirstPassRecord<'src>, + self_name: &'src str, + id: OperationId<'src>, + arguments: &[Argument<'src>], ) -> Result<()> { + let mut names = Vec::with_capacity(arguments.len()); + for argument in arguments { + match argument { + Argument::Single(arg) => names.push(arg.identifier.0), + Argument::Variadic(_) => return Ok(()), + } + } record .interfaces .get_mut(self_name) @@ -143,77 +129,48 @@ fn first_pass_operation<'a>( .operations .entry(id) .and_modify(|operation_data| operation_data.overloaded = true) - .or_insert_with(|| - OperationData { - overloaded: false, - argument_names_same: Default::default(), - } - ) + .or_insert_with(Default::default) .argument_names_same - .entry(arguments.iter().map(|argument| argument.name.clone()).collect()) + .entry(names) .and_modify(|same_argument_names| *same_argument_names = true) .or_insert(false); Ok(()) } -impl FirstPass<()> for webidl::ast::Interface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Interface::*; - - match self { - Partial(interface) => interface.first_pass(record, ()), - NonPartial(interface) => interface.first_pass(record, ()), - // TODO - Callback(..) => { - warn!("Unsupported WebIDL interface: {:?}", self); - Ok(()) - } +impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + { + let interface = record + .interfaces + .entry(self.identifier.0) + .or_insert_with(Default::default); + interface.partial = false; } - } -} -impl FirstPass<()> for webidl::ast::NonPartialInterface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - record - .interfaces - .entry(self.name.clone()) - .and_modify(|interface_data| { - if interface_data.partial { - interface_data.partial = false; - } else { - warn!("Encountered multiple declarations of {}", self.name); - } - }) - .or_insert_with(|| - InterfaceData { - partial: false, - operations: Default::default(), - global: false, - }, - ); - - if ::util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()) } - for extended_attribute in &self.extended_attributes { - extended_attribute.first_pass(record, &self.name)?; + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.first_pass(record, self.identifier.0)?; + } } - for member in &self.members { - member.first_pass(record, &self.name)?; + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; } Ok(()) } } -impl FirstPass<()> for webidl::ast::PartialInterface { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { +impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { record .interfaces - .entry(self.name.clone()) + .entry(self.identifier.0) .or_insert_with(|| InterfaceData { partial: true, @@ -222,191 +179,129 @@ impl FirstPass<()> for webidl::ast::PartialInterface { }, ); - if ::util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()) } - for member in &self.members { - member.first_pass(record, &self.name)?; + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; } Ok(()) } } -impl<'b> FirstPass<&'b str> for webidl::ast::ExtendedAttribute { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { +impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - webidl::ast::ExtendedAttribute::ArgumentList( - webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) - if name == "Constructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &arguments, - ) - } - webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) - if name == "Constructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &[], - ) - } - webidl::ast::ExtendedAttribute::NamedArgumentList( - webidl::ast::NamedArgumentListExtendedAttribute { - lhs_name, - rhs_arguments, - .. - }, - ) - if lhs_name == "NamedConstructor" => - { - first_pass_operation( - record, - self_name, - OperationId::Constructor, - &rhs_arguments, - ) - }, - webidl::ast::ExtendedAttribute::Identifier( - webidl::ast::IdentifierExtendedAttribute { lhs, .. } - ) - | webidl::ast::ExtendedAttribute::IdentifierList( - webidl::ast::IdentifierListExtendedAttribute { lhs, .. } - ) - if lhs == "Global" => - { - record.interfaces.get_mut(self_name).unwrap().global = true; - Ok(()) - } + ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &list.args.body.list, + ) + } + ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &[], + ) + } + ExtendedAttribute::NamedArgList(list) + if list.lhs_identifier.0 == "NamedConstructor" => + { + first_pass_operation( + record, + self_name, + OperationId::Constructor, + &list.args.body.list, + ) + } + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + Ok(()) + } + ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => { + record.interfaces.get_mut(self_name).unwrap().global = true; + Ok(()) + } _ => Ok(()) } } } -impl<'b> FirstPass<&'b str> for webidl::ast::InterfaceMember { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { +impl<'src> FirstPass<'src, &'src str> for weedle::interface::InterfaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { match self { - webidl::ast::InterfaceMember::Operation(op) => op.first_pass(record, self_name), + weedle::interface::InterfaceMember::Operation(op) => { + op.first_pass(record, self_name) + } _ => Ok(()), } } } -impl<'b> FirstPass<&'b str> for webidl::ast::Operation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { - match self { - webidl::ast::Operation::Regular(op) => op.first_pass(record, self_name), - webidl::ast::Operation::Static(op) => op.first_pass(record, self_name), - webidl::ast::Operation::Special(op) => op.first_pass(record, self_name), - // TODO - webidl::ast::Operation::Stringifier(_) => { - warn!("Unsupported WebIDL operation: {:?}", self); - Ok(()) - } +impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if !self.specials.is_empty() && self.specials.len() != 1 { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) + } + if let Some(StringifierOrStatic::Stringifier(_)) = self.modifier { + warn!("Unsupported webidl operation {:?}", self); + return Ok(()) } - } -} - -impl<'b> FirstPass<&'b str> for webidl::ast::RegularOperation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { first_pass_operation( record, self_name, - OperationId::Operation(self.name.clone()), - &self.arguments, - ) - } -} - -impl<'b> FirstPass<&'b str> for webidl::ast::StaticOperation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { - first_pass_operation( - record, - self_name, - OperationId::Operation(self.name.clone()), - &self.arguments, - ) - } -} - -impl<'b> FirstPass<&'b str> for webidl::ast::SpecialOperation { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, self_name: &'b str) -> Result<()> { - first_pass_operation( - record, - self_name, - match self.name { - None => match self.special_keywords.iter().next() { - Some(webidl::ast::Special::Getter) => OperationId::SpecialGetter, - Some(webidl::ast::Special::Setter) => OperationId::SpecialSetter, - Some(webidl::ast::Special::Deleter) => OperationId::SpecialDeleter, - Some(webidl::ast::Special::LegacyCaller) => return Ok(()), - None => { - panic!("unsupported special operation: {:?} of {}", self, self_name); - } + match self.identifier.map(|s| s.0) { + None => match self.specials.get(0) { + None => OperationId::Operation(None), + Some(weedle::interface::Special::Getter(weedle::term::Getter)) => OperationId::SpecialGetter, + Some(weedle::interface::Special::Setter(weedle::term::Setter)) => OperationId::SpecialSetter, + Some(weedle::interface::Special::Deleter(weedle::term::Deleter)) => OperationId::SpecialDeleter, + Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller)) => return Ok(()), }, Some(ref name) => OperationId::Operation(Some(name.clone())), }, - &self.arguments, + &self.args.body.list, ) } } -impl FirstPass<()> for webidl::ast::Mixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - use webidl::ast::Mixin::*; - - match self { - NonPartial(mixin) => mixin.first_pass(record, ()), - Partial(mixin) => mixin.first_pass(record, ()), - } - } -} - -impl FirstPass<()> for webidl::ast::NonPartialMixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - let entry = record +impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record .mixins - .entry(self.name.clone()) - .or_insert_with(Default::default); - if mem::replace(&mut entry.non_partial, Some(self)).is_some() { - warn!( - "Encounterd multiple declarations of {}, using last encountered", - self.name - ); - } - + .entry(self.identifier.0) + .or_insert_with(Default::default) + .push(&self.members.body); Ok(()) } } -impl FirstPass<()> for webidl::ast::PartialMixin { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - let entry = record +impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record .mixins - .entry(self.name.clone()) - .or_insert_with(Default::default); - entry.partials.push(self); - + .entry(self.identifier.0) + .or_insert_with(Default::default) + .push(&self.members.body); Ok(()) } } -impl FirstPass<()> for webidl::ast::Typedef { - fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>, (): ()) -> Result<()> { - if ::util::is_chrome_only(&self.extended_attributes) { +impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if record.typedefs.insert(self.name.clone(), *self.type_.clone()).is_some() { - warn!("Encountered multiple declarations of {}", self.name); + if record.typedefs.insert(self.identifier.0, &self.type_.type_).is_some() { + warn!("Encountered multiple declarations of {}", self.identifier.0); } Ok(()) diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 735152e5..d8434af0 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -9,6 +9,7 @@ emitted for the types and methods described in the WebIDL. #![deny(missing_debug_implementations)] #![doc(html_root_url = "https://docs.rs/wasm-bindgen-webidl/0.2")] +#[macro_use] extern crate failure; #[macro_use] extern crate failure_derive; @@ -21,7 +22,7 @@ extern crate quote; #[macro_use] extern crate syn; extern crate wasm_bindgen_backend as backend; -extern crate webidl; +extern crate weedle; mod first_pass; mod util; @@ -36,11 +37,14 @@ use std::path::Path; use backend::TryToTokens; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::util::{ident_ty, rust_ident, wrap_import_function}; -use failure::{ResultExt, Fail}; +use failure::ResultExt; use heck::{ShoutySnakeCase}; +use weedle::argument::Argument; +use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; use first_pass::{FirstPass, FirstPassRecord}; -use util::{ApplyTypedefs, public, webidl_const_ty_to_syn_ty, webidl_const_v_to_backend_const_v, camel_case_ident, mdn_doc}; +use util::{public, webidl_const_v_to_backend_const_v, TypePosition, camel_case_ident, mdn_doc}; +use util::ToSynType; pub use error::{Error, ErrorKind, Result}; @@ -55,22 +59,31 @@ fn parse_file(webidl_path: &Path) -> Result { /// Parse a string of WebIDL source text into a wasm-bindgen AST. fn parse(webidl_source: &str) -> Result { - let definitions = match webidl::parse_string(webidl_source) { + let definitions = match weedle::parse(webidl_source) { Ok(def) => def, Err(e) => { - let kind = match &e { - webidl::ParseError::InvalidToken { location } => { - ErrorKind::ParsingWebIDLSourcePos(*location) + return Err(match &e { + weedle::Err::Incomplete(needed) => { + format_err!("needed {:?} more bytes", needed) + .context(ErrorKind::ParsingWebIDLSource).into() } - webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { - ErrorKind::ParsingWebIDLSourcePos(*start) + weedle::Err::Error(cx) | + weedle::Err::Failure(cx) => { + let remaining = match cx { + weedle::Context::Code(remaining, _) => remaining, + }; + let pos = webidl_source.len() - remaining.len(); + format_err!("failed to parse WebIDL") + .context(ErrorKind::ParsingWebIDLSourcePos(pos)).into() } - webidl::ParseError::ExtraToken { token: (start, ..) } => { - ErrorKind::ParsingWebIDLSourcePos(*start) - }, - _ => ErrorKind::ParsingWebIDLSource - }; - return Err(e.context(kind).into()); + // webidl::ParseError::UnrecognizedToken { token: Some((start, ..)), .. } => { + // ErrorKind::ParsingWebIDLSourcePos(*start) + // } + // webidl::ParseError::ExtraToken { token: (start, ..) } => { + // ErrorKind::ParsingWebIDLSourcePos(*start) + // }, + // _ => ErrorKind::ParsingWebIDLSource + }); } }; @@ -119,21 +132,21 @@ fn compile_ast(mut ast: backend::ast::Program) -> String { } /// The main trait for parsing WebIDL AST into wasm-bindgen AST. -trait WebidlParse { +trait WebidlParse<'src, Ctx> { /// Parse `self` into wasm-bindgen AST, and insert it into `program`. fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, context: Ctx, ) -> Result<()>; } -impl WebidlParse<()> for [webidl::ast::Definition] { +impl<'src> WebidlParse<'src, ()> for [weedle::Definition<'src>] { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { for def in self { @@ -143,175 +156,154 @@ impl WebidlParse<()> for [webidl::ast::Definition] { } } -impl WebidlParse<()> for webidl::ast::Definition { +impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { match self { - webidl::ast::Definition::Enum(enumeration) => { + weedle::Definition::Enum(enumeration) => { enumeration.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Includes(includes) => { + weedle::Definition::IncludesStatement(includes) => { includes.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Interface(interface) => { + weedle::Definition::Interface(interface) => { interface.webidl_parse(program, first_pass, ())? } - // TODO - webidl::ast::Definition::Callback(..) - | webidl::ast::Definition::Dictionary(..) - | webidl::ast::Definition::Implements(..) - | webidl::ast::Definition::Namespace(..) => { - warn!("Unsupported WebIDL definition: {:?}", self) + weedle::Definition::PartialInterface(interface) => { + interface.webidl_parse(program, first_pass, ())? } - webidl::ast::Definition::Mixin(_) - | webidl::ast::Definition::Typedef(_) => { + weedle::Definition::Typedef(_) | + weedle::Definition::InterfaceMixin(_) | + weedle::Definition::PartialInterfaceMixin(_) => { // handled in the first pass } - } - Ok(()) - } -} - -impl WebidlParse<()> for webidl::ast::Includes { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - (): (), - ) -> Result<()> { - match first_pass.mixins.get(&self.includee) { - Some(mixin) => { - if let Some(non_partial) = mixin.non_partial { - for member in &non_partial.members { - member.webidl_parse(program, first_pass, &self.includer)?; - } - } - for partial in &mixin.partials { - for member in &partial.members { - member.webidl_parse(program, first_pass, &self.includer)?; - } - } - } - None => warn!("Tried to include missing mixin {}", self.includee), - } - Ok(()) - } -} - -impl WebidlParse<()> for webidl::ast::Interface { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - (): (), - ) -> Result<()> { - match self { - webidl::ast::Interface::NonPartial(interface) => { - interface.webidl_parse(program, first_pass, ()) - } - webidl::ast::Interface::Partial(interface) => { - interface.webidl_parse(program, first_pass, ()) + weedle::Definition::Implements(..) => { + // nothing to do for this, ignore it } // TODO - webidl::ast::Interface::Callback(..) => { - warn!("Unsupported WebIDL interface: {:?}", self); - Ok(()) + weedle::Definition::Callback(..) + | weedle::Definition::CallbackInterface(..) + | weedle::Definition::Dictionary(..) + | weedle::Definition::PartialDictionary(..) + | weedle::Definition::Namespace(..) + | weedle::Definition::PartialNamespace(..) => { + warn!("Unsupported WebIDL definition: {:?}", self) } } + Ok(()) } } -impl WebidlParse<()> for webidl::ast::NonPartialInterface { +impl<'src> WebidlParse<'src, ()> for weedle::IncludesStatementDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { + match first_pass.mixins.get(self.rhs_identifier.0) { + Some(member_lists) => { + for member in member_lists.iter().flat_map(|list| list.iter()) { + member.webidl_parse(program, first_pass, self.lhs_identifier.0)?; + } + } + None => warn!("Tried to include missing mixin {}", self.rhs_identifier.0), + } + Ok(()) + } +} + +impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + (): (), + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if util::is_no_interface_object(&self.extended_attributes) { + if util::is_no_interface_object(&self.attributes) { return Ok(()); } - let doc_comment = Some(format!("The `{}` object\n\n{}", &self.name, mdn_doc(&self.name, None))); + let doc_comment = Some(format!( + "The `{}` object\n\n{}", + self.identifier.0, + mdn_doc(self.identifier.0, None), + )); program.imports.push(backend::ast::Import { module: None, - version: None, js_namespace: None, kind: backend::ast::ImportKind::Type(backend::ast::ImportType { vis: public(), - name: rust_ident(camel_case_ident(&self.name).as_str()), + name: rust_ident(camel_case_ident(self.identifier.0).as_str()), attrs: Vec::new(), doc_comment, }), }); - for extended_attribute in &self.extended_attributes { - extended_attribute.webidl_parse(program, first_pass, self)?; + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.webidl_parse(program, first_pass, self)?; + } } - for member in &self.members { - member.webidl_parse(program, first_pass, &self.name)?; + for member in &self.members.body { + member.webidl_parse(program, first_pass, self.identifier.0)?; } Ok(()) } } -impl WebidlParse<()> for webidl::ast::PartialInterface { +impl<'src> WebidlParse<'src, ()> for weedle::PartialInterfaceDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { + if util::is_chrome_only(&self.attributes) { return Ok(()); } - if !first_pass.interfaces.contains_key(&self.name) { + if !first_pass.interfaces.contains_key(self.identifier.0) { warn!( "Partial interface {} missing non-partial interface", - self.name + self.identifier.0 ); } - for member in &self.members { - member.webidl_parse(program, first_pass, &self.name)?; + for member in &self.members.body { + member.webidl_parse(program, first_pass, self.identifier.0)?; } Ok(()) } } -impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::ExtendedAttribute { +impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for ExtendedAttribute<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - interface: &'a webidl::ast::NonPartialInterface, + first_pass: &FirstPassRecord<'src>, + interface: &'src weedle::InterfaceDefinition<'src>, ) -> Result<()> { - let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| { - let arguments = &arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(); - + let mut add_constructor = |arguments: &[Argument], class: &str| { let (overloaded, same_argument_names) = first_pass.get_operation_overloading( arguments, &::first_pass::OperationId::Constructor, - &interface.name, + interface.identifier.0, ); - let self_ty = ident_ty(rust_ident(camel_case_ident(&interface.name).as_str())); + let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str())); let kind = backend::ast::ImportFunctionKind::Method { class: class.to_string(), @@ -340,9 +332,7 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte "new", overloaded, same_argument_names, - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + arguments, Some(self_ty), kind, structural, @@ -354,34 +344,68 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte }; match self { - webidl::ast::ExtendedAttribute::ArgumentList( - webidl::ast::ArgumentListExtendedAttribute { arguments, name }, - ) - if name == "Constructor" => + ExtendedAttribute::ArgList(list) + if list.identifier.0 == "Constructor" => { - add_constructor(arguments, &interface.name) + add_constructor(&list.args.body.list, interface.identifier.0) } - webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) - if name == "Constructor" => + ExtendedAttribute::NoArgs(other) if (other.0).0 == "Constructor" => { + add_constructor(&[], interface.identifier.0) + } + ExtendedAttribute::NamedArgList(list) + if list.lhs_identifier.0 == "NamedConstructor" => { - add_constructor(&[], &interface.name) + add_constructor(&list.args.body.list, list.rhs_identifier.0) } - webidl::ast::ExtendedAttribute::NamedArgumentList( - webidl::ast::NamedArgumentListExtendedAttribute { - lhs_name, - rhs_arguments, - rhs_name, - }, - ) - if lhs_name == "NamedConstructor" => - { - add_constructor(rhs_arguments, rhs_name) - } - webidl::ast::ExtendedAttribute::ArgumentList(_) - | webidl::ast::ExtendedAttribute::Identifier(_) - | webidl::ast::ExtendedAttribute::IdentifierList(_) - | webidl::ast::ExtendedAttribute::NamedArgumentList(_) - | webidl::ast::ExtendedAttribute::NoArguments(_) => { + + // these appear to be mapping to gecko preferences, seems like we + // can safely ignore + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Pref" => {} + + // looks to be a gecko-specific attribute to tie WebIDL back to C++ + // functions perhaps + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Func" => {} + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "JSImplementation" => {} + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "HeaderFile" => {} + + // Not actually mentioned in the spec and presumably a hint to + // Gecko's JS engine? Unsure, but seems like it doesn't matter to us + ExtendedAttribute::NoArgs(id) + if (id.0).0 == "ProbablyShortLivingWrapper" => {} + + // Indicates something about enumerable properties, we're not too + // interested in it + // https://heycam.github.io/webidl/#LegacyUnenumerableNamedProperties + ExtendedAttribute::NoArgs(id) + if (id.0).0 == "LegacyUnenumerableNamedProperties" => {} + + // Indicates where objects are defined (web workers and such), we + // may later want to use this for cfgs but for now we ignore it. + // https://heycam.github.io/webidl/#Exposed + ExtendedAttribute::Ident(id) if id.lhs_identifier.0 == "Exposed" => {} + ExtendedAttribute::IdentList(id) if id.identifier.0 == "Exposed" => {} + + // We handle this with the "structural" attribute elsewhere + ExtendedAttribute::IdentList(id) if id.identifier.0 == "Global" => {} + + // Seems like it's safe to ignore for now, just telling us where a + // binding appears + // https://heycam.github.io/webidl/#SecureContext + ExtendedAttribute::NoArgs(id) if (id.0).0 == "SecureContext" => {} + + // We handle this elsewhere + ExtendedAttribute::NoArgs(id) if (id.0).0 == "Unforgeable" => {} + + // Looks like this attribute just says that we can't call the + // constructor + // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor + ExtendedAttribute::NoArgs(id) if (id.0).0 == "HTMLConstructor" => {} + + ExtendedAttribute::ArgList(_) + | ExtendedAttribute::Ident(_) + | ExtendedAttribute::IdentList(_) + | ExtendedAttribute::NamedArgList(_) + | ExtendedAttribute::NoArgs(_) => { warn!("Unsupported WebIDL extended attribute: {:?}", self); } } @@ -390,29 +414,32 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte } } -impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::InterfaceMember<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { + use weedle::interface::InterfaceMember::*; + match self { - webidl::ast::InterfaceMember::Attribute(attr) => { + Attribute(attr) => { attr.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Operation(op) => { + Operation(op) => { op.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Const(cnst) => { + Const(cnst) => { cnst.webidl_parse(program, first_pass, self_name) } - webidl::ast::InterfaceMember::Iterable(iterable) => { + Iterable(iterable) => { iterable.webidl_parse(program, first_pass, self_name) } // TODO - | webidl::ast::InterfaceMember::Maplike(_) - | webidl::ast::InterfaceMember::Setlike(_) => { + | Maplike(_) + | Stringifier(_) + | Setlike(_) => { warn!("Unsupported WebIDL interface member: {:?}", self); Ok(()) } @@ -420,129 +447,250 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::MixinMember { +impl<'a, 'src> WebidlParse<'src, &'a str> for weedle::mixin::MixinMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, + first_pass: &FirstPassRecord<'src>, self_name: &'a str, ) -> Result<()> { match self { - webidl::ast::MixinMember::Attribute(attr) => { + weedle::mixin::MixinMember::Attribute(attr) => { attr.webidl_parse(program, first_pass, self_name) } - webidl::ast::MixinMember::Operation(op) => { + weedle::mixin::MixinMember::Operation(op) => { op.webidl_parse(program, first_pass, self_name) } // TODO - webidl::ast::MixinMember::Const(_) => { - warn!("Unsupported WebIDL interface member: {:?}", self); - Ok(()) - } - } - } -} -impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - match self { - webidl::ast::Attribute::Regular(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - webidl::ast::Attribute::Static(attr) => { - attr.webidl_parse(program, first_pass, self_name) - } - // TODO - webidl::ast::Attribute::Stringifier(_) => { - warn!("Unsupported WebIDL attribute: {:?}", self); + weedle::mixin::MixinMember::Stringifier(_) | + weedle::mixin::MixinMember::Const(_) => { + warn!("Unsupported WebIDL mixin member: {:?}", self); Ok(()) } } } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::AttributeInterfaceMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - match self { - webidl::ast::Operation::Regular(op) => op.webidl_parse(program, first_pass, self_name), - webidl::ast::Operation::Static(op) => op.webidl_parse(program, first_pass, self_name), - webidl::ast::Operation::Special(op) => op.webidl_parse(program, first_pass, self_name), - // TODO - webidl::ast::Operation::Stringifier(_) => { - warn!("Unsupported WebIDL operation: {:?}", self); - Ok(()) - } - } + member_attribute( + program, + first_pass, + self_name, + &self.attributes, + self.modifier, + self.readonly.is_some(), + &self.type_, + self.identifier.0, + ) } } -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { +impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::AttributeMixinMember<'src> { fn webidl_parse( &self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); + member_attribute( + program, + first_pass, + self_name, + &self.attributes, + if let Some(s) = self.stringifier { + Some(weedle::interface::StringifierOrInheritOrStatic::Stringifier(s)) + } else { + None + }, + self.readonly.is_some(), + &self.type_, + self.identifier.0, + ) + } +} + +fn member_attribute<'src>( + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + attrs: &'src Option, + modifier: Option, + readonly: bool, + type_: &'src weedle::types::AttributedType<'src>, + identifier: &'src str, +) -> Result<()> { + use weedle::interface::StringifierOrInheritOrStatic::*; + + if util::is_chrome_only(attrs) { + return Ok(()); + } + + let statik = match modifier { + Some(Stringifier(_)) => { + warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); + return Ok(()) } + Some(Inherit(_)) => false, + Some(Static(_)) => true, + None => false, + }; - let is_structural = util::is_structural(&self.extended_attributes); - let throws = util::throws(&self.extended_attributes); + if type_.attributes.is_some() { + warn!("Unsupported attributes on type {:?}", (self_name, identifier)); + return Ok(()) + } + let is_structural = util::is_structural(attrs); + let throws = util::throws(attrs); + + first_pass + .create_getter( + identifier, + &type_.type_, + self_name, + statik, + is_structural, + throws, + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + + if !readonly { first_pass - .create_getter( - &self.name, - &self.type_.apply_typedefs(first_pass), + .create_setter( + identifier, + type_.type_.clone(), self_name, - false, + statik, is_structural, throws, ) .map(wrap_import_function) .map(|import| program.imports.push(import)); + } - if !self.read_only { - first_pass - .create_setter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - false, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - } + Ok(()) +} - Ok(()) +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::OperationInterfaceMember<'src> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + ) -> Result<()> { + member_operation( + program, + first_pass, + self_name, + &self.attributes, + self.modifier, + &self.specials, + &self.return_type, + &self.args.body.list, + &self.identifier, + ) } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Iterable { +impl<'src> WebidlParse<'src, &'src str> for weedle::mixin::OperationMixinMember<'src> { + fn webidl_parse( + &self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + ) -> Result<()> { + member_operation( + program, + first_pass, + self_name, + &self.attributes, + None, + &[], + &self.return_type, + &self.args.body.list, + &self.identifier, + ) + } +} + +fn member_operation<'src>( + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + self_name: &'src str, + attrs: &'src Option, + modifier: Option, + specials: &[weedle::interface::Special], + return_type: &'src weedle::types::ReturnType<'src>, + args: &'src [Argument], + identifier: &Option>, +) -> Result<()> { + use weedle::interface::StringifierOrStatic::*; + + if util::is_chrome_only(attrs) { + return Ok(()); + } + let statik = match modifier { + Some(Stringifier(_)) => { + warn!("Unsupported stringifier on type {:?}", (self_name, identifier)); + return Ok(()) + } + Some(Static(_)) => true, + None => false, + }; + + first_pass + .create_basic_method( + args, + match identifier.map(|s| s.0) { + None if specials.is_empty() => ::first_pass::OperationId::Operation(None), + None if specials.len() == 1 => match specials[0] { + weedle::interface::Special::Getter(weedle::term::Getter) => ::first_pass::OperationId::SpecialGetter, + weedle::interface::Special::Setter(weedle::term::Setter) => ::first_pass::OperationId::SpecialSetter, + weedle::interface::Special::Deleter(weedle::term::Deleter) => ::first_pass::OperationId::SpecialDeleter, + weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()), + }, + Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())), + _ => { + warn!("Unsupported specials on type {:?}", (self_name, identifier)); + return Ok(()) + } + }, + return_type, + self_name, + statik, + specials.len() == 1 || first_pass + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false), + util::throws(attrs), + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + Ok(()) +} + +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::IterableInterfaceMember<'src> { fn webidl_parse( &self, _program: &mut backend::ast::Program, - _first_pass: &FirstPassRecord<'_>, - _self_name: &'a str, + _first_pass: &FirstPassRecord<'src>, + _self_name: &'src str, ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } + // if util::is_chrome_only(&self.attributes) { + // return Ok(()); + // } /* TODO let throws = util::throws(&self.extended_attributes); - let return_value = webidl::ast::ReturnType::NonVoid(self.value_type.clone()); + let return_value = weedle::ReturnType::NonVoid(self.value_type.clone()); let args = []; first_pass .create_basic_method( @@ -573,191 +721,31 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Iterable { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute { +impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - let is_structural = util::is_structural(&self.extended_attributes); - let throws = util::throws(&self.extended_attributes); - - first_pass - .create_getter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - true, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - if !self.read_only { - first_pass - .create_setter( - &self.name, - &self.type_.apply_typedefs(first_pass), - self_name, - true, - is_structural, - throws, - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - } - - Ok(()) - } -} - -impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - first_pass - .create_basic_method( - &self - .arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(), - ::first_pass::OperationId::Operation(self.name.clone()), - &self.return_type.apply_typedefs(first_pass), - self_name, - false, - first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(&self.extended_attributes), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - Ok(()) - } -} - -impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - first_pass - .create_basic_method( - &self - .arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(), - ::first_pass::OperationId::Operation(self.name.clone()), - &self.return_type.apply_typedefs(first_pass), - self_name, - true, - first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(&self.extended_attributes), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - Ok(()) - } -} - -impl<'a> WebidlParse<&'a str> for webidl::ast::SpecialOperation { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, - ) -> Result<()> { - if util::is_chrome_only(&self.extended_attributes) { - return Ok(()); - } - - first_pass - .create_basic_method( - &self - .arguments - .iter() - .map(|argument| argument.apply_typedefs(first_pass)) - .collect::>(), - match self.name { - None => match self.special_keywords.iter().next() { - Some(webidl::ast::Special::Getter) => ::first_pass::OperationId::SpecialGetter, - Some(webidl::ast::Special::Setter) => ::first_pass::OperationId::SpecialSetter, - Some(webidl::ast::Special::Deleter) => ::first_pass::OperationId::SpecialDeleter, - Some(webidl::ast::Special::LegacyCaller) => return Ok(()), - None => { - panic!("unsupported special operation: {:?} of {}", self, self_name); - } - }, - Some(ref name) => ::first_pass::OperationId::Operation(Some(name.clone())), - }, - &self.return_type.apply_typedefs(first_pass), - self_name, - false, - true, - util::throws(&self.extended_attributes), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); - - Ok(()) - } -} - -impl<'a> WebidlParse<()> for webidl::ast::Enum { - fn webidl_parse( - &self, - program: &mut backend::ast::Program, - _: &FirstPassRecord<'_>, + _: &FirstPassRecord<'src>, (): (), ) -> Result<()> { + let variants = &self.values.body.list; program.imports.push(backend::ast::Import { module: None, - version: None, js_namespace: None, kind: backend::ast::ImportKind::Enum(backend::ast::ImportEnum { vis: public(), - name: rust_ident(camel_case_ident(&self.name).as_str()), - variants: self - .variants + name: rust_ident(camel_case_ident(self.identifier.0).as_str()), + variants: variants .iter() - .map(|v| - if !v.is_empty() { - rust_ident(camel_case_ident(&v).as_str()) + .map(|v| { + if !v.0.is_empty() { + rust_ident(camel_case_ident(&v.0).as_str()) } else { rust_ident("None") } - ) + }) .collect(), - variant_values: self.variants.clone(), + variant_values: variants.iter().map(|v| v.0.to_string()).collect(), rust_attrs: vec![parse_quote!(#[derive(Copy, Clone, PartialEq, Debug)])], }), }); @@ -766,21 +754,24 @@ impl<'a> WebidlParse<()> for webidl::ast::Enum { } } -impl<'a> WebidlParse<&'a str> for webidl::ast::Const { +impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> { fn webidl_parse( - &self, + &'src self, program: &mut backend::ast::Program, - first_pass: &FirstPassRecord<'_>, - self_name: &'a str, + record: &FirstPassRecord<'src>, + self_name: &'src str, ) -> Result<()> { - let ty = webidl_const_ty_to_syn_ty(&self.type_.apply_typedefs(first_pass)); + let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { + Some(s) => s, + None => return Ok(()), + }; program.consts.push(backend::ast::Const { vis: public(), - name: rust_ident(self.name.to_shouty_snake_case().as_str()), + name: rust_ident(self.identifier.0.to_shouty_snake_case().as_str()), class: Some(rust_ident(camel_case_ident(&self_name).as_str())), ty, - value: webidl_const_v_to_backend_const_v(&self.value), + value: webidl_const_v_to_backend_const_v(&self.const_value), }); Ok(()) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 18379cb4..9c7f9846 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,12 +1,16 @@ -use std::iter::{self, FromIterator}; +use std::iter::FromIterator; use backend; use backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident}; use heck::{CamelCase, SnakeCase}; use proc_macro2::Ident; use syn; -use webidl; -use webidl::ast::ExtendedAttribute; +use weedle; +use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute}; +use weedle::argument::{Argument, SingleArgument}; +use weedle::common::Identifier; +use weedle::types::*; +use weedle::literal::{ConstValue, FloatLit, IntegerLit}; use first_pass::FirstPassRecord; @@ -34,35 +38,392 @@ pub fn mdn_doc(class: &str, method: Option<&str>) -> String { format!("[Documentation]({})", link).into() } -/// For a webidl const type node, get the corresponding syn type node. -pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type { - use webidl::ast::ConstType::*; +pub(crate) trait ToSynType<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option; +} - // similar to webidl_ty_to_syn_ty - match ty { - Boolean => ident_ty(raw_ident("bool")), - Byte => ident_ty(raw_ident("i8")), - Octet => ident_ty(raw_ident("u8")), - RestrictedDouble | UnrestrictedDouble => ident_ty(raw_ident("f64")), - RestrictedFloat | UnrestrictedFloat => ident_ty(raw_ident("f32")), - SignedLong => ident_ty(raw_ident("i32")), - SignedLongLong => ident_ty(raw_ident("i64")), - SignedShort => ident_ty(raw_ident("i16")), - UnsignedLong => ident_ty(raw_ident("u32")), - UnsignedLongLong => ident_ty(raw_ident("u64")), - UnsignedShort => ident_ty(raw_ident("u16")), - Identifier(ref id) => ident_ty(rust_ident(id)), +impl<'src, T: ToSynType<'src>> ToSynType<'src> for MayBeNull { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + let ty = self.type_.to_syn_type(record, pos)?; + if self.q_mark.is_some() { + Some(option_ty(ty)) + } else { + Some(ty) + } } } +impl<'src> ToSynType<'src> for ConstType<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + ConstType::Integer(l) => l.to_syn_type(record, pos), + ConstType::FloatingPoint(l) => l.to_syn_type(record, pos), + ConstType::Boolean(l) => l.to_syn_type(record, pos), + ConstType::Byte(l) => l.to_syn_type(record, pos), + ConstType::Octet(l) => l.to_syn_type(record, pos), + ConstType::Identifier(l) => l.to_syn_type(record, pos), + } + } +} + +impl<'src> ToSynType<'src> for IntegerType { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + match self { + IntegerType::LongLong(l) => l.to_syn_type(record, pos), + IntegerType::Long(l) => l.to_syn_type(record, pos), + IntegerType::Short(l) => l.to_syn_type(record, pos), + } + } +} + +impl<'src> ToSynType<'src> for ShortType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u16"))) + } else { + Some(ident_ty(raw_ident("i16"))) + } + } +} + +impl<'src> ToSynType<'src> for LongType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u32"))) + } else { + Some(ident_ty(raw_ident("i32"))) + } + } +} + +impl<'src> ToSynType<'src> for LongLongType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + if self.unsigned.is_some() { + Some(ident_ty(raw_ident("u64"))) + } else { + Some(ident_ty(raw_ident("i64"))) + } + } +} + +impl<'src> ToSynType<'src> for FloatingPointType { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + match self { + FloatingPointType::Float(_) => Some(ident_ty(raw_ident("f32"))), + FloatingPointType::Double(_) => Some(ident_ty(raw_ident("f64"))), + } + } +} + +impl<'src> ToSynType<'src> for weedle::term::Boolean { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("bool"))) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Byte { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("i8"))) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Octet { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + Some(ident_ty(raw_ident("u8"))) + } +} + +impl<'src> ToSynType<'src> for weedle::common::Identifier<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + if let Some(other) = record.typedefs.get(&self.0) { + return other.to_syn_type(record, pos) + } + // A reference to a type by name becomes the same thing in the + // bindings. + let ty = ident_ty(rust_ident(camel_case_ident(self.0).as_str())); + Some(if record.interfaces.contains_key(self.0) { + if pos == TypePosition::Argument { + shared_ref(ty) + } else { + ty + } + } else if record.dictionaries.contains(self.0) { + ty + } else if record.enums.contains(self.0) { + ty + } else { + warn!("unrecognized type {}", self.0); + ty + }) + } +} + +impl<'src> ToSynType<'src> for weedle::term::DOMString { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // strings -> `&str` for arguments and `String` for return + Some(match pos { + TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), + TypePosition::Return => ident_ty(raw_ident("String")), + }) + } +} + +impl<'src> ToSynType<'src> for weedle::term::ByteString { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // ByteString maps to String in JS - + // https://developer.mozilla.org/en-US/docs/Web/API/ByteString + weedle::term::DOMString.to_syn_type(record, pos) + } +} + +impl<'src> ToSynType<'src> for weedle::term::USVString { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + // USVString maps to String in JS - + // https://developer.mozilla.org/en-US/docs/Web/API/USVString + weedle::term::DOMString.to_syn_type(record, pos) + } +} + +// Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). +fn array(base_ty: &str, pos: TypePosition) -> syn::Type { + match pos { + TypePosition::Argument => { + shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) + } + TypePosition::Return => { + vec_ty(ident_ty(raw_ident(base_ty))) + } + } +} + +impl<'src> ToSynType<'src> for weedle::term::Float32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("f32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Float64Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("f64", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int8Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int16Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i16", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Int32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("i32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint8Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint8ClampedArray { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u8", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint16Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u16", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Uint32Array { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + Some(array("u32", pos)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::ArrayBuffer { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; + Some(leading_colon_path_ty(path)) + } +} + +impl<'src> ToSynType<'src> for weedle::term::Object { + fn to_syn_type(&self, _record: &FirstPassRecord<'src>, _pos: TypePosition) + -> Option + { + let path = vec![rust_ident("js_sys"), rust_ident("Object")]; + Some(leading_colon_path_ty(path)) + } +} + +impl<'src> ToSynType<'src> for weedle::types::Type<'src> { + fn to_syn_type(&self, record: &FirstPassRecord<'src>, pos: TypePosition) + -> Option + { + use weedle::types::NonAnyType::*; + let single = match self { + Type::Single(s) => s, + Type::Union(_) => return None, + }; + + let ty = match single { + // `any` becomes `::wasm_bindgen::JsValue`. + SingleType::Any(_) => { + let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; + return Some(leading_colon_path_ty(path)) + } + SingleType::NonAny(other) => other, + }; + + match ty { + Boolean(s) => s.to_syn_type(record, pos), + Octet(s) => s.to_syn_type(record, pos), + Byte(s) => s.to_syn_type(record, pos), + Identifier(s) => s.to_syn_type(record, pos), + Integer(s) => s.to_syn_type(record, pos), + FloatingPoint(s) => s.to_syn_type(record, pos), + + Float32Array(s) => s.to_syn_type(record, pos), + Float64Array(s) => s.to_syn_type(record, pos), + Int8Array(s) => s.to_syn_type(record, pos), + Int16Array(s) => s.to_syn_type(record, pos), + Int32Array(s) => s.to_syn_type(record, pos), + Uint8Array(s) => s.to_syn_type(record, pos), + Uint8ClampedArray(s) => s.to_syn_type(record, pos), + Uint16Array(s) => s.to_syn_type(record, pos), + Uint32Array(s) => s.to_syn_type(record, pos), + + DOMString(s) => s.to_syn_type(record, pos), + ByteString(s) => s.to_syn_type(record, pos), + USVString(s) => s.to_syn_type(record, pos), + ArrayBuffer(b) => b.to_syn_type(record, pos), + Object(o) => o.to_syn_type(record, pos), + + // Support for these types is not yet implemented, so skip + // generating any bindings for this function. + | DataView(_) + | Error(_) + | FrozenArrayType(_) + | Promise(_) + | RecordType(..) + | Sequence(_) + | Symbol(_) => { + None + } + } + } +} + + /// Map a webidl const value to the correct wasm-bindgen const value -pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend::ast::ConstValue { +pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> backend::ast::ConstValue { + use std::f64::{NEG_INFINITY, INFINITY, NAN}; + use backend::ast; + match *v { - webidl::ast::ConstValue::BooleanLiteral(b) => backend::ast::ConstValue::BooleanLiteral(b), - webidl::ast::ConstValue::FloatLiteral(f) => backend::ast::ConstValue::FloatLiteral(f), - webidl::ast::ConstValue::SignedIntegerLiteral(i) => backend::ast::ConstValue::SignedIntegerLiteral(i), - webidl::ast::ConstValue::UnsignedIntegerLiteral(u) => backend::ast::ConstValue::UnsignedIntegerLiteral(u), - webidl::ast::ConstValue::Null => backend::ast::ConstValue::Null, + ConstValue::Boolean(b) => ast::ConstValue::BooleanLiteral(b.0), + ConstValue::Float(FloatLit::NegInfinity(_)) => { + ast::ConstValue::FloatLiteral(NEG_INFINITY) + } + ConstValue::Float(FloatLit::Infinity(_)) => { + ast::ConstValue::FloatLiteral(INFINITY) + } + ConstValue::Float(FloatLit::NaN(_)) => { + ast::ConstValue::FloatLiteral(NAN) + } + ConstValue::Float(FloatLit::Value(s)) => { + ast::ConstValue::FloatLiteral(s.0.parse().unwrap()) + } + ConstValue::Integer(lit) => { + let mklit = |orig_text: &str, base: u32, offset: usize| { + let (negative, text) = if orig_text.starts_with("-") { + (true, &orig_text[1..]) + } else { + (false, orig_text) + }; + if text == "0" { + return ast::ConstValue::SignedIntegerLiteral(0) + } + let text = &text[offset..]; + let n = u64::from_str_radix(text, base) + .unwrap_or_else(|_| panic!("literal too big: {}", orig_text)); + if negative { + let n = if n > (i64::min_value() as u64).wrapping_neg() { + panic!("literal too big: {}", orig_text) + } else { + n.wrapping_neg() as i64 + }; + ast::ConstValue::SignedIntegerLiteral(n) + } else { + ast::ConstValue::UnsignedIntegerLiteral(n) + } + }; + match lit { + IntegerLit::Hex(h) => mklit(h.0, 16, 2), // leading 0x + IntegerLit::Oct(h) => mklit(h.0, 8, 1), // leading 0 + IntegerLit::Dec(h) => mklit(h.0, 10, 0), + } + } + ConstValue::Null(_) => ast::ConstValue::Null, } } @@ -135,6 +496,22 @@ fn vec_ty(t: syn::Type) -> syn::Type { ty.into() } +/// From `T` create `Option` +fn option_ty(t: syn::Type) -> syn::Type { + let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + colon2_token: None, + lt_token: Default::default(), + args: FromIterator::from_iter(vec![syn::GenericArgument::Type(t)]), + gt_token: Default::default(), + }); + + let ident = raw_ident("Option"); + let seg = syn::PathSegment { ident, arguments }; + let path: syn::Path = seg.into(); + let ty = syn::TypePath { qself: None, path }; + ty.into() +} + /// Possible positions for a type in a function signature. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TypePosition { @@ -142,350 +519,280 @@ pub enum TypePosition { Return, } -fn type_kind_to_const_type(type_kind: &webidl::ast::TypeKind) -> webidl::ast::ConstType { - match type_kind { - webidl::ast::TypeKind::Boolean => webidl::ast::ConstType::Boolean, - webidl::ast::TypeKind::Byte => webidl::ast::ConstType::Byte, - webidl::ast::TypeKind::Identifier(identifier) => webidl::ast::ConstType::Identifier(identifier.clone()), - webidl::ast::TypeKind::Octet => webidl::ast::ConstType::Octet, - webidl::ast::TypeKind::RestrictedDouble => webidl::ast::ConstType::RestrictedDouble, - webidl::ast::TypeKind::RestrictedFloat => webidl::ast::ConstType::RestrictedFloat, - webidl::ast::TypeKind::SignedLong => webidl::ast::ConstType::SignedLong, - webidl::ast::TypeKind::SignedLongLong => webidl::ast::ConstType::SignedLongLong, - webidl::ast::TypeKind::SignedShort => webidl::ast::ConstType::SignedShort, - webidl::ast::TypeKind::UnrestrictedDouble => webidl::ast::ConstType::UnrestrictedDouble, - webidl::ast::TypeKind::UnrestrictedFloat => webidl::ast::ConstType::UnrestrictedFloat, - webidl::ast::TypeKind::UnsignedLong => webidl::ast::ConstType::UnsignedLong, - webidl::ast::TypeKind::UnsignedLongLong => webidl::ast::ConstType::UnsignedLongLong, - webidl::ast::TypeKind::UnsignedShort => webidl::ast::ConstType::UnsignedShort, - _ => panic!("can not convert TypeKind to ConstType: {:#?}", type_kind), - } -} - -/// Implemented on an AST type node to apply typedefs. -pub(crate) trait ApplyTypedefs { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self; -} - -impl ApplyTypedefs for webidl::ast::Type { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - webidl::ast::Type { - extended_attributes: self.extended_attributes.clone(), - kind: self.kind.apply_typedefs(record), - nullable: self.nullable, - } - } -} - -impl ApplyTypedefs for webidl::ast::ReturnType { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::ReturnType::NonVoid(ty) => - webidl::ast::ReturnType::NonVoid(Box::new(ty.apply_typedefs(record))), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::StringType { - fn apply_typedefs<'a>(&self, _: &FirstPassRecord<'a>) -> Self { - *self - } -} - -impl ApplyTypedefs for webidl::ast::ConstType { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::ConstType::Identifier(identifier) => - record - .typedefs - .get(identifier) - .map(|ty| type_kind_to_const_type(&ty.kind)) - .unwrap_or(self.clone()), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::TypeKind { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - match self { - webidl::ast::TypeKind::FrozenArray(ty) => - webidl::ast::TypeKind::FrozenArray(Box::new(ty.apply_typedefs(record))), - webidl::ast::TypeKind::Identifier(identifier) => record - .typedefs - .get(identifier) - .map(|ty| ty.kind.clone()) - .unwrap_or(self.clone()), - webidl::ast::TypeKind::Promise(ty) => - webidl::ast::TypeKind::Promise(ty.apply_typedefs(record)), - webidl::ast::TypeKind::Record(string_type, ty) => webidl::ast::TypeKind::Record( - string_type.apply_typedefs(record), - Box::new(ty.apply_typedefs(record)), - ), - webidl::ast::TypeKind::Union(types) => webidl::ast::TypeKind::Union( - types - .iter() - .map(|ty| Box::new(ty.apply_typedefs(record))) - .collect(), - ), - _ => self.clone(), - } - } -} - -impl ApplyTypedefs for webidl::ast::Argument { - fn apply_typedefs<'a>(&self, record: &FirstPassRecord<'a>) -> Self { - webidl::ast::Argument { - extended_attributes: self.extended_attributes.clone(), - default: self.default.clone(), - name: self.name.clone(), - optional: self.optional, - type_: Box::new(self.type_.apply_typedefs(record)), - variadic: self.variadic, - } - } -} - /// Implemented on an AST type node to generate a snake case name. trait TypeToString { - fn type_to_string(&self) -> String; + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String); } -impl TypeToString for webidl::ast::Type { - fn type_to_string(&self) -> String { - if self.nullable { - "opt_".to_owned() + &self.kind.type_to_string() - } else { - self.kind.type_to_string() +impl TypeToString for MayBeNull { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + if self.q_mark.is_some() { + dst.push_str("opt_"); } + self.type_.type_to_string(record, dst); } } -impl TypeToString for webidl::ast::ReturnType { - fn type_to_string(&self) -> String { +impl<'src> TypeToString for weedle::types::ReturnType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::ReturnType::NonVoid(ty) => (*ty).type_to_string(), - webidl::ast::ReturnType::Void => "void".to_owned(), + weedle::types::ReturnType::Type(ty) => (*ty).type_to_string(record, dst), + weedle::types::ReturnType::Void(_) => dst.push_str("void"), } } } -impl TypeToString for webidl::ast::StringType { - fn type_to_string(&self) -> String { +impl TypeToString for weedle::types::StringType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::StringType::ByteString => "byte_str".to_owned(), - webidl::ast::StringType::DOMString => "dom_str".to_owned(), - webidl::ast::StringType::USVString => "usv_str".to_owned(), + weedle::types::StringType::Byte(_) => dst.push_str("byte_str"), + weedle::types::StringType::DOM(_) => dst.push_str("dom_str"), + weedle::types::StringType::USV(_) => dst.push_str("usv_str"), } } } -impl TypeToString for webidl::ast::TypeKind { - fn type_to_string(&self) -> String { +impl TypeToString for weedle::term::Byte { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i8"); + } +} + +impl TypeToString for weedle::term::Octet { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8"); + } +} + +impl TypeToString for weedle::term::Boolean { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("bool"); + } +} + +impl TypeToString for weedle::term::USVString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("usv_str"); + } +} + +impl TypeToString for weedle::term::ByteString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("byte_str"); + } +} + +impl TypeToString for weedle::term::DOMString { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("dom_str"); + } +} + +impl TypeToString for weedle::term::Float32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("f32_array"); + } +} + +impl TypeToString for weedle::term::Float64Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("f64_array"); + } +} + +impl TypeToString for weedle::term::Int8Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i8_array"); + } +} + +impl TypeToString for weedle::term::Int16Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i16_array"); + } +} + +impl TypeToString for weedle::term::Int32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("i32_array"); + } +} + +impl TypeToString for weedle::term::Uint8Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8_array"); + } +} + +impl TypeToString for weedle::term::Uint8ClampedArray { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u8_clamped_array"); + } +} + +impl TypeToString for weedle::term::Uint16Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u16_array"); + } +} + +impl TypeToString for weedle::term::Uint32Array { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("u32_array"); + } +} + +impl<'src> TypeToString for weedle::common::Identifier<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + match record.typedefs.get(self.0) { + Some(other) => other.type_to_string(record, dst), + None => dst.push_str(&self.0.to_snake_case()), + } + } +} + +impl TypeToString for IntegerType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { match self { - webidl::ast::TypeKind::Any => "any".to_owned(), - webidl::ast::TypeKind::ArrayBuffer => "array_buffer".to_owned(), - webidl::ast::TypeKind::Boolean => "bool".to_owned(), - webidl::ast::TypeKind::Byte => "i8".to_owned(), - webidl::ast::TypeKind::ByteString => "byte_str".to_owned(), - webidl::ast::TypeKind::DOMString => "dom_str".to_owned(), - webidl::ast::TypeKind::DataView => "data_view".to_owned(), - webidl::ast::TypeKind::Error => "error".to_owned(), - webidl::ast::TypeKind::Float32Array => "f32_array".to_owned(), - webidl::ast::TypeKind::Float64Array => "f64_array".to_owned(), - webidl::ast::TypeKind::FrozenArray(ty) => "frozen_array_of_".to_owned() + &ty.type_to_string(), - webidl::ast::TypeKind::Identifier(identifier) => identifier.to_snake_case(), - webidl::ast::TypeKind::Int16Array => "i16_array".to_owned(), - webidl::ast::TypeKind::Int32Array => "i32_array".to_owned(), - webidl::ast::TypeKind::Int8Array => "i8_array".to_owned(), - webidl::ast::TypeKind::Octet => "u8".to_owned(), - webidl::ast::TypeKind::Object => "object".to_owned(), - webidl::ast::TypeKind::Promise(ty) => "promise_of_".to_owned() + &(*ty).type_to_string(), - webidl::ast::TypeKind::Record(string_type, ty) => format!( - "record_from_{}_to_{}", - string_type.type_to_string(), - (*ty).type_to_string() - ), - webidl::ast::TypeKind::RestrictedDouble => "restricted_f64".to_owned(), - webidl::ast::TypeKind::RestrictedFloat => "restricted_f32".to_owned(), - webidl::ast::TypeKind::Sequence(ty) => "sequence_of_".to_owned() + &ty.type_to_string(), - webidl::ast::TypeKind::SignedLong => "i32".to_owned(), - webidl::ast::TypeKind::SignedLongLong => "i64".to_owned(), - webidl::ast::TypeKind::SignedShort => "i16".to_owned(), - webidl::ast::TypeKind::Symbol => "symbol".to_owned(), - webidl::ast::TypeKind::USVString => "usv_str".to_owned(), - webidl::ast::TypeKind::Uint16Array => "u16_array".to_owned(), - webidl::ast::TypeKind::Uint32Array => "u32_array".to_owned(), - webidl::ast::TypeKind::Uint8Array => "u8_array".to_owned(), - webidl::ast::TypeKind::Uint8ClampedArray => "u8_clamped_array".to_owned(), - webidl::ast::TypeKind::Union(types) => "union_of_".to_owned() + &types - .iter() - .map(|ty| (*ty).type_to_string()) - .collect::>() - .join("_and_"), - webidl::ast::TypeKind::UnrestrictedDouble => "unrestricted_f64".to_owned(), - webidl::ast::TypeKind::UnrestrictedFloat => "unrestricted_f32".to_owned(), - webidl::ast::TypeKind::UnsignedLong => "u32".to_owned(), - webidl::ast::TypeKind::UnsignedLongLong => "u64".to_owned(), - webidl::ast::TypeKind::UnsignedShort => "u16".to_owned(), + IntegerType::LongLong(l) if l.unsigned.is_some() => dst.push_str("u64"), + IntegerType::LongLong(_) => dst.push_str("i64"), + IntegerType::Long(l) if l.unsigned.is_some() => dst.push_str("u32"), + IntegerType::Long(_) => dst.push_str("i32"), + IntegerType::Short(l) if l.unsigned.is_some() => dst.push_str("u16"), + IntegerType::Short(_) => dst.push_str("i16"), } } } -impl<'a> FirstPassRecord<'a> { - /// Use information from the first pass to work out the correct Rust type to use for - /// a given WebIDL type. - pub fn webidl_ty_to_syn_ty( - &self, - ty: &webidl::ast::Type, - pos: TypePosition, - ) -> Option { - // Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec`). - let array = |base_ty: &str| { - match pos { - TypePosition::Argument => { - shared_ref(slice_ty(ident_ty(raw_ident(base_ty)))) - } - TypePosition::Return => { - vec_ty(ident_ty(raw_ident(base_ty))) - } - } +impl TypeToString for FloatingPointType { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + match self { + FloatingPointType::Float(_) => dst.push_str("f32"), + FloatingPointType::Double(_) => dst.push_str("f64"), + } + } +} + +impl TypeToString for weedle::term::ArrayBuffer { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("array_buffer"); + } +} + +impl TypeToString for weedle::term::Symbol { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("symbol"); + } +} + +impl TypeToString for weedle::term::Object { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("object"); + } +} + +impl TypeToString for weedle::term::DataView { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("data_view"); + } +} + +impl TypeToString for weedle::term::Error { + fn type_to_string(&self, _record: &FirstPassRecord, dst: &mut String) { + dst.push_str("error"); + } +} + +impl<'src> TypeToString for weedle::types::SequenceType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("seq_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::PromiseType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("promise_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::FrozenArrayType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("frozen_array_"); + self.generics.body.type_to_string(record, dst); + } +} + +impl<'src> TypeToString for weedle::types::RecordType<'src> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + dst.push_str("record_from_"); + self.generics.body.0.type_to_string(record, dst); + dst.push_str("_to_"); + self.generics.body.2.type_to_string(record, dst); + } +} + +impl<'a> TypeToString for weedle::types::Type<'a> { + fn type_to_string(&self, record: &FirstPassRecord, dst: &mut String) { + use weedle::types::NonAnyType::*; + + let single = match self { + Type::Single(s) => s, + Type::Union(_) => panic!("unions not supported"), }; - let base_ty = match ty.kind { - // `any` becomes `::wasm_bindgen::JsValue`. - webidl::ast::TypeKind::Any => { - leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]) - } - - // A reference to a type by name becomes the same thing in the - // bindings. - webidl::ast::TypeKind::Identifier(ref id) => { - let ty = ident_ty(rust_ident(camel_case_ident(&id).as_str())); - if self.interfaces.contains_key(id) { - if pos == TypePosition::Argument { - shared_ref(ty) - } else { - ty - } - } else if self.dictionaries.contains(id) { - ty - } else if self.enums.contains(id) { - ty - } else { - warn!("unrecognized type {}", id); - ty - } - } - - // Scalars. - webidl::ast::TypeKind::Boolean => ident_ty(raw_ident("bool")), - webidl::ast::TypeKind::Byte => ident_ty(raw_ident("i8")), - webidl::ast::TypeKind::Octet => ident_ty(raw_ident("u8")), - webidl::ast::TypeKind::RestrictedDouble | webidl::ast::TypeKind::UnrestrictedDouble => { - ident_ty(raw_ident("f64")) - } - webidl::ast::TypeKind::RestrictedFloat | webidl::ast::TypeKind::UnrestrictedFloat => { - ident_ty(raw_ident("f32")) - } - webidl::ast::TypeKind::SignedLong => ident_ty(raw_ident("i32")), - webidl::ast::TypeKind::SignedLongLong => ident_ty(raw_ident("i64")), - webidl::ast::TypeKind::SignedShort => ident_ty(raw_ident("i16")), - webidl::ast::TypeKind::UnsignedLong => ident_ty(raw_ident("u32")), - webidl::ast::TypeKind::UnsignedLongLong => ident_ty(raw_ident("u64")), - webidl::ast::TypeKind::UnsignedShort => ident_ty(raw_ident("u16")), - - webidl::ast::TypeKind::Float32Array => array("f32"), - webidl::ast::TypeKind::Float64Array => array("f64"), - webidl::ast::TypeKind::Int8Array => array("i8"), - webidl::ast::TypeKind::Int16Array => array("i16"), - webidl::ast::TypeKind::Int32Array => array("i32"), - webidl::ast::TypeKind::Uint8Array => array("u8"), - webidl::ast::TypeKind::Uint8ClampedArray => array("u8"), - webidl::ast::TypeKind::Uint16Array => array("u16"), - webidl::ast::TypeKind::Uint32Array => array("u32"), - - // strings -> `&str` for arguments and `String` for return - // - // Note that DOMString mostly makes sense here, ByteString maps to - // String in JS [1], along with USVString - // - // [1]: https://developer.mozilla.org/en-US/docs/Web/API/ByteString - // [2]: https://developer.mozilla.org/en-US/docs/Web/API/USVString - webidl::ast::TypeKind::DOMString - | webidl::ast::TypeKind::ByteString - | webidl::ast::TypeKind::USVString => { - match pos { - TypePosition::Argument => shared_ref(ident_ty(raw_ident("str"))), - TypePosition::Return => ident_ty(raw_ident("String")), - } - } - - // This seems like a "naively correct" mapping, but the online docs - // are a bit scary in this regard... - // - // https://heycam.github.io/webidl/#es-buffer-source-types - webidl::ast::TypeKind::ArrayBuffer => { - leading_colon_path_ty(vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]) - } - - // The WebIDL `object` maps to the ECMAScript `Object` - // - // https://heycam.github.io/webidl/#es-object - webidl::ast::TypeKind::Object => { - leading_colon_path_ty(vec![rust_ident("js_sys"), rust_ident("Object")]) - } - - // Support for these types is not yet implemented, so skip - // generating any bindings for this function. - | webidl::ast::TypeKind::DataView - | webidl::ast::TypeKind::Error - | webidl::ast::TypeKind::FrozenArray(_) - | webidl::ast::TypeKind::Promise(_) - | webidl::ast::TypeKind::Record(..) - | webidl::ast::TypeKind::Sequence(_) - | webidl::ast::TypeKind::Symbol - | webidl::ast::TypeKind::Union(_) => { - return None; - } + let ty = match single { + SingleType::Any(_) => return dst.push_str("any"), + SingleType::NonAny(other) => other, }; - // Map nullable to an option. - if ty.nullable { - let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - colon2_token: None, - lt_token: Default::default(), - args: FromIterator::from_iter(vec![ - syn::GenericArgument::Type(base_ty), - ]), - gt_token: Default::default(), - }); + match ty { + Boolean(s) => s.type_to_string(record, dst), + Octet(s) => s.type_to_string(record, dst), + Byte(s) => s.type_to_string(record, dst), + Identifier(s) => s.type_to_string(record, dst), + Integer(s) => s.type_to_string(record, dst), + FloatingPoint(s) => s.type_to_string(record, dst), - let ident = raw_ident("Option"); - let seg = syn::PathSegment { ident, arguments }; - let path: syn::Path = seg.into(); - let ty = syn::TypePath { qself: None, path }; - Some(ty.into()) - } else { - Some(base_ty) + Float32Array(s) => s.type_to_string(record, dst), + Float64Array(s) => s.type_to_string(record, dst), + Int8Array(s) => s.type_to_string(record, dst), + Int16Array(s) => s.type_to_string(record, dst), + Int32Array(s) => s.type_to_string(record, dst), + Uint8Array(s) => s.type_to_string(record, dst), + Uint8ClampedArray(s) => s.type_to_string(record, dst), + Uint16Array(s) => s.type_to_string(record, dst), + Uint32Array(s) => s.type_to_string(record, dst), + + DOMString(s) => s.type_to_string(record, dst), + ByteString(s) => s.type_to_string(record, dst), + USVString(s) => s.type_to_string(record, dst), + ArrayBuffer(s) => s.type_to_string(record, dst), + + DataView(s) => s.type_to_string(record, dst), + Error(s) => s.type_to_string(record, dst), + FrozenArrayType(s) => s.type_to_string(record, dst), + Object(s) => s.type_to_string(record, dst), + Promise(s) => s.type_to_string(record, dst), + RecordType(s) => s.type_to_string(record, dst), + Sequence(s) => s.type_to_string(record, dst), + Symbol(s) => s.type_to_string(record, dst), } } +} +impl<'src> FirstPassRecord<'src> { /// Use the first pass to convert webidl function arguments to rust arguments. /// /// `kind` is whether the function is a method, in which case we would need a `self` /// parameter. - fn webidl_arguments_to_syn_arg_captured<'b, I>( + fn webidl_arguments_to_syn_arg_captured( &self, - arguments: I, + arguments: &[Argument], kind: &backend::ast::ImportFunctionKind, ) -> Option> - where - I: Iterator, { - let estimate = arguments.size_hint(); - let len = estimate.1.unwrap_or(estimate.0); let mut res = if let backend::ast::ImportFunctionKind::Method { ty, kind: @@ -495,25 +802,27 @@ impl<'a> FirstPassRecord<'a> { .. } = kind { - let mut res = Vec::with_capacity(len + 1); + let mut res = Vec::with_capacity(arguments.len() + 1); res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone()))); res } else { - Vec::with_capacity(len) + Vec::with_capacity(arguments.len()) }; - for (name, ty, variadic) in arguments { - if variadic { - warn!("Variadic arguments are not supported yet",); - return None; - } - - match self.webidl_ty_to_syn_ty(ty, TypePosition::Argument) { + for argument in arguments { + let argument = match argument { + Argument::Single(arg) => arg, + Argument::Variadic(_) => return None, + }; + match argument.type_.type_.to_syn_type(self, TypePosition::Argument) { None => { - warn!("Argument's type is not yet supported: {:?}", ty); + warn!("Argument's type is not yet supported: {:?}", argument); return None; } - Some(ty) => res.push(simple_fn_arg(rust_ident(&name.to_snake_case()), ty)), + Some(ty) => { + let name = argument.identifier.0.to_snake_case(); + res.push(simple_fn_arg(rust_ident(&name), ty)) + } } } @@ -521,39 +830,38 @@ impl<'a> FirstPassRecord<'a> { } /// Create a wasm-bindgen function, if possible. - pub fn create_function<'b, I>( + pub fn create_function( &self, name: &str, overloaded: bool, same_argument_names: bool, - arguments: I, + arguments: &[Argument], mut ret: Option, kind: backend::ast::ImportFunctionKind, structural: bool, catch: bool, doc_comment: Option, ) -> Option - where - I: Iterator, { - let arguments: Vec<_> = arguments.collect(); + let ast_arguments = self.webidl_arguments_to_syn_arg_captured(arguments, &kind)?; + let rust_name = rust_ident( &if overloaded && !arguments.is_empty() { - let argument_type_names = arguments - .iter() - .map(|&(name, ty, variadic)| { - if same_argument_names { - if variadic { - "variadic_".to_owned() + &ty.type_to_string() - } else { - ty.type_to_string() - } - } else { - name.to_snake_case() - } - }) - .collect::>() - .join("_and_"); + let mut argument_type_names = String::new(); + for arg in arguments { + let arg = match arg { + Argument::Single(single) => single, + Argument::Variadic(_) => return None, + }; + if argument_type_names.len() > 0 { + argument_type_names.push_str("_and_"); + } + if same_argument_names { + arg.type_.type_.type_to_string(self, &mut argument_type_names); + } else { + argument_type_names.push_str(&arg.identifier.0.to_snake_case()); + } + } if name == "new" { "with_".to_owned() + &argument_type_names } else { @@ -563,9 +871,7 @@ impl<'a> FirstPassRecord<'a> { name.to_snake_case() } ); - let name = raw_ident(name); - - let arguments = self.webidl_arguments_to_syn_arg_captured(arguments.into_iter(), &kind)?; + let name = name.to_string(); let js_ret = ret.clone(); @@ -585,7 +891,7 @@ impl<'a> FirstPassRecord<'a> { Some(backend::ast::ImportFunction { function: backend::ast::Function { name, - arguments, + arguments: ast_arguments, ret, rust_attrs: vec![], rust_vis: public(), @@ -603,9 +909,9 @@ impl<'a> FirstPassRecord<'a> { /// Create a wasm-bindgen method, if possible. pub fn create_basic_method( &self, - arguments: &[webidl::ast::Argument], + arguments: &[weedle::argument::Argument], operation_id: ::first_pass::OperationId, - return_type: &webidl::ast::ReturnType, + return_type: &weedle::types::ReturnType, self_name: &str, is_static: bool, structural: bool, @@ -624,7 +930,7 @@ impl<'a> FirstPassRecord<'a> { warn!("Operations without a name are unsupported"); return None; } - Some(ref name) => name.clone(), + Some(name) => name.to_string(), }, ::first_pass::OperationId::SpecialGetter => "get".to_string(), ::first_pass::OperationId::SpecialSetter => "set".to_string(), @@ -647,9 +953,9 @@ impl<'a> FirstPassRecord<'a> { }; let ret = match return_type { - webidl::ast::ReturnType::Void => None, - webidl::ast::ReturnType::NonVoid(ty) => { - match self.webidl_ty_to_syn_ty(ty, TypePosition::Return) { + weedle::types::ReturnType::Void(_) => None, + weedle::types::ReturnType::Type(ty) => { + match ty.to_syn_type(self, TypePosition::Return) { None => { warn!("Operation's return type is not yet supported: {:?}", ty); return None; @@ -676,9 +982,7 @@ impl<'a> FirstPassRecord<'a> { &name, overloaded, same_argument_names, - arguments - .iter() - .map(|arg| (&*arg.name, &*arg.type_, arg.variadic)), + arguments, ret, kind, structural, @@ -691,47 +995,45 @@ impl<'a> FirstPassRecord<'a> { /// whether there overloads with same argument names for given argument types pub fn get_operation_overloading( &self, - arguments: &[webidl::ast::Argument], + arguments: &[weedle::argument::Argument], id: &::first_pass::OperationId, self_name: &str, ) -> (bool, bool) { - self - .interfaces - .get(self_name) - .map(|interface_data| { - interface_data - .operations - .get(id) - .map(|operation_data| - ( - operation_data.overloaded, - *operation_data - .argument_names_same - .get( - &arguments - .iter() - .map(|argument| argument.name.clone()) - .collect::>() - ) - .unwrap_or(&false) - ) - ) - .unwrap_or((false, false)) - }) - .unwrap_or((false, false)) + let data = match self.interfaces.get(self_name) { + Some(data) => data, + None => return (false, false), + }; + let data = match data.operations.get(id) { + Some(data) => data, + None => return (false, false), + }; + let mut names = Vec::with_capacity(arguments.len()); + for arg in arguments { + match arg { + Argument::Single(arg) => names.push(arg.identifier.0), + Argument::Variadic(_) => return (false, false), + } + } + ( + data.overloaded, + *data + .argument_names_same + .get(&names) + .unwrap_or(&false) + ) } /// Create a wasm-bindgen getter method, if possible. pub fn create_getter( &self, name: &str, - ty: &webidl::ast::Type, + ty: &weedle::types::Type, self_name: &str, is_static: bool, is_structural: bool, catch: bool, ) -> Option { - let ret = match self.webidl_ty_to_syn_ty(ty, TypePosition::Return) { + let ret = match ty.to_syn_type(self, TypePosition::Return) { None => { warn!("Attribute's type does not yet support reading: {:?}", ty); return None; @@ -749,14 +1051,14 @@ impl<'a> FirstPassRecord<'a> { }; let doc_comment = Some(format!("The `{}` getter\n\n{}", name, mdn_doc(self_name, Some(name)))); - self.create_function(name, false, false, iter::empty(), ret, kind, is_structural, catch, doc_comment) + self.create_function(name, false, false, &[], ret, kind, is_structural, catch, doc_comment) } /// Create a wasm-bindgen setter method, if possible. pub fn create_setter( &self, name: &str, - ty: &webidl::ast::Type, + ty: weedle::types::Type, self_name: &str, is_static: bool, is_structural: bool, @@ -776,7 +1078,16 @@ impl<'a> FirstPassRecord<'a> { &format!("set_{}", name), false, false, - iter::once((name, ty, false)), + &[Argument::Single(SingleArgument { + attributes: None, + optional: None, + type_: AttributedType { + attributes: None, + type_: ty, + }, + identifier: Identifier(name), + default: None, + })], None, kind, is_structural, @@ -787,41 +1098,35 @@ impl<'a> FirstPassRecord<'a> { } /// Search for an attribute by name in some webidl object's attributes. -fn has_named_attribute(ext_attrs: &[Box], attribute: &str) -> bool { - ext_attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { - name == attribute - } +fn has_named_attribute(list: &Option, attribute: &str) -> bool { + let list = match list { + Some(list) => list, + None => return false, + }; + list.body.list.iter().any(|attr| match attr { + ExtendedAttribute::NoArgs(name) => (name.0).0 == attribute, _ => false, }) } /// ChromeOnly is for things that are only exposed to privileged code in Firefox. -pub fn is_chrome_only(ext_attrs: &[Box]) -> bool { +pub fn is_chrome_only(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs, "ChromeOnly") } /// Whether a webidl object is marked as a no interface object. -pub fn is_no_interface_object(ext_attrs: &[Box]) -> bool { +pub fn is_no_interface_object(ext_attrs: &Option) -> bool { has_named_attribute(ext_attrs, "NoInterfaceObject") } /// Whether a webidl object is marked as structural. -pub fn is_structural(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { - name == "Unforgeable" - } - _ => false, - }) +pub fn is_structural(attrs: &Option) -> bool { + has_named_attribute(attrs, "Unforgeable") } /// Whether a webidl object is marked as throwing. -pub fn throws(attrs: &[Box]) -> bool { - attrs.iter().any(|attr| match &**attr { - ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => name == "Throws", - _ => false, - }) +pub fn throws(attrs: &Option) -> bool { + has_named_attribute(attrs, "Throws") } /// Create a syn `pub` token diff --git a/guide/src/design/import-customization.md b/guide/src/design/import-customization.md index 3db5a505..4f1b6d9a 100644 --- a/guide/src/design/import-customization.md +++ b/guide/src/design/import-customization.md @@ -5,37 +5,6 @@ controlling precisely how imports are imported and what they map to in JS. This section is intended to hopefully be an exhaustive reference of the possibilities! -* `module` and `version` - we've seen `module` so far indicating where we can - import items from but `version` is also allowed: - - ```rust - #[wasm_bindgen(module = "moment", version = "^2.0.0")] - extern { - type Moment; - fn moment() -> Moment; - #[wasm_bindgen(method)] - fn format(this: &Moment) -> String; - } - ``` - - The `module` key is used to configure the module that each item is imported - from. The `version` key does not affect the generated wasm itself but rather - it's an informative directive for tools like [wasm-pack]. Tools like wasm-pack - will generate a `package.json` for you and the `version` listed here, when - `module` is also an NPM package, will correspond to what to write down in - `package.json`. - - In other words the usage of `module` as the name of an NPM package and - `version` as the version requirement allows you to, inline in Rust, depend on - the NPM ecosystem and import functionality from those packages. When bundled - with a tool like [wasm-pack] everything will automatically get wired up with - bundlers and you should be good to go! - - Note that the `version` is *required* if `module` doesn't start with `./`. If - `module` starts with `./` then it is an error to provide a version. - -[wasm-pack]: https://github.com/rustwasm/wasm-pack - * `catch` - this attribute allows catching a JS exception. This can be attached to any imported function and the function must return a `Result` where the `Err` payload is a `JsValue`, like so: diff --git a/package-lock.json b/package-lock.json index efa45e0a..dff028e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -106,9 +106,9 @@ } }, "@types/node": { - "version": "10.5.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.5.tgz", - "integrity": "sha512-6Qnb1gXbp3g1JX9QVJj3A6ORzc9XCyhokxUKaoonHgNXcQhmk8adhotxfkeK8El9TnFeUuH72yI6jQ5nDJKS6w==", + "version": "10.5.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.6.tgz", + "integrity": "sha512-c5Z1j1ysgo4878ptz6gxLcgMfJ6Wf908R3l5KAGabr0XJ72ZFmOCgsaodPpNYTfp4iOrSwgTDvR/BxbFfB4zPQ==", "dev": true }, "@webassemblyjs/ast": { @@ -1759,9 +1759,9 @@ "dev": true }, "eslint": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.2.0.tgz", - "integrity": "sha512-zlggW1qp7/TBjwLfouRoY7eWXrXwJZFqCdIxxh0/LVB/QuuKuIMkzyUZEcDo6LBadsry5JcEMxIqd3H/66CXVg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.3.0.tgz", + "integrity": "sha512-N/tCqlMKkyNvAvLu+zI9AqDasnSLt00K+Hu8kdsERliC9jYEc8ck12XtjvOXrBKu8fK6RrBcN9bat6Xk++9jAg==", "dev": true, "requires": { "ajv": "^6.5.0", @@ -1795,7 +1795,7 @@ "path-is-inside": "^1.0.2", "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^1.1.0", + "regexpp": "^2.0.0", "require-uncached": "^1.0.3", "semver": "^5.5.0", "string.prototype.matchall": "^2.0.0", @@ -2379,7 +2379,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2400,12 +2401,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2420,17 +2423,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2547,7 +2553,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2559,6 +2566,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2573,6 +2581,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2580,12 +2589,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2604,6 +2615,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2684,7 +2696,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2696,6 +2709,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2781,7 +2795,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2817,6 +2832,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2836,6 +2852,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2879,12 +2896,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -3225,9 +3244,9 @@ "dev": true }, "ignore": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.2.tgz", - "integrity": "sha512-uoxnT7PYpyEnsja+yX+7v49B7LXxmzDJ2JALqHH3oEGzpM2U1IGcbfnOr8Dt57z3B/UWs7/iAgPFbmye8m4I0g==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.3.tgz", + "integrity": "sha512-Z/vAH2GGIEATQnBVXMclE2IGV6i0GyVngKThcGZ5kHgHMxLo9Ow2+XHRq1aEKEej5vOF1TPJNbvX6J/anT0M7A==", "dev": true }, "immediate": { @@ -5020,9 +5039,9 @@ } }, "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", + "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", "dev": true }, "remove-trailing-separator": { @@ -6379,9 +6398,9 @@ } }, "webpack": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.4.tgz", - "integrity": "sha512-RqUfwp4qMqv3oFwBQQOoK69C2tdu2FHJEqPABPqgjGDvOIOLqkTOhmmdJjpiRabzNAAH1ahmkA3z4xowlHN+VA==", + "version": "4.16.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.5.tgz", + "integrity": "sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw==", "dev": true, "requires": { "@webassemblyjs/ast": "1.5.13", diff --git a/package.json b/package.json index 45f457e3..ef89d68b 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,14 @@ "run-lint-generated-tests": "eslint ./target/generated-tests/*/out*js" }, "devDependencies": { - "@types/node": "^10.5.5", + "@types/node": "^10.5.6", "babel-eslint": "^8.2.6", - "eslint": "^5.2.0", + "eslint": "^5.3.0", "geckodriver": "^1.12.1", "selenium-webdriver": "^4.0.0-alpha.1", "ts-loader": "^4.4.2", "typescript": "^3.0.1", - "webpack": "^4.16.4", + "webpack": "^4.16.5", "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.5" } diff --git a/tests/all/dependencies.rs b/tests/all/dependencies.rs deleted file mode 100644 index 624ca000..00000000 --- a/tests/all/dependencies.rs +++ /dev/null @@ -1,209 +0,0 @@ -use super::project; - -#[test] -fn dependencies_work() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - extern crate dependency; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn return_dep_ty(x: f64) -> dependency::Foo { - dependency::Foo(x) - } - - #[wasm_bindgen] - pub fn takes_own_dep_ty(foo: dependency::Foo) -> f64 { - foo.0 - } - - #[wasm_bindgen] - pub fn takes_ref_dep_ty(foo: &dependency::Foo) -> f64 { - foo.0 - } - - #[wasm_bindgen] - pub fn takes_mut_dep_ty(foo: &mut dependency::Foo, x: f64) { - foo.0 = x; - } - "#, - ) - .add_local_dependency("dependency", "vendor/dependency") - .file( - "vendor/dependency/Cargo.toml", - &format!( - r#" - [package] - name = "dependency" - version = "0.0.1" - authors = [] - - [dependencies] - wasm-bindgen = {{ path = '{}' }} - "#, - env!("CARGO_MANIFEST_DIR") - ), - ) - .file( - "vendor/dependency/src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub struct Foo(pub f64); - - #[wasm_bindgen] - impl Foo { - pub fn new(x: f64) -> Foo { Foo(x) } - pub fn get(&self) -> f64 { self.0 } - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - const foo = wasm.return_dep_ty(42); - assert.strictEqual(foo.get(), 42); - - const x = wasm.takes_ref_dep_ty(foo); - assert.strictEqual(x, 42); - - const y = 1337; - wasm.takes_mut_dep_ty(foo, y); - assert.strictEqual(foo.get(), y); - - const z = wasm.takes_own_dep_ty(foo); - assert.strictEqual(z, y); - assert.strictEqual(foo.ptr, 0); - } - "#, - ) - .test(); -} - -#[test] -fn same_api_two_crates() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - extern crate a; - extern crate b; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = "./foo")] - extern { - fn assert_next_undefined(); - fn assert_next_ten(); - } - - #[wasm_bindgen] - pub fn test() { - assert_next_undefined(); - a::test(); - assert_next_ten(); - b::test(); - } - "#, - ) - .file( - "foo.js", - r#" - import { strictEqual } from "assert"; - - let next = null; - - export function assert_next_undefined() { - next = undefined; - } - - export function assert_next_ten() { - next = 10; - } - - export function foo(a) { - console.log(a, next); - strictEqual(a, next); - next = null; - } - "#, - ) - .add_local_dependency("a", "a") - .file( - "a/Cargo.toml", - &format!(r#" - [package] - name = 'a' - version = '0.0.0' - - [dependencies] - wasm-bindgen = {{ path = '{}' }} - "#, - env!("CARGO_MANIFEST_DIR") - ), - ) - .file( - "a/src/lib.rs", - " - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = \"./foo\")] - extern { - fn foo(); - } - - pub fn test() { - foo(); - } - ", - ) - .add_local_dependency("b", "b") - .file( - "b/Cargo.toml", - &format!(r#" - [package] - name = 'b' - version = '0.0.0' - - [dependencies] - wasm-bindgen = {{ path = '{}' }} - "#, - env!("CARGO_MANIFEST_DIR") - ), - ) - .file( - "b/src/lib.rs", - " - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = \"./foo\")] - extern { - fn foo(x: u32); - } - - pub fn test() { - foo(10); - } - ", - ) - .test(); -} diff --git a/tests/all/imports.rs b/tests/all/imports.rs deleted file mode 100644 index 0a7add00..00000000 --- a/tests/all/imports.rs +++ /dev/null @@ -1,88 +0,0 @@ -use super::project; - -#[test] -fn unused_imports_not_generated() { - let mut project = project(); - - project - .debug(false) - .file("src/lib.rs", r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern { - pub fn foo(); - } - - #[wasm_bindgen] - pub fn run() { - } - "#) - .file("test.js", r#" - import { run } from "./out"; - - export function test() { - run(); - } - "#) - .test(); - - let contents = project.read_js(); - assert!(contents.contains("run"), "didn't find `run` in {}", contents); - assert!(!contents.contains("foo"), "found `foo` in {}", contents); -} - -#[test] -fn versions() { - project() - .debug(false) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = "webpack", version = "^0.2.0")] - extern { - fn foo(); - } - - #[wasm_bindgen] - pub fn run() { - foo(); - } - "#, - ) - .file( - "test.js", - r#" - import * as fs from 'fs'; - import * as assert from 'assert'; - - export function test() { - const bytes = fs.readFileSync('out_bg.wasm'); - const m = new WebAssembly.Module(bytes); - const name = '__wasm_pack_unstable'; - const sections = WebAssembly.Module.customSections(m, name); - assert.strictEqual(sections.length, 1); - const b = new Uint8Array(sections[0]); - const buf = new Buffer(b); - const map = JSON.parse(buf.toString()); - assert.deepStrictEqual(map, { - version: '0.0.1', - modules: [ - ['webpack', '^0.2.0'] - ] - }); - }; - "#, - ) - .test(); -} diff --git a/tests/all/js_objects.rs b/tests/all/js_objects.rs deleted file mode 100644 index 5bee2819..00000000 --- a/tests/all/js_objects.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::project; - -#[test] -fn serde() { - project() - .serde(true) - .depend("serde = '1.0'") - .depend("serde_derive = '1.0'") - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - #[macro_use] - extern crate serde_derive; - - use wasm_bindgen::prelude::*; - - #[derive(Deserialize, Serialize)] - pub struct Foo { - a: u32, - b: String, - c: Option, - d: Bar, - } - - #[derive(Deserialize, Serialize)] - pub struct Bar { - a: u32, - } - - #[wasm_bindgen(module = "./test")] - extern { - fn verify(a: JsValue) -> JsValue; - } - - #[wasm_bindgen] - pub fn run() { - let js = JsValue::from_serde("foo").unwrap(); - assert_eq!(js.as_string(), Some("foo".to_string())); - - let ret = verify(JsValue::from_serde(&Foo { - a: 0, - b: "foo".to_string(), - c: None, - d: Bar { a: 1 }, - }).unwrap()); - - let foo = ret.into_serde::().unwrap(); - assert_eq!(foo.a, 2); - assert_eq!(foo.b, "bar"); - assert!(foo.c.is_some()); - assert_eq!(foo.c.as_ref().unwrap().a, 3); - assert_eq!(foo.d.a, 4); - } - - #[wasm_bindgen] - pub fn parse(j: &JsValue) { - let s = j.into_serde::().unwrap(); - assert_eq!(s, "bar"); - } - "#, - ) - .file( - "test.js", - r#" - import { run, parse } from "./out"; - import * as assert from "assert"; - - export function verify(a) { - assert.deepStrictEqual(a, { - a: 0, - b: 'foo', - c: null, - d: { a: 1 } - }); - - return { - a: 2, - b: 'bar', - c: { a: 3 }, - d: { a: 4 }, - } - } - - export function test() { - run(); - parse('bar'); - } - "#, - ) - .test(); -} diff --git a/tests/all/main.rs b/tests/all/main.rs index a546d2c4..fecf2e1f 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -2,14 +2,7 @@ extern crate wasm_bindgen_test_project_builder as project_builder; -use project_builder::{project, run}; +use project_builder::project; mod comments; -mod dependencies; -mod js_objects; -mod imports; -mod node; -mod non_debug; -mod non_wasm; -mod simple; mod typescript; diff --git a/tests/all/node.rs b/tests/all/node.rs deleted file mode 100644 index 7928c70c..00000000 --- a/tests/all/node.rs +++ /dev/null @@ -1,161 +0,0 @@ -use super::project; - -#[test] -fn works() { - project() - .debug(false) - .nodejs_experimental_modules(false) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = "./test")] - extern { - static FOO: JsValue; - fn hit(); - } - - #[wasm_bindgen] - pub fn run() { - hit(); - assert_eq!(FOO.as_f64(), Some(1.0)); - } - - #[wasm_bindgen] - pub struct Foo { - contents: u32, - } - - #[wasm_bindgen] - impl Foo { - pub fn new() -> Foo { - Foo::with_contents(0) - } - pub fn with_contents(a: u32) -> Foo { - Foo { contents: a } - } - pub fn add(&mut self, amt: u32) -> u32 { - self.contents += amt; - self.contents - } - } - - #[wasm_bindgen] - pub enum Color { - Green, - Yellow, - Red, - } - #[wasm_bindgen] - pub fn cycle(color: Color) -> Color { - match color { - Color::Green => Color::Yellow, - Color::Yellow => Color::Red, - Color::Red => Color::Green, - } - } - - #[wasm_bindgen] - pub fn math(a: f32, b: f64) -> f64 { - b.acos() + - b.asin() + - b.atan() + - b.atan2(b) + - b.cbrt() + - b.cosh() + - b.exp_m1() + - b.ln_1p() + - b.sinh() + - b.tan() + - b.tanh() + - b.hypot(b) + - b.cos() + - b.exp() + - b.exp2() + - b.mul_add(b, b) + - b.ln() + - b.log(b) + - b.log10() + - b.log2() + - b.powi(8) + - b.powf(b) + - b.round() + - b.sin() + - b.abs() + - b.signum() + - b.floor() + - b.ceil() + - b.trunc() + - b.sqrt() + - (b % (a as f64)) + - ((a.cos() + - a.exp() + - a.exp2() + - a.mul_add(a, a) + - a.ln() + - a.log(a) + - a.log10() + - a.log2() + - a.powi(8) + - a.powf(a) + - a.round() + - a.sin() + - a.abs() + - a.signum() + - a.floor() + - a.ceil() + - a.trunc() + - a.sqrt() + - (a % (b as f32))) as f64) + - (b + 2.0f64.powf(a as f64)) - } - "#, - ) - .file( - "test.js", - r#" - const assert = require('assert'); - - var called = false; - - module.exports.hit = function() { - called = true; - }; - - module.exports.FOO = 1.0; - - const { math, run, Foo, Color, cycle } = require('./out'); - - module.exports.test = function() { - run(); - assert.strictEqual(called, true); - - var r = Foo.new(); - assert.strictEqual(r.add(0), 0); - assert.strictEqual(r.add(1), 1); - assert.strictEqual(r.add(2), 3); - r.free(); - - var r2 = Foo.with_contents(10); - assert.strictEqual(r2.add(0), 10); - assert.strictEqual(r2.add(1), 11); - assert.strictEqual(r2.add(2), 13); - r2.free(); - - assert.strictEqual(Color.Green, 0); - assert.strictEqual(Color.Yellow, 1); - assert.strictEqual(Color.Red, 2); - assert.strictEqual(Object.keys(Color).length, 3); - assert.strictEqual(cycle(Color.Green), Color.Yellow); - - math(1.0, 2.0); - }; - "#, - ) - .test(); -} diff --git a/tests/all/non_debug.rs b/tests/all/non_debug.rs deleted file mode 100644 index e43532da..00000000 --- a/tests/all/non_debug.rs +++ /dev/null @@ -1,48 +0,0 @@ -use super::project; - -#[test] -fn works() { - project() - .debug(false) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub struct A {} - - #[wasm_bindgen] - impl A { - pub fn new() -> A { - A {} - } - } - - #[wasm_bindgen] - pub fn clone(a: &JsValue) -> JsValue { - drop(a.clone()); - a.clone() - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - let sym = Symbol('a'); - assert.strictEqual(wasm.clone(sym), sym); - let a = wasm.A.new(); - a.free(); - } - "#, - ) - .test(); -} diff --git a/tests/all/non_wasm.rs b/tests/all/non_wasm.rs deleted file mode 100644 index 63099863..00000000 --- a/tests/all/non_wasm.rs +++ /dev/null @@ -1,101 +0,0 @@ -use super::{project, run}; -use std::process::Command; - -#[test] -fn works() { - let mut p = project(); - let name = p.crate_name(); - p.rlib(true) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub struct A { - x: u32, - } - - #[wasm_bindgen] - impl A { - pub fn new() -> A { - A { x: 3 } - } - - pub fn foo(&self) { - } - } - - #[wasm_bindgen] - pub fn foo(x: bool) { - A::new().foo(); - - if x { - bar("test"); - baz(JsValue::from(3)); - } - } - - #[wasm_bindgen] - extern { - fn some_import(); - static A: JsValue; - } - - #[wasm_bindgen] - pub fn bar(_: &str) -> JsValue { - some_import(); - A.clone() - } - - #[wasm_bindgen] - pub fn baz(_: JsValue) { - } - "#, - ) - .file( - "tests/foo.rs", - &format!( - " - extern crate {} as mytest; - - #[test] - fn foo() {{ - mytest::foo(false); - mytest::A::new().foo(); - }} - ", - name - ), - ) - .file( - "benches/foo.rs", - &format!( - " - #![feature(test)] - extern crate test; - extern crate {} as mytest; - - #[bench] - fn foo(b: &mut test::Bencher) {{ - b.iter(|| mytest::foo(false)); - }} - ", - name - ), - ); - let (root, target_dir) = p.build(); - let mut cmd = Command::new("cargo"); - cmd.arg("test") - .arg("--test") - .arg("foo") - .arg("--bench") - .arg("foo") - .current_dir(&root) - .env("CARGO_TARGET_DIR", &target_dir); - run(&mut cmd, "cargo"); -} diff --git a/tests/all/simple.rs b/tests/all/simple.rs deleted file mode 100644 index 2243d044..00000000 --- a/tests/all/simple.rs +++ /dev/null @@ -1,564 +0,0 @@ -use super::project; - -#[test] -fn add() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn add(a: u32, b: u32) -> u32 { - a + b - } - - #[wasm_bindgen] - pub fn add3(a: u32) -> u32 { - a + 3 - } - - #[wasm_bindgen] - pub fn get2(_b: bool) -> u32 { - 2 - } - - #[wasm_bindgen] - pub fn return_and_take_bool(a: bool, b: bool) -> bool { - a && b - } - - #[wasm_bindgen] - pub fn raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 { - unsafe { - (*a) = (*b) as u32; - return a - } - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - assert.strictEqual(wasm.add(1, 2), 3); - assert.strictEqual(wasm.add(2, 3), 5); - assert.strictEqual(wasm.add3(2), 5); - assert.strictEqual(wasm.get2(true), 2); - assert.strictEqual(wasm.return_and_take_bool(true, false), false); - } - "#, - ) - .test(); -} - -#[test] -fn add_headless() { - project() - .headless(true) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn add(a: u32, b: u32) -> u32 { - a + b - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - console.log("start `add_headless` test"); - assert.strictEqual(wasm.add(1, 2), 3); - console.log("end `add_headless` test"); - } - "#, - ) - .test(); -} - -#[test] -fn string_arguments() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn assert_foo_and_bar(a: &str, b: &str) { - assert_eq!(a, "foo2"); - assert_eq!(b, "bar"); - } - - #[wasm_bindgen] - pub fn assert_foo(a: &str) { - assert_eq!(a, "foo"); - } - "#, - ) - .file( - "test.js", - r#" - import * as wasm from "./out"; - - export function test() { - wasm.assert_foo("foo"); - wasm.assert_foo_and_bar("foo2", "bar"); - } - "#, - ) - .test(); -} - -#[test] -fn return_a_string() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn clone(a: &str) -> String { - a.to_string() - } - - #[wasm_bindgen] - pub fn concat(a: &str, b: &str, c: i8) -> String { - format!("{} {} {}", a, b, c) - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - assert.strictEqual(wasm.clone("foo"), "foo"); - assert.strictEqual(wasm.clone("another"), "another"); - assert.strictEqual(wasm.concat("a", "b", 3), "a b 3"); - assert.strictEqual(wasm.concat("c", "d", -2), "c d -2"); - } - "#, - ) - .test(); -} - -#[test] -fn exceptions() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - - extern crate wasm_bindgen; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn foo(_a: u32) {} - - #[wasm_bindgen] - pub fn bar(_a: &str) {} - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - assert.throws(() => wasm.foo('a'), /expected a number argument/); - assert.throws(() => wasm.bar(3), /expected a string argument/); - } - "#, - ) - .test(); -} - -// #[test] -// fn other_imports() { -// project() -// .file("src/lib.rs", r#" -// #![feature(use_extern_macros)] -// -// extern crate wasm_bindgen; -// -// use wasm_bindgen::prelude::*; -// -// extern { -// fn another_import(a: u32); -// } -// -// wasm_bindgen! { -// pub fn foo(a: u32) { -// unsafe { another_import(a); } -// } -// } -// "#) -// .file("test.js", r#" -// import * as assert from "assert"; -// import * as wasm from "./out"; -// -// let ARG: number | null = null; -// -// export function test() { -// wasm.foo(2); -// assert.strictEqual(ARG, 2); -// } -// "#) -// .test(); -// } - -#[test] -fn other_exports() { - project() - .file( - "src/lib.rs", - r#" - #[no_mangle] - pub extern fn foo(_a: u32) { - } - "#, - ) - .file( - "test.js", - r#" - import * as wasm from "./out_bg"; - - export function test() { - wasm.foo(2); - } - "#, - ) - .test(); -} - -#[test] -fn no_std() { - project() - .no_std(true) - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - #![no_std] - #![allow(dead_code)] - - extern crate wasm_bindgen; - extern crate std as _some_other_name; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = "./foo")] - extern { - fn test(a: &str); - - type Js; - #[wasm_bindgen(constructor)] - fn new() -> Js; - #[wasm_bindgen(method)] - fn init(this: &Js); - } - - #[wasm_bindgen] - pub fn foo(_a: u32) {} - "#, - ) - .file( - "test.js", - r#" - import * as wasm from "./out_bg"; - - export function test() { - // mostly just testing the project compiles here - wasm.foo(1); - } - "#, - ) - .file( - "foo.js", - r#" - export class Js { - init() { - } - } - "#, - ) - .test(); -} - -#[test] -fn no_std_class() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - #![no_std] - #![allow(dead_code)] - - extern crate wasm_bindgen; - extern crate std as _some_other_name; - - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern { - fn test(a: &str); - - type Js; - #[wasm_bindgen(constructor)] - fn new() -> Js; - #[wasm_bindgen(method, structural)] - fn init(this: &Js); - } - - #[wasm_bindgen] - pub fn foo(_a: u32) {} - - #[wasm_bindgen] - pub struct A {} - - #[wasm_bindgen] - impl A { - pub fn foo(&self) {} - pub fn bar(&mut self) {} - } - "#, - ) - .file( - "test.js", - r#" - import * as wasm from "./out_bg"; - - export function test() { - // mostly just testing the project compiles here - wasm.foo(1); - } - "#, - ) - .test(); -} - -#[test] -fn jsvalue_typeof() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - pub fn is_object(val: &JsValue) -> bool { - val.is_object() - } - - #[wasm_bindgen] - pub fn is_function(val: &JsValue) -> bool { - val.is_function() - } - - #[wasm_bindgen] - pub fn is_string(val: &JsValue) -> bool { - val.is_string() - } - "#, - ) - .file( - "test.js", - r#" - import * as assert from "assert"; - import * as wasm from "./out"; - - export function test() { - assert.ok(wasm.is_object({})); - assert.ok(!wasm.is_object(42)); - assert.ok(wasm.is_function(function() {})); - assert.ok(!wasm.is_function(42)); - assert.ok(wasm.is_string("2b or !2b")); - assert.ok(!wasm.is_string(42)); - } - "#, - ) - .test(); -} - -#[test] -fn binding_to_unimplemented_apis_doesnt_break_everything() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros)] - extern crate wasm_bindgen; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern { - #[derive(Clone)] - type Array; - - #[wasm_bindgen(constructor)] - fn new() -> Array; - - #[wasm_bindgen(method, catch)] - fn standardized_method_this_js_runtime_doesnt_implement_yet(this: &Array) - -> Result<(), JsValue>; - } - - #[wasm_bindgen] - pub fn test() { - let array = Array::new(); - let res = array.standardized_method_this_js_runtime_doesnt_implement_yet(); - assert!(res.is_err()); - } - "#, - ) - .test(); -} - -#[test] -fn optional_slices() { - project() - .file( - "src/lib.rs", - r#" - #![feature(use_extern_macros, wasm_custom_section)] - extern crate wasm_bindgen; - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(module = "./foo")] - extern { - fn optional_str_none(a: Option<&str>); - fn optional_str_some(a: Option<&str>); - fn optional_slice_none(a: Option<&[u8]>); - fn optional_slice_some(a: Option<&[u8]>); - - fn optional_string_none(a: Option); - fn optional_string_some(a: Option); - fn optional_string_some_empty(a: Option); - - fn return_string_none() -> Option; - fn return_string_some() -> Option; - - fn run_rust_tests(); - } - - #[wasm_bindgen] - pub fn test() { - optional_str_none(None); - optional_str_some(Some("x")); - optional_slice_none(None); - optional_slice_some(Some(&[1, 2, 3])); - optional_string_none(None); - optional_string_some_empty(Some(String::new())); - optional_string_some(Some("abcd".to_string())); - - assert_eq!(return_string_none(), None); - assert_eq!(return_string_some(), Some("foo".to_string())); - run_rust_tests(); - } - - #[wasm_bindgen] - pub fn take_optional_str_none(x: Option) { - assert!(x.is_none()) - } - #[wasm_bindgen] - pub fn take_optional_str_some(x: Option) { - assert_eq!(x, Some(String::from("hello"))); - } - #[wasm_bindgen] - pub fn return_optional_str_none() -> Option { - None - } - #[wasm_bindgen] - pub fn return_optional_str_some() -> Option { - Some("world".to_string()) - } - "#, - ) - .file( - "foo.js", - r#" - import { strictEqual } from "assert"; - import * as wasm from "./out"; - - export function optional_str_none(x) { - strictEqual(x, undefined); - } - - export function optional_str_some(x) { - strictEqual(x, 'x'); - } - - export function optional_slice_none(x) { - strictEqual(x, undefined); - } - - export function optional_slice_some(x) { - strictEqual(x.length, 3); - strictEqual(x[0], 1); - strictEqual(x[1], 2); - strictEqual(x[2], 3); - } - - export function optional_string_none(x) { - strictEqual(x, undefined); - } - - export function optional_string_some(x) { - strictEqual(x, 'abcd'); - } - - export function optional_string_some_empty(x) { - strictEqual(x, ''); - } - - export function return_string_none() {} - export function return_string_some() { - return 'foo'; - } - - export function run_rust_tests() { - wasm.take_optional_str_none(); - wasm.take_optional_str_none(null); - wasm.take_optional_str_none(undefined); - wasm.take_optional_str_some('hello'); - strictEqual(wasm.return_optional_str_none(), undefined); - strictEqual(wasm.return_optional_str_some(), 'world'); - } - "# - ) - .test(); -} diff --git a/tests/crates/a/Cargo.toml b/tests/crates/a/Cargo.toml new file mode 100644 index 00000000..d8d93d0a --- /dev/null +++ b/tests/crates/a/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasm-bindgen-test-crate-a" +version = "0.1.0" +authors = ["The wasm-bindgen Authors"] +license = "MIT/Apache-2.0" +description = "internal test crate for wasm-bindgen" + +[dependencies] +wasm-bindgen = { path = '../../..', version = '0.2' } diff --git a/tests/crates/a/src/lib.rs b/tests/crates/a/src/lib.rs new file mode 100644 index 00000000..04dde653 --- /dev/null +++ b/tests/crates/a/src/lib.rs @@ -0,0 +1,14 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "tests/wasm/duplicate_deps.js")] +extern { + fn foo(); +} + +pub fn test() { + foo(); +} diff --git a/tests/crates/b/Cargo.toml b/tests/crates/b/Cargo.toml new file mode 100644 index 00000000..aee2848b --- /dev/null +++ b/tests/crates/b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasm-bindgen-test-crate-b" +version = "0.1.0" +authors = ["The wasm-bindgen Authors"] +license = "MIT/Apache-2.0" +description = "internal test crate for wasm-bindgen" + +[dependencies] +wasm-bindgen = { path = '../../..', version = '0.2' } diff --git a/tests/crates/b/src/lib.rs b/tests/crates/b/src/lib.rs new file mode 100644 index 00000000..87f781da --- /dev/null +++ b/tests/crates/b/src/lib.rs @@ -0,0 +1,14 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "tests/wasm/duplicate_deps.js")] +extern { + fn foo(x: u32); +} + +pub fn test() { + foo(10); +} diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml new file mode 100644 index 00000000..23f30400 --- /dev/null +++ b/tests/no-std/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "no-std" +version = "0.1.0" +authors = ["The wasm-bindgen Developers"] + +[lib] +path = "test.rs" + +[dependencies] +wasm-bindgen = { path = '../..', default-features = false } diff --git a/tests/no-std/lib.rs b/tests/no-std/lib.rs new file mode 100644 index 00000000..e69de29b diff --git a/tests/no-std/test.rs b/tests/no-std/test.rs new file mode 100644 index 00000000..4dc5c91e --- /dev/null +++ b/tests/no-std/test.rs @@ -0,0 +1,28 @@ +//! This is a test that we compile `wasm-bindgen` itself in `no_std` mode and we +//! can export/import various items. +//! +//! This doesn't actually run any tests, it's mostly a compile-time verification +//! that things work. + +#![feature(use_extern_macros)] +#![no_std] +#![allow(dead_code)] + +extern crate wasm_bindgen; +extern crate std as _some_other_name; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn foo(_a: u32) {} + +#[wasm_bindgen] +extern { + fn test(a: &str); + + type Js; + #[wasm_bindgen(constructor)] + fn new() -> Js; + #[wasm_bindgen(method, structural)] + fn init(this: &Js); +} diff --git a/tests/non_wasm.rs b/tests/non_wasm.rs new file mode 100644 index 00000000..7a8128f7 --- /dev/null +++ b/tests/non_wasm.rs @@ -0,0 +1,53 @@ +#![feature(use_extern_macros)] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct A { + x: u32, +} + +#[wasm_bindgen] +impl A { + pub fn new() -> A { + A { x: 3 } + } + + pub fn foo(&self) { + drop(self.x); + } +} + +#[wasm_bindgen] +pub fn foo(x: bool) { + A::new().foo(); + + if x { + bar("test"); + baz(JsValue::from(3)); + } +} + +#[wasm_bindgen] +extern { + fn some_import(); + static A: JsValue; +} + +#[wasm_bindgen] +pub fn bar(_: &str) -> JsValue { + some_import(); + A.clone() +} + +#[wasm_bindgen] +pub fn baz(_: JsValue) { +} + +#[test] +fn test_foo() { + foo(false); + A::new().foo(); +} diff --git a/tests/std-crate-no-std-dep.rs b/tests/std-crate-no-std-dep.rs new file mode 100644 index 00000000..77a9a11e --- /dev/null +++ b/tests/std-crate-no-std-dep.rs @@ -0,0 +1,30 @@ +//! This is a test that we can define items in a `#![no_std]` crate when +//! `wasm-bindgen` is compiled itself with the `std` feature and everything +//! works out just fine. + +#![feature(use_extern_macros)] +#![no_std] + +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern { + fn test(a: &str); + + type Js; + #[wasm_bindgen(constructor)] + fn new() -> Js; + #[wasm_bindgen(method, structural)] + fn init(this: &Js); +} + +#[wasm_bindgen] +pub struct A {} + +#[wasm_bindgen] +impl A { + pub fn foo(&self) {} + pub fn bar(&mut self) {} +} diff --git a/tests/wasm/api.rs b/tests/wasm/api.rs index f7be28ea..1953929b 100644 --- a/tests/wasm/api.rs +++ b/tests/wasm/api.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/api.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/api.js")] extern { fn js_works(); fn js_eq_works(); diff --git a/tests/wasm/char.rs b/tests/wasm/char.rs index 328151a8..6d2ef325 100644 --- a/tests/wasm/char.rs +++ b/tests/wasm/char.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/char.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/char.js")] extern { fn js_identity(c: char) -> char; fn js_works(); diff --git a/tests/wasm/classes.js b/tests/wasm/classes.js index 6764a0a8..1ffd4a9b 100644 --- a/tests/wasm/classes.js +++ b/tests/wasm/classes.js @@ -31,6 +31,10 @@ exports.js_strings = () => { }; exports.js_exceptions = () => { + // this test only works when `--debug` is passed to `wasm-bindgen` (or the + // equivalent thereof) + if (require('process').env.WASM_BINDGEN_NO_DEBUG) + return; assert.throws(() => new wasm.ClassesExceptions1(), /cannot invoke `new` directly/); let a = wasm.ClassesExceptions1.new(); a.free(); diff --git a/tests/wasm/classes.rs b/tests/wasm/classes.rs index ae4bd2e2..2c58c609 100644 --- a/tests/wasm/classes.rs +++ b/tests/wasm/classes.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/classes.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/classes.js")] extern { fn js_simple(); fn js_strings(); diff --git a/tests/wasm/closures.rs b/tests/wasm/closures.rs index 2c926866..84794725 100644 --- a/tests/wasm/closures.rs +++ b/tests/wasm/closures.rs @@ -3,7 +3,7 @@ use wasm_bindgen::prelude::*; use std::cell::Cell; use std::rc::Rc; -#[wasm_bindgen(module = "tests/wasm/closures.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/closures.js")] extern { fn works_call(a: &Fn()); fn works_thread(a: &Fn(u32) -> u32) -> u32; diff --git a/tests/wasm/duplicate_deps.js b/tests/wasm/duplicate_deps.js new file mode 100644 index 00000000..b69139df --- /dev/null +++ b/tests/wasm/duplicate_deps.js @@ -0,0 +1,17 @@ +const assert = require('assert'); + +let next = null; + +exports.assert_next_undefined = function() { + next = undefined; +}; + +exports.assert_next_ten = function() { + next = 10; +}; + +exports.foo = function(a) { + console.log(a, next); + assert.strictEqual(a, next); + next = null; +}; diff --git a/tests/wasm/duplicate_deps.rs b/tests/wasm/duplicate_deps.rs new file mode 100644 index 00000000..0a451301 --- /dev/null +++ b/tests/wasm/duplicate_deps.rs @@ -0,0 +1,18 @@ +use wasm_bindgen::prelude::*; +use wasm_bindgen_test::*; +use wasm_bindgen_test_crate_a as a; +use wasm_bindgen_test_crate_b as b; + +#[wasm_bindgen(module = "tests/wasm/duplicate_deps.js")] +extern { + fn assert_next_undefined(); + fn assert_next_ten(); +} + +#[wasm_bindgen_test] +fn works() { + assert_next_undefined(); + a::test(); + assert_next_ten(); + b::test(); +} diff --git a/tests/wasm/duplicates.rs b/tests/wasm/duplicates.rs index c0beadad..f35e6d37 100644 --- a/tests/wasm/duplicates.rs +++ b/tests/wasm/duplicates.rs @@ -3,7 +3,7 @@ use wasm_bindgen_test::*; pub mod same_function_different_locations_a { use wasm_bindgen::prelude::*; - #[wasm_bindgen(module = "tests/wasm/duplicates_a.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/duplicates_a.js")] extern { pub fn foo(); } @@ -12,7 +12,7 @@ pub mod same_function_different_locations_a { pub mod same_function_different_locations_b { use wasm_bindgen::prelude::*; - #[wasm_bindgen(module = "tests/wasm/duplicates_a.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/duplicates_a.js")] extern { pub fn foo(); } @@ -27,7 +27,7 @@ fn same_function_different_locations() { pub mod same_function_different_modules_a { use wasm_bindgen::prelude::*; - #[wasm_bindgen(module = "tests/wasm/duplicates_b.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/duplicates_b.js")] extern { pub fn foo() -> bool; } @@ -36,7 +36,7 @@ pub mod same_function_different_modules_a { pub mod same_function_different_modules_b { use wasm_bindgen::prelude::*; - #[wasm_bindgen(module = "tests/wasm/duplicates_c.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/duplicates_c.js")] extern { pub fn foo() -> bool; } diff --git a/tests/wasm/enums.rs b/tests/wasm/enums.rs index f49a18b9..4c1ad2dc 100644 --- a/tests/wasm/enums.rs +++ b/tests/wasm/enums.rs @@ -2,7 +2,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; use self::inner::ColorWithCustomValues; -#[wasm_bindgen(module = "tests/wasm/enums.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/enums.js")] extern { fn js_c_style_enum(); fn js_c_style_enum_with_custom_values(); diff --git a/tests/wasm/import_class.rs b/tests/wasm/import_class.rs index 329e9e48..06083ea2 100644 --- a/tests/wasm/import_class.rs +++ b/tests/wasm/import_class.rs @@ -5,7 +5,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/import_class.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/import_class.js")] extern { fn math_log(f: f64) -> f64; diff --git a/tests/wasm/imports.js b/tests/wasm/imports.js index 7351deff..a12d7b67 100644 --- a/tests/wasm/imports.js +++ b/tests/wasm/imports.js @@ -1,5 +1,6 @@ const assert = require('assert'); const wasm = require('wasm-bindgen-test'); +const fs = require('fs'); let ARG = null; let ANOTHER_ARG = null; @@ -90,3 +91,12 @@ exports.touch_custom_type = function() { exports.interpret_2_as_custom_type = function() { assert.throws(wasm.interpret_2_as_custom_type, /expected value of type CustomType/); }; + +exports.baz$ = function() {}; +exports.$foo = 1.0; + +exports.assert_dead_import_not_generated = function() { + const filename = require.resolve("wasm-bindgen-test"); + const bindings = fs.readFileSync(filename); + assert.ok(!bindings.includes("unused_import")); +}; diff --git a/tests/wasm/imports.rs b/tests/wasm/imports.rs index 2ba501fb..1ad9208e 100644 --- a/tests/wasm/imports.rs +++ b/tests/wasm/imports.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/imports.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/imports.js")] extern { fn test_simple(); @@ -42,6 +42,14 @@ extern { fn custom_type_return_2() -> CustomType; #[wasm_bindgen(js_name = interpret_2_as_custom_type)] fn js_interpret_2_as_custom_type(); + + #[wasm_bindgen(js_name = "baz$")] + fn renamed_with_dollar_sign(); + #[wasm_bindgen(js_name = "$foo")] + static RENAMED: JsValue; + + fn unused_import(); + fn assert_dead_import_not_generated(); } #[wasm_bindgen] @@ -153,3 +161,18 @@ impl CustomType { } } + +#[wasm_bindgen_test] +fn rename_with_string() { + renamed_with_dollar_sign(); +} + +#[wasm_bindgen_test] +fn rename_static_with_string() { + assert_eq!(RENAMED.as_f64(), Some(1.0)); +} + +#[wasm_bindgen_test] +fn dead_imports_not_generated() { + assert_dead_import_not_generated(); +} diff --git a/tests/wasm/js_objects.js b/tests/wasm/js_objects.js index f53b84d7..96aa7bcf 100644 --- a/tests/wasm/js_objects.js +++ b/tests/wasm/js_objects.js @@ -82,3 +82,19 @@ exports.js_returning_vector = () => { exports.js_another_vector_return = () => { assert.deepStrictEqual(wasm.another_vector_return_get_array(), [1, 2, 3, 4, 5, 6]); }; + +exports.verify_serde = function(a) { + assert.deepStrictEqual(a, { + a: 0, + b: 'foo', + c: null, + d: { a: 1 } + }); + + return { + a: 2, + b: 'bar', + c: { a: 3 }, + d: { a: 4 }, + } +}; diff --git a/tests/wasm/js_objects.rs b/tests/wasm/js_objects.rs index 6b162410..70b3518e 100644 --- a/tests/wasm/js_objects.rs +++ b/tests/wasm/js_objects.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/js_objects.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/js_objects.js")] extern { fn simple_foo(s: &JsValue); fn js_simple(); @@ -26,6 +26,7 @@ extern { fn js_returning_vector(); fn js_another_vector_return(); + fn verify_serde(val: JsValue) -> JsValue; } #[wasm_bindgen] @@ -105,3 +106,40 @@ pub fn another_vector_return_get_array() -> Vec { fn another_vector_return() { js_another_vector_return(); } + +#[cfg(feature = "serde-serialize")] +#[wasm_bindgen_test] +fn serde() { + #[derive(Deserialize, Serialize)] + pub struct Foo { + a: u32, + b: String, + c: Option, + d: Bar, + } + + #[derive(Deserialize, Serialize)] + pub struct Bar { + a: u32, + } + + + let js = JsValue::from_serde("foo").unwrap(); + assert_eq!(js.as_string(), Some("foo".to_string())); + + let ret = verify_serde(JsValue::from_serde(&Foo { + a: 0, + b: "foo".to_string(), + c: None, + d: Bar { a: 1 }, + }).unwrap()); + + let foo = ret.into_serde::().unwrap(); + assert_eq!(foo.a, 2); + assert_eq!(foo.b, "bar"); + assert!(foo.c.is_some()); + assert_eq!(foo.c.as_ref().unwrap().a, 3); + assert_eq!(foo.d.a, 4); + + assert_eq!(JsValue::from("bar").into_serde::().unwrap(), "bar"); +} diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 3c42a70e..74be2707 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -3,19 +3,28 @@ extern crate wasm_bindgen_test; extern crate wasm_bindgen; +extern crate wasm_bindgen_test_crate_a; +extern crate wasm_bindgen_test_crate_b; + +#[cfg(feature = "serde-serialize")] +#[macro_use] +extern crate serde_derive; pub mod api; pub mod char; pub mod classes; pub mod closures; +pub mod duplicate_deps; pub mod duplicates; pub mod enums; -pub mod imports; pub mod import_class; +pub mod imports; pub mod js_objects; pub mod math; +pub mod node; pub mod option; pub mod optional_primitives; +pub mod simple; pub mod slice; pub mod structural; pub mod u64; diff --git a/tests/wasm/math.rs b/tests/wasm/math.rs index 47ea5ce2..413e8f2f 100644 --- a/tests/wasm/math.rs +++ b/tests/wasm/math.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/math.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/math.js")] extern { fn js_auto_bind_math(); } @@ -64,4 +64,4 @@ pub fn math(a: f32, b: f64) -> f64 { #[wasm_bindgen_test] fn auto_bind_math() { js_auto_bind_math(); -} \ No newline at end of file +} diff --git a/tests/wasm/node.js b/tests/wasm/node.js new file mode 100644 index 00000000..1887904f --- /dev/null +++ b/tests/wasm/node.js @@ -0,0 +1,34 @@ +const assert = require('assert'); +const wasm = require('wasm-bindgen-test'); + +var called = false; + +exports.hit = function() { + called = true; +}; + +exports.FOO = 1.0; + +exports.test_works = function() { + assert.strictEqual(called, true); + + var r = wasm.Foo.new(); + assert.strictEqual(r.add(0), 0); + assert.strictEqual(r.add(1), 1); + assert.strictEqual(r.add(2), 3); + r.free(); + + var r2 = wasm.Foo.with_contents(10); + assert.strictEqual(r2.add(0), 10); + assert.strictEqual(r2.add(1), 11); + assert.strictEqual(r2.add(2), 13); + r2.free(); + + assert.strictEqual(wasm.Color.Green, 0); + assert.strictEqual(wasm.Color.Yellow, 1); + assert.strictEqual(wasm.Color.Red, 2); + assert.strictEqual(Object.keys(wasm.Color).length, 3); + assert.strictEqual(wasm.cycle(wasm.Color.Green), wasm.Color.Yellow); + + wasm.node_math(1.0, 2.0); +}; diff --git a/tests/wasm/node.rs b/tests/wasm/node.rs new file mode 100644 index 00000000..741febfc --- /dev/null +++ b/tests/wasm/node.rs @@ -0,0 +1,105 @@ +use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "tests/wasm/node.js")] +extern { + fn test_works(); + static FOO: JsValue; + fn hit(); +} + +#[wasm_bindgen_test] +fn works() { + hit(); + assert_eq!(FOO.as_f64(), Some(1.0)); + test_works(); +} + +#[wasm_bindgen] +pub struct Foo { + contents: u32, +} + +#[wasm_bindgen] +impl Foo { + pub fn new() -> Foo { + Foo::with_contents(0) + } + pub fn with_contents(a: u32) -> Foo { + Foo { contents: a } + } + pub fn add(&mut self, amt: u32) -> u32 { + self.contents += amt; + self.contents + } +} + +#[wasm_bindgen] +pub enum Color { + Green, + Yellow, + Red, +} +#[wasm_bindgen] +pub fn cycle(color: Color) -> Color { + match color { + Color::Green => Color::Yellow, + Color::Yellow => Color::Red, + Color::Red => Color::Green, + } +} + +#[wasm_bindgen] +pub fn node_math(a: f32, b: f64) -> f64 { + b.acos() + + b.asin() + + b.atan() + + b.atan2(b) + + b.cbrt() + + b.cosh() + + b.exp_m1() + + b.ln_1p() + + b.sinh() + + b.tan() + + b.tanh() + + b.hypot(b) + + b.cos() + + b.exp() + + b.exp2() + + b.mul_add(b, b) + + b.ln() + + b.log(b) + + b.log10() + + b.log2() + + b.powi(8) + + b.powf(b) + + b.round() + + b.sin() + + b.abs() + + b.signum() + + b.floor() + + b.ceil() + + b.trunc() + + b.sqrt() + + (b % (a as f64)) + + ((a.cos() + + a.exp() + + a.exp2() + + a.mul_add(a, a) + + a.ln() + + a.log(a) + + a.log10() + + a.log2() + + a.powi(8) + + a.powf(a) + + a.round() + + a.sin() + + a.abs() + + a.signum() + + a.floor() + + a.ceil() + + a.trunc() + + a.sqrt() + + (a % (b as f32))) as f64) + + (b + 2.0f64.powf(a as f64)) +} diff --git a/tests/wasm/option.rs b/tests/wasm/option.rs index 98c7ece1..8da01e3b 100644 --- a/tests/wasm/option.rs +++ b/tests/wasm/option.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/option.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/option.js")] extern { pub type MyType; #[wasm_bindgen(constructor)] diff --git a/tests/wasm/optional_primitives.rs b/tests/wasm/optional_primitives.rs index 57a4bd1e..3543a4e2 100644 --- a/tests/wasm/optional_primitives.rs +++ b/tests/wasm/optional_primitives.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/optional_primitives.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/optional_primitives.js")] extern { fn optional_i32_js_identity(a: Option) -> Option; fn optional_u32_js_identity(a: Option) -> Option; diff --git a/tests/wasm/simple.js b/tests/wasm/simple.js new file mode 100644 index 00000000..1ac7d041 --- /dev/null +++ b/tests/wasm/simple.js @@ -0,0 +1,89 @@ +const assert = require('assert'); +const wasm = require('wasm-bindgen-test'); + +exports.test_add = function() { + assert.strictEqual(wasm.simple_add(1, 2), 3); + assert.strictEqual(wasm.simple_add(2, 3), 5); + assert.strictEqual(wasm.simple_add3(2), 5); + assert.strictEqual(wasm.simple_get2(true), 2); + assert.strictEqual(wasm.simple_return_and_take_bool(true, false), false); +}; + +exports.test_string_arguments = function() { + wasm.simple_assert_foo("foo"); + wasm.simple_assert_foo_and_bar("foo2", "bar"); +}; + +exports.test_return_a_string = function() { + assert.strictEqual(wasm.simple_clone("foo"), "foo"); + assert.strictEqual(wasm.simple_clone("another"), "another"); + assert.strictEqual(wasm.simple_concat("a", "b", 3), "a b 3"); + assert.strictEqual(wasm.simple_concat("c", "d", -2), "c d -2"); +}; + +exports.test_wrong_types = function() { + // this test only works when `--debug` is passed to `wasm-bindgen` (or the + // equivalent thereof) + if (require('process').env.WASM_BINDGEN_NO_DEBUG) + return; + assert.throws(() => wasm.simple_int('a'), /expected a number argument/); + assert.throws(() => wasm.simple_str(3), /expected a string argument/); +}; + +exports.test_other_exports_still_available = function() { + require('wasm-bindgen-test_bg').foo(3); +}; + +exports.test_jsvalue_typeof = function() { + assert.ok(wasm.is_object({})); + assert.ok(!wasm.is_object(42)); + assert.ok(wasm.is_function(function() {})); + assert.ok(!wasm.is_function(42)); + assert.ok(wasm.is_string("2b or !2b")); + assert.ok(!wasm.is_string(42)); +}; + +exports.optional_str_none = function(x) { + assert.strictEqual(x, undefined); +}; + +exports.optional_str_some = function(x) { + assert.strictEqual(x, 'x'); +}; + +exports.optional_slice_none = function(x) { + assert.strictEqual(x, undefined); +}; + +exports.optional_slice_some = function(x) { + assert.strictEqual(x.length, 3); + assert.strictEqual(x[0], 1); + assert.strictEqual(x[1], 2); + assert.strictEqual(x[2], 3); +} + +exports.optional_string_none = function(x) { + assert.strictEqual(x, undefined); +}; + +exports.optional_string_some = function(x) { + assert.strictEqual(x, 'abcd'); +}; + +exports.optional_string_some_empty = function(x) { + assert.strictEqual(x, ''); +}; + +exports.return_string_none = function() {}; +exports.return_string_some = function() { + return 'foo'; +}; + +exports.test_rust_optional = function() { + wasm.take_optional_str_none(); + wasm.take_optional_str_none(null); + wasm.take_optional_str_none(undefined); + wasm.take_optional_str_some('hello'); + assert.strictEqual(wasm.return_optional_str_none(), undefined); + assert.strictEqual(wasm.return_optional_str_some(), 'world'); +}; diff --git a/tests/wasm/simple.rs b/tests/wasm/simple.rs new file mode 100644 index 00000000..5182a366 --- /dev/null +++ b/tests/wasm/simple.rs @@ -0,0 +1,180 @@ +use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(module = "tests/wasm/simple.js")] +extern { + fn test_add(); + fn test_string_arguments(); + fn test_return_a_string(); + fn test_wrong_types(); + fn test_other_exports_still_available(); + fn test_jsvalue_typeof(); + + fn optional_str_none(a: Option<&str>); + fn optional_str_some(a: Option<&str>); + fn optional_slice_none(a: Option<&[u8]>); + fn optional_slice_some(a: Option<&[u8]>); + fn optional_string_none(a: Option); + fn optional_string_some(a: Option); + fn optional_string_some_empty(a: Option); + fn return_string_none() -> Option; + fn return_string_some() -> Option; + fn test_rust_optional(); +} + +#[wasm_bindgen_test] +fn add() { + test_add(); +} + +#[wasm_bindgen] +pub fn simple_add(a: u32, b: u32) -> u32 { + a + b +} + +#[wasm_bindgen] +pub fn simple_add3(a: u32) -> u32 { + a + 3 +} + +#[wasm_bindgen] +pub fn simple_get2(_b: bool) -> u32 { + 2 +} + +#[wasm_bindgen] +pub fn simple_return_and_take_bool(a: bool, b: bool) -> bool { + a && b +} + +#[wasm_bindgen] +pub fn simple_raw_pointers_work(a: *mut u32, b: *const u8) -> *const u32 { + unsafe { + (*a) = (*b) as u32; + return a + } +} + +#[wasm_bindgen_test] +fn string_arguments() { + test_string_arguments(); +} + +#[wasm_bindgen] +pub fn simple_assert_foo_and_bar(a: &str, b: &str) { + assert_eq!(a, "foo2"); + assert_eq!(b, "bar"); +} + +#[wasm_bindgen] +pub fn simple_assert_foo(a: &str) { + assert_eq!(a, "foo"); +} + +#[wasm_bindgen_test] +fn return_a_string() { + test_return_a_string(); +} + +#[wasm_bindgen] +pub fn simple_clone(a: &str) -> String { + a.to_string() +} + +#[wasm_bindgen] +pub fn simple_concat(a: &str, b: &str, c: i8) -> String { + format!("{} {} {}", a, b, c) +} + +#[wasm_bindgen_test] +fn wrong_types() { + test_wrong_types(); +} + +#[wasm_bindgen] +pub fn simple_int(_a: u32) {} + +#[wasm_bindgen] +pub fn simple_str(_a: &str) {} + +#[wasm_bindgen_test] +fn other_exports() { + test_other_exports_still_available(); +} + +#[no_mangle] +pub extern fn foo(_a: u32) { +} + +#[wasm_bindgen_test] +fn jsvalue_typeof() { + test_jsvalue_typeof(); +} + +#[wasm_bindgen] +pub fn is_object(val: &JsValue) -> bool { + val.is_object() +} + +#[wasm_bindgen] +pub fn is_function(val: &JsValue) -> bool { + val.is_function() +} + +#[wasm_bindgen] +pub fn is_string(val: &JsValue) -> bool { + val.is_string() +} + +#[wasm_bindgen] +extern { + #[derive(Clone)] + type Array; + #[wasm_bindgen(constructor)] + fn new() -> Array; + #[wasm_bindgen(method, catch)] + fn standardized_method_this_js_runtime_doesnt_implement_yet(this: &Array) + -> Result<(), JsValue>; +} + +#[wasm_bindgen_test] +fn binding_to_unimplemented_apis_doesnt_break_everything() { + let array = Array::new(); + let res = array.standardized_method_this_js_runtime_doesnt_implement_yet(); + assert!(res.is_err()); +} + +#[wasm_bindgen_test] +fn optional_slices() { + optional_str_none(None); + optional_str_some(Some("x")); + optional_slice_none(None); + optional_slice_some(Some(&[1, 2, 3])); + optional_string_none(None); + optional_string_some_empty(Some(String::new())); + optional_string_some(Some("abcd".to_string())); + + assert_eq!(return_string_none(), None); + assert_eq!(return_string_some(), Some("foo".to_string())); + test_rust_optional(); +} + +#[wasm_bindgen] +pub fn take_optional_str_none(x: Option) { + assert!(x.is_none()) + +} +#[wasm_bindgen] +pub fn take_optional_str_some(x: Option) { + assert_eq!(x, Some(String::from("hello"))); +} + +#[wasm_bindgen] +pub fn return_optional_str_none() -> Option { + None +} + +#[wasm_bindgen] +pub fn return_optional_str_some() -> Option { + Some("world".to_string()) +} diff --git a/tests/wasm/slice.rs b/tests/wasm/slice.rs index 653ec01c..6347a601 100644 --- a/tests/wasm/slice.rs +++ b/tests/wasm/slice.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/slice.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/slice.js")] extern { fn js_export(); @@ -44,7 +44,7 @@ fn export() { macro_rules! import_macro { ($(($rust:ident, $js:ident, $i:ident))*) => ($( - #[wasm_bindgen(module = "tests/wasm/slice.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/slice.js")] extern { fn $js(a: &[$i]) -> Vec<$i>; } @@ -105,7 +105,7 @@ fn pass_array() { macro_rules! import_mut_macro { ($(($rust:ident, $js:ident, $i:ident))*) => ( $( - #[wasm_bindgen(module = "tests/wasm/slice.js", version = "*")] + #[wasm_bindgen(module = "tests/wasm/slice.js")] extern { fn $js(a: &mut [$i]); } diff --git a/tests/wasm/structural.rs b/tests/wasm/structural.rs index 92a13fe2..4729620c 100644 --- a/tests/wasm/structural.rs +++ b/tests/wasm/structural.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/structural.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/structural.js")] extern { fn js_works(); } diff --git a/tests/wasm/u64.rs b/tests/wasm/u64.rs index d5d60760..1900d3cd 100644 --- a/tests/wasm/u64.rs +++ b/tests/wasm/u64.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/u64.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/u64.js")] extern { fn i64_js_identity(a: i64) -> i64; fn u64_js_identity(a: u64) -> u64; diff --git a/tests/wasm/validate_prt.rs b/tests/wasm/validate_prt.rs index f6446fde..c04f3353 100644 --- a/tests/wasm/validate_prt.rs +++ b/tests/wasm/validate_prt.rs @@ -1,7 +1,7 @@ use wasm_bindgen_test::*; use wasm_bindgen::prelude::*; -#[wasm_bindgen(module = "tests/wasm/validate_prt.js", version = "*")] +#[wasm_bindgen(module = "tests/wasm/validate_prt.js")] extern { fn js_works(); } diff --git a/yarn.lock b/yarn.lock index 13baf34b..1f62c5b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,9 +78,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@types/node@^10.5.5": - version "10.5.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba" +"@types/node@^10.5.6": + version "10.5.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.6.tgz#1640f021dd0eaf12e731e54198c12ad2e020dc8e" "@webassemblyjs/ast@1.5.13": version "1.5.13" @@ -245,9 +245,9 @@ acorn@^5.0.0, acorn@^5.0.3, acorn@^5.6.0, acorn@^5.6.2: version "5.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" -adm-zip@0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" +adm-zip@0.4.11: + version "0.4.11" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.11.tgz#2aa54c84c4b01a9d0fb89bb11982a51f13e3d62a" ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.2.0" @@ -1267,9 +1267,9 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.2.0.tgz#3901ae249195d473e633c4acbc370068b1c964dc" +eslint@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.3.0.tgz#53695aca5213968aacdf970ccb231e42a2b285f8" dependencies: ajv "^6.5.0" babel-code-frame "^6.26.0" @@ -1302,7 +1302,7 @@ eslint@^5.2.0: path-is-inside "^1.0.2" pluralize "^7.0.0" progress "^2.0.0" - regexpp "^1.1.0" + regexpp "^2.0.0" require-uncached "^1.0.3" semver "^5.5.0" string.prototype.matchall "^2.0.0" @@ -1651,11 +1651,11 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -geckodriver@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-1.12.0.tgz#997a1efeca90543192fbcf1eae70d7cb2196330e" +geckodriver@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-1.12.1.tgz#f3b2ecd5224f383462f07841f4fdcf5007d1b42d" dependencies: - adm-zip "0.4.7" + adm-zip "0.4.11" bluebird "3.4.6" got "5.6.0" tar "4.0.2" @@ -3271,9 +3271,9 @@ regexp.prototype.flags@^1.2.0: dependencies: define-properties "^1.1.2" -regexpp@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" +regexpp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.0.tgz#b2a7534a85ca1b033bcf5ce9ff8e56d4e0755365" remove-trailing-separator@^1.0.1: version "1.1.0" @@ -4185,9 +4185,9 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.16.4: - version "4.16.4" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.4.tgz#6b020f76483bc66339164c296d89978aa100d37a" +webpack@^4.16.5: + version "4.16.5" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.5.tgz#29fb39462823d7eb8aefcab8b45f7f241db0d092" dependencies: "@webassemblyjs/ast" "1.5.13" "@webassemblyjs/helper-module-context" "1.5.13"