ASM指南翻译-14 方法生成与转换的相关工具_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > ASM指南翻译-14 方法生成与转换的相关工具

ASM指南翻译-14 方法生成与转换的相关工具

 2011/10/25 8:12:37  aswang  http://aswang.iteye.com  我要评论(0)
  • 摘要:3.3工具在org.objectweb.asm.commons包中预先定义了一些方法适配器,它们可以辅助你定义你自己的适配器。这个章节将介绍其中的三个适配器,展示如何在AddTimerAdapter示例中使用它们(3.2.4)。它也展示了如何使用前面章节中提到的工具来删除方法或者转换。3.3.1基本工具在2.3节中出现的工具也可以针对方法使用。Type很多字节码指令,如Xload,xADD或者xRETURN,都依赖于它所处理的类型。Type这个类提供了一个getOpcode方法,针对这些指令
  • 标签:方法 翻译 工具

?

3.3工具

org.objectweb.asm.commons包中预先定义了一些方法适配器,它们可以辅助你定义你自己的适配器。这个章节将介绍其中的三个适配器,展示如何在AddTimerAdapter示例中使用它们(3.2.4)。它也展示了如何使用前面章节中提到的工具来删除方法或者转换。

?

3.3.1基本工具

2.3节中出现的工具也可以针对方法使用。

Type??

?

很多字节码指令,如XloadxADD或者xRETURN,都依赖于它所处理的类型。Type这个类提供了一个getOpcode方法,针对这些指令,它可以获取一个给定类型的opcode。这个方法以一个int类型的opcode作为参数,然后返回针对调用该方法对象的类型的opcode。例如t.getOpcode(IMUL)返回FMUL,其中tType.FLOAT_TYPE.

?

TraceClassVisitor

?

这个类在前面章节已经介绍过了,主要是打印它所访问类的字节码的文本表示,当然也包括方法的文本表示。这个类也可以用来追踪转换链中方法生成和转换的内容。例如:

java -classpath asm.jar:asm-util.jar \

???????? org.objectweb.asm.util.TraceClassVisitor \

???????? java.lang.Void

将会打印出一下内容:

// class version 49.0 (49)

// access flags 49

public final class java/lang/Void {

???????? // access flags 25

???????? // signature Ljava/lang/Class<Ljava/lang/Void;>;

???????? // declaration: java.lang.Class<java.lang.Void>

???????? public final static Ljava/lang/Class; TYPE

???????? // access flags 2

???????? private <init>()V

?????????????????? ALOAD 0

?????????????????? INVOKESPECIAL java/lang/Object.<init> ()V

?????????????????? RETURN

?????????????????? MAXSTACK = 1

?????????????????? MAXLOCALS = 1

???????? // access flags 8

???????? static <clinit>()V

?????????????????? LDC "void"

?????????????????? INVOKESTATIC java/lang/Class.getPrimitiveClass (...)...

?????????????????? PUTSTATIC java/lang/Void.TYPE : Ljava/lang/Class;

?????????????????? RETURN

?????????????????? MAXSTACK = 1

?????????????????? MAXLOCALS = 0

}

上面的代码展示了如何生成一个静态块static { … },即<clinit>方法。注意,如果你想在链的某个点追踪一个方法的内容,你可以选择使用TraceMethodVisitor,而不是用TraceClassVisitor来追踪所有的内容:

public MethodVisitor visitMethod(int access, String name,

???????? String desc, String signature, String[] exceptions) {

???????? MethodVisitor mv = cv.visitMethod(access, name, desc, signature,

???????? exceptions);

???????? if (debug && mv != null && ...) { // if this method must be traced

?????????????????? mv = new TraceMethodVisitor(mv) {

??????????????????????????? @Override public void visitEnd() {

???????????????????????????????????? print(aPrintWriter); // print it after it has been visited

??????????????????????????? }

?????????????????? };

???????? }

???????? return new MyMethodAdapter(mv);

}

上面的代码将会打印出使用MyMethodAdapter转换后的内容。

?

CheckClassAdapter

?

这个类在前面的章节中也介绍过,用来检查ClassVisitor方法是否按照合适的顺序来调用,如果给以合适的参数,它可以用来检查MethodVisitor中的方法。也就是说它可以用来检查MethodVisitor接口在转换链中是否被正确使用。就像TraceMethodVisitor一样,你可以选择使用CheckMethodAdapter来检查单个方法而不是所有方法:

public MethodVisitor visitMethod(int access, String name,

String desc, String signature, String[] exceptions) {

???????? MethodVisitor mv = cv.visitMethod(access, name, desc, signature,

???????? exceptions);

???????? if (debug && mv != null && ...) { // if this method must be checked

?????????????????? mv = new CheckMethodAdapter(mv);

???????? }

???????? return new MyMethodAdapter(mv);

}

上面的代码用来检查MyMethodAdapter是否正确使用了MethodVisitor接口。但是它不能检查字节码是否正确:如它无法检测ISTORE 1 ALOAD 1是无效的。

?

ASMifierClassVisitor

?

这个类同样在前面章节中介绍过了,它同样也可以用在方法上。它可以用来了解如何使用ASM来生成一些编译后的代码:编写对应的源代码,然后使用javac编译,在使用ASMifierClassVisitor来访问这个类即可。这样你就可以得到ASM代码来生成源码对应的字节码。

?

3.3.2 AnalyzerAdapter

这个方法适配器基于visitFrame访问过的帧,来对比每个指令的栈映射帧。如在3.1.5节中解释的一样,visitFrame方法只能在一个方法中的某些指令之前被调用,这样可以节省空间,并且“其它的帧可以很容易很快地从这些帧推断出来”。这就是这个章节即将做的事情。这个适配器只能工作在那些包含预先计算过的栈映射帧的类上,并且这些类需要使用java6或者更高编译器编译。

?

AddTimerAdapter示例中,这个适配器可以获得在RETURN指令之前的操作数栈的大小,从而可以在visitMaxs方法中为maxStack计算一个最佳的值(事实上,在实际操作中这个方法不推荐使用,因为它没有COMPUTE_MAXS有效):

class AddTimerMethodAdapter2 extends AnalyzerAdapter {

???????? private int maxStack;

???????? public AddTimerMethodAdapter2(String owner, int access,

?????????????????? String name, String desc, MethodVisitor mv) {

???????? ???????? super(owner, access, name, desc, mv);

???????? }

???????? @Override public void visitCode() {

?????????????????? super.visitCode();

?????????????????? mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");

?????????????????? mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",

??????????????????????????? "currentTimeMillis", "()J");

?????????????????? mv.visitInsn(LSUB);

?????????????????? mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");

?????????????????? maxStack = 4;

???????? }

???????? @Override public void visitInsn(int opcode) {

?????????????????? if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {

??????????????????????????? mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");

??????????????????????????? mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",

???????????????????????????????????? "currentTimeMillis", "()J");

??????????????????????????? mv.visitInsn(LADD);

??????????????????????????? mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");

??????????????????????????? maxStack = Math.max(maxStack, stack.size() + 4);

?????????????????? }

?????????????????? super.visitInsn(opcode);

???????? }

???????? @Override public void visitMaxs(int maxStack, int maxLocals) {

?????????????????? super.visitMaxs(Math.max(this.maxStack, maxStack), maxLocals);

???????? }

}

其中stack字段是在AnalyzerAdapter类中定义的,包含了操作数栈中的类型。更经确定地讲,在一个visitXxxInsn方法中,并且在被重写的方法调用之前,这个列表包含了操作数栈的状态。注意,被重写的那个方法也必须被调用,这样stack字段才会被正确的更新(以后将使用super来代替原始代码中的mv

?

此外,可以通过调用父类的方法来插入新的指令:效果就是AnalyzerAdapter将会计算这些指令帧大小。因此,因为这个适配器会基于自己的计算来更新visitMaxs方法的参数,我们就不需要自己更新它们了:

class AddTimerMethodAdapter3 extends AnalyzerAdapter {

???????? public AddTimerMethodAdapter3(String owner, int access,

?????????????????? String name, String desc, MethodVisitor mv) {

?????????????????? super(owner, access, name, desc, mv);

???????? }

???????? @Override public void visitCode() {

?????????????????? super.visitCode();

?????????????????? super.visitFieldInsn(GETSTATIC, owner, "timer", "J");

?????????????????? super.visitMethodInsn(INVOKESTATIC, "java/lang/System",

?????????????????? ???????? "currentTimeMillis", "()J");

?????????????????? super.visitInsn(LSUB);

?????????????????? super.visitFieldInsn(PUTSTATIC, owner, "timer", "J");

???????? }

???????? @Override public void visitInsn(int opcode) {

?????????????????? if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {

??????????????????????????? super.visitFieldInsn(GETSTATIC, owner, "timer", "J");

??????????????????????????? super.visitMethodInsn(INVOKESTATIC, "java/lang/System",

??????????????????????????? "currentTimeMillis", "()J");

??????????????????????????? super.visitInsn(LADD);

??????????????????????????? super.visitFieldInsn(PUTSTATIC, owner, "timer", "J");

?????????????????? }

?????????????????? super.visitInsn(opcode);

???????? }

}

发表评论
用户名: 匿名