Transparent lazy module loading — code loads like data

When the VM or CEK hits an undefined symbol, it checks a symbol→library
index (built from manifest exports at boot), loads the library that
exports it, and returns the value. Execution continues as if the module
was always loaded. No import statements, no load-library! calls, no
Suspense boundaries — just call the function.

This is the same mechanism as IO suspension for data fetching. The
programmer doesn't distinguish between calling a local function and
calling one that needs its module fetched first. The runtime treats
code as just another resource.

Implementation:
- _symbol_resolve_hook in sx_types.ml — called by env_get_id (CEK path)
  and vm_global_get (VM path) when a symbol isn't found
- Symbol→library index built from manifest exports in sx-platform.js
- __resolve-symbol native calls __sxLoadLibrary, module loads, symbol
  appears in globals, execution resumes
- compile-modules.js extracts export lists into module-manifest.json
- Playground page demonstrates: (freeze-scope) triggers freeze.sxbc
  download transparently on first use

2650/2650 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 22:14:19 +00:00
parent f4f8715d06
commit 2f3e727a6f
11 changed files with 961 additions and 67 deletions

View File

@@ -550,6 +550,47 @@
return ok;
};
// ================================================================
// Transparent lazy loading — symbol → library index
//
// When the VM hits an undefined symbol, the resolve hook checks this
// index, loads the library that exports it, and returns the value.
// The programmer just calls the function — loading is invisible.
// ================================================================
var _symbolIndex = null; // symbol name → library key
function buildSymbolIndex() {
if (_symbolIndex) return _symbolIndex;
if (!_manifest) loadManifest();
if (!_manifest) return null;
_symbolIndex = {};
for (var key in _manifest) {
if (key.startsWith('_')) continue;
var entry = _manifest[key];
if (entry.exports) {
for (var i = 0; i < entry.exports.length; i++) {
_symbolIndex[entry.exports[i]] = key;
}
}
}
return _symbolIndex;
}
// Register the resolve hook — called by the VM when GLOBAL_GET fails
K.registerNative("__resolve-symbol", function(args) {
var name = args[0];
if (!name) return null;
var idx = buildSymbolIndex();
if (!idx || !idx[name]) return null;
var lib = idx[name];
if (_loadedLibs[lib]) return null; // already loaded but symbol still missing — real error
// Load the library
__sxLoadLibrary(lib);
// Return null — the VM will re-lookup in globals after the hook loads the module
return null;
});
// ================================================================
// Compatibility shim — expose Sx global matching current JS API
// ================================================================

View File

@@ -1,65 +1,529 @@
{
"sx render": {
"file": "render.sxbc",
"deps": []
"deps": [],
"exports": [
"HTML_TAGS",
"VOID_ELEMENTS",
"BOOLEAN_ATTRS",
"*definition-form-extensions*",
"definition-form?",
"parse-element-args",
"render-attrs",
"eval-cond",
"eval-cond-scheme",
"eval-cond-clojure",
"process-bindings",
"is-render-expr?",
"merge-spread-attrs",
"escape-html",
"escape-attr"
]
},
"sx signals": {
"file": "core-signals.sxbc",
"deps": []
"deps": [],
"exports": [
"make-signal",
"signal?",
"signal-value",
"signal-set-value!",
"signal-subscribers",
"signal-add-sub!",
"signal-remove-sub!",
"signal-deps",
"signal-set-deps!",
"signal",
"deref",
"reset!",
"swap!",
"computed",
"effect",
"*batch-depth*",
"*batch-queue*",
"batch",
"notify-subscribers",
"flush-subscribers",
"dispose-computed",
"with-island-scope",
"register-in-scope"
]
},
"sx signals-web": {
"file": "signals.sxbc",
"deps": [
"sx dom",
"sx browser"
],
"exports": [
"with-marsh-scope",
"dispose-marsh-scope",
"emit-event",
"on-event",
"bridge-event",
"resource"
]
},
"web deps": {
"file": "deps.sxbc",
"deps": []
"deps": [],
"exports": [
"scan-refs",
"scan-refs-walk",
"transitive-deps-walk",
"transitive-deps",
"compute-all-deps",
"scan-components-from-source",
"components-needed",
"page-component-bundle",
"page-css-classes",
"scan-io-refs-walk",
"scan-io-refs",
"transitive-io-refs-walk",
"transitive-io-refs",
"compute-all-io-refs",
"component-io-refs-cached",
"component-pure?",
"render-target",
"page-render-plan",
"env-components"
]
},
"web router": {
"file": "router.sxbc",
"deps": []
"deps": [],
"exports": [
"split-path-segments",
"make-route-segment",
"parse-route-pattern",
"match-route-segments",
"match-route",
"find-matching-route",
"_fn-to-segment",
"sx-url-to-path",
"_count-leading-dots",
"_strip-trailing-close",
"_index-of-safe",
"_last-index-of",
"_pop-sx-url-level",
"_pop-sx-url-levels",
"_split-pos-kw",
"_parse-relative-body",
"_extract-innermost",
"_find-kw-in-tokens",
"_find-keyword-value",
"_replace-kw-in-tokens",
"_set-keyword-in-content",
"_is-delta-value?",
"_apply-delta",
"_apply-kw-pairs",
"_apply-keywords-to-url",
"_normalize-relative",
"resolve-relative-url",
"relative-sx-url?",
"_url-special-forms",
"url-special-form?",
"parse-sx-url",
"url-special-form-name",
"url-special-form-inner",
"url-to-expr",
"auto-quote-unknowns",
"prepare-url-expr"
]
},
"web page-helpers": {
"file": "page-helpers.sxbc",
"deps": []
"deps": [],
"exports": [
"special-form-category-map",
"extract-define-kwargs",
"categorize-special-forms",
"build-ref-items-with-href",
"build-reference-data",
"build-attr-detail",
"build-header-detail",
"build-event-detail",
"build-component-source",
"build-bundle-analysis",
"build-routing-analysis",
"build-affinity-analysis"
]
},
"sx freeze": {
"file": "freeze.sxbc",
"deps": []
"deps": [],
"exports": [
"freeze-registry",
"freeze-signal",
"freeze-scope",
"cek-freeze-scope",
"cek-freeze-all",
"cek-thaw-scope",
"cek-thaw-all",
"freeze-to-sx",
"thaw-from-sx"
]
},
"sx bytecode": {
"file": "bytecode.sxbc",
"deps": []
"deps": [],
"exports": [
"OP_CONST",
"OP_NIL",
"OP_TRUE",
"OP_FALSE",
"OP_POP",
"OP_DUP",
"OP_LOCAL_GET",
"OP_LOCAL_SET",
"OP_UPVALUE_GET",
"OP_UPVALUE_SET",
"OP_GLOBAL_GET",
"OP_GLOBAL_SET",
"OP_JUMP",
"OP_JUMP_IF_FALSE",
"OP_JUMP_IF_TRUE",
"OP_CALL",
"OP_TAIL_CALL",
"OP_RETURN",
"OP_CLOSURE",
"OP_CALL_PRIM",
"OP_APPLY",
"OP_LIST",
"OP_DICT",
"OP_APPEND_BANG",
"OP_ITER_INIT",
"OP_ITER_NEXT",
"OP_MAP_OPEN",
"OP_MAP_APPEND",
"OP_MAP_CLOSE",
"OP_FILTER_TEST",
"OP_HO_MAP",
"OP_HO_FILTER",
"OP_HO_REDUCE",
"OP_HO_FOR_EACH",
"OP_HO_SOME",
"OP_HO_EVERY",
"OP_SCOPE_PUSH",
"OP_SCOPE_POP",
"OP_PROVIDE_PUSH",
"OP_PROVIDE_POP",
"OP_CONTEXT",
"OP_EMIT",
"OP_EMITTED",
"OP_RESET",
"OP_SHIFT",
"OP_DEFINE",
"OP_DEFCOMP",
"OP_DEFISLAND",
"OP_DEFMACRO",
"OP_EXPAND_MACRO",
"OP_STR_CONCAT",
"OP_STR_JOIN",
"OP_SERIALIZE",
"OP_ADD",
"OP_SUB",
"OP_MUL",
"OP_DIV",
"OP_EQ",
"OP_LT",
"OP_GT",
"OP_NOT",
"OP_LEN",
"OP_FIRST",
"OP_REST",
"OP_NTH",
"OP_CONS",
"OP_NEG",
"OP_INC",
"OP_DEC",
"OP_ASER_TAG",
"OP_ASER_FRAG",
"BYTECODE_MAGIC",
"BYTECODE_VERSION",
"CONST_NUMBER",
"CONST_STRING",
"CONST_BOOL",
"CONST_NIL",
"CONST_SYMBOL",
"CONST_KEYWORD",
"CONST_LIST",
"CONST_DICT",
"CONST_CODE",
"opcode-name"
]
},
"sx compiler": {
"file": "compiler.sxbc",
"deps": []
"deps": [],
"exports": [
"make-pool",
"pool-add",
"make-scope",
"scope-define-local",
"scope-resolve",
"make-emitter",
"emit-byte",
"emit-u16",
"emit-i16",
"emit-op",
"emit-const",
"current-offset",
"patch-i16",
"compile-expr",
"compile-symbol",
"compile-dict",
"compile-list",
"compile-if",
"compile-when",
"compile-and",
"compile-or",
"compile-begin",
"compile-let",
"desugar-let-match",
"compile-letrec",
"compile-lambda",
"compile-define",
"compile-set",
"compile-quote",
"compile-cond",
"compile-case",
"compile-case-clauses",
"compile-match",
"compile-thread",
"compile-thread-step",
"compile-defcomp",
"compile-defmacro",
"compile-quasiquote",
"compile-qq-expr",
"compile-qq-list",
"compile-call",
"compile",
"compile-module"
]
},
"sx vm": {
"file": "vm.sxbc",
"deps": []
"deps": [],
"exports": [
"make-upvalue-cell",
"uv-get",
"uv-set!",
"make-vm-code",
"make-vm-closure",
"make-vm-frame",
"make-vm",
"vm-push",
"vm-pop",
"vm-peek",
"frame-read-u8",
"frame-read-u16",
"frame-read-i16",
"vm-push-frame",
"code-from-value",
"vm-closure?",
"*active-vm*",
"*jit-compile-fn*",
"lambda?",
"lambda-compiled",
"lambda-set-compiled!",
"lambda-name",
"cek-call-or-suspend",
"try-jit-call",
"vm-call",
"frame-local-get",
"frame-local-set",
"frame-upvalue-get",
"frame-upvalue-set",
"frame-ip",
"frame-set-ip!",
"frame-base",
"frame-closure",
"closure-code",
"closure-upvalues",
"closure-env",
"code-bytecode",
"code-constants",
"code-locals",
"vm-sp",
"vm-set-sp!",
"vm-stack",
"vm-set-stack!",
"vm-frames",
"vm-set-frames!",
"vm-globals-ref",
"collect-n-from-stack",
"collect-n-pairs",
"pad-n-nils",
"vm-global-get",
"vm-resolve-ho-form",
"vm-call-external",
"vm-global-set",
"env-walk",
"env-walk-set!",
"vm-create-closure",
"vm-run",
"vm-step",
"vm-call-closure",
"vm-execute-module",
"vm-resume-module"
]
},
"sx dom": {
"file": "dom.sxbc",
"deps": []
"deps": [],
"exports": [
"dom-document",
"dom-window",
"dom-body",
"dom-head",
"dom-create-element",
"create-text-node",
"create-fragment",
"create-comment",
"dom-append",
"dom-prepend",
"dom-insert-before",
"dom-insert-after",
"dom-remove",
"dom-is-active-element?",
"dom-is-input-element?",
"dom-is-child-of?",
"dom-attr-list",
"dom-remove-child",
"dom-replace-child",
"dom-clone",
"dom-query",
"dom-query-all",
"dom-query-by-id",
"dom-closest",
"dom-matches?",
"dom-get-attr",
"dom-set-attr",
"dom-remove-attr",
"dom-has-attr?",
"dom-add-class",
"dom-remove-class",
"dom-has-class?",
"dom-text-content",
"dom-set-text-content",
"dom-inner-html",
"dom-set-inner-html",
"dom-outer-html",
"dom-insert-adjacent-html",
"dom-get-style",
"dom-set-style",
"dom-get-prop",
"dom-set-prop",
"dom-tag-name",
"dom-node-type",
"dom-node-name",
"dom-id",
"dom-parent",
"dom-first-child",
"dom-next-sibling",
"dom-child-list",
"dom-is-fragment?",
"dom-child-nodes",
"dom-remove-children-after",
"dom-focus",
"dom-parse-html",
"dom-listen",
"dom-add-listener",
"dom-dispatch",
"event-detail",
"prevent-default",
"stop-propagation",
"event-modifier-key?",
"element-value",
"error-message",
"dom-get-data",
"dom-set-data",
"dom-append-to-head",
"set-document-title"
]
},
"sx browser": {
"file": "browser.sxbc",
"deps": []
"deps": [],
"exports": [
"browser-location-href",
"browser-location-pathname",
"browser-location-origin",
"browser-same-origin?",
"url-pathname",
"browser-push-state",
"browser-replace-state",
"browser-reload",
"browser-navigate",
"local-storage-get",
"local-storage-set",
"local-storage-remove",
"set-timeout",
"set-interval",
"clear-timeout",
"clear-interval",
"request-animation-frame",
"fetch-request",
"new-abort-controller",
"controller-signal",
"controller-abort",
"promise-then",
"promise-resolve",
"promise-delayed",
"browser-confirm",
"browser-prompt",
"browser-media-matches?",
"json-parse",
"log-info",
"log-warn",
"console-log",
"now-ms",
"schedule-idle",
"set-cookie",
"get-cookie"
]
},
"web adapter-html": {
"file": "adapter-html.sxbc",
"deps": [
"sx render"
],
"exports": [
"render-to-html",
"render-value-to-html",
"RENDER_HTML_FORMS",
"render-html-form?",
"render-list-to-html",
"dispatch-html-form",
"render-lambda-html",
"render-html-component",
"render-html-element",
"render-html-lake",
"render-html-marsh",
"render-html-island",
"serialize-island-state"
]
},
"web adapter-sx": {
"file": "adapter-sx.sxbc",
"deps": [
"web boot-helpers"
],
"exports": [
"render-to-sx",
"aser",
"aser-list",
"aser-reserialize",
"aser-fragment",
"aser-call",
"aser-expand-component",
"SPECIAL_FORM_NAMES",
"HO_FORM_NAMES",
"special-form?",
"ho-form?",
"aser-special",
"eval-case-aser"
]
},
"web adapter-dom": {
@@ -67,6 +531,41 @@
"deps": [
"sx dom",
"sx render"
],
"exports": [
"SVG_NS",
"MATH_NS",
"island-scope?",
"contains-deref?",
"dom-on",
"render-to-dom",
"render-dom-list",
"render-dom-element",
"render-dom-component",
"render-dom-fragment",
"render-dom-raw",
"render-dom-unknown-component",
"RENDER_DOM_FORMS",
"render-dom-form?",
"dispatch-render-form",
"render-lambda-dom",
"render-dom-island",
"render-dom-lake",
"render-dom-marsh",
"reactive-text",
"reactive-attr",
"reactive-spread",
"reactive-fragment",
"render-list-item",
"extract-key",
"reactive-list",
"bind-input",
"*use-cek-reactive*",
"enable-cek-reactive!",
"cek-reactive-text",
"cek-reactive-attr",
"render-dom-portal",
"render-dom-error-boundary"
]
},
"web boot-helpers": {
@@ -75,23 +574,178 @@
"sx dom",
"sx browser",
"web adapter-dom"
],
"exports": [
"_sx-bound-prefix",
"mark-processed!",
"is-processed?",
"clear-processed!",
"callable?",
"to-kebab",
"sx-load-components",
"call-expr",
"base-env",
"get-render-env",
"merge-envs",
"sx-render-with-env",
"parse-env-attr",
"store-env-attr",
"resolve-mount-target",
"remove-head-element",
"set-sx-comp-cookie",
"clear-sx-comp-cookie",
"log-parse-error",
"loaded-component-names",
"csrf-token",
"validate-for-request",
"build-request-body",
"abort-previous-target",
"abort-previous",
"track-controller",
"track-controller-target",
"new-abort-controller",
"abort-signal",
"apply-optimistic",
"revert-optimistic",
"dom-has-attr?",
"show-indicator",
"disable-elements",
"clear-loading-state",
"abort-error?",
"promise-catch",
"fetch-request",
"fetch-location",
"fetch-and-restore",
"fetch-preload",
"fetch-streaming",
"dom-parse-html-document",
"dom-body-inner-html",
"create-script-clone",
"cross-origin?",
"browser-scroll-to",
"with-transition",
"event-source-connect",
"event-source-listen",
"bind-boost-link",
"bind-boost-form",
"bind-client-route-click",
"sw-post-message",
"try-parse-json",
"strip-component-scripts",
"extract-response-css",
"sx-render",
"sx-hydrate",
"sx-process-scripts",
"select-from-container",
"children-to-fragment",
"select-html-from-doc",
"register-io-deps",
"resolve-page-data",
"parse-sx-data",
"try-eval-content",
"try-async-eval-content",
"try-rerender-page",
"execute-action",
"bind-preload",
"persist-offline-data",
"retrieve-offline-data"
]
},
"sx hypersx": {
"file": "hypersx.sxbc",
"deps": []
"deps": [],
"exports": [
"hsx-indent",
"hsx-sym-name",
"hsx-kw-name",
"hsx-is-element?",
"hsx-is-component?",
"hsx-extract-css",
"hsx-tag-str",
"hsx-atom",
"hsx-inline",
"hsx-attrs-str",
"hsx-children",
"sx->hypersx-node",
"sx->hypersx"
]
},
"sx harness": {
"file": "harness.sxbc",
"deps": []
"deps": [],
"exports": [
"assert",
"assert=",
"default-platform",
"make-harness",
"harness-reset!",
"harness-log",
"harness-get",
"harness-set!",
"make-interceptor",
"install-interceptors",
"io-calls",
"io-call-count",
"io-call-nth",
"io-call-args",
"io-call-result",
"assert-io-called",
"assert-no-io",
"assert-io-count",
"assert-io-args",
"assert-io-result",
"assert-state"
]
},
"sx harness-reactive": {
"file": "harness-reactive.sxbc",
"deps": []
"deps": [],
"exports": [
"assert-signal-value",
"assert-signal-has-subscribers",
"assert-signal-no-subscribers",
"assert-signal-subscriber-count",
"simulate-signal-set!",
"simulate-signal-swap!",
"assert-computed-dep-count",
"assert-computed-depends-on",
"count-effect-runs",
"make-test-signal",
"assert-batch-coalesces"
]
},
"sx harness-web": {
"file": "harness-web.sxbc",
"deps": []
"deps": [],
"exports": [
"mock-element",
"mock-set-text!",
"mock-append-child!",
"mock-set-attr!",
"mock-get-attr",
"mock-add-listener!",
"simulate-click",
"simulate-input",
"simulate-event",
"assert-text",
"assert-attr",
"assert-class",
"assert-no-class",
"assert-child-count",
"assert-event-fired",
"assert-no-event",
"event-fire-count",
"make-web-harness",
"is-renderable?",
"is-render-leak?",
"assert-renderable",
"render-body-audit",
"assert-render-body-clean",
"mock-render",
"mock-render-fragment",
"assert-single-render-root",
"assert-tag"
]
},
"web engine": {
"file": "engine.sxbc",
@@ -99,6 +753,41 @@
"web boot-helpers",
"sx dom",
"sx browser"
],
"exports": [
"ENGINE_VERBS",
"DEFAULT_SWAP",
"parse-time",
"parse-trigger-spec",
"default-trigger",
"get-verb-info",
"build-request-headers",
"process-response-headers",
"parse-swap-spec",
"parse-retry-spec",
"next-retry-ms",
"filter-params",
"resolve-target",
"apply-optimistic",
"revert-optimistic",
"find-oob-swaps",
"morph-node",
"sync-attrs",
"morph-children",
"morph-island-children",
"morph-marsh",
"process-signal-updates",
"swap-dom-nodes",
"insert-remaining-siblings",
"swap-html-string",
"handle-history",
"PRELOAD_TTL",
"preload-cache-get",
"preload-cache-set",
"classify-trigger",
"should-boost-link?",
"should-boost-form?",
"parse-sse-swap"
]
},
"web orchestration": {
@@ -109,6 +798,67 @@
"sx browser",
"web adapter-dom",
"web engine"
],
"exports": [
"_preload-cache",
"dispatch-trigger-events",
"execute-request",
"do-fetch",
"handle-fetch-success",
"flush-collected-styles",
"handle-sx-response",
"handle-html-response",
"handle-retry",
"bind-triggers",
"bind-event",
"post-swap",
"process-settle-hooks",
"activate-scripts",
"process-oob-swaps",
"hoist-head-elements",
"process-boosted",
"boost-descendants",
"_page-data-cache",
"_page-data-cache-ttl",
"page-data-cache-key",
"page-data-cache-get",
"page-data-cache-set",
"invalidate-page-cache",
"invalidate-all-page-cache",
"update-page-cache",
"process-cache-directives",
"_optimistic-snapshots",
"optimistic-cache-update",
"optimistic-cache-revert",
"optimistic-cache-confirm",
"submit-mutation",
"_is-online",
"_offline-queue",
"offline-is-online?",
"offline-set-online!",
"offline-queue-mutation",
"offline-sync",
"offline-pending-count",
"offline-aware-mutation",
"current-page-layout",
"swap-rendered-content",
"resolve-route-target",
"deps-satisfied?",
"try-client-route",
"bind-client-route-link",
"process-sse",
"bind-sse",
"bind-sse-swap",
"bind-inline-handlers",
"bind-preload-for",
"do-preload",
"VERB_SELECTOR",
"process-elements",
"process-one",
"process-emit-elements",
"save-scroll-position",
"handle-popstate",
"engine-init"
]
},
"_entry": {