js-on-sx: raise JS TypeError for non-callable callee, undefined()
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 59s

Calling a non-callable raised an OCaml-level Eval_error "Not callable"
that JS try/catch couldn't intercept. Added a (js-function? callable)
precheck in js-apply-fn that raises a TypeError instance via
(js-new-call TypeError (list msg)) so e instanceof TypeError is
true. Same swap for the undefined() branch in js-call-plain (was
raising a bare string). built-ins/String: 71/99 → 73/99 (canonical),
74/99 → 75/99 (isolated). conformance.sh: 148/148.
This commit is contained in:
2026-05-07 15:58:16 +00:00
parent cf0ba8a02a
commit 843c3a7e5e
4 changed files with 56 additions and 45 deletions

View File

@@ -425,30 +425,44 @@
(fn-val args)
(let
((callable (if (and (dict? fn-val) (contains? (keys fn-val) "__callable__")) (get fn-val "__callable__") fn-val)))
(cond
((= (len args) 0) (callable))
((= (len args) 1) (callable (nth args 0)))
((= (len args) 2) (callable (nth args 0) (nth args 1)))
((= (len args) 3)
(callable (nth args 0) (nth args 1) (nth args 2)))
((= (len args) 4)
(callable (nth args 0) (nth args 1) (nth args 2) (nth args 3)))
((= (len args) 5)
(callable
(nth args 0)
(nth args 1)
(nth args 2)
(nth args 3)
(nth args 4)))
((= (len args) 6)
(callable
(nth args 0)
(nth args 1)
(nth args 2)
(nth args 3)
(nth args 4)
(nth args 5)))
(else (apply callable args))))))
(if
(not (js-function? callable))
(raise
(js-new-call
TypeError
(list (str (str fn-val) " is not a function"))))
(cond
((= (len args) 0) (callable))
((= (len args) 1) (callable (nth args 0)))
((= (len args) 2)
(callable (nth args 0) (nth args 1)))
((= (len args) 3)
(callable
(nth args 0)
(nth args 1)
(nth args 2)))
((= (len args) 4)
(callable
(nth args 0)
(nth args 1)
(nth args 2)
(nth args 3)))
((= (len args) 5)
(callable
(nth args 0)
(nth args 1)
(nth args 2)
(nth args 3)
(nth args 4)))
((= (len args) 6)
(callable
(nth args 0)
(nth args 1)
(nth args 2)
(nth args 3)
(nth args 4)
(nth args 5)))
(else (apply callable args)))))))
;; ── Relational comparisons ────────────────────────────────────────
@@ -608,7 +622,7 @@
(fn-val args)
(cond
((js-undefined? fn-val)
(error "TypeError: undefined is not a function"))
(raise (js-new-call TypeError (list "undefined is not a function"))))
((and (dict? fn-val) (contains? (keys fn-val) "__callable__"))
(js-call-with-this :js-undefined (get fn-val "__callable__") args))
(else (js-call-with-this :js-undefined fn-val args)))))

View File

@@ -1,22 +1,22 @@
{
"totals": {
"pass": 71,
"fail": 22,
"pass": 73,
"fail": 20,
"skip": 1,
"timeout": 6,
"total": 100,
"runnable": 99,
"pass_rate": 71.7
"pass_rate": 73.7
},
"categories": [
{
"category": "built-ins/String",
"total": 100,
"pass": 71,
"fail": 22,
"pass": 73,
"fail": 20,
"skip": 1,
"timeout": 6,
"pass_rate": 71.7,
"pass_rate": 73.7,
"top_failures": [
[
"Test262Error (assertion failed)",
@@ -31,11 +31,11 @@
6
],
[
"Unhandled: Not callable: \\\\\\",
2
"ReferenceError (undefined symbol)",
1
],
[
"ReferenceError (undefined symbol)",
"SyntaxError (parse/unsupported syntax)",
1
]
]
@@ -54,10 +54,6 @@
"Timeout",
6
],
[
"Unhandled: Not callable: \\\\\\",
2
],
[
"ReferenceError (undefined symbol)",
1
@@ -68,6 +64,6 @@
]
],
"pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33",
"elapsed_seconds": 375.3,
"elapsed_seconds": 280.3,
"workers": 1
}

View File

@@ -1,16 +1,15 @@
# test262 scoreboard
Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`
Wall time: 375.3s
Wall time: 280.3s
**Total:** 71/99 runnable passed (71.7%). Raw: pass=71 fail=22 skip=1 timeout=6 total=100.
**Total:** 73/99 runnable passed (73.7%). Raw: pass=73 fail=20 skip=1 timeout=6 total=100.
## Top failure modes
- **10x** Test262Error (assertion failed)
- **8x** TypeError: not a function
- **6x** Timeout
- **2x** Unhandled: Not callable: \\\
- **1x** ReferenceError (undefined symbol)
- **1x** SyntaxError (parse/unsupported syntax)
@@ -18,14 +17,14 @@ Wall time: 375.3s
| Category | Pass | Fail | Skip | Timeout | Total | Pass % |
|---|---:|---:|---:|---:|---:|---:|
| built-ins/String | 71 | 22 | 1 | 6 | 100 | 71.7% |
| built-ins/String | 73 | 20 | 1 | 6 | 100 | 73.7% |
## Per-category top failures (min 10 runnable, worst first)
### built-ins/String (71/99 — 71.7%)
### built-ins/String (73/99 — 73.7%)
- **10x** Test262Error (assertion failed)
- **8x** TypeError: not a function
- **6x** Timeout
- **2x** Unhandled: Not callable: \\\
- **1x** ReferenceError (undefined symbol)
- **1x** SyntaxError (parse/unsupported syntax)

View File

@@ -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-apply-fn` raises a JS-level `TypeError` instance when the callee isn't callable.** Calling a non-callable (`'a'()`, `(1+2)()`, etc.) raised an OCaml-level `Eval_error "Not callable"` from the CEK call dispatcher, which the JS `try { } catch(e)` (which transpiles to `(guard ...)`) couldn't intercept. Added a `(js-function? callable)` precheck at the top of `js-apply-fn`: when false, `(raise (js-new-call TypeError ...))` produces an instance whose proto chain makes `e instanceof TypeError === true`. Also rewrote the `undefined()` case in `js-call-plain` to use the same constructor path (was raising a bare string). built-ins/String: 71/99 → 73/99 (canonical), 74/99 → 75/99 (isolated). conformance.sh: 148/148.
- 2026-05-07 — **`js-dict-get-walk` falls back to `Object.prototype` when an object has no `__proto__`.** Object literals (`{}`, `{a:1}`) didn't carry a `__proto__` link, so `({}).toString()` couldn't find `Object.prototype.toString` — and overriding `Object.prototype.toString` had no effect on plain objects. Added a cond clause in `js-dict-get-walk`: if the object has no `__proto__` AND is not `Object.prototype` itself, walk into `Object.prototype`. Termination guaranteed because Object.prototype is the recursion base case. Now `({}).toString() === "[object Object]"`, override of `Object.prototype.toString` propagates to plain objects, and `({a:1}).hasOwnProperty('a') === true`. built-ins/String: 69/99 → 71/99 (canonical), 71/99 → 74/99 (isolated). conformance.sh: 148/148.
- 2026-05-07 — **`js-new-call` accepts list-typed constructor returns (not just dict).** `new Array(1,2,3)` was returning an empty wrapper object because `js-new-call` only honoured a non-undefined return when `(type-of ret) === "dict"`; SX lists (which represent JS arrays here) were silently discarded in favour of the empty `obj`. Widened the check to accept `"list"` returns. Fixes `new Array(1,2,3).length`, `String(new Array(1,2,3))`, and any constructor whose body returns a list. built-ins/String 67/99 → 69/99 (canonical), 70/99 → 71/99 (isolated). conformance.sh: 148/148.