|
4 | 4 | [org.graalvm.polyglot Context]) |
5 | 5 | (:require [clojure.java.io :as io])) |
6 | 6 |
|
7 | | -(defonce ^{:dynamic true :private true} *cjs-cwd* (str (System/getProperty "user.dir") |
8 | | - "/node_modules")) |
| 7 | +(defonce ^{:dynamic true :private true} *cjs-cwd* |
| 8 | + (str (System/getProperty "user.dir") |
| 9 | + "/node_modules")) |
9 | 10 |
|
10 | 11 | (defn default-builder |
11 | 12 | "Return a `org.graalvm.polyglot.Context$Builder` object with necessary options set for js4clj to function properly. |
|
31 | 32 | "js.commonjs-require-cwd" *cjs-cwd*})) |
32 | 33 | (.allowIO true))) |
33 | 34 |
|
34 | | -(defonce ^{:dynamic true |
35 | | - :doc "An atom wrapping a instance of `org.graalvm.polyglot.Context$Builder`, used for all the operations "} |
36 | | - *context* (atom (.build (default-builder)))) |
| 35 | +(def ^:dynamic *context-per-thread* |
| 36 | + "Whether to use thread local context when doing js4clj operations." |
| 37 | + false) |
37 | 38 |
|
38 | | -(defn initialize-context! |
39 | | - "Initialize the `*context*` variable with a lambda taking `default-builder` as an argument. |
| 39 | +(def ^:dynamic *customize-builder* |
| 40 | + "A fn takes a `Context$Builder` as an argument and returns a `Context$Builder`" |
| 41 | + nil) |
40 | 42 |
|
41 | | - `defaut-builder` is an `org.graalvm.polyglot.Context$Builder` object with necessary option set for js4clj to function properly. |
42 | | - The lambda should return a `org.graalvm.polyglot.Context` object to initialize the *context* variable. |
| 43 | +(def ^:private builtin-after-init |
| 44 | + (fn [ctx] |
| 45 | + (.eval ctx "js" "globalThis.global = this") |
| 46 | + (.eval ctx "js" "globalThis.process = {}") |
| 47 | + (.eval ctx "js" "globalThis.process.env = {}"))) |
43 | 48 |
|
44 | | - Example: |
45 | | - ```clojure |
46 | | - (initialize-context! (fn [builder] |
47 | | - (.build builder))) |
48 | | - ``` |
49 | | - " |
50 | | - [build-fn] |
51 | | - (let [default-builder (default-builder)] |
52 | | - (reset! *context* (build-fn default-builder)))) |
| 49 | +(def ^:dynamic *after-init-hook* |
| 50 | + "A fn takes no arguments, called after a context is created, with *context* bound to the newly created context." |
| 51 | + nil) |
| 52 | + |
| 53 | +(declare *context*) |
| 54 | + |
| 55 | +(defn context-new |
| 56 | + "Create a context, respect `customize-builder` and `after-init-hook`" |
| 57 | + [] |
| 58 | + (let [context (-> (default-builder) |
| 59 | + (or *customize-builder* identity) |
| 60 | + (.build))] |
| 61 | + (when builtin-after-init (builtin-after-init context)) |
| 62 | + (when *after-init-hook* |
| 63 | + (binding [*context* (ref context)] |
| 64 | + (*after-init-hook*))) |
| 65 | + context)) |
| 66 | + |
| 67 | +(def ^:private global-context (atom nil)) |
53 | 68 |
|
54 | | -(.eval @*context* "js" "globalThis.global = this") |
55 | | -(.eval @*context* "js" "globalThis.process = {}") |
56 | | -(.eval @*context* "js" "globalThis.process.env = {}") |
| 69 | +(def ^:private thread-local-context (ThreadLocal/withInitial (fn [] (context-new)))) |
| 70 | + |
| 71 | +(def ^{:dynamic true |
| 72 | + :doc |
| 73 | + "When `*context-per-thread*` is false, deref it returns the global context, |
| 74 | + When `*context-per-thread*` is true, deref it returns the thread local context. |
| 75 | +
|
| 76 | + In any case, if it is not already initialized, initialize it with `context-new` |
| 77 | +
|
| 78 | + You can bind it with an IDeref instance to manage context."} |
| 79 | + *context* (reify clojure.lang.IDeref |
| 80 | + (deref [_] |
| 81 | + (if *context-per-thread* |
| 82 | + (.get thread-local-context) |
| 83 | + (if (not (nil? @global-context)) |
| 84 | + @global-context |
| 85 | + (swap! global-context |
| 86 | + (fn [old] |
| 87 | + (if old |
| 88 | + old |
| 89 | + (context-new))))))))) |
| 90 | + |
| 91 | +(defn reinitialize-context! |
| 92 | + "If `*context-per-thread*` is false, reinitialize the global `*context*` with `context-new`. |
| 93 | + If `*context-per-thread*` is true, reinitialize the thread-local context with `context-new`. |
| 94 | +
|
| 95 | + Note: Contexts are automatically initialize upon first usage if not already initialized. |
| 96 | + " |
| 97 | + [] |
| 98 | + (if *context-per-thread* |
| 99 | + (.set thread-local-context (context-new)) |
| 100 | + (reset! global-context (context-new)))) |
0 commit comments