Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/398.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在使用bcel时构造方法的堆栈图_Java_Instrumentation_Bcel_Java Bytecode Asm - Fatal编程技术网

Java 在使用bcel时构造方法的堆栈图

Java 在使用bcel时构造方法的堆栈图,java,instrumentation,bcel,java-bytecode-asm,Java,Instrumentation,Bcel,Java Bytecode Asm,我试图通过在特定指令之前插入invoke来修改方法。 似乎我的指令插入将导致不同的stackmap表,它不能由bcel包本身自动生成。 因此,我的插装类文件包含旧的stackmap表,这将导致jvm出错。 我已经尝试过removeCodeAttributes,MethodGen的方法,它可以删除所有代码属性。它可以在简单的情况下工作,例如包装函数。现在对我来说,这是行不通的 public class Insert{ public static void main(String[] arg

我试图通过在特定指令之前插入invoke来修改方法。 似乎我的指令插入将导致不同的stackmap表,它不能由bcel包本身自动生成。 因此,我的插装类文件包含旧的stackmap表,这将导致jvm出错。 我已经尝试过removeCodeAttributes,MethodGen的方法,它可以删除所有代码属性。它可以在简单的情况下工作,例如包装函数。现在对我来说,这是行不通的

public class Insert{
    public static void main(String[] args) throws ClassFormatException, IOException{
        Insert isrt = new Insert();
        String className = "StringBuilder.class";
        JavaClass jclzz = new ClassParser(className).parse();
        ClassGen cgen = new ClassGen(jclzz);
        ConstantPoolGen cpgen = cgen.getConstantPool();
        MethodGen mgen = new MethodGen(jclzz.getMethods()[1], className, cpgen);
        InstructionFactory ifac = new InstructionFactory(cgen);
        InstructionList ilist = mgen.getInstructionList();
        for (InstructionHandle ihandle : ilist.getInstructionHandles()){
            System.out.println(ihandle.toString());
        }
        InstructionFinder f = new InstructionFinder(ilist);
        InstructionHandle[] insert_pos = (InstructionHandle[])(f.search("invokevirtual").next());
        Instruction inserted_inst = ifac.createInvoke("java.lang.System", "currentTimeMillis", Type.LONG, Type.NO_ARGS, Constants.INVOKESTATIC);
        System.out.println(inserted_inst.toString());
        ilist.insert(insert_pos[0], inserted_inst);


        mgen.setMaxStack();
        mgen.setMaxLocals();


        mgen.removeCodeAttributes();
        cgen.replaceMethod(jclzz.getMethods()[1], mgen.getMethod());

        ilist.dispose();
        //output the file
        FileOutputStream fos = new FileOutputStream(className);
        cgen.getJavaClass().dump(fos);
        fos.close();
    }
}

移除
StackMapTable
不是修复错误
StackMapTable
的正确解决方案。重要的理由是:

在版本号为50.0或更高版本的
文件中,如果方法的
code
属性没有
StackMapTable
属性,则该方法具有隐式堆栈映射属性()。此隐式堆栈映射属性相当于
StackMapTable
属性,其中
number\u of_entries
等于零

由于
StackMapTable
必须为每个分支目标都有显式条目,因此这种隐式
StackMapTable
将仅适用于无分支方法。但是在这些情况下,该方法通常没有显式的
StackMapTable
,因此您就不会有这个问题(除非该方法有分支,而您的插装被删除)

另一个结论是,如果将类文件版本号修补到
50
以下的值,则可以删除
StackMapTable
。当然,如果您不需要在版本
50
或更高版本中引入任何类文件特性,那么这只是一个解决方案

在一段宽限期内,JVM支持类文件的回退模式,该类文件的
StackMapTable
s已损坏,仅适用于像您这样工具支持不是最新的场景。(请参阅或)但宽限期现在已经结束,并且该支持已被拒绝,即Java 8 JVM不再支持回退模式

如果您想跟上Java开发和仪器更新的类文件(可能使用这些新版本的功能),您只有两个选择:

  • 手动计算正确的
    StackMapTable
  • 使用支持计算正确的
    StackMapTable
    属性的工具,例如
    ASM
    ,(请参阅)不支持该工具