Получение VerifyError при добавлении блока try/catch в байт-код через ASM

В своем Java-агенте я инструментирую классы с помощью ASM. Я пытаюсь обернуть определенные методы с помощью try/catch, а также отследить methodEnter и methodExit.

С "-noverify" код работает отлично.

Без этой опции он завершается с ошибкой VerifyError («Ожидаемый кадр карты стека в этом месте») — очевидно, потому что я не вызываю visitFrame.

Я использую COMPUTE_MAXS для ClassWriter и EXPAND_FRAMES для ClassReader. Я не могу использовать COMPUTE_FRAMES, потому что это потребует загрузки всех зависимых классов перед инструментированием, что невозможно.

Мой текущий код:

// ...

@Override
protected void onMethodEnter() {
    visitLogMethodCallEntry();
}

@Override
public void visitCode() {
    super.visitCode();
    mv.visitLabel(startFinally);
}

@Override
public void visitMaxs(int maxStack, int maxLocals) {
    Label handler = new Label();
    mv.visitTryCatchBlock(startFinally, handler, handler, null);
    mv.visitLabel(handler);
    visitLogMethodCallException();
    mv.visitInsn(ATHROW);
    mv.visitMaxs(maxStack, maxLocals);
}

@Override
protected void onMethodExit(int opcode) {
    if (opcode != ATHROW) {
        visitLogMethodCallExit();
    }
}

// ...

Любая помощь очень приветствуется.


person Andrey Cheptsov    schedule 29.07.2016    source источник


Ответы (1)


На первый взгляд, не видя полного кода, вызывающего проблему, вы смешиваете вызовы super.visitXX() и mv.visitXX().

Кроме того, зависимые классы не должны быть загружены. Вы можете предоставить собственную реализацию ClassWriter.getCommonSuperClass(). Например, см. ClassWriterComputeFramesTest.

person Eugene Kuleshov    schedule 29.07.2016
comment
Вы правы насчет вызовов super.visitXX() и mv.visitXX(), но проблема не в них. Что касается зависимых классов, то их не обязательно загружать, я вообще не уверен, что вы здесь правы. Если вы используете COMPUTE_MAXS, вам потребуется правильная реализация ClassWriter.getCommonSuperClass(). Я видел ClassWriterComputeFramesTest и не нашел там никакого полезного кода. - person Andrey Cheptsov; 30.07.2016
comment
Что ты имеешь в виду? Там есть перезаписанный getCommonSuperClass(). Проверьте это, но вам, возможно, придется написать собственную абстракцию репозитория классов, чтобы анализировать иерархии объектов без отражения. - person Eugene Kuleshov; 01.08.2016
comment
Что касается другой проблемы, вам придется изолировать ее в простом модульном тесте. Фрагмент, который вы разместили, не является полным. - person Eugene Kuleshov; 01.08.2016
comment
Юджин, если вы посмотрите на ClassWriterComputeFramesTest, вы увидите, что он ссылается на classLoader в getCommonSuperClass() для получения экземпляров классов. Это не будет работать в агенте Java, потому что classLoader еще не загрузил классы. - person Andrey Cheptsov; 02.08.2016
comment
Посмотрите внимательнее. Он использует ClassLoader для загрузки ресурсов и обработки этих ресурсов для получения информации о типе, но не загружает классы. - person Eugene Kuleshov; 03.08.2016
comment
Евгений, вы были правы. Я взял код ClassWriterComputeFramesTest и изменил его, чтобы использовать ClassLoader, переданный ClassFileTransformer, и это сработало. Большое спасибо! - person Andrey Cheptsov; 03.08.2016