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);
}
}
@scrutari да, JDCore, CFR и Procyon показывают VAL как int
VAL — это константа, которая встраивается, поэтому, хотя может быть запись о существовании переменной, она, вероятно, не знает, как определить, что это было.
P.S. если вам нравится работать на уровне байт-кода, стоит обратить внимание на ASM, так как он является основой для многих других библиотек для работы с байт-кодом. asm.ow2.io/asm4-guide.pdf




Это работает так, как compiler сделать некоторые optimization во время генерации byte code. Поскольку ВАЛ = 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-коде, поэтому декомпилятору должно быть легко догадаться, что это целочисленный тип, исходя из контекста. Но похоже, что логический угадчик Фернфлауэра не такой сложный.
Как бы то ни было, это по своей сути трудная проблема. Особенно, если учесть, что байт-код, отличный от Java, не должен следовать тем же шаблонам, что и Java. Для байт-кода вполне допустимо использовать одну и ту же переменную как в целочисленном, так и в логическом контексте. Декомпилятор Кракатау имеет довольно сложный шаг вывода для определения того, должны ли переменные быть логическими или нет, но в подобных ситуациях он все равно будет ошибаться.
Пробовали ли вы другие декомпиляторы для сравнения?