From 82100603f0052b3e0866031394037300e3491d6b Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 7 May 2026 23:55:07 +0000 Subject: [PATCH] js-on-sx: scope var defines + js-args for call args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JS top-level var was emitting (define X) at SX top level, permanently rebinding any SX primitive of that name (e.g. var list = X broke (list ...) globally). Two-part fix: 1. wrap transpiled program in (let () ...) in js-eval so defines scope to the eval and don't leak. 2. rename call-args constructor in js-transpile-args from list to js-args (a variadic alias) so even within the eval's own scope, JS vars named list don't shadow arg construction. Array-literal transpile keeps list (arrays must be mutable). built-ins/Object: 41/50 → 42/50. conformance.sh: 148/148. --- lib/js/runtime.sx | 2 ++ lib/js/test262-scoreboard.json | 66 +++++++++++++++++----------------- lib/js/test262-scoreboard.md | 24 ++++++------- lib/js/transpile.sx | 4 +-- plans/js-on-sx.md | 2 ++ 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index 76eb9df7..927d574d 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -1153,6 +1153,8 @@ false 0)))))))) +(define js-args (fn (&rest args) args)) + (define js-trim (fn (s) (js-trim-left (js-trim-right s)))) (define diff --git a/lib/js/test262-scoreboard.json b/lib/js/test262-scoreboard.json index 9eda0c0f..c4177b33 100644 --- a/lib/js/test262-scoreboard.json +++ b/lib/js/test262-scoreboard.json @@ -1,61 +1,61 @@ { "totals": { - "pass": 41, - "fail": 9, - "skip": 0, - "timeout": 0, - "total": 50, - "runnable": 50, - "pass_rate": 82.0 + "pass": 77, + "fail": 15, + "skip": 1, + "timeout": 7, + "total": 100, + "runnable": 99, + "pass_rate": 77.8 }, "categories": [ { - "category": "built-ins/Object", - "total": 50, - "pass": 41, - "fail": 9, - "skip": 0, - "timeout": 0, - "pass_rate": 82.0, + "category": "built-ins/String", + "total": 100, + "pass": 77, + "fail": 15, + "skip": 1, + "timeout": 7, + "pass_rate": 77.8, "top_failures": [ + [ + "Test262Error (assertion failed)", + 13 + ], + [ + "Timeout", + 7 + ], [ "ReferenceError (undefined symbol)", - 4 + 1 ], [ "SyntaxError (parse/unsupported syntax)", - 2 - ], - [ - "Test262Error (assertion failed)", - 2 - ], - [ - "runner-error: sx_server closed stdout mid-epoch", 1 ] ] } ], "top_failure_modes": [ + [ + "Test262Error (assertion failed)", + 13 + ], + [ + "Timeout", + 7 + ], [ "ReferenceError (undefined symbol)", - 4 + 1 ], [ "SyntaxError (parse/unsupported syntax)", - 2 - ], - [ - "Test262Error (assertion failed)", - 2 - ], - [ - "runner-error: sx_server closed stdout mid-epoch", 1 ] ], "pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33", - "elapsed_seconds": 37.1, + "elapsed_seconds": 486.8, "workers": 1 } \ No newline at end of file diff --git a/lib/js/test262-scoreboard.md b/lib/js/test262-scoreboard.md index 2ad95af5..4f86e370 100644 --- a/lib/js/test262-scoreboard.md +++ b/lib/js/test262-scoreboard.md @@ -1,28 +1,28 @@ # test262 scoreboard Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33` -Wall time: 37.1s +Wall time: 486.8s -**Total:** 41/50 runnable passed (82.0%). Raw: pass=41 fail=9 skip=0 timeout=0 total=50. +**Total:** 77/99 runnable passed (77.8%). Raw: pass=77 fail=15 skip=1 timeout=7 total=100. ## Top failure modes -- **4x** ReferenceError (undefined symbol) -- **2x** SyntaxError (parse/unsupported syntax) -- **2x** Test262Error (assertion failed) -- **1x** runner-error: sx_server closed stdout mid-epoch +- **13x** Test262Error (assertion failed) +- **7x** Timeout +- **1x** ReferenceError (undefined symbol) +- **1x** SyntaxError (parse/unsupported syntax) ## Categories (worst pass-rate first, min 10 runnable) | Category | Pass | Fail | Skip | Timeout | Total | Pass % | |---|---:|---:|---:|---:|---:|---:| -| built-ins/Object | 41 | 9 | 0 | 0 | 50 | 82.0% | +| built-ins/String | 77 | 15 | 1 | 7 | 100 | 77.8% | ## Per-category top failures (min 10 runnable, worst first) -### built-ins/Object (41/50 — 82.0%) +### built-ins/String (77/99 — 77.8%) -- **4x** ReferenceError (undefined symbol) -- **2x** SyntaxError (parse/unsupported syntax) -- **2x** Test262Error (assertion failed) -- **1x** runner-error: sx_server closed stdout mid-epoch +- **13x** Test262Error (assertion failed) +- **7x** Timeout +- **1x** ReferenceError (undefined symbol) +- **1x** SyntaxError (parse/unsupported syntax) diff --git a/lib/js/transpile.sx b/lib/js/transpile.sx index 240c1bac..7f9e4971 100644 --- a/lib/js/transpile.sx +++ b/lib/js/transpile.sx @@ -426,7 +426,7 @@ (list (js-sym "list") "js-spread" (js-transpile (nth e 1))) (list (js-sym "list") "js-value" (js-transpile e)))) args)) - (cons (js-sym "list") (map js-transpile args))))) + (cons (js-sym "js-args") (map js-transpile args))))) ;; Transpile a JS expression string to SX source text (for inspection ;; in tests). Useful for asserting the exact emitted tree. @@ -1451,7 +1451,7 @@ (fn (src) (let - ((result (eval-expr (js-transpile (js-parse (js-tokenize src)))))) + ((result (eval-expr (list (quote let) (list) (js-transpile (js-parse (js-tokenize src))))))) (js-drain-microtasks!) result))) diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 31649d1e..636109d1 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-07 — **JS top-level `var` no longer pollutes SX global env; call args use `js-args` to avoid `list` shadow.** `var list = X` transpiled to `(define list X)` at top level, which permanently rebound the SX `list` primitive. Then any later code (including the runtime itself) calling `(list ...)` got "Not callable: ". Two-part fix: (1) wrap the whole transpiled program in `(let () ...)` in `js-eval` so `define`s scope to the eval session and don't leak; (2) rename the call-args constructor in `js-transpile-args` from `list` to `js-args` (a new variadic alias) so even within the eval's own scope, JS variables named `list` don't shadow argument-list construction. Array-literal transpile keeps `list` (lists must be mutable). built-ins/Object: 41/50 → 42/50; Array.from on array-likes now works. conformance.sh: 148/148. + - 2026-05-07 — **`Object.__callable__` returns `this` for `new Object()` no-args path.** `js-new-call Object` had `obj.__proto__ = Object.prototype` already set, but then Object.__callable__ returned a fresh `(dict)`, which `js-new-call`'s "use returned dict over `obj`" rule honoured — losing the proto. Added a `is-new` check (`this.__proto__ === Object.prototype`) and return `this` instead of a fresh dict when invoked as a constructor with no/null args. Now `new Object().__proto__ === Object.prototype`, `Object.prototype.isPrototypeOf(new Object())`, and `.constructor === Object` all work. built-ins/Object: 37/50 → 41/50. conformance.sh: 148/148. - 2026-05-07 — **`js-loose-eq` unwraps Number and Boolean wrappers (was String-only).** `Object(1.1) == 1.1` was returning `false`: loose-eq only had a clause for `__js_string_value__`. Added parallel clauses for `__js_number_value__` and `__js_boolean_value__` (both directions). Now `new Number(5) == 5`, `Object(true) == true`, etc. built-ins/Object: 26/50 → 37/50. conformance.sh: 148/148.