init in compile mode is ~2.4 s (tier loading, analyzer self-compile, macro recompilation), paid by every process that builds a ctx from source — each jpm-test file, embedders, workers. init-cached marshals the built ctx to a disk image (same root-env dicts as snapshot/fork) and later processes unmarshal it in ~5 ms, any process: nothing from the baking process is needed at load. The cache key fingerprints the embedded .clj stdlib (which covers jolt-core: analyzer, IR, core tiers), the .janet seed sources next to the module, the janet version, the init opts, and the env knobs that shape a ctx (JOLT_PATH/ MUTABLE/AOT_CORE/FEATURES) — any change rebuilds. Corrupt or non-ctx images fall back to a rebuild (unmarshal of garbage can 'succeed' with a scalar, so the shape is checked, not just the throw). Writes are atomic (tmp + rename) so racing cold starts never publish a torn image. JOLT_NO_IMAGE_CACHE=1 opts out; JOLT_IMAGE_CACHE_DIR overrides the location (default TMPDIR). Test consumers switch to init-cached (harness, suite-worker, conformance, the behavioral unit/integration tests); tests that validate the bootstrap itself (bootstrap-fixpoint, staged-bootstrap, aot round-trip, direct-linking) and the deps tests (tmp-dir :paths would fragment the key) keep real init. Full jpm test: 2:46 -> 1:58 (~29%). New ctx-image-test covers cold/warm, cross-process load (subprocess runs defn/redef/macros/protocols/multimethods off the baked image), per-opts keying, and corrupt-image fallback.
118 lines
4 KiB
Text
118 lines
4 KiB
Text
(use ../../src/jolt/evaluator)
|
|
(use ../../src/jolt/types)
|
|
(use ../../src/jolt/reader)
|
|
(use ../../src/jolt/api)
|
|
(use ../../src/jolt/reader)
|
|
# SCI is a clj/cljs-targeted library: its .cljc sources select implementation
|
|
# via #?(:clj ...) and have no :jolt branches — load it under clj-compat
|
|
# features (spec 02-reader S18: feature sets are a property of the loading
|
|
# context; the portable default is #{:jolt :default}).
|
|
(reader-features-set! ["jolt" "clj" "default"])
|
|
|
|
(def ctx (init-cached))
|
|
|
|
(printf "Loading SCI stubs...\n")
|
|
(defn load-stubs [ctx filepath]
|
|
(var s (slurp filepath))
|
|
(var count 0)
|
|
(while (> (length (string/trim s)) 0)
|
|
(def [form rest] (parse-next s))
|
|
(set s rest)
|
|
(++ count)
|
|
(when (not (nil? form))
|
|
(eval-form ctx @{} form)))
|
|
(printf " Loaded %d stub forms\n" count))
|
|
|
|
(load-stubs ctx "src/jolt/clojure/sci/lang_stubs.clj")
|
|
(load-stubs ctx "src/jolt/clojure/sci/io_stubs.clj")
|
|
(load-stubs ctx "src/jolt/clojure/sci/host_stubs.clj")
|
|
|
|
# namespaces.cljc copies vars out of Jolt's own clojure.string/set/walk/edn, so
|
|
# make sure those are loaded before it runs.
|
|
(each lib ["clojure.string" "clojure.set" "clojure.walk" "clojure.edn"]
|
|
(protect (eval-form ctx @{} (first (parse-next (string "(require '[" lib "])"))))))
|
|
|
|
(defn load-file [ctx path]
|
|
(var s (slurp path))
|
|
(var count 0)
|
|
(var ok 0)
|
|
(var fail 0)
|
|
(var failures @[])
|
|
(while (> (length (string/trim s)) 0)
|
|
(def [form rest] (parse-next s))
|
|
(set s rest)
|
|
(++ count)
|
|
(if (not (nil? form))
|
|
(do
|
|
(printf "eval form %d..." count)
|
|
(flush)
|
|
(if (try
|
|
(do (eval-form ctx @{} form) true)
|
|
([err fib]
|
|
(printf " FAIL: %q\n" err)
|
|
(when (os/getenv "SCI_TRACE") (debug/stacktrace fib ""))
|
|
(array/push failures {:form-number count :error (string err) :form (string form)})
|
|
false))
|
|
(do
|
|
(printf " OK\n")
|
|
(++ ok))
|
|
(++ fail)))))
|
|
{:ok ok :fail fail :total count :failures failures})
|
|
|
|
(def sci-base "vendor/sci/src/sci")
|
|
|
|
(def load-order @[
|
|
["impl/macros.cljc" nil]
|
|
["impl/protocols.cljc" nil]
|
|
["impl/types.cljc" nil]
|
|
["impl/unrestrict.cljc" nil]
|
|
["impl/vars.cljc" nil]
|
|
["lang.cljc" nil]
|
|
["impl/utils.cljc" nil]
|
|
["ctx_store.cljc" nil]
|
|
["impl/deftype.cljc" nil]
|
|
["impl/records.cljc" nil]
|
|
["impl/core_protocols.cljc" nil]
|
|
["impl/hierarchies.cljc" nil]
|
|
# pure-Clojure macro/expander modules (loadable from SCI's real source)
|
|
["impl/destructure.cljc" nil]
|
|
["impl/doseq_macro.cljc" nil]
|
|
["impl/for_macro.cljc" nil]
|
|
["impl/fns.cljc" nil]
|
|
["impl/multimethods.cljc" nil]
|
|
["impl/namespaces.cljc" nil]
|
|
["core.cljc" nil]
|
|
])
|
|
|
|
(var total-ok 0)
|
|
(var total-fail 0)
|
|
(var all-failures @[])
|
|
|
|
(each [file expected-ns] load-order
|
|
(def path (string sci-base "/" file))
|
|
(printf "\n=== Loading %s ===\n" file)
|
|
(def result (load-file ctx path))
|
|
(printf " Result: %d ok, %d fail, %d total\n" (result :ok) (result :fail) (result :total))
|
|
(+= total-ok (result :ok))
|
|
(+= total-fail (result :fail))
|
|
(each f (result :failures)
|
|
(array/push all-failures {:file file :form-number (f :form-number) :error (f :error) :form (f :form)})))
|
|
|
|
(printf "\n==============================\n")
|
|
(printf "TOTAL: %d ok, %d fail, %d total\n" total-ok total-fail (+ total-ok total-fail))
|
|
(printf "==============================\n")
|
|
|
|
(printf "\ncurrent ns: %s\n" (ctx-current-ns ctx))
|
|
(printf "sci.core exists: %q\n" (not (nil? (ctx-find-ns ctx "sci.core"))))
|
|
(printf "total namespaces: %d\n" (length (keys ((ctx :env) :namespaces))))
|
|
|
|
(when (> (length all-failures) 0)
|
|
(printf "\n=== FAILURES ===\n")
|
|
(each f all-failures
|
|
(printf "[%s:%d] %s\n" (f :file) (f :form-number) (f :error))
|
|
(printf " form: %s\n" (f :form))))
|
|
|
|
# Regression guard: every form in the loaded SCI modules must evaluate cleanly.
|
|
(assert (= 0 total-fail)
|
|
(string total-fail " SCI form(s) failed to load (see FAILURES above)"))
|
|
(print "\nAll SCI bootstrap forms loaded successfully.")
|