From adc4cb89c6b135f73dd7719717b959aabec1f00d Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 9 May 2026 12:07:34 +0000 Subject: [PATCH] js-on-sx: fn proto chain walks through functions; fn.prototype = X persists --- lib/js/runtime.sx | 8 ++++++++ plans/js-on-sx.md | 2 ++ 2 files changed, 10 insertions(+) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index a14e40c2..ef2c8bb1 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -3300,6 +3300,8 @@ (cond ((= obj nil) js-undefined) ((js-undefined? obj) js-undefined) + ((or (= (type-of obj) "lambda") (= (type-of obj) "function") (= (type-of obj) "component")) + (js-dict-get-walk (get js-function-global "prototype") skey)) ((not (= (type-of obj) "dict")) js-undefined) ((dict-has? obj skey) (get obj skey)) ((dict-has? obj "__proto__") @@ -3383,6 +3385,12 @@ (dict-set! obj sk val) val))) ((= (type-of obj) "list") (do (js-list-set! obj key val) val)) + ((and + (or (= (type-of obj) "lambda") (= (type-of obj) "function") (= (type-of obj) "component")) + (= (js-to-string key) "prototype")) + (let + ((id (js-ctor-id obj))) + (begin (dict-set! __js_proto_table__ id val) val))) (else val)))) (define js-list-set! diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index 04772e19..84035e8a 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 — **Functions inherit through their `__proto__` chain in `js-dict-get-walk`; `fn.prototype = X` actually persists.** Two related fixes around the function-as-object semantics: (1) `js-dict-get-walk` was returning undefined the moment it hit any non-dict in the proto chain — but the chain often runs through a function (e.g. `obj.__proto__ === proto` where `proto` is itself a function returned by `Function()`). Now treats lambda/function/component as if they have `__proto__ === Function.prototype` and continues the walk. (2) `js-set-prop` was a no-op when called on a function with key `"prototype"` (returned val without storing) — so `FACTORY.prototype = proto` silently dropped on the floor. Now redirects to `__js_proto_table__` so the next `new FACTORY` picks up the right proto. Result: built-ins/Function/prototype/call 7/30 → 12/30, apply 12/30 → 16/30. Object 30/30, Map 18/30, Array 18/30 unchanged. conformance.sh: 148/148. + - 2026-05-09 — **`Function.prototype.call` / `apply` substitute global as `this` when caller passes null/undefined.** Per non-strict ES, `f.apply(null)` and `f.call(undefined)` should bind `this` to the global object inside `f`. We were passing `null`/`undefined` straight through to `js-call-with-this`, so `this.field = "green"` (the test pattern) silently failed because the function's `this` was still undefined and `this.field` did nothing. Updated both clauses in `js-invoke-function-method` to swap in `js-global-this` when the caller's `this`-arg is null or `:js-undefined`. Result: built-ins/Function/prototype 4/30 → 11/30 (+7), apply 0+ → 12/30, call 0+ → 7/30. Object 30/30 holds. conformance.sh: 148/148. - 2026-05-09 — **`js-global` exposes more built-in constructors and helpers.** Was missing `Function` (so `typeof this.Function === "undefined"`), the seven Error subclasses, the URI helpers, `eval`, `Promise`, and stubs for `Symbol` / `AggregateError` / `SuppressedError`. Added all of them. Did NOT add `globalThis` as a self-reference — that creates a cycle which makes `inspect` (used by `js-ctor-id`) hang on every error path that tries to format a constructor identity. Result: built-ins/global 19/29 → 22/27. Object 30/30, property-accessors 14/21 unchanged. conformance.sh: 148/148.