Как известно, в байткоде Явы есть инструкция jsr (+ ее вариант jsr_w), которая выполняет прыжок на subroutine, то есть что-то вроде бейсиковской GOSUB. В VM Spec описывается ее применение при компилировании связки try-finally и я благополучно всю жизнь этой самой спецификации и верил. Тем более, что вроде действительно пару раз наблюдал что-то похожее, хотя особо и не приглядывался. А теперь вот решил разобраться с этой конструкциией - а конструкции-то и нет.. При компилировании эталонного кода получаю следующее: Код (Text): void tryFinally() { try { tryItOut(); } finally { wrapItUp(); } } Спецификация: Код (Text): 0 aload_0 1 invokevirtual tryItOut() 4 jsr 14 7 return 8 astore_1 9 jsr 14 12 aload_1 13 athrow 14 astore_2 15 aload_0 16 invokevirtual wrapItUp() 19 ret 2 На самом деле: Код (Text): 0 aload_0 1 invokevirtual tryItOut() 4 aload_0 5 invokevirtual wrapItUp() 8 goto 18 11 astore_1 12 aload_0 13 invokevirtual wrapItUp() 16 aload_1 17 athrow 18 return Проверено на компиляторе Sun всех версий 1.5.x и паре последних 1.4.2.x. То есть тихой сапой выполняется просто инлайн блока finally. Его содержимое в результате копируется n раз, где n = <количество блоков catch> + 2. А если я пять видов Exception ловлю и в finally у меня три страницы кода?? Слабо было хотя бы опцию сделать, инлайнить или нет? Причем под это безобразие они ухитрились еще и теоретическую базу подвести, смотри например The Costs and Benefits of Java Bytecode Subroutines - Freund (1998) Subroutine Inlining and Bytecode Abstraction to Simplify Static and Dynamic Analysis - Biere (2005) Которая в основном сводится к причитаниям, какие эти прыжки сложные и как с ними неудобно жить. Согласен, верифицировать их неудобно и выглядят не здорово, но это же не оправдание, чтобы упрощать себе жизнь таким образом. Ну клоуны, слов нет.. Вопросов в итоге два: 1) Это все действительно так, или я что-то важное упустил из виду? Может есть все-таки какой-нибудь способ заставить компилятор 1.5.х генерировать нормальный код? 2) Получается, что инструкции jsr и jsr_w теперь вообще не будут присутствовать на выходе компилятора? Значит можно просто проверять код на их наличие, и если найдены - вывод, что кто-то поработал руками или обфускатором?
Хм, в шестой версии эти инструкции по-видимому уберут совсем: New Java SE 6 Feature: Type Checking Verifier Следует наверное ожидать заметного улучшения качества декомпиляции
Как оказалось, есть все же в javac соответствующая (естественно недокументированная) опция - называется -XDjsrlimit и задает по-видимому максимальный размер для инлайна. Вызов с -XDjsrlimit=0 будет всегда генерировать jsr/ret. P.S. мда, тихо сам с собой веду беседу.. но вдруг кому понадобится
Stiver Мне интересно! Просто добавить нечего Думаю, что другие java-реверсеры находятся в подобной ситуации.
mandavoshka Автор ответил на этот вопрос: В последствии он же выяснил, что не совсем категорично, но, в принципе, (это уже моё IMHO) можно делать такие выводы. Ещё можно убедиться в отстойности хвалёной жабы с низкоуровневой точки зрения... Для написания собственной оптимизированной ВМ подобные топики (и статьи Stiver'а тут на сайте) тоже представляют ценность.
Видимо я все же предприму - в меру свободного времени - попытку написать деобфускатор для Явы. Так как форум плохо приспособлен для этих целей, то заметки складываю сюда: http://stiver-rus.livejournal.com