Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected use of final in emitted class #8

Open
joinr opened this issue Sep 10, 2020 · 3 comments
Open

Unexpected use of final in emitted class #8

joinr opened this issue Sep 10, 2020 · 3 comments

Comments

@joinr
Copy link

joinr commented Sep 10, 2020

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?

@joinr
Copy link
Author

joinr commented Sep 10, 2020

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;
    }
}

@joinr
Copy link
Author

joinr commented Sep 10, 2020

clj-decompiler may be wrong

@athos
Copy link
Owner

athos commented Sep 10, 2020

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);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants