Skip to content

Commit a052dd8

Browse files
authored
Remove reflection in forking-printer (#733)
Remove reflection to improve speed and reduce garbage creation.
1 parent c3d6b69 commit a052dd8

File tree

4 files changed

+106
-17
lines changed

4 files changed

+106
-17
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Bugs fixed
66

7+
* [#733](https://github.com/clojure-emacs/cider-nrepl/issues/719): `middleware.out`: remove reflection.
78
* [#719](https://github.com/clojure-emacs/cider-nrepl/issues/719): `middleware.test`: gracefully handle exceptions thrown within fixtures.
89
* [#722](https://github.com/clojure-emacs/cider-nrepl/issues/722): `middleware.format`: print otherwise non-serializable objects as strings.
910
* [#708](https://github.com/clojure-emacs/cider-nrepl/issues/708): Upgrade Compliment, improving how autocompletion works in Windows.

project.clj

-7
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,4 @@
147147
:eastwood {:config-files ["eastwood.clj"]
148148
:exclude-namespaces [cider.nrepl.middleware.test-filter-tests]
149149
:ignored-faults {:unused-ret-vals-in-try {cider.nrepl.middleware.profile-test [{:line 25}]}
150-
;; This usage of `proxy` can't avoid reflection warnings given that the `proxy` construct dispatches based on name only:
151-
:reflection {cider.nrepl.middleware.out [{:line 55}
152-
{:line 57}
153-
{:line 59}
154-
{:line 61}
155-
{:line 63}
156-
{:line 65}]}
157150
:suspicious-test {cider.nrepl.middleware.profile-test [{:line 25}]}}}}]})

src/cider/nrepl/middleware/out.clj

+34-9
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ Please do not inline; they must not be recomputed at runtime."}
3030
type is either :out or :err."
3131
[[v msg-seq type] & body]
3232
`(doseq [{:keys [~'session] :as ~'msg} ~msg-seq]
33-
(let [~(with-meta v {:tag Writer}) (get @~'session
34-
(case ~type
35-
:out #'*out*
36-
:err #'*err*))]
33+
(let [~(with-meta v {:tag 'java.io.Writer}) (get @~'session
34+
(case ~type
35+
:out #'*out*
36+
:err #'*err*))]
3737
(try (binding [ieval/*msg* ~'msg]
3838
~@body)
3939
;; If a channel is faulty, dissoc it.
@@ -50,17 +50,42 @@ Please do not inline; they must not be recomputed at runtime."}
5050
[messages type]
5151
(PrintWriter. (proxy [Writer] []
5252
(close [] (.flush ^Writer this))
53+
;; unfortunately we can't type hint the method argument
54+
;; as `int` and `char[]` aren't supported by proxy.
5355
(write
5456
([x]
55-
(.write (original-output type) x)
5657
(with-out-binding [printer messages type]
57-
(.write printer x)))
58+
(cond
59+
(string? x)
60+
(do
61+
(.write ^Writer (original-output type) ^String x)
62+
(.write printer ^String x))
63+
64+
(integer? x)
65+
(do
66+
(.write ^Writer (original-output type) ^int x)
67+
(.write printer ^int x))
68+
:else
69+
(do
70+
(.write ^Writer (original-output type)
71+
^{:tag "[C"} x)
72+
(.write printer ^{:tag "[C"} x)))))
5873
([x ^Integer off ^Integer len]
59-
(.write (original-output type) x off len)
6074
(with-out-binding [printer messages type]
61-
(.write printer x off len))))
75+
(cond
76+
(string? x)
77+
(do
78+
(.write ^Writer (original-output type)
79+
^String x off len)
80+
(.write printer ^String x off len))
81+
82+
:else
83+
(do
84+
(.write ^Writer (original-output type)
85+
^{:tag "[C"} x off len)
86+
(.write printer ^{:tag "[C"} x off len))))))
6287
(flush []
63-
(.flush (original-output type))
88+
(.flush ^Writer (original-output type))
6489
(with-out-binding [printer messages type]
6590
(.flush printer))))
6691
true))

test/clj/cider/nrepl/middleware/out_test.clj

+71-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
(ns cider.nrepl.middleware.out-test
22
(:require
33
[cider.nrepl.middleware.out :as o]
4-
[clojure.test :refer :all]))
4+
[clojure.test :refer :all])
5+
(:import
6+
[java.io PrintWriter StringWriter]))
57

68
(defn random-str []
79
(->> #(format "%x" (rand-int 15))
@@ -32,3 +34,71 @@
3234
(testing "The mapping is computed once; not doing so would defeat is point and create issues."
3335
(is (map? o/original-output))
3436
(is (not (fn? o/original-output)))))
37+
38+
(defmacro with-original-output
39+
[[m] & body]
40+
`(let [orig# o/original-output]
41+
(try
42+
(alter-var-root #'o/original-output (constantly ~m))
43+
~@body
44+
(finally
45+
(alter-var-root #'o/original-output (constantly orig#))))))
46+
47+
(defn- forking-printer-test-streams
48+
[]
49+
(let [out-writer (StringWriter.)
50+
message-writer (StringWriter.)
51+
message {:session (atom {#'*out* message-writer})}
52+
printer (o/forking-printer [message] :out)]
53+
{:out-writer out-writer
54+
:message-writer message-writer
55+
:printer printer}))
56+
57+
(deftest forking-printer-test
58+
(testing "forking-printer prints to all message streams and original stream"
59+
(testing "with String argument "
60+
(let [{:keys [^StringWriter out-writer
61+
^StringWriter message-writer
62+
^PrintWriter printer]}
63+
(forking-printer-test-streams)]
64+
(with-original-output [{:out out-writer}]
65+
(.write printer "Hello")
66+
(is (= "Hello" (.toString out-writer)))
67+
(is (= "Hello" (.toString message-writer))))))
68+
(testing "with int"
69+
(let [{:keys [^StringWriter out-writer
70+
^StringWriter message-writer
71+
^PrintWriter printer]}
72+
(forking-printer-test-streams)
73+
an-int (int 32)]
74+
(with-original-output [{:out out-writer}]
75+
(.write printer an-int)
76+
(is (= " " (.toString out-writer)))
77+
(is (= " " (.toString message-writer))))))
78+
(testing "with char array"
79+
(let [{:keys [^StringWriter out-writer
80+
^StringWriter message-writer
81+
^PrintWriter printer]}
82+
(forking-printer-test-streams)]
83+
(with-original-output [{:out out-writer}]
84+
(.write printer (char-array "and"))
85+
(is (= "and" (.toString out-writer)))
86+
(is (= "and" (.toString message-writer))))))
87+
(testing "with String with offsets"
88+
(let [{:keys [^StringWriter out-writer
89+
^StringWriter message-writer
90+
^PrintWriter printer]}
91+
(forking-printer-test-streams)]
92+
(with-original-output [{:out out-writer}]
93+
(.write printer "12 good34" 3 4)
94+
(is (= "good" (.toString out-writer)))
95+
(is (= "good" (.toString message-writer))))))
96+
(testing "with char array with offsets"
97+
(let [{:keys [^StringWriter out-writer
98+
^StringWriter message-writer
99+
^PrintWriter printer]}
100+
(forking-printer-test-streams)]
101+
(with-original-output [{:out out-writer}]
102+
(.write printer (char-array " bye67") 1 3)
103+
(is (= "bye" (.toString out-writer)))
104+
(is (= "bye" (.toString message-writer))))))))

0 commit comments

Comments
 (0)