jboss-javassist / javassist

Java bytecode engineering toolkit
www.javassist.org
Other
4.12k stars 699 forks source link

Exception thrown when "insertBefore" a return expression #241

Open tangzhenhuang opened 5 years ago

tangzhenhuang commented 5 years ago

Recently, I am doing a mock function on methods, one of which is to modify the bytecode before the method starts. I inserted the following code with the insertBefore method:

        "{" +
            "if (!isRecord()) {" +
            "   return getMockData(\"%s\", $args, $type);" +
            "} "
        "}";

Among them, the definition of the getMockData method is:

/**
     * Get mock data
     *
     * @param uniqueMethodName The name of the method
     * @param args The arguments of the method
     * @param <T> The result type of the method
     * @return return mock data
     * @throws java.lang.Throwable
     */
public static <T> T getMockData(String uniqueMethodName, Object[] args, Class<T> clazz) throws Throwable;

But when it started, it throws the following exceptions:

Exception in thread "main" java.lang.VerifyError: Bad return type
Exception Details:
  Location:
    AnotherPluginTest.test()I @20: areturn
  Reason:
    Type 'java/lang/Object' (current frame, stack[0]) is not assignable to integer (from method signature)
  Current Frame:
    bci: @20
    flags: { }
    locals: { }
    stack: { 'java/lang/Object' }
  Bytecode:
    0x0000000: b800 d99a 0012 12d0 03bd 0006 12da b800
    0x0000010: e0b8 00e2 b004 b800 e4b2 0002 1203 b600
    0x0000020: 0404 a700 104b 12d0 03bd 0006 2ab8 00d2
    0x0000030: 2abf 3d12 d003 bd00 061c bb00 d459 5d58
    0x0000040: b700 d6b8 00d8 1cac                    
  Exception Handler Table:
    bci [25, 37] => handler: 37
  Stackmap Table:
    same_frame(@21)
    same_locals_1_stack_item_frame(@37,Object[#36])
    same_locals_1_stack_item_frame(@50,Integer)

    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)

When I delete the "insertBefore" expression, it was normal again. So I believe there are some problems with this statement("insertBefore" with return expression).

Thanks.

chibash commented 5 years ago

The type parameter T is java.lang.Object at the byte code level. So you must insert explicit type cast before getMockData().

tangzhenhuang commented 5 years ago

But when I changed the code to:

        "{" +
            "if (!isRecord()) {" +
            "   return ($r)getMockData(\"%s\", $args, $type);" +
            "} "
        "}";

It throws the following exception, doesn't $r work?

Caused by: javassist.CannotCompileException: [source error] no such class: $r
    at javassist.CtBehavior.insertBefore(CtBehavior.java:774)
    at javassist.CtBehavior.insertBefore(CtBehavior.java:734)
    at com.seewo.honeycomb.ttm.agent.bytecode.EMethod.insertBefore(EMethod.java:27)
    ... 48 more
Caused by: compile error: no such class: $r
    at javassist.compiler.MemberResolver.searchImports(MemberResolver.java:468)
    at javassist.compiler.MemberResolver.lookupClass(MemberResolver.java:412)
    at javassist.compiler.MemberResolver.lookupClassByName(MemberResolver.java:315)
    at javassist.compiler.MemberResolver.resolveClassName(MemberResolver.java:502)
    at javassist.compiler.TypeChecker.resolveClassName(TypeChecker.java:132)
    at javassist.compiler.TypeChecker.atCastExpr(TypeChecker.java:546)
    at javassist.compiler.JvstTypeChecker.atCastExpr(JvstTypeChecker.java:104)
    at javassist.compiler.ast.CastExpr.accept(CastExpr.java:55)
    at javassist.compiler.CodeGen.doTypeCheck(CodeGen.java:242)
    at javassist.compiler.CodeGen.compileExpr(CodeGen.java:229)
    at javassist.compiler.CodeGen.atReturnStmnt2(CodeGen.java:615)
    at javassist.compiler.JvstCodeGen.atReturnStmnt(JvstCodeGen.java:425)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:363)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:351)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atIfStmnt(CodeGen.java:398)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:355)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:351)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.Javac.compileStmnt(Javac.java:569)
    at javassist.CtBehavior.insertBefore(CtBehavior.java:754)
    ... 50 more
chibash commented 5 years ago

The exception says the type of $r was not found. I'm not sure what is the type of $r but you should check it is in the class path.

tangzhenhuang commented 5 years ago

I think this is because "$r" doesn't have the effect of escaping, because even if I add Class.forName($type.getName()) before this, it doesn't help. According to the documentation, $r doesn't work. It should have the effect of escaping.

chibash commented 5 years ago

OK, you're right. $r was not available in the code passed to insertBefore. I've fixed this bug. Could you try javassist.jar available from HEAD of the master branch.

tangzhenhuang commented 5 years ago

I have tried it, this bug no longer exists, thanks.