Java 修改<;clinit>;带着ASM

Java 修改<;clinit>;带着ASM,java,java-bytecode-asm,Java,Java Bytecode Asm,你好,我对ASM有点问题。它生成一个具有字节码错误的类: Exception in thread "main" java.lang.VerifyError: Bad instruction Exception Details: Location: me/test/Main.<clinit>()V @0: wide Reason: Error exists in the bytecode at java.lang.Class.getDeclaredMe

你好,我对ASM有点问题。它生成一个具有字节码错误的类:

Exception in thread "main" java.lang.VerifyError: Bad instruction
Exception Details:
  Location:
    me/test/Main.<clinit>()V @0: wide
  Reason:
    Error exists in the bytecode

    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
    at java.lang.Class.privateGetMethodRecursive(Unknown Source)
    at java.lang.Class.getMethod0(Unknown Source)
    at java.lang.Class.getMethod(Unknown Source)
    at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
    at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

代码不正确的主要原因是多次误用
visitVarInsn

visitVarInsn
是为参数为局部变量索引的指令而设计的。由于它取决于特定的指令,参数如何编码到指令中,因此当与指令一起使用时,此方法会产生错误的代码,它不是为特定的指令而设计的。因此,与其

this.visitVarInsn(NEWARRAY, 8);
你应该使用

this.visitIntInsn(NEWARRAY, Opcodes.T_BYTE);
这同样适用于
this.visitVarInsn(SIPUSH,key.length)的错误调用
this.visitVarInsn(BIPUSH,key.length),但在这些情况下,复制的代码中还存在其他错误,例如,在第一次出现时

if (key.length > 128)
    this.visitVarInsn(SIPUSH, key.length);
else
    this.visitVarInsn(BIPUSH, key.length);
当常量值正好为
128
时,将中断。此外,
this.visitVarInsn(SIPUSH,i+32768)还包含虚假的
+32768

通常,您不应该重复此代码来生成最佳指令。或者,创建一个只需证明一次其正确性的实用方法,或者使用现有方法。例如,如果您从
指令适配器
继承,而不是直接从
方法访问者
,则可以使用它自动生成
ICONST\u x
BIPUSH
SIPUSH
LDC
。或者,当您从
GeneratorAdapter
继承时,您可以使用来实现相同的功能

因此使用,代码将非常简单

push(key.length);
newArray(Type.BYTE_TYPE);
for(int i = 0; i < key.length; i++) {
    this.visitInsn(Opcodes.DUP);
    push(i);
    push(key[i]);
    visitInsn(Opcodes.BASTORE);
}
push(键长);
newArray(Type.BYTE\u Type);
for(int i=0;i
@Holger密钥始终为2048字节。还有32768的那个我会的check@Holger这是一团糟。。。我删除了+32768,出现了以下情况:原始字节码:修改的字节码:最后一个问题:this()代码生成而不是(编译器)。我怎样才能解决这个问题?代码是一样的。标签不会显示,因为它们未使用。它们出现在编译器输出中,因为该方法附加了有关局部变量及其作用域的调试信息(作用域信息指的是标签)。您可以使用生成类似的调试信息,但是否需要?如前所述,可执行代码是相同的。您可以编译源代码而不使用调试信息(
javac
:使用
-g:none
),以获得与生成的代码类似的结果)。顺便说一句,生成代码仍然错误地将
visitVarInsn
用于
NEWARRAY
,这似乎恰好做了正确的事情,但如果某些(甚至显然不相关的)更改,可能会以令人惊讶的方式中断。这与您发布的代码无关。对于版本为51或更高版本的类文件,
StackMapTable
属性是必需的(如果代码有分支,请参见相关)。ASM可以为您生成它们,请参见或。是否?如果不是这个问题,我想提出一个新问题。
push(key.length);
newArray(Type.BYTE_TYPE);
for(int i = 0; i < key.length; i++) {
    this.visitInsn(Opcodes.DUP);
    push(i);
    push(key[i]);
    visitInsn(Opcodes.BASTORE);
}