From cda71757d329063327f87c9637f8ef2d3fed51db Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 10:09:03 -0700 Subject: [PATCH 01/19] Tweak ArrayBuffer IDL type expansion slightly A few small changes here: * `ArrayBufferView` and `BufferSource` are expanded to themselves plus `Uint8ArrayMut` instead of `Object` to ensure we keep the original type. * Generating an argument type for `ArrayBufferView`, `BufferSource`, and `Object` all now generate shared references instead of owned objects, which is a little more consistent with the other interface types. --- crates/webidl/src/idl_type.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 83d303d45..513b5abe7 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -448,7 +448,11 @@ impl<'a> IdlType<'a> { }, IdlType::Object => { let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - Some(leading_colon_path_ty(path)) + let ty = leading_colon_path_ty(path); + Some(match pos { + TypePosition::Argument => shared_ref(ty, false), + TypePosition::Return => ty, + }) }, IdlType::Symbol => None, IdlType::Error => None, @@ -471,7 +475,11 @@ impl<'a> IdlType<'a> { IdlType::ArrayBufferView | IdlType::BufferSource => { let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - Some(leading_colon_path_ty(path)) + let ty = leading_colon_path_ty(path); + Some(match pos { + TypePosition::Argument => shared_ref(ty, false), + TypePosition::Return => ty, + }) }, IdlType::Interface(name) | IdlType::Dictionary(name) => { @@ -577,9 +585,12 @@ impl<'a> IdlType<'a> { .iter() .flat_map(|idl_type| idl_type.flatten()) .collect(), - IdlType::ArrayBufferView | IdlType::BufferSource => - vec![IdlType::Object, IdlType::Uint8ArrayMut], - + IdlType::ArrayBufferView => { + vec![IdlType::ArrayBufferView, IdlType::Uint8ArrayMut] + } + IdlType::BufferSource => { + vec![IdlType::BufferSource, IdlType::Uint8ArrayMut] + } idl_type @ _ => vec![idl_type.clone()], } } From 5df2347a7a02c0ca6d9b9c3ad45322f053b6339a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 13:32:31 -0700 Subject: [PATCH 02/19] js-sys: Add `extends` to `JSON` Part of #670 --- crates/js-sys/src/lib.rs | 6 +++++- crates/js-sys/tests/wasm/JSON.rs | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 89e728eed..91eedb55e 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3048,8 +3048,12 @@ pub mod WebAssembly { // JSON #[wasm_bindgen] extern "C" { - + /// The `JSON` object contains methods for parsing [JavaScript Object + /// Notation (JSON)](https://json.org/) and converting values to JSON. It + /// can't be called or constructed, and aside from its two method + /// properties, it has no interesting functionality of its own. #[derive(Clone, Debug)] + #[wasm_bindgen(extends = Object)] pub type JSON; /// The `JSON.parse()` method parses a JSON string, constructing the diff --git a/crates/js-sys/tests/wasm/JSON.rs b/crates/js-sys/tests/wasm/JSON.rs index b1d9fd184..d38bae3bc 100644 --- a/crates/js-sys/tests/wasm/JSON.rs +++ b/crates/js-sys/tests/wasm/JSON.rs @@ -1,4 +1,4 @@ -use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; use wasm_bindgen::JsCast; use js_sys::*; @@ -82,3 +82,15 @@ fn stringify_error() { let err_msg: String = From::from(err.message()); assert!(err_msg.contains("rust really rocks")); } + +#[wasm_bindgen_test] +fn json_extends() { + #[wasm_bindgen] + extern { + #[wasm_bindgen(js_name = JSON)] + static json: JSON; + } + + assert!(json.is_instance_of::()); + let _: &Object = json.as_ref(); +} From bfff8661c19d9d88b9baa7e40616f5fd8cead880 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 13:39:30 -0700 Subject: [PATCH 03/19] js-sys: Add `extends` to `Math` Part of #670 --- crates/js-sys/src/lib.rs | 1 + crates/js-sys/tests/wasm/Math.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 91eedb55e..ef3921be3 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -1203,6 +1203,7 @@ extern { #[wasm_bindgen] extern "C" { #[derive(Clone, Debug)] + #[wasm_bindgen(extends = Object)] pub type Math; /// The Math.abs() function returns the absolute value of a number, that is diff --git a/crates/js-sys/tests/wasm/Math.rs b/crates/js-sys/tests/wasm/Math.rs index a46ded34f..c6047fca6 100644 --- a/crates/js-sys/tests/wasm/Math.rs +++ b/crates/js-sys/tests/wasm/Math.rs @@ -1,9 +1,22 @@ use std::f64::consts::PI; use std::f64::{NEG_INFINITY, NAN}; +use wasm_bindgen::{JsCast, prelude::*}; use wasm_bindgen_test::*; use js_sys::*; +#[wasm_bindgen_test] +fn math_extends() { + #[wasm_bindgen] + extern { + #[wasm_bindgen(js_name = Math)] + static math: Math; + } + + assert!(math.is_instance_of::()); + let _: &Object = math.as_ref(); +} + macro_rules! assert_eq { ($a:expr, $b:expr) => ({ let (a, b) = (&$a, &$b); From 66d155d7085222391a3af4337175a88268764c45 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 13:42:16 -0700 Subject: [PATCH 04/19] js-sys: Add `extends` to `Reflect` Part of #670 --- crates/js-sys/src/lib.rs | 1 + crates/js-sys/tests/wasm/Reflect.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index ef3921be3..c2d2bd3aa 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2128,6 +2128,7 @@ extern { #[wasm_bindgen] extern "C" { #[derive(Clone, Debug)] + #[wasm_bindgen(extends = Object)] pub type Reflect; /// The static `Reflect.apply()` method calls a target function with diff --git a/crates/js-sys/tests/wasm/Reflect.rs b/crates/js-sys/tests/wasm/Reflect.rs index 5f6f47f17..da2c5217a 100644 --- a/crates/js-sys/tests/wasm/Reflect.rs +++ b/crates/js-sys/tests/wasm/Reflect.rs @@ -1,4 +1,4 @@ -use wasm_bindgen::prelude::*; +use wasm_bindgen::{JsCast, prelude::*}; use wasm_bindgen_test::*; use js_sys::*; @@ -180,3 +180,15 @@ fn set_prototype_of() { let obj = JsValue::from(obj); assert_eq!(JsValue::from(Reflect::get_prototype_of(&obj)), JsValue::null()); } + +#[wasm_bindgen_test] +fn reflect_extends() { + #[wasm_bindgen] + extern { + #[wasm_bindgen(js_name = Reflect)] + static reflect: Reflect; + } + + assert!(reflect.is_instance_of::()); + let _: &Object = reflect.as_ref(); +} From cb2aa999c0068ced63779c19cad0411e53d1a9a8 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:09:49 -0700 Subject: [PATCH 05/19] js-sys: Define imports for `WebAssembly.Instance` and its constructor Part of #670 and #275 --- crates/js-sys/src/lib.rs | 23 +++++++++++ crates/js-sys/tests/wasm/WebAssembly.js | 11 +++++ crates/js-sys/tests/wasm/WebAssembly.rs | 53 ++++++++++++++++--------- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index c2d2bd3aa..843eb404f 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2896,6 +2896,29 @@ pub mod WebAssembly { pub fn new(message: &str) -> CompileError; } + // WebAssembly.Instance + #[wasm_bindgen] + extern "C" { + /// A `WebAssembly.Instance` object is a stateful, executable instance + /// of a `WebAssembly.Module`. Instance objects contain all the exported + /// WebAssembly functions that allow calling into WebAssembly code from + /// JavaScript. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) + #[wasm_bindgen(extends = Object, js_namespace = WebAssembly)] + #[derive(Clone, Debug)] + pub type Instance; + + /// The `WebAssembly.Instance()` constructor function can be called to + /// synchronously instantiate a given `WebAssembly.Module` + /// object. However, the primary way to get an `Instance` is through the + /// asynchronous `WebAssembly.instantiateStreaming()` function. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) + #[wasm_bindgen(catch, constructor, js_namespace = WebAssembly)] + pub fn new(module: &Module, imports: &Object) -> Result; + } + // WebAssembly.LinkError #[wasm_bindgen] extern "C" { diff --git a/crates/js-sys/tests/wasm/WebAssembly.js b/crates/js-sys/tests/wasm/WebAssembly.js index 51d0c343a..a24342f53 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.js +++ b/crates/js-sys/tests/wasm/WebAssembly.js @@ -21,8 +21,19 @@ function getInvalidTableObject() { return { element: "anyfunc", initial: 1, maximum: 0 } } +function getImports() { + return { + imports: { + imported_func: function () { + return 1; + } + } + }; +} + module.exports = { getInvalidTableObject, getTableObject, getWasmArray, + getImports, }; diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index 35207f968..ad363acc7 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -1,11 +1,11 @@ use futures::Future; use js_sys::*; -use wasm_bindgen::{JsCast, prelude::*}; +use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen_futures::JsFuture; use wasm_bindgen_test::*; #[wasm_bindgen(module = "tests/wasm/WebAssembly.js")] -extern { +extern "C" { #[wasm_bindgen(js_name = getWasmArray)] fn get_wasm_array() -> Uint8Array; @@ -14,6 +14,9 @@ extern { #[wasm_bindgen(js_name = getInvalidTableObject)] fn get_invalid_table_object() -> Object; + + #[wasm_bindgen(js_name = getImports)] + fn get_imports() -> Object; } fn get_invalid_wasm() -> JsValue { @@ -38,23 +41,19 @@ fn validate() { #[wasm_bindgen_test(async)] fn compile_compile_error() -> impl Future { let p = WebAssembly::compile(&get_invalid_wasm()); - JsFuture::from(p) - .map(|_| unreachable!()) - .or_else(|e| { - assert!(e.is_instance_of::()); - Ok(()) - }) + JsFuture::from(p).map(|_| unreachable!()).or_else(|e| { + assert!(e.is_instance_of::()); + Ok(()) + }) } #[wasm_bindgen_test(async)] fn compile_type_error() -> impl Future { let p = WebAssembly::compile(&get_bad_type_wasm()); - JsFuture::from(p) - .map(|_| unreachable!()) - .or_else(|e| { - assert!(e.is_instance_of::()); - Ok(()) - }) + JsFuture::from(p).map(|_| unreachable!()).or_else(|e| { + assert!(e.is_instance_of::()); + Ok(()) + }) } #[wasm_bindgen_test(async)] @@ -63,8 +62,7 @@ fn compile_valid() -> impl Future { JsFuture::from(p) .map(|module| { assert!(module.is_instance_of::()); - }) - .map_err(|_| unreachable!()) + }).map_err(|_| unreachable!()) } #[wasm_bindgen_test] @@ -81,7 +79,9 @@ fn module_error() { let error = WebAssembly::Module::new(&get_invalid_wasm()).err().unwrap(); assert!(error.is_instance_of::()); - let error = WebAssembly::Module::new(&get_bad_type_wasm()).err().unwrap(); + let error = WebAssembly::Module::new(&get_bad_type_wasm()) + .err() + .unwrap(); assert!(error.is_instance_of::()); } @@ -117,7 +117,9 @@ fn table_inheritance() { #[wasm_bindgen_test] fn table_error() { - let error = WebAssembly::Table::new(&get_invalid_table_object()).err().unwrap(); + let error = WebAssembly::Table::new(&get_invalid_table_object()) + .err() + .unwrap(); assert!(error.is_instance_of::()); } @@ -154,6 +156,16 @@ fn runtime_error_inheritance() { let _: &Error = error.as_ref(); } +#[wasm_bindgen_test] +fn instance_constructor_and_inheritance() { + let module = WebAssembly::Module::new(&get_valid_wasm()).unwrap(); + let imports = get_imports(); + let instance = WebAssembly::Instance::new(&module, &imports).unwrap(); + assert!(instance.is_instance_of::()); + assert!(instance.is_instance_of::()); + let _: &Object = instance.as_ref(); +} + #[wasm_bindgen_test] fn memory_works() { let obj = Object::new(); @@ -166,7 +178,10 @@ fn memory_works() { assert_eq!(mem.grow(2), 2); assert_eq!(mem.grow(3), 4); assert_eq!( - mem.buffer().dyn_into::().unwrap().byte_length(), + mem.buffer() + .dyn_into::() + .unwrap() + .byte_length(), 7 * 64 * 1024, ); } From 8b5f5a75601858ad08065544a7a582e1dd6fa763 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:16:28 -0700 Subject: [PATCH 06/19] js-sys: Add `exports` getter to `WebAssembly.Instance` Part of #275 --- crates/js-sys/src/lib.rs | 9 +++++++++ crates/js-sys/tests/wasm/WebAssembly.rs | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 843eb404f..4b6b4c5d0 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2917,6 +2917,15 @@ pub mod WebAssembly { /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance) #[wasm_bindgen(catch, constructor, js_namespace = WebAssembly)] pub fn new(module: &Module, imports: &Object) -> Result; + + /// The `exports` readonly property of the `WebAssembly.Instance` object + /// prototype returns an object containing as its members all the + /// functions exported from the WebAssembly module instance, to allow + /// them to be accessed and used by JavaScript. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports) + #[wasm_bindgen(getter, method, js_namespace = WebAssembly)] + pub fn exports(this: &Instance) -> Object; } // WebAssembly.LinkError diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index ad363acc7..2d84dc4f9 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -157,13 +157,19 @@ fn runtime_error_inheritance() { } #[wasm_bindgen_test] -fn instance_constructor_and_inheritance() { +fn webassembly_instance() { let module = WebAssembly::Module::new(&get_valid_wasm()).unwrap(); let imports = get_imports(); let instance = WebAssembly::Instance::new(&module, &imports).unwrap(); + + // Inheritance chain is correct. assert!(instance.is_instance_of::()); assert!(instance.is_instance_of::()); let _: &Object = instance.as_ref(); + + // Has expected exports. + let exports = instance.exports(); + assert!(Reflect::has(exports.as_ref(), &"exported_func".into())); } #[wasm_bindgen_test] From 021cbbab7113947ea51f9badca28f0dc81565e11 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:35:45 -0700 Subject: [PATCH 07/19] js-sys: Add bindings for `WebAssembly.instantiate` Part of #275 --- crates/js-sys/src/lib.rs | 14 ++++++++++++++ crates/js-sys/tests/wasm/WebAssembly.rs | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 4b6b4c5d0..26b2b300f 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2866,6 +2866,20 @@ pub mod WebAssembly { #[wasm_bindgen(js_namespace = WebAssembly)] pub fn compile(buffer_source: &JsValue) -> Promise; + /// The `WebAssembly.instantiate()` function allows you to compile and + /// instantiate WebAssembly code. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) + #[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiate)] + pub fn instantiate_buffer(buffer: &[u8], imports: &Object) -> Promise; + + /// The `WebAssembly.instantiate()` function allows you to compile and + /// instantiate WebAssembly code. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate) + #[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiate)] + pub fn instantiate_module(module: &Module, imports: &Object) -> Promise; + /// The `WebAssembly.validate()` function validates a given typed /// array of WebAssembly binary code, returning whether the bytes /// form a valid wasm module (`true`) or not (`false`). diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index 2d84dc4f9..72ec451aa 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -172,6 +172,17 @@ fn webassembly_instance() { assert!(Reflect::has(exports.as_ref(), &"exported_func".into())); } +#[wasm_bindgen_test(async)] +fn instantiate_module() -> impl Future { + let module = WebAssembly::Module::new(&get_valid_wasm()).unwrap(); + let imports = get_imports(); + let p = WebAssembly::instantiate_module(&module, &imports); + JsFuture::from(p) + .map(|inst| { + assert!(inst.is_instance_of::()); + }) +} + #[wasm_bindgen_test] fn memory_works() { let obj = Object::new(); From 5a3cd893e014047f496dd5b13d9cdbb8be2fe966 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 5 Sep 2018 23:59:49 -0700 Subject: [PATCH 08/19] Implement `AsRef for Closure` This commit adds an implementation of `AsRef` for the `Closure` type. Previously this was not possible because the `JsValue` didn't actually exist until the closure was passed to JS, but the implementation has been changed to ... something a bit more unconventional. The end result, however, is that `Closure` now always contains a `JsValue`. The end result of this work is intended to be a precursor to binding callbacks in `web-sys` as `JsValue` everywhere but still allowing usage with `Closure`. --- build.rs | 4 +- crates/cli-support/src/descriptor.rs | 7 - crates/cli-support/src/js/closures.rs | 386 ++++++++++++++++++++++++++ crates/cli-support/src/js/mod.rs | 13 +- crates/cli-support/src/js/rust2js.rs | 48 ---- crates/cli-support/src/wasm2es6js.rs | 39 +-- crates/wasm-interpreter/src/lib.rs | 224 +++++++++++++-- src/closure.rs | 103 +++++-- src/lib.rs | 1 + 9 files changed, 687 insertions(+), 138 deletions(-) create mode 100644 crates/cli-support/src/js/closures.rs diff --git a/build.rs b/build.rs index 0d19b8268..d4038035f 100644 --- a/build.rs +++ b/build.rs @@ -1,2 +1,4 @@ // Empty `build.rs` so that `[package] links = ...` works in `Cargo.toml`. -fn main() {} +fn main() { + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index 593e75a23..2a091cde6 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -189,13 +189,6 @@ impl Descriptor { } } - pub fn ref_closure(&self) -> Option<&Closure> { - match *self { - Descriptor::Ref(ref s) => s.closure(), - _ => None, - } - } - pub fn closure(&self) -> Option<&Closure> { match *self { Descriptor::Closure(ref s) => Some(s), diff --git a/crates/cli-support/src/js/closures.rs b/crates/cli-support/src/js/closures.rs new file mode 100644 index 000000000..eaa7a145a --- /dev/null +++ b/crates/cli-support/src/js/closures.rs @@ -0,0 +1,386 @@ +//! Support for closures in wasm-bindgen +//! +//! This module contains the bulk of the support necessary to support closures +//! in `wasm-bindgen`. The main "support" here is that `Closure::wrap` creates +//! a `JsValue` through... well... unconventional mechanisms. +//! +//! This module contains one public function, `rewrite`. The function will +//! rewrite the wasm module to correctly call closure factories and thread +//! through values into the final `Closure` object. More details about how all +//! this works can be found in the code below. + +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::mem; + +use failure::Error; +use parity_wasm::elements::*; + +use descriptor::Descriptor; +use js::Context; +use js::js2rust::Js2Rust; + +pub fn rewrite(input: &mut Context) -> Result<(), Error> { + let info = ClosureDescriptors::new(input); + + // Sanity check to make sure things look ok and skip everything below if + // there's not calls to `Closure::new`. + assert_eq!( + info.element_removal_list.len(), + info.code_idx_to_descriptor.len(), + ); + if info.element_removal_list.len() == 0 { + return Ok(()) + } + + // Make sure the names section is available in the wasm module because we'll + // want to remap those function indices, and then actually remap all + // function indices. We're going to be injecting a few imported functions + // below which will shift the index space for all defined functions. + input.parse_wasm_names(); + Remap { + code_idx_to_descriptor: &info.code_idx_to_descriptor, + old_num_imports: input.module + .import_section() + .map(|s| s.functions()) + .unwrap_or(0) as u32, + }.remap_module(input.module); + + info.delete_function_table_entries(input); + info.inject_imports(input)?; + Ok(()) +} + +#[derive(Default)] +struct ClosureDescriptors { + /// A list of elements to remove from the function table. The first element + /// of the pair is the index of the entry in the element section, and the + /// second element of the pair is the index within that entry to remove. + element_removal_list: Vec<(usize, usize)>, + + /// A map from indexes in the code section which contain calls to + /// `__wbindgen_describe_closure` to the new function the whole function is + /// replaced with as well as the descriptor that the function describes. + /// + /// This map is later used to replace all calls to the keys of this map with + /// calls to the value of the map. + code_idx_to_descriptor: BTreeMap, +} + +impl ClosureDescriptors { + /// Find all invocations of `__wbindgen_describe_closure`. + /// + /// We'll be rewriting all calls to functions who call this import. Here we + /// iterate over all code found in the module, and anything which calls our + /// special imported function is interpreted. The result of interpretation will + /// inform of us of an entry to remove from the function table (as the describe + /// function is never needed at runtime) as well as a `Descriptor` which + /// describes the type of closure needed. + /// + /// All this information is then returned in the `ClosureDescriptors` return + /// value. + fn new(input: &mut Context) -> ClosureDescriptors { + let wbindgen_describe_closure = match input.interpreter.describe_closure_idx() { + Some(i) => i, + None => return Default::default(), + }; + let imports = input.module.import_section() + .map(|s| s.functions()) + .unwrap_or(0); + let mut ret = ClosureDescriptors::default(); + + let code = match input.module.code_section() { + Some(code) => code, + None => return Default::default(), + }; + for (i, function) in code.bodies().iter().enumerate() { + let mut call_found = false; + for instruction in function.code().elements() { + match instruction { + Instruction::Call(idx) if *idx == wbindgen_describe_closure => { + call_found = true; + break + } + _ => {} + } + } + if !call_found { + continue + } + let descriptor = input.interpreter.interpret_closure_descriptor( + i, + input.module, + &mut ret.element_removal_list, + ).unwrap(); + // `new_idx` is the function-space index of the function that we'll + // be injecting. Calls to the code function `i` will instead be + // rewritten to calls to `new_idx`, which is an import that we'll + // inject based on `descriptor`. + let new_idx = (ret.code_idx_to_descriptor.len() + imports) as u32; + ret.code_idx_to_descriptor.insert( + i as u32, + (new_idx, Descriptor::decode(descriptor)), + ); + } + return ret + } + + /// Here we remove elements from the function table. All our descriptor + /// functions are entries in this function table and can be removed once we + /// use them as they're not actually needed at runtime. + /// + /// One option for removal is to replace the function table entry with an + /// index to a dummy function, but for now we simply remove the table entry + /// altogether by splitting the section and having multiple `elem` sections + /// with holes in them. + fn delete_function_table_entries(&self, input: &mut Context) { + let elements = input.module.elements_section_mut().unwrap(); + let mut remove = HashMap::new(); + for (entry, idx) in self.element_removal_list.iter().cloned() { + remove.entry(entry).or_insert(HashSet::new()).insert(idx); + } + + let entries = mem::replace(elements.entries_mut(), Vec::new()); + let empty = HashSet::new(); + for (i, entry) in entries.into_iter().enumerate() { + let to_remove = remove.get(&i).unwrap_or(&empty); + + let mut current = Vec::new(); + assert_eq!(entry.offset().code().len(), 2); + let mut offset = match entry.offset().code()[0] { + Instruction::I32Const(x) => x, + _ => unreachable!(), + }; + for (j, idx) in entry.members().iter().enumerate() { + // If we keep this entry, then keep going + if !to_remove.contains(&j) { + current.push(*idx); + continue + } + + // If we have members of `current` then we save off a section + // of the function table, then update `offset` and keep going. + let next_offset = offset + (current.len() as i32) + 1; + if current.len() > 0 { + let members = mem::replace(&mut current, Vec::new()); + let offset = InitExpr::new(vec![ + Instruction::I32Const(offset), + Instruction::End, + ]); + let new_entry = ElementSegment::new(0, offset, members); + elements.entries_mut().push(new_entry); + } + offset = next_offset; + } + // Any remaining function table entries get pushed at the end. + if current.len() > 0 { + let offset = InitExpr::new(vec![ + Instruction::I32Const(offset), + Instruction::End, + ]); + let new_entry = ElementSegment::new(0, offset, current); + elements.entries_mut().push(new_entry); + } + } + } + + /// Inject new imports into the module. + /// + /// This function will inject new imported functions into the `input` module + /// described by the fields internally. These new imports will be closure + /// factories and are freshly generated shim in JS. + fn inject_imports(&self, input: &mut Context) -> Result<(), Error> { + // We'll be injecting new imports and we'll need to give them all a + // type. The signature is all `(i32, i32) -> i32` currently and we know + // that this signature already exists in the module as it's the + // signature of our `#[inline(never)]` functions. Find the type + // signature index so we can assign it below. + let type_idx = input.module.type_section() + .unwrap() + .types() + .iter() + .position(|ty| { + let fnty = match ty { + Type::Function(f) => f, + }; + fnty.params() == &[ValueType::I32, ValueType::I32] && + fnty.return_type() == Some(ValueType::I32) + }) + .unwrap(); + + // The last piece of the magic. For all our descriptors we found we + // inject a JS shim for the descriptor. This JS shim will manufacture a + // JS `function`, and prepare it to be invoked. + // + // Once all that's said and done we inject a new import into the wasm module + // of our new wrapper, and the `Remap` step above already wrote calls to + // this function within the module. + for (i, (_new_idx, descriptor)) in self.code_idx_to_descriptor.iter() { + let import_name = format!("__wbindgen_closure_wrapper{}", i); + + let closure = descriptor.closure().unwrap(); + + let (js, _ts, _js_doc) = { + let mut builder = Js2Rust::new("", input); + if closure.mutable { + builder + .prelude("let a = this.a;\n") + .prelude("this.a = 0;\n") + .rust_argument("a") + .finally("this.a = a;\n"); + } else { + builder.rust_argument("this.a"); + } + builder + .process(&closure.function)? + .finish("function", "this.f") + }; + input.expose_add_heap_object(); + input.function_table_needed = true; + let body = format!( + "function(ptr, f) {{ + let cb = {}; + cb.f = wasm.__wbg_function_table.get(f); + cb.a = ptr; + let real = cb.bind(cb); + real.original = cb; + return addHeapObject(real); + }}", + js, + ); + input.export(&import_name, &body, None); + + let new_import = ImportEntry::new( + "__wbindgen_placeholder__".to_string(), + import_name, + External::Function(type_idx as u32), + ); + input.module.import_section_mut() + .unwrap() + .entries_mut() + .push(new_import); + } + Ok(()) + } +} + +struct Remap<'a> { + code_idx_to_descriptor: &'a BTreeMap, + old_num_imports: u32, +} + +impl<'a> Remap<'a> { + fn remap_module(&self, module: &mut Module) { + for section in module.sections_mut() { + match section { + Section::Export(e) => self.remap_export_section(e), + Section::Element(e) => self.remap_element_section(e), + Section::Code(e) => self.remap_code_section(e), + Section::Start(i) => { self.remap_idx(i); } + Section::Name(n) => self.remap_name_section(n), + _ => {} + } + } + } + + fn remap_export_section(&self, section: &mut ExportSection) { + for entry in section.entries_mut() { + self.remap_export_entry(entry); + } + } + + fn remap_export_entry(&self, entry: &mut ExportEntry) { + match entry.internal_mut() { + Internal::Function(i) => { self.remap_idx(i); } + _ => {} + } + + } + + fn remap_element_section(&self, section: &mut ElementSection) { + for entry in section.entries_mut() { + self.remap_element_entry(entry); + } + } + + fn remap_element_entry(&self, entry: &mut ElementSegment) { + for member in entry.members_mut() { + self.remap_idx(member); + } + } + + fn remap_code_section(&self, section: &mut CodeSection) { + for body in section.bodies_mut() { + self.remap_func_body(body); + } + } + + fn remap_func_body(&self, body: &mut FuncBody) { + self.remap_instructions(body.code_mut()); + } + + fn remap_instructions(&self, code: &mut Instructions) { + for instr in code.elements_mut() { + self.remap_instruction(instr); + } + } + + fn remap_instruction(&self, instr: &mut Instruction) { + match instr { + Instruction::Call(i) => { self.remap_idx(i); } + _ => {} + } + } + + fn remap_name_section(&self, names: &mut NameSection) { + match names { + NameSection::Function(f) => self.remap_function_name_section(f), + NameSection::Local(f) => self.remap_local_name_section(f), + _ => {} + } + } + + fn remap_function_name_section(&self, names: &mut FunctionNameSection) { + let map = names.names_mut(); + let new = IndexMap::with_capacity(map.len()); + for (mut idx, name) in mem::replace(map, new) { + if !self.remap_idx(&mut idx) { + map.insert(idx, name); + } + } + } + + fn remap_local_name_section(&self, names: &mut LocalNameSection) { + let map = names.local_names_mut(); + let new = IndexMap::with_capacity(map.len()); + for (mut idx, name) in mem::replace(map, new) { + if !self.remap_idx(&mut idx) { + map.insert(idx, name); + } + } + } + + /// Returns whether `idx` pointed to a previously known descriptor function + /// that we're switching to an import + fn remap_idx(&self, idx: &mut u32) -> bool { + // If this was an imported function we didn't reorder those, so nothing + // to do. + if *idx < self.old_num_imports { + return false + } + let code_idx = *idx - self.old_num_imports; + + // If this `idx` points to a function which was effectively a descriptor + // function, then we want to re-point it to our imported function which + // is actually the shim factory. + if let Some((new_idx, _)) = self.code_idx_to_descriptor.get(&code_idx) { + *idx = *new_idx; + return true + } + + // And finally, otherwise this is just a normal function reference we + // don't want to touch, but we're injecting imports which shifts all + // function indices. + *idx += self.code_idx_to_descriptor.len() as u32; + false + } +} diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 8017338cf..43e875582 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -16,6 +16,7 @@ mod js2rust; use self::js2rust::Js2Rust; mod rust2js; use self::rust2js::Rust2Js; +mod closures; pub struct Context<'a> { pub globals: String, @@ -394,6 +395,7 @@ impl<'a> Context<'a> { self.create_memory_export(); self.unexport_unused_internal_exports(); + closures::rewrite(self)?; self.gc()?; // Note that it's important `throw` comes last *after* we gc. The @@ -1715,7 +1717,7 @@ impl<'a> Context<'a> { fn gc(&mut self) -> Result<(), Error> { let module = mem::replace(self.module, Module::default()); - let module = module.parse_names().unwrap_or_else(|p| p.1); + self.parse_wasm_names(); let result = wasm_gc::Config::new() .demangle(self.config.demangle) .keep_debug(self.config.keep_debug || self.config.debug) @@ -1727,9 +1729,16 @@ impl<'a> Context<'a> { Ok(()) } + fn parse_wasm_names(&mut self) { + let module = mem::replace(self.module, Module::default()); + let module = module.parse_names().unwrap_or_else(|p| p.1); + *self.module = module; + } + fn describe(&mut self, name: &str) -> Option { let name = format!("__wbindgen_describe_{}", name); - Some(Descriptor::decode(self.interpreter.interpret(&name, self.module)?)) + let descriptor = self.interpreter.interpret_descriptor(&name, self.module)?; + Some(Descriptor::decode(descriptor)) } fn global(&mut self, s: &str) { diff --git a/crates/cli-support/src/js/rust2js.rs b/crates/cli-support/src/js/rust2js.rs index a0fcd20a7..dd30d6ea9 100644 --- a/crates/cli-support/src/js/rust2js.rs +++ b/crates/cli-support/src/js/rust2js.rs @@ -275,54 +275,6 @@ impl<'a, 'b> Rust2Js<'a, 'b> { return Ok(()); } - if let Some(closure) = arg.ref_closure() { - let (js, _ts, _js_doc) = { - let mut builder = Js2Rust::new("", self.cx); - if closure.mutable { - builder - .prelude("let a = this.a;\n") - .prelude("this.a = 0;\n") - .rust_argument("a") - .finally("this.a = a;\n"); - } else { - builder.rust_argument("this.a"); - } - builder - .process(&closure.function)? - .finish("function", "this.f") - }; - self.cx.expose_get_global_argument()?; - self.cx.expose_uint32_memory(); - self.cx.expose_add_heap_object(); - self.cx.function_table_needed = true; - let reset_idx = format!( - "\ - let cb{0} = {js};\n\ - cb{0}.f = wasm.__wbg_function_table.get(getGlobalArgument({f}));\n\ - cb{0}.a = getGlobalArgument({a});\n\ - let real = cb{0}.bind(cb{0});\n\ - real.original = cb{0};\n\ - idx{0} = getUint32Memory()[{0} / 4] = addHeapObject(real);\n\ - ", - abi, - js = js, - f = self.global_idx(), - a = self.global_idx(), - ); - self.prelude(&format!( - "\ - let idx{0} = getUint32Memory()[{0} / 4];\n\ - if (idx{0} === 0xffffffff) {{\n\ - {1}\ - }}\n\ - ", - abi, &reset_idx - )); - self.cx.expose_get_object(); - self.js_arguments.push(format!("getObject(idx{})", abi)); - return Ok(()); - } - let invoc_arg = match *arg { ref d if d.is_number() => abi, Descriptor::Boolean => format!("{} !== 0", abi), diff --git a/crates/cli-support/src/wasm2es6js.rs b/crates/cli-support/src/wasm2es6js.rs index 952cb9c19..0562ab3a7 100644 --- a/crates/cli-support/src/wasm2es6js.rs +++ b/crates/cli-support/src/wasm2es6js.rs @@ -1,12 +1,9 @@ extern crate base64; extern crate tempfile; -use std::collections::{HashMap, HashSet}; -use std::fs; -use std::io; -use std::process::Command; +use std::collections::HashSet; -use failure::{Error, ResultExt}; +use failure::Error; use parity_wasm::elements::*; pub struct Config { @@ -221,35 +218,3 @@ impl Output { )) } } - -fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { - let output = cmd.output().with_context(|e| { - if e.kind() == io::ErrorKind::NotFound { - format!( - "failed to execute `{}`, is the tool installed \ - from the binaryen project?\ncommand line: {:?}", - program, cmd - ) - } else { - format!("failed to execute: {:?}", cmd) - } - })?; - if output.status.success() { - return Ok(()); - } - - let mut s = format!("failed to execute: {:?}\nstatus: {}\n", cmd, output.status); - if !output.stdout.is_empty() { - s.push_str(&format!( - "----- stdout ------\n{}\n", - String::from_utf8_lossy(&output.stdout) - )); - } - if !output.stderr.is_empty() { - s.push_str(&format!( - "----- stderr ------\n{}\n", - String::from_utf8_lossy(&output.stderr) - )); - } - bail!("{}", s) -} diff --git a/crates/wasm-interpreter/src/lib.rs b/crates/wasm-interpreter/src/lib.rs index da397f3d9..4f9401817 100644 --- a/crates/wasm-interpreter/src/lib.rs +++ b/crates/wasm-interpreter/src/lib.rs @@ -35,17 +35,22 @@ pub struct Interpreter { // calculations) imports: usize, - // Function index of the `__wbindgen_describe` imported function. We special - // case this to know when the environment's imported function is called. + // Function index of the `__wbindgen_describe` and + // `__wbindgen_describe_closure` imported functions. We special case this + // to know when the environment's imported function is called. describe_idx: Option, + describe_closure_idx: Option, // A mapping of string names to the function index, filled with all exported // functions. name_map: HashMap, - // The numerical index of the code section in the wasm module, indexed into + // The numerical index of the sections in the wasm module, indexed into // the module's list of sections. code_idx: Option, + types_idx: Option, + functions_idx: Option, + elements_idx: Option, // The current stack pointer (global 0) and wasm memory (the stack). Only // used in a limited capacity. @@ -60,6 +65,18 @@ pub struct Interpreter { // very specific to wasm-bindgen and is the purpose for the existence of // this module. descriptor: Vec, + + // When invoking the `__wbindgen_describe_closure` imported function, this + // stores the last table index argument, used for finding a different + // descriptor. + descriptor_table_idx: Option, +} + +struct Sections<'a> { + code: &'a CodeSection, + types: &'a TypeSection, + functions: &'a FunctionSection, + elements: &'a ElementSection, } impl Interpreter { @@ -82,6 +99,9 @@ impl Interpreter { for (i, s) in module.sections().iter().enumerate() { match s { Section::Code(_) => ret.code_idx = Some(i), + Section::Element(_) => ret.elements_idx = Some(i), + Section::Type(_) => ret.types_idx = Some(i), + Section::Function(_) => ret.functions_idx = Some(i), _ => {} } } @@ -101,10 +121,11 @@ impl Interpreter { if entry.module() != "__wbindgen_placeholder__" { continue } - if entry.field() != "__wbindgen_describe" { - continue + if entry.field() == "__wbindgen_describe" { + ret.describe_idx = Some(idx - 1 as u32); + } else if entry.field() == "__wbindgen_describe_closure" { + ret.describe_closure_idx = Some(idx - 1 as u32); } - ret.describe_idx = Some(idx - 1 as u32); } } @@ -142,47 +163,171 @@ impl Interpreter { /// /// Returns `Some` if `func` was found in the `module` and `None` if it was /// not found in the `module`. - pub fn interpret(&mut self, func: &str, module: &Module) -> Option<&[u32]> { - self.descriptor.truncate(0); + pub fn interpret_descriptor( + &mut self, + func: &str, + module: &Module, + ) -> Option<&[u32]> { let idx = *self.name_map.get(func)?; - let code = match &module.sections()[self.code_idx.unwrap()] { - Section::Code(s) => s, - _ => panic!(), - }; + self.with_sections(module, |me, sections| { + me.interpret_descriptor_idx(idx, sections) + }) + } + + fn interpret_descriptor_idx( + &mut self, + idx: u32, + sections: &Sections, + ) -> Option<&[u32]> { + self.descriptor.truncate(0); // We should have a blank wasm and LLVM stack at both the start and end // of the call. assert_eq!(self.sp, self.mem.len() as i32); assert_eq!(self.stack.len(), 0); - self.call(idx, code); + self.call(idx, sections); assert_eq!(self.stack.len(), 0); assert_eq!(self.sp, self.mem.len() as i32); Some(&self.descriptor) } - fn call(&mut self, idx: u32, code: &CodeSection) { + /// Interprets a "closure descriptor", figuring out the signature of the + /// closure that was intended. + /// + /// This function will take a `code_idx` which is known to internally + /// execute `__wbindgen_describe_closure` and interpret it. The + /// `wasm-bindgen` crate controls all callers of this internal import. It + /// will then take the index passed to `__wbindgen_describe_closure` and + /// interpret it as a function pointer. This means it'll look up within the + /// element section (function table) which index it points to. Upon finding + /// the relevant entry it'll assume that function is a descriptor function, + /// and then it will execute the descriptor function. + /// + /// The returned value is the return value of the descriptor function found. + /// The `entry_removal_list` list is also then populated with an index of + /// the entry in the elements section (and then the index within that + /// section) of the function that needs to be snip'd out. + pub fn interpret_closure_descriptor( + &mut self, + code_idx: usize, + module: &Module, + entry_removal_list: &mut Vec<(usize, usize)>, + ) -> Option<&[u32]> { + self.with_sections(module, |me, sections| { + me._interpret_closure_descriptor(code_idx, sections, entry_removal_list) + }) + } + + fn _interpret_closure_descriptor( + &mut self, + code_idx: usize, + sections: &Sections, + entry_removal_list: &mut Vec<(usize, usize)>, + ) -> Option<&[u32]> { + // Call the `code_idx` function. This is an internal `#[inline(never)]` + // whose code is completely controlled by the `wasm-bindgen` crate, so + // it should take two arguments and return one (all of which we don't + // care about here). What we're interested in is that while executing + // this function it'll call `__wbindgen_describe_closure` with an + // argument that we look for. + assert!(self.descriptor_table_idx.is_none()); + let closure_descriptor_idx = (code_idx + self.imports) as u32; + self.stack.push(0); + self.stack.push(0); + self.call(closure_descriptor_idx, sections); + assert_eq!(self.stack.len(), 1); + self.stack.pop(); + let descriptor_table_idx = self.descriptor_table_idx.take().unwrap(); + + // After we've got the table index of the descriptor function we're + // interested go take a look in the function table to find what the + // actual index of the function is. + let (entry_idx, offset, entry) = sections.elements.entries() + .iter() + .enumerate() + .filter_map(|(i, entry)| { + let code = entry.offset().code(); + if code.len() != 2 { + return None + } + if code[1] != Instruction::End { + return None + } + match code[0] { + Instruction::I32Const(x) => Some((i, x as u32, entry)), + _ => None, + } + }) + .find(|(_i, offset, entry)| { + *offset <= descriptor_table_idx && + descriptor_table_idx < (*offset + entry.members().len() as u32) + }) + .expect("failed to find index in table elements"); + let idx = (descriptor_table_idx - offset) as usize; + let descriptor_idx = entry.members()[idx]; + + // This is used later to actually remove the entry from the table, but + // we don't do the removal just yet + entry_removal_list.push((entry_idx, idx)); + + // And now execute the descriptor! + self.interpret_descriptor_idx(descriptor_idx, sections) + } + + /// Returns the function space index of the `__wbindgen_describe_closure` + /// imported function. + pub fn describe_closure_idx(&self) -> Option { + self.describe_closure_idx + } + + fn call(&mut self, idx: u32, sections: &Sections) { use parity_wasm::elements::Instruction::*; let idx = idx as usize; assert!(idx >= self.imports); // can't call imported functions - let body = &code.bodies()[idx - self.imports]; + let code_idx = idx - self.imports; + let body = §ions.code.bodies()[code_idx]; // Allocate space for our call frame's local variables. All local // variables should be of the `i32` type. assert!(body.locals().len() <= 1, "too many local types"); - let locals = body.locals() + let nlocals = body.locals() .get(0) .map(|i| { assert_eq!(i.value_type(), ValueType::I32); i.count() }) .unwrap_or(0); - let mut locals = vec![0; locals as usize]; + + let code_sig = sections.functions.entries()[code_idx].type_ref(); + let function_ty = match §ions.types.types()[code_sig as usize] { + Type::Function(t) => t, + }; + let mut locals = Vec::with_capacity(function_ty.params().len() + nlocals as usize); + // Any function parameters we have get popped off the stack and put into + // the first few locals ... + for param in function_ty.params() { + assert_eq!(*param, ValueType::I32); + locals.push(self.stack.pop().unwrap()); + } + // ... and the remaining locals all start as zero ... + for _ in 0..nlocals { + locals.push(0); + } + // ... and we expect one stack slot at the end if there's a returned + // value + let before = self.stack.len(); + let stack_after = match function_ty.return_type() { + Some(t) => { + assert_eq!(t, ValueType::I32); + before + 1 + } + None => before, + }; // Actual interpretation loop! We keep track of our stack's length to // recover it as part of the `Return` instruction, and otherwise this is // a pretty straightforward interpretation loop. - let before = self.stack.len(); for instr in body.code().elements() { match instr { I32Const(x) => self.stack.push(*x), @@ -198,8 +343,14 @@ impl Interpreter { // Otherwise this is a normal call so we recurse. if Some(*idx) == self.describe_idx { self.descriptor.push(self.stack.pop().unwrap() as u32); + } else if Some(*idx) == self.describe_closure_idx { + self.descriptor_table_idx = + Some(self.stack.pop().unwrap() as u32); + assert_eq!(self.stack.pop(), Some(0)); + assert_eq!(self.stack.pop(), Some(0)); + self.stack.push(0); } else { - self.call(*idx, code); + self.call(*idx, sections); } } GetGlobal(0) => self.stack.push(self.sp), @@ -223,7 +374,7 @@ impl Interpreter { let addr = self.stack.pop().unwrap() as u32; self.stack.push(self.mem[((addr + *offset) as usize) / 4]); } - Return => self.stack.truncate(before), + Return => self.stack.truncate(stack_after), End => break, // All other instructions shouldn't be used by our various @@ -238,6 +389,37 @@ impl Interpreter { s => panic!("unknown instruction {:?}", s), } } - assert_eq!(self.stack.len(), before); + assert_eq!(self.stack.len(), stack_after); + } + + fn with_sections<'a, T>( + &'a mut self, + module: &Module, + f: impl FnOnce(&'a mut Self, &Sections) -> T, + ) -> T { + macro_rules! access_with_defaults { + ($( + let $var: ident = module.sections[self.$field:ident] + ($name:ident); + )*) => {$( + let default = Default::default(); + let $var = match self.$field { + Some(i) => { + match &module.sections()[i] { + Section::$name(s) => s, + _ => panic!(), + } + } + None => &default, + }; + )*} + } + access_with_defaults! { + let code = module.sections[self.code_idx] (Code); + let types = module.sections[self.types_idx] (Type); + let functions = module.sections[self.functions_idx] (Function); + let elements = module.sections[self.elements_idx] (Element); + } + f(self, &Sections { code, types, functions, elements }) } } diff --git a/src/closure.rs b/src/closure.rs index 633d35b19..8f3bc76e0 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -4,8 +4,6 @@ //! closures" from Rust to JS. Some more details can be found on the `Closure` //! type itself. -#![allow(const_err)] // FIXME(rust-lang/rust#52603) - use std::cell::UnsafeCell; #[cfg(feature = "nightly")] use std::marker::Unsize; @@ -70,14 +68,12 @@ use throw; /// } /// ``` pub struct Closure { - // Actually a `Rc` pointer, but in raw form so we can easily make copies. - // See below documentation for why this is in an `Rc`. - inner: *const UnsafeCell>, - js: UnsafeCell>, + js: ManuallyDrop, + _keep_this_data_alive: Rc>>, } impl Closure - where T: ?Sized, + where T: ?Sized + WasmClosure, { /// Creates a new instance of `Closure` from the provided Rust closure. /// @@ -103,9 +99,73 @@ impl Closure /// /// This is the function where the JS closure is manufactured. pub fn wrap(t: Box) -> Closure { + let data = Rc::new(UnsafeCell::new(t)); + let ptr = &*data as *const UnsafeCell>; + + // Here we need to create a `JsValue` with the data and `T::invoke()` + // function pointer. To do that we... take a few unconventional turns. + // In essence what happens here is this: + // + // 1. First up, below we call a function, `breaks_if_inlined`. This + // function, as the name implies, does not work if it's inlined. + // More on that in a moment. + // 2. This function internally calls a special import recognized by the + // `wasm-bindgen` CLI tool, `__wbindgen_describe_closure`. This + // imported symbol is similar to `__wbindgen_describe` in that it's + // not intended to show up in the final binary but it's an + // intermediate state for a `wasm-bindgen` binary. + // 3. The `__wbindgen_describe_closure` import is namely passed a + // descriptor function, monomorphized for each invocation. + // + // Most of this doesn't actually make sense to happen at runtime! The + // real magic happens when `wasm-bindgen` comes along and updates our + // generated code. When `wasm-bindgen` runs it performs a few tasks: + // + // * First, it finds all functions that call + // `__wbindgen_describe_closure`. These are all `breaks_if_inlined` + // defined below as the symbol isn't called anywhere else. + // * Next, `wasm-bindgen` executes the `breaks_if_inlined` + // monomorphized functions, passing it dummy arguments. This will + // execute the function just enough to invoke the special import, + // namely telling us about the function pointer that is the describe + // shim. + // * This knowledge is then used to actually find the descriptor in the + // function table which is then executed to figure out the signature + // of the closure. + // * Finally, and probably most heinously, the call to + // `breaks_if_inlined` is rewritten to call an otherwise globally + // imported function. This globally imported function will generate + // the `JsValue` for this closure specialized for the signature in + // question. + // + // Later on `wasm-gc` will clean up all the dead code and ensure that + // we don't actually call `__wbindgen_describe_closure` at runtime. This + // means we will end up not actually calling `breaks_if_inlined` in the + // final binary, all calls to that function should be pruned. + // + // See crates/cli-support/src/js/closures.rs for a more information + // about what's going on here. + + extern fn describe() { + inform(CLOSURE); + T::describe() + } + + #[inline(never)] + unsafe fn breaks_if_inlined( + ptr: usize, + invoke: u32, + ) -> u32 { + super::__wbindgen_describe_closure(ptr as u32, invoke, describe:: as u32) + } + + let idx = unsafe { + breaks_if_inlined::(ptr as usize, T::invoke_fn()) + }; + Closure { - inner: Rc::into_raw(Rc::new(UnsafeCell::new(t))), - js: UnsafeCell::new(ManuallyDrop::new(JsValue { idx: !0 })), + js: ManuallyDrop::new(JsValue { idx }), + _keep_this_data_alive: data, } } @@ -122,7 +182,7 @@ impl Closure /// cleanup as it can. pub fn forget(self) { unsafe { - let idx = (*self.js.get()).idx; + let idx = self.js.idx; if idx != !0 { super::__wbindgen_cb_forget(idx); } @@ -131,12 +191,17 @@ impl Closure } } +impl AsRef for Closure { + fn as_ref(&self) -> &JsValue { + &self.js + } +} + impl WasmDescribe for Closure where T: WasmClosure + ?Sized, { fn describe() { - inform(CLOSURE); - T::describe(); + inform(ANYREF); } } @@ -147,11 +212,7 @@ impl<'a, T> IntoWasmAbi for &'a Closure type Abi = u32; fn into_abi(self, extra: &mut Stack) -> u32 { - unsafe { - extra.push(T::invoke_fn()); - extra.push(self.inner as u32); - &mut (*self.js.get()).idx as *const u32 as u32 - } + (&*self.js).into_abi(extra) } } @@ -170,11 +231,9 @@ impl Drop for Closure { fn drop(&mut self) { unsafe { - let idx = (*self.js.get()).idx; - if idx != !0 { - super::__wbindgen_cb_drop(idx); - } - drop(Rc::from_raw(self.inner)); + // this will implicitly drop our strong reference in addition to + // invalidating all future invocations of the closure + super::__wbindgen_cb_drop(self.js.idx); } } } diff --git a/src/lib.rs b/src/lib.rs index d96b59938..79dcef457 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -433,6 +433,7 @@ externs! { fn __wbindgen_cb_forget(idx: u32) -> (); fn __wbindgen_describe(v: u32) -> (); + fn __wbindgen_describe_closure(a: u32, b: u32, c: u32) -> u32; fn __wbindgen_json_parse(ptr: *const u8, len: usize) -> u32; fn __wbindgen_json_serialize(idx: u32, ptr: *mut *mut u8) -> usize; From fb5e6e9c06aa9cd189e46cb1b9beefeac6da8eea Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:47:37 -0700 Subject: [PATCH 09/19] js-sys: Add bindings for `WebAssembly.instantiateStreaming` Part of #275 --- crates/js-sys/src/lib.rs | 9 +++++++++ crates/js-sys/tests/wasm/WebAssembly.js | 6 ++++++ crates/js-sys/tests/wasm/WebAssembly.rs | 14 ++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 26b2b300f..07ffd5a05 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -2880,6 +2880,15 @@ pub mod WebAssembly { #[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiate)] pub fn instantiate_module(module: &Module, imports: &Object) -> Promise; + /// The `WebAssembly.instantiateStreaming()` function compiles and + /// instantiates a WebAssembly module directly from a streamed + /// underlying source. This is the most efficient, optimized way to load + /// wasm code. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiateStreaming) + #[wasm_bindgen(js_namespace = WebAssembly, js_name = instantiateStreaming)] + pub fn instantiate_streaming(response: &Promise, imports: &Object) -> Promise; + /// The `WebAssembly.validate()` function validates a given typed /// array of WebAssembly binary code, returning whether the bytes /// form a valid wasm module (`true`) or not (`false`). diff --git a/crates/js-sys/tests/wasm/WebAssembly.js b/crates/js-sys/tests/wasm/WebAssembly.js index a24342f53..694046853 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.js +++ b/crates/js-sys/tests/wasm/WebAssembly.js @@ -31,6 +31,12 @@ function getImports() { }; } +// Polyfill `WebAssembly.instantiateStreaming` for node. +if (!global.WebAssembly.instantiateStreaming) { + global.WebAssembly.instantiateStreaming = + (response, imports) => response.then(buf => WebAssembly.instantiate(buf, imports)); +} + module.exports = { getInvalidTableObject, getTableObject, diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index 72ec451aa..e0b10931f 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -183,6 +183,20 @@ fn instantiate_module() -> impl Future { }) } +#[wasm_bindgen_test(async)] +fn instantiate_streaming() -> impl Future { + let response = Promise::resolve(&get_valid_wasm()); + let imports = get_imports(); + let p = WebAssembly::instantiate_streaming(&response, &imports); + JsFuture::from(p) + .map(|obj| { + assert!( + Reflect::get(obj.as_ref(), &"instance".into()) + .is_instance_of::() + ); + }) +} + #[wasm_bindgen_test] fn memory_works() { let obj = Object::new(); From 9d5898ab487bbdee81829b2e595e895349c8ee42 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 11:35:10 -0700 Subject: [PATCH 10/19] Bump to 0.2.20 --- CHANGELOG.md | 31 ++++++++++++++++++++++++++++++ Cargo.toml | 8 ++++---- crates/backend/Cargo.toml | 4 ++-- crates/cli-support/Cargo.toml | 6 +++--- crates/cli/Cargo.toml | 6 +++--- crates/futures/Cargo.toml | 8 ++++---- crates/js-sys/Cargo.toml | 8 ++++---- crates/macro-support/Cargo.toml | 6 +++--- crates/macro/Cargo.toml | 4 ++-- crates/shared/Cargo.toml | 2 +- crates/test-macro/Cargo.toml | 2 +- crates/test/Cargo.toml | 10 +++++----- crates/wasm-interpreter/Cargo.toml | 2 +- crates/web-sys/Cargo.toml | 8 ++++---- crates/webidl/Cargo.toml | 2 +- 15 files changed, 69 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a97046a46..0faaa0cf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,37 @@ Released YYYY-MM-DD. -------------------------------------------------------------------------------- +## 0.2.20 + +Released 2018-09-06 + +### Added + +* All of `wasm-bindgen` is configured to compile on stable Rust as of the + upcoming 1.30.0 release, scheduled for October 25, 2018. +* The underlying `JsValue` of a `Closure` type can now be extracted at any + time. +* Initial and experimental support was added for modules that have shared memory + (use atomic instructions). + +### Removed + +* The `--wasm2asm` flag of `wasm2es6js` was removed because the `wasm2asm` tool + has been removed from upstream Binaryen. This is replaced with the new + `wasm2js` tool from Binaryen. + +### Fixed + +* The "schema" version for wasm-bindgen now changes on all publishes, meaning we + can't forget to update it. This means that the crate version and CLI version + must exactly match. +* The `wasm-bindgen` crate now has a `links` key which forbids multiple versions + of `wasm-bindgen` from being linked into a dependency graph, fixing obscure + linking errors with a more first-class error message. +* Binary releases for Windows has been fixed. + +-------------------------------------------------------------------------------- + ## 0.2.19 (and 0.2.18) Released 2018-08-27. diff --git a/Cargo.toml b/Cargo.toml index 589ea65d1..35840d716 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" # Because only a single `wasm_bindgen` version can be used in a dependency @@ -35,13 +35,13 @@ nightly = [] xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_generated_code"] [dependencies] -wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.19" } +wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.20" } serde = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -js-sys = { path = 'crates/js-sys', version = '0.2.4' } -wasm-bindgen-test = { path = 'crates/test', version = '=0.2.19' } +js-sys = { path = 'crates/js-sys', version = '0.2.5' } +wasm-bindgen-test = { path = 'crates/test', version = '=0.2.20' } serde_derive = "1.0" wasm-bindgen-test-crate-a = { path = 'tests/crates/a', version = '0.1' } wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' } diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml index 4fa919602..051a27199 100644 --- a/crates/backend/Cargo.toml +++ b/crates/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-backend" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend" @@ -21,4 +21,4 @@ proc-macro2 = "0.4.8" quote = '0.6' serde_json = "1.0" syn = { version = '0.14', features = ['full', 'visit'] } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml index fe075b6c7..9240bec16 100644 --- a/crates/cli-support/Cargo.toml +++ b/crates/cli-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-cli-support" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli-support" @@ -17,6 +17,6 @@ parity-wasm = "0.32" serde = "1.0" serde_json = "1.0" tempfile = "3.0" -wasm-bindgen-shared = { path = "../shared", version = '=0.2.19' } -wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.19' } +wasm-bindgen-shared = { path = "../shared", version = '=0.2.20' } +wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.20' } wasm-gc-api = "0.1.9" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 84352a3b5..445e27c91 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-cli" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli" @@ -23,8 +23,8 @@ rouille = { version = "2.1.0", default-features = false } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.19" } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" } +wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.20" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } openssl = { version = '0.10.11', optional = true } [features] diff --git a/crates/futures/Cargo.toml b/crates/futures/Cargo.toml index 1f982b714..eee21c54b 100644 --- a/crates/futures/Cargo.toml +++ b/crates/futures/Cargo.toml @@ -7,12 +7,12 @@ license = "MIT/Apache-2.0" name = "wasm-bindgen-futures" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures" readme = "./README.md" -version = "0.2.19" +version = "0.2.20" [dependencies] futures = "0.1.20" -js-sys = { path = "../js-sys", version = '0.2.4' } -wasm-bindgen = { path = "../..", version = '0.2.19' } +js-sys = { path = "../js-sys", version = '0.2.5' } +wasm-bindgen = { path = "../..", version = '0.2.20' } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = { path = '../test', version = '0.2.19' } +wasm-bindgen-test = { path = '../test', version = '0.2.20' } diff --git a/crates/js-sys/Cargo.toml b/crates/js-sys/Cargo.toml index 3aeca413e..3092f092e 100644 --- a/crates/js-sys/Cargo.toml +++ b/crates/js-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "js-sys" -version = "0.2.4" +version = "0.2.5" authors = ["The wasm-bindgen Developers"] readme = "./README.md" categories = ["wasm"] @@ -18,9 +18,9 @@ test = false doctest = false [dependencies] -wasm-bindgen = { path = "../..", version = "0.2.19" } +wasm-bindgen = { path = "../..", version = "0.2.20" } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] futures = "0.1.20" -wasm-bindgen-test = { path = '../test', version = '=0.2.19' } -wasm-bindgen-futures = { path = '../futures', version = '=0.2.19' } +wasm-bindgen-test = { path = '../test', version = '=0.2.20' } +wasm-bindgen-futures = { path = '../futures', version = '=0.2.20' } diff --git a/crates/macro-support/Cargo.toml b/crates/macro-support/Cargo.toml index bf0e116d2..c665573b1 100644 --- a/crates/macro-support/Cargo.toml +++ b/crates/macro-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-macro-support" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support" @@ -18,5 +18,5 @@ extra-traits = ["syn/extra-traits"] syn = { version = '0.14', features = ['full'] } quote = '0.6' proc-macro2 = "0.4.9" -wasm-bindgen-backend = { path = "../backend", version = "=0.2.19" } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.19" } +wasm-bindgen-backend = { path = "../backend", version = "=0.2.20" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index c262d0c26..3f48d0c17 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-macro" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro" @@ -18,5 +18,5 @@ spans = ["wasm-bindgen-macro-support/spans"] xxx_debug_only_print_generated_code = [] [dependencies] -wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.19" } +wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.20" } quote = "0.6" diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml index e82dd453e..dc4bd3286 100644 --- a/crates/shared/Cargo.toml +++ b/crates/shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-shared" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared" diff --git a/crates/test-macro/Cargo.toml b/crates/test-macro/Cargo.toml index da68c9ba3..913d2989b 100644 --- a/crates/test-macro/Cargo.toml +++ b/crates/test-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-test-macro" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] description = "Internal testing macro for wasm-bindgen" license = "MIT/Apache-2.0" diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index 6d04c3b75..8f960bc0e 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-test" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] description = "Internal testing crate for wasm-bindgen" license = "MIT/Apache-2.0" @@ -9,11 +9,11 @@ repository = "https://github.com/rustwasm/wasm-bindgen" [dependencies] console_error_panic_hook = '0.1' futures = "0.1" -js-sys = { path = '../js-sys', version = '0.2.4' } +js-sys = { path = '../js-sys', version = '0.2.5' } scoped-tls = "0.1" -wasm-bindgen = { path = '../..', version = '0.2.19' } -wasm-bindgen-futures = { path = '../futures', version = '0.2.19' } -wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.19' } +wasm-bindgen = { path = '../..', version = '0.2.20' } +wasm-bindgen-futures = { path = '../futures', version = '0.2.20' } +wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.20' } [lib] test = false diff --git a/crates/wasm-interpreter/Cargo.toml b/crates/wasm-interpreter/Cargo.toml index 7857b47a5..2800d8390 100644 --- a/crates/wasm-interpreter/Cargo.toml +++ b/crates/wasm-interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-wasm-interpreter" -version = "0.2.19" +version = "0.2.20" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/wasm-interpreter" diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml index 4660db48a..63ec1689a 100644 --- a/crates/web-sys/Cargo.toml +++ b/crates/web-sys/Cargo.toml @@ -18,13 +18,13 @@ wasm-bindgen-webidl = { path = "../webidl", version = "=0.2.17" } sourcefile = "0.1" [dependencies] -wasm-bindgen = { path = "../..", version = "0.2.19" } -js-sys = { path = '../js-sys', version = '0.2.4' } +wasm-bindgen = { path = "../..", version = "0.2.20" } +js-sys = { path = '../js-sys', version = '0.2.5' } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] futures = "0.1" -wasm-bindgen-test = { path = '../test', version = '0.2.19' } -wasm-bindgen-futures = { path = '../futures', version = '0.2.19' } +wasm-bindgen-test = { path = '../test', version = '0.2.20' } +wasm-bindgen-futures = { path = '../futures', version = '0.2.20' } # This list is generated by passing `__WASM_BINDGEN_DUMP_FEATURES=foo` when # compiling this crate which dumps the total list of features to a file called diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index e530e8182..68aa64266 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -19,5 +19,5 @@ log = "0.4.1" proc-macro2 = "0.4.8" quote = '0.6' syn = { version = '0.14', features = ['full'] } -wasm-bindgen-backend = { version = "=0.2.19", path = "../backend" } +wasm-bindgen-backend = { version = "=0.2.20", path = "../backend" } weedle = "0.7" From 2d4f36c9da85ec27f1a7127b29c86440ffafb7a5 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:54:49 -0700 Subject: [PATCH 11/19] js-sys: Add bindings to `WebAssembly.Table.prototype.get` Part of #275 --- crates/js-sys/src/lib.rs | 7 +++++++ crates/js-sys/tests/wasm/WebAssembly.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index 07ffd5a05..d2d5e83d5 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3061,6 +3061,13 @@ pub mod WebAssembly { /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/length) #[wasm_bindgen(method, getter, js_namespace = WebAssembly)] pub fn length(this: &Table) -> u32; + + /// The `get()` prototype method of the `WebAssembly.Table()` object + /// retrieves a function reference stored at a given index. + /// + /// [MDN documentation]( + #[wasm_bindgen(method, catch, js_namespace = WebAssembly)] + pub fn get(this: &Table, index: u32) -> Result; } // WebAssembly.Memory diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index e0b10931f..d2e42a697 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -127,6 +127,9 @@ fn table_error() { fn table() { let table = WebAssembly::Table::new(&get_table_object().into()).unwrap(); assert_eq!(table.length(), 1); + + assert!(table.get(0).is_ok()); + assert!(table.get(999).is_err()); } #[wasm_bindgen_test] From 8dbb0fc5f24aee42ab865587ce7bb44bfdab799a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 14:58:46 -0700 Subject: [PATCH 12/19] js-sys: Expose bindings to `WebAssembly.Table.prototype.grow` Par of #275 --- crates/js-sys/src/lib.rs | 10 +++++++++- crates/js-sys/tests/wasm/WebAssembly.rs | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index d2d5e83d5..d6c746f66 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3065,9 +3065,17 @@ pub mod WebAssembly { /// The `get()` prototype method of the `WebAssembly.Table()` object /// retrieves a function reference stored at a given index. /// - /// [MDN documentation]( + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/get) #[wasm_bindgen(method, catch, js_namespace = WebAssembly)] pub fn get(this: &Table, index: u32) -> Result; + + /// The `grow()` prototype method of the `WebAssembly.Table` object + /// increases the size of the `Table` instance by a specified number of + /// elements. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow) + #[wasm_bindgen(method, catch, js_namespace = WebAssembly)] + pub fn grow(this: &Table, additional_capacity: u32) -> Result; } // WebAssembly.Memory diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index d2e42a697..230a4bfcc 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -130,6 +130,9 @@ fn table() { assert!(table.get(0).is_ok()); assert!(table.get(999).is_err()); + + table.grow(1).unwrap(); + assert_eq!(table.length(), 2); } #[wasm_bindgen_test] From 8f9514d21639e5e1981757056cf16d704127defa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 4 Sep 2018 11:32:09 -0700 Subject: [PATCH 13/19] Update syn to 0.15 New and faster parsers! --- crates/backend/Cargo.toml | 2 +- crates/backend/src/defined.rs | 1 + crates/backend/src/error.rs | 14 ++ crates/macro-support/Cargo.toml | 2 +- crates/macro-support/src/lib.rs | 11 +- crates/macro-support/src/parser.rs | 212 ++++++++---------- .../ui-tests/attribute-fails-to-parse.stderr | 2 +- crates/macro/ui-tests/invalid-attr.stderr | 4 +- crates/typescript/Cargo.toml | 2 +- crates/webidl/Cargo.toml | 2 +- 10 files changed, 123 insertions(+), 129 deletions(-) diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml index 051a27199..72361b5f0 100644 --- a/crates/backend/Cargo.toml +++ b/crates/backend/Cargo.toml @@ -20,5 +20,5 @@ log = "0.4" proc-macro2 = "0.4.8" quote = '0.6' serde_json = "1.0" -syn = { version = '0.14', features = ['full', 'visit'] } +syn = { version = '0.15', features = ['full', 'visit'] } wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } diff --git a/crates/backend/src/defined.rs b/crates/backend/src/defined.rs index bf0e22afd..20fd8635d 100644 --- a/crates/backend/src/defined.rs +++ b/crates/backend/src/defined.rs @@ -233,6 +233,7 @@ impl ImportedTypes for syn::GenericArgument { syn::GenericArgument::Type(ty) => ty.imported_types(f), syn::GenericArgument::Binding(_) => {}, // TODO syn::GenericArgument::Const(_) => {}, // TODO + syn::GenericArgument::Constraint(_) => {}, // TODO } } } diff --git a/crates/backend/src/error.rs b/crates/backend/src/error.rs index aecce6f06..733c4c782 100644 --- a/crates/backend/src/error.rs +++ b/crates/backend/src/error.rs @@ -1,5 +1,6 @@ use proc_macro2::*; use quote::{ToTokens, TokenStreamExt}; +use syn::parse::Error; #[macro_export] macro_rules! err_span { @@ -26,6 +27,7 @@ enum Repr { text: String, span: Option<(Span, Span)>, }, + SynError(Error), Multi { diagnostics: Vec, } @@ -62,11 +64,20 @@ impl Diagnostic { pub fn panic(&self) -> ! { match &self.inner { Repr::Single { text, .. } => panic!("{}", text), + Repr::SynError(error) => panic!("{}", error), Repr::Multi { diagnostics } => diagnostics[0].panic(), } } } +impl From for Diagnostic { + fn from(err: Error) -> Diagnostic { + Diagnostic { + inner: Repr::SynError(err), + } + } +} + fn extract_spans(node: &ToTokens) -> Option<(Span, Span)> { let mut t = TokenStream::new(); node.to_tokens(&mut t); @@ -95,6 +106,9 @@ impl ToTokens for Diagnostic { diagnostic.to_tokens(dst); } } + Repr::SynError(err) => { + err.to_compile_error().to_tokens(dst); + } } } } diff --git a/crates/macro-support/Cargo.toml b/crates/macro-support/Cargo.toml index c665573b1..0073487d2 100644 --- a/crates/macro-support/Cargo.toml +++ b/crates/macro-support/Cargo.toml @@ -15,7 +15,7 @@ spans = ["wasm-bindgen-backend/spans"] extra-traits = ["syn/extra-traits"] [dependencies] -syn = { version = '0.14', features = ['full'] } +syn = { version = '0.15.0', features = ['full'] } quote = '0.6' proc-macro2 = "0.4.9" wasm-bindgen-backend = { path = "../backend", version = "=0.2.20" } diff --git a/crates/macro-support/src/lib.rs b/crates/macro-support/src/lib.rs index cb7773374..361d60461 100644 --- a/crates/macro-support/src/lib.rs +++ b/crates/macro-support/src/lib.rs @@ -20,8 +20,8 @@ mod parser; /// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings pub fn expand(attr: TokenStream, input: TokenStream) -> Result { - let item = syn_parse::(input, "rust item")?; - let opts = syn_parse(attr, "#[wasm_bindgen] attribute options")?; + let item = syn::parse2::(input)?; + let opts = syn::parse2(attr)?; let mut tokens = proc_macro2::TokenStream::new(); let mut program = backend::ast::Program::default(); @@ -29,10 +29,3 @@ pub fn expand(attr: TokenStream, input: TokenStream) -> Result(tokens: TokenStream, name: &str) -> Result { - syn::parse2(tokens.clone()) - .map_err(|err| { - Diagnostic::span_error(&tokens, format!("error parsing {}: {}", name, err)) - }) -} diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 91f7f7ad0..dbc63d32b 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -5,6 +5,7 @@ use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; use quote::ToTokens; use shared; use syn; +use syn::parse::{Parse, ParseStream, Result as SynResult}; /// Parsed attributes from a `#[wasm_bindgen(..)]`. #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] @@ -39,7 +40,7 @@ impl BindgenAttrs { if group.delimiter() != Delimiter::Parenthesis { bail_span!(attr, "malformed #[wasm_bindgen] attribute"); } - super::syn_parse(group.stream(), "#[wasm_bindgen] attribute options") + Ok(syn::parse2(group.stream())?) } /// Get the first module attribute @@ -193,19 +194,15 @@ impl BindgenAttrs { } } -impl syn::synom::Synom for BindgenAttrs { - named!(parse -> Self, alt!( - do_parse!( - opts: call!( - syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated - ) >> - (BindgenAttrs { - attrs: opts.into_iter().collect(), - }) - ) => { |s| s } - | - epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } } - )); +impl Parse for BindgenAttrs { + fn parse(input: ParseStream) -> SynResult { + if input.is_empty() { + return Ok(BindgenAttrs { attrs: Vec::new() }) + } + + let opts = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated(input)?; + Ok(BindgenAttrs { attrs: opts.into_iter().collect() }) + } } /// The possible attributes in the `#[wasm_bindgen]`. @@ -230,109 +227,94 @@ pub enum BindgenAttr { Variadic, } -impl syn::synom::Synom for BindgenAttr { - named!(parse -> Self, alt!( - call!(term, "catch") => { |_| BindgenAttr::Catch } - | - call!(term, "constructor") => { |_| BindgenAttr::Constructor } - | - call!(term, "method") => { |_| BindgenAttr::Method } - | - do_parse!( - call!(term, "static_method_of") >> - punct!(=) >> - cls: call!(term2ident) >> - (cls) - )=> { BindgenAttr::StaticMethodOf } - | - do_parse!( - call!(term, "getter") >> - val: option!(do_parse!( - punct!(=) >> - s: call!(term2ident) >> - (s) - )) >> - (val) - )=> { BindgenAttr::Getter } - | - do_parse!( - call!(term, "setter") >> - val: option!(do_parse!( - punct!(=) >> - s: call!(term2ident) >> - (s) - )) >> - (val) - )=> { BindgenAttr::Setter } - | - call!(term, "indexing_getter") => { |_| BindgenAttr::IndexingGetter } - | - call!(term, "indexing_setter") => { |_| BindgenAttr::IndexingSetter } - | - call!(term, "indexing_deleter") => { |_| BindgenAttr::IndexingDeleter } - | - call!(term, "structural") => { |_| BindgenAttr::Structural } - | - call!(term, "readonly") => { |_| BindgenAttr::Readonly } - | - do_parse!( - call!(term, "js_namespace") >> - punct!(=) >> - ns: call!(term2ident) >> - (ns) - )=> { BindgenAttr::JsNamespace } - | - do_parse!( - call!(term, "module") >> - punct!(=) >> - s: syn!(syn::LitStr) >> - (s.value()) - )=> { BindgenAttr::Module } - | - do_parse!( - call!(term, "js_name") >> - punct!(=) >> - name: alt!( - syn!(syn::LitStr) => { |s| s.value() } - | - call!(term2ident) => { |s| s.to_string() } - ) >> - (name) - )=> { BindgenAttr::JsName } - | - do_parse!( - call!(term, "js_class") >> - punct!(=) >> - s: syn!(syn::LitStr) >> - (s.value()) - )=> { BindgenAttr::JsClass } - | - do_parse!( - call!(term, "extends") >> - punct!(=) >> - ns: call!(term2ident) >> - (ns) - )=> { BindgenAttr::Extends } - | - call!(term, "variadic") => { |_| BindgenAttr::Variadic } - )); -} - -/// Consumes a `Ident` with the given name -fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> { - if let Some((ident, next)) = cursor.ident() { - if ident == name { - return Ok(((), next)); +impl Parse for BindgenAttr { + fn parse(input: ParseStream) -> SynResult { + let original = input.fork(); + let attr: Ident = input.parse()?; + if attr == "catch" { + return Ok(BindgenAttr::Catch) } + if attr == "constructor" { + return Ok(BindgenAttr::Constructor) + } + if attr == "method" { + return Ok(BindgenAttr::Method) + } + if attr == "indexing_getter" { + return Ok(BindgenAttr::IndexingGetter) + } + if attr == "indexing_setter" { + return Ok(BindgenAttr::IndexingSetter) + } + if attr == "indexing_deleter" { + return Ok(BindgenAttr::IndexingDeleter) + } + if attr == "structural" { + return Ok(BindgenAttr::Structural) + } + if attr == "readonly" { + return Ok(BindgenAttr::Readonly) + } + if attr == "variadic" { + return Ok(BindgenAttr::Variadic) + } + if attr == "static_method_of" { + input.parse::()?; + return Ok(BindgenAttr::StaticMethodOf(input.parse::()?.0)) + } + if attr == "getter" { + if input.parse::().is_ok() { + return Ok(BindgenAttr::Getter(Some(input.parse::()?.0))) + } else { + return Ok(BindgenAttr::Getter(None)) + } + } + if attr == "setter" { + if input.parse::().is_ok() { + return Ok(BindgenAttr::Setter(Some(input.parse::()?.0))) + } else { + return Ok(BindgenAttr::Setter(None)) + } + } + if attr == "js_namespace" { + input.parse::()?; + return Ok(BindgenAttr::JsNamespace(input.parse::()?.0)) + } + if attr == "extends" { + input.parse::()?; + return Ok(BindgenAttr::Extends(input.parse::()?.0)) + } + if attr == "module" { + input.parse::()?; + return Ok(BindgenAttr::Module(input.parse::()?.value())) + } + if attr == "js_class" { + input.parse::()?; + return Ok(BindgenAttr::JsClass(input.parse::()?.value())) + } + if attr == "js_name" { + input.parse::()?; + let val = match input.parse::() { + Ok(str) => str.value(), + Err(_) => input.parse::()?.0.to_string(), + }; + return Ok(BindgenAttr::JsName(val)) + } + + Err(original.error("unknown attribute")) } - syn::parse_error() } -/// Consumes a `Ident` and returns it. -fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Ident> { - match cursor.ident() { - Some(pair) => Ok(pair), - None => syn::parse_error(), +struct AnyIdent(Ident); + +impl Parse for AnyIdent { + fn parse(input: ParseStream) -> SynResult { + input.step(|cursor| { + match cursor.ident() { + Some((ident, remaining)) => Ok((AnyIdent(ident), remaining)), + None => Err(cursor.error("expected an identifier")), + } + }) } } @@ -841,6 +823,10 @@ impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) { &*item, "type definitions in impls aren't supported with #[wasm_bindgen]" ), + syn::ImplItem::Existential(_) => bail_span!( + &*item, + "existentials in impls aren't supported with #[wasm_bindgen]" + ), syn::ImplItem::Macro(_) => { bail_span!(&*item, "macros in impls aren't supported"); } diff --git a/crates/macro/ui-tests/attribute-fails-to-parse.stderr b/crates/macro/ui-tests/attribute-fails-to-parse.stderr index eb7960ebd..ca296af3f 100644 --- a/crates/macro/ui-tests/attribute-fails-to-parse.stderr +++ b/crates/macro/ui-tests/attribute-fails-to-parse.stderr @@ -1,4 +1,4 @@ -error: error parsing #[wasm_bindgen] attribute options: failed to parse anything +error: unknown attribute --> $DIR/attribute-fails-to-parse.rs:5:16 | 5 | #[wasm_bindgen(nonsense)] diff --git a/crates/macro/ui-tests/invalid-attr.stderr b/crates/macro/ui-tests/invalid-attr.stderr index ee918ec53..9cc44d8df 100644 --- a/crates/macro/ui-tests/invalid-attr.stderr +++ b/crates/macro/ui-tests/invalid-attr.stderr @@ -1,10 +1,10 @@ -error: error parsing #[wasm_bindgen] attribute options: failed to parse anything +error: unknown attribute --> $DIR/invalid-attr.rs:5:16 | 5 | #[wasm_bindgen(x)] | ^ -error: error parsing #[wasm_bindgen] attribute options: failed to parse anything +error: unknown attribute --> $DIR/invalid-attr.rs:10:20 | 10 | #[wasm_bindgen(y)] diff --git a/crates/typescript/Cargo.toml b/crates/typescript/Cargo.toml index 2850e1747..9bf6e3f6b 100644 --- a/crates/typescript/Cargo.toml +++ b/crates/typescript/Cargo.toml @@ -10,6 +10,6 @@ serde_json = "1.0" proc-macro2 = "0.4.8" quote = "0.6" -syn = { version = "0.14", default-features = false } +syn = { version = "0.15", default-features = false } wasm-bindgen = { path = "../..", default-features = false } wasm-bindgen-backend = { path = "../backend", default-features = false } diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index 68aa64266..1a6451f72 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -18,6 +18,6 @@ heck = "0.3" log = "0.4.1" proc-macro2 = "0.4.8" quote = '0.6' -syn = { version = '0.14', features = ['full'] } +syn = { version = '0.15', features = ['full'] } wasm-bindgen-backend = { version = "=0.2.20", path = "../backend" } weedle = "0.7" From bfff31fcb90e1c105f3fd6efe8e2b3e50cc8cbec Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Thu, 6 Sep 2018 15:02:01 -0700 Subject: [PATCH 14/19] js-sys: Expose bindings to `WebAssembly.Table.prototype.set` Part of #275 --- crates/js-sys/src/lib.rs | 7 +++++++ crates/js-sys/tests/wasm/WebAssembly.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/crates/js-sys/src/lib.rs b/crates/js-sys/src/lib.rs index d6c746f66..6f9bc275b 100644 --- a/crates/js-sys/src/lib.rs +++ b/crates/js-sys/src/lib.rs @@ -3076,6 +3076,13 @@ pub mod WebAssembly { /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/grow) #[wasm_bindgen(method, catch, js_namespace = WebAssembly)] pub fn grow(this: &Table, additional_capacity: u32) -> Result; + + /// The `set()` prototype method of the `WebAssembly.Table` object mutates a + /// reference stored at a given index to a different value. + /// + /// [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Table/set) + #[wasm_bindgen(method, catch, js_namespace = WebAssembly)] + pub fn set(this: &Table, index: u32, function: &Function) -> Result<(), JsValue>; } // WebAssembly.Memory diff --git a/crates/js-sys/tests/wasm/WebAssembly.rs b/crates/js-sys/tests/wasm/WebAssembly.rs index 230a4bfcc..65385ec59 100644 --- a/crates/js-sys/tests/wasm/WebAssembly.rs +++ b/crates/js-sys/tests/wasm/WebAssembly.rs @@ -133,6 +133,9 @@ fn table() { table.grow(1).unwrap(); assert_eq!(table.length(), 2); + + let f = table.get(0).unwrap(); + table.set(1, &f).unwrap(); } #[wasm_bindgen_test] From 6e319509e591778eac96bc8c7745fa9d99a88ce9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 15:07:04 -0700 Subject: [PATCH 15/19] Don't test all web-sys features on Windows Unfortunately this blows the command line limit and will fail CI --- .appveyor.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index f441ec2b8..0f1488a04 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,8 +25,11 @@ test_script: - where chromedriver - set CHROMEDRIVER=C:\Tools\WebDriver\chromedriver.exe - cargo test -p js-sys --target wasm32-unknown-unknown - - cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features - cargo test -p webidl-tests --target wasm32-unknown-unknown + # Try just a few features for `web-sys`, unfortunately the whole crate blows + # system command line limits meaning we can't even spawn rustc to enable all + # the features. + - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features 'Node Window Document' branches: only: From d279fe81e7415e3fe90599d9ef99cd2e831c36ba Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 16:22:19 -0700 Subject: [PATCH 16/19] Try to fix AppVeyor --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 0f1488a04..dcee442cd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -29,7 +29,7 @@ test_script: # Try just a few features for `web-sys`, unfortunately the whole crate blows # system command line limits meaning we can't even spawn rustc to enable all # the features. - - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features 'Node Window Document' + - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document" branches: only: From 457efc0f31f336cffa96eca70994956af01c03ab Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 16:18:24 -0700 Subject: [PATCH 17/19] Implement support for WebIDL `Callback` types This commit adds support for the WebIDL `Callback` type by translating all callbacks to the `js_sys::Function` type. This will enable passing raw JS values into callbacks as well as Rust valus using the `Closure` type. This commit doesn't currently implement "callback interfaces" in WebIDL, that's left for a follow-up commit. --- crates/webidl-tests/array_buffer.rs | 2 +- crates/webidl-tests/simple.js | 10 +++ crates/webidl-tests/simple.rs | 25 ++++++++ crates/webidl-tests/simple.webidl | 12 ++++ crates/webidl/src/first_pass.rs | 13 ++-- crates/webidl/src/idl_type.rs | 51 +++++++-------- crates/webidl/src/lib.rs | 2 +- guide/src/web-sys.md | 97 ++++++++++++++++++++++++++++- src/closure.rs | 2 +- 9 files changed, 176 insertions(+), 38 deletions(-) diff --git a/crates/webidl-tests/array_buffer.rs b/crates/webidl-tests/array_buffer.rs index 5aed55f9b..0886ff917 100644 --- a/crates/webidl-tests/array_buffer.rs +++ b/crates/webidl-tests/array_buffer.rs @@ -7,5 +7,5 @@ fn take_and_return_a_bunch_of_slices() { let f = ArrayBufferTest::new().unwrap(); let x = f.get_buffer(); f.set_buffer(None); - f.set_buffer(Some(x)); + f.set_buffer(Some(&x)); } diff --git a/crates/webidl-tests/simple.js b/crates/webidl-tests/simple.js index d68590323..cbad240be 100644 --- a/crates/webidl-tests/simple.js +++ b/crates/webidl-tests/simple.js @@ -146,3 +146,13 @@ global.MixinFoo = class MixinFoo { global.Overloads = class { foo() {} }; + +global.InvokeCallback = class { + invoke(f) { f(); } + callAdd(f) { + return f(1, 2); + } + callRepeat(f) { + return f('ab', 4); + } +}; diff --git a/crates/webidl-tests/simple.rs b/crates/webidl-tests/simple.rs index e5e4d2301..290a0f7a8 100644 --- a/crates/webidl-tests/simple.rs +++ b/crates/webidl-tests/simple.rs @@ -1,4 +1,6 @@ use wasm_bindgen_test::*; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; include!(concat!(env!("OUT_DIR"), "/simple.rs")); @@ -124,3 +126,26 @@ fn overload_naming() { o.foo_with_arg_and_f32("x", 2.0); o.foo_with_arg_and_i16("x", 5); } + +#[wasm_bindgen_test] +fn callback() { + let o = InvokeCallback::new().unwrap(); + { + static mut HIT: bool = false; + let cb = Closure::wrap(Box::new(move || { + unsafe { HIT = true; } + }) as Box); + o.invoke(cb.as_ref().unchecked_ref()); + assert!(unsafe { HIT }); + } + + let cb = Closure::wrap(Box::new(move |a, b| { + a + b + }) as Box u32>); + assert_eq!(o.call_add(cb.as_ref().unchecked_ref()), 3); + + let cb = Closure::wrap(Box::new(move |a: String, b| { + a.repeat(b) + }) as Box String>); + assert_eq!(o.call_repeat(cb.as_ref().unchecked_ref()), "abababab"); +} diff --git a/crates/webidl-tests/simple.webidl b/crates/webidl-tests/simple.webidl index 63d7b9d24..e1e0a800a 100644 --- a/crates/webidl-tests/simple.webidl +++ b/crates/webidl-tests/simple.webidl @@ -95,3 +95,15 @@ interface Overloads { void foo(DOMString arg, optional long a); void foo(DOMString arg, (float or short) b); }; + +callback MyCallback = any(); +callback AddCallback = long(long a, long b); +callback RepeatCallback = DOMString(DOMString a, long cnt); +callback GetAnswer = long(); + +[Constructor()] +interface InvokeCallback { + void invoke(MyCallback callback); + long callAdd(AddCallback callback); + DOMString callRepeat(RepeatCallback callback); +}; diff --git a/crates/webidl/src/first_pass.rs b/crates/webidl/src/first_pass.rs index 3762ad141..1b19d8a16 100644 --- a/crates/webidl/src/first_pass.rs +++ b/crates/webidl/src/first_pass.rs @@ -34,6 +34,7 @@ pub(crate) struct FirstPassRecord<'src> { pub(crate) namespaces: BTreeMap<&'src str, NamespaceData<'src>>, pub(crate) includes: BTreeMap<&'src str, BTreeSet<&'src str>>, pub(crate) dictionaries: BTreeMap<&'src str, DictionaryData<'src>>, + pub(crate) callbacks: BTreeSet<&'src str>, } /// We need to collect interface data during the first pass, to be used later. @@ -135,12 +136,9 @@ impl<'src> FirstPass<'src, ()> for weedle::Definition<'src> { Namespace(namespace) => namespace.first_pass(record, ()), PartialNamespace(namespace) => namespace.first_pass(record, ()), Typedef(typedef) => typedef.first_pass(record, ()), + Callback(callback) => callback.first_pass(record, ()), Implements(_) => Ok(()), - Callback(..) => { - warn!("Unsupported WebIDL Callback definition: {:?}", self); - Ok(()) - } CallbackInterface(..) => { warn!("Unsupported WebIDL CallbackInterface definition: {:?}", self); Ok(()) @@ -685,6 +683,13 @@ impl<'src> FirstPass<'src, &'src str> for weedle::namespace::OperationNamespaceM } } +impl<'src> FirstPass<'src, ()> for weedle::CallbackDefinition<'src> { + fn first_pass(&'src self, record: &mut FirstPassRecord<'src>, _: ()) -> Result<()> { + record.callbacks.insert(self.identifier.0); + Ok(()) + } +} + impl<'a> FirstPassRecord<'a> { pub fn all_superclasses<'me>(&'me self, interface: &str) -> impl Iterator + 'me diff --git a/crates/webidl/src/idl_type.rs b/crates/webidl/src/idl_type.rs index 513b5abe7..7d0fce655 100644 --- a/crates/webidl/src/idl_type.rs +++ b/crates/webidl/src/idl_type.rs @@ -28,6 +28,7 @@ pub(crate) enum IdlType<'a> { Object, Symbol, Error, + Callback, ArrayBuffer, DataView, @@ -293,6 +294,8 @@ impl<'a> ToIdlType<'a> for Identifier<'a> { Some(IdlType::Dictionary(self.0)) } else if record.enums.contains_key(self.0) { Some(IdlType::Enum(self.0)) + } else if record.callbacks.contains(self.0) { + Some(IdlType::Callback) } else { warn!("Unrecognized type: {}", self.0); None @@ -364,6 +367,7 @@ impl<'a> IdlType<'a> { IdlType::Object => dst.push_str("object"), IdlType::Symbol => dst.push_str("symbol"), IdlType::Error => dst.push_str("error"), + IdlType::Callback => dst.push_str("callback"), IdlType::ArrayBuffer => dst.push_str("array_buffer"), IdlType::DataView => dst.push_str("data_view"), @@ -426,6 +430,17 @@ impl<'a> IdlType<'a> { /// Converts to syn type if possible. pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option { + let anyref = |ty| { + Some(match pos { + TypePosition::Argument => shared_ref(ty, false), + TypePosition::Return => ty, + }) + }; + let js_sys = |name: &str| { + let path = vec![rust_ident("js_sys"), rust_ident(name)]; + let ty = leading_colon_path_ty(path); + anyref(ty) + }; match self { IdlType::Boolean => Some(ident_ty(raw_ident("bool"))), IdlType::Byte => Some(ident_ty(raw_ident("i8"))), @@ -446,21 +461,11 @@ impl<'a> IdlType<'a> { TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")), false)), TypePosition::Return => Some(ident_ty(raw_ident("String"))), }, - IdlType::Object => { - let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - let ty = leading_colon_path_ty(path); - Some(match pos { - TypePosition::Argument => shared_ref(ty, false), - TypePosition::Return => ty, - }) - }, + IdlType::Object => js_sys("Object"), IdlType::Symbol => None, IdlType::Error => None, - IdlType::ArrayBuffer => { - let path = vec![rust_ident("js_sys"), rust_ident("ArrayBuffer")]; - Some(leading_colon_path_ty(path)) - }, + IdlType::ArrayBuffer => js_sys("ArrayBuffer"), IdlType::DataView => None, IdlType::Int8Array => Some(array("i8", pos, false)), IdlType::Uint8Array => Some(array("u8", pos, false)), @@ -473,14 +478,7 @@ impl<'a> IdlType<'a> { IdlType::Float32Array => Some(array("f32", pos, false)), IdlType::Float64Array => Some(array("f64", pos, false)), - IdlType::ArrayBufferView | IdlType::BufferSource => { - let path = vec![rust_ident("js_sys"), rust_ident("Object")]; - let ty = leading_colon_path_ty(path); - Some(match pos { - TypePosition::Argument => shared_ref(ty, false), - TypePosition::Return => ty, - }) - }, + IdlType::ArrayBufferView | IdlType::BufferSource => js_sys("Object"), IdlType::Interface(name) | IdlType::Dictionary(name) => { let ty = ident_ty(rust_ident(camel_case_ident(name).as_str())); @@ -495,15 +493,7 @@ impl<'a> IdlType<'a> { IdlType::Nullable(idl_type) => Some(option_ty(idl_type.to_syn_type(pos)?)), IdlType::FrozenArray(_idl_type) => None, IdlType::Sequence(_idl_type) => None, - IdlType::Promise(_idl_type) => { - let path = vec![rust_ident("js_sys"), rust_ident("Promise")]; - let ty = leading_colon_path_ty(path); - if pos == TypePosition::Argument { - Some(shared_ref(ty, false)) - } else { - Some(ty) - } - } + IdlType::Promise(_idl_type) => js_sys("Promise"), IdlType::Record(_idl_type_from, _idl_type_to) => None, IdlType::Union(idl_types) => { // Handles union types in all places except operation argument types. @@ -529,9 +519,10 @@ impl<'a> IdlType<'a> { IdlType::Any => { let path = vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]; - Some(leading_colon_path_ty(path)) + anyref(leading_colon_path_ty(path)) }, IdlType::Void => None, + IdlType::Callback => js_sys("Function"), } } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index c82536444..5f971993e 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -138,7 +138,7 @@ fn builtin_idents() -> BTreeSet { vec![ "str", "char", "bool", "JsValue", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize", "f32", "f64", "Result", "String", "Vec", "Option", - "ArrayBuffer", "Object", "Promise", + "ArrayBuffer", "Object", "Promise", "Function", ].into_iter() .map(|id| proc_macro2::Ident::new(id, proc_macro2::Span::call_site())), ) diff --git a/guide/src/web-sys.md b/guide/src/web-sys.md index 4f24534d9..574a18b46 100644 --- a/guide/src/web-sys.md +++ b/guide/src/web-sys.md @@ -5,4 +5,99 @@ source lives at `wasm-bindgen/crates/web-sys`. The `web-sys` crate is **entirely** mechanically generated inside `build.rs` using `wasm-bindgen`'s WebIDL frontend and the WebIDL interface definitions for -Web APIs. +Web APIs. This means that `web-sys` isn't always the most ergonomic crate to +use, but it's intended to provide verified and correct bindings to the web +platform, and then better interfaces can be iterated on crates.io! + +### Using `web-sys` + +Let's say you want to use an API defined on the web. Chances are this API is +defined in `web-sys`, so let's go through some steps necessary to use it! + +First up, search the [api documentation][api] for your API. For example if +we're looking for JS's [`fetch`][jsfetch] API we'd start out by [searching for +`fetch`][search-fetch]. The first thing you'll probably notice is that there's +no function called `fetch`! Fear not, though, as the API exists in multiple +forms: + +* [`Window::fetch_with_str`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str) +* [`Window::fetch_with_request`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request) +* [`Window::fetch_with_str_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/str_and_inituct.Window.html#method.fetch_with_str_and_init) +* [`Window::fetch_with_request_and_init`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_request_and_init) + +What's happening here is that the [`fetch` function][fetchfn] actually supports +multiple signatures of arguments, and we've taken the WebIDL definition for this +function and expanded it to unique signatures in Rust (as Rust doesn't have +function name overloading). + +When an API is selected it should have documentation pointing at MDN indicating +what web API its binding. This is often a great way to double check arguments +and such as well, MDN is a great resource! You'll also notice in the +documentation that the API may require some `web-sys` Cargo features to be +activated. For example [`fetch_with_str`] requires the `Window` feature to be +activated. In general an API needs features corresponding to all the types +you'll find in the signature to be activated. + +To load up this API let's depend on `web-sys`: + +```toml +[dependencies] +wasm-bindgen = "0.2" +web-sys = { version = "0.1", features = ['Window'] } + +# Or optionally, +# [target.wasm32-unknown-unknown.dependencies] +# ... +``` + +> **Note**: Currently `web-sys` is not available on crates.io so you'll also +> need to do this in your manifest: +> +> ```toml +> [patch.crates-io] +> web-sys = { git = 'https://github.com/rustwasm/wasm-bindgen' } +> wasm-bindgen = { git = 'https://github.com/rustwasm/wasm-bindgen' } +> ``` + +And next up we can use the API like so: + +```rust +extern crate web_sys; +extern crate wasm_bindgen; + +use wasm_bindgen::prelude::*; +use web_sys::Window; + +#[wasm_bindgen] +pub fn run() { + let promise = Window::fetch_with_str("http://example.com/"); + // ... +} +``` + +and you should be good to go! + +### Type translations in `web-sys` + +Most of the types specified in WebIDL have relatively straightforward +translations into `web-sys`, but it's worth calling out a few in particular: + +* `BufferSource` and `ArrayBufferView` - these two types show up in a number of + APIs that generally deal with a buffer of bytes. We bind them in `web-sys` + with two different types, `Object` and `&mut [u8]`. Using `Object` allows + passing in arbitrary JS values which represent a view of bytes (like any typed + array object), and `&mut [u8]` allows using a raw slice in Rust. Unfortunately + we must pessimistically assume that JS will modify all slices as we don't + currently have information of whether they're modified or not. + +* Callbacks are all represented as `js_sys::Function`. This means that all + callbacks going through `web-sys` are a raw JS value. You can work with this + by either juggling actual `js_sys::Function` instances or you can create a + `Closure`, extract the underlying `JsValue` with `as_ref`, and + then use `JsCast::unchecked_ref` to convert it to a `js_sys::Function`. + +[api]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/ +[jsfetch]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API +[search-fetch]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/?search=fetch +[fetchfn]: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch +[`fetch_with_str`]: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.fetch_with_str diff --git a/src/closure.rs b/src/closure.rs index 8f3bc76e0..910e6ac62 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -191,7 +191,7 @@ impl Closure } } -impl AsRef for Closure { +impl AsRef for Closure { fn as_ref(&self) -> &JsValue { &self.js } From e632dd3fda2d555181248ff06e7027fb8d982328 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 22:08:04 -0700 Subject: [PATCH 18/19] Parse names before we take the module Otherwise when we try to parse the names there's no module with contents! --- crates/cli-support/src/js/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 43e875582..29c3be868 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1716,8 +1716,8 @@ impl<'a> Context<'a> { } fn gc(&mut self) -> Result<(), Error> { - let module = mem::replace(self.module, Module::default()); self.parse_wasm_names(); + let module = mem::replace(self.module, Module::default()); let result = wasm_gc::Config::new() .demangle(self.config.demangle) .keep_debug(self.config.keep_debug || self.config.debug) From f18b10ca52d137034e57399907f747bef0494d62 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 6 Sep 2018 22:09:20 -0700 Subject: [PATCH 19/19] Bump to 0.2.21 --- CHANGELOG.md | 15 +++++++++++++++ Cargo.toml | 8 ++++---- crates/backend/Cargo.toml | 4 ++-- crates/cli-support/Cargo.toml | 6 +++--- crates/cli/Cargo.toml | 6 +++--- crates/futures/Cargo.toml | 8 ++++---- crates/js-sys/Cargo.toml | 8 ++++---- crates/macro-support/Cargo.toml | 6 +++--- crates/macro/Cargo.toml | 4 ++-- crates/shared/Cargo.toml | 2 +- crates/test-macro/Cargo.toml | 2 +- crates/test/Cargo.toml | 10 +++++----- crates/wasm-interpreter/Cargo.toml | 2 +- crates/web-sys/Cargo.toml | 8 ++++---- crates/webidl/Cargo.toml | 2 +- 15 files changed, 53 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0faaa0cf3..20d505f53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,21 @@ Released YYYY-MM-DD. -------------------------------------------------------------------------------- +## 0.2.21 + +Released 2018-09-07 + +### Added + +* Added many more bindings for `WebAssembly` in the `js-sys` crate. + +### Fixed + +* The "names" section of the wasm binary is now correctly preserved by + wasm-bindgen. + +-------------------------------------------------------------------------------- + ## 0.2.20 Released 2018-09-06 diff --git a/Cargo.toml b/Cargo.toml index 35840d716..059f471e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" # Because only a single `wasm_bindgen` version can be used in a dependency @@ -35,13 +35,13 @@ nightly = [] xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_generated_code"] [dependencies] -wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.20" } +wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.21" } serde = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -js-sys = { path = 'crates/js-sys', version = '0.2.5' } -wasm-bindgen-test = { path = 'crates/test', version = '=0.2.20' } +js-sys = { path = 'crates/js-sys', version = '0.2.6' } +wasm-bindgen-test = { path = 'crates/test', version = '=0.2.21' } serde_derive = "1.0" wasm-bindgen-test-crate-a = { path = 'tests/crates/a', version = '0.1' } wasm-bindgen-test-crate-b = { path = 'tests/crates/b', version = '0.1' } diff --git a/crates/backend/Cargo.toml b/crates/backend/Cargo.toml index 72361b5f0..d286e3d08 100644 --- a/crates/backend/Cargo.toml +++ b/crates/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-backend" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend" @@ -21,4 +21,4 @@ proc-macro2 = "0.4.8" quote = '0.6' serde_json = "1.0" syn = { version = '0.15', features = ['full', 'visit'] } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" } diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml index 9240bec16..515f478fd 100644 --- a/crates/cli-support/Cargo.toml +++ b/crates/cli-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-cli-support" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli-support" @@ -17,6 +17,6 @@ parity-wasm = "0.32" serde = "1.0" serde_json = "1.0" tempfile = "3.0" -wasm-bindgen-shared = { path = "../shared", version = '=0.2.20' } -wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.20' } +wasm-bindgen-shared = { path = "../shared", version = '=0.2.21' } +wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.21' } wasm-gc-api = "0.1.9" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 445e27c91..eafe72a9c 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-cli" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cli" @@ -23,8 +23,8 @@ rouille = { version = "2.1.0", default-features = false } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.20" } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } +wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.21" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" } openssl = { version = '0.10.11', optional = true } [features] diff --git a/crates/futures/Cargo.toml b/crates/futures/Cargo.toml index eee21c54b..b31cdad27 100644 --- a/crates/futures/Cargo.toml +++ b/crates/futures/Cargo.toml @@ -7,12 +7,12 @@ license = "MIT/Apache-2.0" name = "wasm-bindgen-futures" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures" readme = "./README.md" -version = "0.2.20" +version = "0.2.21" [dependencies] futures = "0.1.20" -js-sys = { path = "../js-sys", version = '0.2.5' } -wasm-bindgen = { path = "../..", version = '0.2.20' } +js-sys = { path = "../js-sys", version = '0.2.6' } +wasm-bindgen = { path = "../..", version = '0.2.21' } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = { path = '../test', version = '0.2.20' } +wasm-bindgen-test = { path = '../test', version = '0.2.21' } diff --git a/crates/js-sys/Cargo.toml b/crates/js-sys/Cargo.toml index 3092f092e..7c807edc4 100644 --- a/crates/js-sys/Cargo.toml +++ b/crates/js-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "js-sys" -version = "0.2.5" +version = "0.2.6" authors = ["The wasm-bindgen Developers"] readme = "./README.md" categories = ["wasm"] @@ -18,9 +18,9 @@ test = false doctest = false [dependencies] -wasm-bindgen = { path = "../..", version = "0.2.20" } +wasm-bindgen = { path = "../..", version = "0.2.21" } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] futures = "0.1.20" -wasm-bindgen-test = { path = '../test', version = '=0.2.20' } -wasm-bindgen-futures = { path = '../futures', version = '=0.2.20' } +wasm-bindgen-test = { path = '../test', version = '=0.2.21' } +wasm-bindgen-futures = { path = '../futures', version = '=0.2.21' } diff --git a/crates/macro-support/Cargo.toml b/crates/macro-support/Cargo.toml index 0073487d2..d5c03b268 100644 --- a/crates/macro-support/Cargo.toml +++ b/crates/macro-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-macro-support" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support" @@ -18,5 +18,5 @@ extra-traits = ["syn/extra-traits"] syn = { version = '0.15.0', features = ['full'] } quote = '0.6' proc-macro2 = "0.4.9" -wasm-bindgen-backend = { path = "../backend", version = "=0.2.20" } -wasm-bindgen-shared = { path = "../shared", version = "=0.2.20" } +wasm-bindgen-backend = { path = "../backend", version = "=0.2.21" } +wasm-bindgen-shared = { path = "../shared", version = "=0.2.21" } diff --git a/crates/macro/Cargo.toml b/crates/macro/Cargo.toml index 3f48d0c17..d1946d0be 100644 --- a/crates/macro/Cargo.toml +++ b/crates/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-macro" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro" @@ -18,5 +18,5 @@ spans = ["wasm-bindgen-macro-support/spans"] xxx_debug_only_print_generated_code = [] [dependencies] -wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.20" } +wasm-bindgen-macro-support = { path = "../macro-support", version = "=0.2.21" } quote = "0.6" diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml index dc4bd3286..d038ad0fe 100644 --- a/crates/shared/Cargo.toml +++ b/crates/shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-shared" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared" diff --git a/crates/test-macro/Cargo.toml b/crates/test-macro/Cargo.toml index 913d2989b..2f5a1f5bf 100644 --- a/crates/test-macro/Cargo.toml +++ b/crates/test-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-test-macro" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] description = "Internal testing macro for wasm-bindgen" license = "MIT/Apache-2.0" diff --git a/crates/test/Cargo.toml b/crates/test/Cargo.toml index 8f960bc0e..6d5f9f82c 100644 --- a/crates/test/Cargo.toml +++ b/crates/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-test" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] description = "Internal testing crate for wasm-bindgen" license = "MIT/Apache-2.0" @@ -9,11 +9,11 @@ repository = "https://github.com/rustwasm/wasm-bindgen" [dependencies] console_error_panic_hook = '0.1' futures = "0.1" -js-sys = { path = '../js-sys', version = '0.2.5' } +js-sys = { path = '../js-sys', version = '0.2.6' } scoped-tls = "0.1" -wasm-bindgen = { path = '../..', version = '0.2.20' } -wasm-bindgen-futures = { path = '../futures', version = '0.2.20' } -wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.20' } +wasm-bindgen = { path = '../..', version = '0.2.21' } +wasm-bindgen-futures = { path = '../futures', version = '0.2.21' } +wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.2.21' } [lib] test = false diff --git a/crates/wasm-interpreter/Cargo.toml b/crates/wasm-interpreter/Cargo.toml index 2800d8390..6dd0f9c5e 100644 --- a/crates/wasm-interpreter/Cargo.toml +++ b/crates/wasm-interpreter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasm-bindgen-wasm-interpreter" -version = "0.2.20" +version = "0.2.21" authors = ["The wasm-bindgen Developers"] license = "MIT/Apache-2.0" repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/wasm-interpreter" diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml index 63ec1689a..5ad947b04 100644 --- a/crates/web-sys/Cargo.toml +++ b/crates/web-sys/Cargo.toml @@ -18,13 +18,13 @@ wasm-bindgen-webidl = { path = "../webidl", version = "=0.2.17" } sourcefile = "0.1" [dependencies] -wasm-bindgen = { path = "../..", version = "0.2.20" } -js-sys = { path = '../js-sys', version = '0.2.5' } +wasm-bindgen = { path = "../..", version = "0.2.21" } +js-sys = { path = '../js-sys', version = '0.2.6' } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] futures = "0.1" -wasm-bindgen-test = { path = '../test', version = '0.2.20' } -wasm-bindgen-futures = { path = '../futures', version = '0.2.20' } +wasm-bindgen-test = { path = '../test', version = '0.2.21' } +wasm-bindgen-futures = { path = '../futures', version = '0.2.21' } # This list is generated by passing `__WASM_BINDGEN_DUMP_FEATURES=foo` when # compiling this crate which dumps the total list of features to a file called diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index 1a6451f72..c7203ca6e 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -19,5 +19,5 @@ log = "0.4.1" proc-macro2 = "0.4.8" quote = '0.6' syn = { version = '0.15', features = ['full'] } -wasm-bindgen-backend = { version = "=0.2.20", path = "../backend" } +wasm-bindgen-backend = { version = "=0.2.21", path = "../backend" } weedle = "0.7"