Welcome! Please see the About page for a little more info on how this works.

0 votes
in tools.namespace by
retagged by

If I call find-ns-decls-in-jarfile jarfile (example below) with any jar I get the exception:

Error printing return value (ClassCastException) at clojure.tools.namespace.find/read-ns-decl-from-jarfile-entry (find.clj:158).
class java.util.jar.JarFile cannot be cast to class java.io.File (java.util.jar.JarFile and java.io.File are in module java.base of loader 'bootstrap')

I can reproduce this with this example:


    (let [filename (format "%s/.m2/repository/org/clojure/tools.namespace/1.4.2/tools.namespace-1.4.2.jar" (System/getenv "HOME"))
      jarfile (JarFile. (io/file filename))]
  (clojure.tools.namespace.find/find-ns-decls-in-jarfile jarfile))

I think the issue is the type hint on line 159. I think this should instead be ^java.util.jar.JarFile.

by
I've also created the following patch (including lint fixes) if that is helpful. I'd be happy to sign the contributor agreement if desired.

<pre>
<code>
diff --git a/src/main/clojure/clojure/tools/namespace/find.clj b/src/main/clojure/clojure/tools/namespace/find.clj
index 88f88ef..d67aaf1 100644
--- a/src/main/clojure/clojure/tools/namespace/find.clj
+++ b/src/main/clojure/clojure/tools/namespace/find.clj
@@ -12,12 +12,10 @@
   clojure.tools.namespace.find
   (:require [clojure.java.classpath :as classpath]
             [clojure.java.io :as io]
-            [clojure.set :as set]
             [clojure.tools.namespace.file :as file]
             [clojure.tools.namespace.parse :as parse])
-  (:import (java.io File FileReader BufferedReader PushbackReader
-                    InputStreamReader)
-           (java.util.jar JarFile JarEntry)))
+  (:import (java.io File PushbackReader)
+           (java.util.jar JarFile)))
 
 (set! *warn-on-reflection* true)
 
@@ -92,7 +90,7 @@
            (let [[_ nom & more :as decl] (file/read-file-ns-decl % (:read-opts platform))]
              (when (and decl nom (symbol? nom))
                (list* 'ns (with-meta nom
-                            {:dir (.getName ^java.io.File dir) :file (.getName ^java.io.File %)})
+                            {:dir (.getName ^File dir) :file (.getName ^File %)})
                       more))))
          (find-sources-in-dir dir platform))))
 
@@ -156,7 +154,7 @@
        (ignore-reader-exception
         (let [[_ nom & more] (parse/read-ns-decl rdr read-opts)]
           (list* 'ns (with-meta nom
-                       {:jar (.getName ^java.io.File jarfile) :file entry-name})
+                       {:jar (.getName ^JarFile jarfile) :file entry-name})
                  more)))))))
 
 (defn find-ns-decls-in-jarfile
</code>
</pre>

1 Answer

0 votes
by
by
Thanks for fixing that. BTW, I think I've uncovered another issue. If I run the jars in my classpath through `find-namespaces` I get errors along the lines of:

<pre>
<code>
Error printing return value (NullPointerException) at clojure.core/with-meta (core.clj:220).
Cannot invoke "clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)" because "x" is null
</code>
</pre>

I think what's going on is the `read-ns-decl` function sometimes fails to return a ns decl (e.g. the file being read is a project.clj, so not in a ns) and then `nom` is `nil` in the return let binding. This then blows up when `with-meta nom` is invoked.

So, I think the `(let [[_ nom & more] (parse/read-ns-decl rdr read-opts)]...` form on line 157 just needs to be changed to a `when-let`. I made that change in a local branch and it worked.

Thanks!

FYI, here's a sample REPL session using a clojure jar from my local m2 directory:

<pre>
<code>
;; Using the existing 1.4.3 implementation
Loading src/main/clojure/clojure/tools/namespace/find.clj... done
(find-namespaces [(io/file "/Users/mbastian/.m2/repository/amalloy/ring-buffer/1.3.1/ring-buffer-1.3.1.jar")])
Error printing return value (NullPointerException) at clojure.core/with-meta (core.clj:220).
Cannot invoke "clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)" because "x" is null
;; Using when-let
Loading src/main/clojure/clojure/tools/namespace/find.clj... done
(find-namespaces [(io/file "/Users/mbastian/.m2/repository/amalloy/ring-buffer/1.3.1/ring-buffer-1.3.1.jar")])
=> (amalloy.ring-buffer)
</code>
</pre>
by
Thanks! I have that covered also in the fix for the `JarFile` type hint and they will both go out with the next version.
by
Great, thanks!
...