Inserting to InsnList before several nodes

醉酒当歌 提交于 2019-12-23 02:56:11

问题


I am trying to:

1) Iterate instructions and find all relevant nodes

2) Insert custom code before found nodes

I used streams and iterator to make it and insert, which works only for the first node

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));

I also tried iterator and it also does not work

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

I was expecting that addition would be inserted before all the return nodes, but it inserted before the first.

InsnList is a linked list, and such insertion must work. Where am I wrong?


回答1:


As the term node in the class name AbstractInsnNode suggests, instances of this class are part of a linked object graph and for this reason, can only be part of one InsnList. See also this answer.

Copying lists may become quiet inefficient, especially, as ASM’s visitor API, on which the Tree API builds on, can handle the task of inserting instructions at occurrences of certain original instructions easily in a single pass.

Since InsnList interoperates with the Visitor API nicely, you may still use it to define the instruction sequence to insert, but instead of copying it to another list, you can use it to emit the instructions at the right place.

Here is the sketch for copying an entire class definition while inserting an instruction sequence at certain places:

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);
        }
    }
}


来源:https://stackoverflow.com/questions/57530863/inserting-to-insnlist-before-several-nodes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!