diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 680f42b2..02a7728d 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -2889,7 +2889,7 @@ impl<'a> Context<'a> { } fn export_function_table(&mut self) -> Result { - match self.module.tables.main_function_table()? { + match self.aux.function_table { Some(id) => Ok(self.export_name_of(id)), None => bail!("no function table found in module"), } diff --git a/crates/cli-support/src/wit/mod.rs b/crates/cli-support/src/wit/mod.rs index e7b94a6d..39e372c0 100644 --- a/crates/cli-support/src/wit/mod.rs +++ b/crates/cli-support/src/wit/mod.rs @@ -164,6 +164,12 @@ impl<'a> Context<'a> { // access to them while processing programs. self.descriptors.extend(descriptors); + // If any closures exist we need to prevent the function table from + // getting gc'd + if closure_imports.len() > 0 { + self.aux.function_table = self.module.tables.main_function_table()?; + } + // Register all the injected closure imports as that they're expected // to manufacture a particular type of closure. // @@ -310,6 +316,9 @@ impl<'a> Context<'a> { } fn bind_intrinsic(&mut self, id: ImportId, intrinsic: Intrinsic) -> Result<(), Error> { + if let Intrinsic::FunctionTable = intrinsic { + self.aux.function_table = self.module.tables.main_function_table()?; + } let id = self.import_adapter(id, intrinsic.signature(), AdapterJsImportKind::Normal)?; self.aux .import_map diff --git a/crates/cli-support/src/wit/nonstandard.rs b/crates/cli-support/src/wit/nonstandard.rs index 146aab28..0687e7a9 100644 --- a/crates/cli-support/src/wit/nonstandard.rs +++ b/crates/cli-support/src/wit/nonstandard.rs @@ -50,6 +50,7 @@ pub struct WasmBindgenAux { /// Information about various internal functions used to manage the `anyref` /// table, later used to process JS bindings. pub anyref_table: Option, + pub function_table: Option, pub anyref_alloc: Option, pub anyref_drop_slice: Option, @@ -366,6 +367,9 @@ impl walrus::CustomSection for WasmBindgenAux { if let Some(id) = self.anyref_table { roots.push_table(id); } + if let Some(id) = self.function_table { + roots.push_table(id); + } if let Some(id) = self.anyref_alloc { roots.push_func(id); } diff --git a/crates/cli-support/src/wit/section.rs b/crates/cli-support/src/wit/section.rs index 7efd5dc4..978d1108 100644 --- a/crates/cli-support/src/wit/section.rs +++ b/crates/cli-support/src/wit/section.rs @@ -50,6 +50,7 @@ pub fn add(module: &mut Module) -> Result<(), Error> { anyref_drop_slice: _, exn_store: _, shadow_stack_pointer: _, + function_table: _, } = *aux; let adapter_context = |id: AdapterId| { diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index 64494d03..a65b89a0 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -334,3 +334,21 @@ $", )?); Ok(()) } + +#[test] +fn function_table_preserved() { + let (mut cmd, _out_dir) = Project::new("function_table_preserved") + .file( + "src/lib.rs", + r#" + use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + pub fn bar() { + Closure::wrap(Box::new(|| {}) as Box); + } + "#, + ) + .wasm_bindgen(""); + cmd.assert().success(); +}