Skip to content

Commit ae62411

Browse files
committed
(WIP)
Out is now directed to the correct place but there are two outstanding issues. The first is that the simple `pipe` no longer works as the value is now JSON. As we depend on Transit already we could use the streaming Jackson JSON parser to address this. The second issue is that since we pipe, the observed order in REPLs is non-deterministic.
1 parent 196e88e commit ae62411

2 files changed

Lines changed: 42 additions & 12 deletions

File tree

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
[clojure.data.json :as json])
1919
(:import [java.net Socket]
2020
[java.lang StringBuilder]
21-
[java.io File BufferedReader BufferedWriter
22-
Writer InputStreamReader IOException]
21+
[java.io File Reader BufferedReader BufferedWriter
22+
InputStreamReader IOException]
2323
[java.lang ProcessBuilder Process]
2424
[java.util.concurrent ConcurrentHashMap]))
2525

2626
(def lock (Object.))
2727
(def outs (ConcurrentHashMap.))
28+
(def errs (ConcurrentHashMap.))
2829

2930
(defn create-socket [^String host port]
3031
(let [socket (Socket. host (int port))
@@ -89,16 +90,22 @@
8990
(defn- alive? [proc]
9091
(try (.exitValue proc) false (catch IllegalThreadStateException _ true)))
9192

92-
(defn- pipe [^Process proc in ^Writer out]
93+
(defn- pipe [^Process proc in stream ios]
9394
;; we really do want system-default encoding here
94-
(with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)]
95-
(loop [buf (char-array 1024)]
95+
(with-open [^Reader in (-> in InputStreamReader. BufferedReader.)]
96+
(loop [buf (char-array (* 64 1024))]
9697
(when (alive? proc)
9798
(try
9899
(let [len (.read in buf)]
99100
(when-not (neg? len)
100-
(.write out buf 0 len)
101-
(.flush out)))
101+
(try
102+
(let [{:strs [repl data]} (json/read-str (String. buf))
103+
stream (or (.get ios repl) stream)]
104+
(.write stream data 0 (.length ^String data))
105+
(.flush stream))
106+
(catch Throwable _
107+
(.write stream buf 0 len)
108+
(.flush stream)))))
102109
(catch IOException e
103110
(when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed")))
104111
(.printStackTrace e *err*))))
@@ -119,18 +126,23 @@
119126
(defn setup
120127
([repl-env] (setup repl-env nil))
121128
([{:keys [host port socket state] :as repl-env} opts]
129+
(let [tname (.getName (Thread/currentThread))]
130+
(.put outs tname *out*)
131+
(.put errs tname *err*))
122132
(locking lock
123133
(when-not @socket
124-
(let [output-dir (io/file (util/output-directory opts))
134+
(let [out *out*
135+
err *err*
136+
output-dir (io/file (util/output-directory opts))
125137
_ (.mkdirs output-dir)
126138
of (io/file output-dir "node_repl.js")
127139
_ (spit of
128140
(string/replace (slurp (io/resource "cljs/repl/node_repl.js"))
129141
"var PORT = 5001;"
130142
(str "var PORT = " (:port repl-env) ";")))
131143
proc (.start (build-process opts repl-env of))
132-
_ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) *out*))))
133-
(.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) *err*)))))
144+
_ (do (.start (Thread. (bound-fn [] (pipe proc (.getInputStream proc) out outs))))
145+
(.start (Thread. (bound-fn [] (pipe proc (.getErrorStream proc) err errs)))))
134146
env (ana/empty-env)
135147
core (io/resource "cljs/core.cljs")
136148
;; represent paths as vectors so we can emit JS arrays, this is to
@@ -209,7 +221,6 @@
209221
(node-eval repl-env
210222
(str "goog.global.CLOSURE_UNCOMPILED_DEFINES = "
211223
(json/write-str (:closure-defines opts)) ";")))))
212-
(.put outs (.getName (Thread/currentThread)) *out*)
213224
(swap! state update :listeners inc)))
214225

215226
(defrecord NodeEnv [host port path socket proc state]
@@ -229,6 +240,9 @@
229240
(load-javascript this provides url))
230241
(-tear-down [this]
231242
(swap! state update :listeners dec)
243+
(let [tname (Thread/currentThread)]
244+
(.remove outs tname)
245+
(.remove errs tname))
232246
(locking lock
233247
(when (zero? (:listeners @state))
234248
(let [sock @socket]

src/main/clojure/cljs/repl/node_repl.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,23 @@ var net = require("net");
1313
var vm = require("vm");
1414
var dom = require("domain").create();
1515
var PORT = 5001;
16+
var repl = null;
17+
18+
process.stdout.write = (function(write) {
19+
return function(chunk, encoding, fd) {
20+
var args = Array.prototype.slice.call(arguments, 0);
21+
args[0] = JSON.stringify({repl: repl, data: chunk});
22+
write.apply(process.stdout, args);
23+
};
24+
})(process.stdout.write);
25+
26+
process.stderr.write = (function(write) {
27+
return function(chunk, encoding, fd) {
28+
var args = Array.prototype.slice.call(arguments, 0);
29+
args[0] = JSON.stringify({repl: repl, data: chunk});
30+
write.apply(process.stderr, args);
31+
};
32+
})(process.stderr.write);
1633

1734
try {
1835
require("source-map-support").install();
@@ -22,7 +39,6 @@ try {
2239
var server = net.createServer(function (socket) {
2340
var buffer = "",
2441
ret = null,
25-
repl = null,
2642
err = null;
2743

2844
socket.write("ready");

0 commit comments

Comments
 (0)