Fix process-bindings scope loss and async-invoke arity, bootstrap async adapter
Two bugs fixed:
1. process-bindings used merge(env) which returns {} for Env objects
(Env is not a dict subclass). Changed to env-extend in render.sx
and adapter-async.sx. This caused "Undefined symbol: theme" etc.
2. async-aser-eval-call passed evaled-args list to async-invoke(&rest),
double-wrapping it. Changed to inline apply + coroutine check.
Also: bootstrap define-async into sx_ref.py (Phase 6), replace ~1000 LOC
hand-written async_eval_ref.py with 24-line thin re-export shim.
Test runner now uses Env (not flat dict) for render envs to catch scope bugs.
8 new regression tests (4 scope chain, 2 native callable arity, 2 render).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1194,7 +1194,7 @@ PLATFORM_JS_PRE = '''
|
||||
|
||||
// JSON / dict helpers for island state serialization
|
||||
function jsonSerialize(obj) {
|
||||
try { return JSON.stringify(obj); } catch(e) { return "{}"; }
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
function isEmptyDict(d) {
|
||||
if (!d || typeof d !== "object") return true;
|
||||
@@ -1204,11 +1204,34 @@ PLATFORM_JS_PRE = '''
|
||||
|
||||
function envHas(env, name) { return name in env; }
|
||||
function envGet(env, name) { return env[name]; }
|
||||
function envSet(env, name, val) { env[name] = val; }
|
||||
function envSet(env, name, val) {
|
||||
// Walk prototype chain to find where the variable is defined (for set!)
|
||||
var obj = env;
|
||||
while (obj !== null && obj !== Object.prototype) {
|
||||
if (obj.hasOwnProperty(name)) { obj[name] = val; return; }
|
||||
obj = Object.getPrototypeOf(obj);
|
||||
}
|
||||
// Not found in any parent scope — set on the immediate env
|
||||
env[name] = val;
|
||||
}
|
||||
function envExtend(env) { return Object.create(env); }
|
||||
function envMerge(base, overlay) {
|
||||
// Same env or overlay is descendant of base — just extend, no copy.
|
||||
// This prevents set! inside lambdas from modifying shadow copies.
|
||||
if (base === overlay) return Object.create(base);
|
||||
var p = overlay;
|
||||
for (var d = 0; p && p !== Object.prototype && d < 100; d++) {
|
||||
if (p === base) return Object.create(base);
|
||||
p = Object.getPrototypeOf(p);
|
||||
}
|
||||
// General case: extend base, copy ONLY overlay properties that don't
|
||||
// exist in the base chain (avoids shadowing closure bindings).
|
||||
var child = Object.create(base);
|
||||
if (overlay) for (var k in overlay) if (overlay.hasOwnProperty(k)) child[k] = overlay[k];
|
||||
if (overlay) {
|
||||
for (var k in overlay) {
|
||||
if (overlay.hasOwnProperty(k) && !(k in base)) child[k] = overlay[k];
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
@@ -1649,8 +1672,11 @@ PLATFORM_DOM_JS = """
|
||||
function domListen(el, name, handler) {
|
||||
if (!_hasDom || !el) return function() {};
|
||||
// Wrap SX lambdas from runtime-evaluated island code into native fns
|
||||
// If lambda takes 0 params, call without event arg (convenience for on-click handlers)
|
||||
var wrapped = isLambda(handler)
|
||||
? function(e) { try { invoke(handler, e); } catch(err) { console.error("[sx-ref] domListen handler error:", name, err); } }
|
||||
? (lambdaParams(handler).length === 0
|
||||
? function(e) { try { invoke(handler); } catch(err) { console.error("[sx-ref] domListen handler error:", name, err); } }
|
||||
: function(e) { try { invoke(handler, e); } catch(err) { console.error("[sx-ref] domListen handler error:", name, err); } })
|
||||
: handler;
|
||||
if (name === "click") logInfo("domListen: click on <" + (el.tagName||"?").toLowerCase() + "> text=" + (el.textContent||"").substring(0,20) + " isLambda=" + isLambda(handler));
|
||||
el.addEventListener(name, wrapped);
|
||||
|
||||
Reference in New Issue
Block a user