Проверка ошибок реализации прокси метода с использованием библиотеки модификации байт-кода ASM

У меня возникают трудности с использованием библиотеки байт-кода ASM для создания прокси-методов.

Я хочу преобразовать следующий код:

public ReturnType doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    ReturnType returnType = new ReturnType();
    returnType.setDataOne( parameterOne.getDataOne() );
    returnType.setDataTwo( parameterTwo.getDataTwo() );
    return returnType;
}

to:

public ReturnType copyOff_doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    ReturnType returnType = new ReturnType();
    returnType.setDataOne( parameterOne.getDataOne() );
    returnType.setDataTwo( parameterTwo.getDataTwo() );
    return returnType;
}

public ReturnType doSomething( ParameterOne parameterOne, 
    ParameterTwo parameterTwo ){

    return copyOff_doSomething( parameterOne, parameterTwo );
}

Чтобы создать метод copyOff_doSomething(), я использую следующий код:

public MethodVisitor visitMethod( int access, String name, String desc, 
    String signature, String[] exceptions ) {

    System.out.println(
        "access= " + access + ", name = " + name + ", desc = " +
                  desc + ", signature = " + signature );

    if ( name.equals( "doSomething" ) ){

        MethodVisitor methodVisitor =
            super.visitMethod( access, "copyOff_" + name, desc, 
                signature, exceptions );

        return methodVisitor;
    }
    else {
        return super.visitMethod( access, name, desc, signature, exceptions );
    }
}

Вышеприведенный код эффективно удаляет исходный метод doSomething() и копирует его в copyOff_doSomething() вместе с его телом кода.

Моя проблема возникает, когда я создаю метод замены doSomething():

@Override
public void visitEnd() {

    MethodVisitor mv = super.visitMethod( ACC_PUBLIC, "doSomething", 
        "(Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;" + 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)" + 
        "Lcom/javaspeak/classloader/tests/proxymethod/ReturnType;", 
            null, null );

    mv.visitCode();
    Label l0 = new Label();
    mv.visitLabel( l0 );
    mv.visitLineNumber( 18, l0 );
    mv.visitVarInsn( ALOAD, 0 );
    mv.visitVarInsn( ALOAD, 1 );
    mv.visitVarInsn( ALOAD, 2 );

    mv.visitMethodInsn( INVOKEVIRTUAL, 
        "com/javaspeak/classloader/tests/proxymethod/FinalMethod", 
           "copyOff_doSomething", 
               "(Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;" +        
               "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)" + 
               "Lcom/javaspeak/classloader/tests/proxymethod/ReturnType;");

    mv.visitInsn( ARETURN );
    Label l1 = new Label();
    mv.visitLabel( l1 );

    mv.visitLocalVariable( "this", 
       "Lcom/javaspeak/classloader/tests/proxymethod/FinalMethod;", 
           null, l0, l1, 0 );

    mv.visitLocalVariable( "parameterOne", 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;", 
            null, l0, l1, 1 );

    mv.visitLocalVariable( "parameterTwo", 
        "Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;", 
            null, l0, l1, 2 );

    mv.visitMaxs( 3, 3 );
    mv.visitEnd();
    super.visitEnd();
}

Проблема в том, что я получаю следующую ошибку:

java.lang.VerifyError: Bad type on operand stack in method 
com.javaspeak.classloader.tests.proxymethod.ProxyMethod.doSomething(
Lcom/javaspeak/classloader/tests/proxymethod/ParameterOne;
Lcom/javaspeak/classloader/tests/proxymethod/ParameterTwo;)
Lcom/javaspeak/classloader/tests/proxymethod/ReturnType; at offset 3

Я не знаю, что означает ошибка и как исправить ошибку. Как упоминалось выше, я использовал ASMfier для генерации кода для замены метода doSomething().

Моя стратегия заключалась в том, чтобы использовать метод visitMethod для переименования doSomething() в copyOff_doSomething(), а затем использовать метод visitEnd() для создания нового замещающего метода doSomething() с нуля, который вызывает метод copyOff_doSomething().

Возможно, ASM не соответствует моей стратегии, и мне следует действовать по-другому?

Возможно, мне нужно изменить код, созданный ASMfier для замены метода doSomething(). Мое понимание сгенерированного кода не так уж хорошо.

Я использую JDK 1.7 и ASM 4.0. Я использовал ASMfier, чтобы посмотреть, какие инструкции байт-кода ASM использовать для генерации замещающего метода doSomething().

Если кто-нибудь знает ASM, ваша помощь будет высоко оценена?

Ваше здоровье

Джон


person John Dickerson    schedule 06.11.2012    source источник
comment
разобрать код doSomething и посмотреть, что находится по смещению 3. Скорее всего, там инструкция invokevirtual. Проверьте, подается ли он с правильными аргументами.   -  person Alexei Kaigorodov    schedule 06.11.2012
comment
В ответ Алексей, как дизассемблировать код? Есть ссылка как разобрать?   -  person John Dickerson    schedule 07.11.2012
comment
javap -c java.class.name.to.decode — javap — это инструмент из JDK. Существуют и другие дизассемблеры - google.ru/search?q=java+disassembler   -  person Alexei Kaigorodov    schedule 07.11.2012
comment
это рассматривается в FAQ по ASM:: 4. Почему я получаю ошибку верификатора [xxx]? asm.ow2.org/doc/faq.html#Q4 это должно быть какой-то помощи.   -  person vijay    schedule 07.11.2012
comment
Спасибо за ответы, Алексей и Виджей.   -  person John Dickerson    schedule 07.11.2012


Ответы (1)


Скорее всего, параметр owner (жестко закодированный в com/javaspeak/classloader/tests/proxymethod/FinalMethod в вашем примере) не соответствует фактическому типу для this. Таким образом, ваш адаптер должен использовать значение name из вызова ClassVisitor.visit(..).

Также см. раздел «Замена тела метода» в моей статье AOSD'07 «Использование ASM». Framework для реализации общих шаблонов преобразования байт-кода».

person Eugene Kuleshov    schedule 07.11.2012
comment
Евгений, спасибо, очень помог ваш ответ. Я изменил имя FinalMethod на ProxyMethod, и это сработало. Очень признателен. Спасибо. - person John Dickerson; 08.11.2012