diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index ee42519e..96999826 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -1265,6 +1265,10 @@ ((contains? (keys v) "__js_string_value__") "[object String]") ((contains? (keys v) "__js_number_value__") "[object Number]") ((contains? (keys v) "__js_boolean_value__") "[object Boolean]") + ((contains? (keys v) "__js_error_data__") "[object Error]") + ((contains? (keys v) "__js_is_date__") "[object Date]") + ((contains? (keys v) "__map_keys__") "[object Map]") + ((contains? (keys v) "__set_items__") "[object Set]") ((= v (get Number "prototype")) "[object Number]") ((= v (get String "prototype")) "[object String]") ((= v (get Boolean "prototype")) "[object Boolean]") @@ -3790,6 +3794,8 @@ (define js-object-freeze (fn (o) o)) +(define __js_ctor_proto__ (dict)) + (define js-object-get-prototype-of (fn @@ -3799,7 +3805,16 @@ ((js-undefined? o) (error "TypeError: Cannot convert undefined to object")) ((dict? o) - (if (contains? (keys o) "__proto__") (get o "__proto__") nil)) + (cond + ((contains? (keys o) "__proto__") (get o "__proto__")) + (else nil))) + ((js-function? o) + (let + ((id (js-ctor-id o))) + (cond + ((dict-has? __js_ctor_proto__ id) + (get __js_ctor_proto__ id)) + (else nil)))) (else nil)))) (define @@ -5659,6 +5674,34 @@ (dict-set! Map "__proto__" (get js-function-global "prototype")) (dict-set! Set "__proto__" (get js-function-global "prototype")) (dict-set! Date "__proto__" (get js-function-global "prototype")) + (dict-set! __js_ctor_proto__ (js-ctor-id TypeError) Error) + (dict-set! __js_ctor_proto__ (js-ctor-id RangeError) Error) + (dict-set! __js_ctor_proto__ (js-ctor-id SyntaxError) Error) + (dict-set! __js_ctor_proto__ (js-ctor-id ReferenceError) Error) + (dict-set! __js_ctor_proto__ (js-ctor-id URIError) Error) + (dict-set! __js_ctor_proto__ (js-ctor-id EvalError) Error) + (dict-set! (js-get-ctor-proto TypeError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto RangeError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto SyntaxError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto ReferenceError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto URIError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto EvalError) "__proto__" (js-get-ctor-proto Error)) + (dict-set! (js-get-ctor-proto Error) "__proto__" (get Object "prototype")) + (dict-set! (js-get-ctor-proto Error) "name" "Error") + (dict-set! (js-get-ctor-proto Error) "message" "") + (dict-set! (js-get-ctor-proto Error) "constructor" Error) + (dict-set! (js-get-ctor-proto TypeError) "name" "TypeError") + (dict-set! (js-get-ctor-proto TypeError) "constructor" TypeError) + (dict-set! (js-get-ctor-proto RangeError) "name" "RangeError") + (dict-set! (js-get-ctor-proto RangeError) "constructor" RangeError) + (dict-set! (js-get-ctor-proto SyntaxError) "name" "SyntaxError") + (dict-set! (js-get-ctor-proto SyntaxError) "constructor" SyntaxError) + (dict-set! (js-get-ctor-proto ReferenceError) "name" "ReferenceError") + (dict-set! (js-get-ctor-proto ReferenceError) "constructor" ReferenceError) + (dict-set! (js-get-ctor-proto URIError) "name" "URIError") + (dict-set! (js-get-ctor-proto URIError) "constructor" URIError) + (dict-set! (js-get-ctor-proto EvalError) "name" "EvalError") + (dict-set! (js-get-ctor-proto EvalError) "constructor" EvalError) (dict-set! (get Array "prototype") "__proto__" (get Object "prototype")) (dict-set! (get Number "prototype") "__proto__" (get Object "prototype")) (dict-set! (get String "prototype") "__proto__" (get Object "prototype")) diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 46ebc5f6..7fe0b552 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-08 — **NativeError prototype chain wired: `Object.getPrototypeOf(EvalError) === Error`, `Error.prototype.constructor === Error`, `[object Error]` brand.** Three pieces: (1) `js-object-tostring-class` now recognises `__js_error_data__` (returns `"[object Error]"`), `__js_is_date__` (`"[object Date]"`), `__map_keys__` / `__set_items__` (`"[object Map]"` / `"[object Set]"`) — these were all falling through to `"[object Object]"`. (2) New `__js_ctor_proto__` side-table maps lambda-ctor identity → its [[Prototype]] constructor; `js-object-get-prototype-of` consults it for non-dict callables. Populated for all six native error subclasses (TypeError/RangeError/SyntaxError/ReferenceError/URIError/EvalError) → Error. (3) Each subclass's `prototype.__proto__` set to `Error.prototype`, and `Error.prototype` gets `name`, `message`, `constructor` populated; each subclass prototype also gets its own `name` and `constructor`. Result: built-ins/NativeErrors 14/30 → 27/30 (+13), built-ins/Error 11/30 → 17/30 (+6). Object/Map/Array unchanged. conformance.sh: 148/148. + - 2026-05-08 — **Object literals get `__proto__: Object.prototype`; try/catch wraps SX error strings into JS Error instances.** Two fixes that work together: (1) `js-make-obj` now sets `__proto__` to `(get Object "prototype")` on every plain object literal `{}` — was missing, so `({}) instanceof Object` was `false`. (2) `js-transpile-try` now wraps the catch param via `js-wrap-exn` — when SX throws an `Eval_error("TypeError: ...")` / `("RangeError: ...")` / `("SyntaxError: ...")` etc. into the catch body, the user previously got a plain string. Now each prefix dispatches to the matching `js-new-call` so `e instanceof TypeError` etc. is truthy. Note: `Eval_error("Undefined symbol: y")` is NOT caught by SX `guard` at all, so the `1 + y → ReferenceError` shape remains unfixable from JS land — out of scope (would need OCaml-side change to make symbol lookup raisable). Result: language/expressions/instanceof 13/30 → 18/30 (+5). Object/Map/Array unchanged. conformance.sh: 148/148. - 2026-05-08 — **`Date` constructor + prototype stubs.** `Date` was undefined globally — every test in `built-ins/Date` died at `new Date(...)` with ReferenceError. Implemented as a dict-with-`__callable__` (same pattern as `Map`/`Set`/`Object`). Constructor accepts 0 args (epoch 0), 1 number arg (ms), 1 string arg (parses leading `YYYY` to compute approx ms via `(year-1970)*31557600000`), or 2+ args (year, month, day → simple ms calc). `__date_value__` is the internal slot. Statics: `Date.now()`, `Date.parse(s)`, `Date.UTC(...)`. Prototype: `getTime` / `valueOf` / `setTime`, all `getX` / `getUTCX` (most return 0/1 — only `getFullYear` actually computes), `toISOString` / `toJSON` / `toString` / `toUTCString` produce `YYYY-01-01T00:00:00.000Z` from the stored year, plus the locale variants. Wired `Date` into `js-global` and the post-init `__proto__` chain. The maths is approximate (ignores leap years, varying month lengths, timezone offsets) — but the structural tests `typeof new Date(...) === "object"` and the basic flow now work. Result: built-ins/Date 0/30 → 3/30 (rest timeouts/assertions on month-rollover/leap-year math we don't model). conformance.sh: 148/148.