Skip to content


Browse files Browse the repository at this point in the history
Support code reflection in the cljs repl.
A few changes have been made to support runtime code reflection in a
cljs repl. These include small changes to cljs.analyzer, a separation of
the server element of cljs.repl.browser into cljs.repl.server, and the
addition of two new namespaces: cljs.repl.reflect (in src/clj) and
clojure.reflect (in src/cljs).

- Arbitrary metadata declared on symbols will now be added to the AST.
  This supports the addition of docstrings.
- Fix a subtle bug in cljs.analyzer/analyze-file, where an uncommon
  code-path would lead to the failed coercion of an absolute-path into a
  URL. An absolute path, including a `file://` protocol, can now be
  passed into the function successfully.

- Add function to analyze source on repl-env -setup. This is used to
  support reflection on user-defined cljs source files, as well as to
  populate the cljs.analyzer/namespaces atom on repl startup.

- The server element of this namespace has been factored out into
  cljs.repl.server to support other services that may require that

- Expose a simple HTTP method and predicate dispatch system to register
  handler functions for incoming requests. (Note: this system seems to
  be relatively brittle, and future change may be warranted.)

- Registers a server handler for incoming requests to "/reflect".
- Queries cljs.analyzer/namespaces for meta information relevant to a
  symbol, responding to requests with compiled javascript.
- Can use "fixed point" macroexpansion on cljs macro forms.

- Expose a set of simple functions for querying meta information of a
  symbol, as well as macroexpanding a cljs form.
  • Loading branch information
zachallaun authored and David Nolen committed Jul 25, 2012
1 parent 0f73237 commit b5b20fd
Show file tree
Hide file tree
Showing 9 changed files with 377 additions and 196 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -9,3 +9,4 @@ closure
3 changes: 2 additions & 1 deletion samples/repl/src/repl/test.cljs
Expand Up @@ -7,7 +7,8 @@
;; You must not remove this notice, or any other, from this software.

(ns repl.test
(:require [clojure.browser.repl :as repl]))
(:require [clojure.browser.repl :as repl]
[clojure.reflect :as reflect]))

(repl/connect "http://localhost:9000/repl")

Expand Down
16 changes: 16 additions & 0 deletions script/browser-repl
@@ -0,0 +1,16 @@

if [ "$CLOJURESCRIPT_HOME" = "" ]; then
CLOJURESCRIPT_HOME="`dirname $0`/.."

for next in lib/*: src/clj: src/cljs: test/cljs; do

java -server -cp $CLJSC_CP clojure.main -e "
(require '[cljs.repl :as r])
(require '[cljs.repl.browser :as b])
(r/repl (b/repl-env))
11 changes: 6 additions & 5 deletions src/clj/cljs/analyzer.clj
Expand Up @@ -290,6 +290,7 @@
([_ sym doc init] {:sym sym :doc doc :init init}))
args (apply pfn form)
sym (:sym args)
sym-meta (meta sym)
tag (-> sym meta :tag)
protocol (-> sym meta :protocol)
dynamic (-> sym meta :dynamic)
Expand Down Expand Up @@ -326,6 +327,7 @@
(let [m (assoc (or m {}) :name name)]
(merge m
(when tag {:tag tag})
(when sym-meta sym-meta)
(when dynamic {:dynamic true})
(when-let [line (:line env)]
{:file *cljs-file* :line line})
Expand Down Expand Up @@ -533,7 +535,7 @@
(when (and known-num-fields (not= known-num-fields argc))
(warning env
(str "WARNING: Wrong number of args (" argc ") passed to " ctor)))

{:env env :op :new :form form :ctor ctorexpr :args argexprs
:children (into [ctorexpr] argexprs)})))

Expand Down Expand Up @@ -677,7 +679,7 @@
:type true
:num-fields (count fields))]
(merge m
{:protocols (-> tsym meta :protocols)}
{:protocols (-> tsym meta :protocols)}
(when-let [line (:line env)]
{:file *cljs-file*
:line line})))))
Expand Down Expand Up @@ -935,8 +937,8 @@
:else {:op :constant :env env :form form}))))

(defn analyze-file
(let [res (if (= \/ (first f)) f (io/resource f))]
[^String f]
(let [res (if (re-find #"^file://" f) ( f) (io/resource f))]
(assert res (str "Can't find " f " in classpath"))
(binding [*cljs-ns* 'cljs.user
*cljs-file* (.getPath ^ res)
Expand All @@ -950,4 +952,3 @@
(when-not (identical? eof r)
(analyze env r)
(recur (read pbr false eof false))))))))))

16 changes: 13 additions & 3 deletions src/clj/cljs/repl.clj
Expand Up @@ -8,6 +8,7 @@

(ns cljs.repl
(:refer-clojure :exclude [load-file])
(:require [clojure.string :as string]
[ :as io]
[cljs.compiler :as comp]
Expand Down Expand Up @@ -149,6 +150,15 @@
'clojure.core/load-file load-file-fn
'load-namespace (fn [repl-env ns] (load-namespace repl-env ns))}))

(defn analyze-source
"Given a source directory, analyzes all .cljs files. Used to populate
cljs.analyzer/namespaces so as to support code reflection."
(if-let [src-dir (and (not (empty? src-dir))
(File. src-dir))]
(doseq [file (comp/cljs-files-in src-dir)]
(ana/analyze-file (str "file://" (.getAbsolutePath file))))))

(defn repl
"Note - repl will reload core.cljs every time, even if supplied old repl-env"
[repl-env & {:keys [verbose warn-on-undeclared special-fns]}]
Expand All @@ -166,12 +176,12 @@
(let [{:keys [status form]} (read-next-form)]
(= form :cljs/quit) :quit

(= status :error) (recur)

(and (seq? form) (is-special-fn? (first form)))
(do (apply (get special-fns (first form)) repl-env (rest form)) (newline) (recur))

(do (eval-and-print repl-env env form) (recur)))))
(-tear-down repl-env))))
Expand Down

0 comments on commit b5b20fd

Please sign in to comment.