我正在尝试:
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
所暗示的那样,此类的实例是链接对象图的一部分,因此,只能是一个InsnList
的一部分。另请参阅此答案。
复制列表可能会变得安静低效,特别是因为 ASM 的访问者 API(树 API 所基于的(可以在一次传递中轻松处理在某些原始指令出现时插入指令的任务。
由于InsnList
与访问者 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);
}
}
}