2

我成功实现了字节码方法内联优化,生成的代码对我来说似乎还可以。然而,验证失败消息:使用ASM api构造FrameNode

java.lang.VerifyError: Expecting a stackmap frame at branch target 47 
Exception Details: 
    Location: 
    code/sxu/asm/example/Caller.test(II)V @44: goto 
    Reason: 
    Expected stackmap frame at this location. 

以及相应的字节码是:

public void test(int, int); 
    flags: ACC_PUBLIC 
    Code: 
     stack=4, locals=8, args_size=3 
     0: iload_1  
     1: iload_2  
     2: iadd   
     3: aload_0  
     4: getfield  #14     // Field _callee:Lcode/sxu/asm/example/Callee; 
     7: iload_1  
     8: iload_2  
     9: istore_3  
     10: istore  4 
     12: astore  5 
     14: aload   5 
     16: getfield  #40     // Field code/sxu/asm/example/Callee._a:Ljava/lang/String; 
     19: invokevirtual #46     // Method java/lang/String.length:()I 
     22: aload   5 
     24: getfield  #49     // Field code/sxu/asm/example/Callee._b:Ljava/lang/String; 
     27: invokevirtual #46     // Method java/lang/String.length:()I 
     30: iadd   
     31: istore  6 
     33: iload   6 
     35: iload   4 
     37: iload_3  
     38: iadd   
     39: iadd   
     40: istore  6 
     42: iload   6 
     44: goto   47 
     47: isub   
     48: istore  7 
     50: getstatic  #59     // Field java/lang/System.out:Ljava/io/PrintStream; 
     53: iload   7 
     55: invokevirtual #65     // Method java/io/PrintStream.println:(I)V 
     58: getstatic  #59     // Field java/lang/System.out:Ljava/io/PrintStream; 
     61: ldc   #67     // String 1.......... 
     63: invokevirtual #70     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     66: return   
     LocalVariableTable: 
     Start Length Slot Name Signature 
       14  33  0 this Lcode/sxu/asm/example/Callee; 
       14  33  1  t I 
       14  33  2  p I 
       33  14  7 tmp I 
       0  67  0 this Lcode/sxu/asm/example/Caller; 
       0  67  1  a I 
       0  67  2  b I 
       50  17  7  r I 
     LineNumberTable: 
     line 16: 0 
     line 13: 14 
     line 14: 33 
     line 15: 42 
     line 18: 50 
     line 19: 58 
     line 20: 66 
} 

线9-11:存储堆叠参数。第14-44行:Callee的内联字节码,其最后的ireturn被替换为GOTO。在Java7验证失败

两个可能的解决方案:

  • 添加-XX:-UseSplitVerifier为VM参数。该选项有效,但在Java8中已弃用。

  • 在第44行(GOTO指令之前)中添加stackmap表,该表列出了跳转目标位置处的类型(来自Stackmap frame description)。

对我来说,选项2是可取的,但我在框架的建设有问题。我的代码是:

if (opcode == Opcodes.RETURN || opcode == Opcodes.IRETURN) { 
     FrameNode stackMap = new FrameNode(Opcodes.NEW, -1, null, -1, null); 
     stackMap.accept(mv); //Visit The framenode before GOTO 
     super.visitJumpInsn(Opcodes.GOTO, end); 
    } else { 
     super.visitInsn(opcode); 
    } 

我认为无论Opcodes.NEW/SMAE应该在这里工作。但它是不可能的,因为访问者还没有访问目标代码,它不知道nStack,nlocals ..

所以任何人都可以在这里构建FrameNode的建议或处理这个例子案件?谢谢。

为FramNode FrameNode ASM Document的描述:

公共FrameNode(整型, INT nLocal, 对象[]本地, INT nStack, 对象[]堆栈)

构造一个新的FrameNode。

参数:

type - the type of this frame. Must be Opcodes.F_NEW for expanded frames, or Opcodes.F_FULL, Opcodes.F_APPEND, Opcodes.F_CHOP, Opcodes.F_SAME or Opcodes.F_APPEND, Opcodes.F_SAME1 for compressed frames. 
nLocal - number of local variables of this stack map frame. 
local - the types of the local variables of this stack map frame. Elements of this list can be Integer, String or LabelNode objects (for primitive, reference and uninitialized types respectively - see MethodVisitor). 
nStack - number of operand stack elements of this stack map frame. 
stack - the types of the operand stack elements of this stack map frame. Elements of this list can be Integer, String or LabelNode objects (for primitive, reference and uninitialized types respectively - see MethodVisitor). 

回答

2

只要你的字节码是正确的,你可以让ASM为您创建框架节点。

我通常不喜欢这样:

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES) 
{ 
    @Override 
    protected String getCommonSuperClass(final String type1, final String type2) 
    { 
     // the default asm merge uses Class.forName(), this prevents that. 
     return "java/lang/Object"; 
    } 
}; 

cn.accept(cw); 

解释:与COMPUTE_FRAMES初始化类的作家,避免类装载问题覆盖getCommonSuperClass

假设无论您使用什么路径生成字节码(访问者,ClassNode等),最终您都会使用ClassWriter来生成类字节。

如果您确实想手动执行此操作,请先尝试此操作,然后使用asmifier来查看如何使用asm编写框架代码。

+0

无论如何,我认为生命太短,无法手动进行帧分析。 –