请在 2024 年 Clojure 状态调查!中分享您的想法。

欢迎!有关如何使用本站更详细的信息,请参阅关于页面。

0
Clojure

问题

  1. 获取方法/构造函数实例的代码在编译器中重复
  2. 解析首选重载方法的代码位于编译器中

通过合并重复的代码,将反射相关部分移至反射器,并提供一个直接的API,应该可以更轻松地阅读和理解方法解析过程。此外,对(例如,CLJ-445)状态成员的反射机制 enhancements 可以在很大的程度上与编译器隔离。并且可以清楚地区分和记录少量协调点(例如,编译器在调用时输出与反射器相同的参数和返回类型)。

15 个答案

0

由:stuart.sierra 评论

补丁直到提交 f5bcf64 时不适用。

0

由:ataggart 评论

是的,一年前的补丁通常是这样。

0

由:jafingerhut 评论

不知道这样的更新是否有帮助,但截至2012年2月20日的Alexander补丁的最新版本可以顺利应用到最新版的Clojure上。虽然它能编译,但Ant测试未能通过。

0

评论者:bronsa

我非常希望这个补丁再次得到考虑,如果有兴趣,我可以帮助将补丁迁移到当前主分支。

我认为在Compiler.java和Reflector.java之间花了些时间的人都会同意,这个重构/清理已经很久需要了。

0

评论者:michaelblume

我再次尝试将补丁前移。Java代码能编译,而且能够编译Clojure源代码,但在编译clojure测试时,由于test-example而出错。

`
test-example

 [java] Exception in thread "main" java.lang.IllegalArgumentException: Found multiple uncheckedByteCast methods in clojure.lang.RT for argtypes: byte, compiling:(clojure/test_clojure/numbers.clj:127:3)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6568)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6556)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:981)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6556)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5693)
 [java] 	at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:6007)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.access$200(Compiler.java:38)
 [java] 	at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:5957)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5693)
 [java] 	at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:2182)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5693)
 [java] 	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5124)
 [java] 	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3753)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6559)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3565)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6563)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:2153)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5691)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5691)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5693)
 [java] 	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5124)
 [java] 	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3753)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6559)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6549)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$MapExpr.parse(Compiler.java:2882)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6360)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:559)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6352)
 [java] 	at clojure.lang.Compiler.analyze(Compiler.java:6313)
 [java] 	at clojure.lang.Compiler.eval(Compiler.java:6624)
 [java] 	at clojure.lang.Compiler.load(Compiler.java:7047)
 [java] 	at clojure.lang.RT.loadResourceScript(RT.java:370)
 [java] 	at clojure.lang.RT.loadResourceScript(RT.java:361)
 [java] 	at clojure.lang.RT.load(RT.java:440)
 [java] 	at clojure.lang.RT.load(RT.java:411)
 [java] 	at clojure.core$load$fn__5424.invoke(core.clj:5848)
 [java] 	at clojure.core$load.doInvoke(core.clj:5847)
 [java] 	at clojure.lang.RestFn.invoke(RestFn.java:408)
 [java] 	at clojure.core$load_one.invoke(core.clj:5653)
 [java] 	at clojure.core$load_lib$fn__5373.invoke(core.clj:5693)
 [java] 	at clojure.core$load_lib.doInvoke(core.clj:5692)
 [java] 	at clojure.lang.RestFn.applyTo(RestFn.java:142)
 [java] 	at clojure.core$apply.invoke(core.clj:628)
 [java] 	at clojure.core$load_libs.doInvoke(core.clj:5731)
 [java] 	at clojure.lang.RestFn.applyTo(RestFn.java:137)
 [java] 	at clojure.core$apply.invoke(core.clj:628)
 [java] 	at clojure.core$require.doInvoke(core.clj:5814)
 [java] 	at clojure.lang.RestFn.invoke(RestFn.java:408)
 [java] 	at user$eval72.invoke(run_test.clj:6)
 [java] 	at clojure.lang.Compiler.eval(Compiler.java:6620)
 [java] 	at clojure.lang.Compiler.load(Compiler.java:7047)
 [java] 	at clojure.lang.Compiler.loadFile(Compiler.java:7003)
 [java] 	at clojure.main$load_script.invoke(main.clj:274)
 [java] 	at clojure.main$script_opt.invoke(main.clj:336)
 [java] 	at clojure.main$main.doInvoke(main.clj:420)
 [java] 	at clojure.lang.RestFn.invoke(RestFn.java:408)
 [java] 	at clojure.lang.Var.invoke(Var.java:379)
 [java] 	at clojure.lang.AFn.applyToHelper(AFn.java:154)
 [java] 	at clojure.lang.Var.applyTo(Var.java:700)
 [java] 	at clojure.main.main(main.java:37)
 [java] Caused by: java.lang.IllegalArgumentException: Found multiple uncheckedByteCast methods in clojure.lang.RT for argtypes: byte
 [java] 	at clojure.lang.Reflector.getMatchingParams(Reflector.java:385)
 [java] 	at clojure.lang.Reflector.getMatchingMember(Reflector.java:419)
 [java] 	at clojure.lang.Reflector.getMatchingMethod(Reflector.java:485)
 [java] 	at clojure.lang.Reflector.getMatchingStaticMethod(Reflector.java:499)
 [java] 	at clojure.lang.Compiler$StaticMethodExpr.<init>(Compiler.java:1578)
 [java] 	at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:983)
 [java] 	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6561)
 [java] 	... 110 more

`

0

评论者:michaelblume

clj-793-v4有一个有用的特性,就是在每次提交时都编译,这样更容易进行二分查找,尽管测试仍然没有运行。

测试似乎在第“将getMatchingParams方法从Compiler移动到Reflector”处开始失败,这对我觉得非常小的变化,所以我有点困惑。

0

评论者:michaelblume

Aha,尝试仅使用IntelliJ重排getMatchingParams并且比较,看起来导致“tied = false”的赋值丢失。这修复了那个测试错误,但留下了一个新的错误。正在二分查找...

0

评论者:michaelblume

顺便说一句,我非常感谢Alexander Taggart编写这个补丁,把它作为一系列小提交,这样就可以相对轻松地前移/二分查找。

0

评论者:michaelblume

好的,这个问题已经修复了 tied=false,但是又出现了新问题

在“将查找实例方法代码从 Compiler 移至 Reflector”中,被移出 Compiler 的代码中有两个对不同 RT.errPrintWriter 的调用,这些调用使用了仅在 Compiler 中可用的信息(列、行等)。当这段代码被移到 Reflector 中时,我在前向传输中试图很巧妙地只保留在 Compiler 中的一个错误打印位置,但是这不起作用,因为这两个调用打印的消息不同。我实际上不确定如何将这部分代码以合适的方式移动到 Reflector 中。

相关的改动也在 https://github.com/MichaelBlume/clojure/commit/56995f3376795a3cfcfc6339e7d8ad24d8459d31

查找对 RT.errPrintWriter 的调用,你会看到发生了什么变化。

0

评论者:michaelblume

总结一下可能更清晰,有一些代码在这个补丁中从 Compiler 移动到 Reflector 中的独立方法,最初在编写补丁时相对容易移动,但现在有额外的反射警告打印,需要知道行/列等信息,这在 Compiler 的原始位置是可用的,但在 Reflector 中提取的函数中不可用。我们可以将行/列参数传递给该方法,但是那就需要最终将这些参数通过几个其他的 Reflector 方法传递,这听起来很糟糕。或者我们可以通过反射方法之外传递一些表示方法无法识别的信息,以便 Compiler 方法可以自己决定如何打印失败信息,但这可能需要发明异常或某种 Success/Failure ADT。

0

评论者:bronsa

Michael,你认为这样做怎么样?

如果 (Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false) == null) RT.errPrintWriter().format("反射警告,%s:%d:%d - 无法解析在 %s 上调用方法 %s (没有此类方法)。\n", SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName()); else RT.errPrintWriter().format("反射警告,%s:%d:%d - 在 %s 上调用方法 %s 无法解析 (参数类型: %s)。\n",SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName(), getTypeStringForArgs(args));

在以
`
if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))`

开始的 Compiler.java 代码块内?

0

评论者:michaelblume

这很有道理,当 Reflector.getMethods 被替换为私有方法 getMethodsForName 时,它可能会遇到麻烦,但也许我们可以通过一些技巧来解决这个问题...

0

评论者:michaelblume

不,仍然无法打印正确的警告信息

`

 [java] Testing clojure.test-clojure.rt
 [java]
 [java] FAIL in (error-messages) (rt.clj:43)
 [java] reflection cannot resolve instance method because it is missing
 [java] expected: (clojure.core/re-matches #"Reflection warning, .*:\d+:\d+ - call to method zap on java\.lang\.String can't be resolved \(no such method\)\.\r?\n" (clojure.test-helper/with-err-string-writer (clojure.test-helper/eval-in-temp-ns (defn foo [x] (.zap x 1)))))
 [java]   actual: (not (clojure.core/re-matches #"Reflection warning, .*:\d+:\d+ - call to method zap on java\.lang\.String can't be resolved \(no such method\)\.\r?\n" "Reflection warning, /Users/michael.blume/workspace/clojure/src/script/run_test.clj:45:28 - call to method zap on java.lang.String can't be resolved (argument types: long).\n"))
 [java]
 [java] FAIL in (error-messages) (rt.clj:43)
 [java] reflection cannot resolve instance method because it is missing
 [java] expected: (clojure.core/re-matches #"Reflection warning, .*:\d+:\d+ - call to method zap on java\.lang\.String can't be resolved \(no such method\)\.\r?\n" (clojure.test-helper/with-err-print-writer (clojure.test-helper/eval-in-temp-ns (defn foo [x] (.zap x 1)))))
 [java]   actual: (not (clojure.core/re-matches #"Reflection warning, .*:\d+:\d+ - call to method zap on java\.lang\.String can't be resolved \(no such method\)\.\r?\n" "Reflection warning, /Users/michael.blume/workspace/clojure/src/script/run_test.clj:45:28 - call to method zap on java.lang.String can't be resolved (argument types: long).\n"))

`

0

评论者:michaelblume

在 v5 版本中的补丁系列也可以在 https://github.com/MichaelBlume/clojure/commits/compile-refactor 找到
包含 Nicolo 提议的补丁系列可以在 https://github.com/MichaelBlume/clojure/commits/compile-refactor-nicolo 找到

0
参考: https://clojure.atlassian.net/browse/CLJ-792 (由 ataggart 提出)
...