Hey folks - I seem to be getting a locals clearing issue with a type-hinted vs non-type-hinted 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)))
(in practice, I'm lazily processing coll within f
and wanted to avoid retaining the head)
The noprim variant clears the coll param correctly; the prim version doesn't seem to.
(I'm aware letfn
doesn't fully support type-hints, but this one seems to be more than just an unexpected boxing, and affects more than just the hinted variable)
In both cases, the caller is calling f.invoke(Object)
(invokeinterface clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
), but the difference comes in the f
function. (Arguably the hinted version could call invokePrim
directly, but as I say, aware letfn
doesn't fully support type-hints)
In the unhinted case, invoke
is trivial:
public java.lang.Object invoke(java.lang.Object, java.lang.Object);
Code:
0: aconst_null
1: areturn
(adding more of a body clears the param as soon as it can)
but in the hinted case, we get this:
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
The invoke
calls through to invokePrim
as normal, but it doesn't clear its locals before doing so. compare to
(defn- defn-prim [coll ^long n]
)
whose bytecode does clear the object in its invoke method:
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
Cheers!
James