Fix server import suspension, dist sync, JIT errors

- cek_run patched to handle import suspensions via _import_hook.
  define-library (import ...) now resolves cleanly on the server.
  IO suspension errors: 190 → 0. JIT failures: ~50 → 0.
- _import_hook wired in sx_server.ml to load .sx files on demand.
- compile-modules.js syncs source .sx files to dist/sx/ before
  compiling — eliminates stale bytecode from out-of-date copies.
- WASM binary rebuilt with all fixes.
- 2658/2658 tests pass (8 new — previously failing import tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 22:52:41 +00:00
parent b0a4be0f22
commit 5ac1ca9756
11 changed files with 480 additions and 408 deletions

View File

@@ -1,6 +1,7 @@
(define-library (sx signals)
(define-library
(sx signals)
(export
make-signal
signal?
@@ -26,205 +27,193 @@
with-island-scope
register-in-scope)
(begin
(define
make-signal
(fn
(value)
(dict "__signal" true "value" value "subscribers" (list) "deps" (list))))
(define signal? (fn (x) (and (dict? x) (has-key? x "__signal"))))
(define signal-value (fn (s) (get s "value")))
(define signal-set-value! (fn (s v) (dict-set! s "value" v)))
(define signal-subscribers (fn (s) (get s "subscribers")))
(define
signal-add-sub!
(fn
(s f)
(when
(not (contains? (get s "subscribers") f))
(dict-set! s "subscribers" (append (get s "subscribers") (list f))))))
(define
signal-remove-sub!
(fn
(s f)
(dict-set!
s
"subscribers"
(filter (fn (sub) (not (identical? sub f))) (get s "subscribers")))))
(define signal-deps (fn (s) (get s "deps")))
(define signal-set-deps! (fn (s deps) (dict-set! s "deps" deps)))
(define
signal
:effects ()
(fn ((initial-value :as any)) (make-signal initial-value)))
(define
deref
:effects ()
(fn
((s :as any))
(if
(not (signal? s))
s
(let
((ctx (context "sx-reactive" nil)))
(define
make-signal
(fn
(value)
(dict
"__signal"
true
"value"
value
"subscribers"
(list)
"deps"
(list))))
(define signal? (fn (x) (and (dict? x) (has-key? x "__signal"))))
(define signal-value (fn (s) (get s "value")))
(define signal-set-value! (fn (s v) (dict-set! s "value" v)))
(define signal-subscribers (fn (s) (get s "subscribers")))
(define
signal-add-sub!
(fn
(s f)
(when
ctx
(not (contains? (get s "subscribers") f))
(dict-set!
s
"subscribers"
(append (get s "subscribers") (list f))))))
(define
signal-remove-sub!
(fn
(s f)
(dict-set!
s
"subscribers"
(filter
(fn (sub) (not (identical? sub f)))
(get s "subscribers")))))
(define signal-deps (fn (s) (get s "deps")))
(define signal-set-deps! (fn (s deps) (dict-set! s "deps" deps)))
(define
signal
:effects ()
(fn ((initial-value :as any)) (make-signal initial-value)))
(define
deref
:effects ()
(fn
((s :as any))
(if
(not (signal? s))
s
(let
((dep-list (get ctx "deps")) (notify-fn (get ctx "notify")))
((ctx (context "sx-reactive" nil)))
(when
(not (contains? dep-list s))
(append! dep-list s)
(signal-add-sub! s notify-fn))))
(signal-value s)))))
(define
reset!
:effects (mutation)
(fn
((s :as signal) value)
(when
(signal? s)
(let
((old (signal-value s)))
ctx
(let
{:notify notify-fn :deps dep-list}
ctx
(when
(not (contains? dep-list s))
(append! dep-list s)
(signal-add-sub! s notify-fn))))
(signal-value s)))))
(define
reset!
:effects (mutation)
(fn
((s :as signal) value)
(when
(not (identical? old value))
(signal-set-value! s value)
(notify-subscribers s))))))
(define
swap!
:effects (mutation)
(fn
((s :as signal) (f :as callable) &rest args)
(when
(signal? s)
(let
((old (signal-value s))
(new-val (trampoline (apply f (cons old args)))))
(signal? s)
(let
((old (signal-value s)))
(when
(not (identical? old value))
(signal-set-value! s value)
(notify-subscribers s))))))
(define
swap!
:effects (mutation)
(fn
((s :as signal) (f :as callable) &rest args)
(when
(not (identical? old new-val))
(signal-set-value! s new-val)
(notify-subscribers s))))))
(define
computed
:effects (mutation)
(fn
((compute-fn :as lambda))
(let
((s (make-signal nil)) (deps (list)) (compute-ctx nil))
(let
((recompute (fn () (for-each (fn ((dep :as signal)) (signal-remove-sub! dep recompute)) (signal-deps s)) (signal-set-deps! s (list)) (let ((ctx (dict "deps" (list) "notify" recompute))) (scope-push! "sx-reactive" ctx) (let ((new-val (cek-call compute-fn nil))) (scope-pop! "sx-reactive") (signal-set-deps! s (get ctx "deps")) (let ((old (signal-value s))) (signal-set-value! s new-val) (when (not (identical? old new-val)) (notify-subscribers s))))))))
(recompute)
(register-in-scope (fn () (dispose-computed s)))
s))))
(define
effect
:effects (mutation)
(fn
((effect-fn :as lambda))
(let
((deps (list)) (disposed false) (cleanup-fn nil))
(let
((run-effect (fn () (when (not disposed) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)) (let ((ctx (dict "deps" (list) "notify" run-effect))) (scope-push! "sx-reactive" ctx) (let ((result (cek-call effect-fn nil))) (scope-pop! "sx-reactive") (set! deps (get ctx "deps")) (when (callable? result) (set! cleanup-fn result))))))))
(run-effect)
(signal? s)
(let
((old (signal-value s))
(new-val (trampoline (apply f (cons old args)))))
(when
(not (identical? old new-val))
(signal-set-value! s new-val)
(notify-subscribers s))))))
(define
computed
:effects (mutation)
(fn
((compute-fn :as lambda))
(let
((dispose-fn (fn () (set! disposed true) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)))))
(register-in-scope dispose-fn)
dispose-fn)))))
(define *batch-depth* 0)
(define *batch-queue* (list))
(define
batch
:effects (mutation)
(fn
((thunk :as lambda))
(set! *batch-depth* (+ *batch-depth* 1))
(cek-call thunk nil)
(set! *batch-depth* (- *batch-depth* 1))
(when
(= *batch-depth* 0)
(let
((queue *batch-queue*))
(set! *batch-queue* (list))
((s (make-signal nil)) (deps (list)) (compute-ctx nil))
(let
((recompute (fn () (for-each (fn ((dep :as signal)) (signal-remove-sub! dep recompute)) (signal-deps s)) (signal-set-deps! s (list)) (let ((ctx (dict "deps" (list) "notify" recompute))) (scope-push! "sx-reactive" ctx) (let ((new-val (cek-call compute-fn nil))) (scope-pop! "sx-reactive") (signal-set-deps! s (get ctx "deps")) (let ((old (signal-value s))) (signal-set-value! s new-val) (when (not (identical? old new-val)) (notify-subscribers s))))))))
(recompute)
(register-in-scope (fn () (dispose-computed s)))
s))))
(define
effect
:effects (mutation)
(fn
((effect-fn :as lambda))
(let
((seen (list)) (pending (list)))
(for-each
(fn
((s :as signal))
((deps (list)) (disposed false) (cleanup-fn nil))
(let
((run-effect (fn () (when (not disposed) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)) (let ((ctx (dict "deps" (list) "notify" run-effect))) (scope-push! "sx-reactive" ctx) (let ((result (cek-call effect-fn nil))) (scope-pop! "sx-reactive") (set! deps (get ctx "deps")) (when (callable? result) (set! cleanup-fn result))))))))
(run-effect)
(let
((dispose-fn (fn () (set! disposed true) (when cleanup-fn (cek-call cleanup-fn nil)) (for-each (fn ((dep :as signal)) (signal-remove-sub! dep run-effect)) deps) (set! deps (list)))))
(register-in-scope dispose-fn)
dispose-fn)))))
(define *batch-depth* 0)
(define *batch-queue* (list))
(define
batch
:effects (mutation)
(fn
((thunk :as lambda))
(set! *batch-depth* (+ *batch-depth* 1))
(cek-call thunk nil)
(set! *batch-depth* (- *batch-depth* 1))
(when
(= *batch-depth* 0)
(let
((queue *batch-queue*))
(set! *batch-queue* (list))
(let
((seen (list)) (pending (list)))
(for-each
(fn
((sub :as lambda))
(when
(not (contains? seen sub))
(append! seen sub)
(append! pending sub)))
(signal-subscribers s)))
queue)
(for-each (fn ((sub :as lambda)) (sub)) pending))))))
(define
notify-subscribers
:effects (mutation)
(fn
((s :as signal))
(if
(> *batch-depth* 0)
(when (not (contains? *batch-queue* s)) (append! *batch-queue* s))
(flush-subscribers s))))
(define
flush-subscribers
:effects (mutation)
(fn
((s :as dict))
(for-each (fn (sub) (cek-call sub nil)) (signal-subscribers s))))
(define
dispose-computed
:effects (mutation)
(fn
((s :as signal))
(when
(signal? s)
(for-each
(fn ((dep :as signal)) (signal-remove-sub! dep nil))
(signal-deps s))
(signal-set-deps! s (list)))))
(define
with-island-scope
:effects (mutation)
(fn
((scope-fn :as lambda) (body-fn :as lambda))
(scope-push! "sx-island-scope" scope-fn)
(let ((result (body-fn))) (scope-pop! "sx-island-scope") result)))
(define
register-in-scope
:effects (mutation)
(fn
((disposable :as lambda))
(let
((collector (scope-peek "sx-island-scope")))
(when collector (cek-call collector (list disposable))))))
)) ;; end define-library
((s :as signal))
(for-each
(fn
((sub :as lambda))
(when
(not (contains? seen sub))
(append! seen sub)
(append! pending sub)))
(signal-subscribers s)))
queue)
(for-each (fn ((sub :as lambda)) (sub)) pending))))))
(define
notify-subscribers
:effects (mutation)
(fn
((s :as signal))
(if
(> *batch-depth* 0)
(when (not (contains? *batch-queue* s)) (append! *batch-queue* s))
(flush-subscribers s))))
(define
flush-subscribers
:effects (mutation)
(fn
((s :as dict))
(for-each (fn (sub) (cek-call sub nil)) (signal-subscribers s))))
(define
dispose-computed
:effects (mutation)
(fn
((s :as signal))
(when
(signal? s)
(for-each
(fn ((dep :as signal)) (signal-remove-sub! dep nil))
(signal-deps s))
(signal-set-deps! s (list)))))
(define
with-island-scope
:effects (mutation)
(fn
((scope-fn :as lambda) (body-fn :as lambda))
(scope-push! "sx-island-scope" scope-fn)
(let ((result (body-fn))) (scope-pop! "sx-island-scope") result)))
(define
register-in-scope
:effects (mutation)
(fn
((disposable :as lambda))
(let
((collector (scope-peek "sx-island-scope")))
(when collector (cek-call collector (list disposable)))))))) ;; end define-library
;; Re-export to global namespace for backward compatibility
(import (sx signals))

View File

@@ -40,7 +40,7 @@
(when
(starts-with? name "~")
(when (not (contains? refs name)) (append! refs name)))))
("list" (for-each (fn (item) (scan-refs-walk item refs)) node))
("list" (for-each (fn (child) (scan-refs-walk child refs)) node))
("dict"
(for-each
(fn (key) (scan-refs-walk (dict-get node key) refs))
@@ -56,27 +56,16 @@
(append! seen n)
(let
((val (env-get env n)))
(match
(type-of val)
("component"
(for-each
(fn
((ref :as string))
(transitive-deps-walk ref seen env))
(scan-refs (component-body val))))
("island"
(for-each
(fn
((ref :as string))
(transitive-deps-walk ref seen env))
(scan-refs (component-body val))))
("macro"
(for-each
(fn
((ref :as string))
(transitive-deps-walk ref seen env))
(scan-refs (macro-body val))))
(_ nil))))))
(cond
(or (= (type-of val) "component") (= (type-of val) "island"))
(for-each
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (component-body val)))
(= (type-of val) "macro")
(for-each
(fn ((ref :as string)) (transitive-deps-walk ref seen env))
(scan-refs (macro-body val)))
:else nil)))))
(define
transitive-deps
:effects ()
@@ -216,37 +205,36 @@
(append! seen n)
(let
((val (env-get env n)))
(match
(type-of val)
("component"
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (component-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (component-body val)))))
("macro"
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (macro-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (macro-body val)))))
(_ nil))))))
(cond
(= (type-of val) "component")
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (component-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (component-body val))))
(= (type-of val) "macro")
(do
(for-each
(fn
((ref :as string))
(when
(not (contains? all-refs ref))
(append! all-refs ref)))
(scan-io-refs (macro-body val) io-names))
(for-each
(fn
((dep :as string))
(transitive-io-refs-walk dep seen all-refs env io-names))
(scan-refs (macro-body val))))
:else nil)))))
(define
transitive-io-refs
:effects ()
@@ -318,15 +306,16 @@
(if
(not (= (type-of val) "component"))
"server"
(match
(component-affinity val)
("server" "server")
("client" "client")
(_
(if
(not (component-pure? name env io-names))
"server"
"client"))))))))
(let
((affinity (component-affinity val)))
(cond
(= affinity "server")
"server"
(= affinity "client")
"client"
(not (component-pure? name env io-names))
"server"
:else "client")))))))
(define
page-render-plan
:effects ()

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,8 @@
;; Registry of freeze scopes: name → list of {name signal} entries
(define-library (sx freeze)
(define-library
(sx freeze)
(export
freeze-registry
freeze-signal
@@ -33,82 +34,96 @@
freeze-to-sx
thaw-from-sx)
(begin
(define freeze-registry (dict))
;; Register a signal in the current freeze scope
(define freeze-signal :effects [mutation]
(fn (name sig)
(let ((scope-name (context "sx-freeze-scope" nil)))
(when scope-name
(let ((entries (or (get freeze-registry scope-name) (list))))
(append! entries (dict "name" name "signal" sig))
(dict-set! freeze-registry scope-name entries))))))
;; Freeze scope delimiter — collects signals registered within body
(define freeze-scope :effects [mutation]
(fn (name body-fn)
(scope-push! "sx-freeze-scope" name)
;; Initialize empty entry list for this scope
(dict-set! freeze-registry name (list))
(cek-call body-fn nil)
(scope-pop! "sx-freeze-scope")
nil))
;; Freeze a named scope → SX dict of signal values
(define cek-freeze-scope :effects []
(fn (name)
(let ((entries (or (get freeze-registry name) (list)))
(signals-dict (dict)))
(for-each (fn (entry)
(dict-set! signals-dict
(get entry "name")
(signal-value (get entry "signal"))))
entries)
(dict "name" name "signals" signals-dict))))
;; Freeze all scopes
(define cek-freeze-all :effects []
(fn ()
(map (fn (name) (cek-freeze-scope name))
(keys freeze-registry))))
;; Thaw a named scope — restore signal values from frozen data
(define cek-thaw-scope :effects [mutation]
(fn (name frozen)
(let ((entries (or (get freeze-registry name) (list)))
(values (get frozen "signals")))
(when values
(for-each (fn (entry)
(let ((sig-name (get entry "name"))
(sig (get entry "signal"))
(val (get values sig-name)))
(when (not (nil? val))
(reset! sig val))))
entries)))))
;; Thaw all scopes from a list of frozen scope dicts
(define cek-thaw-all :effects [mutation]
(fn (frozen-list)
(for-each (fn (frozen)
(cek-thaw-scope (get frozen "name") frozen))
frozen-list)))
;; Serialize a frozen scope to SX text
(define freeze-to-sx :effects []
(fn (name)
(sx-serialize (cek-freeze-scope name))))
;; Restore from SX text
(define thaw-from-sx :effects [mutation]
(fn (sx-text)
(let ((parsed (sx-parse sx-text)))
(when (not (empty? parsed))
(let ((frozen (first parsed)))
(cek-thaw-scope (get frozen "name") frozen))))))
)) ;; end define-library
(define freeze-registry (dict))
(define
freeze-signal
:effects (mutation)
(fn
(name sig)
(let
((scope-name (context "sx-freeze-scope" nil)))
(when
scope-name
(let
((entries (or (get freeze-registry scope-name) (list))))
(append! entries (dict "name" name "signal" sig))
(dict-set! freeze-registry scope-name entries))))))
(define
freeze-scope
:effects (mutation)
(fn
(name body-fn)
(scope-push! "sx-freeze-scope" name)
(dict-set! freeze-registry name (list))
(cek-call body-fn nil)
(scope-pop! "sx-freeze-scope")
nil))
(define
cek-freeze-scope
:effects ()
(fn
(name)
(let
((entries (or (get freeze-registry name) (list)))
(signals-dict (dict)))
(for-each
(fn
(entry)
(dict-set!
signals-dict
(get entry "name")
(signal-value (get entry "signal"))))
entries)
(dict "name" name "signals" signals-dict))))
(define
cek-freeze-all
:effects ()
(fn
()
(map (fn (name) (cek-freeze-scope name)) (keys freeze-registry))))
(define
cek-thaw-scope
:effects (mutation)
(fn
(name frozen)
(let
((entries (or (get freeze-registry name) (list)))
(values (get frozen "signals")))
(when
values
(for-each
(fn
(entry)
(let
((sig-name (get entry "name"))
(sig (get entry "signal"))
(val (get values sig-name)))
(when (not (nil? val)) (reset! sig val))))
entries)))))
(define
cek-thaw-all
:effects (mutation)
(fn
(frozen-list)
(for-each
(fn (frozen) (cek-thaw-scope (get frozen "name") frozen))
frozen-list)))
(define
freeze-to-sx
:effects ()
(fn (name) (sx-serialize (cek-freeze-scope name))))
(define
thaw-from-sx
:effects (mutation)
(fn
(sx-text)
(let
((parsed (sx-parse sx-text)))
(when
(not (empty? parsed))
(let
((frozen (first parsed)))
(cek-thaw-scope (get frozen "name") frozen)))))))) ;; end define-library
;; Re-export to global namespace for backward compatibility
(import (sx freeze))

View File

@@ -1,3 +1,3 @@
(sxbc 1 "320fb4826d09fed3"
(sxbc 1 "050ab6181dc93341"
(code
:constants ("freeze-registry" "dict" "freeze-signal" {:upvalue-count 0 :arity 2 :constants ("context" "sx-freeze-scope" "get" "freeze-registry" "list" "append!" "dict" "name" "signal" "dict-set!") :bytecode (1 1 0 2 52 0 0 2 17 2 16 2 33 55 0 20 3 0 16 2 52 2 0 2 6 34 5 0 5 52 4 0 0 17 3 16 3 1 7 0 16 0 1 8 0 16 1 52 6 0 4 52 5 0 2 5 20 3 0 16 2 16 3 52 9 0 3 32 1 0 2 50)} "freeze-scope" {:upvalue-count 0 :arity 2 :constants ("scope-push!" "sx-freeze-scope" "dict-set!" "freeze-registry" "list" "cek-call" "scope-pop!") :bytecode (1 1 0 16 0 52 0 0 2 5 20 3 0 16 0 52 4 0 0 52 2 0 3 5 16 1 2 52 5 0 2 5 1 1 0 52 6 0 1 5 2 50)} "cek-freeze-scope" {:upvalue-count 0 :arity 1 :constants ("get" "freeze-registry" "list" "dict" "for-each" {:upvalue-count 1 :arity 1 :constants ("dict-set!" "get" "name" "signal-value" "signal") :bytecode (18 0 16 0 1 2 0 52 1 0 2 20 3 0 16 0 1 4 0 52 1 0 2 48 1 52 0 0 3 50)} "name" "signals") :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 1 52 3 0 0 17 2 51 5 0 1 2 16 1 52 4 0 2 5 1 6 0 16 0 1 7 0 16 2 52 3 0 4 50)} "cek-freeze-all" {:upvalue-count 0 :arity 0 :constants ("map" {:upvalue-count 0 :arity 1 :constants ("cek-freeze-scope") :bytecode (20 0 0 16 0 49 1 50)} "keys" "freeze-registry") :bytecode (51 1 0 20 3 0 52 2 0 1 52 0 0 2 50)} "cek-thaw-scope" {:upvalue-count 0 :arity 2 :constants ("get" "freeze-registry" "list" "signals" "for-each" {:upvalue-count 1 :arity 1 :constants ("get" "name" "signal" "not" "nil?" "reset!") :bytecode (16 0 1 1 0 52 0 0 2 17 1 16 0 1 2 0 52 0 0 2 17 2 18 0 16 1 52 0 0 2 17 3 16 3 52 4 0 1 52 3 0 1 33 12 0 20 5 0 16 2 16 3 49 2 32 1 0 2 50)}) :bytecode (20 1 0 16 0 52 0 0 2 6 34 5 0 5 52 2 0 0 17 2 16 1 1 3 0 52 0 0 2 17 3 16 3 33 14 0 51 5 0 1 3 16 2 52 4 0 2 32 1 0 2 50)} "cek-thaw-all" {:upvalue-count 0 :arity 1 :constants ("for-each" {:upvalue-count 0 :arity 1 :constants ("cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 1 2 0 52 1 0 2 16 0 49 2 50)}) :bytecode (51 1 0 16 0 52 0 0 2 50)} "freeze-to-sx" {:upvalue-count 0 :arity 1 :constants ("sx-serialize" "cek-freeze-scope") :bytecode (20 1 0 16 0 48 1 52 0 0 1 50)} "thaw-from-sx" {:upvalue-count 0 :arity 1 :constants ("sx-parse" "not" "empty?" "first" "cek-thaw-scope" "get" "name") :bytecode (20 0 0 16 0 48 1 17 1 16 1 52 2 0 1 52 1 0 1 33 27 0 16 1 52 3 0 1 17 2 20 4 0 16 2 1 6 0 52 5 0 2 16 2 49 2 32 1 0 2 50)} {:library (sx freeze) :op "import"}) :bytecode (52 1 0 0 128 0 0 5 51 3 0 128 2 0 5 51 5 0 128 4 0 5 51 7 0 128 6 0 5 51 9 0 128 8 0 5 51 11 0 128 10 0 5 51 13 0 128 12 0 5 51 15 0 128 14 0 5 51 17 0 128 16 0 5 1 18 0 112 50)))

View File

@@ -79,35 +79,35 @@
(fn
(vm value)
(let
((sp (get vm "sp")) (stack (get vm "stack")))
((sp (vm-sp vm)) (stack (vm-stack vm)))
(when
(>= sp (vm-stack-length stack))
(let
((new-stack (make-vm-stack (* sp 2))))
((new-stack (vm-stack-grow stack sp)))
(vm-stack-copy! stack new-stack sp)
(dict-set! vm "stack" new-stack)
(vm-set-stack! vm new-stack)
(set! stack new-stack)))
(vm-stack-set! stack sp value)
(dict-set! vm "sp" (+ sp 1)))))
(vm-set-sp! vm (+ sp 1)))))
(define
vm-pop
(fn
(vm)
(let
((sp (- (get vm "sp") 1)))
(dict-set! vm "sp" sp)
(vm-stack-get (get vm "stack") sp))))
((sp (- (vm-sp vm) 1)))
(vm-set-sp! vm sp)
(vm-stack-get (vm-stack vm) sp))))
(define
vm-peek
(fn (vm) (vm-stack-get (get vm "stack") (- (get vm "sp") 1))))
(fn (vm) (vm-stack-get (vm-stack vm) (- (vm-sp vm) 1))))
(define
frame-read-u8
(fn
(frame)
(let
((ip (get frame "ip"))
(bc (get (get (get frame "closure") "vm-code") "vc-bytecode")))
(let ((v (nth bc ip))) (dict-set! frame "ip" (+ ip 1)) v))))
((ip (frame-ip frame))
(bc (-> frame frame-closure closure-code code-bytecode)))
(let ((v (nth bc ip))) (frame-set-ip! frame (+ ip 1)) v))))
(define
frame-read-u16
(fn
@@ -206,31 +206,28 @@
(if
(has-key? cells key)
(uv-get (get cells key))
(vm-stack-get (get vm "stack") (+ (get frame "base") slot))))))
(vm-stack-get (vm-stack vm) (+ (frame-base frame) slot))))))
(define
frame-local-set
(fn
(vm frame slot value)
"Write a local variable — to shared cell if captured, else to stack."
"Write a local variable — to shared cell or stack."
(let
((cells (get frame "local-cells")) (key (str slot)))
(if
(has-key? cells key)
(uv-set! (get cells key) value)
(vm-stack-set!
(get vm "stack")
(+ (get frame "base") slot)
value)))))
(vm-stack-set! (vm-stack vm) (+ (frame-base frame) slot) value)))))
(define
frame-upvalue-get
(fn
(frame idx)
(uv-get (nth (get (get frame "closure") "vm-upvalues") idx))))
(uv-get (nth (-> frame frame-closure closure-upvalues) idx))))
(define
frame-upvalue-set
(fn
(frame idx value)
(uv-set! (nth (get (get frame "closure") "vm-upvalues") idx) value)))
(uv-set! (nth (-> frame frame-closure closure-upvalues) idx) value)))
(define frame-ip (fn (frame) (get frame "ip")))
(define frame-set-ip! (fn (frame val) (dict-set! frame "ip" val)))
(define frame-base (fn (frame) (get frame "base")))
@@ -302,12 +299,12 @@
(vm frame name)
"Look up a global: globals table → closure env → primitives → HO wrappers"
(let
((globals (get vm "globals")))
((globals (vm-globals-ref vm)))
(if
(has-key? globals name)
(get globals name)
(let
((closure-env (get (get frame "closure") "closure-env")))
((closure-env (-> frame frame-closure closure-env)))
(if
(nil? closure-env)
(cek-try
@@ -325,41 +322,42 @@
vm-resolve-ho-form
(fn
(vm name)
(cond
(= name "for-each")
(fn
(f coll)
(for-each (fn (x) (vm-call-external vm f (list x))) coll))
(= name "map")
(fn
(f coll)
(map (fn (x) (vm-call-external vm f (list x))) coll))
(= name "map-indexed")
(fn
(f coll)
(map-indexed
(fn (i x) (vm-call-external vm f (list i x)))
coll))
(= name "filter")
(fn
(f coll)
(filter (fn (x) (vm-call-external vm f (list x))) coll))
(= name "reduce")
(fn
(f init coll)
(reduce
(fn (acc x) (vm-call-external vm f (list acc x)))
init
coll))
(= name "some")
(fn
(f coll)
(some (fn (x) (vm-call-external vm f (list x))) coll))
(= name "every?")
(fn
(f coll)
(every? (fn (x) (vm-call-external vm f (list x))) coll))
:else (error (str "VM undefined: " name)))))
(match
name
("for-each"
(fn
(f coll)
(for-each (fn (x) (vm-call-external vm f (list x))) coll)))
("map"
(fn
(f coll)
(map (fn (x) (vm-call-external vm f (list x))) coll)))
("map-indexed"
(fn
(f coll)
(map-indexed
(fn (i x) (vm-call-external vm f (list i x)))
coll)))
("filter"
(fn
(f coll)
(filter (fn (x) (vm-call-external vm f (list x))) coll)))
("reduce"
(fn
(f init coll)
(reduce
(fn (acc x) (vm-call-external vm f (list acc x)))
init
coll)))
("some"
(fn
(f coll)
(some (fn (x) (vm-call-external vm f (list x))) coll)))
("every?"
(fn
(f coll)
(every? (fn (x) (vm-call-external vm f (list x))) coll)))
(_ (error (str "VM undefined: " name))))))
(define
vm-call-external
(fn
@@ -372,14 +370,14 @@
vm-global-set
(fn
(vm frame name value)
"Set a global: write to closure env if name exists there, else globals."
"Set a global: write to closure env if found, else globals table."
(let
((closure-env (get (get frame "closure") "vm-closure-env"))
((closure-env (get (frame-closure frame) "vm-closure-env"))
(written false))
(when
(not (nil? closure-env))
(set! written (env-walk-set! closure-env name value)))
(when (not written) (dict-set! (get vm "globals") name value)))))
(when (not written) (dict-set! (vm-globals-ref vm) name value)))))
(define
env-walk
(fn
@@ -414,20 +412,15 @@
(let
((code (code-from-value code-val))
(uv-count
(if
(dict? code-val)
(let
((n (get code-val "upvalue-count")))
(if (nil? n) 0 n))
0)))
(if (dict? code-val) (or (get code-val "upvalue-count") 0) 0)))
(let
((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (get vm "stack") (+ (get frame "base") index))))) (dict-set! cells key c) c))) (nth (get (get frame "closure") "vm-upvalues") index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result)))
(make-vm-closure code upvalues nil (get vm "globals") nil)))))
((upvalues (let ((result (list)) (i 0)) (define capture-loop (fn () (when (< i uv-count) (let ((is-local (frame-read-u8 frame)) (index (frame-read-u8 frame))) (let ((cell (if (= is-local 1) (let ((cells (get frame "local-cells")) (key (str index))) (if (has-key? cells key) (get cells key) (let ((c (make-upvalue-cell (vm-stack-get (vm-stack vm) (+ (frame-base frame) index))))) (dict-set! cells key c) c))) (nth (-> frame frame-closure closure-upvalues) index)))) (append! result cell) (set! i (+ i 1)) (capture-loop)))))) (capture-loop) result)))
(make-vm-closure code upvalues nil (vm-globals-ref vm) nil)))))
(define
vm-run
(fn
(vm)
"Execute bytecode until all frames are consumed."
"Execute bytecode until all frames are done or IO suspension."
(define
loop
(fn
@@ -438,9 +431,9 @@
((frame (first (vm-frames vm)))
(rest-frames (rest (vm-frames vm))))
(let
((bc (code-bytecode (closure-code (frame-closure frame))))
((bc (-> frame frame-closure closure-code code-bytecode))
(consts
(code-constants (closure-code (frame-closure frame)))))
(-> frame frame-closure closure-code code-constants)))
(if
(>= (frame-ip frame) (len bc))
(vm-set-frames! vm (list))

File diff suppressed because one or more lines are too long

View File

@@ -1792,7 +1792,7 @@
blake2_js_for_wasm_create: blake2_js_for_wasm_create};
}
(globalThis))
({"link":[["runtime-0db9b496",0],["prelude-d7e4b000",0],["stdlib-23ce0836",[]],["sx-d4b9c764",[2]],["jsoo_runtime-f96b44a8",[2]],["js_of_ocaml-651f6707",[2,4]],["dune__exe__Sx_browser-ba5b5565",[2,3,5]],["std_exit-10fb8830",[2]],["start-f5d3f095",0]],"generated":(b=>{var
({"link":[["runtime-0db9b496",0],["prelude-d7e4b000",0],["stdlib-23ce0836",[]],["sx-90abc6ab",[2]],["jsoo_runtime-f96b44a8",[2]],["js_of_ocaml-651f6707",[2,4]],["dune__exe__Sx_browser-216e88df",[2,3,5]],["std_exit-10fb8830",[2]],["start-f5d3f095",0]],"generated":(b=>{var
c=b,a=b?.module?.export||b;return{"env":{"caml_ba_kind_of_typed_array":()=>{throw new
Error("caml_ba_kind_of_typed_array not implemented")},"caml_exn_with_js_backtrace":()=>{throw new
Error("caml_exn_with_js_backtrace not implemented")},"caml_int64_create_lo_mi_hi":()=>{throw new