|
31 | 31 | [clojure.tools.reader.reader-types :as readers])
|
32 | 32 | (:import (clojure.lang IFn DynamicClassLoader Atom)))
|
33 | 33 |
|
| 34 | +(defn write-class |
| 35 | + "(λ ClassName → Bytecode) → Nil |
| 36 | +
|
| 37 | + Writes the given bytecode to a file named by the ClassName and |
| 38 | + *compile-path*. Requires that *compile-path* be set. Returns Nil." |
| 39 | + [name bytecode] |
| 40 | + {:pre [(bound? #'clojure.core/*compile-path*)]} |
| 41 | + (let [path (str *compile-path* "/" name ".class") |
| 42 | + file (io/file path)] |
| 43 | + (.mkdirs (io/file (.getParent file))) |
| 44 | + (with-open [w (java.io.FileOutputStream. path)] |
| 45 | + (.write w bytecode))) |
| 46 | + nil) |
| 47 | + |
34 | 48 | (defn compile-and-load
|
35 | 49 | ([class-ast]
|
36 | 50 | (compile-and-load class-ast (clojure.lang.RT/makeClassLoader)))
|
37 | 51 | ([{:keys [class-name] :as class-ast} class-loader]
|
38 |
| - (.defineClass ^DynamicClassLoader class-loader class-name (t/-compile class-ast) nil))) |
| 52 | + (let [bytecode (t/-compile class-ast)] |
| 53 | + (when (and (bound? #'clojure.core/*compile-files*) |
| 54 | + *compile-files*) |
| 55 | + (write-class class-name bytecode)) |
| 56 | + (.defineClass ^DynamicClassLoader class-loader class-name bytecode nil)))) |
39 | 57 |
|
40 | 58 |
|
41 | 59 | (def passes (into (disj a/default-passes #'trim)
|
|
77 | 95 | :analyze-opts :- (Option analyze-options-map)
|
78 | 96 | An options map that will be passed to the analyzer. The keys which
|
79 | 97 | are significant in this map are documented in the t.a.jvm/analyze
|
80 |
| - docstring." |
| 98 | + docstring. |
| 99 | +
|
| 100 | + :class-loader :- (Option ClassLoader) |
| 101 | + An optional classloader into which compiled functions will be |
| 102 | + injected. If not provided, a new Clojure classloader will be |
| 103 | + used. If a class loader is provided here, one need not be provided |
| 104 | + in eval-opts. |
| 105 | +
|
| 106 | + :compile-files :- (Option Bool) |
| 107 | + Enables or disables writing classfiles for generated classes. False |
| 108 | + by default." |
81 | 109 |
|
82 | 110 | ([form]
|
83 | 111 | (eval form {}))
|
84 |
| - ([form {:keys [debug? emit-opts class-loader analyze-opts] |
85 |
| - :or {debug? false |
86 |
| - emit-opts {} |
87 |
| - analyze-opts a/default-passes-opts |
88 |
| - class-loader (clojure.lang.RT/makeClassLoader)} |
| 112 | + ([form {:keys [debug? emit-opts class-loader analyze-opts compile-files] |
| 113 | + :or {debug? false |
| 114 | + emit-opts {} |
| 115 | + analyze-opts a/default-passes-opts |
| 116 | + compile-files (if (bound? #'clojure.core/*compile-files*) |
| 117 | + *compile-files* false) |
| 118 | + class-loader (clojure.lang.RT/makeClassLoader)} |
89 | 119 | :as options}]
|
90 | 120 | {:pre [(instance? DynamicClassLoader class-loader)]}
|
91 | 121 | (let [mform (binding [macroexpand-1 a/macroexpand-1]
|
|
98 | 128 | (doseq [expr statements]
|
99 | 129 | (eval expr options))
|
100 | 130 | (eval ret options))
|
101 |
| - (binding [a/run-passes run-passes] |
| 131 | + (binding [a/run-passes run-passes |
| 132 | + *compile-files* compile-files] |
102 | 133 | (let [cs (-> (a/analyze `(^:once fn* [] ~mform) (a/empty-env) analyze-opts)
|
103 | 134 | (e/emit-classes (merge {:debug? debug?} emit-opts)))
|
104 | 135 | classes (mapv #(compile-and-load % class-loader) cs)]
|
|
130 | 161 | An optional classloader into which compiled functions will be
|
131 | 162 | injected. If not provided, a new Clojure classloader will be
|
132 | 163 | used. If a class loader is provided here, one need not be provided
|
133 |
| - in eval-opts." |
| 164 | + in eval-opts. |
| 165 | +
|
| 166 | + :compile-files :- (Option Bool) |
| 167 | + Enables or disables writing classfiles for generated classes. False |
| 168 | + by default." |
134 | 169 |
|
135 | 170 | ([res]
|
136 | 171 | (load res {}))
|
137 |
| - ([res {:keys [debug? eval-opts class-loader] |
138 |
| - :or {debug? false |
139 |
| - eval-opts {} |
140 |
| - class-loader (clojure.lang.RT/makeClassLoader)} |
| 172 | + ([res {:keys [debug? eval-opts class-loader compile-files] |
| 173 | + :or {debug? false |
| 174 | + eval-opts {} |
| 175 | + compile-files (if (bound? #'clojure.core/*compile-files*) |
| 176 | + *compile-files* false) |
| 177 | + class-loader (clojure.lang.RT/makeClassLoader)} |
141 | 178 | :as options}]
|
142 | 179 | (let [p (str (apply str (replace {\. \/ \- \_} res)) ".clj")
|
143 | 180 | eof (Object.)
|
|
146 | 183 | (subs (str (root-directory (ns-name *ns*)) "/" p) 1))
|
147 | 184 | file (-> p io/resource io/reader slurp)
|
148 | 185 | reader (readers/indexing-push-back-reader file 1 p)]
|
149 |
| - (binding [*ns* *ns* |
150 |
| - *file* p] |
| 186 | + (binding [*ns* *ns* |
| 187 | + *file* p |
| 188 | + *compile-files* compile-files] |
151 | 189 | (loop []
|
152 | 190 | (let [form (r/read reader false eof)]
|
153 | 191 | (when (not= eof form)
|
154 | 192 | (eval form (merge eval-opts
|
155 | 193 | (when class-loader
|
156 |
| - {:class-loader class-loader}))) |
| 194 | + {:class-loader class-loader |
| 195 | + :compile-files compile-files}))) |
157 | 196 | (recur))))
|
158 | 197 | nil))))
|
0 commit comments