JiSE
JiSE copied to clipboard
Unexpected use of final in emitted class
Original java:
ThreadLocalRandom tlr = ThreadLocalRandom.current();
long l1 = tlr.nextLong(), l2 = tlr.nextLong();
char[] rt = new char[22];
rt[21] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;
rt[20] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;
rt[19] = tbl[(int)l1 & 0x3f]; l1 = l1 >>> 6;
Corresponding JiSE:
(let [^ThreadLocalRandom tlr (ThreadLocalRandom/current)
^long l1 (.nextLong tlr)
^long l2 (.nextLong tlr)
^chars rt (new [char] 22)]
(aset rt 21 (tbl ^int (& l1 0x3f)))(set! l1 (>>> l1 6))
(aset rt 20 (tbl ^int (& l1 0x3f)))(set! l1 (>>> l1 6))
(aset rt 19 (tbl ^int (& l1 0x3f)))(set! l1 (>>> l1 6))
...
)
Inspected with clj-decompiler, using (decompile ...), gives:
final char[] array = new char[22];
final ThreadLocalRandom current = ThreadLocalRandom.current();
final long nextLong = current.nextLong();
final long nextLong2 = current.nextLong();
array[21] = RandM.tbl[(int)(nextLong & 0x3FL)];
final long n = nextLong >>> 6;
array[20] = RandM.tbl[(int)(n & 0x3FL)];
final long n2 = n >>> 6;
array[19] = RandM.tbl[(int)(n2 & 0x3FL)];
...
long variable in let
is set to final. Is there a way to allow it to mutate?
Simple test from Peter Nagy:
^:public
(jise/defclass IsFinal
^:public ^:static ^long
(defm test [^int y]
(let [x 100]
(set! x (+ x y))
(.println System/out x)
(set! x (+ x y))
(+ x x))))
// Decompiling class: phoenix/flake/IsFinal
package phoenix.flake;
public class IsFinal
{
public static long test(final int y) {
final int x = 100 + y;
System.out.println(x);
final int n = x + y;
return n + n;
}
}
clj-decompiler may be wrong
Thank you for the feedback!
After some investigation, I'm figuring out what is going on. Finality information for local variables is not retained in the JVM bytecode and it only exists at compile time (as contrasted with fields’ finality, which is embedded as an access flag in the bytecode). So, nobody knows which local variable was declared final from the generated bytecode.
Probably, clj-java-decompiler (or its dependant library) infers a local variable to be declared final unless the decompiler witnesses an assignment for it. In fact, another Java decompiler JD doesn't seem to emit final
at all for the same code:
package example.aobench;
class IsFinal {
public static long test(int paramInt) {
int i = 100;
i += paramInt;
System.out
.println(i);
i += paramInt;
return (i + i);
}
}