Skip to content

fix(wasm): module-level member writes persist on the web target (#5016)#5022

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-fix-web-module-array-5016
Jun 12, 2026
Merged

fix(wasm): module-level member writes persist on the web target (#5016)#5022
proggeramlug merged 1 commit into
mainfrom
worktree-fix-web-module-array-5016

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Fixes #5016. On the web/wasm target, a write to a module-level array element or object property (A[0] = ..., o.prop = ...) was silently dropped — subsequent reads returned the initializer. Native always worked. This broke the documented module-scope mutable-state pattern (const S = [...]; mutate S[i]) that Perry's own pitfalls guidance recommends, so games/UIs built for web never updated state (discovered shipping Bloom Jump to web). Regression of #1993.

Root cause

The HIR lowers both member-write forms — obj.prop = v and obj[i] = v — to a single Expr::PutValueSet node. The WASM backend (perry-codegen-wasm) never had a match arm for it, so every such assignment fell through emit_expr's _ => TAG_UNDEFINED catch-all and the store was never emitted. The still-present IndexSet/PropertySet arms had become dead code once lowering switched to PutValueSet — which is why the earlier #1993 fix appeared to regress.

Fix

Handle PutValueSet in the WASM backend, mirroring the pre-PutValueSet lowering split so behavior matches the existing arms:

  • string-literal key (obj.prop = v) → class_set_field (so class getters/setters still fire)
  • any other key (obj[i] = v) → object_set_dynamic
  • both return the assigned RHS value (assignment-expression semantics)

Companion expression walkers updated in tandem so the node is fully supported: string_collection.rs (intern key string + value literals), closures.rs (collect closures used as value/key/target), js_fallback.rs (emit equivalent JS assignment).

Verification

  • Issue repro and variants (in-function, top-level, through-parameter, object property, class setter) now match native on web.
  • WASM target suite: 17 → 21 passing, 0 regressions. The four newly-green tests (05_objects_arrays, 07_classes, 08_getters_setters, 21_class_method_i64) were all broken by this same dropped-write bug. The 6 remaining failures are pre-existing and unrelated (higher-order array methods, map/set, regex/date, splice, ffi imports).
  • Added tests/wasm/26_module_array_element_write.ts regression guard.

🤖 Generated with Claude Code

@proggeramlug

Copy link
Copy Markdown
Contributor Author

CI status

This branch is rebased on latest main (v0.5.1161). Beyond the #5016 wasm fix, it also clears two pre-existing cargo-test blockers on main that were surfacing one-per-run:

  1. fix(stream)readable.read() with no size now follows Node's howMuchToRead(NaN) (head-chunk only while flowing; full buffer while paused) instead of always returning the head chunk, fixing the node_stream unshift unit tests regressed by feat(runtime): node:stream + node:stream/web (Readable/Writable/Transform + WHATWG) (#1545) #5017.
  2. fix(zlib) — the API manifest declares deflateRawSync(buf, options?) to match the dispatch arity fix(stdlib): #4917 — real semantics (or loud warn) for silently-ignored adapter options and no-ops #4996 introduced, fixing manifest_param_counts_match_dispatch_table.

Green: lint, api-docs-drift, security-audit, harmonyos-smoke.

Still red — pre-existing main breakage, NOT introduced by this PR (diff is wasm-codegen + one stream fn + one manifest line; touches no GC or native codegen):

Both reproduce on a clean main and need dedicated GC / native-codegen context; fixing them here would be out-of-scope, risky surgery.

…n the web target (#5016)

The HIR lowers both `obj.prop = v` and `obj[i] = v` to a single
Expr::PutValueSet node. The perry-codegen-wasm backend had no arm for it, so
every member write fell through emit_expr's `_ => TAG_UNDEFINED` catch-all and
was silently dropped — module-level array/object mutation appeared immutable on
the web target (reads returned the initializer). Native always worked.
Regression of #1993.

Handle PutValueSet in the WASM backend, mirroring the pre-PutValueSet lowering
split: string-literal key -> class_set_field (preserves class getters/setters),
any other key -> object_set_dynamic; both return the assigned RHS value.
Companion walkers updated in tandem: string_collection (intern key/value
strings), closures (collect closures in operands), js_fallback (emit JS
assignment).

WASM suite: 17->21 passing, 0 regressions (the 4 newly-green tests were broken
by the same bug). Adds tests/wasm/26_module_array_element_write.ts.
@proggeramlug proggeramlug force-pushed the worktree-fix-web-module-array-5016 branch from 68e3b6f to 476f8e4 Compare June 12, 2026 11:12
@proggeramlug proggeramlug merged commit 3025c22 into main Jun 12, 2026
13 checks passed
@proggeramlug proggeramlug deleted the worktree-fix-web-module-array-5016 branch June 12, 2026 11:22
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.

web: writes to module-level array elements inside functions are dropped (reads return initializer)

1 participant