diff --git a/lib/js/test262-runner.py b/lib/js/test262-runner.py index b3913751..3c5c3daf 100644 --- a/lib/js/test262-runner.py +++ b/lib/js/test262-runner.py @@ -1060,11 +1060,45 @@ def _worker_run(args): # --------------------------------------------------------------------------- +_HARNESS_INCLUDE_CACHE: dict = {} + +# Only inline these small harness files per-test. Large ones like propertyHelper.js +# multiply js-eval/JIT cost by ~5-10x and push tests over the per-test timeout. +_INLINE_INCLUDES = {"nans.js", "sta.js", "byteConversionValues.js", "compareArray.js"} + + +def _load_harness_include(name: str) -> str: + """Read an upstream harness include file (e.g. nans.js). + Returns empty string if the file isn't present. + """ + if name in _HARNESS_INCLUDE_CACHE: + return _HARNESS_INCLUDE_CACHE[name] + path = HARNESS_DIR / name + try: + src = path.read_text() + except OSError: + src = "" + _HARNESS_INCLUDE_CACHE[name] = src + return src + + def assemble_source(t): """Return JS source to feed to js-eval. Harness is preloaded, so we only - append the test source (plus negative-test prep if needed). + append the test source (plus a small allowlist of per-test includes). """ - return t.src + if not getattr(t.fm, "includes", None): + return t.src + parts = [] + for inc in t.fm.includes: + if inc not in _INLINE_INCLUDES: + continue + chunk = _load_harness_include(inc) + if chunk: + parts.append(chunk) + if not parts: + return t.src + parts.append(t.src) + return "\n".join(parts) def aggregate(results): @@ -1242,7 +1276,7 @@ def main(argv): shards = [[] for _ in range(n_workers)] for i, t in enumerate(tests): shards[i % n_workers].append( - (t.rel, t.category, t.src, t.fm.negative_phase, t.fm.negative_type) + (t.rel, t.category, assemble_source(t), t.fm.negative_phase, t.fm.negative_type) ) t_run_start = time.monotonic() diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index fe03df54..620c6c4e 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 — **test262-runner inlines small upstream harness includes (`nans.js`, `sta.js`, `byteConversionValues.js`, `compareArray.js`) per-test.** The runner parsed `includes:` frontmatter but never used it, so tests like `built-ins/isNaN/return-true-nan.js` (which depends on `var NaNs = [...]`) failed with "ReferenceError: undefined symbol". Added `_load_harness_include` (cached) and `assemble_source` now prepends each allowlisted include's source to the test. Allowlist excludes large helpers like `propertyHelper.js` because per-test js-eval+JIT cost on a 371-line harness pushes tests over the 15s per-test timeout (regressed Math/abs 7/7 → 4/7 in a first-pass attempt before allowlisting). Result: built-ins/isNaN 2/7 → 3/7. conformance.sh: 148/148. + - 2026-05-10 — **Real `Date.prototype.setFullYear/setMonth/setDate/setHours/setMinutes/setSeconds/setMilliseconds` (+ UTC variants) and a corrected `setTime`.** All Date setters were missing — only `setTime` existed and didn't validate. Added a unified `js-date-setter(d, field, args)` that decomposes the current ms into `(y mo da hh mm ss msv)` via `js-date-decompose`, splices in the `args` per the field's optional-arg contract (e.g. `setHours(h, m?, s?, ms?)`), recomposes via `js-date-civil-to-days`, and TimeClips at ±8.64e15. NaN args anywhere → ms set to NaN. Wired all 14 setters to the helper. Hit a parser gotcha: SX `cond` clause body is single-form only — multi-expression bodies like `(else (dict-set! ...) new-ms)` silently treat the second form as `( new-ms)` ("Not callable: false"). Wrapped these in `(begin ...)`. Result: setFullYear 5/18 → 13/18 (+8). setHours 5/21 → 15/21 (+10). setMonth 3/15 → 9/15 (+6). setMinutes 4/16 → 10/16 (+6). setSeconds 3/15 → 9/15 (+6). setDate 2/12 → 6/12 (+4). setMilliseconds 2/12 → 6/12 (+4). setTime 4/9 → 6/9 (+2). conformance.sh: 148/148. - 2026-05-10 — **`Object.assign` keys now visible to `Object.keys` / `JSON.stringify`.** `Object.assign({}, {a:1})` was mutating the target via `dict-set!` which bypasses our `__js_order__` insertion-order side table; `Object.keys(t)` (which iterates `__js_order__` when present) returned `[]`, and `JSON.stringify` saw nothing. Switched `js-object-assign` to use `js-set-prop` (which calls `js-obj-order-add!` on new keys) for both dict and string sources. Result: built-ins/Object/assign 13/25 → 14/25. conformance.sh: 148/148.