voismager
voismager

Reputation: 453

Java 8 / Fernflower Decompiler: Bug or Feature

OpenJDK 1.8.0_191

I compiled and decompiled a piece of code below using Fernflower.

public class Decompile {
    public static void main(String[] args) {
        final int VAL = 20;
        System.out.println(VAL);
    }
}

The output is:

public class Decompile {

   public static void main(String[] args) {
      boolean VAL = true;
      System.out.println(20);
   }
}

I'm confused, how did VAL become a boolean?

UPDATE:

In Intellij IDEA decompiled code looks like this:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class Decompile {
    public Decompile() {
    }

    public static void main(String[] args) {
        int VAL = true;
        System.out.println(20);
    }
}

Upvotes: 1

Views: 2318

Answers (3)

Antimony
Antimony

Reputation: 39451

The underlying issue is that Java bytecode has no notion of booleans, byte, chars, or shorts (except in type signatures). All local variables with those types are instead compiled to ints. Boolean true and false are compiled to 1 and 0 respectively.

What this means is that the decompiler has to guess whether a given local variable was supposed to be a boolean or an integer type. In this case, the value 20 is stored in the variable, which will never be stored in a variable of boolean type in Java code, so it should be easy for the decompiler to guess that it is an integer type based on the context. But it appears that Fernflower's boolean guesser is not that sophisticated.

For what it's worth, this is an inherently hard problem. Especially when you consider that non-Java bytecode doesn't have to follow the same patterns that Java does. It is perfectly valid for bytecode to use the same variable in both integer and boolean contexts. The Krakatau decompiler has a pretty sophisticated inference step for guessing whether variables should be booleans or not, but it will still get things wrong in situations like this.

Upvotes: 1

Amit Bera
Amit Bera

Reputation: 7325

It works like that as compiler do some optimization during the generation of the byte code. As VAL = 20; is final and not changing, so it can put the 20 in place of VAL without impacting the functionality in the second statement. Now the decompiler has only byte code and when it goes to read the Byte code it found 20 as inline in the second line. Byte code generated by the code as below:

   0: bipush        20
   2: istore_1
   3: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
   6: bipush        20
   8: invokevirtual #26                 // Method java/io/PrintStream.println:(I)V

Upvotes: 0

LppEdd
LppEdd

Reputation: 21134

The bytecode is

L0
 LINENUMBER 5 L0
 BIPUSH 20
 ISTORE 1
L1
 LINENUMBER 6 L1
 GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
 BIPUSH 20
 INVOKEVIRTUAL java/io/PrintStream.println (I)V

As you can see the BIPUSH pushes 20 onto the stack, then ISTORE takes the value and store it into the local variable.

It's a Fernflower problem.


For your interest the output for bytecode version 55 is

int VAL = true;
System.out.println(20);

You can see decompilers can be wrong :)

Upvotes: 2

Related Questions