Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ futures = "0.3.31"
futures-channel = "0.3.31"
futures-util = { version = "0.3", default-features = false }
rustc-hash = "2.1.1"
wasm-bindgen = "0.2.100"
wasm-bindgen = "0.2.105"
wasm-bindgen-futures = "0.4.50"
js-sys = "0.3"
web-sys = { version = "0.3.77", default-features = false }
Expand Down
52 changes: 46 additions & 6 deletions packages/cli/src/build/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
// `relocation-model=pic` synthesizes can reference the functions via the indirect function table
// even if they are not normally synthesized in regular wasm code generation.
//
// Normally, the dynaic linker setup would resolve GOT.func against the same GOT.func export in
// Normally, the dynamic linker setup would resolve GOT.func against the same GOT.func export in
// the main module, but we don't have that. Instead, we simply re-parse the main module, aggregate
// its ifunc table, and then resolve directly to the index in that table.
for (import_id, ifunc_index) in got_funcs {
Expand Down Expand Up @@ -584,19 +584,23 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu

if let Some(table_idx) = name_to_ifunc_old.get(import.name.as_str()) {
new.imports.delete(env_func_import);
convert_import_to_ifunc_call(
convert_func_to_ifunc_call(
&mut new,
ifunc_table_initializer,
func_id,
*table_idx,
name.clone(),
);
continue;
}

if name_is_bindgen_symbol(&name) {
new.imports.delete(env_func_import);
convert_import_to_ifunc_call(&mut new, ifunc_table_initializer, func_id, 0, name);
convert_func_to_ifunc_call(&mut new, ifunc_table_initializer, func_id, 0, name);
continue;
}

tracing::warn!("[hotpatching]: Symbol slipped through the cracks: {}", name);
}

// Wire up the preserved intrinsic functions that we saved before running wasm-bindgen to the expected
Expand All @@ -613,7 +617,34 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
if name_is_bindgen_symbol(&import.name) {
let name = import.name.as_str().to_string();
new.imports.delete(import_id);
convert_import_to_ifunc_call(&mut new, ifunc_table_initializer, func_id, 0, name);
convert_func_to_ifunc_call(&mut new, ifunc_table_initializer, func_id, 0, name);
}
}

// Rewrite the wbg_cast functions to call the indirect functions from the original module.
// This is necessary because wasm-bindgen uses these calls to perform dynamic type casting through
// the JS layer. If we don't rewrite these, they end up as calls to `breaks_if_inlined` functions
// which are no-ops and get rewritten by the wbindgen post-processing step.
//
// Here, we find the corresponding wbg_cast function in the old module by name and then rewrite
// the patch module's cast function to call the indirect function from the original module.
//
// See the wbg_cast implementation in wasm-bindgen for more details:
// <https://github.com/wasm-bindgen/wasm-bindgen/blob/f61a588f674304964a2062b2307edb304aed4d16/src/rt/mod.rs#L30>
let new_func_ids = new.funcs.iter().map(|f| f.id()).collect::<Vec<_>>();
for func_id in new_func_ids {
let Some(name) = new.funcs.get(func_id).name.as_deref() else {
continue;
};

if name.contains("wasm_bindgen4__rt8wbg_cast") && !name.contains("breaks_if_inline") {
let name = name.to_string();
let old_idx = name_to_ifunc_old
.get(&name)
.copied()
.ok_or_else(|| anyhow::anyhow!("Could not find matching wbg_cast function for [{name}] - must generate new JS bindings."))?;

convert_func_to_ifunc_call(&mut new, ifunc_table_initializer, func_id, old_idx, name);
}
}

Expand Down Expand Up @@ -642,8 +673,18 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
let ifunc_count = name_to_ifunc_new.len() as u64;
let mut map = AddressMap::default();
for (name, idx) in name_to_ifunc_new.iter() {
// Find the corresponding ifunc in the old module by name
if let Some(old_idx) = name_to_ifunc_old.get(*name) {
map.insert(*old_idx as u64, *idx as u64);
continue;
}

// Warn if there might be a null entry
if !name.contains("breaks_if_inlined") {
tracing::warn!(
"[hotpatching]: Failed to find ifunc entry in old module for function: {}",
name
);
}
}

Expand All @@ -656,7 +697,7 @@ pub fn create_wasm_jump_table(patch: &Path, cache: &HotpatchModuleCache) -> Resu
})
}

fn convert_import_to_ifunc_call(
fn convert_func_to_ifunc_call(
new: &mut Module,
ifunc_table_initializer: TableId,
func_id: FunctionId,
Expand Down Expand Up @@ -1309,7 +1350,6 @@ fn name_is_bindgen_symbol(name: &str) -> bool {
|| name.contains("wasm_bindgen..describe..WasmDescribe")
|| name.contains("wasm_bindgen..closure..WasmClosure$GT$8describe")
|| name.contains("wasm_bindgen7closure16Closure$LT$T$GT$4wrap8describe")
|| name.contains("wasm_bindgen4__rt8wbg_")
}

/// Manually parse the data section from a wasm module
Expand Down
2 changes: 2 additions & 0 deletions packages/playwright-tests/web-hot-patch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ publish = false

[dependencies]
dioxus = { path = "../../dioxus", features = ["web"] }
wasm-bindgen = { version = "0.2.100" }
web-sys = { version = "*", features = ["MouseEvent"] }

[workspace]
5 changes: 5 additions & 0 deletions packages/playwright-tests/web-hot-patch/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const IMAGE: Asset = asset!("/assets/toasts.png");
fn app() -> Element {
let mut num = use_signal(|| 0);

// make sure to emit funky closure code in this module to test wasm-bindgen handling
let _closures = wasm_bindgen::closure::Closure::<dyn FnMut(web_sys::MouseEvent)>::new(
move |event: web_sys::MouseEvent| {},
);

rsx! {
document::Link {
href: CSS,
Expand Down
Loading