diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index d1107fa..9465cbd 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-07T01:23:32Z"; + var SX_VERSION = "2026-03-07T01:41:53Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -174,8 +174,12 @@ function envHas(env, name) { return name in env; } function envGet(env, name) { return env[name]; } function envSet(env, name, val) { env[name] = val; } - function envExtend(env) { return merge(env); } - function envMerge(base, overlay) { return merge(base, overlay); } + function envExtend(env) { return Object.create(env); } + function envMerge(base, overlay) { + var child = Object.create(base); + if (overlay) for (var k in overlay) if (overlay.hasOwnProperty(k)) child[k] = overlay[k]; + return child; + } function dictSet(d, k, v) { d[k] = v; return v; } function dictGet(d, k) { var v = d[k]; return v !== undefined ? v : NIL; } @@ -515,6 +519,52 @@ return NIL; } + // ========================================================================= + // Performance overrides — evaluator hot path + // ========================================================================= + + // Override parseKeywordArgs: imperative loop instead of reduce+assoc + parseKeywordArgs = function(rawArgs, env) { + var kwargs = {}; + var children = []; + for (var i = 0; i < rawArgs.length; i++) { + var arg = rawArgs[i]; + if (arg && arg._kw && (i + 1) < rawArgs.length) { + kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); + i++; + } else { + children.push(trampoline(evalExpr(arg, env))); + } + } + return [kwargs, children]; + }; + + // Override callComponent: use prototype chain env, imperative kwarg binding + callComponent = function(comp, rawArgs, env) { + var kwargs = {}; + var children = []; + for (var i = 0; i < rawArgs.length; i++) { + var arg = rawArgs[i]; + if (arg && arg._kw && (i + 1) < rawArgs.length) { + kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); + i++; + } else { + children.push(trampoline(evalExpr(arg, env))); + } + } + var local = Object.create(componentClosure(comp)); + for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; + var params = componentParams(comp); + for (var j = 0; j < params.length; j++) { + var p = params[j]; + local[p] = p in kwargs ? kwargs[p] : NIL; + } + if (componentHasChildren(comp)) { + local["children"] = children; + } + return makeThunk(componentBody(comp), local); + }; + // ========================================================================= // Platform: deps module — component dependency analysis // ========================================================================= @@ -2849,6 +2899,84 @@ callExpr.push(dictGet(kwargs, k)); } } function domTagName(el) { return el && el.tagName ? el.tagName : ""; } + // ========================================================================= + // Performance overrides — replace transpiled spec with imperative JS + // ========================================================================= + + // Override renderDomComponent: imperative kwarg parsing, no reduce/assoc + renderDomComponent = function(comp, args, env, ns) { + // Parse keyword args imperatively + var kwargs = {}; + var children = []; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg && arg._kw && (i + 1) < args.length) { + kwargs[arg.name] = trampoline(evalExpr(args[i + 1], env)); + i++; // skip value + } else { + children.push(arg); + } + } + // Build local env via prototype chain + var local = Object.create(componentClosure(comp)); + // Copy caller env own properties + for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; + // Bind params + var params = componentParams(comp); + for (var j = 0; j < params.length; j++) { + var p = params[j]; + local[p] = p in kwargs ? kwargs[p] : NIL; + } + // Bind children + if (componentHasChildren(comp)) { + var childFrag = document.createDocumentFragment(); + for (var c = 0; c < children.length; c++) { + var rendered = renderToDom(children[c], env, ns); + if (rendered) childFrag.appendChild(rendered); + } + local["children"] = childFrag; + } + return renderToDom(componentBody(comp), local, ns); + }; + + // Override renderDomElement: imperative attr parsing, no reduce/assoc + renderDomElement = function(tag, args, env, ns) { + var newNs = tag === "svg" ? SVG_NS : tag === "math" ? MATH_NS : ns; + var el = domCreateElement(tag, newNs); + var extraClasses = []; + var isVoid = contains(VOID_ELEMENTS, tag); + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg && arg._kw && (i + 1) < args.length) { + var attrName = arg.name; + var attrVal = trampoline(evalExpr(args[i + 1], env)); + i++; // skip value + if (isNil(attrVal) || attrVal === false) continue; + if (attrName === "class" && attrVal && attrVal._styleValue) { + extraClasses.push(attrVal.className); + } else if (attrName === "style" && attrVal && attrVal._styleValue) { + extraClasses.push(attrVal.className); + } else if (contains(BOOLEAN_ATTRS, attrName)) { + if (isSxTruthy(attrVal)) el.setAttribute(attrName, ""); + } else if (attrVal === true) { + el.setAttribute(attrName, ""); + } else { + el.setAttribute(attrName, String(attrVal)); + } + } else { + if (!isVoid) { + var child = renderToDom(arg, env, newNs); + if (child) el.appendChild(child); + } + } + } + if (extraClasses.length) { + var existing = el.getAttribute("class") || ""; + el.setAttribute("class", (existing ? existing + " " : "") + extraClasses.join(" ")); + } + return el; + }; + // ========================================================================= // Platform interface — Engine pure logic (browser + node compatible) diff --git a/shared/sx/ref/bootstrap_js.py b/shared/sx/ref/bootstrap_js.py index 37c0425..a66a707 100644 --- a/shared/sx/ref/bootstrap_js.py +++ b/shared/sx/ref/bootstrap_js.py @@ -1710,8 +1710,12 @@ 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 envExtend(env) { return merge(env); } - function envMerge(base, overlay) { return merge(base, overlay); } + function envExtend(env) { return Object.create(env); } + function envMerge(base, overlay) { + var child = Object.create(base); + if (overlay) for (var k in overlay) if (overlay.hasOwnProperty(k)) child[k] = overlay[k]; + return child; + } function dictSet(d, k, v) { d[k] = v; return v; } function dictGet(d, k) { var v = d[k]; return v !== undefined ? v : NIL; } @@ -1852,7 +1856,53 @@ PLATFORM_JS_POST = ''' function forEachIndexed(fn, coll) { for (var i = 0; i < coll.length; i++) fn(i, coll[i]); return NIL; - }''' + } + + // ========================================================================= + // Performance overrides — evaluator hot path + // ========================================================================= + + // Override parseKeywordArgs: imperative loop instead of reduce+assoc + parseKeywordArgs = function(rawArgs, env) { + var kwargs = {}; + var children = []; + for (var i = 0; i < rawArgs.length; i++) { + var arg = rawArgs[i]; + if (arg && arg._kw && (i + 1) < rawArgs.length) { + kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); + i++; + } else { + children.push(trampoline(evalExpr(arg, env))); + } + } + return [kwargs, children]; + }; + + // Override callComponent: use prototype chain env, imperative kwarg binding + callComponent = function(comp, rawArgs, env) { + var kwargs = {}; + var children = []; + for (var i = 0; i < rawArgs.length; i++) { + var arg = rawArgs[i]; + if (arg && arg._kw && (i + 1) < rawArgs.length) { + kwargs[arg.name] = trampoline(evalExpr(rawArgs[i + 1], env)); + i++; + } else { + children.push(trampoline(evalExpr(arg, env))); + } + } + var local = Object.create(componentClosure(comp)); + for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; + var params = componentParams(comp); + for (var j = 0; j < params.length; j++) { + var p = params[j]; + local[p] = p in kwargs ? kwargs[p] : NIL; + } + if (componentHasChildren(comp)) { + local["children"] = children; + } + return makeThunk(componentBody(comp), local); + };''' PLATFORM_DEPS_JS = ''' // ========================================================================= @@ -2117,6 +2167,84 @@ PLATFORM_DOM_JS = """ } function domTagName(el) { return el && el.tagName ? el.tagName : ""; } + + // ========================================================================= + // Performance overrides — replace transpiled spec with imperative JS + // ========================================================================= + + // Override renderDomComponent: imperative kwarg parsing, no reduce/assoc + renderDomComponent = function(comp, args, env, ns) { + // Parse keyword args imperatively + var kwargs = {}; + var children = []; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg && arg._kw && (i + 1) < args.length) { + kwargs[arg.name] = trampoline(evalExpr(args[i + 1], env)); + i++; // skip value + } else { + children.push(arg); + } + } + // Build local env via prototype chain + var local = Object.create(componentClosure(comp)); + // Copy caller env own properties + for (var k in env) if (env.hasOwnProperty(k)) local[k] = env[k]; + // Bind params + var params = componentParams(comp); + for (var j = 0; j < params.length; j++) { + var p = params[j]; + local[p] = p in kwargs ? kwargs[p] : NIL; + } + // Bind children + if (componentHasChildren(comp)) { + var childFrag = document.createDocumentFragment(); + for (var c = 0; c < children.length; c++) { + var rendered = renderToDom(children[c], env, ns); + if (rendered) childFrag.appendChild(rendered); + } + local["children"] = childFrag; + } + return renderToDom(componentBody(comp), local, ns); + }; + + // Override renderDomElement: imperative attr parsing, no reduce/assoc + renderDomElement = function(tag, args, env, ns) { + var newNs = tag === "svg" ? SVG_NS : tag === "math" ? MATH_NS : ns; + var el = domCreateElement(tag, newNs); + var extraClasses = []; + var isVoid = contains(VOID_ELEMENTS, tag); + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg && arg._kw && (i + 1) < args.length) { + var attrName = arg.name; + var attrVal = trampoline(evalExpr(args[i + 1], env)); + i++; // skip value + if (isNil(attrVal) || attrVal === false) continue; + if (attrName === "class" && attrVal && attrVal._styleValue) { + extraClasses.push(attrVal.className); + } else if (attrName === "style" && attrVal && attrVal._styleValue) { + extraClasses.push(attrVal.className); + } else if (contains(BOOLEAN_ATTRS, attrName)) { + if (isSxTruthy(attrVal)) el.setAttribute(attrName, ""); + } else if (attrVal === true) { + el.setAttribute(attrName, ""); + } else { + el.setAttribute(attrName, String(attrVal)); + } + } else { + if (!isVoid) { + var child = renderToDom(arg, env, newNs); + if (child) el.appendChild(child); + } + } + } + if (extraClasses.length) { + var existing = el.getAttribute("class") || ""; + el.setAttribute("class", (existing ? existing + " " : "") + extraClasses.join(" ")); + } + return el; + }; """ PLATFORM_ENGINE_PURE_JS = """