From d145532afec1c0afaf180e25b6402bb0f3cdcef1 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 09:44:48 +0000 Subject: [PATCH] js-on-sx: instanceof accepts function operands (fn instanceof Function/Object) --- lib/js/runtime.sx | 13 ++++++++++++- plans/js-on-sx.md | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index edfe2640..c5984d24 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -769,9 +769,20 @@ (fn (obj ctor) (cond - ((not (= (type-of obj) "dict")) false) ((not (js-function? ctor)) (error "TypeError: Right-hand side of instanceof is not callable")) + ((js-function? obj) + (let + ((proto (js-get-ctor-proto ctor)) + (fnproto (get js-function-global "prototype")) + (objproto (get Object "prototype"))) + (cond + ((= proto fnproto) true) + ((= proto objproto) true) + ((and (= (type-of obj) "dict") (contains? (keys obj) "__proto__")) + (js-instanceof-walk obj proto)) + (else false)))) + ((not (= (type-of obj) "dict")) false) (else (let ((proto (js-get-ctor-proto ctor))) diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index d5e4bec0..b76ae12b 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-09 — **`instanceof` accepts function operands.** `js-instanceof` was returning false on the very first check `(not (= (type-of obj) "dict"))` for any non-dict left-hand side — but functions are objects too, so `MyFunct instanceof Function` should be true (functions inherit from `Function.prototype`) and `MyFunct instanceof Object` likewise. Added a `js-function?` arm that special-cases against `Function.prototype` and `Object.prototype`, and falls through to the proto-walk if the function happens to also have a `__proto__` slot (dict-with-`__callable__` constructors do). Result: language/expressions/instanceof 20/30 → 24/30. Object 30/30, Error 22/30, Function 4/30 unchanged. conformance.sh: 148/148. + - 2026-05-09 — **Relational operators ToPrimitive their operands (string-vs-numeric decision); `<= / >=` short-circuit to false on NaN.** `js-lt` was checking only `(type-of)` for `"string"` to pick the string-compare branch, so `{} < function(){return 1}` fell into `(< NaN NaN)` (returning false) while `{}.toString() < fn.toString()` returned true (lex). Reused `js-add-unwrap` (now extended to coerce lambda/function/component to their `js-to-string` representation, matching the function's `[object Function]` / `function () { [native code] }` semantics) so both operands are first reduced to primitives. Added explicit NaN check in the numeric branch of `js-lt` and `js-le`. `js-le` no longer does `(not (js-lt b a))` — that gave the wrong answer on NaN (NaN ≤ x must be false, not !(x < NaN) = true). `js-ge` similarly switched to `(js-le b a)`. Result: language/expressions/less-than 23/30 → 24/30, greater-than 23/30 → 24/30, addition 24/30 → 25/30. Object 30/30 maintained. conformance.sh: 148/148. - 2026-05-09 — **`Error(msg)` / `TypeError(msg)` / etc. (called without `new`) now return a proper instance.** Was checking `(if (= (type-of this) "dict") nil)` and falling through to return undefined when called as a plain function — but per spec, every Error subclass must return a new instance regardless of `new`. Refactored each constructor to `(js-error-init! (js-error-receiver Ctor) "Name" args)`: `js-error-receiver` returns `this` if it's a dict (the `new`-call case) and otherwise re-enters via `js-new-call ctor (list)` to create a properly-prototyped instance; `js-error-init!` sets `message`, `name`, `__js_error_data__`. Cleaner than the seven near-identical duplicated bodies. Result: built-ins/Error 17/30 → 22/30 (+5), language/expressions/instanceof 18/30 → 20/30. NativeErrors holds at 27/30. conformance.sh: 148/148.