WIP: bytecode module pre-compilation and loading infrastructure
- compile-modules.js: Node.js build tool, all 23 .sx files compile to .sxbc.json
- api_load_module with shared globals (beginModuleLoad/endModuleLoad batch API)
- api_compile_module for runtime compilation
- sx-platform.js: bytecode-first loader with source fallback, JSON deserializer
- Deferred JIT enable (setTimeout after boot)
Known issues:
- WASM browser: loadModule loads but functions not accessible (env writeback
issue with interned keys)
- WASM browser: compileModule fails ("Not callable: nil" — compile-module
function from bytecode not working correctly in WASM context)
- Node.js js_of_ocaml: full roundtrip works (compile → load → call)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -197,6 +197,70 @@
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Deserialize type-tagged JSON constant back to JS value for loadModule.
|
||||
*/
|
||||
function deserializeConstant(c) {
|
||||
if (!c || !c.t) return null;
|
||||
switch (c.t) {
|
||||
case 's': return c.v;
|
||||
case 'n': return c.v;
|
||||
case 'b': return c.v;
|
||||
case 'nil': return null;
|
||||
case 'sym': return { _type: 'symbol', name: c.v };
|
||||
case 'kw': return { _type: 'keyword', name: c.v };
|
||||
case 'list': return { _type: 'list', items: (c.v || []).map(deserializeConstant) };
|
||||
case 'code': return {
|
||||
_type: 'dict',
|
||||
bytecode: { _type: 'list', items: c.v.bytecode },
|
||||
constants: { _type: 'list', items: (c.v.constants || []).map(deserializeConstant) },
|
||||
arity: c.v.arity || 0,
|
||||
'upvalue-count': c.v['upvalue-count'] || 0,
|
||||
locals: c.v.locals || 0,
|
||||
};
|
||||
case 'dict': {
|
||||
var d = { _type: 'dict' };
|
||||
for (var k in c.v) d[k] = deserializeConstant(c.v[k]);
|
||||
return d;
|
||||
}
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try loading a pre-compiled .sxbc.json bytecode module.
|
||||
* 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 {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", url, 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',
|
||||
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;
|
||||
}
|
||||
console.log("[sx-platform] ok " + path + " (bytecode)");
|
||||
return true;
|
||||
} catch(e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an .sx file synchronously via XHR (boot-time only).
|
||||
* Returns the number of expressions loaded, or an error string.
|
||||
@@ -227,7 +291,7 @@
|
||||
|
||||
/**
|
||||
* Load all web adapter .sx files in dependency order.
|
||||
* Called after the 8 FFI primitives are registered.
|
||||
* Tries pre-compiled bytecode first, falls back to source.
|
||||
*/
|
||||
function loadWebStack() {
|
||||
var files = [
|
||||
@@ -264,12 +328,19 @@
|
||||
"sx/boot.sx",
|
||||
];
|
||||
|
||||
var loaded = 0;
|
||||
var loaded = 0, bcCount = 0, srcCount = 0;
|
||||
if (K.beginModuleLoad) K.beginModuleLoad();
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var r = loadSxFile(files[i]);
|
||||
if (typeof r === "number") loaded += r;
|
||||
var r = loadBytecodeFile(files[i]);
|
||||
if (r) { bcCount++; continue; }
|
||||
// Bytecode not available — end batch, load source, restart batch
|
||||
if (K.endModuleLoad) K.endModuleLoad();
|
||||
r = loadSxFile(files[i]);
|
||||
if (typeof r === "number") { loaded += r; srcCount++; }
|
||||
if (K.beginModuleLoad) K.beginModuleLoad();
|
||||
}
|
||||
console.log("[sx-platform] Loaded " + loaded + " expressions from " + files.length + " files");
|
||||
if (K.endModuleLoad) K.endModuleLoad();
|
||||
console.log("[sx-platform] Loaded " + files.length + " files (" + bcCount + " bytecode, " + srcCount + " source, " + loaded + " exprs)");
|
||||
return loaded;
|
||||
}
|
||||
|
||||
@@ -339,7 +410,8 @@
|
||||
var _doInit = function() {
|
||||
loadWebStack();
|
||||
Sx.init();
|
||||
K.eval('(enable-jit!)');
|
||||
// Enable JIT after all boot code has run
|
||||
setTimeout(function() { K.eval('(enable-jit!)'); }, 0);
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
|
||||
Reference in New Issue
Block a user