前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASM - TreeApi Method组件和接口简介

ASM - TreeApi Method组件和接口简介

作者头像
JavaEdge
发布2020-05-27 11:28:26
1.3K0
发布2020-05-27 11:28:26
举报
文章被收录于专栏:JavaEdge

ASM的TreeApi 对于Method的转换、生成也提供了一系列的组件和接口。

MethodNode中大多数属性和方法都和ClassNode类似,其中最主要的属性就是InsnList了。

InsnList是一个双向链表对象,包含了存储方法的字节指令序。先来看下InsnList中的主要是属性和方法。

Java代码

代码语言:javascript
复制
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的主要方法如下。

Java代码

代码语言:javascript
复制
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 属性

代码语言:javascript
复制
/** 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的例子。然后可以对比一下两种实现方式的不同。

Java代码

代码语言:javascript
复制
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这样一个链表结构中。当然,这个链表结构也维系了整个字节码指令的结构。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/04/03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 VarInsnNode
    • 1.1 属性
      • 生成Method
      相关产品与服务
      对象存储
      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档