На уровне байт-кода нет формальных объявлений локальных переменных, по крайней мере, не так, как известно из исходного кода. У метода есть объявление максимального количества локальных переменных, существующих одновременно, или «слотов», которые нужно зарезервировать для них. Локальная переменная появляется, когда ей присваивается фактическое значение (по индексу «слота»), и она существует как минимум до последнего чтения этого значения.
С помощью этих операций невозможно определить, когда заканчивается область действия переменной или имеют ли две переменные с непересекающимися областями общий слот (по сравнению с несколькими назначениями одной и той же переменной). Хорошо, если у них есть полностью несовместимые типы, их назначения дают подсказку.
Чтобы помочь в отладке, есть необязательный атрибут кода, предоставляющий подсказки о объявленных локальных переменных и их области действия, но он не обязателен для заполнения и не влияет на способ выполнения JVM байт-кода. Но здесь, кажется, атрибут присутствовал и использовался декомпилятором.
Когда я компилирую ваш пример кода с javac -g
, я получаю
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=7, args_size=1
0: iconst_3
1: anewarray #2 // class java/lang/String
4: astore_1
5: iconst_3
6: istore_2
7: aload_1
8: astore_3
9: aload_3
10: arraylength
11: istore 4
13: iconst_0
14: istore 5
16: iload 5
18: iload 4
20: if_icmpge 43
23: aload_3
24: iload 5
26: aaload
27: astore 6
29: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
32: aload 6
34: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 5, 1
40: goto 16
43: return
LocalVariableTable:
Start Length Slot Name Signature
29 8 6 name Ljava/lang/String;
0 44 0 args [Ljava/lang/String;
5 39 1 names [Ljava/lang/String;
7 37 2 var3 I
Объявленные переменные args
(параметр метода), names
, var3
и name
были назначены индексам переменных 0
, 1
, 2
и 6
, в таком порядке.
Существуют синтетические переменные без объявления,
- по индексу
3
для хранения ссылки на массив, который цикл повторяет
- по индексу
4
для хранения длины массива
- по индексу
5
для хранения индексной переменной int
, которая будет увеличиваться в цикле
Кажется, у декомпилятора есть простая стратегия для работы с переменными, не содержащимися в LocalVariableTable
. Он генерирует имя, состоящее из префикса "var"
и индекса в кадре стека. Таким образом, он генерировал имена var3
, var4
и var5
для синтетических переменных, описанных выше, и не заботился о том, что между этими сгенерированными именами и явно объявленными именами произошло столкновение имен, то есть var3
.
Теперь неясно, почему декомпилятор генерирует присваивание true
для переменной int
, но полезно знать, что в байт-коде Java нет выделенных boolean
инструкций по обработке, а скорее значений boolean
обрабатываются так же, как int
значения. Требуется соответствующая метаинформация, например объявления переменных, чтобы понять, когда значения должны интерпретироваться как boolean
значений. Возможно, описанное выше столкновение имен привело к тому, что декомпилятор впоследствии перепутал типы переменных, чтобы в конечном итоге считать тип значения не равным int
, и затем обратился к нему как к boolean
. Но это всего лишь предположение; здесь также может быть совершенно не связанная ошибка.