Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Browse files

Fix reflection warnings

Reflection can be a useful tool for achieving polymorphism when methods
do not adhere to a well-defined interface or class hierarchy. It is also
quite expensive (and the warnings generated with *warn-on-reflection*
are very ugly) so its use should be justified.

Slamhound's use of reflection is unnecessary, so this patch resolves all
reflection warnings in the project.

First, we replace .trim and .split calls to their clojure.string
equivalents. This actually increases flexibility at no appreciable cost.

Next we add type hints for static dispatch. Most of these hints are
actually beneficial for clarity, but one function's type signature was
implicitly changed:

-(defn clj? [f]
-  (.endsWith (.getName f) ".clj"))
+(defn clj? [^String path]
+  (.endsWith path ".clj"))

I don't believe this will cause any problems as it is unlikely that
anyone has been tempted to use this function outside of slamhound. This
function, as well as most functions within, are good
candidates for being made private.

Most of the dynamic method calls dealt with organizing classes from
jar files, so there has been a corresponding performance increase for
reconstructing namespaces that use many imports.

For instance, reconstructing is now ~20% faster. The
function is a full ~100% faster.

Preloading speed and alias/refer reconstruction performance has not been
noticeably affected.
  • Loading branch information...
commit bedcadd016e2bbf3a3895cb0592923ebcfc05798 1 parent b85cd05
@guns guns authored
2  src/slam/hound.clj
@@ -45,7 +45,7 @@
(let [tmp-file (File/createTempFile "slamhound_tmp" ".clj")]
(io/copy file tmp-file)
- (let [new-ns (.trim (reconstruct tmp-file))]
+ (let [new-ns (string/trim (reconstruct tmp-file))]
(with-open [rdr (PushbackReader. (io/reader tmp-file))
writer (io/writer file :append true)]
(io/copy "" file)
2  src/slam/hound/regrow.clj
@@ -90,7 +90,7 @@
(case type
:import (let [m (str missing)
ss (for [class-name search/available-classes
- :when (= m (last (.split class-name "\\.")))]
+ :when (= m (last (string/split class-name #"\.")))]
(symbol class-name))]
(into (ns-import-candidates missing) ss))
:alias (set
46 src/slam/hound/search.clj
@@ -17,21 +17,20 @@
(System/getProperty "path.separator"))]
(file f)))
-(defn clj? [f]
- (.endsWith (.getName f) ".clj"))
+(defn clj? [^String path]
+ (.endsWith path ".clj"))
-(defn jar? [f]
- (let [f (file f)]
- (and (.isFile f) (.endsWith (.getName f) ".jar"))))
+(defn jar? [^File f]
+ (and (.isFile f) (.endsWith (.getName f) ".jar")))
-(defn class-file? [f]
- (.endsWith f ".class"))
+(defn class-file? [^String path]
+ (.endsWith path ".class"))
(defn clojure-fn-file? [f]
(re-find #"\$.*__\d+\.class" f))
-(defn clojure-ns-file? [n]
- (.endsWith n "__init.class"))
+(defn clojure-ns-file? [^String path]
+ (.endsWith path "__init.class"))
(defn read-ns-form [r f]
(let [form (try (read r false ::done)
@@ -41,8 +40,8 @@
(when-not (= ::done form)
(recur r f)))))
-(defn find-ns-form [f]
- (when (and (.isFile (file f)) (clj? f))
+(defn find-ns-form [^File f]
+ (when (and (.isFile f) (clj? (.getName f)))
(read-ns-form (PushbackReader. (reader f)) f)))
(defn namespaces-in-dir [dir]
@@ -51,7 +50,7 @@
:when ns-form]
(second ns-form))))
-(defn ns-in-jar-entry [jarfile entry]
+(defn ns-in-jar-entry [^JarFile jarfile ^JarEntry entry]
(with-open [rdr (-> jarfile
(.getInputStream (.getEntry jarfile (.getName entry)))
@@ -59,11 +58,11 @@
(read-ns-form rdr jarfile)))
-(defn namespaces-in-jar [jar]
+(defn namespaces-in-jar [^File jar]
(let [jarfile (JarFile. jar)]
- (for [entry (enumeration-seq (.entries jarfile))
+ (for [^JarEntry entry (enumeration-seq (.entries jarfile))
:when (and (not (.isDirectory entry))
- (clj? entry))]
+ (clj? (.getName entry)))]
(when-let [ns-form (ns-in-jar-entry jarfile entry)]
(second ns-form)))))
@@ -74,15 +73,15 @@
(defn namespaces-from-files
([] (namespaces-from-files classpath-files))
- ([paths] (filter-ns #(.isDirectory %) namespaces-in-dir paths)))
+ ([files] (filter-ns (fn [^File f] (.isDirectory f)) namespaces-in-dir files)))
(defn namespaces-from-jars
([] (namespaces-from-jars classpath-files))
- ([paths] (filter-ns jar? namespaces-in-jar paths)))
+ ([files] (filter-ns jar? namespaces-in-jar files)))
(defn namespaces
([] (namespaces classpath-files))
- ([paths] (into (namespaces-from-files paths) (namespaces-from-jars paths))))
+ ([files] (into (namespaces-from-files files) (namespaces-from-jars files))))
;;; Java classes
@@ -101,12 +100,11 @@
(defn class-or-ns-name
"Returns the Java class or Clojure namespace name for a class relative path."
- [n]
- (.replace
- (if (clojure-ns-file? n)
- (-> n (.replace "__init.class" "") (.replace "_" "-"))
- (.replace n ".class" ""))
- File/separator "."))
+ [^String path]
+ (-> (if (clojure-ns-file? path)
+ (-> path (.replace "__init.class" "") (.replace "_" "-"))
+ (.replace path ".class" ""))
+ (.replace File/separator ".")))
(def path-class-files nil)
(defmulti path-class-files
7 src/slam/hound/stitch.clj
@@ -4,7 +4,7 @@
[slam.hound.prettify :refer [prettify]]))
(defn- get-package [class-name]
- (let [cls ^Class (resolve class-name)]
+ (let [^Class cls (resolve class-name)]
(if-let [pkg (.getPackage cls)]
(.getName pkg)
;; Fall back to string matching for dynamically generated classes
@@ -13,8 +13,9 @@
(defn- group-by-package [imports]
(for [[package classes] (group-by get-package imports)]
(cons (symbol package)
- (sort (for [c classes]
- (-> c resolve .getName (.split "\\.") last symbol))))))
+ (sort (for [c classes
+ :let [^Class c (resolve c)]]
+ (-> c .getName (.split "\\.") last symbol))))))
(defn metadata-from-map
"Returns a vector of: [docstring? meta-map?]"
6 test/slam/hound/search_test.clj
@@ -4,7 +4,8 @@
[ :refer [classpath-files
- namespaces-from-jars]]))
+ namespaces-from-jars]])
+ (:import ( File)))
(def slamhound-namespaces
@@ -24,7 +25,8 @@
(def korma-jar
- (first (filter #(re-find #"\Akorma-.+\.jar\z" (.getName %)) classpath-files)))
+ (first (filter (fn [^File f] (re-find #"\Akorma-.+\.jar\z" (.getName f)))
+ classpath-files)))
(deftest ^:unit test-namespaces-from-files
(is (= (namespaces-from-files (file-seq (io/file "src")))
2 
@@ -24,7 +24,7 @@
- [ ] Improve performance of slam.hound.reload/pre-load-namespaces if possible
* Minor enhancements
- [X] Sort alias candidates by L̶e̶v̶e̶n̶s̶h̶t̶e̶i̶n̶ "alias" distance, with emphasis on initial letters
+ - [X] Satisfy all reflection warnings (there are only a handful)
- [ ] Count dashes as word separators when calculating "alias" distance
- - [ ] Satisfy all reflection warnings (there are only a handful)
- [ ] Support Unicode characters in regrow/missing-sym-name
- [ ] Sort refer candidates by matching arity. e.g. (join []) should prefer clojure.string/join

0 comments on commit bedcadd

Please sign in to comment.
Something went wrong with that request. Please try again.