Java 8 / Fernflower 反编译器:错误或功能
Java 8 / Fernflower Decompiler: Bug or Feature
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);
}
}
它的工作原理就像 compiler
在 byte code
的生成过程中做一些 optimization
。由于 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
字节码是
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 字节码没有布尔值、字节、字符或短裤的概念(类型签名除外)。所有具有这些类型的局部变量都被编译为整数。布尔值 true 和 false 分别编译为 1
和 0
。
这意味着反编译器必须猜测给定的局部变量应该是布尔值还是整数类型。在这种情况下,值20
存储在变量中,在Java代码中永远不会存储在boolean类型的变量中,所以反编译器应该很容易猜到它是一个基于上下文的整数类型。但 Fernflower 的布尔猜测器似乎并不那么复杂。
就其价值而言,这本身就是一个难题。特别是当您认为非 Java 字节码不必遵循与 Java 相同的模式时。字节码在整数和布尔上下文中使用相同的变量是完全有效的。 The Krakatau decompiler 有一个非常复杂的推理步骤来猜测变量是否应该是布尔值,但在这种情况下它仍然会出错。
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);
}
}
它的工作原理就像 compiler
在 byte code
的生成过程中做一些 optimization
。由于 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
字节码是
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 字节码没有布尔值、字节、字符或短裤的概念(类型签名除外)。所有具有这些类型的局部变量都被编译为整数。布尔值 true 和 false 分别编译为 1
和 0
。
这意味着反编译器必须猜测给定的局部变量应该是布尔值还是整数类型。在这种情况下,值20
存储在变量中,在Java代码中永远不会存储在boolean类型的变量中,所以反编译器应该很容易猜到它是一个基于上下文的整数类型。但 Fernflower 的布尔猜测器似乎并不那么复杂。
就其价值而言,这本身就是一个难题。特别是当您认为非 Java 字节码不必遵循与 Java 相同的模式时。字节码在整数和布尔上下文中使用相同的变量是完全有效的。 The Krakatau decompiler 有一个非常复杂的推理步骤来猜测变量是否应该是布尔值,但在这种情况下它仍然会出错。