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

欢迎!有关此操作的更多信息,请参阅关于页面

+1
编译器

This is basically a copy of my post from https://groups.google.com/forum/#!topic/clojure/Ozt5HQyM36I on the mailing list. Based on the replies there I'm not sure whether this should be logged as an enhancement or a defect. Please change the designation to whatever is appropriate.

I have found what appears to be a bug in AOT-compiled Clojure when ns-unmap is used; the root cause of this probably impacts other code that redefines Vars as well. I have the following reduced case

(ns unmap-test.core)

(def a :test-1)

(ns-unmap 'unmap-test.core 'a)

(def a :test-2)

It turns out that a is not resolvable when this namespace is loaded. When I looked at the compiled bytecode,
it appears that the following operations occur

  1. A call to RT.var withe 'unmap-test.core and 'a returns a Var, which is bound to a constant.
    This var is added to the namespaces's mapping during this call.
  2. Same as 1.
  3. The var from 1 is bound to :test-1.
  4. ns-unmap is called.
  5. The var from 2 is bound to :test-2.

Disclaimer: This is the first time I have had occasion to look directly at bytecode and I could be missing something.

The basic problem here is that the var is accessible from the load method, but when step 5 executes the var is no longer
accessible from the namespace mappings. Thus, the root of the Var is set to :test-2, but that Var is not mapped from the namespace.
This works when there is no AOT compilation, as well as when I use

(ns unmap-test.core)

(def a :test-1)

(ns-unmap 'unmap-test.core 'a)

(intern 'unmap-test.core 'a :test-2)

I realize that creating defs, unmapping them, and then recreating them is generally poor practice in Clojure.
We have an odd case in that we need to have an interface and a Var of the same name in the same namespace. Simply
doing definterface and then def causes a compilation failure

user=> (definterface abc)
user.abc
user=> (def abc 1)
CompilerException java.lang.RuntimeException: Expecting var, but abc is mapped to interface user.abc, compiling:(/private/var/folders/3m/tvc28b5d7p50v5_8q5ntj0pmflbdh9/T/form-init4734176956271823921.clj:1:1)

Without going into too much detail, this is basically a hack to allow us to refactor our internal framework code
without immediately changing a very large amount of downstream consumer code. We get rid of the usage of the interface during macroexpansion,
but it still needs to exist on the classpath so it can be imported by downstream namespaces.
There are a number of other ways to accomplish this, so it isn't a particularly big problem for us, but I thought the issue was worth raising.
This was just the first thing I tried and I was surprised when it didn't work.

请注意,我在样例项目中使用了Clojure的1.8.0 RC4版本,但我在1.7.0版本上也遇到了同样的行为。

相关链接

  1. init类的load方法字节码:[点击查看](https://gist.github.com/WilliamParker/d8ef4c0555a30135f35a)
  2. init0方法的字节码:[点击查看](https://gist.github.com/WilliamParker/dc606ad086670915efd9)
  3. init类的反编译Java代码。请注意,根据我的了解,这并不完全与字节码对齐,
    但相比字节码,这可以更快地获得发生情况的一般了解。
    https://gist.github.com/WilliamParker/4cc47939f613d4595d94
  4. 包含上述代码的简单项目:[点击查看](https://github.com/WilliamParker/unmap-test)
    请注意,如果您尝试在没有AOT编译的情况下运行,应删除任何先前编译的类的目标文件夹。

4 条答案

+1
by

评论由:bronsa 提出

此问题类似于CLJ-1604中的问题,提议的补丁将该修复扩展到所有变量,而不仅仅是clojure.core中的变量。

+1
by

评论由:hiredman 提出

这是否被clj-1604修复了?

+1
by

评论由:bronsa 提出

不是,CLJ-1604只处理clojure.core Vars,这个ticket中附加的补丁是在CLJ-1604提交的补丁基础上扩展的,用于处理其他命名空间

0
by
参考:[点击查看](https://clojure.atlassian.net/browse/CLJ-1874)(由 alex+import 报告)
...