ASM框架使用(五)--Tree API修改类和方法

Tree API通过ClassNode创建和修改类,ClassNode类的API:
在这里插入图片描述
创建一个类:

   	ClassNode cn = new ClassNode();
        cn.version = V1_5;
        cn.access = ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE;
        cn.name = "pkg/Comparable";
        cn.superName = "java/lang/Object";
        cn.interfaces.add("pkg/Mesurable");
        cn.fields.add(new FieldNode(ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
                "LESS", "Ljava/lang/String;", null, "sss"));
        cn.fields.add(new FieldNode(ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
                "EQUAL", "I", null, new Integer(0)));
        cn.fields.add(new FieldNode(ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
                "GREATER", "I", null, new Integer(1)));
        cn.methods.add(new MethodNode(ACC_PUBLIC + ACC_ABSTRACT,
                "compareTo", "(Ljava/lang/Object;)I", null, null));
        ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);

Tree API的性能比core API要低30%,但是更加灵活,使用更加方便。

给一个类添加字段和方法:

    private static final String CONST_NM = "N1221";
    public void doT() {
        System.out.println("ss1");
    }

        ClassReader classReader=new ClassReader("bytecode.Node");
        ClassNode cn = new ClassNode();
        classReader.accept(cn,ClassReader.EXPAND_FRAMES);

        cn.fields.add(new FieldNode(ACC_PRIVATE+ACC_STATIC+ACC_FINAL,"CONST_NM","Ljava/lang/String;",null,"N1221"));
        MethodNode mn=new MethodNode(ACC_PUBLIC,"doT","()V",null,null);
        mn.visitCode();
        mn.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mn.visitLdcInsn("ss1");
        mn.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        mn.visitEnd();
        cn.methods.add(mn);
        ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cn.accept(cw);

树API有它的优点,能做一些核心API做不了的事情,比如给一个类添加注解,注解中包含内容的数字签名。这在Core API中需要访问完所有的类内容之后才能计算,但是这时候已经不能添加注解了(也可以通过2遍Reader实现,但是比较麻烦)。使用Tree API可以任意访问元素的特性,就没有这个问题。


Tree API使用 MethodNode 类产生和修改方法。
在这里插入图片描述
大多数字段和意思和ClassNode中的差不多,只有一个instructions不同。
在这里插入图片描述
AbstractInsnNode和InsnList是一 一对应的。把一个AbstractInsnNode添加到一个List中之前,需要先把它从上一个list中移除。
AbstractInsnNode是代表字节码指令的类的父类,API为:
在这里插入图片描述子类是Xxx InsnNode,相当于MethodVisitor的visitXxx Insn。
创建一个方法:

 public static MethodNode createMethod(){
        MethodNode mn = new MethodNode(ACC_PUBLIC,"checkAndSet","(I)V",null,null);
        InsnList il = mn.instructions;
        il.add(new VarInsnNode(ILOAD, 1));
        LabelNode label = new LabelNode();
        il.add(new JumpInsnNode(IFLT, label));
        il.add(new VarInsnNode(ALOAD, 0));
        il.add(new VarInsnNode(ILOAD, 1));
        il.add(new FieldInsnNode(PUTFIELD, "org/by/Cwtest", "f", "I"));
        LabelNode end = new LabelNode();
        il.add(new JumpInsnNode(GOTO, end));
        il.add(label);
        il.add(new FrameNode(F_SAME, 0, null, 0, null));
        il.add(new TypeInsnNode(NEW, "java/lang/IllegalArgumentException"));
        il.add(new InsnNode(DUP));
        il.add(new MethodInsnNode(INVOKESPECIAL,
                "java/lang/IllegalArgumentException", "<init>", "()V",false));
        il.add(new InsnNode(ATHROW));
        il.add(end);
        il.add(new FrameNode(F_SAME, 0, null, 0, null));
        il.add(new InsnNode(RETURN));
        mn.maxStack = 2;
        mn.maxLocals = 2;
        return mn;
    }

效果:

public void checkAndSet(int var1) {
        if(var1 >= 0) {
            this.f = var1;
        } else {
            throw new IllegalArgumentException();
        }
    }

使用Tree API分析方法代码,主要是数据流和控制流。

数据流分析是通过为每一个字节码指令计算方法的执行桢的状态,
控制流的分析通过代码的定向连通图。

有2种数据流分析方式:

  1. 向前分析,对每一个指令,执行完这个指令后的桢与执行前的桢状态对比
  2. 向后分析,对每一个指令,执行这个指令前的桢与执行后的桢状态对比

向前的数据流分析通过模拟当前字节码指令在桢中执行,出栈,结合,入栈。
这看起来和jvm的解释器差不多,但是它们不一样,这个的目的是模拟出所有的执行路径和所有的可能值,而不是有指定参数所决定的特定路径。

ASM的字节码分析的API在org.objectweb.asm.tree.analysis中,像包名所描述的那样,它是基于tree api的。事实上,这个包提供了向前分析数据流的方式。

为了执行多种数据流分析,算法被分为两部分:一部分是固定地,由框架提供,另一部分由users自己定义。

Analyzer和Frame是框架提供的工具类。
Interpreter和Value是给用户自定义覆盖的工具类。

通过覆盖Analyzer的newControlFlowEdge和newControlFlowExceptionEdge方法,可以分析控制流。

BasicInterpreter和BasicValue是预定义的类:

  • UNINITIALIZED_VALUE 所有可能的值
  • INT_VALUE 表示int, short, byte, boolean or char
  • FLOAT_VALUE 表示 float
  • LONG_VALUE 表示 long
  • DOUBLE_VALUE 表示double
  • REFERENCE_VALUE 表示所有的类和数组引用
  • RETURNADDRESS_VALUE 用于子程序(java 6之后被移除了)

下面是使用分析器,去除不可达的代码

public class RemoveDeadCodeAdapter extends MethodVisitor {
String owner;
MethodVisitor next;
public RemoveDeadCodeAdapter(String owner, int access, String name,
String desc, MethodVisitor mv) {
super(ASM4, new MethodNode(access, name, desc, null, null));
this.owner = owner;
next = mv;
}
@Override public void visitEnd() {
MethodNode mn = (MethodNode) mv;
Analyzer<BasicValue> a =
new Analyzer<BasicValue>(new BasicInterpreter());
try {
a.analyze(owner, mn);
Frame<BasicValue>[] frames = a.getFrames();
AbstractInsnNode[] insns = mn.instructions.toArray();
for (int i = 0; i < frames.length; ++i) {
if (frames[i] == null && !(insns[i] instanceof LabelNode)) {
mn.instructions.remove(insns[i]);
}
}
} catch (AnalyzerException ignored) {
}
mn.accept(next);
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值