由Ambrose Bonnaire-Sergeant (@ambrosebs)发现。
给定这个错综复杂的示例
(let [f +
p (promise)
d (delay (f) @@p)]
(deliver p d)
@d)
执行时,它失败
1. Unhandled java.lang.NullPointerException
Cannot invoke "clojure.lang.IFn.invoke()" because "this.f" is null
这两种情况下都会发生相同的错误
(let [f +
p (promise)
d (lazy-seq (f) (first @p))]
(deliver p d)
(first d))
这两种情况的原因是,局部变量清理在与它首次调用之后立即将 this.f = null;
赋值给,所以当递归调用再次达到 (f)
时,引用已经为nil。
(binding [*compiler-options* {:disable-locals-clearing false}]
(clj-java-decompiler.core/decompile
(defn repro []
(let [f +
p (promise)
d (delay (f) @@p)]
(deliver p d)
@d))))
=>
@Override
public Object invoke() {
final Object f = this.f;
this.f = null;
((IFn)f).invoke();
final IFn fn = (IFn)__deref.getRawRoot();
final IFn fn2 = (IFn)__deref.getRawRoot();
final Object p = this.p;
this.p = null;
final Object invoke = fn2.invoke(p);
this = null;
return fn.invoke(invoke);
}
这是错误还是未定义的行为?