ASM - TreeApi Method组件和接口简介
ASM的TreeApi 对于Method的转换、生成也提供了一系列的组件和接口。
MethodNode中大多数属性和方法都和ClassNode类似,其中最主要的属性就是InsnList了。
InsnList是一个双向链表对象,包含了存储方法的字节指令序。先来看下InsnList中的主要是属性和方法。
- public class InsnList { // public accessors omitted
- private int size;
- private AbstractInsnNode first;
- private AbstractInsnNode last;
- AbstractInsnNode[] cache;
- int size();
- AbstractInsnNode getFirst();
- AbstractInsnNode getLast();
- AbstractInsnNode get(int index);
- boolean contains(AbstractInsnNode insn);
- int indexOf(AbstractInsnNode insn);
- void accept(MethodVisitor mv);
- ListIterator iterator();
- ListIterator iterator(int index);
- AbstractInsnNode[] toArray();
- void set(AbstractInsnNode location, AbstractInsnNode insn);
- void add(AbstractInsnNode insn);
- void add(InsnList insns);
- void insert(AbstractInsnNode insn);
- void insert(InsnList insns);
- void insert(AbstractInsnNode location, AbstractInsnNode insn);
- void insert(AbstractInsnNode location, InsnList insns);
- void insertBefore(AbstractInsnNode location, AbstractInsnNode insn);
- void insertBefore(AbstractInsnNode location, InsnList insns);
- void remove(AbstractInsnNode insn);
- void clear();
- }
可以看到InsnList 中主要是对AbstractInsnNode对象的操作方法,AbstractInsnNode也就是链表中的元素。
AbstractInsnNode数组存储了字节码指令对象的链表连接关系。AbstractInsnNode是一个抽象父类,代表了字节指令的一个抽象类。AbstractInsnNode的主要方法如下。
- public abstract class AbstractInsnNode {
- public int getOpcode();
- public int getType();
- public AbstractInsnNode getPrevious();
- public AbstractInsnNode getNext();
- public void accept(MethodVisitor cv);
- public AbstractInsnNode clone(Map labels);
- }
子类
1 VarInsnNode
表示局部变量指令的节点。 局部变量指令是load或store局部变量值的指令。
(代表局部变量表的操作指令对象,如xstore,xload)是和MethodVisitor中的visitVarInsn(int opcode, int var)关联的指令访问方法。
1.1 属性
/** The operand of this instruction. This operand is the index of a local variable. */ public int var;
LabelNode, FrameNode 以及 LineNumberNode也继承了AbstractInsnNode。这样就可以像CoreApi中MethodVisitor提供的visitXX 方法一样,插入在关联的指令前。在TreeApi中可以通过对象的getNext()方法方便找到跳转到的指令,并且移除指令的时候,只要label不变,也不会影响原有的跳转指令的跳转地址。同Core 不同的就是,从调用MethodVisitor各个指令对应的visitXX方法,改成对MethodNode 中InsnList对象的链表节点操作。
生成Method
通过下面这个例子就会更加一目了然。当然,MethodNode生成class的效率要比MethodVisitor低,内存消耗也会大,但是我们可以更轻松得实现一段注入逻辑。
方法内部的字节码结构样例,我们依然沿用一下在CoreApi 的Method介绍中使用的http://yunshen0909.iteye.com/blog/2221144的例子。然后可以对比一下两种实现方式的不同。
- package asm.tree.method;
- import org.objectweb.asm.ClassWriter;
- import org.objectweb.asm.Opcodes;
- import org.objectweb.asm.tree.*;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- /**
- * tree api method 生成字节码 Created by yunshen.ljy on 2015/7/20.
- */
- public class GenerateClasses {
- public static void main(String[] args) throws IOException {
- ClassNode classNode = new ClassNode();
- classNode.version = Opcodes.V1_8;
- classNode.access = Opcodes.ACC_PUBLIC;
- classNode.name = "bytecode/TreeMethodGenClass";
- classNode.superName = "java/lang/Object";
- classNode.fields.add(new FieldNode(Opcodes.ACC_PRIVATE, "espresso", "I", null, null));
- // public void addEspresso(int espresso) 方法生命
- MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC, "addEspresso", "(I)V", null, null);
- classNode.methods.add(mn);
- InsnList il = mn.instructions;
- il.add(new VarInsnNode(Opcodes.ILOAD, 1));
- il.add(new InsnNode(Opcodes.ICONST_1));
- LabelNode label = new LabelNode();
- // if (espresso > 0) 跳转通过LabelNode标记跳转地址
- il.add(new JumpInsnNode(Opcodes.IF_ICMPLE, label));
- il.add(new VarInsnNode(Opcodes.ALOAD, 0));
- il.add(new VarInsnNode(Opcodes.ILOAD, 1));
- // this.espresso = var1;
- il.add(new FieldInsnNode(Opcodes.PUTFIELD, "bytecode/TreeMethodGenClass", "espresso", "I"));
- LabelNode end = new LabelNode();
- il.add(new JumpInsnNode(Opcodes.GOTO, end));
- // label 后紧跟着下一个指令地址
- il.add(label);
- // java7之后对stack map frame 的处理
- il.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null));
- // throw new IllegalArgumentException();
- il.add(new TypeInsnNode(Opcodes.NEW, "java/lang/IllegalArgumentException"));
- il.add(new InsnNode(Opcodes.DUP));
- il.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "()V", false));
- il.add(new InsnNode(Opcodes.ATHROW));
- il.add(end);
- // stack map 的第二次偏移记录
- il.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null));
- il.add(new InsnNode(Opcodes.RETURN));
- // 局部变量表和操作数栈大小的处理
- mn.maxStack = 2;
- mn.maxLocals = 2;
- mn.visitEnd();
- // 打印查看class的生成结果
- ClassWriter cw = new ClassWriter(Opcodes.ASM5);
- classNode.accept(cw);
- File file = new File("TreeMethodGenClass.class");
- FileOutputStream fout = new FileOutputStream(file);
- try {
- fout.write(cw.toByteArray());
- fout.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
InsnList il = mn.instructions;所有的方法指令都放在InsnList这样一个链表结构中。当然,这个链表结构也维系了整个字节码指令的结构。
文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。
原文链接:javaedge.blog.csdn.net/article/details/105286120
- 点赞
- 收藏
- 关注作者
评论(0)