Skip to content

Commit ccd6409

Browse files
author
dnolen
committed
support AOT compilation under Java 7 by conditionally compiling Nashorn support
1 parent 8f817ac commit ccd6409

File tree

2 files changed

+195
-186
lines changed

2 files changed

+195
-186
lines changed

src/main/clojure/cljs/repl/nashorn.clj

+187-186
Original file line numberDiff line numberDiff line change
@@ -12,194 +12,195 @@
1212
[clojure.stacktrace]
1313
[cljs.analyzer :as ana]
1414
[cljs.env :as env]
15+
[cljs.util :as util]
1516
[cljs.repl :as repl]
1617
[cljs.compiler :as comp]
1718
[cljs.closure :as closure])
1819
(:import [java.io File]
1920
[javax.script ScriptEngine ScriptEngineManager ScriptException ScriptEngineFactory]
20-
[jdk.nashorn.api.scripting NashornException]))
21-
22-
;; Nashorn Clojurescript repl binding.
23-
;;
24-
;; Uses the Nashorn load() function to load Javascript files into the script engine.
25-
;;
26-
;; Nashorn's load() function docs:
27-
;; http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html
28-
29-
(comment
30-
(ns init-repl-test
31-
(:require [cljs.repl]
32-
[cljs.repl.nashorn]))
33-
34-
(cljs.repl/repl (cljs.repl.nashorn/repl-env)
35-
:output-dir "resources/public/compiled"
36-
:cache-analysis true)
37-
)
38-
39-
;; Implementation
40-
41-
(defn create-engine
42-
([] (create-engine nil))
43-
([{:keys [code-cache] :or {code-cache true}}]
44-
(let [args (when code-cache ["-pcc"])
45-
factories (.getEngineFactories (ScriptEngineManager.))
46-
factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")]
47-
(if-let [engine (if-not (empty? args)
48-
(.getScriptEngine ^ScriptEngineFactory factory (into-array args))
49-
(.getScriptEngine ^ScriptEngineFactory factory))]
50-
(let [context (.getContext engine)]
51-
(.setWriter context *out*)
52-
(.setErrorWriter context *err*)
53-
engine)
54-
(throw (IllegalArgumentException.
55-
"Cannot find the Nashorn script engine, use a JDK version 8 or higher."))))))
56-
57-
(defn eval-str [^ScriptEngine engine ^String s]
58-
(.eval engine s))
59-
60-
(defn eval-resource
61-
"Evaluate a file on the classpath in the engine."
62-
[engine path debug]
63-
(let [r (io/resource path)]
64-
(eval-str engine (slurp r))
65-
(when debug (println "loaded: " path))))
66-
67-
(defn init-engine [engine output-dir debug]
68-
(eval-resource engine "goog/base.js" debug)
69-
(eval-resource engine "goog/deps.js" debug)
70-
(eval-str engine "var global = this") ; required by React
71-
(eval-str engine
72-
(format
73-
(str "var nashorn_load = function(path) {"
74-
" var outputPath = \"%s\" + \"/\" + path;"
75-
(when debug " print(\"loading: \" + outputPath) ; ")
76-
" load(outputPath);"
77-
"};")
78-
output-dir))
79-
(eval-str engine
80-
(str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {"
81-
" nashorn_load(\"goog/\" + path);"
82-
" return true;"
83-
"};"))
84-
(eval-str engine "goog.global.isProvided_ = function(name) { return false; };")
85-
engine)
86-
87-
(defn load-js-file [engine file]
88-
(eval-str engine (format "nashorn_load(\"%s\");" file)))
89-
90-
;; Create a minimal build of Clojurescript from the core library.
91-
;; Copied from clj.cljs.repl.node.
92-
(defn bootstrap-repl [engine output-dir opts]
93-
(env/ensure
94-
(let [deps-file ".nashorn_repl_deps.js"
95-
core (io/resource "cljs/core.cljs")
96-
core-js (closure/compile core
97-
(assoc opts
98-
:output-file (closure/src-file->target-file core)))
99-
deps (closure/add-dependencies opts core-js)]
100-
;; output unoptimized code and the deps file
101-
;; for all compiled namespaces
102-
(apply closure/output-unoptimized
103-
(assoc opts :output-to (.getPath (io/file output-dir deps-file)))
104-
deps)
105-
;; load the deps file so we can goog.require cljs.core etc.
106-
(load-js-file engine deps-file))))
107-
108-
(defn load-ns [engine ns]
109-
(eval-str engine
110-
(format "goog.require(\"%s\");" (comp/munge (first ns)))))
111-
112-
;; Nashorn script stacktraces have a relative path which includes the output-dir
113-
(defn- strip-file-name [^String file-name output-dir]
114-
(let [with-slash (str output-dir "/")]
115-
(if (.startsWith file-name with-slash)
116-
(string/replace-first file-name with-slash "")
117-
file-name)))
118-
119-
(def repl-filename "<cljs repl>")
120-
121-
(defrecord NashornEnv [engine debug]
122-
repl/IReplEnvOptions
123-
(-repl-options [this]
124-
{:output-dir ".cljs_nashorn_repl"})
125-
repl/IJavaScriptEnv
126-
(-setup [this {:keys [output-dir bootstrap output-to] :as opts}]
127-
(init-engine engine output-dir debug)
128-
(let [env (ana/empty-env)]
129-
(if output-to
130-
(load-js-file engine output-to)
131-
(bootstrap-repl engine output-dir opts))
132-
(repl/evaluate-form this env repl-filename
133-
'(do
134-
(.require js/goog "cljs.core")
135-
(set! *print-newline* false)
136-
(set! *print-fn* js/print)))
137-
;; monkey-patch goog.isProvided_ to suppress useless errors
138-
(repl/evaluate-form this env repl-filename
139-
'(set! js/goog.isProvided_ (fn [ns] false)))
140-
;; monkey-patch goog.require to be more sensible
141-
(repl/evaluate-form this env repl-filename
142-
'(do
143-
(set! *loaded-libs* #{"cljs.core"})
144-
(set! (.-require js/goog)
145-
(fn [name reload]
146-
(when (or (not (contains? *loaded-libs* name)) reload)
147-
(set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
148-
(js/CLOSURE_IMPORT_SCRIPT
149-
(aget (.. js/goog -dependencies_ -nameToPath) name)))))))))
150-
(-evaluate [{engine :engine :as this} filename line js]
151-
(when debug (println "Evaluating: " js))
152-
(try
153-
{:status :success
154-
:value (if-let [r (eval-str engine js)] (.toString r) "")}
155-
(catch ScriptException e
156-
(let [^Throwable root-cause (clojure.stacktrace/root-cause e)]
157-
{:status :exception
158-
:value (.getMessage root-cause)
159-
:stacktrace (NashornException/getScriptStackString root-cause)}))
160-
(catch Throwable e
161-
(let [^Throwable root-cause (clojure.stacktrace/root-cause e)]
162-
{:status :exception
163-
:value (.getMessage root-cause)
164-
:stacktrace
165-
(apply str
166-
(interpose "\n"
167-
(map str
168-
(.getStackTrace root-cause))))}))))
169-
(-load [{engine :engine :as this} ns url]
170-
(load-ns engine ns))
171-
(-tear-down [this])
172-
repl/IParseStacktrace
173-
(-parse-stacktrace [this frames-str ret {output-dir :output-dir}]
174-
(vec
175-
(map
176-
(fn [frame-str]
177-
(let [frame-str (string/replace frame-str #"\s+at\s+" "")
178-
[function file-and-line] (string/split frame-str #"\s+")
179-
[file-part line-part] (string/split file-and-line #":")]
180-
{:file (string/replace (.substring file-part 1)
181-
(str output-dir File/separator) "")
182-
:function function
183-
:line (Integer/parseInt
184-
(.substring line-part 0 (dec (.length line-part))))
185-
:column 0}))
186-
(string/split frames-str #"\n"))))
187-
repl/IParseError
188-
(-parse-error [_ err _]
189-
(update-in err [:stacktrace]
190-
(fn [st]
191-
(string/join "\n" (drop 1 (string/split st #"\n")))))))
192-
193-
(defn repl-env* [{:keys [debug] :as opts}]
194-
(let [engine (create-engine opts)]
195-
(merge
196-
(NashornEnv. engine debug)
197-
opts)))
198-
199-
(defn repl-env
200-
"Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript."
201-
[& {:as opts}]
202-
(repl-env* opts))
203-
204-
(defn -main []
205-
(repl/repl (repl-env)))
21+
[com.google.common.base Throwables]))
22+
23+
(util/compile-if (Class/forName "jdk.nashorn.api.scripting.NashornException")
24+
(do
25+
(import 'jdk.nashorn.api.scripting.NashornException)
26+
;; Implementation
27+
28+
(defn create-engine
29+
([] (create-engine nil))
30+
([{:keys [code-cache] :or {code-cache true}}]
31+
(let [args (when code-cache ["-pcc"])
32+
factories (.getEngineFactories (ScriptEngineManager.))
33+
factory (get (zipmap (map #(.getEngineName %) factories) factories) "Oracle Nashorn")]
34+
(if-let [engine (if-not (empty? args)
35+
(.getScriptEngine ^ScriptEngineFactory factory (into-array args))
36+
(.getScriptEngine ^ScriptEngineFactory factory))]
37+
(let [context (.getContext engine)]
38+
(.setWriter context *out*)
39+
(.setErrorWriter context *err*)
40+
engine)
41+
(throw (IllegalArgumentException.
42+
"Cannot find the Nashorn script engine, use a JDK version 8 or higher."))))))
43+
44+
(defn eval-str [^ScriptEngine engine ^String s]
45+
(.eval engine s))
46+
47+
(defn eval-resource
48+
"Evaluate a file on the classpath in the engine."
49+
[engine path debug]
50+
(let [r (io/resource path)]
51+
(eval-str engine (slurp r))
52+
(when debug (println "loaded: " path))))
53+
54+
(defn init-engine [engine output-dir debug]
55+
(eval-resource engine "goog/base.js" debug)
56+
(eval-resource engine "goog/deps.js" debug)
57+
(eval-str engine "var global = this") ; required by React
58+
(eval-str engine
59+
(format
60+
(str "var nashorn_load = function(path) {"
61+
" var outputPath = \"%s\" + \"/\" + path;"
62+
(when debug " print(\"loading: \" + outputPath) ; ")
63+
" load(outputPath);"
64+
"};")
65+
output-dir))
66+
(eval-str engine
67+
(str "goog.global.CLOSURE_IMPORT_SCRIPT = function(path) {"
68+
" nashorn_load(\"goog/\" + path);"
69+
" return true;"
70+
"};"))
71+
(eval-str engine "goog.global.isProvided_ = function(name) { return false; };")
72+
engine)
73+
74+
(defn load-js-file [engine file]
75+
(eval-str engine (format "nashorn_load(\"%s\");" file)))
76+
77+
;; Create a minimal build of Clojurescript from the core library.
78+
;; Copied from clj.cljs.repl.node.
79+
(defn bootstrap-repl [engine output-dir opts]
80+
(env/ensure
81+
(let [deps-file ".nashorn_repl_deps.js"
82+
core (io/resource "cljs/core.cljs")
83+
core-js (closure/compile core
84+
(assoc opts
85+
:output-file (closure/src-file->target-file core)))
86+
deps (closure/add-dependencies opts core-js)]
87+
;; output unoptimized code and the deps file
88+
;; for all compiled namespaces
89+
(apply closure/output-unoptimized
90+
(assoc opts :output-to (.getPath (io/file output-dir deps-file)))
91+
deps)
92+
;; load the deps file so we can goog.require cljs.core etc.
93+
(load-js-file engine deps-file))))
94+
95+
(defn load-ns [engine ns]
96+
(eval-str engine
97+
(format "goog.require(\"%s\");" (comp/munge (first ns)))))
98+
99+
;; Nashorn script stacktraces have a relative path which includes the output-dir
100+
(defn- strip-file-name [^String file-name output-dir]
101+
(let [with-slash (str output-dir "/")]
102+
(if (.startsWith file-name with-slash)
103+
(string/replace-first file-name with-slash "")
104+
file-name)))
105+
106+
(def repl-filename "<cljs repl>")
107+
108+
(defrecord NashornEnv [engine debug]
109+
repl/IReplEnvOptions
110+
(-repl-options [this]
111+
{:output-dir ".cljs_nashorn_repl"})
112+
repl/IJavaScriptEnv
113+
(-setup [this {:keys [output-dir bootstrap output-to] :as opts}]
114+
(init-engine engine output-dir debug)
115+
(let [env (ana/empty-env)]
116+
(if output-to
117+
(load-js-file engine output-to)
118+
(bootstrap-repl engine output-dir opts))
119+
(repl/evaluate-form this env repl-filename
120+
'(do
121+
(.require js/goog "cljs.core")
122+
(set! *print-newline* false)
123+
(set! *print-fn* js/print)))
124+
;; monkey-patch goog.isProvided_ to suppress useless errors
125+
(repl/evaluate-form this env repl-filename
126+
'(set! js/goog.isProvided_ (fn [ns] false)))
127+
;; monkey-patch goog.require to be more sensible
128+
(repl/evaluate-form this env repl-filename
129+
'(do
130+
(set! *loaded-libs* #{"cljs.core"})
131+
(set! (.-require js/goog)
132+
(fn [name reload]
133+
(when (or (not (contains? *loaded-libs* name)) reload)
134+
(set! *loaded-libs* (conj (or *loaded-libs* #{}) name))
135+
(js/CLOSURE_IMPORT_SCRIPT
136+
(aget (.. js/goog -dependencies_ -nameToPath) name)))))))))
137+
(-evaluate [{engine :engine :as this} filename line js]
138+
(when debug (println "Evaluating: " js))
139+
(try
140+
{:status :success
141+
:value (if-let [r (eval-str engine js)] (.toString r) "")}
142+
(catch ScriptException e
143+
(let [^Throwable root-cause (clojure.stacktrace/root-cause e)]
144+
{:status :exception
145+
:value (.getMessage root-cause)
146+
:stacktrace (NashornException/getScriptStackString root-cause)}))
147+
(catch Throwable e
148+
(let [^Throwable root-cause (clojure.stacktrace/root-cause e)]
149+
{:status :exception
150+
:value (.getMessage root-cause)
151+
:stacktrace
152+
(apply str
153+
(interpose "\n"
154+
(map str
155+
(.getStackTrace root-cause))))}))))
156+
(-load [{engine :engine :as this} ns url]
157+
(load-ns engine ns))
158+
(-tear-down [this])
159+
repl/IParseStacktrace
160+
(-parse-stacktrace [this frames-str ret {output-dir :output-dir}]
161+
(vec
162+
(map
163+
(fn [frame-str]
164+
(let [frame-str (string/replace frame-str #"\s+at\s+" "")
165+
[function file-and-line] (string/split frame-str #"\s+")
166+
[file-part line-part] (string/split file-and-line #":")]
167+
{:file (string/replace (.substring file-part 1)
168+
(str output-dir File/separator) "")
169+
:function function
170+
:line (Integer/parseInt
171+
(.substring line-part 0 (dec (.length line-part))))
172+
:column 0}))
173+
(string/split frames-str #"\n"))))
174+
repl/IParseError
175+
(-parse-error [_ err _]
176+
(update-in err [:stacktrace]
177+
(fn [st]
178+
(string/join "\n" (drop 1 (string/split st #"\n")))))))
179+
180+
(defn repl-env* [{:keys [debug] :as opts}]
181+
(let [engine (create-engine opts)]
182+
(merge
183+
(NashornEnv. engine debug)
184+
opts)))
185+
186+
(defn repl-env
187+
"Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript."
188+
[& {:as opts}]
189+
(repl-env* opts))
190+
191+
(defn -main []
192+
(repl/repl (repl-env))))
193+
(do
194+
(defn repl-env* [{:keys [debug] :as opts}]
195+
(throw (ex-info "Nashorn not supported" {:type :repl-error})))
196+
197+
(defn repl-env
198+
"Create a Nashorn repl-env for use with the repl/repl* method in Clojurescript."
199+
[& {:as opts}]
200+
(throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error})))
201+
202+
(defn -main []
203+
(throw (ex-info "Nashorn not available under this Java runtime" {:type :repl-error})))))
204+
205+
206+

src/main/clojure/cljs/util.cljc

+8
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,11 @@
173173
(debug-prn (str ~msg ", elapsed time:") (/ (double (- (. System (nanoTime)) start#)) 1000000.0) "msecs")
174174
ret#)
175175
~expr)))
176+
177+
(defmacro compile-if
178+
([exp then] `(compile-if ~exp ~then nil))
179+
([exp then else]
180+
(if (try (eval exp)
181+
(catch Throwable _ false))
182+
`(do ~then)
183+
`(do ~else))))

0 commit comments

Comments
 (0)