From 13cac2d0c4f4a6bad08acd85d14e30e48a5ce68d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Oct 2018 16:10:24 -0700 Subject: [PATCH] Fix generated shims if APIs don't exist This commit fixes instantiation of the wasm module even if some of the improted APIs don't exist. This extends the functionality initially added in #409 to attempt to gracefully allow importing values from the environment which don't actually exist in all contexts. In addition to nonexistent methods being handled now entire nonexistent types are now also handled. I suspect that eventually we'll add a CLI flag to `wasm-bindgen` to say "I assert everything exists, don't check it" to trim out the extra JS glue generated here. In the meantime though this'll pave the way for a wasm-bindgen shim to be instantiated in both a web worker and the main thread, while using DOM-like APIs only on the main thread. --- crates/cli-support/src/js/mod.rs | 59 ++++++++++++++------------------ tests/wasm/imports.js | 1 + tests/wasm/imports.rs | 28 +++++++++++++++ 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 4f58190a1bb..0f75b427234 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1542,7 +1542,7 @@ impl<'a> Context<'a> { if (desc) return desc; obj = Object.getPrototypeOf(obj); } - throw new Error(`descriptor for id='${id}' not found`); + return {} } ", ); @@ -1956,29 +1956,26 @@ impl<'a, 'b> SubContext<'a, 'b> { ), } } else { - let (location, binding) = if op.is_static { - ("", format!(".bind({})", class)) - } else { - (".prototype", "".into()) - }; - - match &op.kind { + let target = format!("typeof {0} === 'undefined' ? null : {}{}", + class, + if op.is_static { "" } else { ".prototype" }); + let (mut target, name) = match &op.kind { shared::OperationKind::Regular => { - format!("{}{}.{}{}", class, location, import.function.name, binding) + (format!("{}.{}", target, import.function.name), &import.function.name) } shared::OperationKind::Getter(g) => { self.cx.expose_get_inherited_descriptor(); - format!( - "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').get{}", - class, location, g, binding, - ) + (format!( + "GetOwnOrInheritedPropertyDescriptor({}, '{}').get", + target, g, + ), g) } shared::OperationKind::Setter(s) => { self.cx.expose_get_inherited_descriptor(); - format!( - "GetOwnOrInheritedPropertyDescriptor({}{}, '{}').set{}", - class, location, s, binding, - ) + (format!( + "GetOwnOrInheritedPropertyDescriptor({}, '{}').set", + target, s, + ), s) } shared::OperationKind::IndexingGetter => { panic!("indexing getter should be structural") @@ -1989,24 +1986,20 @@ impl<'a, 'b> SubContext<'a, 'b> { shared::OperationKind::IndexingDeleter => { panic!("indexing deleter should be structural") } - } - }; - - let fallback = if import.structural { - "".to_string() - } else { - format!( - " || function() {{ - throw new Error(`wasm-bindgen: {} does not exist`); - }}", - target - ) + }; + target.push_str(&format!(" || function() {{ + throw new Error(`wasm-bindgen: {}.{} does not exist`); + }}", class, name)); + if op.is_static { + target.insert(0, '('); + target.push_str(").bind("); + target.push_str(&class); + target.push_str(")"); + } + target }; - self.cx.global(&format!( - "const {}_target = {}{};", - import.shim, target, fallback - )); + self.cx.global(&format!("const {}_target = {};", import.shim, target)); Ok(format!( "{}_target{}", import.shim, diff --git a/tests/wasm/imports.js b/tests/wasm/imports.js index a97c8be6f85..a2aef2e1ba3 100644 --- a/tests/wasm/imports.js +++ b/tests/wasm/imports.js @@ -104,3 +104,4 @@ exports.assert_dead_import_not_generated = function() { exports.import_inside_function_works = function() {}; exports.import_inside_private_module = function() {}; +exports.should_call_undefined_functions = () => false; diff --git a/tests/wasm/imports.rs b/tests/wasm/imports.rs index 463aa5aba66..15a6c4c2069 100644 --- a/tests/wasm/imports.rs +++ b/tests/wasm/imports.rs @@ -50,6 +50,7 @@ extern "C" { fn unused_import(); fn assert_dead_import_not_generated(); + fn should_call_undefined_functions() -> bool; } #[wasm_bindgen] @@ -204,3 +205,30 @@ mod private { import_inside_private_module(); } } + +#[wasm_bindgen] +extern { + fn something_not_defined_in_the_environment(); + + type TypeThatIsNotDefined; + #[wasm_bindgen(constructor)] + fn new() -> TypeThatIsNotDefined; + #[wasm_bindgen(method)] + fn method(this: &TypeThatIsNotDefined); + #[wasm_bindgen(method, getter)] + fn property(this: &TypeThatIsNotDefined) -> u32; + #[wasm_bindgen(method, setter)] + fn set_property(this: &TypeThatIsNotDefined, val: u32); +} + +#[wasm_bindgen_test] +fn undefined_function_is_ok() { + if !should_call_undefined_functions() { + return + } + something_not_defined_in_the_environment(); + + let x = TypeThatIsNotDefined::new(); + x.method(); + x.set_property(x.property()); +}