Основной целью Label
является обозначение позиции в последовательности байт-кода. Поскольку это необходимо для целей ветвления, вы можете использовать их для идентификации основных блоков. Но вы должны знать, что они также используются для сообщения номеров строк, когда присутствует атрибут LineNumberTable
, и для сообщения областей локальной переменной, когда атрибут LocalVariableTable
присутствует, а также, для более новых файлов классов, их аннотации типов, записанные в атрибуте RuntimeVisibleTypeAnnotations
. Кроме того, метки могут отмечать защищенную область обработчика исключений. Для кода, сгенерированного из исходного кода Java, эта защищенная область соответствует блоку try
, поэтому он является базовым блоком, но его не нужно хранить для другого байт-кода.
См
visitLocalVariable(java.lang.String name, java.lang.String descriptor, java.lang.String signature, Label start, Label end, int index)
visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, java.lang.String descriptor, boolean visible)
visitTryCatchBlock(Label start, Label end, Label handler, java.lang.String type)
Поскольку область действия локальных переменных может охватывать последнюю инструкцию return
, после этой последней инструкции возможно встретить метки, что и происходит в вашем случае. Вы вводите bipush 7, invokestatic #13
после инструкции return
, что приводит к недоступности кода.
Очевидно, вы также используете опции COMPUTE_FRAMES
, чтобы позволить ASM пересчитать кадры стековой карты с нуля, но невозможно рассчитать кадры для недоступного кода из-за неизвестного начального состояния стека. ASM решает эту проблему, заменяя недоступный код инструкциями nop
, за которыми следует один оператор athrow
. Для этой последовательности можно указать допустимый начальный кадр стека, и это не повлияет на выполнение (так как код недоступен).
Как видите, четыре nop
инструкции плюс одна athrow
инструкция охватывают пять байтов, что соответствует размеру замененной последовательности bipush 7, invokestatic #13
.
Вы можете избавиться от большинства указанных меток, указав ClassReader.SKIP_DEBUG
для его accept
метод . Затем вы получите только одну сообщаемую метку для вашего примера, цель перехода, связанную с оператором if
. Но вы должны обработать visitJumpInsn
, чтобы определить начало условного кода.
Итак, чтобы идентифицировать все основные блоки, вы должны обработать все инструкции ветвления, т. Е. Через visitJumpInsn
, visitLookupSwitchInsn
и visitTableSwitchInsn
, а также все конечные инструкции, т. Е. athrow
и все варианты return
. Далее вам необходимо обработать все visitTryCatchBlock
звонки. Если вам нужно идентифицировать потенциальные цели инструкций ветвления за один проход, я бы использовал visitFrame
вместо меток, поскольку фреймы обязательны для всех целей ветвления для версии файла класса 51 (Java 7) или выше.
Кстати, когда все, что вы вводите, это последовательность загрузки константы и вызова статического метода (в доступных местах), я бы использовал COMPUTE_MAXS
вместо COMPUTE_FRAMES
, так как дорогой пересчет не необходимо, когда общая структура кода не изменяется.