java - 插入到InsnList的几个节点之前

标签 java java-bytecode-asm jvm-bytecode

我正在尝试:

1)迭代指令并找到所有相关节点

2) 在找到的节点之前插入自定义代码

我使用流和迭代器来制作和插入,这只适用于第一个节点

InsnList instructions = methodNode.instructions;
InsnList addition = ...

//It work: found n nodes for n return instructions
Stream<AbstractInsnNode> returnNodes = 
    Stream.iterate(instructions.getFirst(), AbstractInsnNode::getNext).limit(instructions.size())
        .filter(n -> returnOpcodes.contains(n.getOpcode()));

//It not work: inserted only before first node
returnNodes.forEach(n -> instructions.insertBefore(n, addition));

我也尝试过迭代器,但它也不起作用

ListIterator<AbstractInsnNode> iterator = instructions.iterator();
while (iterator.hasNext()) {
     AbstractInsnNode node = iterator.next();
     if (returnOpcodes.contains(node.getOpcode()))
            instructions.insertBefore(node, addition);
}

我期望将addition插入到所有返回节点之前,但它插入到第一个节点之前。

InsnList是一个链表,这样的插入必须有效。 我哪里错了?

最佳答案

正如类名 AbstractInsnNode 中的术语 node 所暗示的那样,此类的实例是链接对象图的一部分,因此只能是一个链接对象图的一部分InsnList。另请参阅this answer .

复制列表可能会变得非常低效,尤其是 ASM’s visitor API ,其中Tree API在此基础上构建,可以在一次传递中轻松处理在出现某些原始指令时插入指令的任务。

由于 InsnList 与 Visitor API 可以很好地互操作,您仍然可以使用它来定义要插入的指令序列,但您可以使用它在以下位置发出指令,而不是将其复制到另一个列表:正确的地方。

下面是复制整个类定义并在某些位置插入指令序列的草图:

class Victim {
    static void foo() {
        System.out.println("original code");
    }
}
public class InjectCode extends ClassVisitor {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        ClassReader cr = new ClassReader(
            InjectCode.class.getResourceAsStream("Victim.class"));
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
        cr.accept(new InjectCode(cw), 0);
        byte[] code = cw.toByteArray();

        MethodHandles.lookup().defineClass(code); // Java 9, for simplification

        Victim.foo();
    }

    InsnList insnList; // to be filled

    public InjectCode(ClassVisitor target) {
        super(Opcodes.ASM5, target);

        // just an example
        insnList = new InsnList();
        insnList.add(new FieldInsnNode(Opcodes.GETSTATIC,
                "java/lang/System", "out", "Ljava/io/PrintStream;"));
        insnList.add(new LdcInsnNode("Hello World"));
        insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
                "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"));
    }

    @Override
    public MethodVisitor visitMethod(
            int access, String name, String desc, String sign, String[] excp) {
        MethodVisitor target = super.visitMethod(access, name, desc, sign, excp);
        if(name.equals("foo")) { // fill your own trigger condition
            target = new InjectCodeMethodVisitor(api, target);
        }
        return target;
    }

    class InjectCodeMethodVisitor extends MethodVisitor {
        public InjectCodeMethodVisitor(int api, MethodVisitor methodVisitor) {
            super(api, methodVisitor);
        }

        @Override
        public void visitInsn(int opcode) {
            switch(opcode) {
                case Opcodes.RETURN: case Opcodes.ARETURN: case Opcodes.IRETURN: 
                case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: 
                case Opcodes.ATHROW:
                    insnList.accept(mv); // inject exiting method
            }
            super.visitInsn(opcode);
        }
    }
}

关于java - 插入到InsnList的几个节点之前,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57530863/

相关文章:

java - 与 ASM 字节码检测功能不兼容的参数

java - 一直坚持使用 Java Swing Timer

Java ASM 字节码修改——改变方法体

java - java中忽略多行注释

java - 如果在 ClassWriter 中设置了 COMPUTE_FRAMES,ASM 会跳过类

java - 如何使用ASM修改常量池?

java-bytecode-asm - 动态Java字节码操作框架比较

tomcat9 - Tomcat 服务未使用弹性 APM java 代理启动

java - 如何以编程方式从 PDF 文档中删除特定图像?

Java JPA - 如何使用列表放置对象并使用 CRUD 更新列表对象?