Skip to content

Commit a095d30

Browse files
mfikesswannodette
authored andcommitted
CLJS-2660: Add cljs.core/eval which, delegates to an overridable *eval*
1 parent ade4ab7 commit a095d30

4 files changed

Lines changed: 102 additions & 69 deletions

File tree

src/main/cljs/cljs/core.cljs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11510,3 +11510,17 @@ reduces them without incurring seq initialization"
1151011510
(.println (.-error system) (.join (into-array args) "")))))))
1151111511

1151211512
(maybe-enable-print!)
11513+
11514+
(defonce
11515+
^{:doc "Runtime environments may provide a way to evaluate ClojureScript
11516+
forms. Whatever function *eval* is bound to will be passed any forms which
11517+
should be evaluated." :dynamic true}
11518+
*eval*
11519+
(fn [_]
11520+
(throw (ex-info "cljs.core/*eval* not bound" {}))))
11521+
11522+
(defn eval
11523+
"Evaluates the form data structure (not text!) and returns the result.
11524+
Delegates to cljs.core/*eval*."
11525+
[form]
11526+
(*eval* form))

src/main/cljs/cljs/js.cljs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
; You must not remove this notice, or any other, from this software.
88

99
(ns cljs.js
10-
(:refer-clojure :exclude [require])
10+
(:refer-clojure :exclude [require eval])
1111
(:require-macros [cljs.js :refer [dump-core]]
1212
[cljs.env.macros :as env])
1313
(:require [clojure.string :as string]
@@ -786,10 +786,13 @@
786786
;; -----------------------------------------------------------------------------
787787
;; Eval
788788

789+
(declare clear-fns!)
790+
789791
(defn- eval* [bound-vars form opts cb]
790792
(let [the-ns (or (:ns opts) 'cljs.user)
791793
bound-vars (cond-> (merge bound-vars {:*cljs-ns* the-ns})
792794
(:source-map opts) (assoc :*sm-data* (sm-data)))]
795+
(clear-fns!)
793796
(binding [env/*compiler* (:*compiler* bound-vars)
794797
*eval-fn* (:*eval-fn* bound-vars)
795798
ana/*cljs-ns* (:*cljs-ns* bound-vars)
@@ -1037,6 +1040,7 @@
10371040
(:source-map opts) (assoc :*sm-data* (sm-data)))
10381041
aname (cond-> name (:macros-ns opts) ana/macro-ns-name)]
10391042
(when (:verbose opts) (debug-prn "Evaluating" name))
1043+
(clear-fns!)
10401044
(trampoline
10411045
(fn compile-loop [ns]
10421046
(binding [env/*compiler* (:*compiler* bound-vars)
@@ -1193,6 +1197,61 @@
11931197
:*eval-fn* (or (:eval opts) *eval-fn*)}
11941198
source name opts cb)))
11951199

1200+
;;; Support for cljs.core/eval
1201+
1202+
;; The following volatiles and fns set up a scheme to
1203+
;; emit function values into JavaScript as numeric
1204+
;; references that are looked up. Needed to implement eval.
1205+
1206+
(defonce ^:private fn-index (volatile! 0))
1207+
(defonce ^:private fn-refs (volatile! {}))
1208+
1209+
(defn- clear-fns!
1210+
"Clears saved functions."
1211+
[]
1212+
(vreset! fn-refs {}))
1213+
1214+
(defn- put-fn
1215+
"Saves a function, returning a numeric representation."
1216+
[f]
1217+
(let [n (vswap! fn-index inc)]
1218+
(vswap! fn-refs assoc n f)
1219+
n))
1220+
1221+
(defn- get-fn
1222+
"Gets a function, given its numeric representation."
1223+
[n]
1224+
(get @fn-refs n))
1225+
1226+
(defn- emit-fn [f]
1227+
(print "cljs.js.get_fn(" (put-fn f) ")"))
1228+
1229+
(defmethod comp/emit-constant js/Function
1230+
[f]
1231+
(emit-fn f))
1232+
1233+
(defmethod comp/emit-constant cljs.core/Var
1234+
[f]
1235+
(emit-fn f))
1236+
1237+
(defn- eval-impl
1238+
([form]
1239+
(eval-impl form (.-name *ns*)))
1240+
([form ns]
1241+
(let [result (atom nil)]
1242+
(let [st env/*compiler*]
1243+
(eval st form
1244+
{:ns ns
1245+
:context :expr
1246+
:def-emits-var true}
1247+
(fn [{:keys [value error]}]
1248+
(if error
1249+
(throw error)
1250+
(reset! result value)))))
1251+
@result)))
1252+
1253+
(set! *eval* eval-impl)
1254+
11961255
(comment
11971256
(require '[cljs.js :as cljs]
11981257
'[cljs.analyzer :as ana])

src/test/cljs/cljs/eval_test.cljs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(ns cljs.eval-test
2+
(:require [cljs.test :refer [deftest is]]))
3+
4+
;;; This test namespace should only be loaded by environments that set up cljs.core/*eval*
5+
6+
(def addition-list-1 (list + 1 2))
7+
(def addition-list-2 (list + 1 'a))
8+
(def addition-list-3 (list (fn [a b] (+ a b)) 1 2))
9+
(defn square [x] (* x x))
10+
(defn cube [x] (* x x x))
11+
12+
(deftest test-eval
13+
(is (== 1 (eval 1)))
14+
(is (== 3 (eval '(+ 1 2))))
15+
(is (== 17 (eval '(let [a 10] (+ 3 4 a)))))
16+
(is (= 'a (:name (meta (eval '(def a 3))))))
17+
(is (== 3 (eval 'a)))
18+
(is (== 3 (eval addition-list-1)))
19+
(is (== 4 (eval addition-list-2)))
20+
(is (== 13 (eval (concat addition-list-1 [10]))))
21+
(is (= 'lucky-number (:name (meta (eval (list 'def 'lucky-number (concat addition-list-1 [20])))))))
22+
(is (== 23 (eval 'lucky-number)))
23+
(is (== 64 ((eval (list comp square cube)) 2)))
24+
(is (== 5 ((eval (eval +)) 2 3)))
25+
(is (== 3 (eval addition-list-3)))
26+
(is (== 4 (eval (list #'inc 3)))))

src/test/self/self_parity/test.cljs

Lines changed: 2 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -258,73 +258,6 @@
258258
(print "caused by: ")
259259
(recur cause)))))
260260

261-
;; The following volatiles and fns set up a scheme to
262-
;; emit function values into JavaScript as numeric
263-
;; references that are looked up. Needed to implement eval.
264-
265-
(defonce ^:private fn-index (volatile! 0))
266-
(defonce ^:private fn-refs (volatile! {}))
267-
268-
(defn- put-fn
269-
"Saves a function, returning a numeric representation."
270-
[f]
271-
(let [n (vswap! fn-index inc)]
272-
(vswap! fn-refs assoc n f)
273-
n))
274-
275-
(defn- get-fn
276-
"Gets a function, given its numeric representation."
277-
[n]
278-
(get @fn-refs n))
279-
280-
(defn- emit-fn [f]
281-
(print "self_parity.test.get_fn(" (put-fn f) ")"))
282-
283-
(defmethod comp/emit-constant js/Function
284-
[f]
285-
(emit-fn f))
286-
287-
(defmethod comp/emit-constant cljs.core/Var
288-
[f]
289-
(emit-fn f))
290-
291-
;; Inject an implementation of eval into needed macros namespaces
292-
293-
(defn- eval
294-
([form]
295-
(eval form (.-name *ns*)))
296-
([form ns]
297-
(let [result (atom nil)]
298-
(cljs/eval st form
299-
{:ns ns
300-
:context :expr
301-
:def-emits-var true}
302-
(fn [{:keys [value error]}]
303-
(if error
304-
(handle-error error (:source-maps @st))
305-
(reset! result value))))
306-
@result)))
307-
308-
(defn- intern
309-
([ns name]
310-
(when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))]
311-
(eval `(def ~name) (ns-name the-ns))))
312-
([ns name val]
313-
(when-let [the-ns (find-ns (cond-> ns (instance? Namespace ns) ns-name))]
314-
(eval `(def ~name ~val) (ns-name the-ns)))))
315-
316-
(defn- inject-eval
317-
[target-ns]
318-
(intern target-ns 'eval eval))
319-
320-
(defn- setup-eval []
321-
(eval-form st 'cljs.user
322-
'(require-macros 'cljs.spec.test.alpha)
323-
(fn [{:keys [value error]}]
324-
(if error
325-
(handle-error error (:source-maps @st))
326-
(inject-eval 'cljs.spec.test.alpha$macros)))))
327-
328261
;; Test suite runner
329262

330263
(defn run-tests
@@ -336,6 +269,7 @@
336269
(eval-form st 'cljs.user
337270
'(ns parity.core
338271
(:require [cljs.test :refer-macros [run-tests]]
272+
[cljs.eval-test]
339273
[cljs.primitives-test]
340274
[cljs.destructuring-test]
341275
[cljs.new-new-test]
@@ -377,6 +311,7 @@
377311
(handle-error error (:source-maps @st))
378312
(eval-form st 'parity.core
379313
'(run-tests
314+
'cljs.eval-test
380315
'cljs.primitives-test
381316
'cljs.destructuring-test
382317
'cljs.new-new-test
@@ -419,7 +354,6 @@
419354

420355
(defn -main [& args]
421356
(init-runtime)
422-
(setup-eval)
423357
(run-tests))
424358

425359
(set! *main-cli-fn* -main)

0 commit comments

Comments
 (0)