From 16f7a14506dd76e14814e499cc0de8c3a0d88457 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 05:31:50 +0000 Subject: [PATCH] js-on-sx: bail out of array set/length at 2^32-1 instead of padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arr[4294967295] = 'x' and arr.length = 4294967295 were padding the SX list with js-undefined for ~4 billion entries — instant timeout. Per ES spec, indices >= 2^32-1 aren't array indices anyway (regular properties, which we can't store on lists). Added (>= i 4294967295) bail clauses to js-list-set! and the length setter. built-ins/Array: 21/45 → 23/45 (5 timeouts → 2). conformance.sh: 148/148. --- lib/js/runtime.sx | 2 ++ lib/js/test262-scoreboard.json | 38 +++++++++++++++++----------------- lib/js/test262-scoreboard.md | 16 +++++++------- plans/js-on-sx.md | 2 ++ 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index fb84ba93..675fa735 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -2866,6 +2866,7 @@ ((i (js-num-to-int key)) (n (len lst))) (cond ((< i 0) nil) + ((>= i 4294967295) nil) ((< i n) (set-nth! lst i val)) ((= i n) (append! lst val)) (else (do (js-pad-list! lst n i) (append! lst val)))))) @@ -2876,6 +2877,7 @@ ((target (js-num-to-int (js-to-number val))) (n (len lst))) (cond ((< target 0) nil) + ((>= target 4294967295) nil) ((> target n) (js-pad-list! lst n target)) (else nil)))) (else nil)))) diff --git a/lib/js/test262-scoreboard.json b/lib/js/test262-scoreboard.json index 87f3b09b..c9a9666d 100644 --- a/lib/js/test262-scoreboard.json +++ b/lib/js/test262-scoreboard.json @@ -1,35 +1,35 @@ { "totals": { - "pass": 21, - "fail": 17, + "pass": 23, + "fail": 20, "skip": 5, - "timeout": 7, + "timeout": 2, "total": 50, "runnable": 45, - "pass_rate": 46.7 + "pass_rate": 51.1 }, "categories": [ { "category": "built-ins/Array", "total": 50, - "pass": 21, - "fail": 17, + "pass": 23, + "fail": 20, "skip": 5, - "timeout": 7, - "pass_rate": 46.7, + "timeout": 2, + "pass_rate": 51.1, "top_failures": [ [ "Test262Error (assertion failed)", - 14 - ], - [ - "Timeout", - 7 + 17 ], [ "TypeError: not a function", 2 ], + [ + "Timeout", + 2 + ], [ "Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\\", 1 @@ -40,22 +40,22 @@ "top_failure_modes": [ [ "Test262Error (assertion failed)", - 14 - ], - [ - "Timeout", - 7 + 17 ], [ "TypeError: not a function", 2 ], + [ + "Timeout", + 2 + ], [ "Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\\", 1 ] ], "pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33", - "elapsed_seconds": 123.5, + "elapsed_seconds": 51.3, "workers": 1 } \ No newline at end of file diff --git a/lib/js/test262-scoreboard.md b/lib/js/test262-scoreboard.md index 1b1bbe7e..d6e189bd 100644 --- a/lib/js/test262-scoreboard.md +++ b/lib/js/test262-scoreboard.md @@ -1,28 +1,28 @@ # test262 scoreboard Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33` -Wall time: 123.5s +Wall time: 51.3s -**Total:** 21/45 runnable passed (46.7%). Raw: pass=21 fail=17 skip=5 timeout=7 total=50. +**Total:** 23/45 runnable passed (51.1%). Raw: pass=23 fail=20 skip=5 timeout=2 total=50. ## Top failure modes -- **14x** Test262Error (assertion failed) -- **7x** Timeout +- **17x** Test262Error (assertion failed) - **2x** TypeError: not a function +- **2x** Timeout - **1x** Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\ ## Categories (worst pass-rate first, min 10 runnable) | Category | Pass | Fail | Skip | Timeout | Total | Pass % | |---|---:|---:|---:|---:|---:|---:| -| built-ins/Array | 21 | 17 | 5 | 7 | 50 | 46.7% | +| built-ins/Array | 23 | 20 | 5 | 2 | 50 | 51.1% | ## Per-category top failures (min 10 runnable, worst first) -### built-ins/Array (21/45 — 46.7%) +### built-ins/Array (23/45 — 51.1%) -- **14x** Test262Error (assertion failed) -- **7x** Timeout +- **17x** Test262Error (assertion failed) - **2x** TypeError: not a function +- **2x** Timeout - **1x** Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\ diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 5eff3395..ba4e5e6d 100644 --- a/plans/js-on-sx.md +++ b/plans/js-on-sx.md @@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green. Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta. +- 2026-05-08 — **Out-of-range array indices and lengths no longer hang.** `arr[4294967295] = 'x'` and `arr.length = 4294967295` were padding the SX list with `js-undefined` for ~4 billion entries — guaranteed timeout. Per ES spec, indices ≥ 2^32-1 aren't array indices (they're regular properties, which we can't store on a list). Added a `(>= i 4294967295)` bail-out clause to both `js-list-set!` (numeric index path) and the `length` setter; both now no-op at that bound. Removed 5 of the 7 Array timeouts. built-ins/Array: 21/45 → 23/45. conformance.sh: 148/148. + - 2026-05-08 — **Built-in `.length` returns spec-defined values for variadic functions.** `String.fromCharCode.length`, `Math.max.length`, `Array.from.length` were all returning `0` because the underlying SX lambdas use `&rest args` with no required params — but the spec assigns each built-in a specific length (`fromCharCode === 1`, `max === 2`, etc.). Added `js-builtin-fn-length` that maps the unmapped JS name to its spec length (12 entries covering fromCharCode, fromCodePoint, raw, of, from, isArray, max, min, hypot, atan2, imul, pow). `js-fn-length` consults this table first and falls back to counting real params. built-ins/String: 79/99 → 80/99, built-ins/Array: 20/45 → 21/45. conformance.sh: 148/148. - 2026-05-08 — **`Object.prototype.toString` dispatches by [[Class]].** Was hardcoded to `"[object Object]"` for everything; per ES it should return `"[object Array]"`, `"[object Function]"`, `"[object Number]"`, etc. based on the receiver's class. Added `js-object-tostring-class` helper that switches on `(type-of v)` and on dict-internal markers (`__js_string_value__`, `__js_number_value__`, `__js_boolean_value__`, `__callable__`). Also added prototype-identity checks so `Object.prototype.toString.call(Number.prototype)` returns `"[object Number]"` (similar for String/Boolean/Array). built-ins/Array: 18/45 → 20/45, built-ins/Number: 43/50 → 44/50. conformance.sh: 148/148.