diff --git a/crates/cli-support/src/externref.rs b/crates/cli-support/src/externref.rs index a7803313541..845780c844f 100644 --- a/crates/cli-support/src/externref.rs +++ b/crates/cli-support/src/externref.rs @@ -26,7 +26,7 @@ pub fn process(module: &mut Module) -> Result<()> { // Transform all exported functions in the module, using the bindings listed // for each exported function. - for (id, adapter) in crate::sorted_iter_mut(&mut section.adapters) { + for (id, adapter) in &mut section.adapters { let instructions = match &mut adapter.kind { AdapterKind::Local { instructions } => instructions, AdapterKind::Import { .. } => continue, @@ -78,7 +78,7 @@ pub fn process(module: &mut Module) -> Result<()> { // Additionally we may need to update some adapter instructions other than // those found for the externref pass. These are some general "fringe support" // things necessary to get absolutely everything working. - for (_, adapter) in crate::sorted_iter_mut(&mut section.adapters) { + for adapter in &mut section.adapters.values_mut() { let instrs = match &mut adapter.kind { AdapterKind::Local { instructions } => instructions, AdapterKind::Import { .. } => continue, diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 22bd9246e6a..786bd22ad73 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -317,7 +317,7 @@ impl<'a> Context<'a> { wasm_import_object.push_str(&format!(" {}: {{\n", crate::PLACEHOLDER_MODULE)); - for (id, js) in crate::sorted_iter(&self.wasm_import_definitions) { + for (id, js) in iter_by_import(&self.wasm_import_definitions, self.module) { let import = self.module.imports.get(*id); wasm_import_object.push_str(&format!("{}: {},\n", &import.name, js.trim())); } @@ -416,8 +416,8 @@ impl<'a> Context<'a> { js.push_str("let wasm;\n"); - for (id, js) in crate::sorted_iter(&self.wasm_import_definitions) { - let import = self.module.imports.get_mut(*id); + for (id, js) in iter_by_import(&self.wasm_import_definitions, self.module) { + let import = self.module.imports.get(*id); footer.push_str("\nmodule.exports."); footer.push_str(&import.name); footer.push_str(" = "); @@ -455,7 +455,7 @@ impl<'a> Context<'a> { // and let the bundler/runtime take care of it. // With Node we manually read the Wasm file from the filesystem and instantiate it. OutputMode::Bundler { .. } | OutputMode::Node { module: true } => { - for (id, js) in crate::sorted_iter(&self.wasm_import_definitions) { + for (id, js) in iter_by_import(&self.wasm_import_definitions, self.module) { let import = self.module.imports.get_mut(*id); import.module = format!("./{}_bg.js", module_name); if let Some(body) = js.strip_prefix("function") { @@ -783,7 +783,7 @@ __wbg_set_wasm(wasm);" imports_init.push_str(module_name); imports_init.push_str(" = {};\n"); - for (id, js) in crate::sorted_iter(&self.wasm_import_definitions) { + for (id, js) in iter_by_import(&self.wasm_import_definitions, self.module) { let import = self.module.imports.get_mut(*id); import.module = module_name.to_string(); imports_init.push_str("imports."); @@ -2523,12 +2523,13 @@ __wbg_set_wasm(wasm);" if self.config.symbol_dispose && !self.aux.structs.is_empty() { self.expose_symbol_dispose()?; } - for (id, adapter) in crate::sorted_iter(&self.wit.adapters) { + + for (id, adapter, kind) in iter_adapeter(self.aux, self.wit, self.module) { let instrs = match &adapter.kind { AdapterKind::Import { .. } => continue, AdapterKind::Local { instructions } => instructions, }; - self.generate_adapter(*id, adapter, instrs)?; + self.generate_adapter(id, adapter, instrs, kind)?; } let mut pairs = self.aux.export_map.iter().collect::>(); @@ -2606,26 +2607,10 @@ __wbg_set_wasm(wasm);" id: AdapterId, adapter: &Adapter, instrs: &[InstructionData], + kind: ContextAdapterKind, ) -> Result<(), Error> { - enum Kind<'a> { - Export(&'a AuxExport), - Import(walrus::ImportId), - Adapter, - } - - let kind = match self.aux.export_map.get(&id) { - Some(export) => Kind::Export(export), - None => { - let core = self.wit.implements.iter().find(|pair| pair.2 == id); - match core { - Some((core, _, _)) => Kind::Import(*core), - None => Kind::Adapter, - } - } - }; - let catch = self.aux.imports_with_catch.contains(&id); - if let Kind::Import(core) = kind { + if let ContextAdapterKind::Import(core) = kind { if !catch && self.attempt_direct_import(core, instrs)? { return Ok(()); } @@ -2635,8 +2620,8 @@ __wbg_set_wasm(wasm);" // export that we're generating. let mut builder = binding::Builder::new(self); builder.log_error(match kind { - Kind::Export(_) | Kind::Adapter => false, - Kind::Import(_) => builder.cx.config.debug, + ContextAdapterKind::Export(_) | ContextAdapterKind::Adapter => false, + ContextAdapterKind::Import(_) => builder.cx.config.debug, }); builder.catch(catch); let mut arg_names = &None; @@ -2644,7 +2629,7 @@ __wbg_set_wasm(wasm);" let mut variadic = false; let mut generate_jsdoc = false; match kind { - Kind::Export(export) => { + ContextAdapterKind::Export(export) => { arg_names = &export.arg_names; asyncness = export.asyncness; variadic = export.variadic; @@ -2659,8 +2644,8 @@ __wbg_set_wasm(wasm);" }, } } - Kind::Import(_) => {} - Kind::Adapter => {} + ContextAdapterKind::Import(_) => {} + ContextAdapterKind::Adapter => {} } // Process the `binding` and generate a bunch of JS/TypeScript/etc. @@ -2684,15 +2669,19 @@ __wbg_set_wasm(wasm);" generate_jsdoc, ) .with_context(|| match kind { - Kind::Export(e) => format!("failed to generate bindings for `{}`", e.debug_name), - Kind::Import(i) => { + ContextAdapterKind::Export(e) => { + format!("failed to generate bindings for `{}`", e.debug_name) + } + ContextAdapterKind::Import(i) => { let i = builder.cx.module.imports.get(i); format!( "failed to generate bindings for import of `{}::{}`", i.module, i.name ) } - Kind::Adapter => "failed to generates bindings for adapter".to_string(), + ContextAdapterKind::Adapter => { + "failed to generates bindings for adapter".to_string() + } })?; self.typescript_refs.extend(ts_refs); @@ -2700,7 +2689,7 @@ __wbg_set_wasm(wasm);" // Once we've got all the JS then put it in the right location depending // on what's being exported. match kind { - Kind::Export(export) => { + ContextAdapterKind::Export(export) => { assert!(!catch); assert!(!log_error); @@ -2787,7 +2776,7 @@ __wbg_set_wasm(wasm);" } } } - Kind::Import(core) => { + ContextAdapterKind::Import(core) => { let code = if catch { format!( "function() {{ return handleError(function {}, arguments) }}", @@ -2804,7 +2793,7 @@ __wbg_set_wasm(wasm);" self.wasm_import_definitions.insert(core, code); } - Kind::Adapter => { + ContextAdapterKind::Adapter => { assert!(!catch); assert!(!log_error); @@ -4122,6 +4111,102 @@ __wbg_set_wasm(wasm);" } } +/// A categorization of adapters for the purpose of code generation. +/// +/// This is different from [`AdapterKind`] and is only used internally in the +/// code generation process. +enum ContextAdapterKind<'a> { + /// An exported function, method, constrctor, or getter/setter. + Export(&'a AuxExport), + /// An imported function or intrinsic. + Import(walrus::ImportId), + Adapter, +} +impl<'a> ContextAdapterKind<'a> { + fn get(id: AdapterId, aux: &'a WasmBindgenAux, wit: &'a NonstandardWitSection) -> Self { + match aux.export_map.get(&id) { + Some(export) => ContextAdapterKind::Export(export), + None => { + let core = wit.implements.iter().find(|pair| pair.2 == id); + match core { + Some((core, _, _)) => ContextAdapterKind::Import(*core), + None => ContextAdapterKind::Adapter, + } + } + } + } +} + +/// Iterate over the adapters in a deterministic order. +fn iter_adapeter<'a>( + aux: &'a WasmBindgenAux, + wit: &'a NonstandardWitSection, + module: &Module, +) -> Vec<(AdapterId, &'a Adapter, ContextAdapterKind<'a>)> { + let mut adapters: Vec<_> = wit + .adapters + .iter() + .map(|(id, adapter)| { + // we need the kind of the adapter to properly sort them + let kind = ContextAdapterKind::get(*id, aux, wit); + (*id, adapter, kind) + }) + .collect(); + + // Since `wit.adapters` is a BTreeMap, the adapters are already sorted by + // their ID. This is good enough for exports and adapters, but imports need + // to be sorted by their name. + // + // Note: we do *NOT* want to sort exports by name. By default, exports are + // the order in which they were defined in the Rust code. Sorting them by + // name would break that order and take away control from the user. + + adapters.sort_by(|(_, _, a), (_, _, b)| { + fn get_kind_order(kind: &ContextAdapterKind) -> u8 { + match kind { + ContextAdapterKind::Import(_) => 0, + ContextAdapterKind::Export(_) => 1, + ContextAdapterKind::Adapter => 2, + } + } + + match (a, b) { + (ContextAdapterKind::Import(a), ContextAdapterKind::Import(b)) => { + let a = module.imports.get(*a); + let b = module.imports.get(*b); + a.name.cmp(&b.name) + } + _ => get_kind_order(a).cmp(&get_kind_order(b)), + } + }); + + adapters +} + +/// Iterate over the imports in a deterministic order. +fn iter_by_import<'a, T>( + map: &'a HashMap, + module: &Module, +) -> Vec<(&'a ImportId, &'a T)> { + let mut items: Vec<_> = map.iter().collect(); + + // Sort by import name. + // + // Imports have a name and a module, and it's important that we *ignore* + // the module. The module of an import is set to its final value *during* + // code generation, so using it here would cause the imports to be sorted + // differently depending on which part of the code generation process we're + // in. + items.sort_by(|&(a, _), &(b, _)| { + let a = module.imports.get(*a); + let b = module.imports.get(*b); + + a.name.cmp(&b.name) + }); + + items +} + fn check_duplicated_getter_and_setter_names( exports: &[(&AdapterId, &AuxExport)], ) -> Result<(), Error> { diff --git a/crates/cli-support/src/multivalue.rs b/crates/cli-support/src/multivalue.rs index a49875381bf..cc0194a6aea 100644 --- a/crates/cli-support/src/multivalue.rs +++ b/crates/cli-support/src/multivalue.rs @@ -14,7 +14,7 @@ pub fn run(module: &mut Module) -> Result<(), Error> { let mut to_xform = Vec::new(); let mut slots = Vec::new(); - for (_, adapter) in crate::sorted_iter_mut(&mut adapters.adapters) { + for adapter in adapters.adapters.values_mut() { extract_xform(module, adapter, &mut to_xform, &mut slots); } if to_xform.is_empty() { diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index f6b3e413712..3e3e355c366 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -4,7 +4,7 @@ use crate::descriptors::WasmBindgenDescriptorsSection; use crate::intrinsic::Intrinsic; use crate::{decode, Bindgen, PLACEHOLDER_MODULE}; use anyhow::{anyhow, bail, Error}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap}; use std::str; use walrus::MemoryId; use walrus::{ExportId, FunctionId, ImportId, Module}; @@ -106,7 +106,9 @@ impl<'a> Context<'a> { // location listed of what to import there for each item. let mut intrinsics = Vec::new(); let mut duplicate_import_map = HashMap::new(); - let mut imports_to_delete = HashSet::new(); + // The order in which imports are deleted later might matter, so we + // use an ordered set here to make everything deterministic. + let mut imports_to_delete = BTreeSet::new(); for import in self.module.imports.iter() { if import.module != PLACEHOLDER_MODULE { continue; diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index c96430c6411..eeadd1a69f8 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -1,13 +1,17 @@ use crate::descriptor::VectorKind; use crate::wit::{AuxImport, WasmBindgenAux}; use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashSet}; use walrus::{FunctionId, ImportId, RefType, TypedCustomSectionId}; #[derive(Default, Debug)] pub struct NonstandardWitSection { /// A list of adapter functions, keyed by their id. - pub adapters: HashMap, + /// + /// This map is iterated over in multiple places, so we use an ordered map + /// to ensure that the order of iteration is deterministic. This map affects + /// all parts of the generated code, so it's important to get this right. + pub adapters: BTreeMap, /// A list of pairs for adapter functions that implement core Wasm imports. pub implements: Vec<(ImportId, FunctionId, AdapterId)>, diff --git a/crates/cli/tests/reference/anyref-import-catch.js b/crates/cli/tests/reference/anyref-import-catch.js index d89f629f896..f8e28d8db96 100644 --- a/crates/cli/tests/reference/anyref-import-catch.js +++ b/crates/cli/tests/reference/anyref-import-catch.js @@ -4,6 +4,21 @@ export function __wbg_set_wasm(val) { } +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_export_2.set(idx, obj); + return idx; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); @@ -24,21 +39,6 @@ function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); } -function addToExternrefTable0(obj) { - const idx = wasm.__externref_table_alloc(); - wasm.__wbindgen_export_0.set(idx, obj); - return idx; -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - const idx = addToExternrefTable0(e); - wasm.__wbindgen_exn_store(idx); - } -} - let cachedDataViewMemory0 = null; function getDataViewMemory0() { @@ -49,7 +49,7 @@ function getDataViewMemory0() { } function takeFromExternrefTable0(idx) { - const value = wasm.__wbindgen_export_0.get(idx); + const value = wasm.__wbindgen_export_2.get(idx); wasm.__externref_table_dealloc(idx); return value; } @@ -72,12 +72,8 @@ export function __wbg_foo_95fe1a04017077db() { return handleError(function () { foo(); }, arguments) }; -export function __wbindgen_throw(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); -}; - export function __wbindgen_init_externref_table() { - const table = wasm.__wbindgen_export_0; + const table = wasm.__wbindgen_export_2; const offset = table.grow(4); table.set(0, undefined); table.set(offset + 0, undefined); @@ -87,3 +83,7 @@ export function __wbindgen_init_externref_table() { ; }; +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/anyref-import-catch.wat b/crates/cli/tests/reference/anyref-import-catch.wat index fd4ba663d5e..7338f544d0a 100644 --- a/crates/cli/tests/reference/anyref-import-catch.wat +++ b/crates/cli/tests/reference/anyref-import-catch.wat @@ -13,9 +13,9 @@ (memory (;0;) 17) (export "memory" (memory 0)) (export "exported" (func $exported)) - (export "__wbindgen_export_0" (table 0)) (export "__wbindgen_exn_store" (func $__wbindgen_exn_store)) (export "__externref_table_alloc" (func $__externref_table_alloc)) + (export "__wbindgen_export_2" (table 0)) (export "__wbindgen_add_to_stack_pointer" (func $__wbindgen_add_to_stack_pointer)) (export "__externref_table_dealloc" (func $__externref_table_dealloc)) (export "__wbindgen_start" (func 0)) diff --git a/crates/cli/tests/reference/raw.js b/crates/cli/tests/reference/raw.js index 6c6e36065ee..d81276a6816 100644 --- a/crates/cli/tests/reference/raw.js +++ b/crates/cli/tests/reference/raw.js @@ -6,34 +6,23 @@ export function __wbg_set_wasm(val) { } -const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; - -let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +const heap = new Array(128).fill(undefined); -cachedTextDecoder.decode(); +heap.push(undefined, null, true, false); -let cachedUint8ArrayMemory0 = null; +let heap_next = heap.length; -function getUint8ArrayMemory0() { - if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8ArrayMemory0; -} +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); + heap[idx] = obj; + return idx; } -const heap = new Array(128).fill(undefined); - -heap.push(undefined, null, true, false); - function getObject(idx) { return heap[idx]; } -let heap_next = heap.length; - function dropObject(idx) { if (idx < 132) return; heap[idx] = heap_next; @@ -45,6 +34,26 @@ function takeObject(idx) { dropObject(idx); return ret; } + +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} /** * @param {number} test * @returns {number} @@ -54,15 +63,6 @@ export function test1(test) { return ret >>> 0; } -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - const TestFinalization = (typeof FinalizationRegistry === 'undefined') ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry(ptr => wasm.__wbg_test_free(ptr >>> 0, 1)); @@ -109,11 +109,11 @@ export function __wbg_test2_39fe629b9aa739cf() { return addHeapObject(ret); }; -export function __wbindgen_throw(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); -}; - export function __wbindgen_object_drop_ref(arg0) { takeObject(arg0); }; +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/web-sys.js b/crates/cli/tests/reference/web-sys.js index 7e4cc173aa7..8851c225ada 100644 --- a/crates/cli/tests/reference/web-sys.js +++ b/crates/cli/tests/reference/web-sys.js @@ -4,10 +4,49 @@ export function __wbg_set_wasm(val) { } +const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +let cachedUint8ArrayMemory0 = null; + +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + const heap = new Array(128).fill(undefined); heap.push(undefined, null, true, false); +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + function getObject(idx) { return heap[idx]; } function debugString(val) { @@ -77,15 +116,6 @@ function debugString(val) { let WASM_VECTOR_LEN = 0; -let cachedUint8ArrayMemory0 = null; - -function getUint8ArrayMemory0() { - if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { - cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8ArrayMemory0; -} - const lTextEncoder = typeof TextEncoder === 'undefined' ? (0, module.require)('util').TextEncoder : TextEncoder; let cachedTextEncoder = new lTextEncoder('utf-8'); @@ -151,19 +181,6 @@ function getDataViewMemory0() { return cachedDataViewMemory0; } -const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder; - -let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true }); - -cachedTextDecoder.decode(); - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); -} - -let heap_next = heap.length; - function dropObject(idx) { if (idx < 132) return; heap[idx] = heap_next; @@ -191,23 +208,6 @@ export function get_media_source() { return __wbindgen_enum_MediaSourceEnum[ret]; } -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - wasm.__wbindgen_exn_store(addHeapObject(e)); - } -} - const __wbindgen_enum_MediaSourceEnum = ["camera", "screen", "application", "window", "browser", "microphone", "audioCapture", "other"]; const __wbindgen_enum_MediaSourceReadyState = ["closed", "open", "ended"]; @@ -225,11 +225,11 @@ export function __wbindgen_debug_string(arg0, arg1) { getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true); }; -export function __wbindgen_throw(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); -}; - export function __wbindgen_object_drop_ref(arg0) { takeObject(arg0); }; +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +}; + diff --git a/crates/cli/tests/reference/web-sys.wat b/crates/cli/tests/reference/web-sys.wat index eefb7ad3e97..b4993ed4ac3 100644 --- a/crates/cli/tests/reference/web-sys.wat +++ b/crates/cli/tests/reference/web-sys.wat @@ -12,9 +12,9 @@ (export "memory" (memory 0)) (export "get_url" (func $get_url)) (export "get_media_source" (func $get_media_source)) + (export "__wbindgen_exn_store" (func $__wbindgen_exn_store)) (export "__wbindgen_malloc" (func $__wbindgen_malloc)) (export "__wbindgen_realloc" (func $__wbindgen_realloc)) - (export "__wbindgen_exn_store" (func $__wbindgen_exn_store)) (@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext") )