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/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/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 4f575a7b..7e694230 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -19,6 +19,8 @@ pub struct Program { pub structs: Vec, /// rust consts pub consts: Vec, + /// rust submodules + pub modules: Vec, } /// A rust to js interface. Allows interaction with rust objects/functions @@ -239,6 +241,18 @@ 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))] +#[derive(Clone)] +pub struct Module { + pub vis: syn::Visibility, + pub name: Ident, + /// js -> rust interfaces + pub imports: Vec, +} + impl Program { pub(crate) fn shared(&self) -> Result { Ok(shared::Program { @@ -246,6 +260,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(), @@ -365,7 +381,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() } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index a40cbf3a..95e1ef9c 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() { @@ -67,6 +69,11 @@ impl TryToTokens for ast::Program { for c in self.consts.iter() { c.to_tokens(tokens); } + for m in self.modules.iter() { + if let Err(e) = m.try_to_tokens(tokens) { + errors.push(e); + } + } Diagnostic::from_vec(errors)?; @@ -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,30 @@ impl ToTokens for ast::Const { } } +impl<'a> TryToTokens for ast::Module { + fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> { + for import in &self.imports { + DescribeImport(&import.kind).to_tokens(tokens); + } + let vis = &self.vis; + let name = &self.name; + let mut errors = Vec::new(); + let mut body = TokenStream::new(); + for import in &self.imports { + if let Err(e) = import.kind.try_to_tokens(&mut body) { + errors.push(e); + } + } + Diagnostic::from_vec(errors)?; + (quote!{ + #vis 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); 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"), 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/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/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(()) + }) +} diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 9452d0a8..bf3c9704 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" { @@ -125,6 +128,7 @@ extern "C" { // Array #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Array; @@ -246,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. /// @@ -347,6 +385,7 @@ extern "C" { // ArrayBuffer #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type ArrayBuffer; @@ -421,6 +460,7 @@ extern "C" { // Boolean #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Boolean; @@ -440,6 +480,7 @@ extern "C" { // DataView #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type DataView; @@ -587,6 +628,7 @@ extern "C" { // Error #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Error; @@ -622,9 +664,27 @@ 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" { + // TODO Uncomment this once TypedArray is added: + // #[wasm_bindgen(extends = Object, extends = TypedArray)] #[derive(Clone, Debug)] pub type Float32Array; @@ -677,6 +737,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 +791,7 @@ extern "C" { // Function #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Function; @@ -1007,6 +1070,7 @@ extern "C" { // Map #[wasm_bindgen] extern { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Map; @@ -1483,6 +1547,7 @@ extern "C" { // Date. #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Date; @@ -1825,6 +1890,37 @@ 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.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, @@ -1988,6 +2084,44 @@ 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; +} + +// 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" { @@ -2146,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. @@ -2266,6 +2414,7 @@ extern { // Set #[wasm_bindgen] extern { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type Set; @@ -2347,6 +2496,46 @@ 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; +} + +// 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" { @@ -2557,9 +2746,29 @@ 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" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type WeakMap; @@ -2603,6 +2812,7 @@ extern "C" { // WeakSet #[wasm_bindgen] extern "C" { + #[wasm_bindgen(extends = Object)] #[derive(Clone, Debug)] pub type WeakSet; @@ -2648,6 +2858,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(catch, static_method_of = JSON)] + pub fn stringify(obj: &JsValue) -> Result; + +} + // JsString #[wasm_bindgen] extern "C" { @@ -3052,6 +3287,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 @@ -3059,19 +3302,65 @@ 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; + } + + // 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 @@ -3109,7 +3398,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 +3406,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 +3423,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, diff --git a/crates/js-sys/tests/wasm/Array.rs b/crates/js-sys/tests/wasm/Array.rs index d6c38954..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]; @@ -287,6 +300,7 @@ 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 = Array::new(); 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/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/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/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::()); +} diff --git a/crates/js-sys/tests/wasm/JSON.rs b/crates/js-sys/tests/wasm/JSON.rs new file mode 100644 index 00000000..b1d9fd18 --- /dev/null +++ b/crates/js-sys/tests/wasm/JSON.rs @@ -0,0 +1,84 @@ +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)).unwrap(); + let rust_str: String = From::from(str); + assert_eq!(rust_str, "[1,true,\"hello\"]"); +} + +#[wasm_bindgen_test] +fn stringify_error() { + let func = Function::new_no_args("throw new Error(\"rust really rocks\")"); + let obj = Object::new(); + Reflect::set(obj.as_ref(), &JsValue::from("toJSON"), func.as_ref()); + + 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")); +} 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 a79a57c6..9d43f8a2 100644 --- a/crates/js-sys/tests/wasm/Number.rs +++ b/crates/js-sys/tests/wasm/Number.rs @@ -1,17 +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())); @@ -112,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/Object.rs b/crates/js-sys/tests/wasm/Object.rs index da245a97..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,6 +37,42 @@ 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 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())); 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/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/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"); 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..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"); 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/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/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/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::()); +} diff --git a/crates/js-sys/tests/wasm/main.rs b/crates/js-sys/tests/wasm/main.rs index 1c4414b0..36271531 100755 --- a/crates/js-sys/tests/wasm/main.rs +++ b/crates/js-sys/tests/wasm/main.rs @@ -14,22 +14,29 @@ pub mod Boolean; pub mod DataView; pub mod Date; pub mod Error; +pub mod EvalError; pub mod Function; pub mod Generator; pub mod Intl; pub mod JsString; +pub mod JSON; pub mod Map; pub mod MapIterator; pub mod Math; pub mod Number; pub mod Object; pub mod Proxy; +pub mod RangeError; +pub mod ReferenceError; pub mod Reflect; pub mod RegExp; pub mod Set; pub mod SetIterator; pub mod Symbol; +pub mod SyntaxError; +pub mod TypeError; pub mod TypedArray; +pub mod UriError; pub mod WeakMap; pub mod WeakSet; pub mod WebAssembly; 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(), 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/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-tests/main.rs b/crates/webidl-tests/main.rs index 2a31140b..0bcae6b8 100644 --- a/crates/webidl-tests/main.rs +++ b/crates/webidl-tests/main.rs @@ -8,5 +8,6 @@ pub mod array; pub mod array_buffer; pub mod consts; pub mod enums; +pub mod namespace; pub mod simple; pub mod throws; diff --git a/crates/webidl-tests/namespace.js b/crates/webidl-tests/namespace.js new file mode 100644 index 00000000..c9e362dc --- /dev/null +++ b/crates/webidl-tests/namespace.js @@ -0,0 +1,9 @@ +global.math_test = { + pow(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..ec2fde2e --- /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_test::add_one(1), 2); + assert_eq!(math_test::pow(1.0, 100.0), 1.0); + assert_eq!(math_test::pow(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..65b20dcb --- /dev/null +++ b/crates/webidl-tests/namespace.webidl @@ -0,0 +1,4 @@ +namespace math_test { + long add_one(long val); + double pow(double base, double exponent); +}; diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 6d2e434a..f3637f61 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -12,7 +12,8 @@ use std::collections::{BTreeMap, BTreeSet}; use weedle::argument::Argument; use weedle::attribute::ExtendedAttribute; use weedle::interface::StringifierOrStatic; -use weedle::mixin::MixinMembers; +use weedle::mixin::MixinMember; +use weedle::namespace::NamespaceMember; use weedle; use super::Result; @@ -28,6 +29,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, MixinData<'src>>, pub(crate) typedefs: BTreeMap<&'src str, &'src weedle::types::Type<'src>>, + pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>, pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>, } @@ -46,7 +48,16 @@ pub(crate) struct InterfaceData<'src> { pub(crate) struct MixinData<'src> { /// Whether only partial mixins were encountered pub(crate) partial: bool, - pub(crate) members: Vec<&'src MixinMembers<'src>>, + pub(crate) members: Vec<&'src MixinMember<'src>>, + pub(crate) operations: BTreeMap, OperationData<'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, + pub(crate) members: Vec<&'src NamespaceMember<'src>>, pub(crate) operations: BTreeMap, OperationData<'src>>, } @@ -94,6 +105,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 @@ -147,9 +160,16 @@ impl<'src> FirstPass<'src, ()> for weedle::IncludesStatementDefinition<'src> { } } +#[derive(Clone, Copy)] +enum FirstPassOperationType { + Interface, + Mixin, + Namespace, +} + fn first_pass_operation<'src>( record: &mut FirstPassRecord<'src>, - mixin: bool, + first_pass_operation_type: FirstPassOperationType, self_name: &'src str, id: OperationId<'src>, arguments: &[Argument<'src>], @@ -161,22 +181,32 @@ fn first_pass_operation<'src>( Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } - if mixin { - &mut record - .mixins - .get_mut(self_name) - .expect(&format!("not found {} mixin", self_name)) - .operations - } else { - &mut record - .interfaces - .get_mut(self_name) - .expect(&format!("not found {} interface", self_name)) - .operations + match first_pass_operation_type{ + FirstPassOperationType::Interface => { + &mut record + .interfaces + .get_mut(self_name) + .expect(&format!("not found {} interface", self_name)) + .operations + }, + FirstPassOperationType::Mixin => { + &mut record + .mixins + .get_mut(self_name) + .expect(&format!("not found {} mixin", self_name)) + .operations + }, + FirstPassOperationType::Namespace => { + &mut record + .namespaces + .get_mut(self_name) + .expect(&format!("not found {} namesace", self_name)) + .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) @@ -195,7 +225,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceDefinition<'src> { let interface_data = record .interfaces .entry(self.identifier.0) - .or_insert_with(Default::default); + .or_default(); interface_data.partial = false; interface_data.superclass = self.inheritance.map(|s| s.identifier.0); } @@ -246,7 +276,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::ArgList(list) if list.identifier.0 == "Constructor" => { first_pass_operation( record, - false, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &list.args.body.list, @@ -255,7 +285,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { ExtendedAttribute::NoArgs(name) if (name.0).0 == "Constructor" => { first_pass_operation( record, - false, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &[], @@ -266,7 +296,7 @@ impl<'src> FirstPass<'src, &'src str> for ExtendedAttribute<'src> { { first_pass_operation( record, - false, + FirstPassOperationType::Interface, self_name, OperationId::Constructor, &list.args.body.list, @@ -312,15 +342,15 @@ impl<'src> FirstPass<'src, &'src str> for weedle::interface::OperationInterfaceM } first_pass_operation( record, - false, + FirstPassOperationType::Interface, 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(_)) => OperationId::IndexingGetter, + Some(weedle::interface::Special::Setter(_)) => OperationId::IndexingSetter, + Some(weedle::interface::Special::Deleter(_)) => OperationId::IndexingDeleter, + Some(weedle::interface::Special::LegacyCaller(_)) => return Ok(()), }, Some(ref name) => OperationId::Operation(Some(name.clone())), }, @@ -341,7 +371,7 @@ impl<'src> FirstPass<'src, ()> for weedle::InterfaceMixinDefinition<'src>{ .entry(self.identifier.0) .or_insert_with(Default::default); mixin_data.partial = false; - mixin_data.members.push(&self.members.body); + mixin_data.members.extend(&self.members.body); } for member in &self.members.body { @@ -369,7 +399,7 @@ impl<'src> FirstPass<'src, ()> for weedle::PartialInterfaceMixinDefinition<'src> }, ) .members - .push(&self.members.body); + .extend(&self.members.body); for member in &self.members.body { member.first_pass(record, self.identifier.0)?; @@ -402,7 +432,7 @@ impl<'src> FirstPass<'src, &'src str> for weedle::mixin::OperationMixinMember<'s } first_pass_operation( record, - true, + FirstPassOperationType::Mixin, self_name, OperationId::Operation(self.identifier.map(|s| s.0.clone())), &self.args.body.list, @@ -424,6 +454,82 @@ 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<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + record + .namespaces + .entry(self.identifier.0) + .and_modify(|namespace_data| namespace_data.partial = false) + .or_insert_with(|| + NamespaceData { + partial: true, + members: Default::default(), + operations: Default::default(), + }, + ) + .members + .extend(&self.members.body); + + 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<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + record + .namespaces + .entry(self.identifier.0) + .or_default() + .members + .extend(&self.members.body); + + 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>, self_name: &'src str) -> Result<()> { + match self { + weedle::namespace::NamespaceMember::Operation(op) => { + op.first_pass(record, self_name) + } + _ => Ok(()), + } + } +} + +impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceMember<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, self_name: &'src str) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()) + } + + first_pass_operation( + record, + FirstPassOperationType::Namespace, + self_name, + OperationId::Operation(self.identifier.map(|s| s.0.clone())), + &self.args.body.list, + ) + } +} + 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 fc1f2f0e..7cbae081 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -37,9 +37,9 @@ 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}; +use heck::{ShoutySnakeCase, SnakeCase}; use proc_macro2::{Ident, Span}; use weedle::argument::Argument; use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList}; @@ -175,24 +175,24 @@ impl<'src> WebidlParse<'src, ()> for weedle::Definition<'src> { weedle::Definition::PartialInterface(interface) => { interface.webidl_parse(program, first_pass, ())? } - weedle::Definition::Typedef(_) | - weedle::Definition::InterfaceMixin(_) | - weedle::Definition::PartialInterfaceMixin(_) => { - // handled in the first pass - } - weedle::Definition::IncludesStatement(..) => { + | weedle::Definition::Typedef(_) + | weedle::Definition::InterfaceMixin(_) + | weedle::Definition::PartialInterfaceMixin(_) + | weedle::Definition::IncludesStatement(..) + | weedle::Definition::PartialNamespace(..)=> { // handled in the first pass } weedle::Definition::Implements(..) => { // nothing to do for this, ignore it } + weedle::Definition::Namespace(namespace) => { + namespace.webidl_parse(program, first_pass, ())? + } // TODO - weedle::Definition::Callback(..) + | 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) } } @@ -250,10 +250,8 @@ impl<'src> WebidlParse<'src, ()> for weedle::InterfaceDefinition<'src> { mixin_name: &str, ) -> Result<()> { if let Some(mixin_data) = first_pass.mixins.get(mixin_name) { - for members in &mixin_data.members { - for member in *members { - member.webidl_parse(program, first_pass, self_name)?; - } + for member in &mixin_data.members { + member.webidl_parse(program, first_pass, self_name)?; } } if let Some(mixin_names) = first_pass.includes.get(mixin_name) { @@ -316,6 +314,7 @@ impl<'src> WebidlParse<'src, &'src weedle::InterfaceDefinition<'src>> for Extend arguments, &::first_pass::OperationId::Constructor, interface.identifier.0, + false, ); let self_ty = ident_ty(rust_ident(camel_case_ident(interface.identifier.0).as_str())); @@ -566,16 +565,16 @@ fn member_attribute<'src>( let is_structural = util::is_structural(attrs); let throws = util::throws(attrs); - for import_function in first_pass.create_getter( - identifier, - &type_.type_, - self_name, - is_static, - is_structural, - throws, - ) { - program.imports.push(wrap_import_function(import_function)); - } + for import_function in first_pass.create_getter( + identifier, + &type_.type_, + self_name, + is_static, + is_structural, + throws, + ) { + program.imports.push(wrap_import_function(import_function)); + } if !readonly { for import_function in first_pass.create_setter( @@ -666,10 +665,10 @@ 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(_) => ::first_pass::OperationId::IndexingGetter, + weedle::interface::Special::Setter(_) => ::first_pass::OperationId::IndexingSetter, + weedle::interface::Special::Deleter(_) => ::first_pass::OperationId::IndexingDeleter, + weedle::interface::Special::LegacyCaller(_) => return Ok(()), }, Some(ref name) if specials.is_empty() => ::first_pass::OperationId::Operation(Some(name.clone())), _ => { @@ -757,8 +756,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") } @@ -777,14 +776,14 @@ 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<()> { if util::is_chrome_only(&self.attributes) { return Ok(()); } - let idl_type = match self.const_type.to_idl_type(record) { + let idl_type = match self.const_type.to_idl_type(first_pass) { None => return Ok(()), Some(idl_type) => idl_type, }; @@ -805,3 +804,100 @@ impl<'src> WebidlParse<'src, &'src str> for weedle::interface::ConstMember<'src> Ok(()) } } + +impl<'src> WebidlParse<'src, ()> for weedle::NamespaceDefinition<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + (): (), + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + if let Some(attrs) = &self.attributes { + for attr in &attrs.body.list { + attr.webidl_parse(program, first_pass, self)?; + } + } + + let mut module = backend::ast::Module { + vis: public(), + name: rust_ident(self.identifier.0.to_snake_case().as_str()), + imports: Default::default(), + }; + + if let Some(namespace_data) = first_pass.namespaces.get(&self.identifier.0) { + for member in &namespace_data.members { + member.webidl_parse(program, first_pass, (&self.identifier.0, &mut module))?; + } + } + + program.modules.push(module); + + Ok(()) + } +} + +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(()) + } +} + +impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::NamespaceMember<'src> { + fn webidl_parse( + &'src self, + program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + (self_name, module): (&'src str, &mut backend::ast::Module), + ) -> Result<()> { + match self { + weedle::namespace::NamespaceMember::Operation(op) => { + op.webidl_parse(program, first_pass, (self_name, module))?; + } + weedle::namespace::NamespaceMember::Attribute(_) => { + warn!("Attribute namespace members are not supported") + } + } + Ok(()) + } +} + +impl<'src> WebidlParse<'src, (&'src str, &'src mut backend::ast::Module)> for weedle::namespace::OperationNamespaceMember<'src> { + fn webidl_parse( + &'src self, + _program: &mut backend::ast::Program, + first_pass: &FirstPassRecord<'src>, + (self_name, module): (&'src str, &mut backend::ast::Module), + ) -> Result<()> { + if util::is_chrome_only(&self.attributes) { + return Ok(()); + } + + for import_function in first_pass.create_namespace_operation( + &self.args.body.list, + self.identifier.as_ref().map(|id| id.0), + &self.return_type, + self_name, + util::throws(&self.attributes) + ) { + module.imports.push( + backend::ast::Import { + module: None, + js_namespace: Some(raw_ident(self_name)), + kind: backend::ast::ImportKind::Function(import_function), + } + ); + }; + + Ok(()) + } +} diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index d94c3e01..eecd403b 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -11,7 +11,7 @@ use weedle::attribute::{ExtendedAttributeList, ExtendedAttribute}; use weedle::argument::Argument; use weedle::literal::{ConstValue, FloatLit, IntegerLit}; -use first_pass::FirstPassRecord; +use first_pass::{self, FirstPassRecord}; use idl_type::{IdlType, ToIdlType, flatten}; /// Take a type and create an immutable shared reference to that type. @@ -367,7 +367,7 @@ 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, @@ -378,20 +378,21 @@ impl<'src> FirstPassRecord<'src> { arguments, &operation_id, self_name, + false, ); 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 Vec::new(); } 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 { @@ -400,11 +401,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, }, }), }; @@ -415,17 +416,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()), }; let arguments = match self.convert_arguments(arguments) { @@ -451,23 +452,24 @@ impl<'src> FirstPassRecord<'src> { pub fn get_operation_overloading( &self, arguments: &[weedle::argument::Argument], - id: &::first_pass::OperationId, + operation_id: &first_pass::OperationId, self_name: &str, + namespace: bool, ) -> (bool, bool) { fn get_operation_data<'src>( record: &'src FirstPassRecord, - id: &'src ::first_pass::OperationId, + operation_id: &'src ::first_pass::OperationId, self_name: &str, mixin_name: &str, ) -> Option<&'src ::first_pass::OperationData<'src>> { if let Some(mixin_data) = record.mixins.get(mixin_name) { - if let Some(operation_data) = mixin_data.operations.get(id) { + if let Some(operation_data) = mixin_data.operations.get(operation_id) { return Some(operation_data); } } if let Some(mixin_names) = record.includes.get(mixin_name) { for mixin_name in mixin_names { - if let Some(operation_data) = get_operation_data(record, id, self_name, mixin_name) { + if let Some(operation_data) = get_operation_data(record, operation_id, self_name, mixin_name) { return Some(operation_data); } } @@ -475,20 +477,28 @@ impl<'src> FirstPassRecord<'src> { None } - let operation_data = self - .interfaces - .get(self_name) - .and_then(|interface_data| interface_data.operations.get(id)) - .unwrap_or_else(|| - get_operation_data(self, id, self_name, self_name) - .expect(&format!("not found operation {:?} in interface {}", id, self_name)) - ); + let operation_data = if !namespace { + self + .interfaces + .get(self_name) + .and_then(|interface_data| interface_data.operations.get(operation_id)) + .unwrap_or_else(|| + get_operation_data(self, operation_id, self_name, self_name) + .expect(&format!("not found operation {:?} in interface {}", operation_id, self_name)) + ) + } else { + self + .namespaces + .get(self_name) + .and_then(|interface_data| interface_data.operations.get(operation_id)) + .expect(&format!("not found operation {:?} in namespace {}", operation_id, self_name)) + }; 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), + for argument in arguments { + match argument { + Argument::Single(single) => names.push(single.identifier.0), + Argument::Variadic(variadic) => names.push(variadic.identifier.0), } } ( @@ -500,6 +510,62 @@ 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: Option<&str>, + return_type: &weedle::types::ReturnType, + self_name: &str, + catch: bool, + ) -> Vec { + let (overloaded, same_argument_names) = self.get_operation_overloading( + arguments, + &first_pass::OperationId::Operation(operation_name), + self_name, + true, + ); + + let name = match operation_name { + Some(name) => name.to_string(), + None => { + warn!("Operations without a name are unsupported"); + return Vec::new(); + } + }; + + let ret = match return_type.to_idl_type(self) { + None => return Vec::new(), + Some(idl_type) => idl_type, + }; + + let doc_comment = Some( + format!( + "The `{}.{}()` function\n\n{}", + self_name, + name, + mdn_doc(self_name, Some(&name)) + ) + ); + + let arguments = match self.convert_arguments(arguments) { + None => return Vec::new(), + Some(arguments) => arguments + }; + + self.create_function( + &name, + overloaded, + same_argument_names, + &arguments, + ret, + backend::ast::ImportFunctionKind::Normal, + false, + catch, + doc_comment, + ) + } + /// Create a wasm-bindgen getter method, if possible. pub fn create_getter( &self, 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/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. 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. 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 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.