jsr:使用javassist插入后字节码中存在错误

jsr:使用javassist插入后字节码中存在错误,java,javassist,bytecode-manipulation,Java,Javassist,Bytecode Manipulation,我尝试使用带有“insertAfter()”的javassist在方法之后添加代码。 但运行代码时报告了错误: try { CtClass ctClass = ClassPool.getDefault().get(className.replace('/', '.')); CtMethod ctMethod = ctClass.getDeclaredMethod("display1"); ctMethod.insertBef

我尝试使用带有“insertAfter()”的javassist在方法之后添加代码。 但运行代码时报告了错误:

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();
        }
运行后,报告了错误:

线程“main”java.lang.VerifyError中出现异常:错误指令:a8

例外情况详情:

地点:

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

Error exists in the 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)
加载类:java/lang/VerifyError

使用javap获取字节码:

  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 */

目前,我可以使用ASM在方法之后添加代码,但效率不高,因为很多字节码都需要手动编写。

在javap命令输出中,如果您还提供类文件头(可以使用-v实现),它将显示类的主要版本,这将非常有用

根据您提供的信息,我打赌该类的主要版本是51(java 7)或52(java 8),因为在这种情况下不支持jrs操作码

如您所知(感谢@Holger提供直接链接。在第一次编辑中,我使用了a,因为我没有找到直接引用):

如果类文件版本号为51.0或更高,则jsr操作码或jsr_w操作码都不会出现在代码数组中

这可以解释为什么会出现验证错误

如果我不得不猜测为什么会出现该操作码,我会说您使用的是一个较旧的javassist版本,它仍然使用JSR操作码实现insertAfterMethod(finally语句使用了该操作码),如果您使用最新的javassist版本,它可能会得到修复

我知道这个答案更多的是基于一种猜测,即在实际的“确凿证据”中,可能并不准确。如果问题不是我所描述的,请编辑您的答案以添加更多信息,例如:

  • 类的标题信息,包括次要版本和主要版本
  • javap输出在javassist操作之前
  • javassist版本
  • 运行代码的当前JVM(品牌和版本)

升级javassist(3.25.0-GA)对我来说有助于解决这个问题。

这是最合理的解释,尤其是
code
具有
StackMapTable
属性。html网页链接是:“如果类文件版本号为51.0或更高,那么jsr操作码或jsr_w操作码都不会出现在代码数组中”@Holger:谢谢,在这种基于(大)猜测的回答中,很高兴看到其他人同意我的观点。关于StackMapTable,Java1.6中引入了它以加快字节码验证。所以op仍然可能运行JVM 1.6,其中jsr应该是一个有效的操作码,而这只是javassist注入造成的混乱。@Holger:还感谢您在规范中提供的HTML链接!我将更新答案只有版本50,其中定义了
StackMapTable
属性,但没有立即禁止
jsr
指令。请注意,
StackMapTable
无效,因为在子例程注入之后,它应该有三个条目,而不是两个条目。但是对于v50类文件,如果
StackMapTable
验证失败,那么验证器将故障切换到旧文件。因此,版本号很可能高于50,特别是当消息明确表示它根本不喜欢
jsr
时。@Holger:分析得很好,我没有注意到这一点!谢谢你提供更多的信息