From ba825c30addd121b7608ea72d84a83a5fa51b5d5 Mon Sep 17 00:00:00 2001 From: Mitch Foley Date: Sun, 1 Dec 2024 22:45:44 -0500 Subject: [PATCH 01/17] allow emscripten --- crates/backend/src/codegen.rs | 33 +++++++++++++++++---------------- crates/test-macro/src/lib.rs | 8 ++++---- src/externref.rs | 2 +- src/lib.rs | 18 +++++++++--------- 4 files changed, 31 insertions(+), 30 deletions(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 73056b70390..33da1f36fd4 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -162,7 +162,7 @@ impl TryToTokens for ast::Program { let prefix_json_bytes = syn::LitByteStr::new(&prefix_json_bytes, Span::call_site()); (quote! { - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] #[automatically_derived] const _: () = { use #wasm_bindgen::__rt::{flat_len, flat_byte_slices}; @@ -281,12 +281,12 @@ impl ToTokens for ast::Struct { let ptr = #wasm_bindgen::convert::IntoWasmAbi::into_abi(value); #[link(wasm_import_module = "__wbindgen_placeholder__")] - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] extern "C" { fn #new_fn(ptr: u32) -> u32; } - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] unsafe fn #new_fn(_: u32) -> u32 { panic!("cannot convert to JsValue outside of the Wasm target") } @@ -298,7 +298,7 @@ impl ToTokens for ast::Struct { } } - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { @@ -388,12 +388,12 @@ impl ToTokens for ast::Struct { let idx = #wasm_bindgen::convert::IntoWasmAbi::into_abi(&value); #[link(wasm_import_module = "__wbindgen_placeholder__")] - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] extern "C" { fn #unwrap_fn(ptr: u32) -> u32; } - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] unsafe fn #unwrap_fn(_: u32) -> u32 { panic!("cannot convert from JsValue outside of the Wasm target") } @@ -501,8 +501,9 @@ impl ToTokens for ast::StructField { #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { - #[cfg_attr(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), no_mangle)] + #[cfg_attr(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")), no_mangle)] #[doc(hidden)] + pub unsafe extern "C" fn #getter(js: u32) -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi> { @@ -540,7 +541,7 @@ impl ToTokens for ast::StructField { let (args, names) = splat(wasm_bindgen, &Ident::new("val", rust_name.span()), &abi); (quote! { - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { @@ -847,7 +848,7 @@ impl TryToTokens for ast::Export { #wasm_bindgen::__wbindgen_coverage! { #(#attrs)* #[cfg_attr( - all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), + all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")), export_name = #export_name, )] pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> { @@ -1124,11 +1125,11 @@ impl ToTokens for ast::ImportType { impl JsCast for #rust_name { fn instanceof(val: &JsValue) -> bool { #[link(wasm_import_module = "__wbindgen_placeholder__")] - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] extern "C" { fn #instanceof_shim(val: u32) -> u32; } - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] unsafe fn #instanceof_shim(_: u32) -> u32 { panic!("cannot check instanceof on non-wasm targets"); } @@ -1838,12 +1839,12 @@ fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> T }; quote! { #[link(wasm_import_module = "__wbindgen_placeholder__")] - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] extern "C" { fn #shim_name() -> #abi_ret; } - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] unsafe fn #shim_name() -> #abi_ret { panic!("cannot access imported statics on non-wasm targets") } @@ -1888,7 +1889,7 @@ impl ToTokens for Descriptor<'_, T> { let attrs = &self.attrs; let wasm_bindgen = &self.wasm_bindgen; (quote! { - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] #[automatically_derived] const _: () = { #wasm_bindgen::__wbindgen_coverage! { @@ -1916,14 +1917,14 @@ fn extern_fn( abi_ret: TokenStream, ) -> TokenStream { quote! { - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] #(#attrs)* #[link(wasm_import_module = "__wbindgen_placeholder__")] extern "C" { fn #import_name(#(#abi_arguments),*) -> #abi_ret; } - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret { #( drop(#abi_argument_names); diff --git a/crates/test-macro/src/lib.rs b/crates/test-macro/src/lib.rs index c9e27eb6f63..be58a20d603 100644 --- a/crates/test-macro/src/lib.rs +++ b/crates/test-macro/src/lib.rs @@ -98,7 +98,7 @@ pub fn wasm_bindgen_test( const _: () = { #wasm_bindgen_path::__rt::wasm_bindgen::__wbindgen_coverage! { #[export_name = ::core::concat!("__wbgt_", #ignore_name, "_", ::core::module_path!(), "::", ::core::stringify!(#ident))] - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] extern "C" fn __wbgt_test(cx: &#wasm_bindgen_path::__rt::Context) { let test_name = ::core::concat!(::core::module_path!(), "::", ::core::stringify!(#ident)); #test_body @@ -110,7 +110,7 @@ pub fn wasm_bindgen_test( if let Some(path) = attributes.unsupported { tokens.extend( - quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))), #path)] }, + quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))), #path)] }, ); if let Some(should_panic) = should_panic { @@ -121,7 +121,7 @@ pub fn wasm_bindgen_test( }; tokens.extend( - quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))), #should_panic)] } + quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))), #should_panic)] } ) } @@ -133,7 +133,7 @@ pub fn wasm_bindgen_test( }; tokens.extend( - quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))), #ignore)] } + quote! { #[cfg_attr(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))), #ignore)] } ) } } diff --git a/src/externref.rs b/src/externref.rs index e575c12a537..79290f9a23d 100644 --- a/src/externref.rs +++ b/src/externref.rs @@ -106,7 +106,7 @@ fn internal_error(msg: &str) -> ! { std::process::abort(); } else if #[cfg(all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") ))] { core::arch::wasm32::unreachable(); } else { diff --git a/src/lib.rs b/src/lib.rs index 0c81941bc39..2a60c1dfa28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,14 +75,14 @@ use crate::convert::{FromWasmAbi, TryFromJsValue, WasmRet, WasmSlice}; macro_rules! externs { ($(#[$attr:meta])* extern "C" { $(fn $name:ident($($args:tt)*) -> $ret:ty;)* }) => ( - #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] + #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))] $(#[$attr])* extern "C" { $(fn $name($($args)*) -> $ret;)* } $( - #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] + #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))))] #[allow(unused_variables)] unsafe extern fn $name($($args)*) -> $ret { panic!("function not implemented on non-wasm32 targets") @@ -1408,7 +1408,7 @@ pub trait UnwrapThrowExt: Sized { #[cfg_attr( any( debug_assertions, - not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))) + not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))) ), track_caller )] @@ -1417,7 +1417,7 @@ pub trait UnwrapThrowExt: Sized { debug_assertions, all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") ) )) { let loc = core::panic::Location::caller(); @@ -1440,7 +1440,7 @@ pub trait UnwrapThrowExt: Sized { #[cfg_attr( any( debug_assertions, - not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))) + not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten"))) ), track_caller )] @@ -1453,7 +1453,7 @@ impl UnwrapThrowExt for Option { if cfg!(all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") )) { if let Some(val) = self { val @@ -1474,7 +1474,7 @@ impl UnwrapThrowExt for Option { fn expect_throw(self, message: &str) -> T { if cfg!(all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") )) { if let Some(val) = self { val @@ -1507,7 +1507,7 @@ where if cfg!(all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") )) { match self { Ok(val) => val, @@ -1537,7 +1537,7 @@ where fn expect_throw(self, message: &str) -> T { if cfg!(all( target_arch = "wasm32", - any(target_os = "unknown", target_os = "none") + any(target_os = "unknown", target_os = "none", target_os = "emscripten") )) { match self { Ok(val) => val, From af0c20a7611efe26f03a3195377c91df9c33295f Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Mon, 23 Dec 2024 15:05:22 -0500 Subject: [PATCH 02/17] setup emscripten mode, this is tested with building --target=emscripten --- crates/cli-support/src/js/mod.rs | 17 +++++++++++------ crates/cli-support/src/lib.rs | 12 ++++++++++++ crates/cli/src/bin/wasm-bindgen.rs | 4 ++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 3ba5be3a719..685c70f08ad 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -209,7 +209,8 @@ impl<'a> Context<'a> { OutputMode::Bundler { .. } | OutputMode::Node { module: true } | OutputMode::Web - | OutputMode::Deno => match export { + | OutputMode::Deno + | OutputMode::Emscripten => match export { ExportJs::Class(class) => { assert_eq!(export_name, definition_name); format!("export {}\n", class) @@ -598,7 +599,7 @@ __wbg_set_wasm(wasm);" // browsers don't support natively importing Wasm right now so we // expose the same initialization function as `--target no-modules` // as the default export of the module. - OutputMode::Web => { + OutputMode::Web | OutputMode::Emscripten => { self.imports_post.push_str("let wasm;\n"); init = self.gen_init(needs_manual_start, Some(&mut imports))?; footer.push_str("export { initSync };\n"); @@ -698,7 +699,8 @@ __wbg_set_wasm(wasm);" OutputMode::Bundler { .. } | OutputMode::Node { module: true } | OutputMode::Web - | OutputMode::Deno => { + | OutputMode::Deno + | OutputMode::Emscripten => { for (module, items) in crate::sorted_iter(&self.js_imports) { imports.push_str("import { "); for (i, (item, rename)) in items.iter().enumerate() { @@ -1771,7 +1773,8 @@ __wbg_set_wasm(wasm);" OutputMode::Deno | OutputMode::Web | OutputMode::NoModules { .. } - | OutputMode::Bundler { browser_only: true } => { + | OutputMode::Bundler { browser_only: true } + | OutputMode::Emscripten => { self.global(&format!("const cached{0} = (typeof {0} !== 'undefined' ? new {0}{1} : {{ {2}: () => {{ throw Error('{0} not available') }} }} );", s, args, op)) } }; @@ -1785,7 +1788,8 @@ __wbg_set_wasm(wasm);" OutputMode::Deno | OutputMode::Web | OutputMode::NoModules { .. } - | OutputMode::Bundler { browser_only: true } => self.global(&format!( + | OutputMode::Bundler { browser_only: true } + | OutputMode::Emscripten => self.global(&format!( "if (typeof {} !== 'undefined') {{ {} }};", s, init )), @@ -3522,7 +3526,8 @@ __wbg_set_wasm(wasm);" OutputMode::Web | OutputMode::Bundler { .. } | OutputMode::Deno - | OutputMode::Node { module: true } => "import.meta.url", + | OutputMode::Node { module: true } + | OutputMode::Emscripten => "import.meta.url", OutputMode::Node { module: false } => { "require('url').pathToFileURL(__filename)" } diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index a07f48306a1..95b8cf23ad6 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -66,6 +66,7 @@ enum OutputMode { NoModules { global: String }, Node { module: bool }, Deno, + Emscripten, } enum Input { @@ -226,6 +227,13 @@ impl Bindgen { Ok(self) } + pub fn emscripten(&mut self, emscripten: bool) -> Result<&mut Bindgen, Error> { + if emscripten { + self.switch_mode(OutputMode::Emscripten, "--target emscripten")?; + } + Ok(self) + } + pub fn debug(&mut self, debug: bool) -> &mut Bindgen { self.debug = debug; self @@ -559,6 +567,10 @@ impl OutputMode { fn no_modules(&self) -> bool { matches!(self, OutputMode::NoModules { .. }) } + + // fn emscripten(&self) -> bool { + // matches!(self, OutputMode::Emscripten { .. }) + // } fn esm_integration(&self) -> bool { matches!( diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index b705e8a09dd..378e3b58261 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -22,6 +22,8 @@ struct Args { #[arg(long, help = "Deprecated, use `--target no-modules`")] no_modules: bool, #[arg(long, help = "Output a TypeScript definition file (on by default)")] + emscripten: bool, + #[arg(long, help = "testing, also see if `--target emscripten` is hooked up properly")] typescript: bool, #[arg(long, help = "Don't emit a *.d.ts file")] no_typescript: bool, @@ -110,6 +112,7 @@ fn rmain(args: &Args) -> Result<(), Error> { "nodejs" => b.nodejs(true)?, "deno" => b.deno(true)?, "experimental-nodejs-module" => b.nodejs_module(true)?, + "emscripten" => b.emscripten(true)?, s => bail!("invalid encode-into mode: `{}`", s), }; } @@ -118,6 +121,7 @@ fn rmain(args: &Args) -> Result<(), Error> { .web(args.web)? .browser(args.browser)? .no_modules(args.no_modules)? + .emscripten(args.emscripten)? .debug(args.debug) .demangle(!args.no_demangle) .keep_lld_exports(args.keep_lld_exports) From c956845834dffb03bc7cb59e0207da25091f0e4b Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Mon, 23 Dec 2024 15:05:22 -0500 Subject: [PATCH 03/17] setup emscripten mode, this is tested with building --target=emscripten --- crates/backend/src/codegen.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 33da1f36fd4..f1bb1ffe1f5 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -503,7 +503,6 @@ impl ToTokens for ast::StructField { #wasm_bindgen::__wbindgen_coverage! { #[cfg_attr(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")), no_mangle)] #[doc(hidden)] - pub unsafe extern "C" fn #getter(js: u32) -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi> { From 4bef9f5334df51b705d42edaa332afa269a1e241 Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Mon, 23 Dec 2024 15:05:22 -0500 Subject: [PATCH 04/17] setup emscripten mode, this is tested with building --target=emscripten --- crates/cli-support/src/lib.rs | 4 ---- crates/cli/src/bin/wasm-bindgen.rs | 3 --- 2 files changed, 7 deletions(-) diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 95b8cf23ad6..95b50029e99 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -567,10 +567,6 @@ impl OutputMode { fn no_modules(&self) -> bool { matches!(self, OutputMode::NoModules { .. }) } - - // fn emscripten(&self) -> bool { - // matches!(self, OutputMode::Emscripten { .. }) - // } fn esm_integration(&self) -> bool { matches!( diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index 378e3b58261..c9c600dc139 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -22,8 +22,6 @@ struct Args { #[arg(long, help = "Deprecated, use `--target no-modules`")] no_modules: bool, #[arg(long, help = "Output a TypeScript definition file (on by default)")] - emscripten: bool, - #[arg(long, help = "testing, also see if `--target emscripten` is hooked up properly")] typescript: bool, #[arg(long, help = "Don't emit a *.d.ts file")] no_typescript: bool, @@ -121,7 +119,6 @@ fn rmain(args: &Args) -> Result<(), Error> { .web(args.web)? .browser(args.browser)? .no_modules(args.no_modules)? - .emscripten(args.emscripten)? .debug(args.debug) .demangle(!args.no_demangle) .keep_lld_exports(args.keep_lld_exports) From f798c1d236a87dc90a0a4f9632e949256262cadc Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Mon, 13 Jan 2025 14:34:50 -0500 Subject: [PATCH 05/17] When targeting Emscripten, generate pre.js and library_wbg.js separately that are consumed by Emscripten. --- crates/cli-support/src/js/mod.rs | 175 +++++++++++++++++++++++-------- crates/cli-support/src/lib.rs | 9 +- 2 files changed, 142 insertions(+), 42 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 685c70f08ad..e7a3ae7969f 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -23,6 +23,7 @@ mod binding; pub struct Context<'a> { globals: String, + emscripten_library: String, imports_post: String, typescript: String, exposed_globals: Option>>, @@ -146,6 +147,7 @@ impl<'a> Context<'a> { ) -> Result, Error> { Ok(Context { globals: String::new(), + emscripten_library: String::new(), imports_post: String::new(), typescript: "/* tslint:disable */\n/* eslint-disable */\n".to_string(), exposed_globals: Some(Default::default()), @@ -209,8 +211,7 @@ impl<'a> Context<'a> { OutputMode::Bundler { .. } | OutputMode::Node { module: true } | OutputMode::Web - | OutputMode::Deno - | OutputMode::Emscripten => match export { + | OutputMode::Deno => match export { ExportJs::Class(class) => { assert_eq!(export_name, definition_name); format!("export {}\n", class) @@ -231,6 +232,29 @@ impl<'a> Context<'a> { format!("export const {} = {};\n", export_name, expr) } }, + OutputMode::Emscripten => match export { + ExportJs::Class(class) => { + assert_eq!(export_name, definition_name); + format!("{}\nModule.{} = {};\n", class.replace("wasm", "wasmExports"), export_name, export_name) + } + ExportJs::Function(function) => { + let body = function.strip_prefix("function") + .unwrap() + .replace("wasm", "wasmExports"); + if export_name == definition_name { + format!("Module.{} = function{}\n", export_name, body) + } else { + format!( + "function {}{}\nexport {{ {} as {} }};\n", + definition_name, body, definition_name, export_name, + ) + } + } + ExportJs::Expression(expr) => { + assert_eq!(export_name, definition_name); + format!("export const {} = {};\n", export_name, expr.replace("wasm", "wasmExports")) + } + }, }; self.global(&global); Ok(()) @@ -239,7 +263,7 @@ impl<'a> Context<'a> { pub fn finalize( &mut self, module_name: &str, - ) -> Result<(String, String, Option), Error> { + ) -> Result<(String, String, String, Option), Error> { // Finalize all bindings for JS classes. This is where we'll generate JS // glue for all classes as well as finish up a few final imports like // `__wrap` and such. @@ -444,9 +468,10 @@ impl<'a> Context<'a> { &mut self, module_name: &str, needs_manual_start: bool, - ) -> Result<(String, String, Option), Error> { + ) -> Result<(String, String, String, Option), Error> { let mut ts; let mut js = String::new(); + let mut js_emscripten_library = String::new(); let mut start = None; if let OutputMode::NoModules { global } = &self.config.mode { @@ -636,15 +661,30 @@ __wbg_set_wasm(wasm);" } }; - push_with_newline(&imports); - push_with_newline(&self.imports_post); - - // Emit all our exports from this module - push_with_newline(&self.globals); + if matches!(self.config.mode, OutputMode::Emscripten) { + js_emscripten_library.push_str(&self.emscripten_library); + js_emscripten_library.push('\n'); + js_emscripten_library.push_str("var LibraryWbg = {\n"); + js_emscripten_library.push_str(&init_js); + js_emscripten_library.push_str( + "}; + \n + addToLibrary(LibraryWbg);"); + push_with_newline("var Module = {\n + onRuntimeInitialized: () => { + wasmExports.__wbindgen_start();"); + push_with_newline(&self.globals); + push_with_newline("}\n};"); + } else { + push_with_newline(&imports); + push_with_newline(&self.imports_post); + // Emit all our exports from this module + push_with_newline(&self.globals); + // Generate the initialization glue, if there was any + push_with_newline(&init_js); + push_with_newline(&footer); + } - // Generate the initialization glue, if there was any - push_with_newline(&init_js); - push_with_newline(&footer); if self.config.mode.no_modules() { js.push_str("})();\n"); } @@ -653,7 +693,7 @@ __wbg_set_wasm(wasm);" js = js.replace("\n\n\n", "\n\n"); } - Ok((js, ts, start)) + Ok((js, js_emscripten_library, ts, start)) } fn js_import_header(&self) -> Result { @@ -860,20 +900,41 @@ __wbg_set_wasm(wasm);" // directed to wire up. let mut imports_init = String::new(); - imports_init.push_str("imports."); - imports_init.push_str(module_name); - imports_init.push_str(" = {};\n"); + if !matches!(self.config.mode, OutputMode::Emscripten) { + imports_init.push_str("imports."); + imports_init.push_str(module_name); + imports_init.push_str(" = {};\n"); + } 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."); - imports_init.push_str(module_name); - imports_init.push('.'); - imports_init.push_str(&import.name); - imports_init.push_str(" = "); - imports_init.push_str(js.trim()); - imports_init.push_str(";\n"); + if !matches!(self.config.mode, OutputMode::Emscripten) { + imports_init.push_str("imports."); + imports_init.push_str(module_name); + imports_init.push('.'); + imports_init.push_str(&import.name); + imports_init.push_str(" = "); + imports_init.push_str(js.trim()); + imports_init.push_str(";\n"); + } else { + imports_init.push_str(&import.name); + if import.name == "__wbindgen_init_externref_table" { + imports_init.push_str(": () =>"); + imports_init.push_str(&js + .trim() + .strip_prefix("function()") + .unwrap() + .replace("wasm", "wasmExports")); + imports_init.push_str(",\n"); + } else { + imports_init.push_str(": (function() {\n"); + imports_init.push_str("return "); + imports_init.push_str(&js.trim().replace("wasm", "wasmExports")); + imports_init.push_str(";\n"); + imports_init.push_str("}()),\n"); + } + } } let extra_modules = self @@ -897,7 +958,9 @@ __wbg_set_wasm(wasm);" ), }; imports.push_str(&format!("import * as __wbg_star{} from '{}';\n", i, extra)); - imports_init.push_str(&format!("imports['{}'] = __wbg_star{};\n", extra, i)); + if !matches!(self.config.mode, OutputMode::Emscripten) { + imports_init.push_str(&format!("imports['{}'] = __wbg_star{};\n", extra, i)); + } } let mut init_memviews = String::new(); @@ -916,7 +979,13 @@ __wbg_set_wasm(wasm);" } } - let js = format!( + let js = match &self.config.mode { + OutputMode::Emscripten => format!( + "\ + {imports_init}", + imports_init = imports_init + ), + _ => format!( "\ async function __wbg_load(module, imports) {{ if (typeof Response === 'function' && module instanceof Response) {{ @@ -1052,8 +1121,9 @@ __wbg_set_wasm(wasm);" ) } else { String::new() - }, - ); + } + ) + }; Ok((js, ts)) } @@ -1773,10 +1843,12 @@ __wbg_set_wasm(wasm);" OutputMode::Deno | OutputMode::Web | OutputMode::NoModules { .. } - | OutputMode::Bundler { browser_only: true } - | OutputMode::Emscripten => { + | OutputMode::Bundler { browser_only: true } => { self.global(&format!("const cached{0} = (typeof {0} !== 'undefined' ? new {0}{1} : {{ {2}: () => {{ throw Error('{0} not available') }} }} );", s, args, op)) } + OutputMode::Emscripten => { + self.emscripten_library.push_str(&format!("const cached{0} = (typeof {0} !== 'undefined' ? new {0}{1} : {{ {2}: () => {{ throw Error('{0} not available') }} }} );\n\n", s, args, op)); + } }; if let Some(init) = init { @@ -1788,11 +1860,14 @@ __wbg_set_wasm(wasm);" OutputMode::Deno | OutputMode::Web | OutputMode::NoModules { .. } - | OutputMode::Bundler { browser_only: true } - | OutputMode::Emscripten => self.global(&format!( + | OutputMode::Bundler { browser_only: true } => self.global(&format!( "if (typeof {} !== 'undefined') {{ {} }};", s, init )), + OutputMode::Emscripten => self.emscripten_library.push_str(&format!( + "if (typeof {} !== 'undefined') {{ {} }};\n\n", + s, init + )), } } @@ -1821,16 +1896,27 @@ __wbg_set_wasm(wasm);" // string itself. let is_shared = self.module.memories.get(memory).shared; let method = if is_shared { "slice" } else { "subarray" }; - - self.global(&format!( - " - function {}(ptr, len) {{ - ptr = ptr >>> 0; - return cachedTextDecoder.decode({}().{}(ptr, ptr + len)); - }} - ", - ret, mem, method - )); + if matches!(self.config.mode, OutputMode::Emscripten) { + self.emscripten_library.push_str(&format!( + " + function {}(ptr, len) {{ + ptr = ptr >>> 0; + return cachedTextDecoder.decode(HEAP8.{}(ptr, ptr + len)); + }}\n + ", + ret, method + )); + } else { + self.global(&format!( + " + function {}(ptr, len) {{ + ptr = ptr >>> 0; + return cachedTextDecoder.decode({}().{}(ptr, ptr + len)); + }} + ", + ret, mem, method + )); + } Ok(ret) } @@ -2083,6 +2169,13 @@ __wbg_set_wasm(wasm);" format!("{cache}.byteLength === 0", cache = cache) }; + if matches!(self.config.mode, OutputMode::Emscripten) { + // Emscripten provides its own version of getMemory + // so don't write out the memory function. + // See https://emscripten.org/docs/api_reference/preamble.js.html#type-accessors-for-the-memory-model + // for more details. + return view; + } self.global(&format!("let {cache} = null;\n")); self.global(&format!( diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 95b50029e99..0acc499223a 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -51,6 +51,7 @@ pub struct Output { struct Generated { mode: OutputMode, js: String, + js_emscripten_library: String, ts: String, start: Option, snippets: HashMap>, @@ -440,7 +441,7 @@ impl Bindgen { .unwrap(); let mut cx = js::Context::new(&mut module, self, &adapters, &aux)?; cx.generate()?; - let (js, ts, start) = cx.finalize(stem)?; + let (js, js_emscripten_library, ts, start) = cx.finalize(stem)?; let generated = Generated { snippets: aux.snippets.clone(), local_modules: aux.local_modules.clone(), @@ -448,6 +449,7 @@ impl Bindgen { typescript: self.typescript, npm_dependencies: cx.npm_dependencies.clone(), js, + js_emscripten_library, ts, start, }; @@ -725,6 +727,11 @@ export * from \"./{js_name}\"; )?; } write(out_dir.join(&js_name), reset_indentation(&gen.js))?; + } else if matches!(self.generated.mode, OutputMode::Emscripten) { + let wbg_path = out_dir.join("library_wbg.js"); + let pre_js_path = out_dir.join("wbg_pre.js"); + write(&wbg_path, reset_indentation(&gen.js_emscripten_library))?; + write(&pre_js_path, reset_indentation(&gen.js))?; } else { write(&js_path, reset_indentation(&gen.js))?; } From 31e00db0387503df9ca2accea92451cc30ef5b60 Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Tue, 11 Feb 2025 11:53:55 -0500 Subject: [PATCH 06/17] Merge the two generated .js into one library_bindgen.js. --- crates/cli-support/src/js/mod.rs | 44 ++++++++++++++------------------ crates/cli-support/src/lib.rs | 10 +++----- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index e7a3ae7969f..b31aadbeb78 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -235,12 +235,11 @@ impl<'a> Context<'a> { OutputMode::Emscripten => match export { ExportJs::Class(class) => { assert_eq!(export_name, definition_name); - format!("{}\nModule.{} = {};\n", class.replace("wasm", "wasmExports"), export_name, export_name) + format!("{}\nModule.{} = {};\n", class, export_name, export_name) } ExportJs::Function(function) => { let body = function.strip_prefix("function") - .unwrap() - .replace("wasm", "wasmExports"); + .unwrap(); if export_name == definition_name { format!("Module.{} = function{}\n", export_name, body) } else { @@ -252,7 +251,7 @@ impl<'a> Context<'a> { } ExportJs::Expression(expr) => { assert_eq!(export_name, definition_name); - format!("export const {} = {};\n", export_name, expr.replace("wasm", "wasmExports")) + format!("export const {} = {};\n", export_name, expr) } }, }; @@ -263,7 +262,7 @@ impl<'a> Context<'a> { pub fn finalize( &mut self, module_name: &str, - ) -> Result<(String, String, String, Option), Error> { + ) -> Result<(String, String, Option), Error> { // Finalize all bindings for JS classes. This is where we'll generate JS // glue for all classes as well as finish up a few final imports like // `__wrap` and such. @@ -468,10 +467,9 @@ impl<'a> Context<'a> { &mut self, module_name: &str, needs_manual_start: bool, - ) -> Result<(String, String, String, Option), Error> { + ) -> Result<(String, String, Option), Error> { let mut ts; let mut js = String::new(); - let mut js_emscripten_library = String::new(); let mut start = None; if let OutputMode::NoModules { global } = &self.config.mode { @@ -662,19 +660,18 @@ __wbg_set_wasm(wasm);" }; if matches!(self.config.mode, OutputMode::Emscripten) { - js_emscripten_library.push_str(&self.emscripten_library); - js_emscripten_library.push('\n'); - js_emscripten_library.push_str("var LibraryWbg = {\n"); - js_emscripten_library.push_str(&init_js); - js_emscripten_library.push_str( - "}; - \n - addToLibrary(LibraryWbg);"); - push_with_newline("var Module = {\n - onRuntimeInitialized: () => { + push_with_newline("var LibraryWbg = {\n"); + push_with_newline(&init_js); + push_with_newline("$initBindgen__postset: 'addOnInit(initBindgen);',"); + push_with_newline("$initBindgen: () => {\n wasmExports.__wbindgen_start();"); + self.globals = self.globals.replace("wasm", "wasmExports"); push_with_newline(&self.globals); - push_with_newline("}\n};"); + push_with_newline("},"); + push_with_newline( + "};\n + extraLibraryFuncs.push('$initBindgen'); + addToLibrary(LibraryWbg);"); } else { push_with_newline(&imports); push_with_newline(&self.imports_post); @@ -693,7 +690,7 @@ __wbg_set_wasm(wasm);" js = js.replace("\n\n\n", "\n\n"); } - Ok((js, js_emscripten_library, ts, start)) + Ok((js, ts, start)) } fn js_import_header(&self) -> Result { @@ -929,6 +926,7 @@ __wbg_set_wasm(wasm);" imports_init.push_str(",\n"); } else { imports_init.push_str(": (function() {\n"); + imports_init.push_str(&self.emscripten_library); imports_init.push_str("return "); imports_init.push_str(&js.trim().replace("wasm", "wasmExports")); imports_init.push_str(";\n"); @@ -1847,7 +1845,6 @@ __wbg_set_wasm(wasm);" self.global(&format!("const cached{0} = (typeof {0} !== 'undefined' ? new {0}{1} : {{ {2}: () => {{ throw Error('{0} not available') }} }} );", s, args, op)) } OutputMode::Emscripten => { - self.emscripten_library.push_str(&format!("const cached{0} = (typeof {0} !== 'undefined' ? new {0}{1} : {{ {2}: () => {{ throw Error('{0} not available') }} }} );\n\n", s, args, op)); } }; @@ -1864,10 +1861,7 @@ __wbg_set_wasm(wasm);" "if (typeof {} !== 'undefined') {{ {} }};", s, init )), - OutputMode::Emscripten => self.emscripten_library.push_str(&format!( - "if (typeof {} !== 'undefined') {{ {} }};\n\n", - s, init - )), + OutputMode::Emscripten => {}, } } @@ -1901,7 +1895,7 @@ __wbg_set_wasm(wasm);" " function {}(ptr, len) {{ ptr = ptr >>> 0; - return cachedTextDecoder.decode(HEAP8.{}(ptr, ptr + len)); + return UTF8Decoder.decode(HEAP8.{}(ptr, ptr + len)); }}\n ", ret, method diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index 0acc499223a..94adfc4663c 100755 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -51,7 +51,6 @@ pub struct Output { struct Generated { mode: OutputMode, js: String, - js_emscripten_library: String, ts: String, start: Option, snippets: HashMap>, @@ -441,7 +440,7 @@ impl Bindgen { .unwrap(); let mut cx = js::Context::new(&mut module, self, &adapters, &aux)?; cx.generate()?; - let (js, js_emscripten_library, ts, start) = cx.finalize(stem)?; + let (js, ts, start) = cx.finalize(stem)?; let generated = Generated { snippets: aux.snippets.clone(), local_modules: aux.local_modules.clone(), @@ -449,7 +448,6 @@ impl Bindgen { typescript: self.typescript, npm_dependencies: cx.npm_dependencies.clone(), js, - js_emscripten_library, ts, start, }; @@ -728,10 +726,8 @@ export * from \"./{js_name}\"; } write(out_dir.join(&js_name), reset_indentation(&gen.js))?; } else if matches!(self.generated.mode, OutputMode::Emscripten) { - let wbg_path = out_dir.join("library_wbg.js"); - let pre_js_path = out_dir.join("wbg_pre.js"); - write(&wbg_path, reset_indentation(&gen.js_emscripten_library))?; - write(&pre_js_path, reset_indentation(&gen.js))?; + let emscripten_js_path = out_dir.join("library_bindgen.js"); + write(&emscripten_js_path, reset_indentation(&gen.js))?; } else { write(&js_path, reset_indentation(&gen.js))?; } From c3a0e589d97a2dc456e3fe4626ff9c0b28f9ac61 Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Thu, 20 Feb 2025 15:29:57 -0500 Subject: [PATCH 07/17] Expand emscripten support to importing JS closures Notably, to work with emscripten, the emitted js has explicit dependencies listed for the imports. --- .cargo/config.toml | 8 + crates/cli-support/Cargo.toml | 1 + crates/cli-support/src/js/binding.rs | 9 +- crates/cli-support/src/js/mod.rs | 279 +++++++++++++++++++-------- 4 files changed, 214 insertions(+), 83 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 6440da0591e..8c0bf6db2fb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,10 @@ [target.'cfg(target_arch = "wasm32")'] runner = 'cargo run -p wasm-bindgen-cli --bin wasm-bindgen-test-runner --' + +[build] +[target.'cfg(all(target_arch = "wasm32", target_os = "emscripten"))'] +rustflags = [ + "-Cllvm-args=-enable-emscripten-cxx-exceptions=0", + "-Clink-arg=-Wno-undefined", + "-Crelocation-model=static", +] diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml index bb4ba82842e..1873b7711cb 100644 --- a/crates/cli-support/Cargo.toml +++ b/crates/cli-support/Cargo.toml @@ -22,6 +22,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tempfile = "3.0" walrus = { version = "0.23", features = ['parallel'] } +regex = "1" wasm-bindgen-externref-xform = { path = '../externref-xform', version = '=0.2.100' } wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.100' } wasm-bindgen-shared = { path = "../shared", version = '=0.2.100' } diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index 09501a67b40..7534072c5c1 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -130,6 +130,7 @@ impl<'a, 'b> Builder<'a, 'b> { debug_name: &str, ret_ty_override: &Option, ret_desc: &Option, + import_deps: &mut Vec, ) -> Result { if self .cx @@ -194,6 +195,7 @@ impl<'a, 'b> Builder<'a, 'b> { &instr.instr, &mut self.log_error, &self.constructor, + import_deps, )?; } @@ -727,6 +729,7 @@ fn instruction( instr: &Instruction, log_error: &mut bool, constructor: &Option, + import_deps: &mut Vec, ) -> Result<(), Error> { fn wasm_to_string_enum(name: &str, index: &str) -> String { // e.g. ["a","b","c"][someIndex] @@ -807,7 +810,7 @@ fn instruction( } // Call the function through an export of the underlying module. - let call = invoc.invoke(js.cx, &args, &mut js.prelude, log_error)?; + let call = invoc.invoke(js.cx, &args, &mut js.prelude, log_error, import_deps)?; // And then figure out how to actually handle where the call // happens. This is pretty conditional depending on the number of @@ -1637,6 +1640,7 @@ impl Invocation { args: &[String], prelude: &mut String, log_error: &mut bool, + import_deps: &mut Vec, ) -> Result { match self { Invocation::Core { id, .. } => { @@ -1656,7 +1660,8 @@ impl Invocation { if cx.import_never_log_error(import) { *log_error = false; } - cx.invoke_import(import, kind, args, variadic, prelude) + let ret = cx.invoke_import(import, kind, args, variadic, prelude, import_deps); + return ret } } } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index b31aadbeb78..2ab01c23166 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -18,12 +18,14 @@ use std::fs; use std::path::{Path, PathBuf}; use walrus::{FunctionId, ImportId, MemoryId, Module, TableId, ValType}; use wasm_bindgen_shared::identifier::is_valid_ident; +use regex::Regex; mod binding; pub struct Context<'a> { globals: String, emscripten_library: String, + emscripten_deps: HashSet, imports_post: String, typescript: String, exposed_globals: Option>>, @@ -148,6 +150,7 @@ impl<'a> Context<'a> { Ok(Context { globals: String::new(), emscripten_library: String::new(), + emscripten_deps: HashSet::new(), imports_post: String::new(), typescript: "/* tslint:disable */\n/* eslint-disable */\n".to_string(), exposed_globals: Some(Default::default()), @@ -659,19 +662,26 @@ __wbg_set_wasm(wasm);" } }; + let set_to_list = |set: &HashSet| -> Vec { + set.iter().cloned().collect() + }; + if matches!(self.config.mode, OutputMode::Emscripten) { push_with_newline("var LibraryWbg = {\n"); + push_with_newline(&self.emscripten_library); push_with_newline(&init_js); + push_with_newline("$initBindgen__deps: ['$addOnInit'],"); push_with_newline("$initBindgen__postset: 'addOnInit(initBindgen);',"); push_with_newline("$initBindgen: () => {\n wasmExports.__wbindgen_start();"); - self.globals = self.globals.replace("wasm", "wasmExports"); + self.globals = self.globals.replace("wasm.", "wasmExports."); push_with_newline(&self.globals); push_with_newline("},"); + let deps: Vec = set_to_list(&self.emscripten_deps); push_with_newline( - "};\n - extraLibraryFuncs.push('$initBindgen'); - addToLibrary(LibraryWbg);"); + &format!("}};\n + extraLibraryFuncs.push('$initBindgen','$addOnInit',{}); + addToLibrary(LibraryWbg);", deps.join(","))); } else { push_with_newline(&imports); push_with_newline(&self.imports_post); @@ -925,12 +935,9 @@ __wbg_set_wasm(wasm);" .replace("wasm", "wasmExports")); imports_init.push_str(",\n"); } else { - imports_init.push_str(": (function() {\n"); - imports_init.push_str(&self.emscripten_library); - imports_init.push_str("return "); + imports_init.push_str(": "); imports_init.push_str(&js.trim().replace("wasm", "wasmExports")); - imports_init.push_str(";\n"); - imports_init.push_str("}()),\n"); + imports_init.push_str(",\n"); } } } @@ -1893,10 +1900,10 @@ __wbg_set_wasm(wasm);" if matches!(self.config.mode, OutputMode::Emscripten) { self.emscripten_library.push_str(&format!( " - function {}(ptr, len) {{ + ${}(ptr, len) {{ ptr = ptr >>> 0; return UTF8Decoder.decode(HEAP8.{}(ptr, ptr + len)); - }}\n + }},\n ", ret, method )); @@ -2501,34 +2508,68 @@ __wbg_set_wasm(wasm);" // while we invoke it. If we finish and the closure wasn't // destroyed, then we put back the pointer so a future // invocation can succeed. - self.global(&format!( - " - function makeMutClosure(arg0, arg1, dtor, f) {{ - const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; - const real = (...args) => {{ - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - const a = state.a; - state.a = 0; - try {{ - return f(a, state.b, ...args); - }} finally {{ - if (--state.cnt === 0) {{ - wasm.{table}.get(state.dtor)(a, state.b); - CLOSURE_DTORS.unregister(state); - }} else {{ - state.a = a; + + if matches!(self.config.mode, OutputMode::Emscripten) { + self.emscripten_library.push_str(&format!( + " + $makeMutClosure: function(arg0, arg1, dtor, f) {{ + const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; + const real = (...args) => {{ + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try {{ + return f(a, state.b, ...args); + }} finally {{ + if (--state.cnt === 0) {{ + wasmExports.{table}.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + }} else {{ + state.a = a; + }} }} - }} - }}; - real.original = state; - CLOSURE_DTORS.register(real, state, state); - return real; - }} - ", - )); + }}; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; + }}, + $makeMutClosure__deps: ['$CLOSURE_DTORS'],\n + ", + )); + self.emscripten_deps.insert("'$CLOSURE_DTORS'".to_string()); + } else { + self.global(&format!( + " + function makeMutClosure(arg0, arg1, dtor, f) {{ + const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; + const real = (...args) => {{ + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + const a = state.a; + state.a = 0; + try {{ + return f(a, state.b, ...args); + }} finally {{ + if (--state.cnt === 0) {{ + wasm.{table}.get(state.dtor)(a, state.b); + CLOSURE_DTORS.unregister(state); + }} else {{ + state.a = a; + }} + }} + }}; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; + }},\n + ", + )); + } Ok(()) } @@ -2547,32 +2588,61 @@ __wbg_set_wasm(wasm);" // executing the destructor, however, we clear out the // `this.a` pointer to prevent it being used again the // future. - self.global(&format!( - " - function makeClosure(arg0, arg1, dtor, f) {{ - const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; - const real = (...args) => {{ - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - try {{ - return f(state.a, state.b, ...args); - }} finally {{ - if (--state.cnt === 0) {{ - wasm.{table}.get(state.dtor)(state.a, state.b); - state.a = 0; - CLOSURE_DTORS.unregister(state); + if matches!(self.config.mode, OutputMode::Emscripten) { + self.emscripten_library.push_str(&format!( + " + $makeClosure: function(arg0, arg1, dtor, f) {{ + const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; + const real = (...args) => {{ + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + try {{ + return f(state.a, state.b, ...args); + }} finally {{ + if (--state.cnt === 0) {{ + wasmExports.{table}.get(state.dtor)(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + }} }} - }} - }}; - real.original = state; - CLOSURE_DTORS.register(real, state, state); - return real; - }} - ", - )); - + }}; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; + }}, + $makeClosure__deps: ['$CLOSURE_DTORS'],\n + " + )); + self.emscripten_deps.insert("'$CLOSURE_DTORS'".to_string()); + } else { + self.global(&format!( + " + function makeClosure(arg0, arg1, dtor, f) {{ + const state = {{ a: arg0, b: arg1, cnt: 1, dtor }}; + const real = (...args) => {{ + // First up with a closure we increment the internal reference + // count. This ensures that the Rust closure environment won't + // be deallocated while we're invoking it. + state.cnt++; + try {{ + return f(state.a, state.b, ...args); + }} finally {{ + if (--state.cnt === 0) {{ + wasm.{table}.get(state.dtor)(state.a, state.b); + state.a = 0; + CLOSURE_DTORS.unregister(state); + }} + }} + }}; + real.original = state; + CLOSURE_DTORS.register(real, state, state); + return real; + }} + ", + )); + } Ok(()) } @@ -2581,16 +2651,28 @@ __wbg_set_wasm(wasm);" return Ok(()); } let table = self.export_function_table()?; - self.global(&format!( - " - const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') - ? {{ register: () => {{}}, unregister: () => {{}} }} + if matches!(self.config.mode, OutputMode::Emscripten) { + self.emscripten_library.push_str(&format!( + " + $CLOSURE_DTORS: `(typeof FinalizationRegistry === 'undefined') + ? {{ register: () => {{}}, unregister: () => {{}} }} : new FinalizationRegistry(state => {{ - wasm.{table}.get(state.dtor)(state.a, state.b) - }}); - " - )); - + wasmExports.__indirect_function_table.get(state.dtor)(state.a, state.b) + }})`,\n + " + )); + + } else { + self.global(&format!( + " + const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') + ? {{ register: () => {{}}, unregister: () => {{}} }} + : new FinalizationRegistry(state => {{ + wasm.{table}.get(state.dtor)(state.a, state.b) + }}); + " + )); + } Ok(()) } fn global(&mut self, s: &str) { @@ -2956,7 +3038,7 @@ __wbg_set_wasm(wasm);" ContextAdapterKind::Export(e) => format!("`{}`", e.debug_name), ContextAdapterKind::Adapter => format!("adapter {}", id.0), }; - + let mut import_deps: Vec = Default::default(); // Process the `binding` and generate a bunch of JS/TypeScript/etc. let binding::JsFunction { ts_sig, @@ -2980,6 +3062,7 @@ __wbg_set_wasm(wasm);" &debug_name, ret_ty_override, ret_desc, + &mut import_deps, ) .with_context(|| "failed to generates bindings for ".to_string() + &debug_name)?; @@ -3104,19 +3187,36 @@ __wbg_set_wasm(wasm);" code ) } else { - format!("function{}", code) + if !import_deps.is_empty() { + for dep in &import_deps{ + self.emscripten_deps.insert(dep.clone()); + } + format!("function{},\n{}__deps: [{}]", code, self.module.imports.get(core).name, import_deps.join(",")) + } + else { + format!("function{}\n",code) + } }; + self.wasm_import_definitions.insert(core, code); } ContextAdapterKind::Adapter => { assert!(!catch); assert!(!log_error); - self.globals.push_str("function "); - self.globals.push_str(&self.adapter_name(id)); - self.globals.push_str(&code); - self.globals.push_str("\n\n"); + if matches!(self.config.mode, OutputMode::Emscripten) { + self.emscripten_library.push_str("$"); + self.emscripten_library.push_str(&self.adapter_name(id)); + self.emscripten_library.push_str(": function"); + self.emscripten_library.push_str(&code.replace("wasm.", "wasmExports.")); + self.emscripten_library.push_str(",\n\n"); + } else { + self.globals.push_str("function "); + self.globals.push_str(&self.adapter_name(id)); + self.globals.push_str(&code); + self.globals.push_str("\n\n"); + } } } Ok(()) @@ -3340,6 +3440,7 @@ __wbg_set_wasm(wasm);" args: &[String], variadic: bool, prelude: &mut String, + import_deps: &mut Vec, ) -> Result { let variadic_args = |js_arguments: &[String]| { Ok(if !variadic { @@ -3356,6 +3457,18 @@ __wbg_set_wasm(wasm);" } }) }; + + // Emscripten needs the function name extracted from the full function call here + // to construct dependency lists for each import. + if matches!(self.config.mode, OutputMode::Emscripten) { + let re = Regex::new(r"([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(").unwrap(); + for arg in args { + if let Some(result) = re.captures(arg) { + import_deps.push(format!("'${}'", &result[1])); + } + } + } + match import { AuxImport::Value(val) => match kind { AdapterJsImportKind::Constructor => { @@ -3467,10 +3580,12 @@ __wbg_set_wasm(wasm);" assert_eq!(args.len(), 3); let call = self.adapter_name(*adapter); - + import_deps.push(format!("'${}'", call)); if *mutable { self.expose_make_mut_closure()?; - + if matches!(self.config.mode, OutputMode::Emscripten) { + import_deps.push("'$makeMutClosure'".to_string()); + } Ok(format!( "makeMutClosure({arg0}, {arg1}, {dtor}, {call})", arg0 = &args[0], @@ -3480,7 +3595,9 @@ __wbg_set_wasm(wasm);" )) } else { self.expose_make_closure()?; - + if matches!(self.config.mode, OutputMode::Emscripten) { + import_deps.push("'$makeClosure'".to_string()); + } Ok(format!( "makeClosure({arg0}, {arg1}, {dtor}, {call})", arg0 = &args[0], From 948df4bf7b83b3e87fdb2c140d51a5f8ac266f71 Mon Sep 17 00:00:00 2001 From: google-yfyang Date: Mon, 24 Feb 2025 15:37:50 -0500 Subject: [PATCH 08/17] Add the necessary emscripten test mode. Because the generated .js file when targeting emscripten in wasm-bindgen is meant to be consumed by emscripten rather than a standalone executable, we need some custom testing logic for emscripten. --- crates/cli-support/src/js/mod.rs | 2 +- .../emscripten_test.js | 55 ++++++++++++ .../index-emscripten.html | 14 +++ .../src/bin/wasm-bindgen-test-runner/main.rs | 36 +++++++- .../bin/wasm-bindgen-test-runner/server.rs | 87 ++++++++++++++----- crates/test/src/lib.rs | 8 ++ tests/headless/main.rs | 2 +- tests/wasm/main.rs | 2 +- tests/wasm32-emscripten/main.rs | 57 ++++++++++++ 9 files changed, 233 insertions(+), 30 deletions(-) create mode 100644 crates/cli/src/bin/wasm-bindgen-test-runner/emscripten_test.js create mode 100644 crates/cli/src/bin/wasm-bindgen-test-runner/index-emscripten.html create mode 100644 tests/wasm32-emscripten/main.rs diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 2ab01c23166..750d16d12ad 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2566,7 +2566,7 @@ __wbg_set_wasm(wasm);" real.original = state; CLOSURE_DTORS.register(real, state, state); return real; - }},\n + }} ", )); } diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/emscripten_test.js b/crates/cli/src/bin/wasm-bindgen-test-runner/emscripten_test.js new file mode 100644 index 00000000000..56427051f41 --- /dev/null +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/emscripten_test.js @@ -0,0 +1,55 @@ +(function() {{ + var elem = document.querySelector('#output'); + window.extraLibraryFuncs = []; + window.addToLibrary = function(LibraryWbg) { + window.wasmExports = {__wbindgen_start:() => {}}; + window.cachedTextEncoder = {encodeInto:() => {}}; + window.Module = {}; + + try { + LibraryWbg.$initBindgen(); + } catch (e) { + elem.innerText = 'test setup failed: ' + e; + } + + function testExtraLibraryFuncs () { + ['$initBindgen', '$addOnInit', '$CLOSURE_DTORS', '$getStringFromWasm0'].forEach((value) => { + if (!extraLibraryFuncs.includes(value)) { + return { status: false, e: `test result: ${value} not found`}; + } + }); + return {status: true, e: 'test result: ok'}; + } + + function testLibraryWbg () { + if (typeof Module.hello !== 'function') { + return {status: false, e:'test result: hello() is not found'}; + } + if (typeof Module.Interval !== 'function') { + return {status: false, e:'test result: Interval is not found'}; + } + + const keys = Object.keys(LibraryWbg); + const testNames = ['clearInterval', 'setInterval', 'log']; + + for (const name of testNames) { + const regex = new RegExp(`^__wbg_${name}`); + const res = keys.find(key => regex.test(key)); + if (!res) { + return {status: false, e:`test result: ${name} not found`}; + } + } + return {status: true, e:'test result: ok'}; + } + + const tests = [testExtraLibraryFuncs(), testLibraryWbg()]; + for (const res of tests) { + if (!res.status) { + elem.innerText = res.e; + return; + } + } + elem.innerText = 'test result: ok'; + + }; +}}()); \ No newline at end of file diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/index-emscripten.html b/crates/cli/src/bin/wasm-bindgen-test-runner/index-emscripten.html new file mode 100644 index 00000000000..a931b750a7a --- /dev/null +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/index-emscripten.html @@ -0,0 +1,14 @@ + + + + + + +
Loading scripts...
+

+    

+    

+    

+    
+  
+
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
index a4b8c58de2b..1cfcacbf6c3 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
@@ -111,16 +111,27 @@ fn main() -> anyhow::Result<()> {
 
     let shell = shell::Shell::new();
 
-    let file_name = cli
+    let mut file_name = cli
         .file
         .file_name()
         .map(Path::new)
         .context("file to test is not a valid file, can't extract file name")?;
 
+    let mut file_name_buf= PathBuf::from(cli.file.clone());
+
+    // Repoint the file to be read from "name.js" to "name.wasm" in the case of emscripten.
+    // Rustc generates a .js and a .wasm file when targeting emscripten. It lists the .js
+    // file as the primary executor which is inconsitent with what is expected here.
+    if file_name.extension().unwrap() == "js" {
+        file_name_buf.pop();
+        file_name_buf.push(file_name.file_stem().unwrap());
+        file_name_buf.set_extension("wasm");
+        file_name = Path::new(&file_name_buf);
+    }
     // Collect all tests that the test harness is supposed to run. We assume
     // that any exported function with the prefix `__wbg_test` is a test we need
     // to execute.
-    let wasm = fs::read(&cli.file).context("failed to read Wasm file")?;
+    let wasm = fs::read(&file_name_buf).context("failed to read Wasm file")?;
     let mut wasm =
         walrus::Module::from_buffer(&wasm).context("failed to deserialize Wasm module")?;
     let mut tests = Tests::new();
@@ -203,6 +214,7 @@ fn main() -> anyhow::Result<()> {
         Some(section) if section.data.contains(&0x03) => TestMode::SharedWorker { no_modules },
         Some(section) if section.data.contains(&0x04) => TestMode::ServiceWorker { no_modules },
         Some(section) if section.data.contains(&0x05) => TestMode::Node { no_modules },
+        Some(section) if section.data.contains(&0x06) => TestMode::Emscripten {},
         Some(_) => bail!("invalid __wasm_bingen_test_unstable value"),
         None => {
             let mut modes = Vec::new();
@@ -295,6 +307,9 @@ fn main() -> anyhow::Result<()> {
             } else {
                 b.web(true)?
             }
+        },
+        TestMode::Emscripten {} => {
+            b.emscripten(true)?
         }
     };
 
@@ -316,6 +331,19 @@ fn main() -> anyhow::Result<()> {
         TestMode::Node { no_modules } => {
             node::execute(module, tmpdir.path(), cli, tests, !no_modules, coverage)?
         }
+        TestMode::Emscripten => {
+            let srv = server::spawn_emscripten(
+                &"127.0.0.1:0".parse().unwrap(), 
+                tmpdir.path(), 
+                std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err()).context("failed to spawn server")?;
+            let addr = srv.server_addr();
+            println!(
+                "Tests are now available at http://{}",
+                addr
+            );
+            thread::spawn(|| srv.run());
+            headless::run(&addr, &shell, driver_timeout, browser_timeout)?;
+        }
         TestMode::Deno => deno::execute(module, tmpdir.path(), cli, tests)?,
         TestMode::Browser { .. }
         | TestMode::DedicatedWorker { .. }
@@ -372,6 +400,7 @@ enum TestMode {
     DedicatedWorker { no_modules: bool },
     SharedWorker { no_modules: bool },
     ServiceWorker { no_modules: bool },
+    Emscripten,
 }
 
 impl TestMode {
@@ -384,7 +413,7 @@ impl TestMode {
 
     fn no_modules(self) -> bool {
         match self {
-            Self::Deno => true,
+            Self::Deno | Self::Emscripten => true,
             Self::Browser { no_modules }
             | Self::Node { no_modules }
             | Self::DedicatedWorker { no_modules }
@@ -401,6 +430,7 @@ impl TestMode {
             TestMode::DedicatedWorker { .. } => "WASM_BINDGEN_USE_DEDICATED_WORKER",
             TestMode::SharedWorker { .. } => "WASM_BINDGEN_USE_SHARED_WORKER",
             TestMode::ServiceWorker { .. } => "WASM_BINDGEN_USE_SERVICE_WORKER",
+            TestMode::Emscripten { .. } => "WASM_BINDGEN_USE_EMSCRIPTEN",
         }
     }
 }
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
index f5b088a17b6..9f7c31ff3ab 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
@@ -353,37 +353,76 @@ pub(crate) fn spawn(
         response
     })
     .map_err(|e| anyhow!("{}", e))?;
-    return Ok(srv);
+    return Ok(srv);    
+}
+
+pub(crate) fn spawn_emscripten(
+    addr: &SocketAddr,
+    tmpdir: &Path,
+    isolate_origin: bool,
+) -> Result Response + Send + Sync>, Error> {
+    let js_path = tmpdir.join("run.js");
+    fs::write(js_path, include_str!("emscripten_test.js")).context("failed to write JS file")?;
+    let tmpdir = tmpdir.to_path_buf();
+    let srv = Server::new(addr, move |request| {
+        if request.url() == "/" {
+            let s = 
+                include_str!("index-emscripten.html");
+            let s = 
+                s.replace(
+                    "",
+                    "\n     ",
+                );
+
+            let response = Response::from_data("text/html", s);
 
-    fn try_asset(request: &Request, dir: &Path) -> Response {
-        let response = rouille::match_assets(request, dir);
-        if response.is_success() {
             return response;
         }
 
-        // When a browser is doing ES imports it's using the directives we
-        // write in the code that *don't* have file extensions (aka we say `from
-        // 'foo'` instead of `from 'foo.js'`. Fixup those paths here to see if a
-        // `js` file exists.
-        if let Some(part) = request.url().split('/').last() {
-            if !part.contains('.') {
-                let new_request = Request::fake_http(
-                    request.method(),
-                    format!("{}.js", request.url()),
-                    request
-                        .headers()
-                        .map(|(a, b)| (a.to_string(), b.to_string()))
-                        .collect(),
-                    Vec::new(),
-                );
-                let response = rouille::match_assets(&new_request, dir);
-                if response.is_success() {
-                    return response;
-                }
-            }
+        let mut response = try_asset(request, &tmpdir);
+        if !response.is_success() {
+            response = try_asset(request, ".".as_ref());
+        }
+        // Make sure browsers don't cache anything (Chrome appeared to with this
+        // header?)
+        response.headers.retain(|(k, _)| k != "Cache-Control");
+        if isolate_origin {
+            set_isolate_origin_headers(&mut response)
         }
         response
+    })
+    .map_err(|e| anyhow!("{}", e))?;
+    return Ok(srv);
+}
+
+fn try_asset(request: &Request, dir: &Path) -> Response {
+    let response = rouille::match_assets(request, dir);
+    if response.is_success() {
+        return response;
+    }
+
+    // When a browser is doing ES imports it's using the directives we
+    // write in the code that *don't* have file extensions (aka we say `from
+    // 'foo'` instead of `from 'foo.js'`. Fixup those paths here to see if a
+    // `js` file exists.
+    if let Some(part) = request.url().split('/').last() {
+        if !part.contains('.') {
+            let new_request = Request::fake_http(
+                request.method(),
+                format!("{}.js", request.url()),
+                request
+                    .headers()
+                    .map(|(a, b)| (a.to_string(), b.to_string()))
+                    .collect(),
+                Vec::new(),
+            );
+            let response = rouille::match_assets(&new_request, dir);
+            if response.is_success() {
+                return response;
+            }
+        }
     }
+    response
 }
 
 fn handle_coverage_dump(profraw_path: &Path, request: &Request) -> anyhow::Result<()> {
diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs
index 36ee18248c9..f8de117fc2e 100644
--- a/crates/test/src/lib.rs
+++ b/crates/test/src/lib.rs
@@ -106,6 +106,14 @@ macro_rules! wasm_bindgen_test_configure {
             $crate::wasm_bindgen_test_configure!($($others)*);
         };
     );
+    (run_in_emscripten $($others:tt)*) => (
+        const _: () = {
+            #[link_section = "__wasm_bindgen_test_unstable"]
+            #[cfg(target_arch = "wasm32")]
+            pub static __WBG_TEST_run_in_emscripten: [u8; 1] = [0x06];
+            $crate::wasm_bindgen_test_configure!($($others)*);
+        };
+    );
     () => ()
 }
 
diff --git a/tests/headless/main.rs b/tests/headless/main.rs
index b5f6f12a0c7..79199af3db2 100644
--- a/tests/headless/main.rs
+++ b/tests/headless/main.rs
@@ -1,4 +1,4 @@
-#![cfg(target_arch = "wasm32")]
+#![cfg(all(target_arch = "wasm32", target_os = "unknown"))]
 
 extern crate wasm_bindgen;
 extern crate wasm_bindgen_test;
diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs
index b5640177fc1..325af948b2e 100644
--- a/tests/wasm/main.rs
+++ b/tests/wasm/main.rs
@@ -1,4 +1,4 @@
-#![cfg(target_arch = "wasm32")]
+#![cfg(all(target_arch = "wasm32", target_os = "unknown"))]
 #![allow(renamed_and_removed_lints)] // clippy::drop_ref will be renamed to drop_ref
 #![allow(clippy::drop_ref, clippy::drop_non_drop)]
 
diff --git a/tests/wasm32-emscripten/main.rs b/tests/wasm32-emscripten/main.rs
new file mode 100644
index 00000000000..9cb1296606d
--- /dev/null
+++ b/tests/wasm32-emscripten/main.rs
@@ -0,0 +1,57 @@
+#![cfg(all(target_arch = "wasm32", target_os = "emscripten"))]
+
+extern crate wasm_bindgen;
+extern crate wasm_bindgen_test;
+
+use wasm_bindgen::prelude::*;
+use wasm_bindgen_test::*;
+
+wasm_bindgen_test_configure!(run_in_emscripten);
+
+#[wasm_bindgen]
+extern "C" {
+    fn setInterval(closure: &Closure, millis: u32) -> f64;
+    fn clearInterval(token: f64);
+
+    #[wasm_bindgen(js_namespace = console)]
+    fn log(s: &str);
+}
+
+#[wasm_bindgen]
+pub struct Interval {
+    closure: Closure,
+    token: f64,
+}
+
+impl Interval {
+    pub fn new(millis: u32, f: F) -> Interval
+    where
+        F: FnMut()
+    {
+        // Construct a new closure.
+        let closure = Closure::new(f);
+
+        // Pass the closure to JS, to run every n milliseconds.
+        let token = setInterval(&closure, millis);
+
+        Interval { closure, token }
+    }
+}
+
+// When the Interval is destroyed, clear its `setInterval` timer.
+impl Drop for Interval {
+    fn drop(&mut self) {
+        clearInterval(self.token);
+    }
+}
+
+// Keep logging "hello" every second until the resulting `Interval` is dropped.
+#[wasm_bindgen]
+pub fn hello() -> Interval {
+    Interval::new(1_000, || log("hello"))
+}
+
+#[wasm_bindgen_test]
+fn hello_test() {
+    hello();
+}

From 718b6feb446afbfccb8329660e27ddb1e0b769b1 Mon Sep 17 00:00:00 2001
From: Mitch Foley 
Date: Thu, 27 Feb 2025 16:24:56 -0500
Subject: [PATCH 09/17] run cargo fmt

---
 crates/cli-support/src/js/binding.rs          |  2 +-
 crates/cli-support/src/js/mod.rs              | 65 ++++++++++---------
 crates/cli-support/src/lib.rs                 |  2 +-
 .../src/bin/wasm-bindgen-test-runner/main.rs  | 19 +++---
 .../bin/wasm-bindgen-test-runner/server.rs    | 14 ++--
 src/lib.rs                                    | 40 ++++++++++--
 tests/wasm32-emscripten/main.rs               |  2 +-
 7 files changed, 85 insertions(+), 59 deletions(-)

diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs
index 7534072c5c1..0ff53fdcf91 100644
--- a/crates/cli-support/src/js/binding.rs
+++ b/crates/cli-support/src/js/binding.rs
@@ -1661,7 +1661,7 @@ impl Invocation {
                     *log_error = false;
                 }
                 let ret = cx.invoke_import(import, kind, args, variadic, prelude, import_deps);
-                return ret
+                return ret;
             }
         }
     }
diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index 750d16d12ad..62213b712ac 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -10,6 +10,7 @@ use crate::wit::{JsImport, JsImportName, NonstandardWitSection, WasmBindgenAux};
 use crate::{reset_indentation, Bindgen, EncodeInto, OutputMode, PLACEHOLDER_MODULE};
 use anyhow::{anyhow, bail, Context as _, Error};
 use binding::TsReference;
+use regex::Regex;
 use std::borrow::Cow;
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
 use std::fmt;
@@ -18,7 +19,6 @@ use std::fs;
 use std::path::{Path, PathBuf};
 use walrus::{FunctionId, ImportId, MemoryId, Module, TableId, ValType};
 use wasm_bindgen_shared::identifier::is_valid_ident;
-use regex::Regex;
 
 mod binding;
 
@@ -241,8 +241,7 @@ impl<'a> Context<'a> {
                     format!("{}\nModule.{} = {};\n", class, export_name, export_name)
                 }
                 ExportJs::Function(function) => {
-                    let body = function.strip_prefix("function")
-                                .unwrap();
+                    let body = function.strip_prefix("function").unwrap();
                     if export_name == definition_name {
                         format!("Module.{} = function{}\n", export_name, body)
                     } else {
@@ -662,9 +661,7 @@ __wbg_set_wasm(wasm);"
             }
         };
 
-        let set_to_list = |set: &HashSet| -> Vec {
-            set.iter().cloned().collect()
-        };
+        let set_to_list = |set: &HashSet| -> Vec { set.iter().cloned().collect() };
 
         if matches!(self.config.mode, OutputMode::Emscripten) {
             push_with_newline("var LibraryWbg = {\n");
@@ -672,16 +669,20 @@ __wbg_set_wasm(wasm);"
             push_with_newline(&init_js);
             push_with_newline("$initBindgen__deps: ['$addOnInit'],");
             push_with_newline("$initBindgen__postset: 'addOnInit(initBindgen);',");
-            push_with_newline("$initBindgen: () => {\n
-    wasmExports.__wbindgen_start();");
+            push_with_newline(
+                "$initBindgen: () => {\n
+    wasmExports.__wbindgen_start();",
+            );
             self.globals = self.globals.replace("wasm.", "wasmExports.");
             push_with_newline(&self.globals);
             push_with_newline("},");
-            let deps: Vec = set_to_list(&self.emscripten_deps); 
-            push_with_newline(
-                &format!("}};\n
+            let deps: Vec = set_to_list(&self.emscripten_deps);
+            push_with_newline(&format!(
+                "}};\n
                 extraLibraryFuncs.push('$initBindgen','$addOnInit',{});
-                addToLibrary(LibraryWbg);", deps.join(",")));
+                addToLibrary(LibraryWbg);",
+                deps.join(",")
+            ));
         } else {
             push_with_newline(&imports);
             push_with_newline(&self.imports_post);
@@ -928,16 +929,17 @@ __wbg_set_wasm(wasm);"
                 imports_init.push_str(&import.name);
                 if import.name == "__wbindgen_init_externref_table" {
                     imports_init.push_str(": () =>");
-                    imports_init.push_str(&js
-                        .trim()
-                        .strip_prefix("function()")
-                        .unwrap()
-                        .replace("wasm", "wasmExports"));
+                    imports_init.push_str(
+                        &js.trim()
+                            .strip_prefix("function()")
+                            .unwrap()
+                            .replace("wasm", "wasmExports"),
+                    );
                     imports_init.push_str(",\n");
                 } else {
-                   imports_init.push_str(": ");
-                   imports_init.push_str(&js.trim().replace("wasm", "wasmExports"));
-                   imports_init.push_str(",\n");
+                    imports_init.push_str(": ");
+                    imports_init.push_str(&js.trim().replace("wasm", "wasmExports"));
+                    imports_init.push_str(",\n");
                 }
             }
         }
@@ -984,7 +986,7 @@ __wbg_set_wasm(wasm);"
             }
         }
 
-         let js = match &self.config.mode {
+        let js = match &self.config.mode {
             OutputMode::Emscripten => format!(
             "\
                     {imports_init}",
@@ -1868,7 +1870,7 @@ __wbg_set_wasm(wasm);"
                     "if (typeof {} !== 'undefined') {{ {} }};",
                     s, init
                 )),
-                OutputMode::Emscripten => {},
+                OutputMode::Emscripten => {}
             }
         }
 
@@ -2661,7 +2663,6 @@ __wbg_set_wasm(wasm);"
                 }})`,\n
                 "
             ));
-           
         } else {
             self.global(&format!(
                 "
@@ -3188,17 +3189,20 @@ __wbg_set_wasm(wasm);"
                     )
                 } else {
                     if !import_deps.is_empty() {
-                        for dep in &import_deps{
+                        for dep in &import_deps {
                             self.emscripten_deps.insert(dep.clone());
                         }
-                        format!("function{},\n{}__deps:  [{}]", code, self.module.imports.get(core).name, import_deps.join(","))
-                    }
-                    else {
-                        format!("function{}\n",code)
+                        format!(
+                            "function{},\n{}__deps:  [{}]",
+                            code,
+                            self.module.imports.get(core).name,
+                            import_deps.join(",")
+                        )
+                    } else {
+                        format!("function{}\n", code)
                     }
                 };
 
-
                 self.wasm_import_definitions.insert(core, code);
             }
             ContextAdapterKind::Adapter => {
@@ -3209,7 +3213,8 @@ __wbg_set_wasm(wasm);"
                     self.emscripten_library.push_str("$");
                     self.emscripten_library.push_str(&self.adapter_name(id));
                     self.emscripten_library.push_str(": function");
-                    self.emscripten_library.push_str(&code.replace("wasm.", "wasmExports."));
+                    self.emscripten_library
+                        .push_str(&code.replace("wasm.", "wasmExports."));
                     self.emscripten_library.push_str(",\n\n");
                 } else {
                     self.globals.push_str("function ");
diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs
index 94adfc4663c..8ddffc5713a 100755
--- a/crates/cli-support/src/lib.rs
+++ b/crates/cli-support/src/lib.rs
@@ -66,7 +66,7 @@ enum OutputMode {
     NoModules { global: String },
     Node { module: bool },
     Deno,
-    Emscripten,    
+    Emscripten,
 }
 
 enum Input {
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
index 1cfcacbf6c3..901b7f723e5 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
@@ -117,7 +117,7 @@ fn main() -> anyhow::Result<()> {
         .map(Path::new)
         .context("file to test is not a valid file, can't extract file name")?;
 
-    let mut file_name_buf= PathBuf::from(cli.file.clone());
+    let mut file_name_buf = PathBuf::from(cli.file.clone());
 
     // Repoint the file to be read from "name.js" to "name.wasm" in the case of emscripten.
     // Rustc generates a .js and a .wasm file when targeting emscripten. It lists the .js
@@ -307,10 +307,8 @@ fn main() -> anyhow::Result<()> {
             } else {
                 b.web(true)?
             }
-        },
-        TestMode::Emscripten {} => {
-            b.emscripten(true)?
         }
+        TestMode::Emscripten {} => b.emscripten(true)?,
     };
 
     if std::env::var("WASM_BINDGEN_SPLIT_LINKED_MODULES").is_ok() {
@@ -333,14 +331,13 @@ fn main() -> anyhow::Result<()> {
         }
         TestMode::Emscripten => {
             let srv = server::spawn_emscripten(
-                &"127.0.0.1:0".parse().unwrap(), 
-                tmpdir.path(), 
-                std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err()).context("failed to spawn server")?;
+                &"127.0.0.1:0".parse().unwrap(),
+                tmpdir.path(),
+                std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err(),
+            )
+            .context("failed to spawn server")?;
             let addr = srv.server_addr();
-            println!(
-                "Tests are now available at http://{}",
-                addr
-            );
+            println!("Tests are now available at http://{}", addr);
             thread::spawn(|| srv.run());
             headless::run(&addr, &shell, driver_timeout, browser_timeout)?;
         }
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
index 9f7c31ff3ab..5f69079ca17 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
@@ -353,7 +353,7 @@ pub(crate) fn spawn(
         response
     })
     .map_err(|e| anyhow!("{}", e))?;
-    return Ok(srv);    
+    return Ok(srv);
 }
 
 pub(crate) fn spawn_emscripten(
@@ -366,13 +366,11 @@ pub(crate) fn spawn_emscripten(
     let tmpdir = tmpdir.to_path_buf();
     let srv = Server::new(addr, move |request| {
         if request.url() == "/" {
-            let s = 
-                include_str!("index-emscripten.html");
-            let s = 
-                s.replace(
-                    "",
-                    "\n     ",
-                );
+            let s = include_str!("index-emscripten.html");
+            let s = s.replace(
+                "",
+                "\n     ",
+            );
 
             let response = Response::from_data("text/html", s);
 
diff --git a/src/lib.rs b/src/lib.rs
index 2a60c1dfa28..cd6743ffcd3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1408,7 +1408,10 @@ pub trait UnwrapThrowExt: Sized {
     #[cfg_attr(
         any(
             debug_assertions,
-            not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))
+            not(all(
+                target_arch = "wasm32",
+                any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            ))
         ),
         track_caller
     )]
@@ -1417,7 +1420,11 @@ pub trait UnwrapThrowExt: Sized {
             debug_assertions,
             all(
                 target_arch = "wasm32",
-                any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+                any(
+                    target_os = "unknown",
+                    target_os = "none",
+                    target_os = "emscripten"
+                )
             )
         )) {
             let loc = core::panic::Location::caller();
@@ -1440,7 +1447,10 @@ pub trait UnwrapThrowExt: Sized {
     #[cfg_attr(
         any(
             debug_assertions,
-            not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none", target_os = "emscripten")))
+            not(all(
+                target_arch = "wasm32",
+                any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            ))
         ),
         track_caller
     )]
@@ -1453,7 +1463,11 @@ impl UnwrapThrowExt for Option {
 
         if cfg!(all(
             target_arch = "wasm32",
-            any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            any(
+                target_os = "unknown",
+                target_os = "none",
+                target_os = "emscripten"
+            )
         )) {
             if let Some(val) = self {
                 val
@@ -1474,7 +1488,11 @@ impl UnwrapThrowExt for Option {
     fn expect_throw(self, message: &str) -> T {
         if cfg!(all(
             target_arch = "wasm32",
-            any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            any(
+                target_os = "unknown",
+                target_os = "none",
+                target_os = "emscripten"
+            )
         )) {
             if let Some(val) = self {
                 val
@@ -1507,7 +1525,11 @@ where
 
         if cfg!(all(
             target_arch = "wasm32",
-            any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            any(
+                target_os = "unknown",
+                target_os = "none",
+                target_os = "emscripten"
+            )
         )) {
             match self {
                 Ok(val) => val,
@@ -1537,7 +1559,11 @@ where
     fn expect_throw(self, message: &str) -> T {
         if cfg!(all(
             target_arch = "wasm32",
-            any(target_os = "unknown", target_os = "none", target_os = "emscripten")
+            any(
+                target_os = "unknown",
+                target_os = "none",
+                target_os = "emscripten"
+            )
         )) {
             match self {
                 Ok(val) => val,
diff --git a/tests/wasm32-emscripten/main.rs b/tests/wasm32-emscripten/main.rs
index 9cb1296606d..8cc8059baeb 100644
--- a/tests/wasm32-emscripten/main.rs
+++ b/tests/wasm32-emscripten/main.rs
@@ -26,7 +26,7 @@ pub struct Interval {
 impl Interval {
     pub fn new(millis: u32, f: F) -> Interval
     where
-        F: FnMut()
+        F: FnMut(),
     {
         // Construct a new closure.
         let closure = Closure::new(f);

From 9130158dfe730a80c7d657bc81603d2063c69f09 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Tue, 4 Mar 2025 12:48:11 -0500
Subject: [PATCH 10/17] fix CI errors in wasm-bindgen

---
 .cargo/config.toml                            |  7 ++--
 crates/cli-support/Cargo.toml                 |  2 +-
 crates/cli-support/src/js/binding.rs          |  3 +-
 crates/cli-support/src/js/mod.rs              | 40 +++++++++----------
 .../src/bin/wasm-bindgen-test-runner/main.rs  |  2 +-
 .../bin/wasm-bindgen-test-runner/server.rs    |  4 +-
 src/convert/impls.rs                          |  4 +-
 7 files changed, 29 insertions(+), 33 deletions(-)

diff --git a/.cargo/config.toml b/.cargo/config.toml
index 8c0bf6db2fb..fa4ad575c1f 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -2,9 +2,10 @@
 runner = 'cargo run -p wasm-bindgen-cli --bin wasm-bindgen-test-runner --'
 
 [build]
+
 [target.'cfg(all(target_arch = "wasm32", target_os = "emscripten"))']
 rustflags = [
-    "-Cllvm-args=-enable-emscripten-cxx-exceptions=0", 
-    "-Clink-arg=-Wno-undefined",    
-    "-Crelocation-model=static",                                                                                                                                                                                                     
+  "-Cllvm-args=-enable-emscripten-cxx-exceptions=0",
+  "-Clink-arg=-Wno-undefined",
+  "-Crelocation-model=static",
 ]
diff --git a/crates/cli-support/Cargo.toml b/crates/cli-support/Cargo.toml
index 1873b7711cb..f0257ae60f7 100644
--- a/crates/cli-support/Cargo.toml
+++ b/crates/cli-support/Cargo.toml
@@ -17,12 +17,12 @@ version = "0.2.100"
 anyhow = "1.0"
 base64 = "0.22"
 log = "0.4"
+regex = "1"
 rustc-demangle = "0.1.13"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 tempfile = "3.0"
 walrus = { version = "0.23", features = ['parallel'] }
-regex = "1"
 wasm-bindgen-externref-xform = { path = '../externref-xform', version = '=0.2.100' }
 wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.100' }
 wasm-bindgen-shared = { path = "../shared", version = '=0.2.100' }
diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs
index 0ff53fdcf91..42a5f33203f 100644
--- a/crates/cli-support/src/js/binding.rs
+++ b/crates/cli-support/src/js/binding.rs
@@ -1660,8 +1660,7 @@ impl Invocation {
                 if cx.import_never_log_error(import) {
                     *log_error = false;
                 }
-                let ret = cx.invoke_import(import, kind, args, variadic, prelude, import_deps);
-                return ret;
+                cx.invoke_import(import, kind, args, variadic, prelude, import_deps)
             }
         }
     }
diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index 62213b712ac..006af1ffdc1 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -987,11 +987,7 @@ __wbg_set_wasm(wasm);"
         }
 
         let js = match &self.config.mode {
-            OutputMode::Emscripten => format!(
-            "\
-                    {imports_init}",
-                imports_init = imports_init
-            ),
+            OutputMode::Emscripten => imports_init.to_string(),
             _ => format!(
             "\
                 async function __wbg_load(module, imports) {{
@@ -2654,15 +2650,15 @@ __wbg_set_wasm(wasm);"
         }
         let table = self.export_function_table()?;
         if matches!(self.config.mode, OutputMode::Emscripten) {
-            self.emscripten_library.push_str(&format!(
+            self.emscripten_library.push_str(
                 "
                 $CLOSURE_DTORS: `(typeof FinalizationRegistry === 'undefined')
                     ? {{ register: () => {{}}, unregister: () => {{}} }}
                 : new FinalizationRegistry(state => {{
                     wasmExports.__indirect_function_table.get(state.dtor)(state.a, state.b)
                 }})`,\n
-                "
-            ));
+                ",
+            );
         } else {
             self.global(&format!(
                 "
@@ -3187,20 +3183,20 @@ __wbg_set_wasm(wasm);"
                         "function() {{ return logError(function {}, arguments) }}",
                         code
                     )
-                } else {
-                    if !import_deps.is_empty() {
-                        for dep in &import_deps {
-                            self.emscripten_deps.insert(dep.clone());
-                        }
-                        format!(
-                            "function{},\n{}__deps:  [{}]",
-                            code,
-                            self.module.imports.get(core).name,
-                            import_deps.join(",")
-                        )
-                    } else {
-                        format!("function{}\n", code)
+                } else if (matches!(self.config.mode, OutputMode::Emscripten)
+                    && !import_deps.is_empty())
+                {
+                    for dep in &import_deps {
+                        self.emscripten_deps.insert(dep.clone());
                     }
+                    format!(
+                        "function{},\n{}__deps:  [{}]",
+                        code,
+                        self.module.imports.get(core).name,
+                        import_deps.join(",")
+                    )
+                } else {
+                    format!("function{}\n", code)
                 };
 
                 self.wasm_import_definitions.insert(core, code);
@@ -3210,7 +3206,7 @@ __wbg_set_wasm(wasm);"
                 assert!(!log_error);
 
                 if matches!(self.config.mode, OutputMode::Emscripten) {
-                    self.emscripten_library.push_str("$");
+                    self.emscripten_library.push('$');
                     self.emscripten_library.push_str(&self.adapter_name(id));
                     self.emscripten_library.push_str(": function");
                     self.emscripten_library
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
index 901b7f723e5..60a9618e157 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
@@ -117,7 +117,7 @@ fn main() -> anyhow::Result<()> {
         .map(Path::new)
         .context("file to test is not a valid file, can't extract file name")?;
 
-    let mut file_name_buf = PathBuf::from(cli.file.clone());
+    let mut file_name_buf = cli.file.clone();
 
     // Repoint the file to be read from "name.js" to "name.wasm" in the case of emscripten.
     // Rustc generates a .js and a .wasm file when targeting emscripten. It lists the .js
diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
index 5f69079ca17..24c62417c15 100644
--- a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
+++ b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
@@ -353,7 +353,7 @@ pub(crate) fn spawn(
         response
     })
     .map_err(|e| anyhow!("{}", e))?;
-    return Ok(srv);
+    Ok(srv)
 }
 
 pub(crate) fn spawn_emscripten(
@@ -390,7 +390,7 @@ pub(crate) fn spawn_emscripten(
         response
     })
     .map_err(|e| anyhow!("{}", e))?;
-    return Ok(srv);
+    Ok(srv)
 }
 
 fn try_asset(request: &Request, dir: &Path) -> Response {
diff --git a/src/convert/impls.rs b/src/convert/impls.rs
index 44bd846f683..5cecbf69ca2 100644
--- a/src/convert/impls.rs
+++ b/src/convert/impls.rs
@@ -44,7 +44,7 @@ impl WasmAbi for i128 {
 
     #[inline]
     fn join(low: u64, high: u64, _: (), _: ()) -> Self {
-        ((high as u128) << 64 | low as u128) as i128
+        (((high as u128) << 64) | low as u128) as i128
     }
 }
 impl WasmAbi for u128 {
@@ -62,7 +62,7 @@ impl WasmAbi for u128 {
 
     #[inline]
     fn join(low: u64, high: u64, _: (), _: ()) -> Self {
-        (high as u128) << 64 | low as u128
+        ((high as u128) << 64) | low as u128
     }
 }
 

From 5c89b0943b05ea34a867ce77d9e22a9092389935 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Wed, 12 Mar 2025 16:58:39 -0400
Subject: [PATCH 11/17] Get futures to work with emscripten mode

---
 .cargo/config.toml                   |   1 +
 crates/cli-support/src/js/binding.rs | 120 ++++---
 crates/cli-support/src/js/mod.rs     | 499 +++++++++++++++++++--------
 3 files changed, 435 insertions(+), 185 deletions(-)

diff --git a/.cargo/config.toml b/.cargo/config.toml
index fa4ad575c1f..e787276b404 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -6,6 +6,7 @@ runner = 'cargo run -p wasm-bindgen-cli --bin wasm-bindgen-test-runner --'
 [target.'cfg(all(target_arch = "wasm32", target_os = "emscripten"))']
 rustflags = [
   "-Cllvm-args=-enable-emscripten-cxx-exceptions=0",
+  "-Clink-arg=-sERROR_ON_UNDEFINED_SYMBOLS=0",
   "-Clink-arg=-Wno-undefined",
   "-Crelocation-model=static",
 ]
diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs
index 42a5f33203f..f1d6af9e258 100644
--- a/crates/cli-support/src/js/binding.rs
+++ b/crates/cli-support/src/js/binding.rs
@@ -9,6 +9,7 @@ use crate::wit::InstructionData;
 use crate::wit::{
     Adapter, AdapterId, AdapterKind, AdapterType, AuxFunctionArgumentData, Instruction,
 };
+use crate::OutputMode;
 use anyhow::{anyhow, bail, Error};
 use std::collections::HashSet;
 use std::fmt::Write;
@@ -130,7 +131,7 @@ impl<'a, 'b> Builder<'a, 'b> {
         debug_name: &str,
         ret_ty_override: &Option,
         ret_desc: &Option,
-        import_deps: &mut Vec,
+        import_deps: &mut HashSet,
     ) -> Result {
         if self
             .cx
@@ -257,7 +258,7 @@ impl<'a, 'b> Builder<'a, 'b> {
         };
 
         if self.catch {
-            js.cx.expose_handle_error()?;
+            js.cx.expose_handle_error(import_deps)?;
         }
 
         // Generate a try/catch block in debug mode which handles unexpected and
@@ -265,7 +266,7 @@ impl<'a, 'b> Builder<'a, 'b> {
         // logs what happened, but keeps the exception being thrown to propagate
         // elsewhere.
         if self.log_error {
-            js.cx.expose_log_error();
+            js.cx.expose_log_error(import_deps);
         }
 
         code.push_str(&call);
@@ -641,11 +642,11 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         self.prelude(&format!("_assertBoolean({});", arg));
     }
 
-    fn assert_optional_number(&mut self, arg: &str) {
+    fn assert_optional_number(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_is_like_none();
+        self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
         self.assert_number(arg);
         self.prelude("}");
@@ -661,21 +662,21 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         self.prelude(&format!("_assertChar({});", arg));
     }
 
-    fn assert_optional_bigint(&mut self, arg: &str) {
+    fn assert_optional_bigint(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_is_like_none();
+        self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
         self.assert_bigint(arg);
         self.prelude("}");
     }
 
-    fn assert_optional_bool(&mut self, arg: &str) {
+    fn assert_optional_bool(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_is_like_none();
+        self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
         self.assert_bool(arg);
         self.prelude("}");
@@ -700,8 +701,9 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         mem: walrus::MemoryId,
         malloc: walrus::FunctionId,
         realloc: Option,
+        import_deps: &mut HashSet,
     ) -> Result<(), Error> {
-        let pass = self.cx.expose_pass_string_to_wasm(mem)?;
+        let pass = self.cx.expose_pass_string_to_wasm(mem, import_deps)?;
         let val = self.pop();
         let malloc = self.cx.export_name_of(malloc);
         let i = self.tmp();
@@ -720,6 +722,7 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         self.prelude(&format!("const len{} = WASM_VECTOR_LEN;", i));
         self.push(format!("ptr{}", i));
         self.push(format!("len{}", i));
+        import_deps.insert(format!("'${}'", pass));
         Ok(())
     }
 }
@@ -729,7 +732,7 @@ fn instruction(
     instr: &Instruction,
     log_error: &mut bool,
     constructor: &Option,
-    import_deps: &mut Vec,
+    import_deps: &mut HashSet,
 ) -> Result<(), Error> {
     fn wasm_to_string_enum(name: &str, index: &str) -> String {
         // e.g. ["a","b","c"][someIndex]
@@ -880,8 +883,8 @@ fn instruction(
 
         Instruction::OptionInt128ToWasm => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_bigint(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_bigint(&val, import_deps);
             let (low, high) = int128_to_int64x2(&val);
             js.push(format!("!isLikeNone({val})"));
             js.push(format!("isLikeNone({val}) ? BigInt(0) : {low}"));
@@ -924,7 +927,7 @@ fn instruction(
             let enum_val = js.pop();
             js.cx.expose_string_enum(name);
             let enum_val_expr = string_enum_to_wasm(name, *invalid, &enum_val);
-            js.cx.expose_is_like_none();
+            js.cx.expose_is_like_none(import_deps);
 
             // e.g. isLikeNone(someEnumVal) ? 4 : (string_enum_to_wasm(someEnumVal))
             js.push(format!(
@@ -944,7 +947,7 @@ fn instruction(
             malloc,
             realloc,
         } => {
-            js.string_to_memory(*mem, *malloc, *realloc)?;
+            js.string_to_memory(*mem, *malloc, *realloc, import_deps)?;
         }
 
         Instruction::Retptr { size } => {
@@ -969,7 +972,7 @@ fn instruction(
             // Note that we always assume the return pointer is argument 0,
             // which is currently the case for LLVM.
             let val = js.pop();
-            let expr = format!(
+            let mut expr = format!(
                 "{}().{}({} + {} * {}, {}, true);",
                 mem,
                 method,
@@ -978,6 +981,16 @@ fn instruction(
                 offset,
                 val,
             );
+            if matches!(js.cx.config.mode, OutputMode::Emscripten) {
+                expr = format!(
+                    "HEAP_DATA_VIEW.{}({} + {} * {}, {}, true);",
+                    method,
+                    js.arg(0),
+                    size,
+                    offset,
+                    val,
+                );
+            }
             js.prelude(&expr);
         }
 
@@ -997,10 +1010,18 @@ fn instruction(
             // If we're loading from the return pointer then we must have pushed
             // it earlier, and we always push the same value, so load that value
             // here
-            let expr = format!(
+            let mut expr = format!(
                 "{}().{}(retptr + {} * {}, true)",
                 mem, method, size, scaled_offset
             );
+
+            if matches!(js.cx.config.mode, OutputMode::Emscripten) {
+                expr = format!(
+                    "HEAP_DATA_VIEW.{}(retptr + {} * {}, true)",
+                    method, size, scaled_offset
+                );
+            }
+
             js.prelude(&format!("var r{} = {};", offset, expr));
             js.push(format!("r{}", offset));
         }
@@ -1053,7 +1074,7 @@ fn instruction(
 
         Instruction::I32FromOptionRust { class } => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
+            js.cx.expose_is_like_none(import_deps);
             let i = js.tmp();
             js.prelude(&format!("let ptr{} = 0;", i));
             js.prelude(&format!("if (!isLikeNone({0})) {{", val));
@@ -1066,10 +1087,12 @@ fn instruction(
 
         Instruction::I32FromOptionExternref { table_and_alloc } => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
+            js.cx.expose_is_like_none(import_deps);
             match table_and_alloc {
                 Some((table, alloc)) => {
-                    let alloc = js.cx.expose_add_to_externref_table(*table, *alloc)?;
+                    let alloc = js
+                        .cx
+                        .expose_add_to_externref_table(*table, *alloc, import_deps)?;
                     js.push(format!("isLikeNone({0}) ? 0 : {1}({0})", val, alloc));
                 }
                 None => {
@@ -1081,22 +1104,22 @@ fn instruction(
 
         Instruction::I32FromOptionU32Sentinel => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_number(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_number(&val, import_deps);
             js.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0}", val));
         }
 
         Instruction::I32FromOptionBool => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_bool(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_bool(&val, import_deps);
             js.push(format!("isLikeNone({0}) ? 0xFFFFFF : {0} ? 1 : 0", val));
         }
 
         Instruction::I32FromOptionChar => {
             let val = js.pop();
             let i = js.tmp();
-            js.cx.expose_is_like_none();
+            js.cx.expose_is_like_none(import_deps);
             js.prelude(&format!(
                 "const char{i} = isLikeNone({0}) ? 0xFFFFFF : {0}.codePointAt(0);",
                 val
@@ -1111,15 +1134,15 @@ fn instruction(
 
         Instruction::I32FromOptionEnum { hole } => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_number(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_number(&val, import_deps);
             js.push(format!("isLikeNone({0}) ? {1} : {0}", val, hole));
         }
 
         Instruction::F64FromOptionSentinelInt { signed } => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_number(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_number(&val, import_deps);
 
             // We need to convert the given number to a 32-bit integer before
             // passing it to the ABI for 2 reasons:
@@ -1141,8 +1164,8 @@ fn instruction(
         }
         Instruction::F64FromOptionSentinelF32 => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_number(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_number(&val, import_deps);
 
             // Similar to the above 32-bit integer variant, we convert the
             // number to a 32-bit *float* before passing it to the ABI. This
@@ -1156,11 +1179,11 @@ fn instruction(
 
         Instruction::FromOptionNative { ty } => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
+            js.cx.expose_is_like_none(import_deps);
             if *ty == ValType::I64 {
-                js.assert_optional_bigint(&val);
+                js.assert_optional_bigint(&val, import_deps);
             } else {
-                js.assert_optional_number(&val);
+                js.assert_optional_number(&val, import_deps);
             }
             js.push(format!("!isLikeNone({0})", val));
             js.push(format!(
@@ -1177,7 +1200,9 @@ fn instruction(
 
         Instruction::VectorToMemory { kind, malloc, mem } => {
             let val = js.pop();
-            let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
+            let func = js
+                .cx
+                .pass_to_wasm_function(kind.clone(), *mem, import_deps)?;
             let malloc = js.cx.export_name_of(*malloc);
             let i = js.tmp();
             js.prelude(&format!(
@@ -1190,6 +1215,7 @@ fn instruction(
             js.prelude(&format!("const len{} = WASM_VECTOR_LEN;", i));
             js.push(format!("ptr{}", i));
             js.push(format!("len{}", i));
+            import_deps.insert(format!("'${}'", func));
         }
 
         Instruction::UnwrapResult { table_and_drop } => {
@@ -1260,8 +1286,8 @@ fn instruction(
             malloc,
             realloc,
         } => {
-            let func = js.cx.expose_pass_string_to_wasm(*mem)?;
-            js.cx.expose_is_like_none();
+            let func = js.cx.expose_pass_string_to_wasm(*mem, import_deps)?;
+            js.cx.expose_is_like_none(import_deps);
             let i = js.tmp();
             let malloc = js.cx.export_name_of(*malloc);
             let val = js.pop();
@@ -1280,11 +1306,14 @@ fn instruction(
             js.prelude(&format!("var len{} = WASM_VECTOR_LEN;", i));
             js.push(format!("ptr{}", i));
             js.push(format!("len{}", i));
+            import_deps.insert(format!("'${}'", func));
         }
 
         Instruction::OptionVector { kind, mem, malloc } => {
-            let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
-            js.cx.expose_is_like_none();
+            let func = js
+                .cx
+                .pass_to_wasm_function(kind.clone(), *mem, import_deps)?;
+            js.cx.expose_is_like_none(import_deps);
             let i = js.tmp();
             let malloc = js.cx.export_name_of(*malloc);
             let val = js.pop();
@@ -1298,12 +1327,15 @@ fn instruction(
             js.prelude(&format!("var len{} = WASM_VECTOR_LEN;", i));
             js.push(format!("ptr{}", i));
             js.push(format!("len{}", i));
+            import_deps.insert(format!("'${}'", func));
         }
 
         Instruction::MutableSliceToMemory { kind, malloc, mem } => {
             // Copy the contents of the typed array into wasm.
             let val = js.pop();
-            let func = js.cx.pass_to_wasm_function(kind.clone(), *mem)?;
+            let func = js
+                .cx
+                .pass_to_wasm_function(kind.clone(), *mem, import_deps)?;
             let malloc = js.cx.export_name_of(*malloc);
             let i = js.tmp();
             js.prelude(&format!(
@@ -1320,6 +1352,7 @@ fn instruction(
             // Then we give Wasm a reference to the original typed array, so that it can
             // update it with modifications made on the Wasm side before returning.
             js.push(val);
+            import_deps.insert(format!("'${}'", func));
         }
 
         Instruction::BoolFromI32 => {
@@ -1421,6 +1454,7 @@ fn instruction(
                 .collect::>()
                 .join(", ");
             let wrapper = js.cx.adapter_name(*adapter);
+            import_deps.insert(format!("'${}'", wrapper));
             if *mutable {
                 // Mutable closures need protection against being called
                 // recursively, so ensure that we clear out one of the
@@ -1566,8 +1600,8 @@ fn instruction(
 
         Instruction::I32FromOptionNonNull => {
             let val = js.pop();
-            js.cx.expose_is_like_none();
-            js.assert_optional_number(&val);
+            js.cx.expose_is_like_none(import_deps);
+            js.assert_optional_number(&val, import_deps);
             js.push(format!("isLikeNone({0}) ? 0 : {0}", val));
         }
 
@@ -1640,7 +1674,7 @@ impl Invocation {
         args: &[String],
         prelude: &mut String,
         log_error: &mut bool,
-        import_deps: &mut Vec,
+        import_deps: &mut HashSet,
     ) -> Result {
         match self {
             Invocation::Core { id, .. } => {
diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index 006af1ffdc1..e77608edaed 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -243,7 +243,7 @@ impl<'a> Context<'a> {
                 ExportJs::Function(function) => {
                     let body = function.strip_prefix("function").unwrap();
                     if export_name == definition_name {
-                        format!("Module.{} = function{}\n", export_name, body)
+                        format!("Module.{} = function{};\n", export_name, body)
                     } else {
                         format!(
                             "function {}{}\nexport {{ {} as {} }};\n",
@@ -667,8 +667,6 @@ __wbg_set_wasm(wasm);"
             push_with_newline("var LibraryWbg = {\n");
             push_with_newline(&self.emscripten_library);
             push_with_newline(&init_js);
-            push_with_newline("$initBindgen__deps: ['$addOnInit'],");
-            push_with_newline("$initBindgen__postset: 'addOnInit(initBindgen);',");
             push_with_newline(
                 "$initBindgen: () => {\n
     wasmExports.__wbindgen_start();",
@@ -987,7 +985,31 @@ __wbg_set_wasm(wasm);"
         }
 
         let js = match &self.config.mode {
-            OutputMode::Emscripten => imports_init.to_string(),
+            OutputMode::Emscripten => {
+            let mut global_emscripten_initializer: String = Default::default();
+            for global_dep in self.emscripten_deps.iter() {
+                let mut global = "";
+                if global_dep == "'$WASM_VECTOR_LEN'" {
+                    global = "$WASM_VECTOR_LEN: '0',";
+                } else if global_dep == "'$TextEncoder'" {
+                    global = "$textEncoder: \"new TextEncoder()\",";
+                }
+
+                if global != "" {
+                    global_emscripten_initializer = format!(
+                        "{}{}\n",
+                        global_emscripten_initializer, global
+                    );
+                }
+            }
+            format!(
+            "\
+            {}
+                {}
+                $initBindgen__deps: ['$addOnInit'],
+                $initBindgen__postset: 'addOnInit(initBindgen);',
+            ", imports_init.to_string(), global_emscripten_initializer
+            )}
             _ => format!(
             "\
                 async function __wbg_load(module, imports) {{
@@ -1520,15 +1542,25 @@ __wbg_set_wasm(wasm);"
         );
     }
 
-    fn expose_wasm_vector_len(&mut self) {
+    fn expose_wasm_vector_len(&mut self, import_deps: &mut HashSet) {
+        import_deps.insert("'$WASM_VECTOR_LEN'".to_string());
         if !self.should_write_global("wasm_vector_len") {
             return;
         }
-        self.global("let WASM_VECTOR_LEN = 0;");
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            self.emscripten_deps
+                .insert("'$WASM_VECTOR_LEN'".to_string());
+        } else {
+            self.global("let WASM_VECTOR_LEN = 0;");
+        }
     }
 
-    fn expose_pass_string_to_wasm(&mut self, memory: MemoryId) -> Result {
-        self.expose_wasm_vector_len();
+    fn expose_pass_string_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
+        self.expose_wasm_vector_len(import_deps);
 
         let debug = if self.config.debug {
             "
@@ -1548,55 +1580,101 @@ __wbg_set_wasm(wasm);"
         }
         self.expose_text_encoder()?;
 
+        let mut text_encoder = "cachedTextEncoder";
+        let mut mem_formatted = format!("{mem}()");
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            text_encoder = "textEncoder";
+            mem_formatted = "HEAP8".to_string();
+        };
         // The first implementation we have for this is to use
         // `TextEncoder#encode` which has been around for quite some time.
-        let encode = "function (arg, view) {
-            const buf = cachedTextEncoder.encode(arg);
+        let encode = format!(
+            "function (arg, view) {{
+            const buf = {text_encoder}.encode(arg);
             view.set(buf);
-            return {
+            return {{
                 read: arg.length,
                 written: buf.length
-            };
-        }";
+            }};
+        }}"
+        );
 
         // Another possibility is to use `TextEncoder#encodeInto` which is much
         // newer and isn't implemented everywhere yet. It's more efficient,
         // however, because it allows us to elide an intermediate allocation.
-        let encode_into = "function (arg, view) {
-            return cachedTextEncoder.encodeInto(arg, view);
-        }";
+        let encode_into = format!(
+            "function (arg, view) {{
+            return {text_encoder}.encodeInto(arg, view);
+        }}"
+        );
 
         // Looks like `encodeInto` doesn't currently work when the memory passed
         // in is backed by a `SharedArrayBuffer`, so force usage of `encode` if
         // a `SharedArrayBuffer` is in use.
         let shared = self.module.memories.get(memory).shared;
 
-        match self.config.encode_into {
-            EncodeInto::Always if !shared => {
-                self.global(&format!(
-                    "
-                    const encodeString = {};
-                ",
-                    encode_into
-                ));
-            }
-            EncodeInto::Test if !shared => {
-                self.global(&format!(
-                    "
-                    const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
-                        ? {}
-                        : {});
-                ",
-                    encode_into, encode
-                ));
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            match self.config.encode_into {
+                EncodeInto::Always if !shared => {
+                    self.emscripten_library.push_str(&format!(
+                        "
+                        $encodeString : {},
+                    ",
+                        encode_into
+                    ));
+                }
+                EncodeInto::Test if !shared => {
+                    self.emscripten_library.push_str(&format!(
+                        "
+                        $encodeString : function (arg, view) {{
+                        if (typeof TextEncoder.encodeInto === 'function') {{
+                            return {}
+                        }}
+                        return {}
+                        }},
+                    ",
+                        encode_into, encode
+                    ));
+                }
+                _ => {
+                    self.emscripten_library.push_str(&format!(
+                        "
+                        $encodeString : {},
+                    ",
+                        encode
+                    ));
+                }
             }
-            _ => {
-                self.global(&format!(
-                    "
-                    const encodeString = {};
-                ",
-                    encode
-                ));
+            self.emscripten_library
+                .push_str("$encodeString__deps: ['$textEncoder'],\n");
+        } else {
+            match self.config.encode_into {
+                EncodeInto::Always if !shared => {
+                    self.global(&format!(
+                        "
+                        const encodeString = {};
+                    ",
+                        encode_into
+                    ));
+                }
+                EncodeInto::Test if !shared => {
+                    self.global(&format!(
+                        "
+                        const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
+                            ? {}
+                            : {});
+                    ",
+                        encode_into, encode
+                    ));
+                }
+                _ => {
+                    self.global(&format!(
+                        "
+                        const encodeString = {};
+                    ",
+                        encode
+                    ));
+                }
             }
         }
 
@@ -1612,9 +1690,9 @@ __wbg_set_wasm(wasm);"
         let encode_as_ascii = format!(
             "\
                 if (realloc === undefined) {{
-                    const buf = cachedTextEncoder.encode(arg);
+                    const buf = {text_encoder}.encode(arg);
                     const ptr = malloc(buf.length, 1) >>> 0;
-                    {mem}().subarray(ptr, ptr + buf.length).set(buf);
+                    {mem_formatted}.subarray(ptr, ptr + buf.length).set(buf);
                     WASM_VECTOR_LEN = buf.length;
                     return ptr;
                 }}
@@ -1622,7 +1700,7 @@ __wbg_set_wasm(wasm);"
                 let len = arg.length;
                 let ptr = malloc(len, 1) >>> 0;
 
-                const mem = {mem}();
+                const mem = {mem_formatted};
 
                 let offset = 0;
 
@@ -1631,12 +1709,11 @@ __wbg_set_wasm(wasm);"
                     if (code > 0x7F) break;
                     mem[ptr + offset] = code;
                 }}
-            ",
-            mem = mem,
+            "
         );
-
-        self.global(&format!(
-            "function {name}(arg, malloc, realloc) {{
+        self.write_js_function(
+            &format!(
+                "
                 {debug}
                 {ascii}
                 if (offset !== len) {{
@@ -1644,7 +1721,7 @@ __wbg_set_wasm(wasm);"
                         arg = arg.slice(offset);
                     }}
                     ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
-                    const view = {mem}().subarray(ptr + offset, ptr + len);
+                    const view = {mem_formatted}.subarray(ptr + offset, ptr + len);
                     const ret = encodeString(arg, view);
                     {debug_end}
                     offset += ret.written;
@@ -1654,51 +1731,86 @@ __wbg_set_wasm(wasm);"
                 WASM_VECTOR_LEN = offset;
                 return ptr;
             }}",
-            name = ret,
-            debug = debug,
-            ascii = encode_as_ascii,
-            mem = mem,
-            debug_end = if self.config.debug {
-                "if (ret.read !== arg.length) throw new Error('failed to pass whole string');"
-            } else {
-                ""
-            },
-        ));
+                debug = debug,
+                ascii = encode_as_ascii,
+                mem_formatted = mem_formatted,
+                debug_end = if self.config.debug {
+                    "if (ret.read !== arg.length) throw new Error('failed to pass whole string');"
+                } else {
+                    ""
+                },
+            ),
+            &format!("{ret}"),
+            "(arg, malloc, realloc)",
+            &vec![
+                format!("'$encodeString'"),
+                format!("'${text_encoder}'"),
+                format!("'$WASM_VECTOR_LEN'"),
+            ],
+        );
 
         Ok(ret)
     }
 
-    fn expose_pass_array8_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array8_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_uint8_memory(memory);
-        self.pass_array_to_wasm("passArray8ToWasm", view, 1)
+        self.pass_array_to_wasm("passArray8ToWasm", view, 1, import_deps)
     }
 
-    fn expose_pass_array16_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array16_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_uint16_memory(memory);
-        self.pass_array_to_wasm("passArray16ToWasm", view, 2)
+        self.pass_array_to_wasm("passArray16ToWasm", view, 2, import_deps)
     }
 
-    fn expose_pass_array32_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array32_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_uint32_memory(memory);
-        self.pass_array_to_wasm("passArray32ToWasm", view, 4)
+        self.pass_array_to_wasm("passArray32ToWasm", view, 4, import_deps)
     }
 
-    fn expose_pass_array64_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array64_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_uint64_memory(memory);
-        self.pass_array_to_wasm("passArray64ToWasm", view, 8)
+        self.pass_array_to_wasm("passArray64ToWasm", view, 8, import_deps)
     }
 
-    fn expose_pass_array_f32_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array_f32_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_f32_memory(memory);
-        self.pass_array_to_wasm("passArrayF32ToWasm", view, 4)
+        self.pass_array_to_wasm("passArrayF32ToWasm", view, 4, import_deps)
     }
 
-    fn expose_pass_array_f64_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array_f64_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let view = self.expose_f64_memory(memory);
-        self.pass_array_to_wasm("passArrayF64ToWasm", view, 8)
+        self.pass_array_to_wasm("passArrayF64ToWasm", view, 8, import_deps)
     }
 
-    fn expose_pass_array_jsvalue_to_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_pass_array_jsvalue_to_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let mem = self.expose_dataview_memory(memory);
         let ret = MemView {
             name: "passArrayJsValueToWasm".into(),
@@ -1707,15 +1819,15 @@ __wbg_set_wasm(wasm);"
         if !self.should_write_global(ret.to_string()) {
             return Ok(ret);
         }
-        self.expose_wasm_vector_len();
+        self.expose_wasm_vector_len(import_deps);
         match (self.aux.externref_table, self.aux.externref_alloc) {
             (Some(table), Some(alloc)) => {
                 // TODO: using `addToExternrefTable` goes back and forth between wasm
                 // and JS a lot, we should have a bulk operation for this.
-                let add = self.expose_add_to_externref_table(table, alloc)?;
-                self.global(&format!(
-                    "
-                        function {ret}(array, malloc) {{
+                let add = self.expose_add_to_externref_table(table, alloc, import_deps)?;
+                self.write_js_function(
+                    &format!(
+                        "
                             const ptr = malloc(array.length * 4, 4) >>> 0;
                             for (let i = 0; i < array.length; i++) {{
                                 const add = {add}(array[i]);
@@ -1725,7 +1837,11 @@ __wbg_set_wasm(wasm);"
                             return ptr;
                         }}
                     ",
-                ));
+                    ),
+                    &format!("{ret}"),
+                    "(array, malloc)",
+                    &vec![format!("'${add}'"), format!("'$WASM_VECTOR_LEN'")],
+                );
             }
             _ => {
                 self.expose_add_heap_object();
@@ -1752,6 +1868,7 @@ __wbg_set_wasm(wasm);"
         name: &'static str,
         view: MemView,
         size: usize,
+        import_deps: &mut HashSet,
     ) -> Result {
         let ret = MemView {
             name: name.into(),
@@ -1760,7 +1877,7 @@ __wbg_set_wasm(wasm);"
         if !self.should_write_global(ret.to_string()) {
             return Ok(ret);
         }
-        self.expose_wasm_vector_len();
+        self.expose_wasm_vector_len(import_deps);
         self.global(&format!(
             "
             function {}(arg, malloc) {{
@@ -1789,6 +1906,7 @@ __wbg_set_wasm(wasm);"
         if !self.should_write_global("text_encoder") {
             return Ok(());
         }
+        self.emscripten_deps.insert("'$TextEncoder'".to_string());
         self.expose_text_processor("TextEncoder", "encode", "('utf-8')", None)
     }
 
@@ -2313,7 +2431,8 @@ __wbg_set_wasm(wasm);"
         ));
     }
 
-    fn expose_handle_error(&mut self) -> Result<(), Error> {
+    fn expose_handle_error(&mut self, import_deps: &mut HashSet) -> Result<(), Error> {
+        import_deps.insert("'$handleError'".to_string());
         if !self.should_write_global("handle_error") {
             return Ok(());
         }
@@ -2324,10 +2443,10 @@ __wbg_set_wasm(wasm);"
         let store = self.export_name_of(store);
         match (self.aux.externref_table, self.aux.externref_alloc) {
             (Some(table), Some(alloc)) => {
-                let add = self.expose_add_to_externref_table(table, alloc)?;
-                self.global(&format!(
-                    "\
-                    function handleError(f, args) {{
+                let add = self.expose_add_to_externref_table(table, alloc, import_deps)?;
+                self.write_js_function(
+                    &format!(
+                        "\
                         try {{
                             return f.apply(this, args);
                         }} catch (e) {{
@@ -2336,13 +2455,18 @@ __wbg_set_wasm(wasm);"
                         }}
                     }}
                     ",
-                    add, store,
-                ));
+                        add, store,
+                    ),
+                    "handleError",
+                    "(f, args)",
+                    &vec![format!("'${add}'")],
+                );
             }
             _ => {
                 self.expose_add_heap_object();
-                self.global(&format!(
-                    "\
+                self.write_js_function(
+                    &format!(
+                        "\
                     function handleError(f, args) {{
                         try {{
                             return f.apply(this, args);
@@ -2351,20 +2475,24 @@ __wbg_set_wasm(wasm);"
                         }}
                     }}
                     ",
-                    store,
-                ));
+                        store,
+                    ),
+                    "handleError",
+                    "(f, args)",
+                    &vec![format!("'$addHeapObject'")],
+                );
             }
         }
         Ok(())
     }
 
-    fn expose_log_error(&mut self) {
+    fn expose_log_error(&mut self, import_deps: &mut HashSet) {
+        import_deps.insert("'$logError'".to_string());
         if !self.should_write_global("log_error") {
             return;
         }
-        self.global(
+        self.write_js_function(
             "\
-            function logError(f, args) {
                 try {
                     return f.apply(this, args);
                 } catch (e) {
@@ -2384,22 +2512,38 @@ __wbg_set_wasm(wasm);"
                 }
             }
             ",
+            "logError",
+            "(f, args)",
+            &Default::default(),
         );
     }
 
-    fn pass_to_wasm_function(&mut self, t: VectorKind, memory: MemoryId) -> Result {
+    fn pass_to_wasm_function(
+        &mut self,
+        t: VectorKind,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         match t {
-            VectorKind::String => self.expose_pass_string_to_wasm(memory),
+            VectorKind::String => self.expose_pass_string_to_wasm(memory, import_deps),
             VectorKind::I8 | VectorKind::U8 | VectorKind::ClampedU8 => {
-                self.expose_pass_array8_to_wasm(memory)
+                self.expose_pass_array8_to_wasm(memory, import_deps)
+            }
+            VectorKind::U16 | VectorKind::I16 => {
+                self.expose_pass_array16_to_wasm(memory, import_deps)
+            }
+            VectorKind::I32 | VectorKind::U32 => {
+                self.expose_pass_array32_to_wasm(memory, import_deps)
+            }
+            VectorKind::I64 | VectorKind::U64 => {
+                self.expose_pass_array64_to_wasm(memory, import_deps)
+            }
+            VectorKind::F32 => self.expose_pass_array_f32_to_wasm(memory, import_deps),
+            VectorKind::F64 => self.expose_pass_array_f64_to_wasm(memory, import_deps),
+            VectorKind::Externref => self.expose_pass_array_jsvalue_to_wasm(memory, import_deps),
+            VectorKind::NamedExternref(_) => {
+                self.expose_pass_array_jsvalue_to_wasm(memory, import_deps)
             }
-            VectorKind::U16 | VectorKind::I16 => self.expose_pass_array16_to_wasm(memory),
-            VectorKind::I32 | VectorKind::U32 => self.expose_pass_array32_to_wasm(memory),
-            VectorKind::I64 | VectorKind::U64 => self.expose_pass_array64_to_wasm(memory),
-            VectorKind::F32 => self.expose_pass_array_f32_to_wasm(memory),
-            VectorKind::F64 => self.expose_pass_array_f64_to_wasm(memory),
-            VectorKind::Externref => self.expose_pass_array_jsvalue_to_wasm(memory),
-            VectorKind::NamedExternref(_) => self.expose_pass_array_jsvalue_to_wasm(memory),
         }
     }
 
@@ -2453,17 +2597,31 @@ __wbg_set_wasm(wasm);"
         );
     }
 
-    fn expose_is_like_none(&mut self) {
+    fn expose_is_like_none(&mut self, import_deps: &mut HashSet) {
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            import_deps.insert("'$isLikeNone'".to_string());
+        }
         if !self.should_write_global("is_like_none") {
             return;
         }
-        self.global(
-            "
-            function isLikeNone(x) {
-                return x === undefined || x === null;
-            }
+
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            self.emscripten_library.push_str(
+                "
+            $isLikeNone: function(x) {
+                return x == null;
+            },
         ",
-        );
+            );
+        } else {
+            self.global(
+                "
+                function isLikeNone(x) {
+                    return x == null;
+                }
+            ",
+            );
+        }
     }
 
     fn expose_assert_non_null(&mut self) {
@@ -2653,10 +2811,10 @@ __wbg_set_wasm(wasm);"
             self.emscripten_library.push_str(
                 "
                 $CLOSURE_DTORS: `(typeof FinalizationRegistry === 'undefined')
-                    ? {{ register: () => {{}}, unregister: () => {{}} }}
-                : new FinalizationRegistry(state => {{
+                    ? { register: () => {}, unregister: () => {} }
+                : new FinalizationRegistry(state => {
                     wasmExports.__indirect_function_table.get(state.dtor)(state.a, state.b)
-                }})`,\n
+                })`,\n
                 ",
             );
         } else {
@@ -2684,6 +2842,50 @@ __wbg_set_wasm(wasm);"
         self.globals.push('\n');
     }
 
+    fn emscripten_library(&mut self, s: &str) {
+        let s = s.trim();
+
+        // Ensure a blank line between adjacent items, and ensure everything is
+        // terminated with a newline.
+        while !self.emscripten_library.ends_with("\n\n\n")
+            && !self.emscripten_library.ends_with("*/\n")
+        {
+            self.emscripten_library.push('\n');
+        }
+        self.emscripten_library.push_str(s);
+        self.emscripten_library.push('\n');
+    }
+
+    fn write_js_function(&mut self, body: &str, func_name: &str, args: &str, deps: &Vec) {
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            self.emscripten_library(&format!(
+                "
+                ${}: function{} {{
+                {},
+                ",
+                func_name,
+                args,
+                body.trim().replace("wasm.", "wasmExports.")
+            ));
+            if !deps.is_empty() {
+                self.emscripten_library.push_str(&format!(
+                    "${}_deps: [{}],
+                    ",
+                    func_name,
+                    deps.join(",")
+                ));
+            }
+        } else {
+            self.global(&format!(
+                "
+            function {}{} {{
+            {}
+            ",
+                func_name, args, body
+            ));
+        }
+    }
+
     fn require_class_wrap(&mut self, name: &str) {
         require_class(&mut self.exported_classes, name).wrap_needed = true;
     }
@@ -2869,24 +3071,30 @@ __wbg_set_wasm(wasm);"
         &mut self,
         table: TableId,
         alloc: FunctionId,
+        import_deps: &mut HashSet,
     ) -> Result {
         let view = self.memview_table("addToExternrefTable", table);
         assert!(self.config.externref);
+        import_deps.insert(format!("'${}'", view).to_string());
         if !self.should_write_global(view.to_string()) {
             return Ok(view);
         }
         let alloc = self.export_name_of(alloc);
         let table = self.export_name_of(table);
-        self.global(&format!(
-            "
-                function {}(obj) {{
+        self.write_js_function(
+            &format!(
+                "
                     const idx = wasm.{}();
                     wasm.{}.set(idx, obj);
                     return idx;
                 }}
             ",
-            view, alloc, table,
-        ));
+                alloc, table,
+            ),
+            &format!("{}", view),
+            "(obj)",
+            &Default::default(),
+        );
 
         Ok(view)
     }
@@ -3035,7 +3243,7 @@ __wbg_set_wasm(wasm);"
             ContextAdapterKind::Export(e) => format!("`{}`", e.debug_name),
             ContextAdapterKind::Adapter => format!("adapter {}", id.0),
         };
-        let mut import_deps: Vec = Default::default();
+        let mut import_deps: HashSet = Default::default();
         // Process the `binding` and generate a bunch of JS/TypeScript/etc.
         let binding::JsFunction {
             ts_sig,
@@ -3173,7 +3381,7 @@ __wbg_set_wasm(wasm);"
                 }
             }
             ContextAdapterKind::Import(core) => {
-                let code = if catch {
+                let mut code = if catch {
                     format!(
                         "function() {{ return handleError(function {}, arguments) }}",
                         code
@@ -3183,22 +3391,25 @@ __wbg_set_wasm(wasm);"
                         "function() {{ return logError(function {}, arguments) }}",
                         code
                     )
-                } else if (matches!(self.config.mode, OutputMode::Emscripten)
-                    && !import_deps.is_empty())
-                {
-                    for dep in &import_deps {
-                        self.emscripten_deps.insert(dep.clone());
-                    }
-                    format!(
-                        "function{},\n{}__deps:  [{}]",
-                        code,
-                        self.module.imports.get(core).name,
-                        import_deps.join(",")
-                    )
                 } else {
-                    format!("function{}\n", code)
+                    format!("function{}", code)
                 };
 
+                if matches!(self.config.mode, OutputMode::Emscripten) && !import_deps.is_empty() {
+                    let mut import_deps_vec = import_deps
+                        .iter()
+                        .map(|s| s.as_str())
+                        .collect::>();
+                    // sort to generate deterministic output.
+                    import_deps_vec.sort();
+                    let deps: String = format!(
+                        "{}__deps:  [{}]\n",
+                        self.module.imports.get(core).name,
+                        import_deps_vec.join(",")
+                    );
+                    code = format!("{},\n{}\n", code, deps);
+                }
+
                 self.wasm_import_definitions.insert(core, code);
             }
             ContextAdapterKind::Adapter => {
@@ -3441,7 +3652,7 @@ __wbg_set_wasm(wasm);"
         args: &[String],
         variadic: bool,
         prelude: &mut String,
-        import_deps: &mut Vec,
+        import_deps: &mut HashSet,
     ) -> Result {
         let variadic_args = |js_arguments: &[String]| {
             Ok(if !variadic {
@@ -3465,7 +3676,7 @@ __wbg_set_wasm(wasm);"
             let re = Regex::new(r"([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(").unwrap();
             for arg in args {
                 if let Some(result) = re.captures(arg) {
-                    import_deps.push(format!("'${}'", &result[1]));
+                    import_deps.insert(format!("'${}'", &result[1]));
                 }
             }
         }
@@ -3581,11 +3792,11 @@ __wbg_set_wasm(wasm);"
                 assert_eq!(args.len(), 3);
 
                 let call = self.adapter_name(*adapter);
-                import_deps.push(format!("'${}'", call));
+                import_deps.insert(format!("'${}'", call));
                 if *mutable {
                     self.expose_make_mut_closure()?;
                     if matches!(self.config.mode, OutputMode::Emscripten) {
-                        import_deps.push("'$makeMutClosure'".to_string());
+                        import_deps.insert("'$makeMutClosure'".to_string());
                     }
                     Ok(format!(
                         "makeMutClosure({arg0}, {arg1}, {dtor}, {call})",
@@ -3597,7 +3808,7 @@ __wbg_set_wasm(wasm);"
                 } else {
                     self.expose_make_closure()?;
                     if matches!(self.config.mode, OutputMode::Emscripten) {
-                        import_deps.push("'$makeClosure'".to_string());
+                        import_deps.insert("'$makeClosure'".to_string());
                     }
                     Ok(format!(
                         "makeClosure({arg0}, {arg1}, {dtor}, {call})",
@@ -3719,7 +3930,7 @@ __wbg_set_wasm(wasm);"
             AuxImport::Intrinsic(intrinsic) => {
                 assert!(kind == AdapterJsImportKind::Normal);
                 assert!(!variadic);
-                self.invoke_intrinsic(intrinsic, args, prelude)
+                self.invoke_intrinsic(intrinsic, args, prelude, import_deps)
             }
 
             AuxImport::LinkTo(path, content) => {
@@ -3785,6 +3996,7 @@ __wbg_set_wasm(wasm);"
         intrinsic: &Intrinsic,
         args: &[String],
         prelude: &mut String,
+        import_deps: &mut HashSet,
     ) -> Result {
         let expr = match intrinsic {
             Intrinsic::JsvalEq => {
@@ -4121,6 +4333,7 @@ __wbg_set_wasm(wasm);"
             Intrinsic::DebugString => {
                 assert_eq!(args.len(), 1);
                 self.expose_debug_string();
+                import_deps.insert("'$debugString'".to_string());
                 format!("debugString({})", args[0])
             }
 
@@ -4395,9 +4608,8 @@ __wbg_set_wasm(wasm);"
             return;
         }
 
-        self.global(
+        self.write_js_function(
             "
-           function debugString(val) {
                 // primitive types
                 const type = typeof val;
                 if (type == 'number' || type == 'boolean' || val == null) {
@@ -4462,6 +4674,9 @@ __wbg_set_wasm(wasm);"
                 return className;
             }
         ",
+            "debugString",
+            "(val)",
+            &Default::default(),
         );
     }
 

From 79892c44e0182920bfb4f228f3fb828d852e1bc9 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Wed, 12 Mar 2025 16:58:39 -0400
Subject: [PATCH 12/17] Get futures to work with emscripten mode

---
 crates/cli-support/src/js/mod.rs | 49 ++++++++++++++------------------
 1 file changed, 21 insertions(+), 28 deletions(-)

diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index e77608edaed..f759aade9c0 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -995,7 +995,7 @@ __wbg_set_wasm(wasm);"
                     global = "$textEncoder: \"new TextEncoder()\",";
                 }
 
-                if global != "" {
+                if !global.is_empty() {
                     global_emscripten_initializer = format!(
                         "{}{}\n",
                         global_emscripten_initializer, global
@@ -1008,7 +1008,7 @@ __wbg_set_wasm(wasm);"
                 {}
                 $initBindgen__deps: ['$addOnInit'],
                 $initBindgen__postset: 'addOnInit(initBindgen);',
-            ", imports_init.to_string(), global_emscripten_initializer
+            ", imports_init, global_emscripten_initializer
             )}
             _ => format!(
             "\
@@ -1729,8 +1729,7 @@ __wbg_set_wasm(wasm);"
                 }}
 
                 WASM_VECTOR_LEN = offset;
-                return ptr;
-            }}",
+                return ptr;",
                 debug = debug,
                 ascii = encode_as_ascii,
                 mem_formatted = mem_formatted,
@@ -1742,10 +1741,10 @@ __wbg_set_wasm(wasm);"
             ),
             &format!("{ret}"),
             "(arg, malloc, realloc)",
-            &vec![
-                format!("'$encodeString'"),
+            &[
+                "'$encodeString'".to_string(),
                 format!("'${text_encoder}'"),
-                format!("'$WASM_VECTOR_LEN'"),
+                "'$WASM_VECTOR_LEN'".to_string(),
             ],
         );
 
@@ -1835,12 +1834,11 @@ __wbg_set_wasm(wasm);"
                             }}
                             WASM_VECTOR_LEN = array.length;
                             return ptr;
-                        }}
                     ",
                     ),
                     &format!("{ret}"),
                     "(array, malloc)",
-                    &vec![format!("'${add}'"), format!("'$WASM_VECTOR_LEN'")],
+                    &[format!("'${add}'"), "'$WASM_VECTOR_LEN'".to_string()],
                 );
             }
             _ => {
@@ -2453,13 +2451,12 @@ __wbg_set_wasm(wasm);"
                             const idx = {}(e);
                             wasm.{}(idx);
                         }}
-                    }}
                     ",
                         add, store,
                     ),
                     "handleError",
                     "(f, args)",
-                    &vec![format!("'${add}'")],
+                    &[format!("'${add}'")],
                 );
             }
             _ => {
@@ -2467,19 +2464,17 @@ __wbg_set_wasm(wasm);"
                 self.write_js_function(
                     &format!(
                         "\
-                    function handleError(f, args) {{
                         try {{
                             return f.apply(this, args);
                         }} catch (e) {{
                             wasm.{}(addHeapObject(e));
                         }}
-                    }}
                     ",
                         store,
                     ),
                     "handleError",
                     "(f, args)",
-                    &vec![format!("'$addHeapObject'")],
+                    &["'$addHeapObject'".to_string()],
                 );
             }
         }
@@ -2510,11 +2505,10 @@ __wbg_set_wasm(wasm);"
                                     error);
                     throw e;
                 }
-            }
             ",
             "logError",
             "(f, args)",
-            &Default::default(),
+            &[],
         );
     }
 
@@ -2856,12 +2850,12 @@ __wbg_set_wasm(wasm);"
         self.emscripten_library.push('\n');
     }
 
-    fn write_js_function(&mut self, body: &str, func_name: &str, args: &str, deps: &Vec) {
+    fn write_js_function(&mut self, body: &str, func_name: &str, args: &str, deps: &[String]) {
         if matches!(self.config.mode, OutputMode::Emscripten) {
             self.emscripten_library(&format!(
-                "
-                ${}: function{} {{
-                {},
+                "${}: function{} {{
+                {}
+                }},
                 ",
                 func_name,
                 args,
@@ -2877,11 +2871,12 @@ __wbg_set_wasm(wasm);"
             }
         } else {
             self.global(&format!(
-                "
-            function {}{} {{
-            {}
+                "function {}{} {{{}
+            }}
             ",
-                func_name, args, body
+                func_name,
+                args,
+                body.trim()
             ));
         }
     }
@@ -3087,13 +3082,12 @@ __wbg_set_wasm(wasm);"
                     const idx = wasm.{}();
                     wasm.{}.set(idx, obj);
                     return idx;
-                }}
             ",
                 alloc, table,
             ),
             &format!("{}", view),
             "(obj)",
-            &Default::default(),
+            &[],
         );
 
         Ok(view)
@@ -4672,11 +4666,10 @@ __wbg_set_wasm(wasm);"
                 }
                 // TODO we could test for more things here, like `Set`s and `Map`s.
                 return className;
-            }
         ",
             "debugString",
             "(val)",
-            &Default::default(),
+            &[],
         );
     }
 

From 5af1192dd3d766d2de988d88b72a64ed1cfd6f2e Mon Sep 17 00:00:00 2001
From: Mitch Foley 
Date: Fri, 14 Mar 2025 18:08:14 -0400
Subject: [PATCH 13/17] handle wasmExports better

---
 crates/cli-support/src/js/mod.rs | 39 ++++++++++++++++++++------------
 1 file changed, 24 insertions(+), 15 deletions(-)

diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index f759aade9c0..b5c3a80394c 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -669,9 +669,9 @@ __wbg_set_wasm(wasm);"
             push_with_newline(&init_js);
             push_with_newline(
                 "$initBindgen: () => {\n
-    wasmExports.__wbindgen_start();",
+    wasmExports['__wbindgen_start']();",
             );
-            self.globals = self.globals.replace("wasm.", "wasmExports.");
+            self.globals = self.globals.replace("wasm.", "_");
             push_with_newline(&self.globals);
             push_with_newline("},");
             let deps: Vec = set_to_list(&self.emscripten_deps);
@@ -853,7 +853,12 @@ __wbg_set_wasm(wasm);"
         needs_manual_start: bool,
         mut imports: Option<&mut String>,
     ) -> Result<(String, String), Error> {
-        let module_name = "wbg";
+        let module_name;
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            module_name = "env";
+        } else {
+            module_name = "wbg";
+        }
         let mut init_memory_arg = "";
         let mut init_memory = String::new();
         let mut has_memory = false;
@@ -931,12 +936,11 @@ __wbg_set_wasm(wasm);"
                         &js.trim()
                             .strip_prefix("function()")
                             .unwrap()
-                            .replace("wasm", "wasmExports"),
                     );
                     imports_init.push_str(",\n");
                 } else {
                     imports_init.push_str(": ");
-                    imports_init.push_str(&js.trim().replace("wasm", "wasmExports"));
+                    imports_init.push_str(&js.trim().replace("wasm.", "_"));
                     imports_init.push_str(",\n");
                 }
             }
@@ -2675,7 +2679,7 @@ __wbg_set_wasm(wasm);"
                             return f(a, state.b, ...args);
                         }} finally {{
                             if (--state.cnt === 0) {{
-                                wasmExports.{table}.get(state.dtor)(a, state.b);
+                                wasmExports['{table}'].get(state.dtor)(a, state.b);
                                 CLOSURE_DTORS.unregister(state);
                             }} else {{
                                 state.a = a;
@@ -2752,7 +2756,7 @@ __wbg_set_wasm(wasm);"
                             return f(state.a, state.b, ...args);
                         }} finally {{
                             if (--state.cnt === 0) {{
-                                wasmExports.{table}.get(state.dtor)(state.a, state.b);
+                                wasmExports['{table}'].get(state.dtor)(state.a, state.b);
                                 state.a = 0;
                                 CLOSURE_DTORS.unregister(state);
                             }}
@@ -2807,7 +2811,7 @@ __wbg_set_wasm(wasm);"
                 $CLOSURE_DTORS: `(typeof FinalizationRegistry === 'undefined')
                     ? { register: () => {}, unregister: () => {} }
                 : new FinalizationRegistry(state => {
-                    wasmExports.__indirect_function_table.get(state.dtor)(state.a, state.b)
+                    wasmExports.['__indirect_function_table'].get(state.dtor)(state.a, state.b)
                 })`,\n
                 ",
             );
@@ -3415,7 +3419,7 @@ __wbg_set_wasm(wasm);"
                     self.emscripten_library.push_str(&self.adapter_name(id));
                     self.emscripten_library.push_str(": function");
                     self.emscripten_library
-                        .push_str(&code.replace("wasm.", "wasmExports."));
+                        .push_str(&code.replace("wasm.", "_"));
                     self.emscripten_library.push_str(",\n\n");
                 } else {
                     self.globals.push_str("function ");
@@ -4404,20 +4408,25 @@ __wbg_set_wasm(wasm);"
                     .aux
                     .externref_table
                     .ok_or_else(|| anyhow!("must enable externref to use externref intrinsic"))?;
+                let mut base = "\n".to_string();
                 let name = self.export_name_of(table);
+
+                if matches!(self.config.mode, OutputMode::Emscripten) {
+                    base.push_str(&format!("const table = wasmExports['{name}'];\n"));
+                } else {
+                    base.push_str(&format!("const table = wasm.{name};\n"));
+                }
+
                 // Grow the table to insert our initial values, and then also
                 // set the 0th slot to `undefined` since that's what we've
                 // historically used for our ABI which is that the index of 0
                 // returns `undefined` for types like `None` going out.
-                let mut base = format!(
-                    "
-                      const table = wasm.{};
-                      const offset = table.grow({});
+                base.push_str(&format!(
+                    " const offset = table.grow({});
                       table.set(0, undefined);
                     ",
-                    name,
                     INITIAL_HEAP_VALUES.len(),
-                );
+                ));
                 for (i, value) in INITIAL_HEAP_VALUES.iter().enumerate() {
                     base.push_str(&format!("table.set(offset + {}, {});\n", i, value));
                 }

From c398c44c52b32bf7bafc3888370c914760f6ab20 Mon Sep 17 00:00:00 2001
From: Mitch Foley 
Date: Mon, 17 Mar 2025 11:23:19 -0400
Subject: [PATCH 14/17] update wasmparser

---
 crates/cli/Cargo.toml              | 2 +-
 crates/threads-xform/Cargo.toml    | 2 +-
 crates/wasm-conventions/Cargo.toml | 2 +-
 crates/wasm-conventions/src/lib.rs | 6 +++---
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
index 2e9b8a28019..dab9db83e00 100644
--- a/crates/cli/Cargo.toml
+++ b/crates/cli/Cargo.toml
@@ -41,7 +41,7 @@ assert_cmd = "2"
 diff = "0.1"
 predicates = "3"
 rayon = "1.0"
-wasmparser = "0.214"
+wasmparser = "0.227"
 wasmprinter = "0.214"
 
 [[test]]
diff --git a/crates/threads-xform/Cargo.toml b/crates/threads-xform/Cargo.toml
index f32e53d59e2..701a2a16705 100644
--- a/crates/threads-xform/Cargo.toml
+++ b/crates/threads-xform/Cargo.toml
@@ -20,7 +20,7 @@ wasm-bindgen-wasm-conventions = { path = "../wasm-conventions", version = "=0.2.
 
 [dev-dependencies]
 rayon = "1.0"
-wasmparser = "0.214"
+wasmparser = "0.227"
 wasmprinter = "0.214"
 wat = "1.0"
 
diff --git a/crates/wasm-conventions/Cargo.toml b/crates/wasm-conventions/Cargo.toml
index 3554087cbde..3bbf49f35b3 100644
--- a/crates/wasm-conventions/Cargo.toml
+++ b/crates/wasm-conventions/Cargo.toml
@@ -17,7 +17,7 @@ walrus = "0.23"
 # Matching the version `walrus` depends on.
 anyhow = "1.0"
 log = "0.4"
-wasmparser = "0.214"
+wasmparser = "0.227"
 
 [lints]
 workspace = true
diff --git a/crates/wasm-conventions/src/lib.rs b/crates/wasm-conventions/src/lib.rs
index beb0fe9da09..b7e8022bdb7 100755
--- a/crates/wasm-conventions/src/lib.rs
+++ b/crates/wasm-conventions/src/lib.rs
@@ -13,7 +13,7 @@ use walrus::{
     ir::Value, ConstExpr, ElementId, ElementItems, FunctionBuilder, FunctionId, FunctionKind,
     GlobalId, GlobalKind, MemoryId, Module, RawCustomSection, ValType,
 };
-use wasmparser::{BinaryReader, WasmFeatures};
+use wasmparser::BinaryReader;
 
 /// Get a Wasm module's canonical linear memory.
 pub fn get_memory(module: &Module) -> Result {
@@ -192,7 +192,7 @@ pub fn target_feature(module: &Module, feature: &str) -> Result {
             .as_any()
             .downcast_ref()
             .context("failed to read section")?;
-        let mut reader = BinaryReader::new(§ion.data, 0, WasmFeatures::default());
+        let mut reader = BinaryReader::new(§ion.data, 0);
         // The first integer contains the target feature count.
         let count = reader.read_var_u32()?;
 
@@ -237,7 +237,7 @@ pub fn insert_target_feature(module: &mut Module, new_feature: &str) -> Result<(
             .as_any_mut()
             .downcast_mut()
             .context("failed to read section")?;
-        let mut reader = BinaryReader::new(§ion.data, 0, WasmFeatures::default());
+        let mut reader = BinaryReader::new(§ion.data, 0);
         // The first integer contains the target feature count.
         let count = reader.read_var_u32()?;
 

From 63c503c4234f19677aadd476bd3f87a9026ec209 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Mon, 17 Mar 2025 12:06:09 -0400
Subject: [PATCH 15/17] Formatting and updating a few tests.

Reference test goldens are updated to match the change with
isLikeNone() as well as some minor updates with an extra new line at the
beginning of some functions.
---
 crates/cli-support/src/js/mod.rs               | 18 +++++++-----------
 crates/cli/tests/reference/echo.js             |  3 +--
 crates/cli/tests/reference/enums.js            |  2 +-
 crates/cli/tests/reference/getter-setter.js    |  3 +--
 crates/cli/tests/reference/int128.js           |  2 +-
 crates/cli/tests/reference/optional-args.js    |  2 +-
 crates/cli/tests/reference/static.js           |  2 +-
 crates/cli/tests/reference/string-arg.js       |  1 -
 .../cli/tests/reference/wasm-export-types.js   |  1 -
 crates/cli/tests/reference/web-sys.js          |  1 -
 10 files changed, 13 insertions(+), 22 deletions(-)

diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index b5c3a80394c..cae232ce5e5 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -853,12 +853,11 @@ __wbg_set_wasm(wasm);"
         needs_manual_start: bool,
         mut imports: Option<&mut String>,
     ) -> Result<(String, String), Error> {
-        let module_name;
-        if matches!(self.config.mode, OutputMode::Emscripten) {
-            module_name = "env";
+        let module_name = if matches!(self.config.mode, OutputMode::Emscripten) {
+            "env"
         } else {
-            module_name = "wbg";
-        }
+            "wbg"
+        };
         let mut init_memory_arg = "";
         let mut init_memory = String::new();
         let mut has_memory = false;
@@ -932,11 +931,7 @@ __wbg_set_wasm(wasm);"
                 imports_init.push_str(&import.name);
                 if import.name == "__wbindgen_init_externref_table" {
                     imports_init.push_str(": () =>");
-                    imports_init.push_str(
-                        &js.trim()
-                            .strip_prefix("function()")
-                            .unwrap()
-                    );
+                    imports_init.push_str(js.trim().strip_prefix("function()").unwrap());
                     imports_init.push_str(",\n");
                 } else {
                     imports_init.push_str(": ");
@@ -2875,7 +2870,8 @@ __wbg_set_wasm(wasm);"
             }
         } else {
             self.global(&format!(
-                "function {}{} {{{}
+                "function {}{} {{
+                {}
             }}
             ",
                 func_name,
diff --git a/crates/cli/tests/reference/echo.js b/crates/cli/tests/reference/echo.js
index c786581f554..9b439df97e7 100644
--- a/crates/cli/tests/reference/echo.js
+++ b/crates/cli/tests/reference/echo.js
@@ -98,7 +98,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
 });
 
 function passStringToWasm0(arg, malloc, realloc) {
-
     if (realloc === undefined) {
         const buf = cachedTextEncoder.encode(arg);
         const ptr = malloc(buf.length, 1) >>> 0;
@@ -146,7 +145,7 @@ function getDataViewMemory0() {
 }
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 
 const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
diff --git a/crates/cli/tests/reference/enums.js b/crates/cli/tests/reference/enums.js
index 10d698dc7e5..edc9b7e134a 100644
--- a/crates/cli/tests/reference/enums.js
+++ b/crates/cli/tests/reference/enums.js
@@ -33,7 +33,7 @@ export function enum_echo(color) {
 }
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 /**
  * @param {Color | null} [color]
diff --git a/crates/cli/tests/reference/getter-setter.js b/crates/cli/tests/reference/getter-setter.js
index a9df5536a5b..497e3bc3b96 100644
--- a/crates/cli/tests/reference/getter-setter.js
+++ b/crates/cli/tests/reference/getter-setter.js
@@ -25,7 +25,7 @@ function getStringFromWasm0(ptr, len) {
 }
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 
 let WASM_VECTOR_LEN = 0;
@@ -48,7 +48,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
 });
 
 function passStringToWasm0(arg, malloc, realloc) {
-
     if (realloc === undefined) {
         const buf = cachedTextEncoder.encode(arg);
         const ptr = malloc(buf.length, 1) >>> 0;
diff --git a/crates/cli/tests/reference/int128.js b/crates/cli/tests/reference/int128.js
index 32de778621c..7bcc40d1e58 100644
--- a/crates/cli/tests/reference/int128.js
+++ b/crates/cli/tests/reference/int128.js
@@ -42,7 +42,7 @@ export function echo_u128(a) {
 }
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 /**
  * @param {bigint | null} [a]
diff --git a/crates/cli/tests/reference/optional-args.js b/crates/cli/tests/reference/optional-args.js
index 43ead5d86c1..d907ecc3bf3 100644
--- a/crates/cli/tests/reference/optional-args.js
+++ b/crates/cli/tests/reference/optional-args.js
@@ -5,7 +5,7 @@ export function __wbg_set_wasm(val) {
 
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 /**
  * @param {number | null} [a]
diff --git a/crates/cli/tests/reference/static.js b/crates/cli/tests/reference/static.js
index 34aa7975e7a..d96485a94cf 100644
--- a/crates/cli/tests/reference/static.js
+++ b/crates/cli/tests/reference/static.js
@@ -5,7 +5,7 @@ export function __wbg_set_wasm(val) {
 
 
 function isLikeNone(x) {
-    return x === undefined || x === null;
+    return x == null;
 }
 
 function addToExternrefTable0(obj) {
diff --git a/crates/cli/tests/reference/string-arg.js b/crates/cli/tests/reference/string-arg.js
index 9d52328a50f..41a902fcee6 100644
--- a/crates/cli/tests/reference/string-arg.js
+++ b/crates/cli/tests/reference/string-arg.js
@@ -44,7 +44,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
 });
 
 function passStringToWasm0(arg, malloc, realloc) {
-
     if (realloc === undefined) {
         const buf = cachedTextEncoder.encode(arg);
         const ptr = malloc(buf.length, 1) >>> 0;
diff --git a/crates/cli/tests/reference/wasm-export-types.js b/crates/cli/tests/reference/wasm-export-types.js
index de290e90502..4a034f8c293 100644
--- a/crates/cli/tests/reference/wasm-export-types.js
+++ b/crates/cli/tests/reference/wasm-export-types.js
@@ -36,7 +36,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
 });
 
 function passStringToWasm0(arg, malloc, realloc) {
-
     if (realloc === undefined) {
         const buf = cachedTextEncoder.encode(arg);
         const ptr = malloc(buf.length, 1) >>> 0;
diff --git a/crates/cli/tests/reference/web-sys.js b/crates/cli/tests/reference/web-sys.js
index 37300cfdcaa..eb714775b3d 100644
--- a/crates/cli/tests/reference/web-sys.js
+++ b/crates/cli/tests/reference/web-sys.js
@@ -124,7 +124,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
 });
 
 function passStringToWasm0(arg, malloc, realloc) {
-
     if (realloc === undefined) {
         const buf = cachedTextEncoder.encode(arg);
         const ptr = malloc(buf.length, 1) >>> 0;

From 5d4984dc248a9145e8203acd435bb69c5c249675 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Wed, 19 Mar 2025 11:28:51 -0400
Subject: [PATCH 16/17] Update the license files.

---
 ...icenses-to-futures-and-web-sys-crate.patch | 830 ++++++++++++++++++
 crates/futures/Cargo.toml                     |   2 +-
 crates/futures/LICENSE-MPL-2.0                | 373 ++++++++
 crates/web-sys/Cargo.toml                     |   2 +-
 crates/web-sys/LICENSE-BSD-3-Clause           |  11 +
 crates/web-sys/LICENSE-MPL-2.0                | 373 ++++++++
 6 files changed, 1589 insertions(+), 2 deletions(-)
 create mode 100644 0001-Add-additional-licenses-to-futures-and-web-sys-crate.patch
 create mode 100644 crates/futures/LICENSE-MPL-2.0
 create mode 100644 crates/web-sys/LICENSE-BSD-3-Clause
 create mode 100644 crates/web-sys/LICENSE-MPL-2.0

diff --git a/0001-Add-additional-licenses-to-futures-and-web-sys-crate.patch b/0001-Add-additional-licenses-to-futures-and-web-sys-crate.patch
new file mode 100644
index 00000000000..e456fc130f3
--- /dev/null
+++ b/0001-Add-additional-licenses-to-futures-and-web-sys-crate.patch
@@ -0,0 +1,830 @@
+From f601a40e0b27e9b761af626468df61b742fe584f Mon Sep 17 00:00:00 2001
+From: Martin Braenne 
+Date: Wed, 19 Mar 2025 14:48:22 +0000
+Subject: [PATCH] Add additional licenses to "futures" and "web-sys" crates.
+
+Various source files mention these licenses, but they have not so far
+been included as LICENSE files or referenced in the Cargo.toml files for
+the crates.
+
+The MPL 2.0 license is used by
+creates/futures/src/task/wait_async_polyfill.rs as well as many files in
+crates/web-sys/webidls/enabled.
+
+The BSD 3-Clause license is used by
+crates/web-sys/webidls/enabled/Streams.webidl.
+---
+ crates/futures/Cargo.toml           |   2 +-
+ crates/futures/LICENSE-MPL-2.0      | 373 ++++++++++++++++++++++++++++
+ crates/web-sys/Cargo.toml           |   2 +-
+ crates/web-sys/LICENSE-BSD-3-Clause |  11 +
+ crates/web-sys/LICENSE-MPL-2.0      | 373 ++++++++++++++++++++++++++++
+ 5 files changed, 759 insertions(+), 2 deletions(-)
+ create mode 100644 crates/futures/LICENSE-MPL-2.0
+ create mode 100644 crates/web-sys/LICENSE-BSD-3-Clause
+ create mode 100644 crates/web-sys/LICENSE-MPL-2.0
+
+diff --git a/crates/futures/Cargo.toml b/crates/futures/Cargo.toml
+index caf381c89..7b7f54cc8 100644
+--- a/crates/futures/Cargo.toml
++++ b/crates/futures/Cargo.toml
+@@ -5,7 +5,7 @@ documentation = "https://docs.rs/wasm-bindgen-futures"
+ edition = "2021"
+ homepage = "https://rustwasm.github.io/wasm-bindgen/"
+ include = ["/LICENSE-*", "/src"]
+-license = "MIT OR Apache-2.0"
++license = "(MIT OR Apache-2.0) AND MPL-2.0"
+ name = "wasm-bindgen-futures"
+ readme = "./README.md"
+ repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures"
+diff --git a/crates/futures/LICENSE-MPL-2.0 b/crates/futures/LICENSE-MPL-2.0
+new file mode 100644
+index 000000000..ee6256cdb
+--- /dev/null
++++ b/crates/futures/LICENSE-MPL-2.0
+@@ -0,0 +1,373 @@
++Mozilla Public License Version 2.0
++==================================
++
++1. Definitions
++--------------
++
++1.1. "Contributor"
++    means each individual or legal entity that creates, contributes to
++    the creation of, or owns Covered Software.
++
++1.2. "Contributor Version"
++    means the combination of the Contributions of others (if any) used
++    by a Contributor and that particular Contributor's Contribution.
++
++1.3. "Contribution"
++    means Covered Software of a particular Contributor.
++
++1.4. "Covered Software"
++    means Source Code Form to which the initial Contributor has attached
++    the notice in Exhibit A, the Executable Form of such Source Code
++    Form, and Modifications of such Source Code Form, in each case
++    including portions thereof.
++
++1.5. "Incompatible With Secondary Licenses"
++    means
++
++    (a) that the initial Contributor has attached the notice described
++        in Exhibit B to the Covered Software; or
++
++    (b) that the Covered Software was made available under the terms of
++        version 1.1 or earlier of the License, but not also under the
++        terms of a Secondary License.
++
++1.6. "Executable Form"
++    means any form of the work other than Source Code Form.
++
++1.7. "Larger Work"
++    means a work that combines Covered Software with other material, in 
++    a separate file or files, that is not Covered Software.
++
++1.8. "License"
++    means this document.
++
++1.9. "Licensable"
++    means having the right to grant, to the maximum extent possible,
++    whether at the time of the initial grant or subsequently, any and
++    all of the rights conveyed by this License.
++
++1.10. "Modifications"
++    means any of the following:
++
++    (a) any file in Source Code Form that results from an addition to,
++        deletion from, or modification of the contents of Covered
++        Software; or
++
++    (b) any new file in Source Code Form that contains any Covered
++        Software.
++
++1.11. "Patent Claims" of a Contributor
++    means any patent claim(s), including without limitation, method,
++    process, and apparatus claims, in any patent Licensable by such
++    Contributor that would be infringed, but for the grant of the
++    License, by the making, using, selling, offering for sale, having
++    made, import, or transfer of either its Contributions or its
++    Contributor Version.
++
++1.12. "Secondary License"
++    means either the GNU General Public License, Version 2.0, the GNU
++    Lesser General Public License, Version 2.1, the GNU Affero General
++    Public License, Version 3.0, or any later versions of those
++    licenses.
++
++1.13. "Source Code Form"
++    means the form of the work preferred for making modifications.
++
++1.14. "You" (or "Your")
++    means an individual or a legal entity exercising rights under this
++    License. For legal entities, "You" includes any entity that
++    controls, is controlled by, or is under common control with You. For
++    purposes of this definition, "control" means (a) the power, direct
++    or indirect, to cause the direction or management of such entity,
++    whether by contract or otherwise, or (b) ownership of more than
++    fifty percent (50%) of the outstanding shares or beneficial
++    ownership of such entity.
++
++2. License Grants and Conditions
++--------------------------------
++
++2.1. Grants
++
++Each Contributor hereby grants You a world-wide, royalty-free,
++non-exclusive license:
++
++(a) under intellectual property rights (other than patent or trademark)
++    Licensable by such Contributor to use, reproduce, make available,
++    modify, display, perform, distribute, and otherwise exploit its
++    Contributions, either on an unmodified basis, with Modifications, or
++    as part of a Larger Work; and
++
++(b) under Patent Claims of such Contributor to make, use, sell, offer
++    for sale, have made, import, and otherwise transfer either its
++    Contributions or its Contributor Version.
++
++2.2. Effective Date
++
++The licenses granted in Section 2.1 with respect to any Contribution
++become effective for each Contribution on the date the Contributor first
++distributes such Contribution.
++
++2.3. Limitations on Grant Scope
++
++The licenses granted in this Section 2 are the only rights granted under
++this License. No additional rights or licenses will be implied from the
++distribution or licensing of Covered Software under this License.
++Notwithstanding Section 2.1(b) above, no patent license is granted by a
++Contributor:
++
++(a) for any code that a Contributor has removed from Covered Software;
++    or
++
++(b) for infringements caused by: (i) Your and any other third party's
++    modifications of Covered Software, or (ii) the combination of its
++    Contributions with other software (except as part of its Contributor
++    Version); or
++
++(c) under Patent Claims infringed by Covered Software in the absence of
++    its Contributions.
++
++This License does not grant any rights in the trademarks, service marks,
++or logos of any Contributor (except as may be necessary to comply with
++the notice requirements in Section 3.4).
++
++2.4. Subsequent Licenses
++
++No Contributor makes additional grants as a result of Your choice to
++distribute the Covered Software under a subsequent version of this
++License (see Section 10.2) or under the terms of a Secondary License (if
++permitted under the terms of Section 3.3).
++
++2.5. Representation
++
++Each Contributor represents that the Contributor believes its
++Contributions are its original creation(s) or it has sufficient rights
++to grant the rights to its Contributions conveyed by this License.
++
++2.6. Fair Use
++
++This License is not intended to limit any rights You have under
++applicable copyright doctrines of fair use, fair dealing, or other
++equivalents.
++
++2.7. Conditions
++
++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
++in Section 2.1.
++
++3. Responsibilities
++-------------------
++
++3.1. Distribution of Source Form
++
++All distribution of Covered Software in Source Code Form, including any
++Modifications that You create or to which You contribute, must be under
++the terms of this License. You must inform recipients that the Source
++Code Form of the Covered Software is governed by the terms of this
++License, and how they can obtain a copy of this License. You may not
++attempt to alter or restrict the recipients' rights in the Source Code
++Form.
++
++3.2. Distribution of Executable Form
++
++If You distribute Covered Software in Executable Form then:
++
++(a) such Covered Software must also be made available in Source Code
++    Form, as described in Section 3.1, and You must inform recipients of
++    the Executable Form how they can obtain a copy of such Source Code
++    Form by reasonable means in a timely manner, at a charge no more
++    than the cost of distribution to the recipient; and
++
++(b) You may distribute such Executable Form under the terms of this
++    License, or sublicense it under different terms, provided that the
++    license for the Executable Form does not attempt to limit or alter
++    the recipients' rights in the Source Code Form under this License.
++
++3.3. Distribution of a Larger Work
++
++You may create and distribute a Larger Work under terms of Your choice,
++provided that You also comply with the requirements of this License for
++the Covered Software. If the Larger Work is a combination of Covered
++Software with a work governed by one or more Secondary Licenses, and the
++Covered Software is not Incompatible With Secondary Licenses, this
++License permits You to additionally distribute such Covered Software
++under the terms of such Secondary License(s), so that the recipient of
++the Larger Work may, at their option, further distribute the Covered
++Software under the terms of either this License or such Secondary
++License(s).
++
++3.4. Notices
++
++You may not remove or alter the substance of any license notices
++(including copyright notices, patent notices, disclaimers of warranty,
++or limitations of liability) contained within the Source Code Form of
++the Covered Software, except that You may alter any license notices to
++the extent required to remedy known factual inaccuracies.
++
++3.5. Application of Additional Terms
++
++You may choose to offer, and to charge a fee for, warranty, support,
++indemnity or liability obligations to one or more recipients of Covered
++Software. However, You may do so only on Your own behalf, and not on
++behalf of any Contributor. You must make it absolutely clear that any
++such warranty, support, indemnity, or liability obligation is offered by
++You alone, and You hereby agree to indemnify every Contributor for any
++liability incurred by such Contributor as a result of warranty, support,
++indemnity or liability terms You offer. You may include additional
++disclaimers of warranty and limitations of liability specific to any
++jurisdiction.
++
++4. Inability to Comply Due to Statute or Regulation
++---------------------------------------------------
++
++If it is impossible for You to comply with any of the terms of this
++License with respect to some or all of the Covered Software due to
++statute, judicial order, or regulation then You must: (a) comply with
++the terms of this License to the maximum extent possible; and (b)
++describe the limitations and the code they affect. Such description must
++be placed in a text file included with all distributions of the Covered
++Software under this License. Except to the extent prohibited by statute
++or regulation, such description must be sufficiently detailed for a
++recipient of ordinary skill to be able to understand it.
++
++5. Termination
++--------------
++
++5.1. The rights granted under this License will terminate automatically
++if You fail to comply with any of its terms. However, if You become
++compliant, then the rights granted under this License from a particular
++Contributor are reinstated (a) provisionally, unless and until such
++Contributor explicitly and finally terminates Your grants, and (b) on an
++ongoing basis, if such Contributor fails to notify You of the
++non-compliance by some reasonable means prior to 60 days after You have
++come back into compliance. Moreover, Your grants from a particular
++Contributor are reinstated on an ongoing basis if such Contributor
++notifies You of the non-compliance by some reasonable means, this is the
++first time You have received notice of non-compliance with this License
++from such Contributor, and You become compliant prior to 30 days after
++Your receipt of the notice.
++
++5.2. If You initiate litigation against any entity by asserting a patent
++infringement claim (excluding declaratory judgment actions,
++counter-claims, and cross-claims) alleging that a Contributor Version
++directly or indirectly infringes any patent, then the rights granted to
++You by any and all Contributors for the Covered Software under Section
++2.1 of this License shall terminate.
++
++5.3. In the event of termination under Sections 5.1 or 5.2 above, all
++end user license agreements (excluding distributors and resellers) which
++have been validly granted by You or Your distributors under this License
++prior to termination shall survive termination.
++
++************************************************************************
++*                                                                      *
++*  6. Disclaimer of Warranty                                           *
++*  -------------------------                                           *
++*                                                                      *
++*  Covered Software is provided under this License on an "as is"       *
++*  basis, without warranty of any kind, either expressed, implied, or  *
++*  statutory, including, without limitation, warranties that the       *
++*  Covered Software is free of defects, merchantable, fit for a        *
++*  particular purpose or non-infringing. The entire risk as to the     *
++*  quality and performance of the Covered Software is with You.        *
++*  Should any Covered Software prove defective in any respect, You     *
++*  (not any Contributor) assume the cost of any necessary servicing,   *
++*  repair, or correction. This disclaimer of warranty constitutes an   *
++*  essential part of this License. No use of any Covered Software is   *
++*  authorized under this License except under this disclaimer.         *
++*                                                                      *
++************************************************************************
++
++************************************************************************
++*                                                                      *
++*  7. Limitation of Liability                                          *
++*  --------------------------                                          *
++*                                                                      *
++*  Under no circumstances and under no legal theory, whether tort      *
++*  (including negligence), contract, or otherwise, shall any           *
++*  Contributor, or anyone who distributes Covered Software as          *
++*  permitted above, be liable to You for any direct, indirect,         *
++*  special, incidental, or consequential damages of any character      *
++*  including, without limitation, damages for lost profits, loss of    *
++*  goodwill, work stoppage, computer failure or malfunction, or any    *
++*  and all other commercial damages or losses, even if such party      *
++*  shall have been informed of the possibility of such damages. This   *
++*  limitation of liability shall not apply to liability for death or   *
++*  personal injury resulting from such party's negligence to the       *
++*  extent applicable law prohibits such limitation. Some               *
++*  jurisdictions do not allow the exclusion or limitation of           *
++*  incidental or consequential damages, so this exclusion and          *
++*  limitation may not apply to You.                                    *
++*                                                                      *
++************************************************************************
++
++8. Litigation
++-------------
++
++Any litigation relating to this License may be brought only in the
++courts of a jurisdiction where the defendant maintains its principal
++place of business and such litigation shall be governed by laws of that
++jurisdiction, without reference to its conflict-of-law provisions.
++Nothing in this Section shall prevent a party's ability to bring
++cross-claims or counter-claims.
++
++9. Miscellaneous
++----------------
++
++This License represents the complete agreement concerning the subject
++matter hereof. If any provision of this License is held to be
++unenforceable, such provision shall be reformed only to the extent
++necessary to make it enforceable. Any law or regulation which provides
++that the language of a contract shall be construed against the drafter
++shall not be used to construe this License against a Contributor.
++
++10. Versions of the License
++---------------------------
++
++10.1. New Versions
++
++Mozilla Foundation is the license steward. Except as provided in Section
++10.3, no one other than the license steward has the right to modify or
++publish new versions of this License. Each version will be given a
++distinguishing version number.
++
++10.2. Effect of New Versions
++
++You may distribute the Covered Software under the terms of the version
++of the License under which You originally received the Covered Software,
++or under the terms of any subsequent version published by the license
++steward.
++
++10.3. Modified Versions
++
++If you create software not governed by this License, and you want to
++create a new license for such software, you may create and use a
++modified version of this License if you rename the license and remove
++any references to the name of the license steward (except to note that
++such modified license differs from this License).
++
++10.4. Distributing Source Code Form that is Incompatible With Secondary
++Licenses
++
++If You choose to distribute Source Code Form that is Incompatible With
++Secondary Licenses under the terms of this version of the License, the
++notice described in Exhibit B of this License must be attached.
++
++Exhibit A - Source Code Form License Notice
++-------------------------------------------
++
++  This Source Code Form is subject to the terms of the Mozilla Public
++  License, v. 2.0. If a copy of the MPL was not distributed with this
++  file, You can obtain one at https://mozilla.org/MPL/2.0/.
++
++If it is not possible or desirable to put the notice in a particular
++file, then You may include the notice in a location (such as a LICENSE
++file in a relevant directory) where a recipient would be likely to look
++for such a notice.
++
++You may add additional accurate notices of copyright ownership.
++
++Exhibit B - "Incompatible With Secondary Licenses" Notice
++---------------------------------------------------------
++
++  This Source Code Form is "Incompatible With Secondary Licenses", as
++  defined by the Mozilla Public License, v. 2.0.
+diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml
+index c3a73eaa3..24c73d8de 100644
+--- a/crates/web-sys/Cargo.toml
++++ b/crates/web-sys/Cargo.toml
+@@ -7,7 +7,7 @@ documentation = "https://rustwasm.github.io/wasm-bindgen/api/web_sys/"
+ edition = "2021"
+ homepage = "https://rustwasm.github.io/wasm-bindgen/web-sys/index.html"
+ include = ["/LICENSE-*", "/src"]
+-license = "MIT OR Apache-2.0"
++license = "(MIT OR Apache-2.0) AND MPL-2.0 AND BSD-3-Clause"
+ name = "web-sys"
+ readme = "./README.md"
+ repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys"
+diff --git a/crates/web-sys/LICENSE-BSD-3-Clause b/crates/web-sys/LICENSE-BSD-3-Clause
+new file mode 100644
+index 000000000..db7d3cd12
+--- /dev/null
++++ b/crates/web-sys/LICENSE-BSD-3-Clause
+@@ -0,0 +1,11 @@
++Copyright © WHATWG (Apple, Google, Mozilla, Microsoft).
++
++Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
++
++1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
++
++2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
++
++3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
++
++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+diff --git a/crates/web-sys/LICENSE-MPL-2.0 b/crates/web-sys/LICENSE-MPL-2.0
+new file mode 100644
+index 000000000..ee6256cdb
+--- /dev/null
++++ b/crates/web-sys/LICENSE-MPL-2.0
+@@ -0,0 +1,373 @@
++Mozilla Public License Version 2.0
++==================================
++
++1. Definitions
++--------------
++
++1.1. "Contributor"
++    means each individual or legal entity that creates, contributes to
++    the creation of, or owns Covered Software.
++
++1.2. "Contributor Version"
++    means the combination of the Contributions of others (if any) used
++    by a Contributor and that particular Contributor's Contribution.
++
++1.3. "Contribution"
++    means Covered Software of a particular Contributor.
++
++1.4. "Covered Software"
++    means Source Code Form to which the initial Contributor has attached
++    the notice in Exhibit A, the Executable Form of such Source Code
++    Form, and Modifications of such Source Code Form, in each case
++    including portions thereof.
++
++1.5. "Incompatible With Secondary Licenses"
++    means
++
++    (a) that the initial Contributor has attached the notice described
++        in Exhibit B to the Covered Software; or
++
++    (b) that the Covered Software was made available under the terms of
++        version 1.1 or earlier of the License, but not also under the
++        terms of a Secondary License.
++
++1.6. "Executable Form"
++    means any form of the work other than Source Code Form.
++
++1.7. "Larger Work"
++    means a work that combines Covered Software with other material, in 
++    a separate file or files, that is not Covered Software.
++
++1.8. "License"
++    means this document.
++
++1.9. "Licensable"
++    means having the right to grant, to the maximum extent possible,
++    whether at the time of the initial grant or subsequently, any and
++    all of the rights conveyed by this License.
++
++1.10. "Modifications"
++    means any of the following:
++
++    (a) any file in Source Code Form that results from an addition to,
++        deletion from, or modification of the contents of Covered
++        Software; or
++
++    (b) any new file in Source Code Form that contains any Covered
++        Software.
++
++1.11. "Patent Claims" of a Contributor
++    means any patent claim(s), including without limitation, method,
++    process, and apparatus claims, in any patent Licensable by such
++    Contributor that would be infringed, but for the grant of the
++    License, by the making, using, selling, offering for sale, having
++    made, import, or transfer of either its Contributions or its
++    Contributor Version.
++
++1.12. "Secondary License"
++    means either the GNU General Public License, Version 2.0, the GNU
++    Lesser General Public License, Version 2.1, the GNU Affero General
++    Public License, Version 3.0, or any later versions of those
++    licenses.
++
++1.13. "Source Code Form"
++    means the form of the work preferred for making modifications.
++
++1.14. "You" (or "Your")
++    means an individual or a legal entity exercising rights under this
++    License. For legal entities, "You" includes any entity that
++    controls, is controlled by, or is under common control with You. For
++    purposes of this definition, "control" means (a) the power, direct
++    or indirect, to cause the direction or management of such entity,
++    whether by contract or otherwise, or (b) ownership of more than
++    fifty percent (50%) of the outstanding shares or beneficial
++    ownership of such entity.
++
++2. License Grants and Conditions
++--------------------------------
++
++2.1. Grants
++
++Each Contributor hereby grants You a world-wide, royalty-free,
++non-exclusive license:
++
++(a) under intellectual property rights (other than patent or trademark)
++    Licensable by such Contributor to use, reproduce, make available,
++    modify, display, perform, distribute, and otherwise exploit its
++    Contributions, either on an unmodified basis, with Modifications, or
++    as part of a Larger Work; and
++
++(b) under Patent Claims of such Contributor to make, use, sell, offer
++    for sale, have made, import, and otherwise transfer either its
++    Contributions or its Contributor Version.
++
++2.2. Effective Date
++
++The licenses granted in Section 2.1 with respect to any Contribution
++become effective for each Contribution on the date the Contributor first
++distributes such Contribution.
++
++2.3. Limitations on Grant Scope
++
++The licenses granted in this Section 2 are the only rights granted under
++this License. No additional rights or licenses will be implied from the
++distribution or licensing of Covered Software under this License.
++Notwithstanding Section 2.1(b) above, no patent license is granted by a
++Contributor:
++
++(a) for any code that a Contributor has removed from Covered Software;
++    or
++
++(b) for infringements caused by: (i) Your and any other third party's
++    modifications of Covered Software, or (ii) the combination of its
++    Contributions with other software (except as part of its Contributor
++    Version); or
++
++(c) under Patent Claims infringed by Covered Software in the absence of
++    its Contributions.
++
++This License does not grant any rights in the trademarks, service marks,
++or logos of any Contributor (except as may be necessary to comply with
++the notice requirements in Section 3.4).
++
++2.4. Subsequent Licenses
++
++No Contributor makes additional grants as a result of Your choice to
++distribute the Covered Software under a subsequent version of this
++License (see Section 10.2) or under the terms of a Secondary License (if
++permitted under the terms of Section 3.3).
++
++2.5. Representation
++
++Each Contributor represents that the Contributor believes its
++Contributions are its original creation(s) or it has sufficient rights
++to grant the rights to its Contributions conveyed by this License.
++
++2.6. Fair Use
++
++This License is not intended to limit any rights You have under
++applicable copyright doctrines of fair use, fair dealing, or other
++equivalents.
++
++2.7. Conditions
++
++Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
++in Section 2.1.
++
++3. Responsibilities
++-------------------
++
++3.1. Distribution of Source Form
++
++All distribution of Covered Software in Source Code Form, including any
++Modifications that You create or to which You contribute, must be under
++the terms of this License. You must inform recipients that the Source
++Code Form of the Covered Software is governed by the terms of this
++License, and how they can obtain a copy of this License. You may not
++attempt to alter or restrict the recipients' rights in the Source Code
++Form.
++
++3.2. Distribution of Executable Form
++
++If You distribute Covered Software in Executable Form then:
++
++(a) such Covered Software must also be made available in Source Code
++    Form, as described in Section 3.1, and You must inform recipients of
++    the Executable Form how they can obtain a copy of such Source Code
++    Form by reasonable means in a timely manner, at a charge no more
++    than the cost of distribution to the recipient; and
++
++(b) You may distribute such Executable Form under the terms of this
++    License, or sublicense it under different terms, provided that the
++    license for the Executable Form does not attempt to limit or alter
++    the recipients' rights in the Source Code Form under this License.
++
++3.3. Distribution of a Larger Work
++
++You may create and distribute a Larger Work under terms of Your choice,
++provided that You also comply with the requirements of this License for
++the Covered Software. If the Larger Work is a combination of Covered
++Software with a work governed by one or more Secondary Licenses, and the
++Covered Software is not Incompatible With Secondary Licenses, this
++License permits You to additionally distribute such Covered Software
++under the terms of such Secondary License(s), so that the recipient of
++the Larger Work may, at their option, further distribute the Covered
++Software under the terms of either this License or such Secondary
++License(s).
++
++3.4. Notices
++
++You may not remove or alter the substance of any license notices
++(including copyright notices, patent notices, disclaimers of warranty,
++or limitations of liability) contained within the Source Code Form of
++the Covered Software, except that You may alter any license notices to
++the extent required to remedy known factual inaccuracies.
++
++3.5. Application of Additional Terms
++
++You may choose to offer, and to charge a fee for, warranty, support,
++indemnity or liability obligations to one or more recipients of Covered
++Software. However, You may do so only on Your own behalf, and not on
++behalf of any Contributor. You must make it absolutely clear that any
++such warranty, support, indemnity, or liability obligation is offered by
++You alone, and You hereby agree to indemnify every Contributor for any
++liability incurred by such Contributor as a result of warranty, support,
++indemnity or liability terms You offer. You may include additional
++disclaimers of warranty and limitations of liability specific to any
++jurisdiction.
++
++4. Inability to Comply Due to Statute or Regulation
++---------------------------------------------------
++
++If it is impossible for You to comply with any of the terms of this
++License with respect to some or all of the Covered Software due to
++statute, judicial order, or regulation then You must: (a) comply with
++the terms of this License to the maximum extent possible; and (b)
++describe the limitations and the code they affect. Such description must
++be placed in a text file included with all distributions of the Covered
++Software under this License. Except to the extent prohibited by statute
++or regulation, such description must be sufficiently detailed for a
++recipient of ordinary skill to be able to understand it.
++
++5. Termination
++--------------
++
++5.1. The rights granted under this License will terminate automatically
++if You fail to comply with any of its terms. However, if You become
++compliant, then the rights granted under this License from a particular
++Contributor are reinstated (a) provisionally, unless and until such
++Contributor explicitly and finally terminates Your grants, and (b) on an
++ongoing basis, if such Contributor fails to notify You of the
++non-compliance by some reasonable means prior to 60 days after You have
++come back into compliance. Moreover, Your grants from a particular
++Contributor are reinstated on an ongoing basis if such Contributor
++notifies You of the non-compliance by some reasonable means, this is the
++first time You have received notice of non-compliance with this License
++from such Contributor, and You become compliant prior to 30 days after
++Your receipt of the notice.
++
++5.2. If You initiate litigation against any entity by asserting a patent
++infringement claim (excluding declaratory judgment actions,
++counter-claims, and cross-claims) alleging that a Contributor Version
++directly or indirectly infringes any patent, then the rights granted to
++You by any and all Contributors for the Covered Software under Section
++2.1 of this License shall terminate.
++
++5.3. In the event of termination under Sections 5.1 or 5.2 above, all
++end user license agreements (excluding distributors and resellers) which
++have been validly granted by You or Your distributors under this License
++prior to termination shall survive termination.
++
++************************************************************************
++*                                                                      *
++*  6. Disclaimer of Warranty                                           *
++*  -------------------------                                           *
++*                                                                      *
++*  Covered Software is provided under this License on an "as is"       *
++*  basis, without warranty of any kind, either expressed, implied, or  *
++*  statutory, including, without limitation, warranties that the       *
++*  Covered Software is free of defects, merchantable, fit for a        *
++*  particular purpose or non-infringing. The entire risk as to the     *
++*  quality and performance of the Covered Software is with You.        *
++*  Should any Covered Software prove defective in any respect, You     *
++*  (not any Contributor) assume the cost of any necessary servicing,   *
++*  repair, or correction. This disclaimer of warranty constitutes an   *
++*  essential part of this License. No use of any Covered Software is   *
++*  authorized under this License except under this disclaimer.         *
++*                                                                      *
++************************************************************************
++
++************************************************************************
++*                                                                      *
++*  7. Limitation of Liability                                          *
++*  --------------------------                                          *
++*                                                                      *
++*  Under no circumstances and under no legal theory, whether tort      *
++*  (including negligence), contract, or otherwise, shall any           *
++*  Contributor, or anyone who distributes Covered Software as          *
++*  permitted above, be liable to You for any direct, indirect,         *
++*  special, incidental, or consequential damages of any character      *
++*  including, without limitation, damages for lost profits, loss of    *
++*  goodwill, work stoppage, computer failure or malfunction, or any    *
++*  and all other commercial damages or losses, even if such party      *
++*  shall have been informed of the possibility of such damages. This   *
++*  limitation of liability shall not apply to liability for death or   *
++*  personal injury resulting from such party's negligence to the       *
++*  extent applicable law prohibits such limitation. Some               *
++*  jurisdictions do not allow the exclusion or limitation of           *
++*  incidental or consequential damages, so this exclusion and          *
++*  limitation may not apply to You.                                    *
++*                                                                      *
++************************************************************************
++
++8. Litigation
++-------------
++
++Any litigation relating to this License may be brought only in the
++courts of a jurisdiction where the defendant maintains its principal
++place of business and such litigation shall be governed by laws of that
++jurisdiction, without reference to its conflict-of-law provisions.
++Nothing in this Section shall prevent a party's ability to bring
++cross-claims or counter-claims.
++
++9. Miscellaneous
++----------------
++
++This License represents the complete agreement concerning the subject
++matter hereof. If any provision of this License is held to be
++unenforceable, such provision shall be reformed only to the extent
++necessary to make it enforceable. Any law or regulation which provides
++that the language of a contract shall be construed against the drafter
++shall not be used to construe this License against a Contributor.
++
++10. Versions of the License
++---------------------------
++
++10.1. New Versions
++
++Mozilla Foundation is the license steward. Except as provided in Section
++10.3, no one other than the license steward has the right to modify or
++publish new versions of this License. Each version will be given a
++distinguishing version number.
++
++10.2. Effect of New Versions
++
++You may distribute the Covered Software under the terms of the version
++of the License under which You originally received the Covered Software,
++or under the terms of any subsequent version published by the license
++steward.
++
++10.3. Modified Versions
++
++If you create software not governed by this License, and you want to
++create a new license for such software, you may create and use a
++modified version of this License if you rename the license and remove
++any references to the name of the license steward (except to note that
++such modified license differs from this License).
++
++10.4. Distributing Source Code Form that is Incompatible With Secondary
++Licenses
++
++If You choose to distribute Source Code Form that is Incompatible With
++Secondary Licenses under the terms of this version of the License, the
++notice described in Exhibit B of this License must be attached.
++
++Exhibit A - Source Code Form License Notice
++-------------------------------------------
++
++  This Source Code Form is subject to the terms of the Mozilla Public
++  License, v. 2.0. If a copy of the MPL was not distributed with this
++  file, You can obtain one at https://mozilla.org/MPL/2.0/.
++
++If it is not possible or desirable to put the notice in a particular
++file, then You may include the notice in a location (such as a LICENSE
++file in a relevant directory) where a recipient would be likely to look
++for such a notice.
++
++You may add additional accurate notices of copyright ownership.
++
++Exhibit B - "Incompatible With Secondary Licenses" Notice
++---------------------------------------------------------
++
++  This Source Code Form is "Incompatible With Secondary Licenses", as
++  defined by the Mozilla Public License, v. 2.0.
+-- 
+2.49.0.rc1.451.g8f38331e32-goog
+
diff --git a/crates/futures/Cargo.toml b/crates/futures/Cargo.toml
index caf381c899b..7b7f54cc8f1 100644
--- a/crates/futures/Cargo.toml
+++ b/crates/futures/Cargo.toml
@@ -5,7 +5,7 @@ documentation = "https://docs.rs/wasm-bindgen-futures"
 edition = "2021"
 homepage = "https://rustwasm.github.io/wasm-bindgen/"
 include = ["/LICENSE-*", "/src"]
-license = "MIT OR Apache-2.0"
+license = "(MIT OR Apache-2.0) AND MPL-2.0"
 name = "wasm-bindgen-futures"
 readme = "./README.md"
 repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures"
diff --git a/crates/futures/LICENSE-MPL-2.0 b/crates/futures/LICENSE-MPL-2.0
new file mode 100644
index 00000000000..ee6256cdb62
--- /dev/null
+++ b/crates/futures/LICENSE-MPL-2.0
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/crates/web-sys/Cargo.toml b/crates/web-sys/Cargo.toml
index c3a73eaa363..24c73d8deaf 100644
--- a/crates/web-sys/Cargo.toml
+++ b/crates/web-sys/Cargo.toml
@@ -7,7 +7,7 @@ documentation = "https://rustwasm.github.io/wasm-bindgen/api/web_sys/"
 edition = "2021"
 homepage = "https://rustwasm.github.io/wasm-bindgen/web-sys/index.html"
 include = ["/LICENSE-*", "/src"]
-license = "MIT OR Apache-2.0"
+license = "(MIT OR Apache-2.0) AND MPL-2.0 AND BSD-3-Clause"
 name = "web-sys"
 readme = "./README.md"
 repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/web-sys"
diff --git a/crates/web-sys/LICENSE-BSD-3-Clause b/crates/web-sys/LICENSE-BSD-3-Clause
new file mode 100644
index 00000000000..db7d3cd120f
--- /dev/null
+++ b/crates/web-sys/LICENSE-BSD-3-Clause
@@ -0,0 +1,11 @@
+Copyright © WHATWG (Apple, Google, Mozilla, Microsoft).
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/crates/web-sys/LICENSE-MPL-2.0 b/crates/web-sys/LICENSE-MPL-2.0
new file mode 100644
index 00000000000..ee6256cdb62
--- /dev/null
+++ b/crates/web-sys/LICENSE-MPL-2.0
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.

From 6a670043868eecbec0919261d147a4be036c8ec4 Mon Sep 17 00:00:00 2001
From: google-yfyang 
Date: Fri, 11 Apr 2025 11:18:17 -0400
Subject: [PATCH 17/17] Expose more globals for emscripten

---
 crates/cli-support/src/js/binding.rs |  66 +++--
 crates/cli-support/src/js/mod.rs     | 429 +++++++++++++++++++--------
 2 files changed, 338 insertions(+), 157 deletions(-)

diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs
index f1d6af9e258..c04b0f5a83d 100644
--- a/crates/cli-support/src/js/binding.rs
+++ b/crates/cli-support/src/js/binding.rs
@@ -613,32 +613,32 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         self.stack.push(arg);
     }
 
-    fn assert_class(&mut self, arg: &str, class: &str) {
-        self.cx.expose_assert_class();
+    fn assert_class(&mut self, arg: &str, class: &str, import_deps: &mut HashSet) {
+        self.cx.expose_assert_class(import_deps);
         self.prelude(&format!("_assertClass({}, {});", arg, class));
     }
 
-    fn assert_number(&mut self, arg: &str) {
+    fn assert_number(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_assert_num();
+        self.cx.expose_assert_num(import_deps);
         self.prelude(&format!("_assertNum({});", arg));
     }
 
-    fn assert_bigint(&mut self, arg: &str) {
+    fn assert_bigint(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_assert_bigint();
+        self.cx.expose_assert_bigint(import_deps);
         self.prelude(&format!("_assertBigInt({});", arg));
     }
 
-    fn assert_bool(&mut self, arg: &str) {
+    fn assert_bool(&mut self, arg: &str, import_deps: &mut HashSet) {
         if !self.cx.config.debug {
             return;
         }
-        self.cx.expose_assert_bool();
+        self.cx.expose_assert_bool(import_deps);
         self.prelude(&format!("_assertBoolean({});", arg));
     }
 
@@ -648,7 +648,7 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         }
         self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
-        self.assert_number(arg);
+        self.assert_number(arg, import_deps);
         self.prelude("}");
     }
 
@@ -668,7 +668,7 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         }
         self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
-        self.assert_bigint(arg);
+        self.assert_bigint(arg, import_deps);
         self.prelude("}");
     }
 
@@ -678,7 +678,7 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
         }
         self.cx.expose_is_like_none(import_deps);
         self.prelude(&format!("if (!isLikeNone({})) {{", arg));
-        self.assert_bool(arg);
+        self.assert_bool(arg, import_deps);
         self.prelude("}");
     }
 
@@ -839,7 +839,7 @@ fn instruction(
 
         Instruction::Int32ToWasm => {
             let val = js.pop();
-            js.assert_number(&val);
+            js.assert_number(&val, import_deps);
             js.push(val);
         }
         Instruction::WasmToInt32 { unsigned_32 } => {
@@ -856,7 +856,7 @@ fn instruction(
 
         Instruction::Int64ToWasm => {
             let val = js.pop();
-            js.assert_bigint(&val);
+            js.assert_bigint(&val, import_deps);
             js.push(val);
         }
         Instruction::WasmToInt64 { unsigned } => {
@@ -870,7 +870,7 @@ fn instruction(
 
         Instruction::Int128ToWasm => {
             let val = js.pop();
-            js.assert_bigint(&val);
+            js.assert_bigint(&val, import_deps);
             let (low, high) = int128_to_int64x2(&val);
             js.push(low);
             js.push(high);
@@ -1028,7 +1028,7 @@ fn instruction(
 
         Instruction::I32FromBool => {
             let val = js.pop();
-            js.assert_bool(&val);
+            js.assert_bool(&val, import_deps);
             // JS will already coerce booleans into numbers for us
             js.push(val);
         }
@@ -1049,7 +1049,7 @@ fn instruction(
         }
 
         Instruction::I32FromExternrefBorrow => {
-            js.cx.expose_borrowed_objects();
+            js.cx.expose_borrowed_objects(import_deps);
             js.cx.expose_global_stack_pointer();
             let val = js.pop();
             js.push(format!("addBorrowedObject({})", val));
@@ -1058,7 +1058,7 @@ fn instruction(
 
         Instruction::I32FromExternrefRustOwned { class } => {
             let val = js.pop();
-            js.assert_class(&val, class);
+            js.assert_class(&val, class, import_deps);
             js.assert_not_moved(&val);
             let i = js.tmp();
             js.prelude(&format!("var ptr{} = {}.__destroy_into_raw();", i, val));
@@ -1067,7 +1067,7 @@ fn instruction(
 
         Instruction::I32FromExternrefRustBorrow { class } => {
             let val = js.pop();
-            js.assert_class(&val, class);
+            js.assert_class(&val, class, import_deps);
             js.assert_not_moved(&val);
             js.push(format!("{}.__wbg_ptr", val));
         }
@@ -1078,7 +1078,7 @@ fn instruction(
             let i = js.tmp();
             js.prelude(&format!("let ptr{} = 0;", i));
             js.prelude(&format!("if (!isLikeNone({0})) {{", val));
-            js.assert_class(&val, class);
+            js.assert_class(&val, class, import_deps);
             js.assert_not_moved(&val);
             js.prelude(&format!("ptr{} = {}.__destroy_into_raw();", i, val));
             js.prelude("}");
@@ -1224,7 +1224,7 @@ fn instruction(
                     .expose_take_from_externref_table(table, drop)?
                     .to_string()
             } else {
-                js.cx.expose_take_object();
+                js.cx.expose_take_object(import_deps);
                 "takeObject".to_string()
             };
             // is_err is popped first. The original layout was: ResultAbi {
@@ -1253,7 +1253,7 @@ fn instruction(
                     .expose_take_from_externref_table(table, drop)?
                     .to_string()
             } else {
-                js.cx.expose_take_object();
+                js.cx.expose_take_object(import_deps);
                 "takeObject".to_string()
             };
             let is_err = js.pop();
@@ -1366,7 +1366,7 @@ fn instruction(
                     .expose_take_from_externref_table(table, drop)?
                     .to_string()
             } else {
-                js.cx.expose_take_object();
+                js.cx.expose_take_object(import_deps);
                 "takeObject".to_string()
             };
             let val = js.pop();
@@ -1417,7 +1417,9 @@ fn instruction(
             let ptr = js.pop();
             let tmp = js.tmp();
 
-            let get = js.cx.expose_get_cached_string_from_wasm(*mem, *table)?;
+            let get = js
+                .cx
+                .expose_get_cached_string_from_wasm(*mem, *table, import_deps)?;
 
             js.prelude(&format!("var v{} = {}({}, {});", tmp, get, ptr, len));
 
@@ -1436,7 +1438,7 @@ fn instruction(
 
         Instruction::TableGet => {
             let val = js.pop();
-            js.cx.expose_get_object();
+            js.cx.expose_get_object(import_deps);
             js.push(format!("getObject({})", val));
         }
 
@@ -1493,7 +1495,9 @@ fn instruction(
         Instruction::VectorLoad { kind, mem, free } => {
             let len = js.pop();
             let ptr = js.pop();
-            let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
+            let f = js
+                .cx
+                .expose_get_vector_from_wasm(kind.clone(), *mem, import_deps)?;
             let i = js.tmp();
             let free = js.cx.export_name_of(*free);
             js.prelude(&format!("var v{} = {}({}, {}).slice();", i, f, ptr, len));
@@ -1510,7 +1514,9 @@ fn instruction(
         Instruction::OptionVectorLoad { kind, mem, free } => {
             let len = js.pop();
             let ptr = js.pop();
-            let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
+            let f = js
+                .cx
+                .expose_get_vector_from_wasm(kind.clone(), *mem, import_deps)?;
             let i = js.tmp();
             let free = js.cx.export_name_of(*free);
             js.prelude(&format!("let v{};", i));
@@ -1530,14 +1536,18 @@ fn instruction(
         Instruction::View { kind, mem } => {
             let len = js.pop();
             let ptr = js.pop();
-            let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
+            let f = js
+                .cx
+                .expose_get_vector_from_wasm(kind.clone(), *mem, import_deps)?;
             js.push(format!("{f}({ptr}, {len})", ptr = ptr, len = len, f = f));
         }
 
         Instruction::OptionView { kind, mem } => {
             let len = js.pop();
             let ptr = js.pop();
-            let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem)?;
+            let f = js
+                .cx
+                .expose_get_vector_from_wasm(kind.clone(), *mem, import_deps)?;
             js.push(format!(
                 "{ptr} === 0 ? undefined : {f}({ptr}, {len})",
                 ptr = ptr,
diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs
index cae232ce5e5..64d94562899 100644
--- a/crates/cli-support/src/js/mod.rs
+++ b/crates/cli-support/src/js/mod.rs
@@ -987,11 +987,17 @@ __wbg_set_wasm(wasm);"
             OutputMode::Emscripten => {
             let mut global_emscripten_initializer: String = Default::default();
             for global_dep in self.emscripten_deps.iter() {
-                let mut global = "";
+                let mut global: String = "".to_string();
                 if global_dep == "'$WASM_VECTOR_LEN'" {
-                    global = "$WASM_VECTOR_LEN: '0',";
+                    global = "$WASM_VECTOR_LEN: '0',".to_string();
                 } else if global_dep == "'$TextEncoder'" {
-                    global = "$textEncoder: \"new TextEncoder()\",";
+                    global = "$textEncoder: \"new TextEncoder()\",".to_string();
+                } else if global_dep == "'$heap'" {
+                    global = format!("
+                    $heap: \"new Array({}).fill(undefined)\",
+                    \"heap.push({})\",", INITIAL_HEAP_OFFSET, INITIAL_HEAP_VALUES.join(", "));
+                } else if global_dep == "'$stack_pointer'" {
+                    global = format!("$stack_pointer : \"{}\",", INITIAL_HEAP_OFFSET)
                 }
 
                 if !global.is_empty() {
@@ -1435,7 +1441,9 @@ __wbg_set_wasm(wasm);"
         }
     }
 
-    fn expose_drop_ref(&mut self) {
+    fn expose_drop_ref(&mut self, import_deps: &mut HashSet) {
+        let func_name = "dropObject";
+        import_deps.insert(format!("'${func_name}'"));
         if !self.should_write_global("drop_ref") {
             return;
         }
@@ -1450,16 +1458,19 @@ __wbg_set_wasm(wasm);"
         //
         // Otherwise the free operation here is pretty simple, just appending to
         // the linked list of heap slots that are free.
-        self.global(&format!(
-            "
-            function dropObject(idx) {{
+        self.write_js_function(
+            &format!(
+                "
                 if (idx < {}) return;
                 heap[idx] = heap_next;
                 heap_next = idx;
-            }}
             ",
-            INITIAL_HEAP_OFFSET + INITIAL_HEAP_VALUES.len(),
-        ));
+                INITIAL_HEAP_OFFSET + INITIAL_HEAP_VALUES.len(),
+            ),
+            "dropObject",
+            "(idx)",
+            &[],
+        );
     }
 
     fn expose_global_heap(&mut self) {
@@ -1467,6 +1478,10 @@ __wbg_set_wasm(wasm);"
             return;
         }
         assert!(!self.config.externref);
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            self.emscripten_deps.insert("'$heap'".to_string());
+            return;
+        }
         self.global(&format!(
             "const heap = new Array({}).fill(undefined);",
             INITIAL_HEAP_OFFSET
@@ -1482,7 +1497,9 @@ __wbg_set_wasm(wasm);"
         self.global("let heap_next = heap.length;");
     }
 
-    fn expose_get_object(&mut self) {
+    fn expose_get_object(&mut self, import_deps: &mut HashSet) {
+        let func_name = "getObject";
+        import_deps.insert(format!("'$_{func_name}'"));
         if !self.should_write_global("get_object") {
             return;
         }
@@ -1500,37 +1517,41 @@ __wbg_set_wasm(wasm);"
         self.global("function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }");
     }
 
-    fn expose_assert_num(&mut self) {
+    fn expose_assert_num(&mut self, import_deps: &mut HashSet) {
+        let func_name = "_assertNum";
+        import_deps.insert(format!("'$_{func_name}'"));
         if !self.should_write_global("assert_num") {
             return;
         }
-        self.global(
+        self.write_js_function(
             "
-            function _assertNum(n) {
                 if (typeof(n) !== 'number') throw new Error(`expected a number argument, found ${typeof(n)}`);
-            }
-            ",
+            ", func_name, "(n)", &[]
         );
     }
 
-    fn expose_assert_bigint(&mut self) {
+    fn expose_assert_bigint(&mut self, import_deps: &mut HashSet) {
+        let func_name = "_assertBigInt";
+        import_deps.insert(format!("'$_{func_name}'"));
         if !self.should_write_global("assert_bigint") {
             return;
         }
-        self.global(
+        self.write_js_function(
             "
             function _assertBigInt(n) {
                 if (typeof(n) !== 'bigint') throw new Error(`expected a bigint argument, found ${typeof(n)}`);
             }
-            ",
+            ", func_name, "(n)", &[]
         );
     }
 
-    fn expose_assert_bool(&mut self) {
+    fn expose_assert_bool(&mut self, import_deps: &mut HashSet) {
+        let func_name = "_assertBoolean";
+        import_deps.insert(format!("'$_{func_name}'"));
         if !self.should_write_global("assert_bool") {
             return;
         }
-        self.global(
+        self.write_js_function(
             "
             function _assertBoolean(n) {
                 if (typeof(n) !== 'boolean') {
@@ -1538,6 +1559,9 @@ __wbg_set_wasm(wasm);"
                 }
             }
             ",
+            func_name,
+            "(n)",
+            &[],
         );
     }
 
@@ -1583,7 +1607,7 @@ __wbg_set_wasm(wasm);"
         let mut mem_formatted = format!("{mem}()");
         if matches!(self.config.mode, OutputMode::Emscripten) {
             text_encoder = "textEncoder";
-            mem_formatted = "HEAP8".to_string();
+            mem_formatted = format!("{}", mem.name);
         };
         // The first implementation we have for this is to use
         // `TextEncoder#encode` which has been around for quite some time.
@@ -1842,8 +1866,9 @@ __wbg_set_wasm(wasm);"
             }
             _ => {
                 self.expose_add_heap_object();
-                self.global(&format!(
-                    "
+                self.write_js_function(
+                    &format!(
+                        "
                         function {ret}(array, malloc) {{
                             const ptr = malloc(array.length * 4, 4) >>> 0;
                             const mem = {mem}();
@@ -1854,7 +1879,14 @@ __wbg_set_wasm(wasm);"
                             return ptr;
                         }}
                     ",
-                ));
+                    ),
+                    &format!("{ret}"),
+                    "(array, malloc)",
+                    &[
+                        "'$addHeapObject'".to_string(),
+                        "'$WASM_VECTOR_LEN'".to_string(),
+                    ],
+                );
             }
         }
         Ok(ret)
@@ -1875,19 +1907,26 @@ __wbg_set_wasm(wasm);"
             return Ok(ret);
         }
         self.expose_wasm_vector_len(import_deps);
-        self.global(&format!(
-            "
-            function {}(arg, malloc) {{
+        let view_formatted = if matches!(self.config.mode, OutputMode::Emscripten) {
+            &format!("{}", view.name)
+        } else {
+            &format!("{view}()")
+        };
+        self.write_js_function(
+            &format!(
+                "
                 const ptr = malloc(arg.length * {size}, {size}) >>> 0;
-                {}().set(arg, ptr / {size});
+                {}.set(arg, ptr / {size});
                 WASM_VECTOR_LEN = arg.length;
                 return ptr;
-            }}
             ",
-            ret,
-            view,
-            size = size
-        ));
+                view_formatted,
+                size = size
+            ),
+            &format!("{ret}"),
+            "(arg, malloc)",
+            &["'$WASM_VECTOR_LEN'".to_string()],
+        );
         Ok(ret)
     }
 
@@ -2015,10 +2054,10 @@ __wbg_set_wasm(wasm);"
                 "
                 ${}(ptr, len) {{
                     ptr = ptr >>> 0;
-                    return UTF8Decoder.decode(HEAP8.{}(ptr, ptr + len));
+                    return UTF8Decoder.decode({}.{}(ptr, ptr + len));
                 }},\n
                 ",
-                ret, method
+                ret, mem.name, method
             ));
         } else {
             self.global(&format!(
@@ -2038,11 +2077,12 @@ __wbg_set_wasm(wasm);"
         &mut self,
         memory: MemoryId,
         table: Option,
+        import_deps: &mut HashSet,
     ) -> Result {
         let get_object = if let Some(table) = table {
             self.expose_get_from_externref_table(table)?.to_string()
         } else {
-            self.expose_get_object();
+            self.expose_get_object(import_deps);
             "getObject".to_string()
         };
         let get_string = self.expose_get_string_from_wasm(memory)?;
@@ -2063,8 +2103,9 @@ __wbg_set_wasm(wasm);"
         //
         // If `ptr` and `len` are both `0` then that means it's `None`, in that case we rely upon
         // the fact that `getObject(0)` is guaranteed to be `undefined`.
-        self.global(&format!(
-            "
+        self.write_js_function(
+            &format!(
+                "
             function {name}(ptr, len) {{
                 if (ptr === 0) {{
                     return {get_object}(len);
@@ -2073,31 +2114,45 @@ __wbg_set_wasm(wasm);"
                 }}
             }}
             ",
-            name = ret,
-            get_string = get_string,
-            get_object = get_object
-        ));
+                name = ret,
+                get_string = get_string,
+                get_object = get_object
+            ),
+            &format!("{ret}"),
+            "(ptr, len)",
+            &[format!("'${get_object}'"), format!("'${get_string}'")],
+        );
         Ok(ret)
     }
 
-    fn expose_get_array_js_value_from_wasm(&mut self, memory: MemoryId) -> Result {
+    fn expose_get_array_js_value_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> Result {
         let mem = self.expose_dataview_memory(memory);
         let ret = MemView {
             name: "getArrayJsValueFromWasm".into(),
             num: mem.num,
         };
+        import_deps.insert(format!("'${ret}'"));
         if !self.should_write_global(ret.to_string()) {
             return Ok(ret);
         }
+        let mem_formatted = if matches!(self.config.mode, OutputMode::Emscripten) {
+            format!("{mem}()")
+        } else {
+            format!("{}", mem.name)
+        };
         match (self.aux.externref_table, self.aux.externref_drop_slice) {
             (Some(table), Some(drop)) => {
                 let table = self.export_name_of(table);
                 let drop = self.export_name_of(drop);
-                self.global(&format!(
-                    "
-                    function {}(ptr, len) {{
+                self.write_js_function(
+                    &format!(
+                        "
                         ptr = ptr >>> 0;
-                        const mem = {}();
+                        const mem = {};
                         const result = [];
                         for (let i = ptr; i < ptr + 4 * len; i += 4) {{
                             result.push(wasm.{}.get(mem.getUint32(i, true)));
@@ -2106,16 +2161,20 @@ __wbg_set_wasm(wasm);"
                         return result;
                     }}
                     ",
-                    ret, mem, table, drop,
-                ));
+                        mem_formatted, table, drop
+                    ),
+                    &format!("{ret}"),
+                    "(ptr, len)",
+                    &[format!("'${table}'"), format!("'${drop}'")],
+                );
             }
             _ => {
-                self.expose_take_object();
-                self.global(&format!(
-                    "
-                    function {}(ptr, len) {{
+                self.expose_take_object(import_deps);
+                self.write_js_function(
+                    &format!(
+                        "
                         ptr = ptr >>> 0;
-                        const mem = {}();
+                        const mem = {};
                         const result = [];
                         for (let i = ptr; i < ptr + 4 * len; i += 4) {{
                             result.push(takeObject(mem.getUint32(i, true)));
@@ -2123,69 +2182,123 @@ __wbg_set_wasm(wasm);"
                         return result;
                     }}
                     ",
-                    ret, mem,
-                ));
+                        mem_formatted,
+                    ),
+                    &format!("{ret}"),
+                    "(ptr, len)",
+                    &[format!("'$takeObject'")],
+                );
             }
         }
         Ok(ret)
     }
 
-    fn expose_get_array_i8_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_i8_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_int8_memory(memory);
-        self.arrayget("getArrayI8FromWasm", view, 1)
+        self.arrayget("getArrayI8FromWasm", view, 1, import_deps)
     }
 
-    fn expose_get_array_u8_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_u8_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_uint8_memory(memory);
-        self.arrayget("getArrayU8FromWasm", view, 1)
+        self.arrayget("getArrayU8FromWasm", view, 1, import_deps)
     }
 
-    fn expose_get_clamped_array_u8_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_clamped_array_u8_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_clamped_uint8_memory(memory);
-        self.arrayget("getClampedArrayU8FromWasm", view, 1)
+        self.arrayget("getClampedArrayU8FromWasm", view, 1, import_deps)
     }
 
-    fn expose_get_array_i16_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_i16_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_int16_memory(memory);
-        self.arrayget("getArrayI16FromWasm", view, 2)
+        self.arrayget("getArrayI16FromWasm", view, 2, import_deps)
     }
 
-    fn expose_get_array_u16_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_u16_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_uint16_memory(memory);
-        self.arrayget("getArrayU16FromWasm", view, 2)
+        self.arrayget("getArrayU16FromWasm", view, 2, import_deps)
     }
 
-    fn expose_get_array_i32_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_i32_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_int32_memory(memory);
-        self.arrayget("getArrayI32FromWasm", view, 4)
+        self.arrayget("getArrayI32FromWasm", view, 4, import_deps)
     }
 
-    fn expose_get_array_u32_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_u32_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_uint32_memory(memory);
-        self.arrayget("getArrayU32FromWasm", view, 4)
+        self.arrayget("getArrayU32FromWasm", view, 4, import_deps)
     }
 
-    fn expose_get_array_i64_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_i64_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_int64_memory(memory);
-        self.arrayget("getArrayI64FromWasm", view, 8)
+        self.arrayget("getArrayI64FromWasm", view, 8, import_deps)
     }
 
-    fn expose_get_array_u64_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_u64_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_uint64_memory(memory);
-        self.arrayget("getArrayU64FromWasm", view, 8)
+        self.arrayget("getArrayU64FromWasm", view, 8, import_deps)
     }
 
-    fn expose_get_array_f32_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_f32_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_f32_memory(memory);
-        self.arrayget("getArrayF32FromWasm", view, 4)
+        self.arrayget("getArrayF32FromWasm", view, 4, import_deps)
     }
 
-    fn expose_get_array_f64_from_wasm(&mut self, memory: MemoryId) -> MemView {
+    fn expose_get_array_f64_from_wasm(
+        &mut self,
+        memory: MemoryId,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let view = self.expose_f64_memory(memory);
-        self.arrayget("getArrayF64FromWasm", view, 8)
+        self.arrayget("getArrayF64FromWasm", view, 8, import_deps)
     }
 
-    fn arrayget(&mut self, name: &'static str, view: MemView, size: usize) -> MemView {
+    fn arrayget(
+        &mut self,
+        name: &'static str,
+        view: MemView,
+        size: usize,
+        import_deps: &mut HashSet,
+    ) -> MemView {
         let ret = MemView {
             name: name.into(),
             num: view.num,
@@ -2193,17 +2306,25 @@ __wbg_set_wasm(wasm);"
         if !self.should_write_global(name) {
             return ret;
         }
-        self.global(&format!(
-            "
-            function {name}(ptr, len) {{
+        let mem = if !matches!(self.config.mode, OutputMode::Emscripten) {
+            format!("{view}()")
+        } else {
+            format!("{}", view.name)
+        };
+        import_deps.insert(format!("'${ret}'"));
+        self.write_js_function(
+            &format!(
+                "
                 ptr = ptr >>> 0;
-                return {mem}().subarray(ptr / {size}, ptr / {size} + len);
-            }}
+                return {mem}.subarray(ptr / {size}, ptr / {size} + len);
             ",
-            name = ret,
-            mem = view,
-            size = size,
-        ));
+                mem = mem,
+                size = size,
+            ),
+            &format!("{ret}"),
+            "(ptr, len)",
+            &[],
+        );
         ret
     }
 
@@ -2256,6 +2377,26 @@ __wbg_set_wasm(wasm);"
     }
 
     fn memview(&mut self, kind: &'static str, memory: walrus::MemoryId) -> MemView {
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            // Emscripten provides its own version of getMemory
+            // so don't write out the memory function.
+            // See https://emscripten.org/docs/api_reference/preamble.js.html#type-accessors-for-the-memory-model
+            // for more details.
+            let emscripten_heap: &'static str = match kind {
+                "Int8Array" => "HEAP8",
+                "Uint8Array" | "Uint8ClampedArray" => "HEAPU8",
+                "Int16Array" => "HEAP16",
+                "Uint16Array" => "HEAPU16",
+                "Int32Array" => "HEAP32",
+                "Uint32Array" => "HEAPU32",
+                "Float32Array" => "HEAPF32",
+                "Float64Array" => "HEAPF64",
+                "DataView" => "HEAP_DATA_VIEW",
+                _ => "",
+            };
+            return self.memview_memory(emscripten_heap, memory);
+        }
+
         let view = self.memview_memory(kind, memory);
         if !self.should_write_global(view.name.clone()) {
             return view;
@@ -2283,13 +2424,6 @@ __wbg_set_wasm(wasm);"
             format!("{cache}.byteLength === 0", cache = cache)
         };
 
-        if matches!(self.config.mode, OutputMode::Emscripten) {
-            // Emscripten provides its own version of getMemory
-            // so don't write out the memory function.
-            // See https://emscripten.org/docs/api_reference/preamble.js.html#type-accessors-for-the-memory-model
-            // for more details.
-            return view;
-        }
         self.global(&format!("let {cache} = null;\n"));
 
         self.global(&format!(
@@ -2317,9 +2451,16 @@ __wbg_set_wasm(wasm);"
             .entry(memory)
             .or_insert((next, Default::default()));
         kinds.insert(kind);
-        MemView {
-            name: format!("get{}Memory", kind).into(),
-            num,
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            MemView {
+                name: format!("{}", kind).into(),
+                num,
+            }
+        } else {
+            MemView {
+                name: format!("get{}Memory", kind).into(),
+                num,
+            }
         }
     }
 
@@ -2332,18 +2473,21 @@ __wbg_set_wasm(wasm);"
         }
     }
 
-    fn expose_assert_class(&mut self) {
+    fn expose_assert_class(&mut self, import_deps: &mut HashSet) {
+        let func_name = "_assertClass";
+        import_deps.insert(format!("'${func_name}'"));
         if !self.should_write_global("assert_class") {
             return;
         }
-        self.global(
+        self.write_js_function(
             "
-            function _assertClass(instance, klass) {
                 if (!(instance instanceof klass)) {
                     throw new Error(`expected instance of ${klass.name}`);
                 }
-            }
             ",
+            func_name,
+            "(instance, klass)",
+            &[],
         );
     }
 
@@ -2351,10 +2495,16 @@ __wbg_set_wasm(wasm);"
         if !self.should_write_global("stack_pointer") {
             return;
         }
+        if matches!(self.config.mode, OutputMode::Emscripten) {
+            self.emscripten_deps.insert("'$stack_pointer'".to_string());
+            return;
+        }
         self.global(&format!("let stack_pointer = {};", INITIAL_HEAP_OFFSET));
     }
 
-    fn expose_borrowed_objects(&mut self) {
+    fn expose_borrowed_objects(&mut self, import_deps: &mut HashSet) {
+        let func_name = "addBorrowedObject";
+        import_deps.insert(format!("'${func_name}'"));
         if !self.should_write_global("borrowed_objects") {
             return;
         }
@@ -2365,31 +2515,35 @@ __wbg_set_wasm(wasm);"
         // after executing this. Once we've reserved stack space we write the
         // value. Eventually underflow will throw an exception, but JS sort of
         // just handles it today...
-        self.global(
+        self.write_js_function(
             "
-            function addBorrowedObject(obj) {
                 if (stack_pointer == 1) throw new Error('out of js stack');
                 heap[--stack_pointer] = obj;
                 return stack_pointer;
-            }
             ",
+            func_name,
+            "(obj)",
+            &[],
         );
     }
 
-    fn expose_take_object(&mut self) {
+    fn expose_take_object(&mut self, import_deps: &mut HashSet) {
+        let func_name = "takeObject";
+        import_deps.insert(format!("'${func_name}'"));
         if !self.should_write_global("take_object") {
             return;
         }
-        self.expose_get_object();
-        self.expose_drop_ref();
-        self.global(
+        self.expose_get_object(import_deps);
+        self.expose_drop_ref(import_deps);
+        self.write_js_function(
             "
-            function takeObject(idx) {
                 const ret = getObject(idx);
                 dropObject(idx);
                 return ret;
-            }
             ",
+            func_name,
+            "(idx)",
+            &["'$getObject'".to_string(), "'$dropObject'".to_string()],
         );
     }
 
@@ -2544,22 +2698,29 @@ __wbg_set_wasm(wasm);"
         &mut self,
         ty: VectorKind,
         memory: MemoryId,
+        import_deps: &mut HashSet,
     ) -> Result {
         Ok(match ty {
             VectorKind::String => self.expose_get_string_from_wasm(memory)?,
-            VectorKind::I8 => self.expose_get_array_i8_from_wasm(memory),
-            VectorKind::U8 => self.expose_get_array_u8_from_wasm(memory),
-            VectorKind::ClampedU8 => self.expose_get_clamped_array_u8_from_wasm(memory),
-            VectorKind::I16 => self.expose_get_array_i16_from_wasm(memory),
-            VectorKind::U16 => self.expose_get_array_u16_from_wasm(memory),
-            VectorKind::I32 => self.expose_get_array_i32_from_wasm(memory),
-            VectorKind::U32 => self.expose_get_array_u32_from_wasm(memory),
-            VectorKind::I64 => self.expose_get_array_i64_from_wasm(memory),
-            VectorKind::U64 => self.expose_get_array_u64_from_wasm(memory),
-            VectorKind::F32 => self.expose_get_array_f32_from_wasm(memory),
-            VectorKind::F64 => self.expose_get_array_f64_from_wasm(memory),
-            VectorKind::Externref => self.expose_get_array_js_value_from_wasm(memory)?,
-            VectorKind::NamedExternref(_) => self.expose_get_array_js_value_from_wasm(memory)?,
+            VectorKind::I8 => self.expose_get_array_i8_from_wasm(memory, import_deps),
+            VectorKind::U8 => self.expose_get_array_u8_from_wasm(memory, import_deps),
+            VectorKind::ClampedU8 => {
+                self.expose_get_clamped_array_u8_from_wasm(memory, import_deps)
+            }
+            VectorKind::I16 => self.expose_get_array_i16_from_wasm(memory, import_deps),
+            VectorKind::U16 => self.expose_get_array_u16_from_wasm(memory, import_deps),
+            VectorKind::I32 => self.expose_get_array_i32_from_wasm(memory, import_deps),
+            VectorKind::U32 => self.expose_get_array_u32_from_wasm(memory, import_deps),
+            VectorKind::I64 => self.expose_get_array_i64_from_wasm(memory, import_deps),
+            VectorKind::U64 => self.expose_get_array_u64_from_wasm(memory, import_deps),
+            VectorKind::F32 => self.expose_get_array_f32_from_wasm(memory, import_deps),
+            VectorKind::F64 => self.expose_get_array_f64_from_wasm(memory, import_deps),
+            VectorKind::Externref => {
+                self.expose_get_array_js_value_from_wasm(memory, import_deps)?
+            }
+            VectorKind::NamedExternref(_) => {
+                self.expose_get_array_js_value_from_wasm(memory, import_deps)?
+            }
         })
     }
 
@@ -2806,7 +2967,7 @@ __wbg_set_wasm(wasm);"
                 $CLOSURE_DTORS: `(typeof FinalizationRegistry === 'undefined')
                     ? { register: () => {}, unregister: () => {} }
                 : new FinalizationRegistry(state => {
-                    wasmExports.['__indirect_function_table'].get(state.dtor)(state.a, state.b)
+                    wasmExports['__indirect_function_table'].get(state.dtor)(state.a, state.b)
                 })`,\n
                 ",
             );
@@ -2851,6 +3012,13 @@ __wbg_set_wasm(wasm);"
 
     fn write_js_function(&mut self, body: &str, func_name: &str, args: &str, deps: &[String]) {
         if matches!(self.config.mode, OutputMode::Emscripten) {
+            let re = Regex::new(r"(?:wasmExports|wasm)\.([a-zA-Z0-9_$]*)").unwrap();
+            let formatted_body = if let Some(_) = re.captures(body) {
+                re.replace_all(body, "wasmExports[\'$1\']").to_string()
+            } else {
+                body.to_string()
+            };
+
             self.emscripten_library(&format!(
                 "${}: function{} {{
                 {}
@@ -2858,7 +3026,7 @@ __wbg_set_wasm(wasm);"
                 ",
                 func_name,
                 args,
-                body.trim().replace("wasm.", "wasmExports.")
+                formatted_body.trim()
             ));
             if !deps.is_empty() {
                 self.emscripten_library.push_str(&format!(
@@ -4315,7 +4483,10 @@ __wbg_set_wasm(wasm);"
                     );
                 }
                 drop(memories);
-                format!("wasm.{}", self.export_name_of(memory))
+                match self.config.mode {
+                    OutputMode::Emscripten { .. } => "HEAPU8".to_string(),
+                    _ => format!("wasm.{}", self.export_name_of(memory)),
+                }
             }
 
             Intrinsic::FunctionTable => {