From 7f4663b70bd492278bf0e7bba4eeddb3d840c868 Mon Sep 17 00:00:00 2001 From: Liam Murphy Date: Mon, 27 Jun 2022 22:27:37 +1000 Subject: [PATCH] Fixes #2961 (#2965) In the bundler target, there's a circular dependency between the bindings and the wasm module. That means that the wasm module's exports aren't available at the top level. In #2886, I didn't realise that and made the memory views be initialised at the top level, which resulted in an error from the wasm module's memory not being available yet. This fixes that by lazily initialising the memory views like they were before #2886, except that they're reset to uninitialised in `init` to make sure they're updated if it's called multiple times (the reason I made them be immediately initialised in the first place). --- crates/cli-support/src/js/mod.rs | 64 +++++++------------ .../tests/reference/anyref-import-catch.js | 9 ++- crates/cli/tests/reference/import-catch.js | 5 +- crates/cli/tests/reference/result-string.js | 9 ++- crates/cli/tests/reference/string-arg.js | 5 +- 5 files changed, 35 insertions(+), 57 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 2c9cddadbf9..303c18546f8 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -12,7 +12,7 @@ use std::fmt; use std::fmt::Write; use std::fs; use std::path::{Path, PathBuf}; -use walrus::{ExportItem, FunctionId, ImportId, MemoryId, Module, TableId, ValType}; +use walrus::{FunctionId, ImportId, MemoryId, Module, TableId, ValType}; mod binding; @@ -403,8 +403,6 @@ impl<'a> Context<'a> { ))), ); - footer.push_str(&self.post_instantiate()); - if needs_manual_start { footer.push_str("\nwasm.__wbindgen_start();\n"); } @@ -418,7 +416,6 @@ impl<'a> Context<'a> { footer.push_str(&self.generate_deno_wasm_loading(module_name)); footer.push_str("\n\n"); - footer.push_str(&self.post_instantiate()); if needs_manual_start { footer.push_str("\nwasm.__wbindgen_start();\n"); @@ -454,9 +451,6 @@ impl<'a> Context<'a> { } } - footer.push('\n'); - footer.push_str(&self.post_instantiate()); - if needs_manual_start { start = Some("\nwasm.__wbindgen_start();\n".to_string()); } @@ -587,37 +581,6 @@ impl<'a> Context<'a> { Ok(imports) } - /// Returns JS to be run immediately after the wasm module is instantiated, - /// before the start function is called. - fn post_instantiate(&self) -> String { - let mut out = String::new(); - // Initialise all the memory views. - for (&mem_id, &(num, ref views)) in &self.memories { - // We can't just use `export_name_of` because it takes `&mut self` and we've already borrowed `views`. - let mem = match self - .module - .exports - .iter() - .find(|export| matches!(export.item, ExportItem::Memory(id) if id == mem_id)) - { - Some(export) => &export.name, - None => continue, - }; - - for kind in views { - writeln!( - out, - "cached{kind}Memory{num} = new {kind}Array(wasm.{mem}.buffer);", - kind = kind, - num = num, - mem = mem, - ) - .unwrap() - } - } - out - } - fn ts_for_init_fn( &self, has_memory: bool, @@ -797,6 +760,22 @@ impl<'a> Context<'a> { imports_init.push_str(&format!("imports['{}'] = __wbg_star{};\n", extra, i)); } + let mut init_memviews = String::new(); + for &(num, ref views) in self.memories.values() { + for kind in views { + writeln!( + init_memviews, + // Reset the memory views to empty in case `init` gets called multiple times. + // Without this, the `length = 0` check would never detect that the view was + // outdated. + "cached{kind}Memory{num} = new {kind}Array();", + kind = kind, + num = num, + ) + .unwrap() + } + } + let js = format!( "\ async function load(module, imports) {{ @@ -847,7 +826,7 @@ impl<'a> Context<'a> { function finalizeInit(instance, module) {{ wasm = instance.exports; init.__wbindgen_wasm_module = module; - {post_instantiate} + {init_memviews} {start} return wasm; }} @@ -881,7 +860,7 @@ impl<'a> Context<'a> { init_memory_arg = init_memory_arg, default_module_path = default_module_path, init_memory = init_memory, - post_instantiate = self.post_instantiate(), + init_memviews = init_memviews, start = if needs_manual_start { "wasm.__wbindgen_start();" } else { @@ -1749,9 +1728,12 @@ impl<'a> Context<'a> { format!("{cache}.byteLength === 0", cache = cache) }; + // Initialize the cache to an empty array, which will trigger the resized check + // on the first call and initialise the view. + self.global(&format!("let {cache} = new {kind}Array();\n")); + self.global(&format!( " - let {cache}; function {name}() {{ if ({resized_check}) {{ {cache} = new {kind}Array(wasm.{mem}.buffer); diff --git a/crates/cli/tests/reference/anyref-import-catch.js b/crates/cli/tests/reference/anyref-import-catch.js index c4eb9714c06..6d5394d788a 100644 --- a/crates/cli/tests/reference/anyref-import-catch.js +++ b/crates/cli/tests/reference/anyref-import-catch.js @@ -6,7 +6,8 @@ let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true cachedTextDecoder.decode(); -let cachedUint8Memory0; +let cachedUint8Memory0 = new Uint8Array(); + function getUint8Memory0() { if (cachedUint8Memory0.byteLength === 0) { cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); @@ -33,7 +34,8 @@ function handleError(f, args) { } } -let cachedInt32Memory0; +let cachedInt32Memory0 = new Int32Array(); + function getInt32Memory0() { if (cachedInt32Memory0.byteLength === 0) { cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); @@ -81,6 +83,3 @@ export function __wbindgen_init_externref_table() { ; }; -cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); -cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); - diff --git a/crates/cli/tests/reference/import-catch.js b/crates/cli/tests/reference/import-catch.js index d7a7bf837f3..6f9a1bc1578 100644 --- a/crates/cli/tests/reference/import-catch.js +++ b/crates/cli/tests/reference/import-catch.js @@ -23,7 +23,8 @@ function handleError(f, args) { } } -let cachedInt32Memory0; +let cachedInt32Memory0 = new Int32Array(); + function getInt32Memory0() { if (cachedInt32Memory0.byteLength === 0) { cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); @@ -64,5 +65,3 @@ export function __wbg_foo_8d66ddef0ff279d6() { return handleError(function () { foo(); }, arguments) }; -cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - diff --git a/crates/cli/tests/reference/result-string.js b/crates/cli/tests/reference/result-string.js index 708880bb8b8..a9d60cc3cb0 100644 --- a/crates/cli/tests/reference/result-string.js +++ b/crates/cli/tests/reference/result-string.js @@ -15,7 +15,8 @@ function addHeapObject(obj) { return idx; } -let cachedInt32Memory0; +let cachedInt32Memory0 = new Int32Array(); + function getInt32Memory0() { if (cachedInt32Memory0.byteLength === 0) { cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); @@ -43,7 +44,8 @@ let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true cachedTextDecoder.decode(); -let cachedUint8Memory0; +let cachedUint8Memory0 = new Uint8Array(); + function getUint8Memory0() { if (cachedUint8Memory0.byteLength === 0) { cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); @@ -83,6 +85,3 @@ export function __wbindgen_number_new(arg0) { return addHeapObject(ret); }; -cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); -cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); - diff --git a/crates/cli/tests/reference/string-arg.js b/crates/cli/tests/reference/string-arg.js index 9c93a69b759..deba025f926 100644 --- a/crates/cli/tests/reference/string-arg.js +++ b/crates/cli/tests/reference/string-arg.js @@ -6,7 +6,8 @@ let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true cachedTextDecoder.decode(); -let cachedUint8Memory0; +let cachedUint8Memory0 = new Uint8Array(); + function getUint8Memory0() { if (cachedUint8Memory0.byteLength === 0) { cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); @@ -87,5 +88,3 @@ export function __wbindgen_throw(arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; -cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); -