Skip to content

fix(wasm): #1037 — String.fromCharCode returned undefined on --target web - #1043

Merged
proggeramlug merged 1 commit into
mainfrom
fix/1037-wasm-string-concat
May 18, 2026
Merged

fix(wasm): #1037 — String.fromCharCode returned undefined on --target web#1043
proggeramlug merged 1 commit into
mainfrom
fix/1037-wasm-string-concat

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Expr::StringFromCharCode and Expr::StringFromCodePoint in the WASM emitter were calling mem_call with the bridge name string_from_char_code (snake_case). The runtime's __memDispatch table only has string_fromCharCode (camelCase). The mismatch made mem_call fall through to __classDispatch(code, "string_from_char_code", []), which returned undefined. s += String.fromCharCode(0) then concatenated "undefined" (9 chars) per iteration — Ralph's 16-iter repro produced length 144 = 16 × 9.

(The "9× growth ratio matches handle-ID digit count" hypothesis from the issue comment was a coincidence: 'undefined'.length is 9.)

Fix

Two-character edit per arm — string_from_char_codestring_fromCharCode. Same bridge dispatches both String.fromCharCode and String.fromCodePoint (the StringFromCodePoint arm is currently a BMP-only stub aliased to fromCharCode).

Variant matrix (Ralph's table, all on --target wasm)

Variant Pre-fix Post-fix
result += 'x' (literal) 16 ✓ 16 ✓
result += String.fromCharCode(0) 144 16 ✓
result = result + String.fromCharCode(0) 144 16 ✓
4-iter result += String.fromCharCode(0) 36 4 ✓
String.fromCodePoint(65) undefined "A" ✓

Bundled harness fix

tests/wasm/run_wasm_tests.sh was failing every test with document is not definedwasm_runtime.js injects a <style> element at module load (document.head.appendChild) since the web-UI ship. Added a minimal document/window polyfill to the Node eval block in the runner. Local-only — CI doesn't execute this script.

Same-suite results (binary built from origin/main, my new test removed):
12 pass, 7 fail (05_objects_arrays, 10_higher_order, 11_map_set_json, 14_regex_date, 17_class_field_splice, 18_module_splice, 19_ffi_imports).
With fix applied: identical 7 pre-existing failures + 1 new passing test (20_string_from_char_code). No regressions.

Out of scope (worth a follow-up)

The audit found seven other bridge names with the same snake_case/camelCase mismatch, all in the _ => Handle instance method calls on objects catch-all in Expr::NativeMethodCall (emit.rs:5588..5840): string_char_at, string_index_of, string_to_lower_case, string_to_upper_case, string_starts_with, string_ends_with, string_pad_start, string_pad_end. They are unreachable for normal str.charAt(i) etc. — those hit emit_method_call (emit.rs:4081, camelCase, correct) — so the bugs are latent. Not touching them here to keep the PR focused on the user-visible bug from #1037; happy to sweep in a follow-up.

Test plan

Closes #1037.

…et web

`Expr::StringFromCharCode`/`StringFromCodePoint` in `crates/perry-codegen-wasm/src/emit.rs` emitted a `mem_call` to the bridge name `string_from_char_code` (snake_case). The runtime's `__memDispatch` table only registers `string_fromCharCode` (camelCase), so the call fell through to `__classDispatch(code, "string_from_char_code", [])`, which returned undefined. `s += String.fromCharCode(0)` then appended the string `"undefined"` (9 chars) per iteration — matching the 16-iter repro of length 144.

Fixed both arms to emit the camelCase bridge name. Regression test in `tests/wasm/20_string_from_char_code.ts` covers `+=`, `=` + `+`, 16-iter and 4-iter shapes, plus `fromCodePoint`.

Bundled fix: the wasm test runner at `tests/wasm/run_wasm_tests.sh` was failing every test because `wasm_runtime.js` injects a `<style>` tag at module load (`document.head.appendChild`); added a minimal DOM polyfill so the harness can execute the wasm in Node again. Same-suite baseline (pre-fix, my test removed) and post-fix: identical 7 pre-existing failures, none caused by this change.
@proggeramlug
proggeramlug merged commit 0a90839 into main May 18, 2026
9 checks passed
@proggeramlug
proggeramlug deleted the fix/1037-wasm-string-concat branch May 18, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wasm runtime: post-parse hang — object_get_dynamic repeats indefinitely with identical args after keyword tokenizer enters its outer loop

1 participant