0

I tried to use javassist add code after method with "insertAfter()". But error reported when running the code:

try {
            CtClass ctClass = ClassPool.getDefault().get(className.replace('/', '.'));
            CtMethod ctMethod = ctClass.getDeclaredMethod("display1");
            ctMethod.insertBefore(
                    "name=\"我是name!这次用javassist了哦!\";" +
                    "value=\"我是value!\";" +
                    "System.out.println(\"我是加进去的哦,哈哈:\" + name);"
            );
            ctMethod.insertAfter("System.out.println(value);");
            return ctClass.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }

After running it, error reported:

Exception in thread "main" java.lang.VerifyError: Bad instruction: a8

Exception Details:

Location:

com/atlassian/api/examples/ForASMTestClass.display1()V @62: jsr

Reason:

Error exists in the bytecode

Bytecode:

0x0000000: 2a12 28b5 0026 2a12 2cb5 002a b200 2ebb

0x0000010: 0030 59b7 0032 1234 b600 382a b400 3ab6

0x0000020: 003c b600 40b6 0042 b200 122a b400 18b6

0x0000030: 001a b200 122a b400 20b6 001a 014d a800

0x0000040: 04b1 4cb2 0044 2ab4 0046 b600 48a9 01  

at transformer.modifycode.InstrumentationMain.main(InstrumentationMain.java:7)

load class:java/lang/VerifyError

Used javap to get the bytecode:

  public void display1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
  stack=4, locals=4, args_size=1
     0: aload_0
     1: ldc           #43                 // String xx
     3: putfield      #41                 // Field name:Ljava/lang/String;
     6: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
     9: aload_0
    10: getfield      #24                 // Field name:Ljava/lang/String;
    13: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    16: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
    19: aload_0
    20: getfield      #32                 // Field value:Ljava/lang/String;
    23: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    26: goto          42
    29: astore_1
    30: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
    33: aload_0
    34: getfield      #32                 // Field value:Ljava/lang/String;
    37: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    40: aload_1
    41: athrow
    42: getstatic     #18                 // Field java/lang/System.out:Ljava/io/PrintStream;
    45: aload_0
    46: getfield      #32                 // Field value:Ljava/lang/String;
    49: invokevirtual #26                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    52: aconst_null
    53: astore_3
    54: jsr           58
    57: return
    58: astore_2
    59: getstatic     #45                 // Field java/lang/System.out:Ljava/io/PrintStream;
    62: aload_0
    63: getfield      #47                 // Field value:Ljava/lang/String;
    66: invokevirtual #49                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    69: ret           2
  Exception table:
     from    to  target type
         6    29    29   any
  LineNumberTable:
    line 11: 6
    line 12: 16
    line 13: 26
    line 14: 30
    line 15: 40
    line 14: 42
    line 16: 52
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        0      58     0  this   Lcom/test/swing/ForASMTestClass;
  StackMapTable: number_of_entries = 2
    frame_type = 87 /* same_locals_1_stack_item */
      stack = [ class java/lang/Throwable ]
    frame_type = 12 /* same */

Currently, i can use ASM to add code after method, but it is not efficient since a lot of bytecode need to write mannually.

Hao Ma
  • 1
  • 3

2 Answers2

2

In your javap command output it would be helpful if you would also provided the class file header (that you can achieve by using -v) that would show the major version of your class.

From the info you provided my bet is that the major version of that class is either 51 (java 7) or 52 (java 8), because in such cases jrs opcode is not supported.

As you can read here (thank you to @Holger to provide the direct web reference link. In the first edit I had used a book reference since I hadn't found the direct reference):

If the class file version number is 51.0 or above, then neither the jsr opcode or the jsr_w opcode may appear in the code array.

This would explain why you would get that verification error.

If I would have to guess why that opcode shows up there I would say that you are using an older javassist version that still implements the insertAfterMethod using JSR opcode (which was used by finally statements) and it probably will be fixed if you use the latest javassist version.

I know this answer is more based in a guess that in actual "hard evidences" and may not be accurate. If the problem is not what I described, please edit your answer to add a bit more information, such as:

  • Header information of the class including minor and major versions
  • javap output before javassist manipulation
  • javassist version
  • current JVM (brand and version) you are running the code on
pabrantes
  • 2,161
  • 1
  • 28
  • 34
  • 2
    That’s the most plausible explanation, especially as the `Code` has a `StackMapTable` attribute. And the html web link is [«here»](http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.9.1-110-B.1): *“If the class file version number is 51.0 or above, then neither the jsr opcode or the jsr_w opcode may appear in the code array”* – Holger Aug 20 '15 at 17:55
  • @Holger: Thank you, in this kind of answer where it's based in a (big) guess it's nice to see others agreeing with me. Regarding StackMapTable, it was introduced in java 1.6 to fasten up bytecode verification. So it can still happen that op is running JVM 1.6 where jsr should be a valid opcode and it was just javassist injection messing things up. – pabrantes Aug 20 '15 at 18:03
  • @Holger: also thank you for the HTML link in the specification! I'll update the answer – pabrantes Aug 20 '15 at 18:04
  • 2
    There is only version 50, where `StackMapTable` attributes were defined but `jsr` instruction not immediately banned. Note that the `StackMapTable` is invalid as it should have three entries instead of two after the subroutine injection. But with v50 class files, the verifier fails over to the old one if `StackMapTable` validation fails. So it’s very likely that the version number is higher than 50 especially as the message clearly says that it doesn’t like the `jsr` at all. – Holger Aug 20 '15 at 18:20
  • @Holger: great analysis, I hadn't notice that! Thank you for the additional information – pabrantes Aug 21 '15 at 08:12
0

Upgrade of javassist (3.25.0-GA) was helpful to resolve this issues for me.

Deepak Singhvi
  • 727
  • 6
  • 13