这是我在邮件列表https://groups.google.com/forum/#!topic/clojure/Ozt5HQyM36I上的帖子的翻版。基于这些回复,我不确定这是应作为增强功能还是缺陷记录。请将标识更改为您认为合适的。
我发现在使用ns-unmap时,AOT编译的Clojure代码中出现了一个似乎的bug;这个bug的根本原因可能影响其他重新定义Vars的代码。我有以下简化示例
(ns unmap-test.core)
(def a :test-1)
(ns-unmap 'unmap-test.core 'a)
(def a :test-2)
结果是,在加载这个命名空间时,变量a无法解析。当我查看编译的字节码时,
似乎发生了以下操作
- RT.var对'UNMAP-TEST.CORE和'a的调用返回了一个Var,该Var绑定到一个常量。
在这次调用期间,将该Var添加到命名空间的映射中。
- 与1相同。
- 将1处的var绑定到:TEST-1。
- 调用ns-unmap。
- 将2处的var绑定到:TEST-2。
免责声明:这是我第一次直接查看字节码,我可能遗漏了某些东西。
这里的基本问题是,var可以从加载方法中访问,但当执行到步骤5时,var不再
可以从命名空间映射中访问。因此,Var的根设置为:TEST-2,但该Var没有从命名空间映射。
在没有AOT编译的情况下,这也可行,以及当我会使用
(ns unmap-test.core)
(def a :test-1)
(ns-unmap 'unmap-test.core 'a)
(intern 'unmap-test.core 'a :test-2)
我明白,在Clojure中创建def,取消其映射,然后再次创建它通常是一种糟糕的做法。
我们有一个奇特的情况,我们需要在同一命名空间中有一个接口和一个同名的Var。简单地
执行definterface然后def会导致编译失败
user=> (definterface abc)
user.abc
user=> (def abc 1)
CompilerException java.lang.RuntimeException: 期望var,但abc映射到接口user.abc,编译:(/private/var/folders/3m/tvc28b5d7p50v5_8q5ntj0pmflbdh9/T/form-init4734176956271823921.clj:1:1)
不过不深入细节,这基本上是一个维护内部框架代码的投机方法
而无需立即修改大量的下游用户代码。我们消除了在宏展开时使用接口的用法,
但仍需要在类路径上存在,以便它可以被下游命名空间导入。
有其他几种实现这种方法的方式,所以这并不是我们特别大的问题,但我认为这个问题值得提出。
这只是我第一次尝试,结果让我感到惊讶。
请注意,在我的示例项目中使用了Clojure 1.8.0 RC4版本,但在1.7.0上也出现了相同的行为。
相关链接
- 初始化类load方法的字节码: https://gist.github.com/WilliamParker/d8ef4c0555a30135f35a
- init0方法的字节码: https://gist.github.com/WilliamParker/dc606ad086670915efd9
- 初始化类的反编译Java代码。请注意,根据我所知,这并不完全与字节码对齐,
但它比字节码更快地获得对正在发生的事情的总体了解。
https://gist.github.com/WilliamParker/4cc47939f613d4595d94
- 包含上述代码的简单项目: https://github.com/WilliamParker/unmap-test
请注意,如果您在没有AOT编译的情况下尝试它,应删除包含之前编译类的目标文件夹。