From 0cfaeb91362368f48b316bb173173398dd6c4d30 Mon Sep 17 00:00:00 2001 From: giles Date: Fri, 8 May 2026 05:01:12 +0000 Subject: [PATCH] js-on-sx: built-in .length returns spec-defined values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit String.fromCharCode.length, Math.max.length, Array.from.length were returning 0 because their SX lambdas use &rest args with no required params — but spec assigns each a specific length. Added js-builtin-fn-length mapping JS name to spec length (12 entries). js-fn-length consults the 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. --- lib/js/runtime.sx | 24 +++++++++++++- lib/js/test262-scoreboard.json | 58 ++++++++++++++++++++++------------ lib/js/test262-scoreboard.md | 20 +++++++----- plans/js-on-sx.md | 2 ++ 4 files changed, 74 insertions(+), 30 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index b8c8afc2..fb84ba93 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -113,13 +113,35 @@ (let ((t (type-of f))) (cond - ((= t "lambda") (js-count-real-params (lambda-params f))) + ((= t "lambda") + (let + ((mapped (js-builtin-fn-length (js-unmap-fn-name (js-extract-fn-name f))))) + (if (>= mapped 0) mapped (js-count-real-params (lambda-params f))))) ((= t "function") 0) ((= t "component") 0) ((and (= t "dict") (contains? (keys f) "__callable__")) (js-fn-length (get f "__callable__"))) (else 0))))) +(define + js-builtin-fn-length + (fn + (name) + (cond + ((= name "fromCharCode") 1) + ((= name "fromCodePoint") 1) + ((= name "raw") 1) + ((= name "of") 0) + ((= name "from") 1) + ((= name "isArray") 1) + ((= name "max") 2) + ((= name "min") 2) + ((= name "hypot") 2) + ((= name "atan2") 2) + ((= name "imul") 2) + ((= name "pow") 2) + (else -1)))) + (define js-extract-fn-name (fn diff --git a/lib/js/test262-scoreboard.json b/lib/js/test262-scoreboard.json index 45a88075..87f3b09b 100644 --- a/lib/js/test262-scoreboard.json +++ b/lib/js/test262-scoreboard.json @@ -1,45 +1,61 @@ { "totals": { - "pass": 44, - "fail": 3, - "skip": 0, - "timeout": 3, + "pass": 21, + "fail": 17, + "skip": 5, + "timeout": 7, "total": 50, - "runnable": 50, - "pass_rate": 88.0 + "runnable": 45, + "pass_rate": 46.7 }, "categories": [ { - "category": "built-ins/Number", + "category": "built-ins/Array", "total": 50, - "pass": 44, - "fail": 3, - "skip": 0, - "timeout": 3, - "pass_rate": 88.0, + "pass": 21, + "fail": 17, + "skip": 5, + "timeout": 7, + "pass_rate": 46.7, "top_failures": [ [ - "Timeout", - 3 + "Test262Error (assertion failed)", + 14 ], [ - "Test262Error (assertion failed)", - 3 + "Timeout", + 7 + ], + [ + "TypeError: not a function", + 2 + ], + [ + "Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\\", + 1 ] ] } ], "top_failure_modes": [ [ - "Timeout", - 3 + "Test262Error (assertion failed)", + 14 ], [ - "Test262Error (assertion failed)", - 3 + "Timeout", + 7 + ], + [ + "TypeError: not a function", + 2 + ], + [ + "Unhandled: Not callable: {:2 43 :1 42 :0 41 :length 3}\\", + 1 ] ], "pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33", - "elapsed_seconds": 57.4, + "elapsed_seconds": 123.5, "workers": 1 } \ No newline at end of file diff --git a/lib/js/test262-scoreboard.md b/lib/js/test262-scoreboard.md index de6707c5..1b1bbe7e 100644 --- a/lib/js/test262-scoreboard.md +++ b/lib/js/test262-scoreboard.md @@ -1,24 +1,28 @@ # test262 scoreboard Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33` -Wall time: 57.4s +Wall time: 123.5s -**Total:** 44/50 runnable passed (88.0%). Raw: pass=44 fail=3 skip=0 timeout=3 total=50. +**Total:** 21/45 runnable passed (46.7%). Raw: pass=21 fail=17 skip=5 timeout=7 total=50. ## Top failure modes -- **3x** Timeout -- **3x** Test262Error (assertion failed) +- **14x** Test262Error (assertion failed) +- **7x** Timeout +- **2x** TypeError: not a function +- **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/Number | 44 | 3 | 0 | 3 | 50 | 88.0% | +| built-ins/Array | 21 | 17 | 5 | 7 | 50 | 46.7% | ## Per-category top failures (min 10 runnable, worst first) -### built-ins/Number (44/50 — 88.0%) +### built-ins/Array (21/45 — 46.7%) -- **3x** Timeout -- **3x** Test262Error (assertion failed) +- **14x** Test262Error (assertion failed) +- **7x** Timeout +- **2x** TypeError: not a function +- **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 12240798..5eff3395 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 — **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. - 2026-05-08 — **`Math.X.name` returns the JS-style method name.** `Math.acos.name`, `Math.acosh.name`, `Math.asin.name` were returning the SX symbol name (`"js-math-acos"` etc.). `js-unmap-fn-name` had mappings for the older Math methods but not the trig/hyperbolic/log family added later. Added mappings for sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, exp, log, log2, log10, expm1, log1p, clz32, imul, fround. built-ins/Math: 42/45 → 45/45 (100%). conformance.sh: 148/148.