Skip to content

Commit c63b12c

Browse files
committed
Optimize member-info
Its usage of laziness could likely mean that more objects would be retained than necessary, causing OOMs in constrainted environments. Reported in https://clojurians.slack.com/archives/C0617A8PQ/p1645530472596039
1 parent dd08e5d commit c63b12c

File tree

3 files changed

+16
-6
lines changed

3 files changed

+16
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## master (unreleased)
44

5+
* Guard against OOMs in `orchard.java/member-info`.
6+
57
## 0.9.1 (2022-01-17)
68

79
### Bugs fixed

src/orchard/java.clj

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,16 @@
268268
implemention. If the member is an instance member, `this` is prepended to its
269269
arglists."
270270
[class member]
271-
(let [c (->> (class-info class)
272-
(iterate (comp class-info :super))
273-
(take-while identity)
274-
(filter #(get-in % [:members member]))
275-
(first))]
271+
(let [c
272+
;; NOTE: the following code uses `loop` to avoid retaining more objects in memory than necessary.
273+
;; `class-info` calls can be expensive given the JavaDoc parsing they can perform.
274+
(loop [next-class class]
275+
(let [{:keys [super] :as c-i} (class-info next-class)
276+
v (get-in c-i [:members member])]
277+
(cond
278+
v c-i
279+
(not super) nil
280+
:else (recur super))))]
276281
(when-let [m (get-in c [:members member])]
277282
(let [m* (first (sort-by :line (vals m)))
278283
static? (or (:static (:modifiers m*)) (= class member))

test/orchard/java_test.clj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(:require
33
[clojure.java.io :as io]
44
[clojure.java.javadoc :as javadoc]
5+
[clojure.string :as string]
56
[clojure.test :refer [are deftest is testing]]
67
[orchard.java :refer [cache class-info class-info* javadoc-url jdk-tools member-info resolve-class resolve-javadoc-path resolve-member resolve-symbol resolve-type source-info]]
78
[orchard.misc :as misc]
@@ -130,7 +131,9 @@
130131
(testing "implemented on immediate superclass"
131132
(is (not= 'java.lang.Object (:class m6))))
132133
(testing "implemented on ancestor superclass"
133-
(is (not= 'java.lang.Object (:class m7))))))))
134+
(is (not= 'java.lang.Object (:class m7)))
135+
(is (-> m6 :doc (string/starts-with? "Called by the garbage collector on an object when garbage collection "))
136+
"Contains doc that is clearly defined in Object (the superclass)"))))))
134137

135138
(deftest arglists-test
136139
(let [+this (comp #{'this} first)]

0 commit comments

Comments
 (0)