From 9122308d3e5d2b6444ec998cdf7593d392b5c8d7 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 6 Dec 2025 12:20:15 +0530 Subject: [PATCH 1/9] test(module-loader): test for cpp loading and preference checks .jank overrides .cljc overrides .cpp based modules --- .../src/cpp/jank/runtime/module/loader.cpp | 8 ++++---- compiler+runtime/test/bash/module/cpp/pass-test | 4 ++++ .../test/bash/module/cpp/src/jank_test/a.cpp | 13 +++++++++++++ .../test/bash/module/cpp/src/jank_test/a.jank | 2 ++ .../test/bash/module/cpp/src/jank_test/b.cljc | 2 ++ .../test/bash/module/cpp/src/jank_test/b.cpp | 12 ++++++++++++ .../test/bash/module/cpp/src/jank_test/cljc.cljc | 2 ++ .../test/bash/module/cpp/src/jank_test/cpp.cpp | 12 ++++++++++++ .../bash/module/cpp/src/jank_test/test_runner.cljc | 11 +++++++++++ 9 files changed, 62 insertions(+), 4 deletions(-) create mode 100755 compiler+runtime/test/bash/module/cpp/pass-test create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/a.cpp create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/a.jank create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/b.cljc create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/b.cpp create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/cljc.cljc create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/cpp.cpp create mode 100644 compiler+runtime/test/bash/module/cpp/src/jank_test/test_runner.cljc diff --git a/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp b/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp index 2687ff978..aa7f965ea 100644 --- a/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp +++ b/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp @@ -791,10 +791,6 @@ namespace jank::runtime::module return find_result{ entry, module_type }; } } - else if(entry.cpp.is_some()) - { - return find_result{ entry, module_type::cpp }; - } else if(entry.jank.is_some()) { return find_result{ entry, module_type::jank }; @@ -803,6 +799,10 @@ namespace jank::runtime::module { return find_result{ entry, module_type::cljc }; } + else if(entry.cpp.is_some()) + { + return find_result{ entry, module_type::cpp }; + } } return error::internal_runtime_failure( diff --git a/compiler+runtime/test/bash/module/cpp/pass-test b/compiler+runtime/test/bash/module/cpp/pass-test new file mode 100755 index 000000000..fe92f4f76 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/pass-test @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +jank --module-path src run-main jank-test.test-runner | grep '^:success$' diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/a.cpp b/compiler+runtime/test/bash/module/cpp/src/jank_test/a.cpp new file mode 100644 index 000000000..850df22aa --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/a.cpp @@ -0,0 +1,13 @@ +using jank_object_ref = void *; + +extern "C" void jank_module_set_loaded(char const *module); +extern "C" jank_object_ref jank_eval(jank_object_ref s); +extern "C" jank_object_ref jank_string_create(char const *s); + +extern "C" void jank_load_jank_test_a() +{ + jank_eval(jank_string_create("(ns jank-test.a)")); + jank_module_set_loaded("jank-test.a"); + jank_eval(jank_string_create("(defn -main [] \"a.cpp\")")); +} + diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/a.jank b/compiler+runtime/test/bash/module/cpp/src/jank_test/a.jank new file mode 100644 index 000000000..315263851 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/a.jank @@ -0,0 +1,2 @@ +(ns jank-test.a) +(defn -main [] "a.jank") diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cljc b/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cljc new file mode 100644 index 000000000..9d9c48750 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cljc @@ -0,0 +1,2 @@ +(ns jank-test.b) +(defn -main [] "b.cljc") diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cpp b/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cpp new file mode 100644 index 000000000..d32850a95 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/b.cpp @@ -0,0 +1,12 @@ +using jank_object_ref = void *; + +extern "C" void jank_module_set_loaded(char const *module); +extern "C" jank_object_ref jank_eval(jank_object_ref s); +extern "C" jank_object_ref jank_string_create(char const *s); + +extern "C" void jank_load_jank_test_b() +{ + jank_eval(jank_string_create("(ns jank-test.b)")); + jank_module_set_loaded("jank-test.b"); + jank_eval(jank_string_create("(defn -main [] \"b.cpp\")")); +} diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/cljc.cljc b/compiler+runtime/test/bash/module/cpp/src/jank_test/cljc.cljc new file mode 100644 index 000000000..e41fbcdd7 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/cljc.cljc @@ -0,0 +1,2 @@ +(ns jank-test.cljc) +(defn -main [] #?(:clj :clj :jank :jank :default :default)) diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/cpp.cpp b/compiler+runtime/test/bash/module/cpp/src/jank_test/cpp.cpp new file mode 100644 index 000000000..6b7c15ab6 --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/cpp.cpp @@ -0,0 +1,12 @@ +using jank_object_ref = void *; + +extern "C" void jank_module_set_loaded(char const *module); +extern "C" jank_object_ref jank_eval(jank_object_ref s); +extern "C" jank_object_ref jank_string_create(char const *s); + +extern "C" void jank_load_jank_test_cpp() +{ + jank_eval(jank_string_create("(ns jank-test.cpp)")); + jank_module_set_loaded("jank-test.cpp"); + jank_eval(jank_string_create("(defn -main [] :cpp)")); +} diff --git a/compiler+runtime/test/bash/module/cpp/src/jank_test/test_runner.cljc b/compiler+runtime/test/bash/module/cpp/src/jank_test/test_runner.cljc new file mode 100644 index 000000000..8ecfea90a --- /dev/null +++ b/compiler+runtime/test/bash/module/cpp/src/jank_test/test_runner.cljc @@ -0,0 +1,11 @@ +(ns jank-test.test-runner + (:require jank-test.a + jank-test.b + jank-test.cpp)) + +(defn -main [] + (assert (= "a.jank" (jank-test.a/-main)) ".jank overrides .cpp") + (assert (= "b.cljc" (jank-test.b/-main)) ".cljc overrides .cpp") + (assert (= :cpp (jank-test.cpp/-main)) "cpp module should be loaded correctly") + ;; exit code not reliable enough https://github.com/jank-lang/jank/issues/181 + (println :success)) From 48d15273576eb0a2e48294e9dd9d9c1c303b72a1 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 6 Dec 2025 16:22:18 +0530 Subject: [PATCH 2/9] test(module-loader): bash test for compiled module and its loadability Should not be loaded from a jar --- .../bash/module/compiled-modules/pass-test | 73 +++++++++++++++++++ .../compiled-modules/src/jank_test/a.cljc | 9 +++ 2 files changed, 82 insertions(+) create mode 100755 compiler+runtime/test/bash/module/compiled-modules/pass-test create mode 100644 compiler+runtime/test/bash/module/compiled-modules/src/jank_test/a.cljc diff --git a/compiler+runtime/test/bash/module/compiled-modules/pass-test b/compiler+runtime/test/bash/module/compiled-modules/pass-test new file mode 100755 index 000000000..4169a0b4a --- /dev/null +++ b/compiler+runtime/test/bash/module/compiled-modules/pass-test @@ -0,0 +1,73 @@ +#!/usr/bin/env bb + +^{:clj-kondo/ignore [:namespace-name-mismatch]} +(ns jank.test.module.compiled-module + (:require [babashka.fs :as fs] + [babashka.process :as proc] + [clojure.string :as string] + [clojure.test :as t :refer [deftest is testing use-fixtures]])) + +(def this-nsym (ns-name *ns*)) + +(def module-a "jank-test.a") +(def module-a-path "./src/jank_test/a.cljc") +(defn compiled-a-path [] + (first (fs/glob "./target" "**/a.o"))) + +(defn jank [command module-name & {:keys [extra-module-path]}] + (proc/sh ["jank" "--module-path" + (str "src" (when-not (empty? extra-module-path) + (str ":" extra-module-path))) + command module-name])) + +(use-fixtures + :each + (fn [f] + (jank "compile-module" module-a) + (f) + (fs/delete-tree "./target"))) + +(deftest compiled-module-gets-loaded + (testing "loads a compiled module instead of the source" + (is (false? (-> (jank "run-main" module-a) + :out + (string/includes? ":only-during-read"))))) + + (testing "Source fresher than the compiled module, loads from source" + (proc/sh ["touch" module-a-path]) + (-> (jank "run-module" module-a) + :out + println) + (is (true? (-> (jank "run-main" module-a) + :out + (string/includes? ":only-during-read")))))) + +(defn create-jar-with-a [] + (let [jar-name "test.jar" + [compilation-dir rest-of-module-path] (->> (fs/glob "target" "**/a.o") + (first) + (fs/components) + (split-at 2) + (map (partial apply fs/path)) + (map str))] + (proc/sh {:dir compilation-dir} "jar" "cf" jar-name rest-of-module-path) + (fs/path compilation-dir jar-name))) + +(deftest compiled-module-loadability + (testing "Doesn't load a module from archive" + (fs/with-temp-dir [tmp-dir] + (let [jar (-> (create-jar-with-a) + (fs/move tmp-dir))] + (fs/delete-tree "target") + (is (true? (-> (jank "run-main" module-a :extra-module-path jar) + :out + (string/includes? ":only-during-read")))))))) + +(defn -main [] + (System/exit + (if (t/successful? (t/run-tests this-nsym)) + 0 + 1))) + +(when (= *file* (System/getProperty "babashka.file")) + (apply -main *command-line-args*)) diff --git a/compiler+runtime/test/bash/module/compiled-modules/src/jank_test/a.cljc b/compiler+runtime/test/bash/module/compiled-modules/src/jank_test/a.cljc new file mode 100644 index 000000000..4ea03148e --- /dev/null +++ b/compiler+runtime/test/bash/module/compiled-modules/src/jank_test/a.cljc @@ -0,0 +1,9 @@ +(ns jank-test.a) + +(defmacro with-prelog [& body] + (println :only-during-read) + `(do ~@body)) + +(with-prelog + (defn -main [] + (println :success))) From 59ea5d7cefa2951bc8a958ce170341d42f0ef7b0 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 6 Dec 2025 22:31:26 +0530 Subject: [PATCH 3/9] test(module-loader): should not load compiled module without source --- .../bash/module/compiled-modules/pass-test | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/compiler+runtime/test/bash/module/compiled-modules/pass-test b/compiler+runtime/test/bash/module/compiled-modules/pass-test index 4169a0b4a..5b7ad9e09 100755 --- a/compiler+runtime/test/bash/module/compiled-modules/pass-test +++ b/compiler+runtime/test/bash/module/compiled-modules/pass-test @@ -20,6 +20,29 @@ (str ":" extra-module-path))) command module-name])) +(defn create-jar-with-a [] + (let [jar-name "test.jar" + [compilation-dir rest-of-module-path] (->> (fs/glob "target" "**/a.o") + (first) + (fs/components) + (split-at 2) + (map (partial apply fs/path)) + (map str))] + (proc/sh {:dir compilation-dir} "jar" "cf" jar-name rest-of-module-path) + (fs/path compilation-dir jar-name))) + +(defmacro with-hidden-file + "Temporarily renames `path` to `path.bak`, evaluates body, + and restores the file in a `finally` block." + [path & body] + `(let [path# ~path + bak# (str path# ".bak")] + (fs/move path# bak#) + (try + ~@body + (finally + (fs/move bak# path#))))) + (use-fixtures :each (fn [f] @@ -42,26 +65,21 @@ :out (string/includes? ":only-during-read")))))) -(defn create-jar-with-a [] - (let [jar-name "test.jar" - [compilation-dir rest-of-module-path] (->> (fs/glob "target" "**/a.o") - (first) - (fs/components) - (split-at 2) - (map (partial apply fs/path)) - (map str))] - (proc/sh {:dir compilation-dir} "jar" "cf" jar-name rest-of-module-path) - (fs/path compilation-dir jar-name))) - (deftest compiled-module-loadability (testing "Doesn't load a module from archive" (fs/with-temp-dir [tmp-dir] (let [jar (-> (create-jar-with-a) (fs/move tmp-dir))] - (fs/delete-tree "target") - (is (true? (-> (jank "run-main" module-a :extra-module-path jar) - :out - (string/includes? ":only-during-read")))))))) + (with-hidden-file "target" + (is (true? (-> (jank "run-main" module-a :extra-module-path jar) + :out + (string/includes? ":only-during-read")))))))) + + (testing "Doesn't load module if source not found" + (with-hidden-file module-a-path + (is (true? (-> (jank "run-main" module-a) + :out + (string/includes? "No sources for registered module"))))))) (defn -main [] (System/exit From b85fa040de9ab917fc8a91bd4faa1ecdbc8a52b3 Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 12:11:45 +0530 Subject: [PATCH 4/9] fix(module-loader): Remove `loaded-libs` check in loader::load() We introduced checking whether a module is loaded already i.e. exists in `clojure.core/*loaded-libs*`, in `jank::runtime::module::loader::load()` to ensure clojure.core when loaded from the functions in main.cpp, is only loaded once. Clojure only interacts with `*loaded-libs` from its `clojure.core` ns. jank needs to interact with it from our cpp source because there we directly rely on `jank::runtime::context::load_module` instead of going through `(clojure.core/load-lib ...)`. Now that within the main, when loading `clojure.core`, we also mark it as loaded in `*loaded-libs*`, we don't need this check. In the hindsight, this approach is a better one than modifying the `loader::load()` logic. Reverting this logic, does have a bit of performance impact. The reason being not being short-circuited when the module already is loaded within the `loader::load()` function. This is how Clojure does it right now. We'll see later, if this should be optimized. --- compiler+runtime/src/cpp/jank/runtime/module/loader.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp b/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp index aa7f965ea..598180a36 100644 --- a/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp +++ b/compiler+runtime/src/cpp/jank/runtime/module/loader.cpp @@ -912,11 +912,6 @@ namespace jank::runtime::module jtl::result loader::load(jtl::immutable_string const &module, origin const ori) { - if(ori != origin::source && loader::is_loaded(module)) - { - return ok(); - } - auto const &found_module{ loader::find(module, ori) }; if(found_module.is_err()) { @@ -954,11 +949,11 @@ namespace jank::runtime::module return res; } - loader::set_is_loaded(module); { auto const locked_ordered_modules{ __rt_ctx->loaded_modules_in_order.wlock() }; locked_ordered_modules->push_back(module); } + return ok(); } From e6e7c774603250a156988c79acdb622946764f9c Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 18:05:10 +0530 Subject: [PATCH 5/9] test(module-loader): new vars available on module reload --- .../src/cpp/jank/environment/check_health.cpp | 3 +- .../test/bash/module/reload/pass-test | 90 +++++++++++++++++++ .../reload/src/jank_test/dependency.cljc | 4 + .../reload/src/jank_test/dependent.cljc | 5 ++ 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100755 compiler+runtime/test/bash/module/reload/pass-test create mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc create mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc diff --git a/compiler+runtime/src/cpp/jank/environment/check_health.cpp b/compiler+runtime/src/cpp/jank/environment/check_health.cpp index 8bd1f8cfb..2bc521998 100644 --- a/compiler+runtime/src/cpp/jank/environment/check_health.cpp +++ b/compiler+runtime/src/cpp/jank/environment/check_health.cpp @@ -390,12 +390,13 @@ namespace jank::environment [=] { util::cli::opts = saved_opts; } }; + runtime::__rt_ctx->module_loader.set_is_loaded("/clojure.core"); #ifdef JANK_PHASE_2 jank_load_clojure_core(); - runtime::__rt_ctx->module_loader.set_is_loaded("/clojure.core"); #else runtime::__rt_ctx->load_module("/clojure.core", runtime::module::origin::latest).expect_ok(); #endif + runtime::__rt_ctx->module_loader.add_path(path_tmp); runtime::__rt_ctx->compile_module(util::cli::opts.target_module).expect_ok(); diff --git a/compiler+runtime/test/bash/module/reload/pass-test b/compiler+runtime/test/bash/module/reload/pass-test new file mode 100755 index 000000000..54c492535 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/pass-test @@ -0,0 +1,90 @@ +#!/usr/bin/env bb + +^{:clj-kondo/ignore [:namespace-name-mismatch]} +(ns jank.test.module.reload + (:require [babashka.fs :as fs] + [babashka.process :as ps] + [clojure.string :as string] + [clojure.java.io :as io] + [clojure.test :as t :refer [deftest is testing use-fixtures]])) + +(def this-nsym (ns-name *ns*)) + +(def dependent-module-path "src/jank_test/dependent.cljc") +(def dependency-module-path "src/jank_test/dependency.cljc") + +(defn send-command [writer command] + (doto writer + (.write command) + (.flush))) + +(defn read-available [reader] + (let [sb (StringBuilder.)] + (while (.ready reader) + (.append sb (char (.read reader)))) + (str sb))) + +(defmacro with-backup [path & body] + `(fs/with-temp-dir [dir#] + (let [tmp-path# (fs/path dir# ~path)] + (fs/copy-tree ~path tmp-path#) + (try + ~@body + (finally + (fs/delete-tree ~path) + (fs/copy-tree tmp-path# ~path)))))) + +(use-fixtures + :each + (fn [f] + (with-backup "src/jank_test" + (f)))) + +(deftest a-single-module-reload + (let [proc (ps/process ["jank" "--module-path" "src" "repl"])] + (with-open [repl-writer (io/writer (:in proc)) + repl-reader (io/reader (:out proc))] + (doto repl-writer + (send-command "(require '[jank-test.dependent :as d])\n") + (send-command "(d/first-fn)\n")) + + ;; Wait for repl to start + (Thread/sleep 2000) + (is (string/includes? + (read-available repl-reader) + "Hello")) + + (send-command repl-writer "(d/hi)\n") + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Unable to resolve symbol 'jank-test.dependent/hi'")) + + (testing "new var should be added to the module" + ;; Add a new var + (doto dependent-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi!")) :append true)) + + (doto repl-writer + (send-command "(require '[jank-test.dependent :as d] :reload)\n") + (send-command "(d/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Hi!"))) + + (testing "var removed from the source should still available") + (testing "dependency module should not reload")) + (ps/destroy proc))) + +(defn -main [] + (System/exit + (if (t/successful? (t/run-tests this-nsym)) + 0 + 1))) + +(when (= *file* (System/getProperty "babashka.file")) + (apply -main *command-line-args*)) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc new file mode 100644 index 000000000..a450f6441 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc @@ -0,0 +1,4 @@ +(ns jank-test.dependency) + +(defn say-hello! [] + (println "Hello")) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc new file mode 100644 index 000000000..c5a2d8441 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc @@ -0,0 +1,5 @@ +(ns jank-test.dependent + (:require [jank-test.dependency :as dep])) + +(defn first-fn [] + (dep/say-hello!)) From fcd6633ff79b13f4c39abc1362181a59a3e3cc62 Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 20:48:07 +0530 Subject: [PATCH 6/9] test(module-loader): on reload removed var from source, still available in the ns --- compiler+runtime/test/bash/module/reload/hello.clj | 3 +++ .../test/bash/module/reload/hello.cljc | 3 +++ compiler+runtime/test/bash/module/reload/pass-test | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 compiler+runtime/test/bash/module/reload/hello.clj create mode 100644 compiler+runtime/test/bash/module/reload/hello.cljc diff --git a/compiler+runtime/test/bash/module/reload/hello.clj b/compiler+runtime/test/bash/module/reload/hello.clj new file mode 100644 index 000000000..ef0a7d0f9 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/hello.clj @@ -0,0 +1,3 @@ +(ns hello ^{:zip/branch? #object[rewrite_clj.node.protocols$fn__29657$G__29633__29662 0x7beba217 "rewrite_clj.node.protocols$fn__29657$G__29633__29662@7beba217"], :zip/children #object[clojure.core$comp$fn__5895 0x7eef8a2b "clojure.core$comp$fn__5895@7eef8a2b"], :zip/make-node #object[rewrite_clj.node.protocols$fn__29644$G__29637__29651 0x5894853a "rewrite_clj.node.protocols$fn__29644$G__29637__29651@5894853a"], :rewrite-clj.zip/opts {:track-position? false, :auto-resolve #object[rewrite_clj.node.protocols$default_auto_resolve 0x399989bd "rewrite_clj.node.protocols$default_auto_resolve@399989bd"]}} [(println "Ahoi") {:l [], :pnodes [(println "Ahoi")], :ppath nil, :r nil}]) + +(println "Hello") diff --git a/compiler+runtime/test/bash/module/reload/hello.cljc b/compiler+runtime/test/bash/module/reload/hello.cljc new file mode 100644 index 000000000..834ac76f8 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/hello.cljc @@ -0,0 +1,3 @@ +(ns hello) + +(println "Hello") diff --git a/compiler+runtime/test/bash/module/reload/pass-test b/compiler+runtime/test/bash/module/reload/pass-test index 54c492535..954d0700e 100755 --- a/compiler+runtime/test/bash/module/reload/pass-test +++ b/compiler+runtime/test/bash/module/reload/pass-test @@ -41,7 +41,8 @@ (f)))) (deftest a-single-module-reload - (let [proc (ps/process ["jank" "--module-path" "src" "repl"])] + (let [proc (ps/process ["jank" "--module-path" "src" "repl"]) + original-dependent-content (slurp dependent-module-path)] (with-open [repl-writer (io/writer (:in proc)) repl-reader (io/reader (:out proc))] (doto repl-writer @@ -76,7 +77,16 @@ (read-available repl-reader) "Hi!"))) - (testing "var removed from the source should still available") + (testing "var removed from the source should still available" + (spit dependent-module-path original-dependent-content) + (doto repl-writer + (send-command "(require '[jank-test.dependent :as d] :reload)\n") + (send-command "(d/hi)\n")) + + (Thread/sleep 1000) + (is (string/includes? + (read-available repl-reader) + "Hi!"))) (testing "dependency module should not reload")) (ps/destroy proc))) From 16ddc4c3a9685c5757bd7ea71a2dc127a5a1ea80 Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 21:12:07 +0530 Subject: [PATCH 7/9] test(module-loader): dependency module should not be reloaded --- .../test/bash/module/reload/pass-test | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler+runtime/test/bash/module/reload/pass-test b/compiler+runtime/test/bash/module/reload/pass-test index 954d0700e..5bb8805da 100755 --- a/compiler+runtime/test/bash/module/reload/pass-test +++ b/compiler+runtime/test/bash/module/reload/pass-test @@ -83,11 +83,24 @@ (send-command "(require '[jank-test.dependent :as d] :reload)\n") (send-command "(d/hi)\n")) - (Thread/sleep 1000) + (Thread/sleep 500) (is (string/includes? (read-available repl-reader) "Hi!"))) - (testing "dependency module should not reload")) + (testing "dependency module should not reload" + (doto dependency-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi in dependency!")) :append true)) + (doto repl-writer + (send-command "(require '[jank-test.dependency :as dep])\n") + (send-command "(require '[jank-test.dependent :as d] :reload)\n") + (send-command "(dep/hi)\n")) + + (Thread/sleep 500) + + (is (string/includes? + (read-available repl-reader) + "Unable to resolve symbol 'jank-test.dependency/hi'")))) (ps/destroy proc))) (defn -main [] From 495e2274b2a4a5ded92f553f71773f493e94398d Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 21:18:45 +0530 Subject: [PATCH 8/9] test(module-loader): :reload-all reloads depedent and all its dependency recursively --- .../test/bash/module/reload/hello.clj | 3 - .../test/bash/module/reload/hello.cljc | 3 - .../test/bash/module/reload/pass-test | 171 ++++++++++++------ .../reload/src/jank_test/dependent.cljc | 5 - .../jank_test/{ => reload}/dependency.cljc | 2 +- .../src/jank_test/reload/dependent.cljc | 5 + .../src/jank_test/reload_all/dependency.cljc | 4 + .../src/jank_test/reload_all/dependent.cljc | 5 + 8 files changed, 129 insertions(+), 69 deletions(-) delete mode 100644 compiler+runtime/test/bash/module/reload/hello.clj delete mode 100644 compiler+runtime/test/bash/module/reload/hello.cljc delete mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc rename compiler+runtime/test/bash/module/reload/src/jank_test/{ => reload}/dependency.cljc (56%) create mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependent.cljc create mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependency.cljc create mode 100644 compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependent.cljc diff --git a/compiler+runtime/test/bash/module/reload/hello.clj b/compiler+runtime/test/bash/module/reload/hello.clj deleted file mode 100644 index ef0a7d0f9..000000000 --- a/compiler+runtime/test/bash/module/reload/hello.clj +++ /dev/null @@ -1,3 +0,0 @@ -(ns hello ^{:zip/branch? #object[rewrite_clj.node.protocols$fn__29657$G__29633__29662 0x7beba217 "rewrite_clj.node.protocols$fn__29657$G__29633__29662@7beba217"], :zip/children #object[clojure.core$comp$fn__5895 0x7eef8a2b "clojure.core$comp$fn__5895@7eef8a2b"], :zip/make-node #object[rewrite_clj.node.protocols$fn__29644$G__29637__29651 0x5894853a "rewrite_clj.node.protocols$fn__29644$G__29637__29651@5894853a"], :rewrite-clj.zip/opts {:track-position? false, :auto-resolve #object[rewrite_clj.node.protocols$default_auto_resolve 0x399989bd "rewrite_clj.node.protocols$default_auto_resolve@399989bd"]}} [(println "Ahoi") {:l [], :pnodes [(println "Ahoi")], :ppath nil, :r nil}]) - -(println "Hello") diff --git a/compiler+runtime/test/bash/module/reload/hello.cljc b/compiler+runtime/test/bash/module/reload/hello.cljc deleted file mode 100644 index 834ac76f8..000000000 --- a/compiler+runtime/test/bash/module/reload/hello.cljc +++ /dev/null @@ -1,3 +0,0 @@ -(ns hello) - -(println "Hello") diff --git a/compiler+runtime/test/bash/module/reload/pass-test b/compiler+runtime/test/bash/module/reload/pass-test index 5bb8805da..e60b4bdf9 100755 --- a/compiler+runtime/test/bash/module/reload/pass-test +++ b/compiler+runtime/test/bash/module/reload/pass-test @@ -6,13 +6,10 @@ [babashka.process :as ps] [clojure.string :as string] [clojure.java.io :as io] - [clojure.test :as t :refer [deftest is testing use-fixtures]])) + [clojure.test :as t :refer [deftest is testing]])) (def this-nsym (ns-name *ns*)) -(def dependent-module-path "src/jank_test/dependent.cljc") -(def dependency-module-path "src/jank_test/dependency.cljc") - (defn send-command [writer command] (doto writer (.write command) @@ -34,74 +31,134 @@ (fs/delete-tree ~path) (fs/copy-tree tmp-path# ~path)))))) -(use-fixtures - :each - (fn [f] - (with-backup "src/jank_test" - (f)))) - (deftest a-single-module-reload - (let [proc (ps/process ["jank" "--module-path" "src" "repl"]) - original-dependent-content (slurp dependent-module-path)] - (with-open [repl-writer (io/writer (:in proc)) - repl-reader (io/reader (:out proc))] - (doto repl-writer - (send-command "(require '[jank-test.dependent :as d])\n") - (send-command "(d/first-fn)\n")) - - ;; Wait for repl to start - (Thread/sleep 2000) - (is (string/includes? - (read-available repl-reader) - "Hello")) - - (send-command repl-writer "(d/hi)\n") - - (Thread/sleep 500) - (is (string/includes? - (read-available repl-reader) - "Unable to resolve symbol 'jank-test.dependent/hi'")) - - (testing "new var should be added to the module" - ;; Add a new var - (doto dependent-module-path - (spit "\n" :append true) - (spit (pr-str '(defn hi [] "Hi!")) :append true)) - + (with-backup "src/jank_test/reload" + (let [proc (ps/process ["jank" "--module-path" "src" "repl"]) + dependent-module-path "src/jank_test/reload/dependent.cljc" + dependency-module-path "src/jank_test/reload/dependency.cljc" + original-dependent-content (slurp dependent-module-path)] + (with-open [repl-writer (io/writer (:in proc)) + repl-reader (io/reader (:out proc))] (doto repl-writer - (send-command "(require '[jank-test.dependent :as d] :reload)\n") - (send-command "(d/hi)\n")) + (send-command "(require '[jank-test.reload.dependent :as d])\n") + (send-command "(d/first-fn)\n")) - (Thread/sleep 500) + ;; Wait for repl to start + (Thread/sleep 2000) (is (string/includes? (read-available repl-reader) - "Hi!"))) + "Hello")) - (testing "var removed from the source should still available" - (spit dependent-module-path original-dependent-content) - (doto repl-writer - (send-command "(require '[jank-test.dependent :as d] :reload)\n") - (send-command "(d/hi)\n")) + (send-command repl-writer "(d/hi)\n") (Thread/sleep 500) (is (string/includes? (read-available repl-reader) - "Hi!"))) - (testing "dependency module should not reload" - (doto dependency-module-path - (spit "\n" :append true) - (spit (pr-str '(defn hi [] "Hi in dependency!")) :append true)) + "Unable to resolve symbol 'jank-test.reload.dependent/hi'")) + + (testing "new var should be added to the module" + ;; Add a new var + (doto dependent-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi!")) :append true)) + + (doto repl-writer + (send-command "(require '[jank-test.reload.dependent :as d] :reload)\n") + (send-command "(d/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Hi!"))) + + (testing "var removed from the source should still available" + (spit dependent-module-path original-dependent-content) + (doto repl-writer + (send-command "(require '[jank-test.reload.dependent :as d] :reload)\n") + (send-command "(d/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Hi!"))) + (testing "dependency module should not reload" + (doto dependency-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi in dependency!")) :append true)) + (doto repl-writer + (send-command "(require '[jank-test.reload.dependency :as dep])\n") + (send-command "(require '[jank-test.reload.dependent :as d] :reload)\n") + (send-command "(dep/hi)\n")) + + (Thread/sleep 500) + + (is (string/includes? + (read-available repl-reader) + "Unable to resolve symbol 'jank-test.reload.dependency/hi'")))) + (ps/destroy proc)))) + +(deftest reload-all + (with-backup "src/jank_test/reload_all" + (let [proc (ps/process ["jank" "--module-path" "src" "repl"]) + dependent-module-path "src/jank_test/reload_all/dependent.cljc" + dependency-module-path "src/jank_test/reload_all/dependency.cljc"] + (with-open [repl-writer (io/writer (:in proc)) + repl-reader (io/reader (:out proc))] (doto repl-writer - (send-command "(require '[jank-test.dependency :as dep])\n") - (send-command "(require '[jank-test.dependent :as d] :reload)\n") - (send-command "(dep/hi)\n")) + (send-command "(require '[jank-test.reload-all.dependent :as d])\n") + (send-command "(d/first-fn)\n")) - (Thread/sleep 500) + ;; Wait for repl to start + (Thread/sleep 2000) + (is (string/includes? + (read-available repl-reader) + "Hello")) + (send-command repl-writer "(d/hi)\n") + + (Thread/sleep 500) (is (string/includes? (read-available repl-reader) - "Unable to resolve symbol 'jank-test.dependency/hi'")))) - (ps/destroy proc))) + "Unable to resolve symbol 'jank-test.reload-all.dependent/hi'")) + + (testing "module is reloaded" + ;; Add a new var + (doto dependent-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi!")) :append true)) + + (doto repl-writer + (send-command "(require '[jank-test.reload-all.dependent :as d] :reload-all)\n") + (send-command "(d/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Hi!"))) + + (testing "module's dependency is also reloaded" + (doto dependency-module-path + (spit "\n" :append true) + (spit (pr-str '(defn hi [] "Hi in dependency!")) :append true)) + + (doto repl-writer + (send-command "(require '[jank-test.reload-all.dependency :as dep])\n") + (send-command "(dep/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Unable to resolve symbol 'jank-test.reload-all.dependency/hi'")) + + (doto repl-writer + (send-command "(require '[jank-test.reload-all.dependent :as d] :reload-all)\n") + (send-command "(dep/hi)\n")) + + (Thread/sleep 500) + (is (string/includes? + (read-available repl-reader) + "Hi in dependency!")))) + (ps/destroy proc)))) (defn -main [] (System/exit diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc deleted file mode 100644 index c5a2d8441..000000000 --- a/compiler+runtime/test/bash/module/reload/src/jank_test/dependent.cljc +++ /dev/null @@ -1,5 +0,0 @@ -(ns jank-test.dependent - (:require [jank-test.dependency :as dep])) - -(defn first-fn [] - (dep/say-hello!)) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependency.cljc similarity index 56% rename from compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc rename to compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependency.cljc index a450f6441..b96cc15a4 100644 --- a/compiler+runtime/test/bash/module/reload/src/jank_test/dependency.cljc +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependency.cljc @@ -1,4 +1,4 @@ -(ns jank-test.dependency) +(ns jank-test.reload.dependency) (defn say-hello! [] (println "Hello")) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependent.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependent.cljc new file mode 100644 index 000000000..b123de1f9 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/reload/dependent.cljc @@ -0,0 +1,5 @@ +(ns jank-test.reload.dependent + (:require [jank-test.reload.dependency :as dep])) + +(defn first-fn [] + (dep/say-hello!)) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependency.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependency.cljc new file mode 100644 index 000000000..3e256c97b --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependency.cljc @@ -0,0 +1,4 @@ +(ns jank-test.reload-all.dependency) + +(defn say-hello! [] + (println "Hello")) diff --git a/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependent.cljc b/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependent.cljc new file mode 100644 index 000000000..315380ba3 --- /dev/null +++ b/compiler+runtime/test/bash/module/reload/src/jank_test/reload_all/dependent.cljc @@ -0,0 +1,5 @@ +(ns jank-test.reload-all.dependent + (:require [jank-test.reload-all.dependency :as dep])) + +(defn first-fn [] + (dep/say-hello!)) From 3c95f6ec56c5c5f52dae7e2015ba73a1f18d59a2 Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 14 Dec 2025 21:24:44 +0530 Subject: [PATCH 9/9] chore: adds todo to remove context::loaded_module_in_order --- compiler+runtime/include/cpp/jank/runtime/context.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler+runtime/include/cpp/jank/runtime/context.hpp b/compiler+runtime/include/cpp/jank/runtime/context.hpp index 0f23616f1..eee94c75b 100644 --- a/compiler+runtime/include/cpp/jank/runtime/context.hpp +++ b/compiler+runtime/include/cpp/jank/runtime/context.hpp @@ -144,6 +144,11 @@ namespace jank::runtime /* TODO: This needs to be a dynamic var. */ native_unordered_map> module_dependencies; + + /* TODO: Remove this in favor of calling module's `jank_load` functions + * on demand. At the moment it is being used to load all the compiled modules + * in ahead of time compiled binaries at startup (which is not an ideal way to + * achieve that). */ folly::Synchronized> loaded_modules_in_order; jtl::immutable_string binary_cache_dir; module::loader module_loader;