fix(runtime): #157 Int32Array/Float64Array length=0 and Uint8ClampedArray no-clamp#165
Merged
proggeramlug merged 1 commit intomainfrom Apr 23, 2026
Merged
fix(runtime): #157 Int32Array/Float64Array length=0 and Uint8ClampedArray no-clamp#165proggeramlug merged 1 commit intomainfrom
proggeramlug merged 1 commit intomainfrom
Conversation
b7ccdec to
b9a6a00
Compare
…egative NaN (v0.5.184) Closes #157 via PR #165. Four typed-array bugs, one commit. 1. `new Int32Array(3)` (and other numeric-length ctors) produced length 0 because `TypedArrayNew` codegen passed the length through `unbox_to_i64`, which masks the lower 48 bits of a plain f64. `3.0_f64` bits are `0x4008_0000_0000_0000` — lower 48 zero. Detect literal integer/float args at codegen and call `js_typed_array_new_empty` directly; non-literal args go through a new `js_typed_array_new(kind, val)` runtime dispatch that peels the NaN-box tag: POINTER → copy from array, INT32 → length, plain double → length. 2. Negative element values stored as NaN. `jsvalue_to_f64` only accepted `top16 < 0x7FF8` as a plain double — negative doubles have `top16 >= 0x8000` and fell through to the tag path returning `f64::NAN`. Widened to `top16 < 0x7FFA || top16 >= 0x8000` so negative doubles and non-tag NaN patterns pass through unchanged. 3. `Uint8ClampedArray` was silently aliased to `KIND_UINT8` in `ir.rs::typed_array_kind_for_name`, skipping clamp semantics — `c[0] = 300` produced `44` (truncate-wrap) instead of `255`. New `KIND_UINT8_CLAMPED = 8` + `CLASS_ID_UINT8_CLAMPED_ARRAY`, with ToUint8Clamp (NaN→0, ≤0→0, ≥255→255, otherwise round-half-to-even then clamp) in `store_at`. Name mapping in `ir.rs` + removal from `lower.rs`'s `TypedArrayNew` exclusion. 4. Added typed-array dispatch to `js_array_set_f64` / `js_array_set_f64_extend` so `tArr[i] = v` through the generic array path still goes through per-kind store instead of writing raw f64 at the wrong offset. Also added `TypedArrayNew` arm to `refine_type_from_init_simple` so `.length` and method dispatch take the typed-array fast path. ## Maintainer fixup Reverted the PR's IndexGet/IndexSet numeric-fallback routing (from `js_array_get_f64` / `_set_f64` calls back to inline `ptr+8+idx*8` load/store) to preserve the load-bearing Object-with-numeric-keys storage. `const constMap: Record<number, string> = {}; constMap[0] = "x"` relies on the inline scheme because the dispatch path treats non-registered pointers as `ArrayHeader` and misreads the `ObjectHeader`'s first field as length. Typed-array element indexing has its own dedicated HIR path (`TypedArrayGet` / `Set` → `js_typed_array_{get,set}` with correct per-kind width), so the revert doesn't reintroduce the original #157 alignment bug. Added `test_edge_enums_const` + `test_edge_iteration` to `known_failures.json` as CWD-dependent flakes in the same family as `test_edge_map_set` (noted in v0.5.178). After the revert, the two tests pass when compiled from `/tmp` but diverge when compiled from `test-files/`. Reverting the dispatch change does *not* eliminate the path-dependency — confirming the divergence is a pre-existing compiler bug surfaced by (but not caused by) the #157 branch. Tracked separately. Cloud-authored PR, manually audited and metadata (version bump + CLAUDE.md entry) folded in at merge.
e9ac7e1 to
2008f8f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #157. Four bugs, one commit.
Length=0 for numeric-length constructors (
new Int32Array(3)→ length 0):TypedArrayNewcodegen passed the length throughunbox_to_i64, which masks the lower 48 bits of a plain f64 —3.0_f64bits are0x4008_0000_0000_0000, so the lower 48 bits are zero. Fixed by detecting literal integer/float args at codegen time and callingjs_typed_array_new_emptydirectly; non-literal args use a newjs_typed_array_newruntime dispatch function that decodes length from any NaN-boxed JS value.Negative values stored as NaN (
f1[1] = -2.718→ NaN):jsvalue_to_f64only treatedtop16 < 0x7FF8as a plain double, but negative f64 values havetop16 >= 0x8000(sign bit set) and fell through to returnf64::NAN. Fixed: condition is nowtop16 < 0x7FFA || top16 >= 0x8000.Uint8ClampedArray not clamping (
c[0] = 300→ 44 instead of 255):Uint8ClampedArraywas mapped toKIND_UINT8inir.rsand excluded from theTypedArrayNewrouting inlower.rs, falling through toExpr::Newwith no clamping. Fixed by addingKIND_UINT8_CLAMPED = 8with correctToUint8Clamprounding (round-half-to-even, clamp [0, 255]) instore_at, mapping the name inir.rs, and removing the exclusion fromlower.rs.Element reads/writes at wrong memory offset: The generic
IndexGet/IndexSetnumeric fallback hard-coded an 8-byte header offset (ptr+8+idx*8), butTypedArrayHeaderis 16 bytes wide with variable element sizes. Fixed by replacing the inline loads/stores with calls tojs_array_get_f64/js_array_set_f64(which dispatch throughTYPED_ARRAY_REGISTRY), and adding typed-array dispatch tojs_array_set_f64/js_array_set_f64_extendinarray.rs.Files changed
crates/perry-runtime/src/typedarray.rsKIND_UINT8_CLAMPED, fixjsvalue_to_f64, addjs_typed_array_newcrates/perry-runtime/src/array.rsjs_array_set_f64/js_array_set_f64_extendcrates/perry-hir/src/ir.rs"Uint8ClampedArray"→KIND_UINT8_CLAMPEDcrates/perry-hir/src/lower.rsUint8ClampedArrayfromTypedArrayNewexclusioncrates/perry-codegen/src/expr.rsTypedArrayNewarg dispatch; fixIndexGet/IndexSetnumeric fallbackcrates/perry-codegen/src/runtime_decls.rsjs_typed_array_newexterncrates/perry-codegen/src/boxed_vars.rsTypedArrayNewarm torefine_type_from_init_simpleTest plan
new Int32Array(3); i1[0]=-100; i1[1]=0; i1[2]=2147483647→3/-100 0 2147483647new Float64Array(3); f1[0]=3.14; f1[1]=-2.718; f1[2]=0.0→3/3.14 -2.718 0new Uint8ClampedArray(3); c[0]=300; c[1]=-10; c[2]=128→255 0 128test-files/test_gap_typed_arrays.tsoutput matchesnode --experimental-strip-typesbyte-for-byte (all lines)cargo test --release -p perry-runtime --lib— 111/111 passedhttps://claude.ai/code/session_01VEUgSm4YtjrXWuJTQJAEh5
Generated by Claude Code