当前位置: 首页 > 知识库问答 >
问题:

使用ASM api构建FrameNode

林彬
2023-03-14

我成功地实现了字节码方法内联优化,生成的代码对我来说似乎还可以。然而,验证失败,并显示以下消息:

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的内联字节码,其最后一个ibackGOTO替换。

Java7中验证失败的两种可能解决方案:

>

  • 添加-XX:-UseSplitVerifier用于VM参数。该选项有效,但在Java8中不推荐使用它。

    在第44行之前添加stackmap表(GOTO指令之前的表),该表列出了跳转目标位置的类型(来自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);
        }
    

    我认为两者都是操作码。新的/SMAE应该可以在这里使用。但无法计算其余四个参数,因为访问者没有访问目标代码,也不知道nStack、nlocals。。

    那么有人可以在这里给出构建FrameNode的建议或示例来处理这种情况吗?谢谢。

    FramNodeFrameNode ASM文档的说明:

    公共框架节点(int type、int nLocal、Object[]local、int nStack、Object[]stack)

    构造一个新的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). 
    
  • 共有1个答案

    宫元徽
    2023-03-14

    如果字节码正确,则可以让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\u FRAMES初始化类编写器,并避免类加载问题覆盖getCommonSuperClass。

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

    如果确实要手动执行,请先尝试此操作,然后使用asm生成器查看如何使用asm编写帧代码。

     类似资料:
    • 问题内容: 我是Gradle新手。我想构建一个uberjar(AKA fatjar),其中包括项目的所有传递依赖项。我需要在“ build.gradle”中添加哪些行? 这是我目前拥有的:(我是从几天前的某个地方复制的,但是不要从那里收集。) 问题答案: 您是否尝试过 gradle食谱中 的fatjar示例? 您正在寻找的是gradle 的影子插件

    • 注意:本书中的 Service Mesh 章节已不再维护,请转到 istio-handbook 中浏览。 本文介绍的内容将包括 : SOFAMosn 与 SOFAMesh 的关系 部署 SOFAMesh 至于 SOFAMesh 的使用跟 Istio 没有区别,只是截止本文发稿时 SOFAMosn 的流量管理只支持: 按 version 路由 按 weight 路由 按照特定 header 路由 其

    • 我开始学习AWS的CI/CD功能。到目前为止,我一直在基于microsoft/windowsservercore映像在Windows Server 2016本地创建docker映像,并手动将其推送到ECR(amazon容器注册表)。 在这一点上,我没有试图在CodeBuild中编译应用程序。我只是想建造容器。在本地,二进制文件位于子目录中,并复制到容器中。 CodeBuild项目失败,出现错误:无

    • 本文向大家介绍使用GruntJS构建Web程序之构建篇,包括了使用GruntJS构建Web程序之构建篇的使用技巧和注意事项,需要的朋友参考一下 大概有如下步骤     新建项目Bejs     新建文件package.json     新建文件Gruntfile.js     命令行执行grunt任务  一、新建项目Bejs 源码放在src下,该目录有两个js文件,selector.js和ajax

    • 请注意:在泽维尔的回答之后编辑的回答 我试图在Android Studio中为一个相同的应用程序项目使用不同的构建风格。然而,我似乎有一个可怕的时间配置它适当地工作。 步骤: null **我检查了分级设置,显然使用自动导入已经启用。尽管如此,对build.gradle文件进行更改不会自动更新构建变体。注意:我还尝试使用Build-Rebuild项目和/或Build-Make项目。我仍然必须关闭项

    • 当我使用API远程触发作业时,jenkins上的作业会说:“由远程主机IP启动”。但我的工作可能会因不同的原因和不同的来源而触发,所以如果我在詹金斯能看到一份工作并直接看到触发的原因,那就太好了。 我知道我可以传递一个字符串参数,然后在每个作业中查看参数-但这不是很明显。我希望在所有工作的列表中看到它。类似于:。 有什么办法可以做到这一点吗? 我注意到我们的发布作业采用了一个显示我想要的版本参数—