Momenteel ben ik bezig met een tool die java bytecode analyseert. Tijdens deze analyze is het zeer wenselijk om het type van een loop te kunnen bepalen aan de hand van de bytecode (do of while). De vraag is echter of dit altijd mogelijk is. Om dit onderscheid te kunnen maken moet je in de bytecode in feite zoeken naar een de loop-condition (als aanwezig) en de loop-body. In de meeste gevallen is een loop-condition wel te vinden ahv control flow analyse (als de loop-constructie slechts een code block bevat met een exit-edge (een tak die de loop verlaat), dan is dat het blok met de loop-condition). Als er meerdere code-blokken met een dergelijke exit-edge bestaan dan kan er blijkbaar ook vanuit de body uit de loop gesprongen worden. Exceptions daargelaten kan dat alleen met een return of een break statement. Een return is gemakkelijk op te sporen, maar breaks zijn een lastig verhaal. In deze situatie is het vaak nog mogelijk een onderscheid tussen loop-condition en loop-body te maken door te kijken naar het type van de laatste bytecode-instructie in elk blok, aangezien een loop-condition altijd met een IF-instructie eindigt, en een loop-body doorgaans niet; er zijn echter uitzonderingen, zie de code hieronder:
Gegeven bijvoorbeeld de volgende methode*:
*Let ff niet op de flauwe betekenis van deze methode, ik heb hem puur geschreven als voorbeeld
Javac compileert dit naar de volgende bytecode:
In de bytecode is de while-loop terug te vinden in de instructies 10 t/m 28. Aan de hand van de source code is makkelijk te achterhalen dat de loop-condition bestaat uit de instructies 10 t/m 12 en de loop-body beslaat dan 15 t/m 28, eindigend op een IF-statement. Het enige teken dat de ontsnapping aan de loop in instructie 28 door een break wordt veroorzaakt is de goto-instructie van regel 31. Het is echter niet ondenkbaar dat deze instructie door een andere compiler in een simpele optimalisatie-stap verwijderd wordt, aangezien deze goto-instructie naar de daaropvolgende instructie verwijst. Als we deze goto-instructie even wegdenken dan kunnen we de bytecode net zo goed als een do-loop decompileren:
Heb ik zojuist ontdekt dat het bepalen van het loop-type vanuit java bytecode, doch niet geheel willekeurig, een niet algemeen oplosbaar probleem is, of zie ik details over het hoofd?
Gegeven bijvoorbeeld de volgende methode*:
*Let ff niet op de flauwe betekenis van deze methode, ik heb hem puur geschreven als voorbeeld
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
| public static void earlyBreakWhile() {
System.out.print("....");
int i = 0;
while (i <= 5) {
System.out.print(i);
if (i++ >= 10)
break;
}
System.out.println();
} |
Javac compileert dit naar de volgende bytecode:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| public static void earlyBreakWhile(); Code: 0: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #32; //String ...: 5: invokevirtual #29; //Method java/io/PrintStream.print:(Ljava/lang/String;)V 8: iconst_0 9: istore_0 10: iload_0 11: iconst_5 12: if_icmpgt 34 15: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 18: iload_0 19: invokevirtual #30; //Method java/io/PrintStream.print:(I)V 22: iload_0 23: iinc 0, 1 26: bipush 10 28: if_icmplt 10 31: goto 34 34: getstatic #7; //Field java/lang/System.out:Ljava/io/PrintStream; 37: invokevirtual #31; //Method java/io/PrintStream.println:()V 40: return |
In de bytecode is de while-loop terug te vinden in de instructies 10 t/m 28. Aan de hand van de source code is makkelijk te achterhalen dat de loop-condition bestaat uit de instructies 10 t/m 12 en de loop-body beslaat dan 15 t/m 28, eindigend op een IF-statement. Het enige teken dat de ontsnapping aan de loop in instructie 28 door een break wordt veroorzaakt is de goto-instructie van regel 31. Het is echter niet ondenkbaar dat deze instructie door een andere compiler in een simpele optimalisatie-stap verwijderd wordt, aangezien deze goto-instructie naar de daaropvolgende instructie verwijst. Als we deze goto-instructie even wegdenken dan kunnen we de bytecode net zo goed als een do-loop decompileren:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
| public static void earlyBreakDo() {
System.out.print("...");
int i = 0;
do {
if (i > 5)
break;
System.out.print(i);
} while (i++ < 10);
System.out.println();
} |
Heb ik zojuist ontdekt dat het bepalen van het loop-type vanuit java bytecode, doch niet geheel willekeurig, een niet algemeen oplosbaar probleem is, of zie ik details over het hoofd?