两位用户报告了 clojure.reflect 返回值中添加的协议元导致的回归问题,这个问题出现在 Clojure 1.10 中。
最简单的再现方法是:
(require 'clojure.reflect)
(eval `~(clojure.reflect/reflect String))
;; 执行错误(IllegalArgumentException)在 user$eval9/<clinit> (REPL:1)。
;; 没有找到与类 clojure.reflect$typesym$fn__11946 匹配的构造函数
ExceptionInInitializerError
sun.reflect.NativeConstructorAccessorImpl.newInstance0 (NativeConstructorAccessorImpl.java:-2)
sun.reflect.NativeConstructorAccessorImpl.newInstance (NativeConstructorAccessorImpl.java:62)
sun.reflect.DelegatingConstructorAccessorImpl.newInstance (DelegatingConstructorAccessorImpl.java:45)
java.lang.reflect.Constructor.newInstance (Constructor.java:423)
java.lang.Class.newInstance (Class.java:442)
clojure.lang.Compiler$ObjExpr.eval (Compiler.java:4996)
clojure.lang.Compiler.eval (Compiler.java:7175)
clojure.lang.Compiler.eval (Compiler.java:7131)
clojure.core/eval (core.clj:3214)
clojure.core/eval (core.clj:3210)
user/eval7 (NO_SOURCE_FILE:1)
user/eval7 (NO_SOURCE_FILE:1)
原因
IllegalArgumentException 没有找到与类 clojure.reflect$typesym$fn__11946 匹配的构造函数
clojure.lang.Reflector.invokeConstructor (Reflector.java:288)
clojure.lang.LispReader$EvalReader.invoke (LispReader.java:1317)
clojure.lang.LispReader$DispatchReader.invoke (LispReader.java:853)
clojure.lang.LispReader.read (LispReader.java:285)
clojure.lang.LispReader.read (LispReader.java:216)
clojure.lang.LispReader.read (LispReader.java:205)
clojure.lang.RT.readString (RT.java:1878)
clojure.lang.RT.readString (RT.java:1873)
user/eval9 (NO_SOURCE_FILE:1)
sun.reflect.NativeConstructorAccessorImpl.newInstance0 (NativeConstructorAccessorImpl.java:-2)
sun.reflect.NativeConstructorAccessorImpl.newInstance (NativeConstructorAccessorImpl.java:62)
实际上,这个问题是 clojure.reflect 在宏返回值中使用时发生的(例如见
https://groups.google.com/forum/#!msg/clojure/pxLN9tYti4c/zbhLCOrTBgAJ)。
原因:clojure.reflect 返回的类符号现在类似于
#:clojure.core.protocols{datafy #object[clojure.reflect$typesym$fn__11946 0x49fc609f "clojure.reflect$typesym$fn__11946@49fc609f"]}
这种形式有内嵌的 datafy 函数。编译器将其发射到编译类池中(MetaExpr 包装对象)。
解决方案:解决方案是用得宏内部剥离符号上的元数据,然后返回剥离后的符号。