From df5e36aa5ea443263de3c262482656f30c1fba45 Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 10 May 2026 04:18:20 +0000 Subject: [PATCH] js-on-sx: number/boolean method dispatch falls back to Number/Boolean.prototype --- lib/js/runtime.sx | 30 ++++++++++++++++++++---------- plans/js-on-sx.md | 2 ++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index c2177bf2..6db5b41b 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -442,11 +442,16 @@ ((= key "toPrecision") (js-to-string recv)) ((= key "toExponential") (js-to-string recv)) (else - (error - (str - "TypeError: " - (js-to-string key) - " is not a function (on number)")))))) + (let + ((m (js-dict-get-walk (get Number "prototype") (js-to-string key)))) + (cond + ((js-undefined? m) + (error + (str + "TypeError: " + (js-to-string key) + " is not a function (on number)"))) + (else (js-call-with-this recv m args)))))))) (define js-invoke-function-objproto @@ -472,11 +477,16 @@ ((= key "toString") (if recv "true" "false")) ((= key "valueOf") recv) (else - (error - (str - "TypeError: " - (js-to-string key) - " is not a function (on boolean)")))))) + (let + ((m (js-dict-get-walk (get Boolean "prototype") (js-to-string key)))) + (cond + ((js-undefined? m) + (error + (str + "TypeError: " + (js-to-string key) + " is not a function (on boolean)"))) + (else (js-call-with-this recv m args)))))))) (define js-num-to-str-radix diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 6d7dffec..c4ce3bad 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-10 — **Number/Boolean primitive method dispatch falls back to `Number.prototype` / `Boolean.prototype`.** When a user assigned a String method onto `Number.prototype` (e.g. `Number.prototype.toUpperCase = String.prototype.toUpperCase; NaN.toUpperCase()`), `js-invoke-number-method` rejected the unknown key with "is not a function (on number)" — it never walked the prototype. Added a fallback in both `js-invoke-number-method` and `js-invoke-boolean-method`: on unknown keys, `js-dict-get-walk` the constructor prototype; if found, `js-call-with-this` it. Result: built-ins/String/prototype/toUpperCase 16/25 → 19/25 (+3). Boolean 29/30. conformance.sh: 148/148. + - 2026-05-10 — **`String.prototype.*` ToString-coerces non-string/non-undef this; `.call` / `.apply` skip global-coercion for built-in callables.** `String.prototype.trim.call(false)` was returning `"[object Object]"` because (a) `.call`/`.apply` blanket-coerced null/undefined `thisArg` to `js-global-this`, swallowing the original null, and (b) `js-string-proto-fn` fell back to `"[object Object]"` for any non-string this. (1) `js-string-proto-fn` now ToString-coerces primitive thisVal and raises TypeError for null/undefined (matches `RequireObjectCoercible` semantics for built-in String methods). (2) New `js-call-this-coerce` helper applies the legacy `js-coerce-this-arg` only when `recv` is a user lambda/component; built-in dict-with-`__callable__` methods get the raw `thisArg` (so they can see and reject null/undefined themselves, or accept primitive thisArgs without ToObject). Result: built-ins/String/prototype/trim 7/30 → 30/30 (+23). Function/prototype/apply 10/30 → 21/30. expressions/array 21/30 → 22/30. conformance.sh: 148/148. - 2026-05-10 — **`**` / `Math.pow` honour JS spec edge cases for NaN, ±0, abs(base)=1+Infinity, plus `Number.prototype.valueOf` accepts ignored args.** (1) New `js-pow-spec` shared by `js-pow` (operator) and `js-math-pow`: NaN exponent → NaN, exponent 0 → 1 (even with NaN base), NaN base + non-zero exp → NaN, abs(base)=1 with exp=±Infinity → NaN. Underlying `pow` handles the rest. (2) Number.prototype.valueOf was `(fn () ...)` and rejected the spec-allowed extra arg with "lambda expects 0 args, got 1"; now `(fn (&rest args) ...)`. Result: language/expressions/exponentiation 23/30 → 25/30 (+2). built-ins/Math/pow 27/27 holds. conformance.sh: 148/148.