OpenJDK 1.8.0_191
我使用Fernflower编译并反编译了下面的一段代码。
public class Decompile {
public static void main(String[] args) {
final int VAL = 20;
System.out.println(VAL);
}
}
输出是:
public class Decompile {
public static void main(String[] args) {
boolean VAL = true;
System.out.println(20);
}
}
我很困惑,VAL
怎么变成布尔值?
更新:
在Intellij IDEA反编译代码如下所示:
//
// 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);
}
}
字节码是
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
正如你所看到的那样,BIPUSH
将20
推入堆栈,然后ISTORE
获取值并将其存储到局部变量中。
这是一个Fernflower
问题。
为了您的兴趣,字节码版本55
的输出是
int VAL = true;
System.out.println(20);
你可以看到反编译器可能是错的:)
根本问题是Java字节码没有布尔值,字节,字符或短路的概念(类型签名除外)。具有这些类型的所有局部变量都被编译为int。布尔值true和false分别编译为1
和0
。
这意味着反编译器必须猜测给定的局部变量是应该是布尔值还是整数类型。在这种情况下,值20
存储在变量中,该变量永远不会存储在Java代码中的布尔类型变量中,因此反编译器应该很容易根据上下文猜测它是一个整数类型。但看起来Fernflower的布尔猜测并不那么复杂。
对于它的价值,这是一个固有的难题。特别是当您考虑非Java字节码不必遵循与Java相同的模式时。字节码在整数和布尔上下文中使用相同的变量是完全有效的。 The Krakatau decompiler有一个相当复杂的推理步骤,用于猜测变量是否应该是布尔值,但在这种情况下它仍然会出错。
它就像那样,因为compiler
在optimization
的生成过程中做了一些byte code
。因为VAL = 20;是最终的而不是改变,所以它可以把20
代替VAL
而没有impacting
在第二个声明中的功能。现在decompiler
只有byte code
,当它去读取字节代码时,它发现20
在second line
内联。代码生成的字节码如下:
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