From 836b31a5b63e70d5f2e48a4ef2da014e0ea5ede8 Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 10 May 2026 05:56:33 +0000 Subject: [PATCH] js-on-sx: arguments object is a mutable list copy --- lib/js/transpile.sx | 8 +++++++- plans/js-on-sx.md | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/js/transpile.sx b/lib/js/transpile.sx index 5e177205..7942d895 100644 --- a/lib/js/transpile.sx +++ b/lib/js/transpile.sx @@ -994,6 +994,12 @@ (define js-arguments-build-form + (fn + (params) + (list (js-sym "js-list-copy") (js-arguments-build-form-raw params)))) + +(define + js-arguments-build-form-raw (fn (params) (cond @@ -1005,7 +1011,7 @@ (list (js-sym "cons") (js-param-sym (first params)) - (js-arguments-build-form (rest params))))))) + (js-arguments-build-form-raw (rest params))))))) (define js-param-init-forms diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index a6a0a666..ef4f32b2 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 — **`arguments` object inside functions is now a mutable list.** `js-arguments-build-form` produced `(cons p1 (cons p2 __extra_args__))` which yielded a structurally-shared (immutable) list — `arguments[1] = 7; arguments[1]++` raised "set-nth!: list is immutable". Wrapping the build in `js-list-copy` so each function entry constructs a fresh mutable list. Existing reads (`arguments.length`, `arguments[i]`) unaffected. Result: language/expressions/postfix-increment 14/30 → 15/30. conformance.sh: 148/148. + - 2026-05-10 — **`String.prototype.split(undefined)` returns `[wholeString]`; function-expression bodies have spec-correct implicit `undefined` return.** (1) `js-string-method "split"` was calling `js-to-string` on the separator unconditionally, so `"undefinedd".split(undefined)` produced `["", "d"]` (split by `"undefined"`); also `limit=0` returned the whole-string list instead of `[]`. New arms: `undefined` separator → `[s]`, `limit=0` → `[]`, otherwise existing string-split. (2) Function expressions wrapped the body in `(call/cc (fn (__return__) (begin )))` and used the begin's last expression as the implicit return value. So `function F(){ this.x = function(){return 99} }` returned the inner lambda (because `js-set-prop` returns the rhs), and `new F()` saw a callable return and replaced the freshly-allocated `this` with it — so `i.x` was missing. Append `nil` to the begin so the implicit completion is always `:js-undefined`; explicit `return` still works via call/cc as before. Result: built-ins/String/prototype/split 8/30 → 10/30. Constructors with function-valued `this.X` now keep their assignments. conformance.sh: 148/148. - 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.