Fix HS browser activation: host-get function sentinel, runtime symbol shadow, lazy dep chain
Three bugs fixed: 1. host-get in sx-platform.js: return true for function-valued properties so dom-get-attr/dom-set-attr guards pass (functions can't cross WASM boundary) 2. hs-runtime.sx: renamed host-get→hs-host-get and dom-query→hs-dom-query to stop shadowing platform natives when loaded as .sx source 3. compile-modules.js: HS dependency chain (integration→runtime→compiler→parser→tokenizer) so lazy loading pulls in all deps. Non-library modules load as .sx source for CEK env visibility. Result: 8/8 elements activate, hs-on attaches listeners. Click handler needs IO suspension support (VmSuspended in sx_browser.ml) to fire — next step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -382,9 +382,16 @@ for (const file of FILES) {
|
||||
const defines = extractDefines(src);
|
||||
if (defines.length > 0) {
|
||||
const key = file.replace(/\.sx$/, '');
|
||||
// HS modules form a dependency chain — loading one loads all predecessors.
|
||||
const HS_DEPS = {
|
||||
'hs-parser': ['hs-tokenizer'],
|
||||
'hs-compiler': ['hs-tokenizer', 'hs-parser'],
|
||||
'hs-runtime': ['hs-tokenizer', 'hs-parser', 'hs-compiler'],
|
||||
'hs-integration': ['hs-tokenizer', 'hs-parser', 'hs-compiler', 'hs-runtime'],
|
||||
};
|
||||
manifest[key] = {
|
||||
file: sxbcFile,
|
||||
deps: [],
|
||||
deps: HS_DEPS[key] || [],
|
||||
exports: defines,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,7 +40,12 @@
|
||||
var obj = args[0], prop = args[1];
|
||||
if (obj == null) return null;
|
||||
var v = obj[prop];
|
||||
return v === undefined ? null : v;
|
||||
if (v === undefined) return null;
|
||||
// Functions can't cross the WASM boundary — return true as a truthy
|
||||
// sentinel so (host-get el "getAttribute") works as a guard.
|
||||
// Use host-call to actually invoke the method.
|
||||
if (typeof v === "function") return true;
|
||||
return v;
|
||||
});
|
||||
|
||||
K.registerNative("host-set!", function(args) {
|
||||
@@ -474,9 +479,19 @@
|
||||
// will see it as already loaded and skip rather than infinite-looping.
|
||||
_loadedLibs[name] = true;
|
||||
|
||||
// Load this module
|
||||
var ok = loadBytecodeFile("sx/" + info.file);
|
||||
if (!ok) {
|
||||
// Load this module.
|
||||
// Non-library modules (no space in name, e.g. "hs-runtime") use source
|
||||
// loading so their defines go into the CEK global env — needed when
|
||||
// eval-expr-cek evaluates compiled hyperscript at runtime.
|
||||
var isLibrary = name.indexOf(' ') >= 0 || name === '_entry';
|
||||
var ok;
|
||||
if (isLibrary) {
|
||||
ok = loadBytecodeFile("sx/" + info.file);
|
||||
if (!ok) {
|
||||
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
||||
ok = loadSxFile("sx/" + sxFile);
|
||||
}
|
||||
} else {
|
||||
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
||||
ok = loadSxFile("sx/" + sxFile);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@
|
||||
;; Toggle a single class on an element.
|
||||
(define
|
||||
hs-toggle-class!
|
||||
(fn (target cls) (host-call (host-get target "classList") "toggle" cls)))
|
||||
(fn
|
||||
(target cls)
|
||||
(host-call (hs-host-get target "classList") "toggle" cls)))
|
||||
|
||||
;; Toggle between two classes — exactly one is active at a time.
|
||||
(define
|
||||
@@ -136,7 +138,7 @@
|
||||
(find-prev sibling)))))
|
||||
|
||||
;; First element matching selector within a scope.
|
||||
(define hs-query-first (fn (sel) (dom-query sel)))
|
||||
(define hs-query-first (fn (sel) (hs-dom-query sel)))
|
||||
|
||||
;; Last element matching selector.
|
||||
(define
|
||||
@@ -466,9 +468,11 @@
|
||||
d))))
|
||||
;; ── Sandbox/test runtime additions ──────────────────────────────
|
||||
;; Property access — dot notation and .length
|
||||
(define host-get (fn (obj key) (if (= key "length") (len obj) (get obj key))))
|
||||
(define
|
||||
hs-host-get
|
||||
(fn (obj key) (if (= key "length") (len obj) (get obj key))))
|
||||
;; DOM query stub — sandbox returns empty list
|
||||
(define dom-query (fn (selector) (list)))
|
||||
(define hs-dom-query (fn (selector) (list)))
|
||||
;; Method dispatch — obj.method(args)
|
||||
(define hs-method-call (fn (obj method &rest args)
|
||||
(cond
|
||||
@@ -488,7 +492,7 @@
|
||||
;; beep! — debug logging, returns value unchanged
|
||||
(define hs-beep (fn (v) v))
|
||||
;; Property-based is — check obj.key truthiness
|
||||
(define hs-prop-is (fn (obj key) (not (hs-falsy? (host-get obj key)))))
|
||||
(define hs-prop-is (fn (obj key) (not (hs-falsy? (hs-host-get obj key)))))
|
||||
;; Array slicing (inclusive both ends)
|
||||
(define hs-slice (fn (col start end)
|
||||
(let ((s (if (nil? start) 0 start))
|
||||
|
||||
@@ -40,7 +40,12 @@
|
||||
var obj = args[0], prop = args[1];
|
||||
if (obj == null) return null;
|
||||
var v = obj[prop];
|
||||
return v === undefined ? null : v;
|
||||
if (v === undefined) return null;
|
||||
// Functions can't cross the WASM boundary — return true as a truthy
|
||||
// sentinel so (host-get el "getAttribute") works as a guard.
|
||||
// Use host-call to actually invoke the method.
|
||||
if (typeof v === "function") return true;
|
||||
return v;
|
||||
});
|
||||
|
||||
K.registerNative("host-set!", function(args) {
|
||||
@@ -474,9 +479,19 @@
|
||||
// will see it as already loaded and skip rather than infinite-looping.
|
||||
_loadedLibs[name] = true;
|
||||
|
||||
// Load this module
|
||||
var ok = loadBytecodeFile("sx/" + info.file);
|
||||
if (!ok) {
|
||||
// Load this module.
|
||||
// Non-library modules (no space in name, e.g. "hs-runtime") use source
|
||||
// loading so their defines go into the CEK global env — needed when
|
||||
// eval-expr-cek evaluates compiled hyperscript at runtime.
|
||||
var isLibrary = name.indexOf(' ') >= 0 || name === '_entry';
|
||||
var ok;
|
||||
if (isLibrary) {
|
||||
ok = loadBytecodeFile("sx/" + info.file);
|
||||
if (!ok) {
|
||||
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
||||
ok = loadSxFile("sx/" + sxFile);
|
||||
}
|
||||
} else {
|
||||
var sxFile = info.file.replace(/\.sxbc$/, '.sx');
|
||||
ok = loadSxFile("sx/" + sxFile);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@
|
||||
;; Toggle a single class on an element.
|
||||
(define
|
||||
hs-toggle-class!
|
||||
(fn (target cls) (host-call (host-get target "classList") "toggle" cls)))
|
||||
(fn
|
||||
(target cls)
|
||||
(host-call (hs-host-get target "classList") "toggle" cls)))
|
||||
|
||||
;; Toggle between two classes — exactly one is active at a time.
|
||||
(define
|
||||
@@ -136,7 +138,7 @@
|
||||
(find-prev sibling)))))
|
||||
|
||||
;; First element matching selector within a scope.
|
||||
(define hs-query-first (fn (sel) (dom-query sel)))
|
||||
(define hs-query-first (fn (sel) (hs-dom-query sel)))
|
||||
|
||||
;; Last element matching selector.
|
||||
(define
|
||||
@@ -466,9 +468,11 @@
|
||||
d))))
|
||||
;; ── Sandbox/test runtime additions ──────────────────────────────
|
||||
;; Property access — dot notation and .length
|
||||
(define host-get (fn (obj key) (if (= key "length") (len obj) (get obj key))))
|
||||
(define
|
||||
hs-host-get
|
||||
(fn (obj key) (if (= key "length") (len obj) (get obj key))))
|
||||
;; DOM query stub — sandbox returns empty list
|
||||
(define dom-query (fn (selector) (list)))
|
||||
(define hs-dom-query (fn (selector) (list)))
|
||||
;; Method dispatch — obj.method(args)
|
||||
(define hs-method-call (fn (obj method &rest args)
|
||||
(cond
|
||||
@@ -488,7 +492,7 @@
|
||||
;; beep! — debug logging, returns value unchanged
|
||||
(define hs-beep (fn (v) v))
|
||||
;; Property-based is — check obj.key truthiness
|
||||
(define hs-prop-is (fn (obj key) (not (hs-falsy? (host-get obj key)))))
|
||||
(define hs-prop-is (fn (obj key) (not (hs-falsy? (hs-host-get obj key)))))
|
||||
;; Array slicing (inclusive both ends)
|
||||
(define hs-slice (fn (col start end)
|
||||
(let ((s (if (nil? start) 0 start))
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -927,7 +927,9 @@
|
||||
},
|
||||
"hs-parser": {
|
||||
"file": "hs-parser.sxbc",
|
||||
"deps": [],
|
||||
"deps": [
|
||||
"hs-tokenizer"
|
||||
],
|
||||
"exports": [
|
||||
"hs-parse",
|
||||
"hs-compile"
|
||||
@@ -935,7 +937,10 @@
|
||||
},
|
||||
"hs-compiler": {
|
||||
"file": "hs-compiler.sxbc",
|
||||
"deps": [],
|
||||
"deps": [
|
||||
"hs-tokenizer",
|
||||
"hs-parser"
|
||||
],
|
||||
"exports": [
|
||||
"hs-to-sx",
|
||||
"hs-to-sx-from-source"
|
||||
@@ -943,7 +948,11 @@
|
||||
},
|
||||
"hs-runtime": {
|
||||
"file": "hs-runtime.sxbc",
|
||||
"deps": [],
|
||||
"deps": [
|
||||
"hs-tokenizer",
|
||||
"hs-parser",
|
||||
"hs-compiler"
|
||||
],
|
||||
"exports": [
|
||||
"hs-on",
|
||||
"hs-on-every",
|
||||
@@ -982,8 +991,8 @@
|
||||
"hs-last",
|
||||
"hs-template",
|
||||
"hs-make-object",
|
||||
"host-get",
|
||||
"dom-query",
|
||||
"hs-host-get",
|
||||
"hs-dom-query",
|
||||
"hs-method-call",
|
||||
"hs-beep",
|
||||
"hs-prop-is",
|
||||
@@ -998,7 +1007,12 @@
|
||||
},
|
||||
"hs-integration": {
|
||||
"file": "hs-integration.sxbc",
|
||||
"deps": [],
|
||||
"deps": [
|
||||
"hs-tokenizer",
|
||||
"hs-parser",
|
||||
"hs-compiler",
|
||||
"hs-runtime"
|
||||
],
|
||||
"exports": [
|
||||
"hs-handler",
|
||||
"hs-activate!",
|
||||
|
||||
Reference in New Issue
Block a user