Fix JIT closure isolation, SX wire format, server diagnostics
Root cause: _env_bind_hook mirrored ALL env_bind calls (including
lambda parameter bindings) to the shared VM globals table. Factory
functions like make-page-fn that return closures capturing different
values for the same param names (default-name, prefix, suffix) would
have the last call's values overwrite all previous closures' captured
state in globals. OP_GLOBAL_GET reads globals first, so all closures
returned the last factory call's values.
Fix: only sync root-env bindings (parent=None) to VM globals. Lambda
parameter bindings stay in their local env, found via vm_closure_env
fallback in OP_GLOBAL_GET.
Also in this commit:
- OP_CLOSURE propagates parent vm_closure_env to child closures
- Remove JIT globals injection (closure vars found via env chain)
- sx_server.ml: SX-Request header → returns text/sx (aser only)
- sx_server.ml: diagnostic endpoint GET /sx/_debug/{env,eval,route}
- sx_server.ml: page helper stubs for deep page rendering
- sx_server.ml: skip client-libs/ dir (browser-only definitions)
- adapter-html.sx: unknown components → HTML comment (not error)
- sx-platform.js: .sxbc fallback loader for bytecode modules
- Delete sx_http.ml (standalone HTTP server, unused)
- Delete stale .sxbc.json files (arity=0 bug, replaced by .sxbc)
- 7 new closure isolation tests in test-closure-isolation.sx
- mcp_tree.ml: emit arity + upvalue-count in .sxbc.json output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -228,38 +228,58 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Try loading a pre-compiled .sxbc.json bytecode module.
|
||||
* Try loading a pre-compiled bytecode module.
|
||||
* Tries .sxbc.json first, then .sxbc (SX s-expression format).
|
||||
* Returns true on success, null on failure (caller falls back to .sx source).
|
||||
*/
|
||||
function loadBytecodeFile(path) {
|
||||
var bcPath = path.replace(/\.sx$/, '.sxbc.json');
|
||||
var url = _baseUrl + bcPath + _cacheBust;
|
||||
// Try .sxbc.json (JSON dict format)
|
||||
var jsonPath = path.replace(/\.sx$/, '.sxbc.json');
|
||||
var jsonUrl = _baseUrl + jsonPath + _cacheBust;
|
||||
try {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url, false);
|
||||
xhr.open("GET", jsonUrl, false);
|
||||
xhr.send();
|
||||
if (xhr.status !== 200) return null;
|
||||
|
||||
var json = JSON.parse(xhr.responseText);
|
||||
if (!json.module || json.magic !== 'SXBC') return null;
|
||||
|
||||
var module = {
|
||||
_type: 'dict',
|
||||
arity: json.module.arity || 0,
|
||||
bytecode: { _type: 'list', items: json.module.bytecode },
|
||||
constants: { _type: 'list', items: json.module.constants.map(deserializeConstant) },
|
||||
};
|
||||
|
||||
var result = K.loadModule(module);
|
||||
if (typeof result === 'string' && result.indexOf('Error') === 0) {
|
||||
console.warn("[sx-platform] bytecode FAIL " + path + ":", result);
|
||||
return null;
|
||||
if (xhr.status === 200) {
|
||||
var json = JSON.parse(xhr.responseText);
|
||||
if (json.module && json.magic === 'SXBC') {
|
||||
var module = {
|
||||
_type: 'dict',
|
||||
arity: json.module.arity || 0,
|
||||
bytecode: { _type: 'list', items: json.module.bytecode },
|
||||
constants: { _type: 'list', items: json.module.constants.map(deserializeConstant) },
|
||||
};
|
||||
var result = K.loadModule(module);
|
||||
if (typeof result !== 'string' || result.indexOf('Error') !== 0) {
|
||||
console.log("[sx-platform] ok " + path + " (bytecode-json)");
|
||||
return true;
|
||||
}
|
||||
console.warn("[sx-platform] bytecode-json FAIL " + path + ":", result);
|
||||
}
|
||||
}
|
||||
console.log("[sx-platform] ok " + path + " (bytecode)");
|
||||
return true;
|
||||
} catch(e) {
|
||||
return null;
|
||||
}
|
||||
} catch(e) { /* fall through to .sxbc */ }
|
||||
|
||||
// Try .sxbc (SX s-expression format, loaded via load-sxbc primitive)
|
||||
var sxbcPath = path.replace(/\.sx$/, '.sxbc');
|
||||
var sxbcUrl = _baseUrl + sxbcPath + _cacheBust;
|
||||
try {
|
||||
var xhr2 = new XMLHttpRequest();
|
||||
xhr2.open("GET", sxbcUrl, false);
|
||||
xhr2.send();
|
||||
if (xhr2.status === 200) {
|
||||
// Store text in global, parse via SX to avoid JS string escaping
|
||||
window.__sxbcText = xhr2.responseText;
|
||||
var result2 = K.eval('(load-sxbc (first (parse (host-global "__sxbcText"))))');
|
||||
delete window.__sxbcText;
|
||||
if (typeof result2 !== 'string' || result2.indexOf('Error') !== 0) {
|
||||
console.log("[sx-platform] ok " + path + " (bytecode-sx)");
|
||||
return true;
|
||||
}
|
||||
console.warn("[sx-platform] bytecode-sx FAIL " + path + ":", result2);
|
||||
}
|
||||
} catch(e) { delete window.__sxbcText; /* fall through to source */ }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user