Skip to content

Commit c5c7acc

Browse files
authored
Preserve the function table explicitly (#1970)
The main gc pass of unused items in wasm-bindgen was accidentally removing the function table because we weren't properly rooting it in the auxiliary section which has a few ways that imports can reference the function table via intrinsics and closures. Closes #1967
1 parent bb066e6 commit c5c7acc

File tree

5 files changed

+33
-1
lines changed

5 files changed

+33
-1
lines changed

crates/cli-support/src/js/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2889,7 +2889,7 @@ impl<'a> Context<'a> {
28892889
}
28902890

28912891
fn export_function_table(&mut self) -> Result<String, Error> {
2892-
match self.module.tables.main_function_table()? {
2892+
match self.aux.function_table {
28932893
Some(id) => Ok(self.export_name_of(id)),
28942894
None => bail!("no function table found in module"),
28952895
}

crates/cli-support/src/wit/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ impl<'a> Context<'a> {
164164
// access to them while processing programs.
165165
self.descriptors.extend(descriptors);
166166

167+
// If any closures exist we need to prevent the function table from
168+
// getting gc'd
169+
if closure_imports.len() > 0 {
170+
self.aux.function_table = self.module.tables.main_function_table()?;
171+
}
172+
167173
// Register all the injected closure imports as that they're expected
168174
// to manufacture a particular type of closure.
169175
//
@@ -310,6 +316,9 @@ impl<'a> Context<'a> {
310316
}
311317

312318
fn bind_intrinsic(&mut self, id: ImportId, intrinsic: Intrinsic) -> Result<(), Error> {
319+
if let Intrinsic::FunctionTable = intrinsic {
320+
self.aux.function_table = self.module.tables.main_function_table()?;
321+
}
313322
let id = self.import_adapter(id, intrinsic.signature(), AdapterJsImportKind::Normal)?;
314323
self.aux
315324
.import_map

crates/cli-support/src/wit/nonstandard.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub struct WasmBindgenAux {
5050
/// Information about various internal functions used to manage the `anyref`
5151
/// table, later used to process JS bindings.
5252
pub anyref_table: Option<walrus::TableId>,
53+
pub function_table: Option<walrus::TableId>,
5354
pub anyref_alloc: Option<walrus::FunctionId>,
5455
pub anyref_drop_slice: Option<walrus::FunctionId>,
5556

@@ -366,6 +367,9 @@ impl walrus::CustomSection for WasmBindgenAux {
366367
if let Some(id) = self.anyref_table {
367368
roots.push_table(id);
368369
}
370+
if let Some(id) = self.function_table {
371+
roots.push_table(id);
372+
}
369373
if let Some(id) = self.anyref_alloc {
370374
roots.push_func(id);
371375
}

crates/cli-support/src/wit/section.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub fn add(module: &mut Module) -> Result<(), Error> {
5050
anyref_drop_slice: _,
5151
exn_store: _,
5252
shadow_stack_pointer: _,
53+
function_table: _,
5354
} = *aux;
5455

5556
let adapter_context = |id: AdapterId| {

crates/cli/tests/wasm-bindgen/main.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,21 @@ $",
334334
)?);
335335
Ok(())
336336
}
337+
338+
#[test]
339+
fn function_table_preserved() {
340+
let (mut cmd, _out_dir) = Project::new("function_table_preserved")
341+
.file(
342+
"src/lib.rs",
343+
r#"
344+
use wasm_bindgen::prelude::*;
345+
346+
#[wasm_bindgen]
347+
pub fn bar() {
348+
Closure::wrap(Box::new(|| {}) as Box<dyn Fn()>);
349+
}
350+
"#,
351+
)
352+
.wasm_bindgen("");
353+
cmd.assert().success();
354+
}

0 commit comments

Comments
 (0)