嗨,各位 - 我发现带类型提示与不带类型提示的letfn
似乎存在一个局部清除问题
(defn- letfn-prim []
(letfn [(f [coll ^long n]
)]
(f (take 3 (range)) 0)))
(defn- letfn-noprim []
(letfn [(f [coll n]
)]
(f (take 3 (range)) 0)))
在实践中,我在f
中懒加载地处理coll,并想避免保留头(head)
noprim变体正确地清除了coll参数,而prim版本并没有。
我知道letfn
并不完全支持类型提示,但这个似乎不仅是意外的装箱(boxing),而且影响远不止提示的变量。
在这两种情况下,调用者都调用f.invoke(Object)
(invokeinterface clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
),但差异在于f
函数。(可争论带类型提示的版本可以直接调用invokePrim
,但正如我所言,已知letfn
并不完全支持类型提示)
在未提示的情况下,invoke
很 trivial
public java.lang.Object invoke(java.lang.Object, java.lang.Object);
Code:
0: aconst_null
1: areturn
添加更多主体后,它会尽快清除参数。
但在提示的情况下,我们得到这个
public final java.lang.Object invokePrim(java.lang.Object, long);
Code:
0: aconst_null
1: areturn
public java.lang.Object invoke(java.lang.Object, java.lang.Object);
Code:
0: aload_0
1: aload_1
2: aload_2
3: checkcast #22 // class java/lang/Number
6: invokestatic #28 // Method clojure/lang/RT.longCast:(Ljava/lang/Object;)J
9: invokeinterface #30, 4 // InterfaceMethod clojure/lang/IFn$OLO.invokePrim:(Ljava/lang/Object;J)Ljava/lang/Object;
14: areturn
invoke
像平常一样调用到invokePrim
,但它不会在这样做之前清除其局部变量。
(defn- defn-prim [coll ^long n]
)
与之相比,其字节码中的invoke方法确实清除了对象。
public static java.lang.Object invokeStatic(java.lang.Object, long);
Code:
0: aconst_null
1: areturn
public java.lang.Object invoke(java.lang.Object, java.lang.Object);
Code:
0: aload_1
1: aconst_null
2: astore_1 // <--- local cleared here
3: aload_2
4: checkcast #21 // class java/lang/Number
7: invokestatic #27 // Method clojure/lang/RT.longCast:(Ljava/lang/Object;)J
10: invokestatic #29 // Method invokeStatic:(Ljava/lang/Object;J)Ljava/lang/Object;
13: areturn
public final java.lang.Object invokePrim(java.lang.Object, long);
Code:
0: aload_1
1: aconst_null
2: astore_1
3: lload_2
4: invokestatic #29 // Method invokeStatic:(Ljava/lang/Object;J)Ljava/lang/Object;
7: areturn
祝大家好!
詹姆斯