From cc8095d0654e568b38c32b75f86acf86d43ef780 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Wed, 8 Aug 2018 22:49:06 -0400 Subject: [PATCH 01/41] Add extends attributes for several types Part of #670 --- crates/js-sys/src/lib.rs | 16 ++++++++++++++++ crates/js-sys/tests/wasm/Array.rs | 8 ++++++++ crates/js-sys/tests/wasm/ArrayBuffer.rs | 8 ++++++++ crates/js-sys/tests/wasm/Boolean.rs | 8 ++++++++ crates/js-sys/tests/wasm/DataView.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/Date.rs | 8 ++++++++ crates/js-sys/tests/wasm/Error.rs | 8 ++++++++ crates/js-sys/tests/wasm/Function.rs | 7 +++++++ crates/js-sys/tests/wasm/Map.rs | 8 ++++++++ crates/js-sys/tests/wasm/Number.rs | 8 ++++++++ crates/js-sys/tests/wasm/Set.rs | 8 ++++++++ crates/js-sys/tests/wasm/Symbol.rs | 1 + crates/js-sys/tests/wasm/WeakMap.rs | 8 ++++++++ crates/js-sys/tests/wasm/WeakSet.rs | 8 ++++++++ 14 files changed, 118 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 30ec0855..e0f01f7b 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -125,6 +125,7 @@ extern "C" { // Array #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Array; @@ -347,6 +348,7 @@ extern "C" { // ArrayBuffer #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type ArrayBuffer; @@ -421,6 +423,7 @@ extern "C" { // Boolean #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Boolean; @@ -440,6 +443,7 @@ extern "C" { // DataView #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type DataView; @@ -587,6 +591,7 @@ extern "C" { // Error #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Error; @@ -625,6 +630,8 @@ extern "C" { // Float32Array #[wasm_bindgen] extern "C" { + // TODO Uncomment this once TypedArray is added: + // #[wasm_bindgen(extends = Object, extends = TypedArray)] #[derive(Clone, Debug)] pub type Float32Array; @@ -677,6 +684,8 @@ extern "C" { // Float64Array #[wasm_bindgen] extern "C" { + // TODO Uncomment this once TypedArray is added: + // #[wasm_bindgen(extends = Object, extends = TypedArray)] #[derive(Clone, Debug)] pub type Float64Array; @@ -729,6 +738,7 @@ extern "C" { // Function #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Function; @@ -1007,6 +1017,7 @@ extern "C" { // Map #[wasm_bindgen] extern { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Map; @@ -1385,6 +1396,7 @@ extern "C" { // Number. #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Number; @@ -1482,6 +1494,7 @@ extern "C" { // Date. #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Date; @@ -2265,6 +2278,7 @@ extern { // Set #[wasm_bindgen] extern { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Set; @@ -2559,6 +2573,7 @@ extern "C" { // WeakMap #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type WeakMap; @@ -2602,6 +2617,7 @@ extern "C" { // WeakSet #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type WeakSet; diff --git a/crates/js-sys/tests/wasm/Array.rs b/crates/js-sys/tests/wasm/Array.rs index 9525a913..ae171bb7 100644 --- a/crates/js-sys/tests/wasm/Array.rs +++ b/crates/js-sys/tests/wasm/Array.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; macro_rules! js_array { @@ -286,3 +287,10 @@ fn for_each() { assert_eq!(sum_indices_of_evens(&js_array![1, 3, 5, 7]), 0); assert_eq!(sum_indices_of_evens(&js_array![3, 5, 7, 10]), 3); } + +#[wasm_bindgen_test] +fn array_inheritance() { + let array = js_array![0]; + assert!(array.is_instance_of::()); + assert!(array.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/ArrayBuffer.rs b/crates/js-sys/tests/wasm/ArrayBuffer.rs index ddfcdeef..21fa0491 100644 --- a/crates/js-sys/tests/wasm/ArrayBuffer.rs +++ b/crates/js-sys/tests/wasm/ArrayBuffer.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -34,3 +35,10 @@ fn slice_with_end() { let slice = buf.slice_with_end(1, 2); assert!(JsValue::from(slice).is_object()); } + +#[wasm_bindgen_test] +fn arraybuffer_inheritance() { + let buf = ArrayBuffer::new(4); + assert!(buf.is_instance_of::()); + assert!(buf.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Boolean.rs b/crates/js-sys/tests/wasm/Boolean.rs index 6f0befaa..34c53e01 100644 --- a/crates/js-sys/tests/wasm/Boolean.rs +++ b/crates/js-sys/tests/wasm/Boolean.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -11,3 +12,10 @@ fn new_undefined() { fn new_truely() { assert_eq!(Boolean::new(&JsValue::from("foo")).value_of(), true); } + +#[wasm_bindgen_test] +fn boolean_inheritance() { + let b = Boolean::new(&JsValue::from(true)); + assert!(b.is_instance_of::()); + assert!(b.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/DataView.rs b/crates/js-sys/tests/wasm/DataView.rs index b7d952ae..8efe0c6d 100644 --- a/crates/js-sys/tests/wasm/DataView.rs +++ b/crates/js-sys/tests/wasm/DataView.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -37,3 +38,16 @@ fn test() { // TODO: figure out how to do `bytes[2]` bytes.subarray(2, 3).for_each(&mut |x, _, _| assert_eq!(x, 42)); } + +#[wasm_bindgen_test] +fn dataview_inheritance() { + let bytes = Int8Array::new(&JsValue::from(10)); + + // TODO: figure out how to do `bytes[2] = 2` + bytes.subarray(2, 3).fill(2, 0, 1); + + let v = DataView::new(&bytes.buffer(), 2, 8); + + assert!(v.is_instance_of::()); + assert!(v.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Date.rs b/crates/js-sys/tests/wasm/Date.rs index 22286796..c4026b47 100644 --- a/crates/js-sys/tests/wasm/Date.rs +++ b/crates/js-sys/tests/wasm/Date.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -406,3 +407,10 @@ fn value_of() { let date = Date::new(&Date::utc(2018f64, 6f64).into()); assert_eq!(date.value_of(), 1530403200000.0); } + +#[wasm_bindgen_test] +fn date_inheritance() { + let date = Date::new(&"August 19, 1975 23:15:30".into()); + assert!(date.is_instance_of::()); + assert!(date.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Error.rs b/crates/js-sys/tests/wasm/Error.rs index 7c258b52..abe856f1 100644 --- a/crates/js-sys/tests/wasm/Error.rs +++ b/crates/js-sys/tests/wasm/Error.rs @@ -1,5 +1,6 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -35,3 +36,10 @@ fn to_string() { error.set_name("error_name_1"); assert_eq!(JsValue::from(error.to_string()), "error_name_1: error message 1"); } + +#[wasm_bindgen_test] +fn error_inheritance() { + let error = Error::new("test"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Function.rs b/crates/js-sys/tests/wasm/Function.rs index f2339496..c7fed283 100644 --- a/crates/js-sys/tests/wasm/Function.rs +++ b/crates/js-sys/tests/wasm/Function.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen] @@ -60,3 +61,9 @@ fn name() { fn to_string() { assert!(MAX.to_string().length() > 0); } + +#[wasm_bindgen_test] +fn function_inheritance() { + assert!(MAX.is_instance_of::()); + assert!(MAX.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Map.rs b/crates/js-sys/tests/wasm/Map.rs index 6395b6bf..f9ba7740 100644 --- a/crates/js-sys/tests/wasm/Map.rs +++ b/crates/js-sys/tests/wasm/Map.rs @@ -1,4 +1,5 @@ use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -86,3 +87,10 @@ fn size() { map.set(&"bar".into(), &"baz".into()); assert_eq!(map.size(), 2); } + +#[wasm_bindgen_test] +fn map_inheritance() { + let map = Map::new(); + assert!(map.is_instance_of::()); + assert!(map.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Number.rs b/crates/js-sys/tests/wasm/Number.rs index fee72abc..9d43f8a2 100644 --- a/crates/js-sys/tests/wasm/Number.rs +++ b/crates/js-sys/tests/wasm/Number.rs @@ -2,6 +2,7 @@ use std::f64::{INFINITY, NAN}; use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen_test] @@ -104,3 +105,10 @@ fn to_exponential() { assert_eq!(Number::new(&123456.into()).to_exponential(2).unwrap(), "1.23e+5"); assert!(Number::new(&10.into()).to_exponential(101).is_err()); } + +#[wasm_bindgen_test] +fn number_inheritance() { + let n = Number::new(&JsValue::from(42)); + assert!(n.is_instance_of::()); + assert!(n.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Set.rs b/crates/js-sys/tests/wasm/Set.rs index 92cca443..419d66b8 100644 --- a/crates/js-sys/tests/wasm/Set.rs +++ b/crates/js-sys/tests/wasm/Set.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; fn set2vec(s: &Set) -> Vec { @@ -80,3 +81,10 @@ fn size() { set.add(&3.into()); assert_eq!(set.size(), 3); } + +#[wasm_bindgen_test] +fn set_inheritance() { + let set = Set::new(&JsValue::undefined()); + assert!(set.is_instance_of::()); + assert!(set.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/Symbol.rs b/crates/js-sys/tests/wasm/Symbol.rs index a34a1ca2..fbaacf2a 100644 --- a/crates/js-sys/tests/wasm/Symbol.rs +++ b/crates/js-sys/tests/wasm/Symbol.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen(module = "tests/wasm/Symbol.js")] diff --git a/crates/js-sys/tests/wasm/WeakMap.rs b/crates/js-sys/tests/wasm/WeakMap.rs index b6c4c405..8cfe464c 100644 --- a/crates/js-sys/tests/wasm/WeakMap.rs +++ b/crates/js-sys/tests/wasm/WeakMap.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen] @@ -50,3 +51,10 @@ fn delete() { map.delete(&key); assert!(!map.has(&key)); } + +#[wasm_bindgen_test] +fn weakmap_inheritance() { + let map = WeakMap::new(); + assert!(map.is_instance_of::()); + assert!(map.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/WeakSet.rs b/crates/js-sys/tests/wasm/WeakSet.rs index 0d996334..dc70ca8b 100644 --- a/crates/js-sys/tests/wasm/WeakSet.rs +++ b/crates/js-sys/tests/wasm/WeakSet.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen] @@ -40,3 +41,10 @@ fn delete() { assert!(!set.has(&value)); assert!(!set.delete(&value)); } + +#[wasm_bindgen_test] +fn weakset_inheritance() { + let set = WeakSet::new(); + assert!(set.is_instance_of::()); + assert!(set.is_instance_of::()); +} From cc76963bad43c1454d282453e93b0e8b22e52acc Mon Sep 17 00:00:00 2001 From: Mario Reder Date: Thu, 9 Aug 2018 17:46:38 +0200 Subject: [PATCH 02/41] guide: typo in arbitrary data with serde --- guide/src/reference/arbitrary-data-with-serde.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/reference/arbitrary-data-with-serde.md b/guide/src/reference/arbitrary-data-with-serde.md index a8eb7065..070a61e4 100644 --- a/guide/src/reference/arbitrary-data-with-serde.md +++ b/guide/src/reference/arbitrary-data-with-serde.md @@ -89,7 +89,7 @@ pub fn receive_example_from_js(val: &JsValue) { ## JavaScript Usage -In the `JsValue` that JavaScript gets, `fied1` will be an `Object` (not a +In the `JsValue` that JavaScript gets, `field1` will be an `Object` (not a JavaScript `Map`), `field2` will be a JavaScript `Array` whose members are `Array`s of numbers, and `field3` will be an `Array` of numbers. From 1e02ca7eab7a26ce15d578c3ae7f28b291790c60 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Thu, 9 Aug 2018 18:07:41 +0100 Subject: [PATCH 03/41] Add support for modules to the backend. --- crates/backend/src/ast.rs | 15 +++++++++++++++ crates/backend/src/codegen.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 57869a89..08c64975 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -19,6 +19,19 @@ pub struct Program { pub structs: Vec, /// rust consts pub consts: Vec, + /// rust submodules + pub modules: Vec, +} + +/// A rust module +/// +/// This exists to give the ability to namespace js imports. +#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +pub struct Module { + /// module name + pub name: String, + /// js -> rust interfaces + pub imports: Vec, } /// A rust to js interface. Allows interaction with rust objects/functions @@ -227,6 +240,8 @@ impl Program { structs: self.structs.iter().map(|a| a.shared()).collect(), enums: self.enums.iter().map(|a| a.shared()).collect(), imports: self.imports.iter() + // add in imports from inside modules + .chain(self.modules.iter().flat_map(|m| m.imports.iter())) .map(|a| a.shared()) .collect::>()?, version: shared::version(), diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a40cbf3a..5274a0ce 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -43,6 +43,8 @@ impl TryToTokens for ast::Program { for i in self.imports.iter() { DescribeImport(&i.kind).to_tokens(tokens); + // If there is a js namespace, check that name isn't a type. If it is, + // this import might be a method on that type. if let Some(ns) = &i.js_namespace { if types.contains(ns) && i.kind.fits_on_impl() { let kind = match i.kind.try_to_token_stream() { @@ -61,6 +63,11 @@ impl TryToTokens for ast::Program { errors.push(e); } } + for m in self.modules.iter() { + if let Err(e) = m.try_to_tokens(tokens) { + errors.push(e); + } + } for e in self.enums.iter() { e.to_tokens(tokens); } @@ -87,6 +94,7 @@ impl TryToTokens for ast::Program { // Each JSON blob is prepended with the length of the JSON blob so when // all these sections are concatenated in the final wasm file we know // how to extract all the JSON pieces, so insert the byte length here. + // The value is little-endian. let generated_static_length = description.len() + 4; let mut bytes = vec![ (description.len() >> 0) as u8, @@ -1103,6 +1111,29 @@ impl ToTokens for ast::Const { } } +impl TryToTokens for ast::Module { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { + let mut errors = Vec::new(); + for i in self.imports.iter() { + DescribeImport(&i.kind).to_tokens(tokens); + } + let name = &self.name; + let mut body = TokenStream::new(); + for i in self.imports.iter() { + if let Err(e) = i.kind.try_to_tokens(&mut body) { + errors.push(e); + } + } + Diagnostic::from_vec(errors)?; + (quote!{ + pub mod #name { + #body + } + }).to_tokens(tokens); + Ok(()) + } +} + /// Emits the necessary glue tokens for "descriptor", generating an appropriate /// symbol name as well as attributes around the descriptor function itself. struct Descriptor<'a, T>(&'a Ident, T); From c371c4a5091693b069b3286bd8c18695018f9d75 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 9 Aug 2018 14:36:37 -0400 Subject: [PATCH 04/41] Fixup from merge commit --- crates/js-sys/tests/wasm/Number.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/js-sys/tests/wasm/Number.rs b/crates/js-sys/tests/wasm/Number.rs index e67eefbc..9d43f8a2 100644 --- a/crates/js-sys/tests/wasm/Number.rs +++ b/crates/js-sys/tests/wasm/Number.rs @@ -1,18 +1,10 @@ use std::f64::{INFINITY, NAN}; -use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; use wasm_bindgen_test::*; use wasm_bindgen::JsCast; use js_sys::*; -#[wasm_bindgen_test] -fn number_inheritance() { - let number = Number::new(&JsValue::from(10)); - assert!(number.is_instance_of::()); - assert!(number.is_instance_of::()); -} - #[wasm_bindgen_test] fn is_finite() { assert!(Number::is_finite(&42.into())); From e3011d629e2f2f03cfed5d0da27bd8a37b1490c4 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 9 Aug 2018 13:08:30 -0700 Subject: [PATCH 05/41] js-sys: Promise methods should take JS things by shared reference --- crates/futures/src/lib.rs | 2 +- crates/js-sys/src/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/futures/src/lib.rs b/crates/futures/src/lib.rs index 992bc4e1..41bdd4e3 100644 --- a/crates/futures/src/lib.rs +++ b/crates/futures/src/lib.rs @@ -54,7 +54,7 @@ //! pub fn new() -> NextTick { //! // Create a resolved promise that will run its callbacks on the next //! // tick of the micro task queue. -//! let promise = js_sys::Promise::resolve(JsValue::NULL); +//! let promise = js_sys::Promise::resolve(&JsValue::NULL); //! // Convert the promise into a `JsFuture`. //! let inner = JsFuture::from(promise); //! NextTick { inner } diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 9452d0a8..25320c24 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3109,7 +3109,7 @@ extern { /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all #[wasm_bindgen(static_method_of = Promise)] - pub fn all(obj: JsValue) -> Promise; + pub fn all(obj: &JsValue) -> Promise; /// The `Promise.race(iterable)` method returns a promise that resolves or /// rejects as soon as one of the promises in the iterable resolves or @@ -3117,14 +3117,14 @@ extern { /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race #[wasm_bindgen(static_method_of = Promise)] - pub fn race(obj: JsValue) -> Promise; + pub fn race(obj: &JsValue) -> Promise; /// The `Promise.reject(reason)` method returns a `Promise` object that is /// rejected with the given reason. /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject #[wasm_bindgen(static_method_of = Promise)] - pub fn reject(obj: JsValue) -> Promise; + pub fn reject(obj: &JsValue) -> Promise; /// The `Promise.resolve(value)` method returns a `Promise` object that is /// resolved with the given value. If the value is a promise, that promise @@ -3134,7 +3134,7 @@ extern { /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve #[wasm_bindgen(static_method_of = Promise)] - pub fn resolve(obj: JsValue) -> Promise; + pub fn resolve(obj: &JsValue) -> Promise; /// The `catch()` method returns a `Promise` and deals with rejected cases /// only. It behaves the same as calling `Promise.prototype.then(undefined, From 96ad97a9f924d04c3ed56d98fb164ce1cfeaf444 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 9 Aug 2018 13:08:51 -0700 Subject: [PATCH 06/41] js-sys: Document that new bindings should take JS things by shared ref --- crates/js-sys/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 25320c24..b822f35e 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -31,17 +31,20 @@ use wasm_bindgen::prelude::*; // * Keep imports in alphabetical order. // // * Rename imports with `js_name = ...` according to the note about `camelCase` -// and `snake_case` in the module's documentation above. +// and `snake_case` in the module's documentation above. // // * Include the one sentence summary of the import from the MDN link in the -// module's documentation above, and the MDN link itself. +// module's documentation above, and the MDN link itself. // // * If a function or method can throw an exception, make it catchable by adding -// `#[wasm_bindgen(catch)]`. +// `#[wasm_bindgen(catch)]`. // // * Add a new `#[test]` into the appropriate file in the // `crates/js-sys/tests/wasm/` directory. If the imported function or method // can throw an exception, make sure to also add test coverage for that case. +// +// * Arguments that are `JsValue`s or imported JavaScript types should be taken +// by reference. #[wasm_bindgen] extern "C" { From 615f8fbc4d9c00af681fdeda13f51ecde7b64fd5 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Thu, 9 Aug 2018 21:38:37 +0100 Subject: [PATCH 07/41] Push updates - still WIP --- crates/backend/src/ast.rs | 25 ++++++++++--------- crates/backend/src/codegen.rs | 23 ++++++++++++++---- crates/web-sys/build.rs | 13 +++++----- crates/webidl/src/lib.rs | 45 +++++++++++++++++++++++++++-------- 4 files changed, 71 insertions(+), 35 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 08c64975..719f385f 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use proc_macro2::{Ident, Span}; use shared; use syn; @@ -20,18 +21,7 @@ pub struct Program { /// rust consts pub consts: Vec, /// rust submodules - pub modules: Vec, -} - -/// A rust module -/// -/// This exists to give the ability to namespace js imports. -#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] -pub struct Module { - /// module name - pub name: String, - /// js -> rust interfaces - pub imports: Vec, + pub modules: HashMap, } /// A rust to js interface. Allows interaction with rust objects/functions @@ -233,6 +223,15 @@ pub enum ConstValue { Null, } +/// A rust module +/// +/// This exists to give the ability to namespace js imports. +#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +pub struct Module { + /// js -> rust interfaces + pub imports: Vec, +} + impl Program { pub(crate) fn shared(&self) -> Result { Ok(shared::Program { @@ -241,7 +240,7 @@ impl Program { enums: self.enums.iter().map(|a| a.shared()).collect(), imports: self.imports.iter() // add in imports from inside modules - .chain(self.modules.iter().flat_map(|m| m.imports.iter())) + .chain(self.modules.values().flat_map(|m| m.imports.iter())) .map(|a| a.shared()) .collect::>()?, version: shared::version(), diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 5274a0ce..1908441d 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -64,7 +64,7 @@ impl TryToTokens for ast::Program { } } for m in self.modules.iter() { - if let Err(e) = m.try_to_tokens(tokens) { + if let Err(e) = ModuleInIter::from(m).try_to_tokens(tokens) { errors.push(e); } } @@ -1111,15 +1111,28 @@ impl ToTokens for ast::Const { } } -impl TryToTokens for ast::Module { +/// Struct to help implementing TryToTokens over the key/value pairs from the hashmap. +struct ModuleInIter<'a> { + name: &'a Ident, + module: &'a ast::Module +} + +impl<'a> From<(&'a Ident, &'a ast::Module)> for ModuleInIter<'a> { + fn from((name, module): (&'a Ident, &'a ast::Module)) -> ModuleInIter<'a> { + ModuleInIter { name, module } + } +} + +impl<'a> TryToTokens for ModuleInIter<'a> { fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { + let name = &self.name; + let imports = &self.module.imports; let mut errors = Vec::new(); - for i in self.imports.iter() { + for i in imports.iter() { DescribeImport(&i.kind).to_tokens(tokens); } - let name = &self.name; let mut body = TokenStream::new(); - for i in self.imports.iter() { + for i in imports.iter() { if let Err(e) = i.kind.try_to_tokens(&mut body) { errors.push(e); } diff --git a/crates/web-sys/build.rs b/crates/web-sys/build.rs index 8dbb232c..630975c9 100644 --- a/crates/web-sys/build.rs +++ b/crates/web-sys/build.rs @@ -13,6 +13,8 @@ use std::path; use std::process::{self, Command}; fn main() { + env_logger::init(); + if let Err(e) = try_main() { eprintln!("Error: {}", e); for c in e.iter_causes() { @@ -24,11 +26,9 @@ fn main() { fn try_main() -> Result<(), failure::Error> { println!("cargo:rerun-if-changed=build.rs"); - env_logger::init(); - println!("cargo:rerun-if-changed=webidls/enabled"); - let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?; + let entries = fs::read_dir("webidls/enabled").context("reading webidls/enabled directory")?; let mut source = SourceFile::default(); for entry in entries { let entry = entry.context("getting webidls/enabled/*.webidl entry")?; @@ -38,8 +38,7 @@ fn try_main() -> Result<(), failure::Error> { } println!("cargo:rerun-if-changed={}", path.display()); source = source.add_file(&path) - .with_context(|_| format!("reading contents of file \"{}\"", - path.display()))?; + .with_context(|_| format!("reading contents of file \"{}\"", path.display()))?; } let bindings = match wasm_bindgen_webidl::compile(&source.contents) { @@ -70,9 +69,9 @@ fn try_main() -> Result<(), failure::Error> { let status = Command::new("rustfmt") .arg(&out_file_path) .status() - .context("running rustfmt")?; + .context("running rustfmt")?; if !status.success() { - bail!("rustfmt failed: {}", status) + bail!("rustfmt failed: {}", status) } } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 0fb33c90..76d2cad4 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -185,13 +185,18 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { weedle::Definition::Implements(..) => { // nothing to do for this, ignore it } + weedle::Definition::Namespace(namespace) => { + namespace.webidl_parse(program, first_pass, ())? + } + weedle::Definition::PartialNamespace(namespace) => { + // TODO + warn!("Unsupported WebIDL definition: {:?}", self) + } // TODO weedle::Definition::Callback(..) | weedle::Definition::CallbackInterface(..) | weedle::Definition::Dictionary(..) - | weedle::Definition::PartialDictionary(..) - | weedle::Definition::Namespace(..) - | weedle::Definition::PartialNamespace(..) => { + | weedle::Definition::PartialDictionary(..) => { warn!("Unsupported WebIDL definition: {:?}", self) } } @@ -657,12 +662,17 @@ fn member_operation<'src>( 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::IndexingGetter, - weedle::interface::Special::Setter(weedle::term::Setter) => ::first_pass::OperationId::IndexingSetter, - weedle::interface::Special::Deleter(weedle::term::Deleter) => ::first_pass::OperationId::IndexingDeleter, - weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => return Ok(()), + weedle::interface::Special::Getter(weedle::term::Getter) => + ::first_pass::OperationId::IndexingGetter, + weedle::interface::Special::Setter(weedle::term::Setter) => + ::first_pass::OperationId::IndexingSetter, + weedle::interface::Special::Deleter(weedle::term::Deleter) => + ::first_pass::OperationId::IndexingDeleter, + weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller) => + return Ok(()), }, - Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())), + Some(ref name) if specials.is_empty() => + ::first_pass::OperationId::Operation(Some(name.clone())), _ => { warn!("Unsupported specials on type {:?}", (self_name, identifier)); return Ok(()) @@ -744,8 +754,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::EnumDefinition<'src> { variants: variants .iter() .map(|v| { - if !v.0.is_empty() { - rust_ident(camel_case_ident(&v.0).as_str()) + if !v.0.is_empty() { + rust_ident(camel_case_ident(&v.0).as_str()) } else { rust_ident("None") } @@ -783,3 +793,18 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> Ok(()) } } + +impl<'src> WebidlParse<'src, ()> for weedle::interface::NamespaceDefinition<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + record: &FirstPassRecord<'src>, + (): (), + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + + } +} From f9ac4e9c90cd42243a45052fab1896c2cb097482 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 9 Aug 2018 16:17:34 -0700 Subject: [PATCH 08/41] Always bind static operations to their class For example, `Promise.resolve` must always be called with the `Promise` constructor as its `this`, or else it will throw an error. --- crates/cli-support/src/js/mod.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index e52e6721..eb28b4ad 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1868,24 +1868,28 @@ impl<'a, 'b> SubContext<'a, 'b> { ), } } else { - let location = if *is_static { "" } else { ".prototype" }; + let (location, binding) = if *is_static { + ("", format!(".bind({})", class)) + } else { + (".prototype", "".into()) + }; match kind { shared::OperationKind::Regular => { - format!("{}{}.{}", class, location, import.function.name) + format!("{}{}.{}{}", class, location, import.function.name, binding) } shared::OperationKind::Getter(g) => { self.cx.expose_get_inherited_descriptor(); format!( - "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get", - class, location, g, + "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get{}", + class, location, g, binding, ) } shared::OperationKind::Setter(s) => { self.cx.expose_get_inherited_descriptor(); format!( - "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set", - class, location, s, + "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set{}", + class, location, s, binding, ) } shared::OperationKind::IndexingGetter => panic!("indexing getter should be structural"), From ff835948828113251bf046c3fc92946f03be1f88 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 9 Aug 2018 16:21:12 -0700 Subject: [PATCH 09/41] futures: Add sanity tests for conversion between Promises and Futures Part of #614 --- .travis.yml | 4 +-- crates/futures/Cargo.toml | 3 +++ crates/futures/tests/tests.rs | 51 +++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100755 crates/futures/tests/tests.rs diff --git a/.travis.yml b/.travis.yml index 2721f4c3..bfd64e01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,9 +62,9 @@ matrix: - cargo test --target wasm32-unknown-unknown --features serde-serialize # Make sure the `std` feature works if disabled - cargo test --target wasm32-unknown-unknown -p no-std - # Make sure the `wasm-bindgen-futures` tests pass. Right now, this just - # verifies that the example program in the crate level docs compiles. + # Make sure the `wasm-bindgen-futures` tests pass. - cargo test -p wasm-bindgen-futures + - cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown addons: firefox: latest if: branch = master diff --git a/crates/futures/Cargo.toml b/crates/futures/Cargo.toml index bffa5588..1c521f1d 100644 --- a/crates/futures/Cargo.toml +++ b/crates/futures/Cargo.toml @@ -7,3 +7,6 @@ authors = ["The wasm-bindgen Developers"] futures = "0.1.20" js-sys = { path = "../js-sys", version = '0.2.0' } wasm-bindgen = { path = "../..", version = '0.2.15' } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = { path = '../test', version = '0.2.15' } diff --git a/crates/futures/tests/tests.rs b/crates/futures/tests/tests.rs new file mode 100755 index 00000000..a6225737 --- /dev/null +++ b/crates/futures/tests/tests.rs @@ -0,0 +1,51 @@ +#![feature(use_extern_macros)] +#![cfg(target_arch = "wasm32")] + +extern crate futures; +extern crate js_sys; +extern crate wasm_bindgen; +extern crate wasm_bindgen_futures; +extern crate wasm_bindgen_test; + +use futures::Future; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::{future_to_promise, JsFuture}; +use wasm_bindgen_test::*; + +#[wasm_bindgen_test(async)] +fn promise_resolve_is_ok_future() -> impl Future { + let p = js_sys::Promise::resolve(&JsValue::from(42)); + JsFuture::from(p) + .map(|x| { + assert_eq!(x, 42); + }).map_err(|_| unreachable!()) +} + +#[wasm_bindgen_test(async)] +fn promise_reject_is_error_future() -> impl Future { + let p = js_sys::Promise::reject(&JsValue::from(42)); + JsFuture::from(p).map(|_| unreachable!()).or_else(|e| { + assert_eq!(e, 42); + Ok(()) + }) +} + +#[wasm_bindgen_test(async)] +fn ok_future_is_resolved_promise() -> impl Future { + let f = futures::future::ok(JsValue::from(42)); + let p = future_to_promise(f); + JsFuture::from(p) + .map(|x| { + assert_eq!(x, 42); + }).map_err(|_| unreachable!()) +} + +#[wasm_bindgen_test(async)] +fn error_future_is_rejected_promise() -> impl Future { + let f = futures::future::err(JsValue::from(42)); + let p = future_to_promise(f); + JsFuture::from(p).map(|_| unreachable!()).or_else(|e| { + assert_eq!(e, 42); + Ok(()) + }) +} From 70406fe18a84ad6502f68a129ece2e699fddfa8a Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 9 Aug 2018 19:21:06 -0400 Subject: [PATCH 10/41] Add support and tests for EvalError --- crates/js-sys/src/lib.rs | 17 +++++++++ crates/js-sys/tests/wasm/EvalError.rs | 55 +++++++++++++++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 73 insertions(+) create mode 100644 crates/js-sys/tests/wasm/EvalError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index e0f01f7b..b6b6e36a 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -627,6 +627,23 @@ extern "C" { pub fn to_string(this: &Error) -> JsString; } +// EvalError +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Object, extends = Error)] + #[derive(Clone, Debug)] + pub type EvalError; + + /// The EvalError object indicates an error regarding the global eval() function. This + /// exception is not thrown by JavaScript anymore, however the EvalError object remains for + /// compatibility. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> EvalError; +} + + // Float32Array #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/EvalError.rs b/crates/js-sys/tests/wasm/EvalError.rs new file mode 100644 index 00000000..d1deb73a --- /dev/null +++ b/crates/js-sys/tests/wasm/EvalError.rs @@ -0,0 +1,55 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +// Note: This error is not thrown any more, so there are no tests that will generate this error. +// Instead we just have to manually construct it + +#[wasm_bindgen_test] +fn new() { + let error = EvalError::new("some message"); + let base_error: &Error = error.dyn_ref().unwrap(); + assert_eq!(JsValue::from(base_error.message()), "some message"); +} + +#[wasm_bindgen_test] +fn set_message() { + let error = EvalError::new("test"); + let base_error: &Error = error.dyn_ref().unwrap(); + base_error.set_message("another"); + assert_eq!(JsValue::from(base_error.message()), "another"); +} + +#[wasm_bindgen_test] +fn name() { + let error = EvalError::new("test"); + let base_error: &Error = error.dyn_ref().unwrap(); + assert_eq!(JsValue::from(base_error.name()), "EvalError"); +} + +#[wasm_bindgen_test] +fn set_name() { + let error = EvalError::new("test"); + let base_error: &Error = error.dyn_ref().unwrap(); + base_error.set_name("different"); + assert_eq!(JsValue::from(base_error.name()), "different"); +} + +#[wasm_bindgen_test] +fn to_string() { + let error = EvalError::new("error message 1"); + let base_error: &Error = error.dyn_ref().unwrap(); + assert_eq!(JsValue::from(base_error.to_string()), "EvalError: error message 1"); + base_error.set_name("error_name_1"); + assert_eq!(JsValue::from(base_error.to_string()), "error_name_1: error message 1"); +} + + +#[wasm_bindgen_test] +fn evalerror_inheritance() { + let error = EvalError::new("some message"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 1c4414b0..9b8b2f53 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -14,6 +14,7 @@ pub mod Boolean; pub mod DataView; pub mod Date; pub mod Error; +pub mod EvalError; pub mod Function; pub mod Generator; pub mod Intl; From 23cb0ea656cd7447c80a3b31c17d629485a6a7d9 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Thu, 9 Aug 2018 20:54:13 -0400 Subject: [PATCH 11/41] Add initial support and tests for JSON --- crates/js-sys/src/lib.rs | 25 ++++++++++++ crates/js-sys/tests/wasm/JSON.rs | 69 ++++++++++++++++++++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 95 insertions(+) create mode 100644 crates/js-sys/tests/wasm/JSON.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index e0f01f7b..caaf9cda 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2663,6 +2663,31 @@ extern "C" { pub fn validate(bufferSource: &JsValue) -> Result; } +// JSON +#[wasm_bindgen] +extern "C" { + + #[derive(Clone, Debug)] + pub type JSON; + + /// The `JSON.parse()` method parses a JSON string, constructing the + /// JavaScript value or object described by the string. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse + #[wasm_bindgen(catch, static_method_of = JSON)] + pub fn parse(text: &str) -> Result; + + /// The JSON.stringify() method converts a JavaScript value to a JSON string, + /// optionally replacing values if a replacer function is specified or + /// optionally including only the specified properties if a replacer array is + /// specified. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify + #[wasm_bindgen(static_method_of = JSON)] + pub fn stringify(obj: &JsValue) -> JsString; + +} + // JsString #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/JSON.rs b/crates/js-sys/tests/wasm/JSON.rs new file mode 100644 index 00000000..70ece6e8 --- /dev/null +++ b/crates/js-sys/tests/wasm/JSON.rs @@ -0,0 +1,69 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn parse_array() { + + let js_array = JSON::parse("[1, 2, 3]").unwrap();; + assert!(Array::is_array(&js_array)); + + let array = Array::from(&js_array); + assert_eq!(array.length(), 3); + assert_eq!(array.pop(), 3); + assert_eq!(array.pop(), 2); + assert_eq!(array.pop(), 1); + +} + +#[wasm_bindgen_test] +fn parse_object() { + + let js_object = JSON::parse("{\"x\": 5, \"y\": true, \"z\": [\"foo\", \"bar\"]}").unwrap(); + assert!(js_object.is_object()); + + let obj = Object::from(js_object); + let keys = Object::keys(&obj); + assert_eq!(keys.length(), 3); + assert_eq!(keys.pop().as_string().unwrap(), "z"); + assert_eq!(keys.pop().as_string().unwrap(), "y"); + assert_eq!(keys.pop().as_string().unwrap(), "x"); + + let values = Object::values(&obj); + assert_eq!(values.length(), 3); + + let z = values.pop(); + assert!(Array::is_array(&z)); + let z_array = Array::from(&z); + assert_eq!(z_array.length(), 2); + + let y = values.pop(); + assert_eq!(y.as_bool(), Some(true)); + + let x = values.pop(); + assert!(Number::is_integer(&x)); + let x_num = Number::new(&x); + assert_eq!(x_num.value_of(), 5.0); + +} + +#[wasm_bindgen_test] +fn parse_error() { + let js_object = JSON::parse("invalid json"); + assert!(js_object.is_err()); + let err = js_object.unwrap_err(); + assert!(err.is_instance_of::()); +} + +#[wasm_bindgen_test] +fn stringify() { + let arr = Array::new(); + arr.push(&JsValue::from(1)); + arr.push(&JsValue::from(true)); + arr.push(&JsValue::from("hello")); + + let str = JSON::stringify(&JsValue::from(arr)); + let rust_str: String = From::from(str); + assert_eq!(rust_str, "[1,true,\"hello\"]"); +} \ No newline at end of file diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 1c4414b0..fdc5bbef 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -18,6 +18,7 @@ pub mod Function; pub mod Generator; pub mod Intl; pub mod JsString; +pub mod JSON; pub mod Map; pub mod MapIterator; pub mod Math; From 56b0f64d0baba03d062f47d1ef85bad57fa31825 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 10 Aug 2018 10:29:16 +0100 Subject: [PATCH 12/41] Fix warning in doc gen --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2f6a2be0..a4290155 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -790,7 +790,7 @@ pub mod __rt { /// above. That means if this function is called and referenced we'll pull /// in the object file and link the intrinsics. /// - /// Note that this is an #[inline] function to remove the function call + /// Note that this is an `#[inline]` function to remove the function call /// overhead we inject in functions, but right now it's unclear how to do /// this in a zero-cost fashion. The lowest cost seems to be generating a /// store that can't be optimized away (to a global), which is listed below. From 6c1f32fa5b98b84d77c6105e910ef24cd7004c80 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 10 Aug 2018 17:06:11 +0100 Subject: [PATCH 13/41] Saving commit --- crates/backend/src/ast.rs | 1 + crates/webidl/src/first_pass.rs | 54 ++++++++++-- crates/webidl/src/lib.rs | 150 +++++++++++++++++++++++++++++++- crates/webidl/src/util.rs | 134 +++++++++++++++++++++++----- 4 files changed, 307 insertions(+), 32 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 719f385f..0a495de5 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -227,6 +227,7 @@ pub enum ConstValue { /// /// This exists to give the ability to namespace js imports. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] +#[derive(Default)] pub struct Module { /// js -> rust interfaces pub imports: Vec, diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 4e58443a..004dc31a 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -28,6 +28,7 @@ pub(crate) struct FirstPassRecord<'src> { /// The mixins, mapping their name to the webidl ast node for the mixin. pub(crate) mixins: BTreeMap<&'src str, Vec<&'src MixinMembers<'src>>>, pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, + pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>, } /// We need to collect interface data during the first pass, to be used later. @@ -40,6 +41,34 @@ pub(crate) struct InterfaceData<'src> { pub(crate) superclass: Option<&'src str>, } +impl<'src> Default for InterfaceData<'src> { + fn default() -> Self { + InterfaceData { + partial: true, + global: false, + operations: Default::default(), + superclass: Default::default(), + } + } +} + +/// We need to collect namespace data during the first pass, to be used later. +#[derive(Default)] +pub(crate) struct NamespaceData<'src> { + /// Whether only partial namespaces were encountered + pub(crate) partial: bool, + pub(crate) operations: BTreeMap, OperationData<'src>>, +} + +impl<'src> Default for NamespaceData<'src> { + fn default() -> Self { + NamespaceData { + partial: true, + operations: Default::default(), + } + } +} + #[derive(PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum OperationId<'src> { Constructor, @@ -83,6 +112,8 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { PartialInterface(interface) => interface.first_pass(record, ()), InterfaceMixin(mixin) => mixin.first_pass(record, ()), PartialInterfaceMixin(mixin) => mixin.first_pass(record, ()), + Namespace(namespace) => namespace.first_pass(record, ()), + PartialNamespace(namespace) => namespace.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), _ => { // Other definitions aren't currently used in the first pass @@ -111,7 +142,8 @@ impl<'src> FirstPass<'src, ()> for weedle::EnumDefinition<'src> { } } -fn first_pass_operation<'src>( +/// Helper function to add an operation to an interface. +fn first_pass_interface_operation<'src>( record: &mut FirstPassRecord<'src>, self_name: &'src str, id: OperationId<'src>, @@ -199,7 +231,7 @@ 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 { ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { - first_pass_operation( + first_pass_interface_operation( record, self_name, OperationId::Constructor, @@ -207,7 +239,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ) } ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { - first_pass_operation( + first_pass_interface_operation( record, self_name, OperationId::Constructor, @@ -217,7 +249,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::NamedArgList(list) if list.lhs_identifier.0 == "NamedConstructor" => { - first_pass_operation( + first_pass_interface_operation( record, self_name, OperationId::Constructor, @@ -258,16 +290,20 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM warn!("Unsupported webidl operation {:?}", self); return Ok(()) } - first_pass_operation( + first_pass_interface_operation( record, 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::IndexingGetter, - Some(weedle::interface::Special::Setter(weedle::term::Setter)) => OperationId::IndexingSetter, - Some(weedle::interface::Special::Deleter(weedle::term::Deleter)) => OperationId::IndexingDeleter, - Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller)) => return Ok(()), + Some(weedle::interface::Special::Getter(weedle::term::Getter)) + => OperationId::IndexingGetter, + Some(weedle::interface::Special::Setter(weedle::term::Setter)) + => OperationId::IndexingSetter, + Some(weedle::interface::Special::Deleter(weedle::term::Deleter)) + => OperationId::IndexingDeleter, + Some(weedle::interface::Special::LegacyCaller(weedle::term::LegacyCaller)) + => return Ok(()), }, Some(ref name) => OperationId::Operation(Some(name.clone())), }, diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 76d2cad4..7fcf0460 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -38,7 +38,7 @@ use backend::TryToTokens; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; use backend::util::{ident_ty, rust_ident, wrap_import_function}; use failure::ResultExt; -use heck::{ShoutySnakeCase}; +use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use weedle::argument::Argument; use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; @@ -308,7 +308,7 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend interface: &'src weedle::InterfaceDefinition<'src>, ) -> Result<()> { let mut add_constructor = |arguments: &[Argument], class: &str| { - let (overloaded, same_argument_names) = first_pass.get_operation_overloading( + let (overloaded, same_argument_names) = first_pass.get_method_overloading( arguments, &::first_pass::OperationId::Constructor, interface.identifier.0, @@ -794,7 +794,7 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> } } -impl<'src> WebidlParse<'src, ()> for weedle::interface::NamespaceDefinition<'src> { +impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, @@ -805,6 +805,150 @@ impl<'src> WebidlParse<'src, ()> for weedle::interface::NamespaceDefinition<'src return Ok(()); } + let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); + let js_name = &self.identifier.0; + program.modules.entry(rust_name.clone()) + .and_modify(|_| warn!("Namespace with rust name {:?} added more than once", rust_name)) + .or_default(); + + for member in self.members.body.iter() { + member.webidl_parse(program, record, js_name)? + } + Ok(()) } } + +impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + record: &FirstPassRecord<'src>, + module_name: &'src str + ) -> Result<()> { + match self { + weedle::namespace::NamespaceMember::Operation(op) => { + op.webidl_parse(program, record, module_name)?; + } + weedle::namespace::NamespaceMember::Attribute(_) => { + warn!("Attribute namespace members are not supported") + } + } + Ok(()) + } +} + +impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + record: &FirstPassRecord<'src>, + module_name: &'src str + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + let structural = util::is_structural(&self.attributes); + + let name = match &self.identifier { + Some(ident) => ident.0, + None => { + warn!("Operations without identifiers are not supported"); + return Ok(()); + } + + let import_fn = first_pass + .create_function( + name, + false, + same_argument_names: bool, + arguments: &[Argument], + mut ret: Option, + kind: backend::ast::ImportFunctionKind, + structural: bool, + catch: bool, + doc_comment: Option, + ) -> Option + .create_basic_method( + args, + match identifier.map(|s| s.0) { + Some(ref name) if specials.is_empty() => + ::first_pass::OperationId::Operation(Some(name.clone())), + }, + &self.return_type, + self_name, + specials.len() == 1 || first_pass + .interfaces + .get(self_name) + .map(|interface_data| interface_data.global) + .unwrap_or(false), + util::throws(&self.attributes), + ) + .map(wrap_import_function) + .map(|import| program.imports.push(import)); + Ok(()) + } +} + +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::IndexingGetter, + weedle::interface::Special::Setter(weedle::term::Setter) => + ::first_pass::OperationId::IndexingSetter, + weedle::interface::Special::Deleter(weedle::term::Deleter) => + ::first_pass::OperationId::IndexingDeleter, + 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(()) +} diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 554f90f0..3320c38b 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -12,7 +12,7 @@ use weedle::common::Identifier; use weedle::types::*; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; -use first_pass::FirstPassRecord; +use first_pass::{self, FirstPassRecord}; /// Take a type and create an immutable shared reference to that type. fn shared_ref(ty: syn::Type) -> syn::Type { @@ -830,6 +830,8 @@ impl<'src> FirstPassRecord<'src> { } /// Create a wasm-bindgen function, if possible. + /// + /// Currently fails on any variadic args. pub fn create_function( &self, name: &str, @@ -910,31 +912,31 @@ impl<'src> FirstPassRecord<'src> { pub fn create_basic_method( &self, arguments: &[weedle::argument::Argument], - operation_id: ::first_pass::OperationId, + operation_id: first_pass::OperationId, return_type: &weedle::types::ReturnType, self_name: &str, is_static: bool, structural: bool, catch: bool, ) -> Option { - let (overloaded, same_argument_names) = self.get_operation_overloading( + let (overloaded, same_argument_names) = self.get_method_overloading( arguments, &operation_id, self_name, ); let name = match &operation_id { - ::first_pass::OperationId::Constructor => panic!("constructors are unsupported"), - ::first_pass::OperationId::Operation(name) => match name { + first_pass::OperationId::Constructor => panic!("constructors are unsupported"), + first_pass::OperationId::Operation(name) => match name { None => { warn!("Operations without a name are unsupported"); return None; } Some(name) => name.to_string(), }, - ::first_pass::OperationId::IndexingGetter => "get".to_string(), - ::first_pass::OperationId::IndexingSetter => "set".to_string(), - ::first_pass::OperationId::IndexingDeleter => "delete".to_string(), + first_pass::OperationId::IndexingGetter => "get".to_string(), + first_pass::OperationId::IndexingSetter => "set".to_string(), + first_pass::OperationId::IndexingDeleter => "delete".to_string(), }; let kind = backend::ast::ImportFunctionKind::Method { @@ -943,11 +945,11 @@ impl<'src> FirstPassRecord<'src> { kind: backend::ast::MethodKind::Operation(backend::ast::Operation { is_static, kind: match &operation_id { - ::first_pass::OperationId::Constructor => panic!("constructors are unsupported"), - ::first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular, - ::first_pass::OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter, - ::first_pass::OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter, - ::first_pass::OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter, + first_pass::OperationId::Constructor => panic!("constructors are unsupported"), + first_pass::OperationId::Operation(_) => backend::ast::OperationKind::Regular, + first_pass::OperationId::IndexingGetter => backend::ast::OperationKind::IndexingGetter, + first_pass::OperationId::IndexingSetter => backend::ast::OperationKind::IndexingSetter, + first_pass::OperationId::IndexingDeleter => backend::ast::OperationKind::IndexingDeleter, }, }), }; @@ -965,17 +967,17 @@ impl<'src> FirstPassRecord<'src> { } }; let doc_comment = match &operation_id { - ::first_pass::OperationId::Constructor => panic!("constructors are unsupported"), - ::first_pass::OperationId::Operation(_) => Some( + first_pass::OperationId::Constructor => panic!("constructors are unsupported"), + first_pass::OperationId::Operation(_) => Some( format!( "The `{}()` method\n\n{}", name, mdn_doc(self_name, Some(&name)) ) ), - ::first_pass::OperationId::IndexingGetter => Some("The indexing getter\n\n".to_string()), - ::first_pass::OperationId::IndexingSetter => Some("The indexing setter\n\n".to_string()), - ::first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()), + first_pass::OperationId::IndexingGetter => Some("The indexing getter\n\n".to_string()), + first_pass::OperationId::IndexingSetter => Some("The indexing setter\n\n".to_string()), + first_pass::OperationId::IndexingDeleter => Some("The indexing deleter\n\n".to_string()), }; self.create_function( @@ -993,10 +995,10 @@ impl<'src> FirstPassRecord<'src> { /// Whether operation is overloaded and /// whether there overloads with same argument names for given argument types - pub fn get_operation_overloading( + pub fn get_method_overloading( &self, arguments: &[weedle::argument::Argument], - id: &::first_pass::OperationId, + id: &first_pass::OperationId, self_name: &str, ) -> (bool, bool) { let data = match self.interfaces.get(self_name) { @@ -1023,6 +1025,98 @@ impl<'src> FirstPassRecord<'src> { ) } + /// Create a wasm-bindgen operation (free function with no `self` type), if possible. + pub fn create_namespace_operation( + &self, + arguments: &[weedle::argument::Argument], + operation_name: &str, + return_type: &weedle::types::ReturnType, + structural: bool, + catch: bool, + ) -> Option { + let (overloaded, same_argument_names) = self.get_namespaced_operation_overloading( + arguments, + &first_pass::OperationId::Operation(Some(operation_name)) + self_name, + ); + + let name = match &operation_id { + first_pass::OperationId::Operation(name) => match name { + None => { + warn!("Operations without a name are unsupported"); + return None; + } + Some(name) => name.to_string(), + }, + _ => { + warn!("operations in namespaces must be normal functions"); + return None; + } + }; + + let kind = backend::ast::ImportFunctionKind::Normal; + + let ret = match return_type { + 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; + } + Some(ty) => Some(ty), + } + } + }; + let doc_comment = format!("The `{}.{}()` function\n\n{}", + name, + mdn_doc(self_name, Some(&name))); + + self.create_function( + &name, + overloaded, + same_argument_names, + arguments, + ret, + kind, + structural, + catch, + doc_comment, + ) + } + + /// Whether operation is overloaded and + /// whether there overloads with same argument names for given argument types + pub fn get_namespaced_operation_overloading( + &self, + arguments: &[weedle::argument::Argument], + id: &first_pass::OperationId, + namespace_name: &str, + ) -> (bool, bool) { + let data = match self.namespaces.get(namespace_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, From 95c55d0b4cdf9fa5a1ae3cab815fe8c5066556e5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 10:29:22 -0700 Subject: [PATCH 14/41] js-sys: Add bindings to `Array.of` --- crates/js-sys/src/lib.rs | 34 +++++++++++++++++++++++++++++++ crates/js-sys/tests/wasm/Array.rs | 13 ++++++++++++ 2 files changed, 47 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 252e0b30..dec2e5f3 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -250,6 +250,40 @@ extern "C" { #[wasm_bindgen(method)] pub fn map(this: &Array, predicate: &mut FnMut(JsValue, u32, Array) -> JsValue) -> Array; + /// The `Array.of()` method creates a new Array instance with a variable + /// number of arguments, regardless of number or type of the arguments. + /// + /// The difference between `Array.of()` and the `Array` constructor is in the + /// handling of integer arguments: `Array.of(7)` creates an array with a single + /// element, `7`, whereas `Array(7)` creates an empty array with a `length` + /// property of `7` (Note: this implies an array of 7 empty slots, not slots + /// with actual undefined values). + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + /// + /// # Notes + /// + /// There are a few bindings to `of` in `js-sys`: `of1`, `of2`, etc... + /// with different arities. + #[wasm_bindgen(static_method_of = Array, js_name = of)] + pub fn of1(a: &JsValue) -> Array; + + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + #[wasm_bindgen(static_method_of = Array, js_name = of)] + pub fn of2(a: &JsValue, b: &JsValue) -> Array; + + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + #[wasm_bindgen(static_method_of = Array, js_name = of)] + pub fn of3(a: &JsValue, b: &JsValue, c: &JsValue) -> Array; + + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + #[wasm_bindgen(static_method_of = Array, js_name = of)] + pub fn of4(a: &JsValue, b: &JsValue, c: &JsValue, d: &JsValue) -> Array; + + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of + #[wasm_bindgen(static_method_of = Array, js_name = of)] + pub fn of5(a: &JsValue, b: &JsValue, c: &JsValue, d: &JsValue, e: &JsValue) -> Array; + /// The pop() method removes the last element from an array and returns that /// element. This method changes the length of the array. /// diff --git a/crates/js-sys/tests/wasm/Array.rs b/crates/js-sys/tests/wasm/Array.rs index 15c2d463..604bbf85 100644 --- a/crates/js-sys/tests/wasm/Array.rs +++ b/crates/js-sys/tests/wasm/Array.rs @@ -126,6 +126,19 @@ fn copy_within() { assert_eq!(to_rust(&characters)[5], JsValue::from(3)); } +#[wasm_bindgen_test] +fn of() { + let a = JsValue::from("a"); + let b = JsValue::from("b"); + let c = JsValue::from("c"); + let arr = Array::of3(&a, &b, &c); + let vec = to_rust(&arr); + assert_eq!(vec.len(), 3); + assert_eq!(vec[0], a); + assert_eq!(vec[1], b); + assert_eq!(vec[2], c); +} + #[wasm_bindgen_test] fn pop() { let characters = js_array![8, 5, 4, 3, 1, 2]; From e667400a836683052675b05a6fc952d07e372d11 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 10:29:35 -0700 Subject: [PATCH 15/41] js-sys: Remove unused import in symbol tests --- crates/js-sys/tests/wasm/Symbol.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/js-sys/tests/wasm/Symbol.rs b/crates/js-sys/tests/wasm/Symbol.rs index fbaacf2a..a34a1ca2 100644 --- a/crates/js-sys/tests/wasm/Symbol.rs +++ b/crates/js-sys/tests/wasm/Symbol.rs @@ -1,6 +1,5 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; -use wasm_bindgen::JsCast; use js_sys::*; #[wasm_bindgen(module = "tests/wasm/Symbol.js")] From a66c4de8925d227c0debe0c197073dd5cc1368a1 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 10:40:43 -0700 Subject: [PATCH 16/41] js-sys: remove extra new line --- crates/js-sys/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index dec2e5f3..bda4a73a 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -680,7 +680,6 @@ extern "C" { pub fn new(message: &str) -> EvalError; } - // Float32Array #[wasm_bindgen] extern "C" { From 7f5d0a215810949665db8bdee2398f9507a83e91 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 10:46:53 -0700 Subject: [PATCH 17/41] js-sys: Move `Intl` from a type to a module, since it is a namespace --- crates/js-sys/src/lib.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index bda4a73a..1c4639d8 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3127,19 +3127,21 @@ extern "C" { pub fn value_of(this: &Symbol) -> Symbol; } -// Intl -#[wasm_bindgen] -extern "C" { - #[derive(Clone, Debug)] - pub type Intl; +#[allow(non_snake_case)] +pub mod Intl { + use super::*; - /// The `Intl.getCanonicalLocales()` method returns an array containing - /// the canonical locale names. Duplicates will be omitted and elements - /// will be validated as structurally valid language tags. - /// - /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales - #[wasm_bindgen(static_method_of = Intl, js_name = getCanonicalLocales)] - pub fn get_canonical_locales(s: &JsValue) -> Array; + // Intl + #[wasm_bindgen] + extern "C" { + /// The `Intl.getCanonicalLocales()` method returns an array containing + /// the canonical locale names. Duplicates will be omitted and elements + /// will be validated as structurally valid language tags. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales + #[wasm_bindgen(js_name = getCanonicalLocales, js_namespace = Intl)] + pub fn get_canonical_locales(s: &JsValue) -> Array; + } } // Promise From 0d897e9b8d0112104466d0aa66e0d60e96963fa0 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 10 Aug 2018 19:00:56 +0100 Subject: [PATCH 18/41] Unsure about error --- crates/backend/src/ast.rs | 6 +- crates/backend/src/codegen.rs | 3 +- crates/webidl/src/first_pass.rs | 107 ++++++++++++++++--- crates/webidl/src/lib.rs | 177 ++++++++++++-------------------- crates/webidl/src/util.rs | 37 +++---- 5 files changed, 178 insertions(+), 152 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 0a495de5..a95fbe61 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use proc_macro2::{Ident, Span}; use shared; use syn; @@ -21,7 +21,7 @@ pub struct Program { /// rust consts pub consts: Vec, /// rust submodules - pub modules: HashMap, + pub modules: BTreeMap, } /// A rust to js interface. Allows interaction with rust objects/functions @@ -227,8 +227,8 @@ pub enum ConstValue { /// /// This exists to give the ability to namespace js imports. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] -#[derive(Default)] pub struct Module { + pub vis: syn::Visibility, /// js -> rust interfaces pub imports: Vec, } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 1908441d..ac8f1096 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -1131,6 +1131,7 @@ impl<'a> TryToTokens for ModuleInIter<'a> { for i in imports.iter() { DescribeImport(&i.kind).to_tokens(tokens); } + let vis = &self.module.vis; let mut body = TokenStream::new(); for i in imports.iter() { if let Err(e) = i.kind.try_to_tokens(&mut body) { @@ -1139,7 +1140,7 @@ impl<'a> TryToTokens for ModuleInIter<'a> { } Diagnostic::from_vec(errors)?; (quote!{ - pub mod #name { + #vis mod #name { #body } }).to_tokens(tokens); diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 004dc31a..09ffafbc 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -41,17 +41,6 @@ pub(crate) struct InterfaceData<'src> { pub(crate) superclass: Option<&'src str>, } -impl<'src> Default for InterfaceData<'src> { - fn default() -> Self { - InterfaceData { - partial: true, - global: false, - operations: Default::default(), - superclass: Default::default(), - } - } -} - /// We need to collect namespace data during the first pass, to be used later. #[derive(Default)] pub(crate) struct NamespaceData<'src> { @@ -60,9 +49,10 @@ pub(crate) struct NamespaceData<'src> { pub(crate) operations: BTreeMap, OperationData<'src>>, } -impl<'src> Default for NamespaceData<'src> { - fn default() -> Self { - NamespaceData { +impl<'src> NamespaceData<'src> { + /// Same as `Default::default` but sets `partial` to true. + pub(crate) fn default_for_partial() -> Self { + Self { partial: true, operations: Default::default(), } @@ -178,7 +168,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { let interface = record .interfaces .entry(self.identifier.0) - .or_insert_with(Default::default); + .or_default(); interface.partial = false; interface.superclass = self.inheritance.map(|s| s.identifier.0); } @@ -348,6 +338,93 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { } } +impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + { + let namespace = record + .namespaces + .entry(self.identifier.0) + .or_default(); + namespace.partial = false; + } + + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + // We ignore all attributes. + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + + Ok(()) + } +} + +impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { + record + .namespaces + .entry(self.identifier.0) + .or_insert_with(NamespaceData::default_for_partial); + + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + for member in &self.members.body { + member.first_pass(record, self.identifier.0)?; + } + + Ok(()) + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { + fn first_pass(&'src self, + record: &mut FirstPassRecord<'src>, + namespace_name: &'src str) -> Result<()> + { + match self { + weedle::namespace::NamespaceMember::Operation(op) => { + op.first_pass(record, namespace_name) + } + _ => Ok(()), + } + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { + fn first_pass(&'src self, + record: &mut FirstPassRecord<'src>, + namespace_name: &'src str) -> Result<()> + { + let identifier = self.identifier.map(|s| s.0); + let arguments = &self.args.body.list; + 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 + .namespaces + .get_mut(namespace_name) + .unwrap() // call this after creating the namespace + .operations + .entry(identifier) + .and_modify(|operation_data| operation_data.overloaded = true) + .or_insert_with(Default::default) + .argument_names_same + .entry(names) + .and_modify(|same_argument_names| *same_argument_names = true) + .or_insert(false); + Ok(()) + } +} + impl<'a> FirstPassRecord<'a> { pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator + 'me diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 7fcf0460..20bc0d44 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -774,10 +774,10 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, + first_pass: &FirstPassRecord<'src>, self_name: &'src str, ) -> Result<()> { - let ty = match self.const_type.to_syn_type(record, TypePosition::Return) { + let ty = match self.const_type.to_syn_type(first_pass, TypePosition::Return) { Some(s) => s, None => return Ok(()), }; @@ -798,7 +798,7 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, + first_pass: &FirstPassRecord<'src>, (): (), ) -> Result<()> { if util::is_chrome_only(&self.attributes) { @@ -806,29 +806,59 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { } let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); - let js_name = &self.identifier.0; program.modules.entry(rust_name.clone()) .and_modify(|_| warn!("Namespace with rust name {:?} added more than once", rust_name)) - .or_default(); + .or_insert_with(|| backend::ast::Module { + vis: public(), + imports: Default::default() + }); - for member in self.members.body.iter() { - member.webidl_parse(program, record, js_name)? + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.webidl_parse(program, first_pass, self)?; + } } + + let namespace_names = NamespaceNames { + rust_name: &rust_name, + js_name: &self.identifier.0, + }; + for member in &self.members.body { + member.webidl_parse(program, first_pass, namespace_names)?; + } + Ok(()) } } -impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::NamespaceMember<'src> { +impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for ExtendedAttribute<'src> { + fn webidl_parse( + &'src self, + _program: &mut backend::ast::Program, + _first_pass: &FirstPassRecord<'src>, + _namespace: &'src weedle::NamespaceDefinition<'src>, + ) -> Result<()> { + warn!("Unsupported WebIDL extended attribute: {:?}", self); + Ok(()) + } +} +#[derive(Copy, Clone)] +struct NamespaceNames<'a> { + rust_name: &'a Ident, + js_name: &'a str, +} + +impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::NamespaceMember<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, - module_name: &'src str + first_pass: &FirstPassRecord<'src>, + ns_names: NamespaceNames<'src> ) -> Result<()> { match self { weedle::namespace::NamespaceMember::Operation(op) => { - op.webidl_parse(program, record, module_name)?; + op.webidl_parse(program, first_pass, ns_names)?; } weedle::namespace::NamespaceMember::Attribute(_) => { warn!("Attribute namespace members are not supported") @@ -838,117 +868,42 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::NamespaceMember<' } } -impl<'src> WebidlParse<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { +impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::OperationNamespaceMember<'src> { fn webidl_parse( &'src self, program: &mut backend::ast::Program, - record: &FirstPassRecord<'src>, - module_name: &'src str + first_pass: &FirstPassRecord<'src>, + ns_names: NamespaceNames<'src> ) -> Result<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } - let structural = util::is_structural(&self.attributes); + let imported_fn = match first_pass.create_namespace_operation( + &self.args.body.list, + self.identifier.as_ref().map(|id| id.0), + &self.return_type, + ns_names.js_name, + util::throws(&self.attributes) + ) { + Some(f) => f, + None => { return Ok(()) } + }; - let name = match &self.identifier { - Some(ident) => ident.0, - None => { - warn!("Operations without identifiers are not supported"); - return Ok(()); - } + let import = backend::ast::Import { + module: None, + js_namespace: Some(ns_names.js_name.clone()), + kind: backend::ast::ImportKind::Function(imported_fn), + }; + + program + .modules + .get_mut(ns_names.rust_name) + .unwrap() + .imports + .push(import); - let import_fn = first_pass - .create_function( - name, - false, - same_argument_names: bool, - arguments: &[Argument], - mut ret: Option, - kind: backend::ast::ImportFunctionKind, - structural: bool, - catch: bool, - doc_comment: Option, - ) -> Option - .create_basic_method( - args, - match identifier.map(|s| s.0) { - Some(ref name) if specials.is_empty() => - ::first_pass::OperationId::Operation(Some(name.clone())), - }, - &self.return_type, - self_name, - specials.len() == 1 || first_pass - .interfaces - .get(self_name) - .map(|interface_data| interface_data.global) - .unwrap_or(false), - util::throws(&self.attributes), - ) - .map(wrap_import_function) - .map(|import| program.imports.push(import)); Ok(()) } } -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::IndexingGetter, - weedle::interface::Special::Setter(weedle::term::Setter) => - ::first_pass::OperationId::IndexingSetter, - weedle::interface::Special::Deleter(weedle::term::Deleter) => - ::first_pass::OperationId::IndexingDeleter, - 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(()) -} diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 3320c38b..f654937b 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1029,33 +1029,25 @@ impl<'src> FirstPassRecord<'src> { pub fn create_namespace_operation( &self, arguments: &[weedle::argument::Argument], - operation_name: &str, + operation_name: Option<&str>, return_type: &weedle::types::ReturnType, - structural: bool, + namespace_name: &str, catch: bool, ) -> Option { let (overloaded, same_argument_names) = self.get_namespaced_operation_overloading( arguments, - &first_pass::OperationId::Operation(Some(operation_name)) - self_name, + operation_name, + namespace_name, ); - let name = match &operation_id { - first_pass::OperationId::Operation(name) => match name { - None => { - warn!("Operations without a name are unsupported"); - return None; - } - Some(name) => name.to_string(), - }, - _ => { - warn!("operations in namespaces must be normal functions"); + let name = match operation_name { + Some(name) => name.to_string(), + None => { + warn!("Operations without a name are unsupported"); return None; } }; - let kind = backend::ast::ImportFunctionKind::Normal; - let ret = match return_type { weedle::types::ReturnType::Void(_) => None, weedle::types::ReturnType::Type(ty) => { @@ -1069,8 +1061,9 @@ impl<'src> FirstPassRecord<'src> { } }; let doc_comment = format!("The `{}.{}()` function\n\n{}", + namespace_name, name, - mdn_doc(self_name, Some(&name))); + mdn_doc(namespace_name, Some(&name))); // TODO check link self.create_function( &name, @@ -1078,10 +1071,10 @@ impl<'src> FirstPassRecord<'src> { same_argument_names, arguments, ret, - kind, - structural, + backend::ast::ImportFunctionKind::Normal, + false, catch, - doc_comment, + Some(doc_comment), ) } @@ -1090,14 +1083,14 @@ impl<'src> FirstPassRecord<'src> { pub fn get_namespaced_operation_overloading( &self, arguments: &[weedle::argument::Argument], - id: &first_pass::OperationId, + operation_name: Option<&str>, namespace_name: &str, ) -> (bool, bool) { let data = match self.namespaces.get(namespace_name) { Some(data) => data, None => return (false, false), }; - let data = match data.operations.get(id) { + let data = match data.operations.get(&operation_name) { Some(data) => data, None => return (false, false), }; From f5203bba8a025342aa0a9d335e855989b40ffb38 Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Fri, 10 Aug 2018 14:10:51 -0400 Subject: [PATCH 19/41] Handle exceptions from JSON::stringify --- crates/js-sys/src/lib.rs | 4 ++-- crates/js-sys/tests/wasm/JSON.js | 4 ++++ crates/js-sys/tests/wasm/JSON.rs | 26 ++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 crates/js-sys/tests/wasm/JSON.js diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index caaf9cda..282c77d6 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2683,8 +2683,8 @@ extern "C" { /// specified. /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify - #[wasm_bindgen(static_method_of = JSON)] - pub fn stringify(obj: &JsValue) -> JsString; + #[wasm_bindgen(catch, static_method_of = JSON)] + pub fn stringify(obj: &JsValue) -> Result; } diff --git a/crates/js-sys/tests/wasm/JSON.js b/crates/js-sys/tests/wasm/JSON.js new file mode 100644 index 00000000..796bd200 --- /dev/null +++ b/crates/js-sys/tests/wasm/JSON.js @@ -0,0 +1,4 @@ +exports.set_in_object = function(obj, name, value) { + obj[name] = value; +}; + diff --git a/crates/js-sys/tests/wasm/JSON.rs b/crates/js-sys/tests/wasm/JSON.rs index 70ece6e8..c7f0f4bb 100644 --- a/crates/js-sys/tests/wasm/JSON.rs +++ b/crates/js-sys/tests/wasm/JSON.rs @@ -1,8 +1,15 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use js_sys::*; + +#[wasm_bindgen(module = "tests/wasm/JSON.js")] +extern { + fn set_in_object(obj: &Object, name: &str, value: &JsValue); +} + #[wasm_bindgen_test] fn parse_array() { @@ -63,7 +70,22 @@ fn stringify() { arr.push(&JsValue::from(true)); arr.push(&JsValue::from("hello")); - let str = JSON::stringify(&JsValue::from(arr)); + let str = JSON::stringify(&JsValue::from(arr)).unwrap(); let rust_str: String = From::from(str); assert_eq!(rust_str, "[1,true,\"hello\"]"); -} \ No newline at end of file +} + +#[wasm_bindgen_test] +fn stringify_error() { + let func = Function::new_no_args("throw new Error(\"rust really rocks\")"); + let obj = Object::new(); + set_in_object(&obj, "toJSON", &JsValue::from(func)); + + let result = JSON::stringify(&JsValue::from(obj)); + assert!(result.is_err()); + let err_obj = result.unwrap_err(); + assert!(err_obj.is_instance_of::()); + let err: &Error = err_obj.dyn_ref().unwrap(); + let err_msg: String = From::from(err.message()); + assert!(err_msg.contains("rust really rocks")); +} From f0444d1614082deed18c651b91378d5d695c9a4c Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 11:20:06 -0700 Subject: [PATCH 20/41] js-sys: Add bindings for `Intl.Collator` --- crates/js-sys/src/lib.rs | 44 ++++++++++++++++++++++++++++++++ crates/js-sys/tests/wasm/Intl.rs | 15 ++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 1c4639d8..6cb8c9d4 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3142,6 +3142,50 @@ pub mod Intl { #[wasm_bindgen(js_name = getCanonicalLocales, js_namespace = Intl)] pub fn get_canonical_locales(s: &JsValue) -> Array; } + + // Intl.Collator + #[wasm_bindgen] + extern "C" { + /// The Intl.Collator object is a constructor for collators, objects + /// that enable language sensitive string comparison. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator + #[wasm_bindgen(js_namespace = Intl)] + #[derive(Clone, Debug)] + pub type Collator; + + /// The Intl.Collator object is a constructor for collators, objects + /// that enable language sensitive string comparison. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator + #[wasm_bindgen(constructor, js_namespace = Intl)] + pub fn new(locales: &Array, options: &Object) -> Collator; + + /// The Intl.Collator.prototype.compare property returns a function that + /// compares two strings according to the sort order of this Collator + /// object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/compare + #[wasm_bindgen(method, getter, js_class = "Intl.Collator")] + pub fn compare(this: &Collator) -> Function; + + /// The Intl.Collator.prototype.resolvedOptions() method returns a new + /// object with properties reflecting the locale and collation options + /// computed during initialization of this Collator object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/resolvedOptions + #[wasm_bindgen(method, js_namespace = Intl, js_name = resolvedOptions)] + pub fn resolved_options(this: &Collator) -> Object; + + /// The Intl.Collator.supportedLocalesOf() method returns an array + /// containing those of the provided locales that are supported in + /// collation without having to fall back to the runtime's default + /// locale. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator/supportedLocalesOf + #[wasm_bindgen(static_method_of = Collator, js_namespace = Intl, js_name = supportedLocalesOf)] + pub fn supported_locales_of(locales: &Array, options: &Object) -> Array; + } } // Promise diff --git a/crates/js-sys/tests/wasm/Intl.rs b/crates/js-sys/tests/wasm/Intl.rs index a739656b..e037fdfd 100644 --- a/crates/js-sys/tests/wasm/Intl.rs +++ b/crates/js-sys/tests/wasm/Intl.rs @@ -1,4 +1,4 @@ -use wasm_bindgen::JsValue; +use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen_test::*; use js_sys::*; @@ -23,3 +23,16 @@ fn get_canonical_locales() { assert_eq!(l, "en-US"); }); } + +#[wasm_bindgen_test] +fn collator() { + let locales = Array::of1(&JsValue::from("en-US")); + let opts = Object::new(); + + let c = Intl::Collator::new(&locales, &opts); + assert!(c.compare().is_instance_of::()); + assert!(c.resolved_options().is_instance_of::()); + + let a = Intl::Collator::supported_locales_of(&locales, &opts); + assert!(a.is_instance_of::()); +} From 1092816652d6da212825cc11cb9a339b9c84b97f Mon Sep 17 00:00:00 2001 From: Andrew Chin Date: Fri, 10 Aug 2018 14:33:21 -0400 Subject: [PATCH 21/41] Use Reflect::set instead of javascript helper --- crates/js-sys/tests/wasm/JSON.js | 4 ---- crates/js-sys/tests/wasm/JSON.rs | 9 +-------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 crates/js-sys/tests/wasm/JSON.js diff --git a/crates/js-sys/tests/wasm/JSON.js b/crates/js-sys/tests/wasm/JSON.js deleted file mode 100644 index 796bd200..00000000 --- a/crates/js-sys/tests/wasm/JSON.js +++ /dev/null @@ -1,4 +0,0 @@ -exports.set_in_object = function(obj, name, value) { - obj[name] = value; -}; - diff --git a/crates/js-sys/tests/wasm/JSON.rs b/crates/js-sys/tests/wasm/JSON.rs index c7f0f4bb..b1d9fd18 100644 --- a/crates/js-sys/tests/wasm/JSON.rs +++ b/crates/js-sys/tests/wasm/JSON.rs @@ -1,15 +1,8 @@ use wasm_bindgen::JsValue; use wasm_bindgen_test::*; -use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use js_sys::*; - -#[wasm_bindgen(module = "tests/wasm/JSON.js")] -extern { - fn set_in_object(obj: &Object, name: &str, value: &JsValue); -} - #[wasm_bindgen_test] fn parse_array() { @@ -79,7 +72,7 @@ fn stringify() { fn stringify_error() { let func = Function::new_no_args("throw new Error(\"rust really rocks\")"); let obj = Object::new(); - set_in_object(&obj, "toJSON", &JsValue::from(func)); + Reflect::set(obj.as_ref(), &JsValue::from("toJSON"), func.as_ref()); let result = JSON::stringify(&JsValue::from(obj)); assert!(result.is_err()); From 4ea1603ddbd8e9e29573c34e9a4b89e31927ae61 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 11:41:58 -0700 Subject: [PATCH 22/41] js-sys: Add bindings to `Object.assign` --- crates/js-sys/src/lib.rs | 24 ++++++++++++++++++++++++ crates/js-sys/tests/wasm/Object.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 6cb8c9d4..dfb45d66 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -1890,6 +1890,30 @@ extern "C" { #[derive(Clone, Debug)] pub type Object; + /// The Object.assign() method is used to copy the values of all enumerable + /// own properties from one or more source objects to a target object. It + /// will return the target object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + #[wasm_bindgen(static_method_of = Object)] + pub fn assign(target: &Object, source: &Object) -> Object; + + /// The Object.assign() method is used to copy the values of all enumerable + /// own properties from one or more source objects to a target object. It + /// will return the target object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + #[wasm_bindgen(static_method_of = Object, js_name = assign)] + pub fn assign2(target: &Object, source1: &Object, source2: &Object) -> Object; + + /// The Object.assign() method is used to copy the values of all enumerable + /// own properties from one or more source objects to a target object. It + /// will return the target object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + #[wasm_bindgen(static_method_of = Object, js_name = assign)] + pub fn assign3(target: &Object, source1: &Object, source2: &Object, source3: &Object) -> Object; + /// The `Object.freeze()` method freezes an object: that is, prevents new /// properties from being added to it; prevents existing properties from /// being removed; and prevents existing properties, or their enumerability, diff --git a/crates/js-sys/tests/wasm/Object.rs b/crates/js-sys/tests/wasm/Object.rs index da245a97..ee448ea8 100644 --- a/crates/js-sys/tests/wasm/Object.rs +++ b/crates/js-sys/tests/wasm/Object.rs @@ -36,6 +36,33 @@ fn new() { assert!(JsValue::from(Object::new()).is_object()); } + +#[wasm_bindgen_test] +fn assign() { + let a = JsValue::from("a"); + let b = JsValue::from("b"); + let c = JsValue::from("c"); + + let target = Object::new(); + Reflect::set(target.as_ref(), a.as_ref(), a.as_ref()); + + let src1 = Object::new(); + Reflect::set(src1.as_ref(), &a, &c); + + let src2 = Object::new(); + Reflect::set(src2.as_ref(), &b, &b); + + let src3 = Object::new(); + Reflect::set(src3.as_ref(), &c, &c); + + let res = Object::assign3(&target, &src1, &src2, &src3); + + assert!(Object::is(target.as_ref(), res.as_ref())); + assert_eq!(Reflect::get(target.as_ref(), &a), c); + assert_eq!(Reflect::get(target.as_ref(), &b), b); + assert_eq!(Reflect::get(target.as_ref(), &c), c); +} + #[wasm_bindgen_test] fn has_own_property() { assert!(foo_42().has_own_property(&"foo".into())); From adad606ee3bba6a448361f6ae1196ffea623a13c Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 11:49:22 -0700 Subject: [PATCH 23/41] js-sys: Add bindings for `Object.create` --- crates/js-sys/src/lib.rs | 7 +++++++ crates/js-sys/tests/wasm/Object.rs | 22 ++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index dfb45d66..c3091332 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -1914,6 +1914,13 @@ extern "C" { #[wasm_bindgen(static_method_of = Object, js_name = assign)] pub fn assign3(target: &Object, source1: &Object, source2: &Object, source3: &Object) -> Object; + /// The Object.create() method creates a new object, using an existing + /// object to provide the newly created object's prototype. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create + #[wasm_bindgen(static_method_of = Object)] + pub fn create(prototype: &Object) -> Object; + /// The `Object.freeze()` method freezes an object: that is, prevents new /// properties from being added to it; prevents existing properties from /// being removed; and prevents existing properties, or their enumerability, diff --git a/crates/js-sys/tests/wasm/Object.rs b/crates/js-sys/tests/wasm/Object.rs index ee448ea8..97cc7b6a 100644 --- a/crates/js-sys/tests/wasm/Object.rs +++ b/crates/js-sys/tests/wasm/Object.rs @@ -1,17 +1,18 @@ -use wasm_bindgen::prelude::*; -use wasm_bindgen_test::*; -use std::f64::NAN; use js_sys::*; +use std::f64::NAN; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_test::*; #[wasm_bindgen] -extern { +extern "C" { type Foo42; #[wasm_bindgen(method, setter, structural)] fn set_foo(this: &Foo42, val: JsValue); } #[wasm_bindgen(module = "tests/wasm/Object.js")] -extern { +extern "C" { fn map_with_symbol_key() -> Object; fn symbol_key() -> JsValue; @@ -36,7 +37,6 @@ fn new() { assert!(JsValue::from(Object::new()).is_object()); } - #[wasm_bindgen_test] fn assign() { let a = JsValue::from("a"); @@ -63,6 +63,16 @@ fn assign() { assert_eq!(Reflect::get(target.as_ref(), &c), c); } +#[wasm_bindgen_test] +fn create() { + let array_proto = eval("Array.prototype") + .unwrap() + .dyn_into::() + .unwrap(); + let my_array = Object::create(&array_proto); + assert!(my_array.is_instance_of::()); +} + #[wasm_bindgen_test] fn has_own_property() { assert!(foo_42().has_own_property(&"foo".into())); From 879fd43edbda008d8f8fe5d175e1d24ab309e76e Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:03:44 -0700 Subject: [PATCH 24/41] js-sys: Add bindings to `RangeError` --- crates/js-sys/src/lib.rs | 19 +++++++++++++++++++ crates/js-sys/tests/wasm/RangeError.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 34 insertions(+) create mode 100644 crates/js-sys/tests/wasm/RangeError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index c3091332..ed212618 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2084,6 +2084,25 @@ extern { pub fn revocable(target: &JsValue, handler: &Object) -> Object; } +// RangeError +#[wasm_bindgen] +extern { + /// The RangeError object indicates an error when a value is not in the set + /// or range of allowed values. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError + #[wasm_bindgen(extends = Error)] + #[derive(Clone, Debug)] + pub type RangeError; + + /// The RangeError object indicates an error when a value is not in the set + /// or range of allowed values. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> RangeError; +} + // Reflect #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/RangeError.rs b/crates/js-sys/tests/wasm/RangeError.rs new file mode 100644 index 00000000..c3470d26 --- /dev/null +++ b/crates/js-sys/tests/wasm/RangeError.rs @@ -0,0 +1,14 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn range_error() { + let error = RangeError::new("out of range yo"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + + let base: &Error = error.as_ref(); + assert_eq!(JsValue::from(base.message()), "out of range yo"); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 9b8b2f53..49319b4d 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -25,6 +25,7 @@ pub mod Math; pub mod Number; pub mod Object; pub mod Proxy; +pub mod RangeError; pub mod Reflect; pub mod RegExp; pub mod Set; From dc028d38c828e1c3ad0679d35a5f46f2ace9712e Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:03:56 -0700 Subject: [PATCH 25/41] js-sys: Add bindings to `ReferenceError` --- crates/js-sys/src/lib.rs | 19 +++++++++++++++++++ crates/js-sys/tests/wasm/ReferenceError.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 34 insertions(+) create mode 100644 crates/js-sys/tests/wasm/ReferenceError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index ed212618..01a75c1a 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2103,6 +2103,25 @@ extern { pub fn new(message: &str) -> RangeError; } +// ReferenceError +#[wasm_bindgen] +extern { + /// The ReferenceError object represents an error when a non-existent + /// variable is referenced. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError + #[wasm_bindgen(extends = Error)] + #[derive(Clone, Debug)] + pub type ReferenceError; + + /// The ReferenceError object represents an error when a non-existent + /// variable is referenced. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> ReferenceError; +} + // Reflect #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/ReferenceError.rs b/crates/js-sys/tests/wasm/ReferenceError.rs new file mode 100644 index 00000000..22e2518c --- /dev/null +++ b/crates/js-sys/tests/wasm/ReferenceError.rs @@ -0,0 +1,14 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn reference_error() { + let error = ReferenceError::new("bad reference, fool"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + + let base: &Error = error.as_ref(); + assert_eq!(JsValue::from(base.message()), "bad reference, fool"); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 49319b4d..628eb59b 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -26,6 +26,7 @@ pub mod Number; pub mod Object; pub mod Proxy; pub mod RangeError; +pub mod ReferenceError; pub mod Reflect; pub mod RegExp; pub mod Set; From 178a5e89df2529cd6907b69bc599ef74f1f92eb5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:14:54 -0700 Subject: [PATCH 26/41] js-sys: add bindings for `regexp.lastIndex` --- crates/js-sys/src/lib.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/RegExp.rs | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 01a75c1a..c21f7fdc 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2280,6 +2280,20 @@ extern { #[wasm_bindgen(static_method_of = RegExp, getter)] pub fn input() -> JsString; + /// The lastIndex is a read/write integer property of regular expression + /// instances that specifies the index at which to start the next match. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex + #[wasm_bindgen(structural, getter = lastIndex, method)] + pub fn last_index(this: &RegExp) -> u32; + + /// The lastIndex is a read/write integer property of regular expression + /// instances that specifies the index at which to start the next match. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex + #[wasm_bindgen(structural, setter = lastIndex, method)] + pub fn set_last_index(this: &RegExp, index: u32); + /// The non-standard lastMatch property is a static and read-only /// property of regular expressions that contains the last matched /// characters. RegExp.$& is an alias for this property. diff --git a/crates/js-sys/tests/wasm/RegExp.rs b/crates/js-sys/tests/wasm/RegExp.rs index edaf5c92..a77d4892 100644 --- a/crates/js-sys/tests/wasm/RegExp.rs +++ b/crates/js-sys/tests/wasm/RegExp.rs @@ -56,6 +56,15 @@ fn input() { assert_eq!(RegExp::input(), "hi there!"); } +#[wasm_bindgen_test] +fn last_index() { + let re = RegExp::new("hi", "g"); + assert_eq!(re.last_index(), 0); + + re.set_last_index(42); + assert_eq!(re.last_index(), 42); +} + #[wasm_bindgen_test] fn last_match() { let re = RegExp::new("hi", "g"); From 016449ab3c7629cd145259e35615efdeeea41aaf Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:15:12 -0700 Subject: [PATCH 27/41] backend: when complaining about setter names, show the name we are complaining about --- crates/backend/src/ast.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 57869a89..634e6e4e 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -346,7 +346,9 @@ impl ImportFunction { /// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`) fn infer_setter_property(&self) -> String { let name = self.function.name.to_string(); - assert!(name.starts_with("set_"), "setters must start with `set_`"); + if !name.starts_with("set_") { + panic!("error: setters must start with `set_`, found: {}", name); + } name[4..].to_string() } From 7af4e62d9943b57016d3f1a20eb4fe9fd952d970 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:23:17 -0700 Subject: [PATCH 28/41] js-sys: Add bindings for `Symbol.unscopables` --- crates/js-sys/src/lib.rs | 8 ++++++++ crates/js-sys/tests/wasm/Symbol.rs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index c21f7fdc..5d7a0d38 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3203,6 +3203,14 @@ extern "C" { #[wasm_bindgen(method, js_name = toString)] pub fn to_string(this: &Symbol) -> JsString; + /// The Symbol.unscopables well-known symbol is used to specify an object + /// value of whose own and inherited property names are excluded from the + /// with environment bindings of the associated object. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/unscopables + #[wasm_bindgen(static_method_of = Symbol, getter, structural)] + pub fn unscopables() -> Symbol; + /// The valueOf() method returns the primitive value of a Symbol object. /// /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/valueOf diff --git a/crates/js-sys/tests/wasm/Symbol.rs b/crates/js-sys/tests/wasm/Symbol.rs index a34a1ca2..876e1d88 100644 --- a/crates/js-sys/tests/wasm/Symbol.rs +++ b/crates/js-sys/tests/wasm/Symbol.rs @@ -99,6 +99,11 @@ fn to_string() { assert_eq!(gensym("desc".into()).to_string(), "Symbol(desc)"); } +#[wasm_bindgen_test] +fn unscopables() { + assert_eq!(Symbol::unscopables().to_string(), "Symbol(Symbol.unscopables)"); +} + #[wasm_bindgen_test] fn value_of() { let a = Symbol::for_("foo"); From 4148d3b4acaa12fc4b042823e18127a064d2852f Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:36:47 -0700 Subject: [PATCH 29/41] macro-support: obey `js_class = ...` for constructor methods Fixes #668 --- crates/macro-support/src/parser.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 5cc1f0c6..ae7815d5 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -166,7 +166,7 @@ impl BindgenAttrs { }).next() } - /// Get the first js_name attribute + /// Get the first js_class attribute fn js_class(&self) -> Option<&str> { self.attrs .iter() @@ -495,6 +495,10 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option)> for syn::ForeignItemFn _ => bail_span!(self, "return value of constructor must be a bare path"), }; let class_name = extract_path_ident(class_name)?; + let class_name = opts + .js_class() + .map(Into::into) + .unwrap_or_else(|| class_name.to_string()); ast::ImportFunctionKind::Method { class: class_name.to_string(), From a58c2584b3a9a5d7ba920e1076a54ad17d41e00a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:37:34 -0700 Subject: [PATCH 30/41] js-sys: Add bindings to `URIError` --- crates/js-sys/src/lib.rs | 19 +++++++++++++++++++ crates/js-sys/tests/wasm/UriError.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 34 insertions(+) create mode 100644 crates/js-sys/tests/wasm/UriError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 5d7a0d38..2745f7ef 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2706,6 +2706,25 @@ extern "C" { pub fn byte_offset(this: &Uint32Array) -> u32; } +// URIError +#[wasm_bindgen] +extern { + /// The URIError object represents an error when a global URI handling + /// function was used in a wrong way. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError + #[wasm_bindgen(extends = Error, js_name = URIError)] + #[derive(Clone, Debug)] + pub type UriError; + + /// The URIError object represents an error when a global URI handling + /// function was used in a wrong way. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError + #[wasm_bindgen(constructor, js_class = "URIError")] + pub fn new(message: &str) -> UriError; +} + // WeakMap #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/UriError.rs b/crates/js-sys/tests/wasm/UriError.rs new file mode 100644 index 00000000..86ff417e --- /dev/null +++ b/crates/js-sys/tests/wasm/UriError.rs @@ -0,0 +1,14 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn uri_error() { + let error = UriError::new("msg"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + + let base: &Error = error.as_ref(); + assert_eq!(JsValue::from(base.message()), "msg"); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 628eb59b..f1e1138b 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -33,6 +33,7 @@ pub mod Set; pub mod SetIterator; pub mod Symbol; pub mod TypedArray; +pub mod UriError; pub mod WeakMap; pub mod WeakSet; pub mod WebAssembly; From 38ef5f9ffe8221a323c007161e324f1d8d377f9e Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:42:13 -0700 Subject: [PATCH 31/41] js-sys: Add bindings for `SyntaxError` --- crates/js-sys/src/lib.rs | 21 +++++++++++++++++++++ crates/js-sys/tests/wasm/SyntaxError.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 36 insertions(+) create mode 100644 crates/js-sys/tests/wasm/SyntaxError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 2745f7ef..3bdb3a0f 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2496,6 +2496,27 @@ extern { pub fn values(set: &Set) -> Iterator; } +// SyntaxError +#[wasm_bindgen] +extern { + /// A SyntaxError is thrown when the JavaScript engine encounters tokens or + /// token order that does not conform to the syntax of the language when + /// parsing code. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError + #[wasm_bindgen(extends = Error)] + #[derive(Clone, Debug)] + pub type SyntaxError; + + /// A SyntaxError is thrown when the JavaScript engine encounters tokens or + /// token order that does not conform to the syntax of the language when + /// parsing code. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> SyntaxError; +} + // Uint8Array #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/SyntaxError.rs b/crates/js-sys/tests/wasm/SyntaxError.rs new file mode 100644 index 00000000..57d86371 --- /dev/null +++ b/crates/js-sys/tests/wasm/SyntaxError.rs @@ -0,0 +1,14 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn syntax_error() { + let error = SyntaxError::new("msg"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + + let base: &Error = error.as_ref(); + assert_eq!(JsValue::from(base.message()), "msg"); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index f1e1138b..e3b6ec90 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -32,6 +32,7 @@ pub mod RegExp; pub mod Set; pub mod SetIterator; pub mod Symbol; +pub mod SyntaxError; pub mod TypedArray; pub mod UriError; pub mod WeakMap; From 36e15149c7877839b7dbd0afd8877e7213b2eb9b Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 13:45:39 -0700 Subject: [PATCH 32/41] js-sys: Add bindings for `TypeError` --- crates/js-sys/src/lib.rs | 19 +++++++++++++++++++ crates/js-sys/tests/wasm/TypeError.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/main.rs | 1 + 3 files changed, 34 insertions(+) create mode 100644 crates/js-sys/tests/wasm/TypeError.rs diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 3bdb3a0f..aaa3080b 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2517,6 +2517,25 @@ extern { pub fn new(message: &str) -> SyntaxError; } +// TypeError +#[wasm_bindgen] +extern { + /// The TypeError object represents an error when a value is not of the + /// expected type. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError + #[wasm_bindgen(extends = Error)] + #[derive(Clone, Debug)] + pub type TypeError; + + /// The TypeError object represents an error when a value is not of the + /// expected type. + /// + /// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError + #[wasm_bindgen(constructor)] + pub fn new(message: &str) -> TypeError; +} + // Uint8Array #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/TypeError.rs b/crates/js-sys/tests/wasm/TypeError.rs new file mode 100644 index 00000000..e82cf091 --- /dev/null +++ b/crates/js-sys/tests/wasm/TypeError.rs @@ -0,0 +1,14 @@ +use wasm_bindgen::JsValue; +use wasm_bindgen_test::*; +use wasm_bindgen::JsCast; +use js_sys::*; + +#[wasm_bindgen_test] +fn type_error() { + let error = TypeError::new("msg"); + assert!(error.is_instance_of::()); + assert!(error.is_instance_of::()); + + let base: &Error = error.as_ref(); + assert_eq!(JsValue::from(base.message()), "msg"); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index e3b6ec90..faef8e52 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -33,6 +33,7 @@ pub mod Set; pub mod SetIterator; pub mod Symbol; pub mod SyntaxError; +pub mod TypeError; pub mod TypedArray; pub mod UriError; pub mod WeakMap; From e87498e9391198a5c6eadd89ba10414b9c1c76a9 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Fri, 10 Aug 2018 16:56:40 -0700 Subject: [PATCH 33/41] guide: Start adding example usage to "supported types" section --- Cargo.toml | 1 + .../guide-supported-types-examples/.gitignore | 3 + .../guide-supported-types-examples/Cargo.toml | 10 ++ .../guide-supported-types-examples/README.md | 61 ++++++++ .../bootstrap.js | 2 + .../guide-supported-types-examples/build.sh | 12 ++ .../exported_types.js | 1 + .../imported_types.js | 26 ++++ .../guide-supported-types-examples/index.html | 8 + .../guide-supported-types-examples/index.js | 5 + .../package.json | 10 ++ .../src/exported_types.rs | 6 + .../src/imported_types.rs | 25 ++++ .../guide-supported-types-examples/src/lib.rs | 7 + .../webpack.config.js | 10 ++ guide/src/reference/types.md | 139 +++++++++++++++--- 16 files changed, 307 insertions(+), 19 deletions(-) create mode 100644 examples/guide-supported-types-examples/.gitignore create mode 100644 examples/guide-supported-types-examples/Cargo.toml create mode 100644 examples/guide-supported-types-examples/README.md create mode 100644 examples/guide-supported-types-examples/bootstrap.js create mode 100755 examples/guide-supported-types-examples/build.sh create mode 100644 examples/guide-supported-types-examples/exported_types.js create mode 100644 examples/guide-supported-types-examples/imported_types.js create mode 100644 examples/guide-supported-types-examples/index.html create mode 100644 examples/guide-supported-types-examples/index.js create mode 100644 examples/guide-supported-types-examples/package.json create mode 100644 examples/guide-supported-types-examples/src/exported_types.rs create mode 100644 examples/guide-supported-types-examples/src/imported_types.rs create mode 100755 examples/guide-supported-types-examples/src/lib.rs create mode 100644 examples/guide-supported-types-examples/webpack.config.js diff --git a/Cargo.toml b/Cargo.toml index 66579efa..af895770 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ members = [ "examples/comments", "examples/console_log", "examples/dom", + "examples/guide-supported-types-examples", "examples/hello_world", "examples/import_js", "examples/julia_set", diff --git a/examples/guide-supported-types-examples/.gitignore b/examples/guide-supported-types-examples/.gitignore new file mode 100644 index 00000000..d0b14fad --- /dev/null +++ b/examples/guide-supported-types-examples/.gitignore @@ -0,0 +1,3 @@ +package-lock.json +guide_supported_types_examples.js +guide_supported_types_examples_bg.wasm diff --git a/examples/guide-supported-types-examples/Cargo.toml b/examples/guide-supported-types-examples/Cargo.toml new file mode 100644 index 00000000..9c73077a --- /dev/null +++ b/examples/guide-supported-types-examples/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "guide-supported-types-examples" +version = "0.1.0" +authors = ["Nick Fitzgerald "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = { path = "../.." } diff --git a/examples/guide-supported-types-examples/README.md b/examples/guide-supported-types-examples/README.md new file mode 100644 index 00000000..a1f75d4e --- /dev/null +++ b/examples/guide-supported-types-examples/README.md @@ -0,0 +1,61 @@ +# Adding Numbers + +[View this example online](https://webassembly.studio/?f=612vwsrmwft) + +This directory is an example of using the `#[wasm_bindgen]` macro to simply add +two numbers. The neat part about this is that it's an example of how to generate +the smallest wasm-bindgen binary. + +You can build the example with: + +``` +$ ./build.sh +``` + +(or running the commands on Windows manually) + +Currently this generates a 651 byte wasm binary: + +``` +$ ls -alh add_bg.wasm +-rw-rw-r-- 1 alex alex 651 Apr 20 22:16 add_bg.wasm +``` + +If you run [wasm-opt], a C++ tool for optimize WebAssembly, you can make it even +smaller too! + +``` +$ wasm-opt -Os add_bg.wasm -o add.wasm +$ ls -alh add.wasm +-rw-rw-r-- 1 alex alex 100 Apr 20 22:19 add.wasm +``` + +And sure enough, using the [wasm2wat] tool it's quite small! + +``` +$ wasm2wat add.wasm +(module + (type (;0;) (func (param i32 i32) (result i32))) + (func (;0;) (type 0) (param i32 i32) (result i32) + get_local 1 + get_local 0 + i32.add) + (memory (;0;) 2) + (export "memory" (memory 0)) + (export "add" (func 0)) +(data (i32.const 1545) "invalid malloc request")) +``` + +Note that it's important to point out that the size reductions here are because +the wasm is compiled in release mode by the build script and this crate's +workspace has the following configuration + +```toml +[profile.release] +lto = true +opt-level = 's' +panic = 'abort' +``` + +[wasm2wat]: https://github.com/webassembly/wabt +[wasm-opt]: https://github.com/webassembly/binaryen diff --git a/examples/guide-supported-types-examples/bootstrap.js b/examples/guide-supported-types-examples/bootstrap.js new file mode 100644 index 00000000..ddd62bb5 --- /dev/null +++ b/examples/guide-supported-types-examples/bootstrap.js @@ -0,0 +1,2 @@ +import * as imported_types from './imported_types.js'; +import * as exported_types from './exported_types.js'; diff --git a/examples/guide-supported-types-examples/build.sh b/examples/guide-supported-types-examples/build.sh new file mode 100755 index 00000000..d99253c5 --- /dev/null +++ b/examples/guide-supported-types-examples/build.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# For more coments about what's going on here, see the `hello_world` example + +set -ex + +cargo +nightly build --target wasm32-unknown-unknown --release +cargo +nightly run --manifest-path ../../crates/cli/Cargo.toml \ + --bin wasm-bindgen -- \ + ../../target/wasm32-unknown-unknown/release/guide_supported_types_examples.wasm --out-dir . +npm install +npm run serve diff --git a/examples/guide-supported-types-examples/exported_types.js b/examples/guide-supported-types-examples/exported_types.js new file mode 100644 index 00000000..97dd4313 --- /dev/null +++ b/examples/guide-supported-types-examples/exported_types.js @@ -0,0 +1 @@ +console.log("todo") diff --git a/examples/guide-supported-types-examples/imported_types.js b/examples/guide-supported-types-examples/imported_types.js new file mode 100644 index 00000000..41d1c682 --- /dev/null +++ b/examples/guide-supported-types-examples/imported_types.js @@ -0,0 +1,26 @@ +import { + imported_type_by_value, + imported_type_by_shared_ref, + return_imported_type, + take_option_imported_type, + return_option_imported_type, +} from './guide_supported_types_examples'; + +imported_type_by_value(new SomeJsType()); +imported_type_by_shared_ref(new SomeJsType()); + +let x = return_imported_type(); +console.log(x instanceof SomeJsType); +// true + +take_option_imported_type(null); +take_option_imported_type(undefined); +take_option_imported_type(new SomeJsType()); + +let y = return_option_imported_type(); +if (y == null) { + // ... +} else { + console.log(y instanceof SomeJsType); + // true +} diff --git a/examples/guide-supported-types-examples/index.html b/examples/guide-supported-types-examples/index.html new file mode 100644 index 00000000..bad47a7c --- /dev/null +++ b/examples/guide-supported-types-examples/index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/examples/guide-supported-types-examples/index.js b/examples/guide-supported-types-examples/index.js new file mode 100644 index 00000000..a1819bb5 --- /dev/null +++ b/examples/guide-supported-types-examples/index.js @@ -0,0 +1,5 @@ +// For more comments about what's going on here, check out the `hello_world` +// example +import('./bootstrap').then(() => { + console.log("done"); +}); diff --git a/examples/guide-supported-types-examples/package.json b/examples/guide-supported-types-examples/package.json new file mode 100644 index 00000000..41eba8c8 --- /dev/null +++ b/examples/guide-supported-types-examples/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "serve": "webpack-dev-server" + }, + "devDependencies": { + "webpack": "^4.11.1", + "webpack-cli": "^2.0.10", + "webpack-dev-server": "^3.1.0" + } +} diff --git a/examples/guide-supported-types-examples/src/exported_types.rs b/examples/guide-supported-types-examples/src/exported_types.rs new file mode 100644 index 00000000..018374cb --- /dev/null +++ b/examples/guide-supported-types-examples/src/exported_types.rs @@ -0,0 +1,6 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct RustType { + inner: u32, +} diff --git a/examples/guide-supported-types-examples/src/imported_types.rs b/examples/guide-supported-types-examples/src/imported_types.rs new file mode 100644 index 00000000..22578ca5 --- /dev/null +++ b/examples/guide-supported-types-examples/src/imported_types.rs @@ -0,0 +1,25 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern { + pub type SomeJsType; +} + +#[wasm_bindgen] +pub fn imported_type_by_value(x: SomeJsType) { /* ... */ } + +#[wasm_bindgen] +pub fn imported_type_by_shared_ref(x: &SomeJsType) { /* ... */ } + +#[wasm_bindgen] +pub fn return_imported_type() -> SomeJsType { + unimplemented!() +} + +#[wasm_bindgen] +pub fn take_option_imported_type(x: Option) { /* ... */ } + +#[wasm_bindgen] +pub fn return_option_imported_type() -> Option { + unimplemented!() +} diff --git a/examples/guide-supported-types-examples/src/lib.rs b/examples/guide-supported-types-examples/src/lib.rs new file mode 100755 index 00000000..afe92e4b --- /dev/null +++ b/examples/guide-supported-types-examples/src/lib.rs @@ -0,0 +1,7 @@ +#![feature(use_extern_macros)] +#![allow(unused_variables, dead_code)] + +extern crate wasm_bindgen; + +pub mod imported_types; +pub mod exported_types; diff --git a/examples/guide-supported-types-examples/webpack.config.js b/examples/guide-supported-types-examples/webpack.config.js new file mode 100644 index 00000000..dce27149 --- /dev/null +++ b/examples/guide-supported-types-examples/webpack.config.js @@ -0,0 +1,10 @@ +const path = require('path'); + +module.exports = { + entry: './index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + }, + mode: 'development' +}; diff --git a/guide/src/reference/types.md b/guide/src/reference/types.md index c3b0a7dc..a29d61fd 100644 --- a/guide/src/reference/types.md +++ b/guide/src/reference/types.md @@ -1,21 +1,122 @@ -# Supported Types +# Supported Rust Types and their JavaScript Representations -The table below provides an overview of all the types that `wasm-bindgen` can -send and receive across the WebAssembly ABI boundary. +This section provides an overview of all the types that `wasm-bindgen` can send +and receive across the WebAssembly ABI boundary, and how they translate into +JavaScript. -| Type | `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | -|:---:|:---:|:---:|:---:|:---:|:---:| -| Imported `extern Whatever;` JavaScript Types | Yes | Yes | Yes | Yes | Yes | Yes | -| Exported `struct Whatever` Rust Types | Yes | Yes | Yes | Yes | Yes | Yes | -| `str` | No | Yes | No | Yes | Yes | No | -| `String` | Yes | No | No | Yes | Yes | Yes | -| `char` | Yes | No | No | Yes | No | No | -| `bool` | Yes | No | No | Yes | No | No | -| `JsValue` | Yes | Yes | Yes | Yes | No | No | -| `Box<[JsValue]>` | Yes | No | No | Yes | Yes | yes | -| `*const T` | Yes | No | No | Yes | No | No | -| `*mut T` | Yes | No | No | Yes | No | No | -| `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` | Yes | No | No | Yes | No | No | -| `u32` `i32` `f32` `f64` | Yes | Yes | Yes | Yes | No | No | -| `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]`> | Yes | No | No | Yes | Yes | Yes | -| `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]` | No | Yes | Yes | No | Yes | No | +## Imported `extern Whatever;` JavaScript Types + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | Yes | No | Yes | Yes | Yes | Instances of the extant `Whatever` JavaScript class / prototype constructor | + +### Example Rust Usage + +```rust +{{#include ../../../examples/guide-supported-types-examples/src/imported_types.rs}} +``` + +### Example JavaScript Usage + +```js +{{#include ../../../examples/guide-supported-types-examples/imported_types.js}} +``` + +## Exported `struct Whatever` Rust Types + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | Yes | Yes | Yes | Yes | Yes | Instances of a `wasm-bindgen`-generated JavaScript `class Whatever { ... }` | + +### Example Rust Usage + +```rust +{{#include ../../../examples/guide-supported-types-examples/src/exported_types.rs}} +``` + +### Example JavaScript Usage + +```js +{{#include ../../../examples/guide-supported-types-examples/exported_types.js}} +``` + +## `str` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| No | Yes | No | Yes | Yes | No | JavaScript string value | + +Copies the string's contents back and forth between the JavaScript +garbage-collected heap and the Wasm linear memory with `TextDecoder` and +`TextEncoder`. If you don't want to perform this copy, and would rather work +with handles to JavaScript string values, use the `js_sys::JsString` type. + +## `String` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | Yes | Yes | JavaScript string value | + +Copies the string's contents back and forth between the JavaScript +garbage-collected heap and the Wasm linear memory with `TextDecoder` and +`TextEncoder` + +## `char` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | No | No | A JavaScript string value | + +## `bool` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | No | No | A JavaScript boolean value | + +## `wasm_bindgen::JsValue` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | Yes | Yes | Yes | No | No | Any JavaScript value | + +## `Box<[JsValue]>` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | Yes | Yes | A JavaScript `Array` object | + +## `*const T` `*mut T` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | No | No | A JavaScript number value | + +## `u8` `i8` `u16` `i16` `u64` `i64` `isize` `size` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | No | No | A JavaScript number value | + +## `u32` `i32` `f32` `f64` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | Yes | Yes | Yes | No | No | A JavaScript number value | + +## `Box<[u8]>` `Box<[i8]>` `Box<[u16]>` `Box<[i16]>` `Box<[u32]>` `Box<[i32]>` `Box<[u64]>` `Box<[i64]>` `Box<[f32]>` `Box<[f64]>` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Yes | No | No | Yes | Yes | Yes | A JavaScript `TypedArray` view of the Wasm memory for the boxed slice of the appropriate type (`Int32Array`, `Uint8Array`, etc) | + +Note that this does ***not*** copy the whole slice of memory back and forth into +the JavaScript heap from the Wasm linear memory. + +## `[u8]` `[i8]` `[u16]` `[i16]` `[u32]` `[i32]` `[u64]` `[i64]` `[f32]` `[f64]` + +| `T` parameter | `&T` parameter | `&mut T` parameter | `T` return value | `Option` parameter | `Option` return value | JavaScript representation | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| No | Yes | Yes | No | Yes | No | A JavaScript `TypedArray` view of the Wasm memory for the boxed slice of the appropriate type (`Int32Array`, `Uint8Array`, etc) | + +Note that this does ***not*** copy the whole slice of memory back and forth into +the JavaScript heap from the Wasm linear memory. From a23fa03ad06a06e9ab00627a581dd19fdb03a327 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sat, 11 Aug 2018 12:38:58 +0100 Subject: [PATCH 34/41] Closer to finished - Tried `cargo doc` and seen methods generated. - Added test with a few method calls to the console operations. --- crates/web-sys/tests/wasm/console.rs | 9 +++++++++ crates/web-sys/tests/wasm/main.rs | 1 + crates/webidl/src/lib.rs | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 crates/web-sys/tests/wasm/console.rs diff --git a/crates/web-sys/tests/wasm/console.rs b/crates/web-sys/tests/wasm/console.rs new file mode 100644 index 00000000..61ce5c29 --- /dev/null +++ b/crates/web-sys/tests/wasm/console.rs @@ -0,0 +1,9 @@ +use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; +use web_sys::console; + +#[wasm_bindgen_test] +fn test_console() { + console::time("test label"); + console::time_end("test label"); +} diff --git a/crates/web-sys/tests/wasm/main.rs b/crates/web-sys/tests/wasm/main.rs index 279f9bde..69e4d6f0 100644 --- a/crates/web-sys/tests/wasm/main.rs +++ b/crates/web-sys/tests/wasm/main.rs @@ -14,6 +14,7 @@ pub mod anchor_element; pub mod body_element; pub mod br_element; pub mod button_element; +pub mod console; pub mod div_element; pub mod element; pub mod event; diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 20bc0d44..f3628590 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -36,7 +36,7 @@ use std::path::Path; use backend::TryToTokens; use backend::defined::{ImportedTypeDefinitions, RemoveUndefinedImports}; -use backend::util::{ident_ty, rust_ident, wrap_import_function}; +use backend::util::{ident_ty, rust_ident, raw_ident, wrap_import_function}; use failure::ResultExt; use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; @@ -892,7 +892,7 @@ impl<'src> WebidlParse<'src, NamespaceNames<'src>> for weedle::namespace::Operat let import = backend::ast::Import { module: None, - js_namespace: Some(ns_names.js_name.clone()), + js_namespace: Some(raw_ident(ns_names.js_name)), kind: backend::ast::ImportKind::Function(imported_fn), }; From df1342398dc4a09efaedb62cbfebf11ac19b5019 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sat, 11 Aug 2018 12:50:18 +0100 Subject: [PATCH 35/41] Add support for partial namespaces --- crates/webidl/src/lib.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index f3628590..4d139c0c 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -189,8 +189,7 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { namespace.webidl_parse(program, first_pass, ())? } weedle::Definition::PartialNamespace(namespace) => { - // TODO - warn!("Unsupported WebIDL definition: {:?}", self) + namespace.webidl_parse(program, first_pass, ())? } // TODO weedle::Definition::Callback(..) @@ -832,6 +831,38 @@ impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { } } +impl<'src> WebidlParse<'src, ()> for weedle::PartialNamespaceDefinition<'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(()); + } + + let rust_name = rust_ident(self.identifier.0.to_snake_case().as_str()); + + if !first_pass.namespaces.contains_key(self.identifier.0) { + warn!( + "Partial namespace {} missing non-partial namespace", + self.identifier.0 + ); + } + + let namespace_names = NamespaceNames { + rust_name: &rust_name, + js_name: &self.identifier.0, + }; + for member in &self.members.body { + member.webidl_parse(program, first_pass, namespace_names)?; + } + + Ok(()) + } +} + impl<'src> WebidlParse<'src, &'src weedle::NamespaceDefinition<'src>> for ExtendedAttribute<'src> { fn webidl_parse( &'src self, From eaacdc8966c545d64073cdd2260f019d31a469c2 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sat, 11 Aug 2018 12:57:45 +0100 Subject: [PATCH 36/41] Mark that link is checked; --- crates/webidl/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index f654937b..2754ea7a 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1063,7 +1063,7 @@ impl<'src> FirstPassRecord<'src> { let doc_comment = format!("The `{}.{}()` function\n\n{}", namespace_name, name, - mdn_doc(namespace_name, Some(&name))); // TODO check link + mdn_doc(namespace_name, Some(&name))); // checked link self.create_function( &name, From e66d4da8358b5a31b22bcd9ab6ac75624ebb27e1 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 12 Aug 2018 12:11:09 +0100 Subject: [PATCH 37/41] Fix some of @ohanar issues --- crates/webidl/src/first_pass.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 09ffafbc..46263fd7 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -42,7 +42,6 @@ pub(crate) struct InterfaceData<'src> { } /// We need to collect namespace data during the first pass, to be used later. -#[derive(Default)] pub(crate) struct NamespaceData<'src> { /// Whether only partial namespaces were encountered pub(crate) partial: bool, @@ -50,8 +49,15 @@ pub(crate) struct NamespaceData<'src> { } impl<'src> NamespaceData<'src> { - /// Same as `Default::default` but sets `partial` to true. - pub(crate) fn default_for_partial() -> Self { + /// Creates an empty node for a non-partial namespace. + pub(crate) fn empty_non_partial() -> Self { + Self { + partial: false, + operations: Default::default(), + } + } + /// Creates an empty node for a partial namespace. + pub(crate) fn empty_partial() -> Self { Self { partial: true, operations: Default::default(), @@ -153,7 +159,7 @@ fn first_pass_interface_operation<'src>( .operations .entry(id) .and_modify(|operation_data| operation_data.overloaded = true) - .or_insert_with(Default::default) + .or_default() .argument_names_same .entry(names) .and_modify(|same_argument_names| *same_argument_names = true) @@ -340,13 +346,11 @@ impl<'src> FirstPass<'src, ()> for weedle::TypedefDefinition<'src> { impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, (): ()) -> Result<()> { - { - let namespace = record - .namespaces - .entry(self.identifier.0) - .or_default(); - namespace.partial = false; - } + record + .namespaces + .entry(self.identifier.0) + .and_modify(|entry| entry.partial = false) + .or_insert_with(Namespace::empty_non_partial); if util::is_chrome_only(&self.attributes) { return Ok(()) @@ -367,7 +371,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialNamespaceDefinition<'src> { record .namespaces .entry(self.identifier.0) - .or_insert_with(NamespaceData::default_for_partial); + .or_insert_with(NamespaceData::empty_partial); if util::is_chrome_only(&self.attributes) { return Ok(()) From 833099dd0dd17554a14ac05616ab4cc1a126e5e5 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 12 Aug 2018 13:11:53 +0100 Subject: [PATCH 38/41] Fix error --- crates/webidl/src/first_pass.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 46263fd7..f6c109fe 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -350,7 +350,7 @@ impl<'src> FirstPass<'src, ()> for weedle::NamespaceDefinition<'src> { .namespaces .entry(self.identifier.0) .and_modify(|entry| entry.partial = false) - .or_insert_with(Namespace::empty_non_partial); + .or_insert_with(NamespaceData::empty_non_partial); if util::is_chrome_only(&self.attributes) { return Ok(()) From 23009dbc1e0eff25b644f749e4884dc292f943d3 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 12 Aug 2018 21:11:02 +0100 Subject: [PATCH 39/41] Add simple test for namespaces. --- crates/webidl-tests/main.rs | 1 + crates/webidl-tests/namespace.js | 11 +++++++++++ crates/webidl-tests/namespace.rs | 10 ++++++++++ crates/webidl-tests/namespace.webidl | 4 ++++ 4 files changed, 26 insertions(+) create mode 100644 crates/webidl-tests/namespace.js create mode 100644 crates/webidl-tests/namespace.rs create mode 100644 crates/webidl-tests/namespace.webidl diff --git a/crates/webidl-tests/main.rs b/crates/webidl-tests/main.rs index 2a31140b..12635f50 100644 --- a/crates/webidl-tests/main.rs +++ b/crates/webidl-tests/main.rs @@ -10,3 +10,4 @@ pub mod consts; pub mod enums; pub mod simple; pub mod throws; +pub mod namespace; diff --git a/crates/webidl-tests/namespace.js b/crates/webidl-tests/namespace.js new file mode 100644 index 00000000..a3b83d1f --- /dev/null +++ b/crates/webidl-tests/namespace.js @@ -0,0 +1,11 @@ +const strictEqual = require('assert').strictEqual; + +global.math = class { + powf(base, exp) { + return Math.pow(base, exp); + } + + add_one(val) { + return val + 1; + } +}; diff --git a/crates/webidl-tests/namespace.rs b/crates/webidl-tests/namespace.rs new file mode 100644 index 00000000..848239aa --- /dev/null +++ b/crates/webidl-tests/namespace.rs @@ -0,0 +1,10 @@ +use wasm_bindgen_test::*; + +include!(concat!(env!("OUT_DIR"), "/namespace.rs")); + +#[wasm_bindgen_test] +fn simple_namespace_test() { + assert_eq!(math::add_one(1), 2); + assert_eq!(math::powf(1.0, 100.0), 1.0); + assert_eq!(math::powf(10.0, 2.0), 100.0); +} diff --git a/crates/webidl-tests/namespace.webidl b/crates/webidl-tests/namespace.webidl new file mode 100644 index 00000000..c42a4cf6 --- /dev/null +++ b/crates/webidl-tests/namespace.webidl @@ -0,0 +1,4 @@ +namespace math { + long add_one(long val); + double powf(double base, double exponent); +}; From 4f0ddd25ce745f426fd04b23e291eb5aa98aece4 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 12 Aug 2018 21:27:27 +0100 Subject: [PATCH 40/41] Fix tests --- crates/webidl-tests/namespace.js | 16 ++++++++-------- crates/webidl-tests/namespace.rs | 6 +++--- crates/webidl-tests/namespace.webidl | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/webidl-tests/namespace.js b/crates/webidl-tests/namespace.js index a3b83d1f..7c86dcd5 100644 --- a/crates/webidl-tests/namespace.js +++ b/crates/webidl-tests/namespace.js @@ -1,11 +1,11 @@ const strictEqual = require('assert').strictEqual; -global.math = class { - powf(base, exp) { - return Math.pow(base, exp); - } +global.mathtest = {}; - add_one(val) { - return val + 1; - } -}; +global.mathtest.powf = function powf(base, exp) { + return Math.pow(base, exp); +} + +global.mathtest.add_one = function add_one(val) { + return val + 1; +} diff --git a/crates/webidl-tests/namespace.rs b/crates/webidl-tests/namespace.rs index 848239aa..49afc623 100644 --- a/crates/webidl-tests/namespace.rs +++ b/crates/webidl-tests/namespace.rs @@ -4,7 +4,7 @@ include!(concat!(env!("OUT_DIR"), "/namespace.rs")); #[wasm_bindgen_test] fn simple_namespace_test() { - assert_eq!(math::add_one(1), 2); - assert_eq!(math::powf(1.0, 100.0), 1.0); - assert_eq!(math::powf(10.0, 2.0), 100.0); + assert_eq!(mathtest::add_one(1), 2); + assert_eq!(mathtest::powf(1.0, 100.0), 1.0); + assert_eq!(mathtest::powf(10.0, 2.0), 100.0); } diff --git a/crates/webidl-tests/namespace.webidl b/crates/webidl-tests/namespace.webidl index c42a4cf6..1c3dbf3a 100644 --- a/crates/webidl-tests/namespace.webidl +++ b/crates/webidl-tests/namespace.webidl @@ -1,4 +1,4 @@ -namespace math { +namespace mathtest { long add_one(long val); double powf(double base, double exponent); }; From ea05235985e90827a052546acdc95e8c7f22e2fd Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 12 Aug 2018 21:28:59 +0100 Subject: [PATCH 41/41] Fix docs about testing webidl --- guide/src/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/testing.md b/guide/src/testing.md index 8f4c2974..fe3bca62 100644 --- a/guide/src/testing.md +++ b/guide/src/testing.md @@ -26,7 +26,7 @@ cargo test ## The Web IDL Frontend's Tests ``` -cargo test -p wasm-bindgen-webidl +cargo test -p webidl-tests --target wasm32-unknown-unknown ``` ## The Macro UI Tests