Рекомендую прочитать JVM Spec §3.12. Throwing and Handling Exceptions. Он содержит пример, который очень прост, но все еще демонстрирует проблемы с вашей идеей:
Компиляция конструкций try-catch проста. Например:
void catchOne() {
try {
tryItOut();
} catch (TestExc e) {
handleExc(e);
}
}
скомпилирован как:
Method void catchOne()
0 aload_0 // Beginning of try block
1 invokevirtual #6 // Method Example.tryItOut()V
4 return // End of try block; normal return
5 astore_1 // Store thrown value in local var 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual #5 // Invoke handler method:
// Example.handleExc(LTestExc;)V
11 return // Return after handling TestExc
Exception table:
From To Target Type
0 4 5 Class TestExc
Здесь catch
блока заканчивается return
инструкции, таким образом, не присоединяется с исходным кодом потоком. Это, однако, не является обязательным поведением. Вместо этого, скомпилированный код может иметь ветвь последней return
инструкции в месте 4 return
инструкции, т.е.
Method void catchOne()
0: aload_0
1: invokevirtual #6 // Method tryItOut:()V
4: goto 13
7: astore_1
8: aload_0
9: aload_1
10: invokevirtual #5 // Method handleExc:(LTestExc;)V
13: return
Exception table:
From To Target Type
0 4 7 Class TestExc
(например, по меньшей мере, одна версия Eclipse, составленный на примере именно так)
Но это могло также быть наоборот, с ответом на инструкцию 4
вместо последней инструкции return
.
Method void catchOne()
0 aload_0
1 invokevirtual #6 // Method Example.tryItOut()V
4 return
5 astore_1
6 aload_0
7 aload_1
8 invokevirtual #5 // Method Example.handleExc(LTestExc;)V
11 goto 4
Exception table:
From To Target Type
0 4 5 Class TestExc
Таким образом, у вас уже есть три возможности скомпилировать этот простой пример, который не содержит никаких условных обозначений. Условные ветви, связанные с циклами или команды if
, необязательно указывают на инструкцию сразу после условного блока кода.Если за этим блоком кода будет следовать другая команда управления потоком, условная ветвь (то же самое относится к switch
целям) может привести к короткому замыканию ветви.
Так что очень сложно определить, какой код принадлежит блоку catch
. На уровне байтового кода он даже не должен быть смежным блоком, но может чередоваться с другим кодом.
И в это время мы даже не говорили о compiling finally
и synchronized
или новее try(…)
with resources заявление. Все они создают обработчики исключений, которые выглядят как catch
блоков на уровне байтового кода.
Поскольку отраслевые инструкции внутри обработчика исключений могут предназначаться код за пределами обработчика при восстановлении после исключения, пересекающего кода графика обработчика исключений здесь не помогут, как обрабатывать команды перехода правильно требует очень информации о целевой ветке, которую вы на самом деле хотите собрать.
Таким образом, единственный способ справиться с этой задачей - сделать напротив. Вы должны пересечь граф кода из начала метода для не исключительного выполнения и рассматривать каждую встреченную команду как не принадлежащую обработчику исключений. Для простой задачи удаления обработчиков исключений это уже достаточно, так как вам просто нужно сохранить все встреченные инструкции и сбросить все остальные.
Но как найти (4)? – Brandon
Следуя цепочке инструкций через все возможные ветви, условные прыжки и вложенные блоки try/catch. –