ASM指南翻译-7

15 篇文章 0 订阅
15 篇文章 0 订阅

2.3工具

除了ClassVisitor接口,以及与之相关的三个组件ClassReader ClassAdapterClassWriterASMorg.objectweb.asm.util包中提供了一些工具用来帮助开发类生成器或者适配器,这些工具在运行时并不需要。ASM提供了一些实用类用来在运行时操作内部名称,类型描述符以及方法描述符。所有的这些工具将在下面介绍。

2.3.1类型

就像在前面章节中见到的,ASM API将编译后的class中的java类型以内部名称或者类型描述符的方式展现出来。当然,也可以将这些类型还原成源代码中定义的样子,这样就更便于阅读。但是,这需要在ClassReaderClassWriter之间进行系统转换,但是这会降低性能。这就是为什么ASM没有透明地转换内部名称和类型描述符为源代码中对等的形式。尽管如此,ASM还是提供了Type类用来在需要的时候手工地进行转换。

 

一个Type对象代表了一个java类型,它可以通过类型描述符或者Class对象来构造。这个Type类也包含了一些静态变量,用来表示基本类型,例如Type.INT_TYPEint类型的Type对象。

 

getInternalName方法返回一个Type的内部名称,例如,Type.getType(String.class).getInternalName()返回了String类的内部名称”java/lang/String”.这个方法只能用于类或者借口类型。

 

getDescriptor方法返回一个Type的描述符,例如,你可以使用Type.getType(String.class).getDescriptor()来代替”Ljava/lang/String;”。或者,使用Type.INT_TYPE.getDescriptor()来代替I

 

Type类也提供了一些静态方法用来获取一个方法的参数的Type对象和返回值的Type对象,主要是通过它的类型描述符或者java.lang.reflect.Method对象来获得。例如,Type.getArgumentTypes(“(I)V”)返回一个包含Type.INT_TYPE的数组,同样地Type.getReturnType(“(I)V”)返回一个Type.VOID_TYPE对象。

 

2.3.2 TraceClassVisitor

为了检查类的生成或者转换是否如你期望,单靠ClassWriter返回的字节数组是没有多大帮助的,因为它不可读。相比较而言,一个文本表示更易于阅读和使用,而这就是TraceClassVisitor提供的。这个类,就如它的名字暗示的一样,实现了ClassVisitor借口,并且构造解析过的类的文本表示。因此,你可以使用TraceClassVisitor来替代ClassWriter,这样你可以跟踪真正生成的是什么。更好的办法是同时使用这两者,TraceClassVisitor可以跟踪代码生成,除此之外,它也可以将所有的调用委托给另外一个visitor,如ClassWriter:

ClassWriter cw = new ClassWriter(0);

TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);

cv.visit(...);

...

cv.visitEnd();

byte b[] = cw.toByteArray();

 

上面的代码创建了一个TraceClassVisitor,然后委托所有对它的调用给cw,并且将对这些方法的调用以文本方式交给printWriter来打印。例如,在2.2.3章节中的例子使用TraceClassVisitor会输出如下内容:

// class version 49.0 (49)

// access flags 1537

public abstract interface pkg/Comparable implements pkg/Mesurable {

// access flags 25

public final static I LESS = -1

// access flags 25

public final static I EQUAL = 0

// access flags 25

public final static I GREATER = 1

// access flags 1025

public abstract compareTo(Ljava/lang/Object;)I

}

注意,为了弄清楚在转换链中到底发生了什么,你可以在生成类或者转换链过程的任何点使用TraceClassVisitor,而不仅仅是在ClassWriter之前使用。注意,通过这个类生成的类的文本表示可以通过String.equals()很容易地进行比较。

 

2.3.3 CheckClassAdapter

ClassWriter并不会检查它的方法调用是否按照合适的顺序以及参数是否有效。这样就可能生成无效的代码,而被java虚拟机的验证工具所拒绝。为了尽可能地检测出这些错误,可以使用CheckClassAdapter。和TraceClassVisitor一样,这个类也实现了ClassVisitor接口,它也会将对它的方法调用委托给其他的ClassVisitor,例如一个TraceClassVisitor或者ClassWriter。尽管如此,除了打印类的文本表示,这个类会在将方法调用委托给下一个ClassVisitor之前,检查对它的方法调用顺序是否合理,以及参数是否有效。如果发生错误,将会抛出IllegalStateException或者IllegalArgumentException

 

为了检查一个类,并且打印它的文本表示,最终创建一个字节数组,你可以参考下面的代码:

ClassWriter cw = new ClassWriter(0);

TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter);

CheckClassAdapter cv = new CheckClassAdapter(tcv);

cv.visit(...);

...

cv.visitEnd();

byte b[] = cw.toByteArray();

 

注意,如果这些ClassVisitor的顺序不同,那么它们会以不同的顺序执行。例如,下面的代码会导致在跟踪代码以后再检查类。

ClassWriter cw = new ClassWriter(0);

CheckClassAdapter cca = new CheckClassAdapter(cw);

TraceClassVisitor cv = new TraceClassVisitor(cca, printWriter);

 

就像TraceClassVisitor一样,为了检查类是否有效,你也可以在生成类或者转换类的链的任何节点使用CheckClassAdapter,而不仅仅是在ClassWriter之前。

 

2.3.4 ASMifierClassVisitor

这个类也实现了ClassVisitor接口,它的每个方法会打印出调用它的java代码。例如,调用visitEnd会打印出cv.visitEnd();,结果是,当这个visitor解析一个类时,它会打印出使用ASM来生成这个类的源代码。当你使用这个类去解析一个存在的类时,你会发现它很有用。例如,如果你不知道如何使用ASM来生成一些编译后的类,那么你可以先写出这些类的源代码,然后使用javac来编译,再然后使用ASMifierClassVisitor来解析,这样就能够得到使用ASM来生成这些类的源代码了。

 

ASMifierClassVisitor可以通过命令行直接使用,如下面的例子:

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

         org.objectweb.asm.util.ASMifierClassVisitor \

         java.lang.Runnable

 

生成的代码经过缩进以后,就是下面的代码:

package asm.java.lang;

import org.objectweb.asm.*;

public class RunnableDump implements Opcodes {

         public static byte[] dump() throws Exception {

                   ClassWriter cw = new ClassWriter(0);

                   FieldVisitor fv;

                   MethodVisitor mv;

                   AnnotationVisitor av0;

                   cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,

                            "java/lang/Runnable", null, "java/lang/Object", null);

                   {

                            mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "run", "()V",

                                     null, null);

                            mv.visitEnd();

                   }

                   cw.visitEnd();

                   return cw.toByteArray();

         }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值