- 浏览: 848218 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
loveseed1989:
您好,我用您的方法运行Cone.java,会给我报java.l ...
vtk学习笔记 --- 编译vtk库和java库 -
60love5:
60love5 写道首先谢谢你的解析,但你这个验证可见性的小程 ...
多线程中共享对象的可见性 -
60love5:
首先谢谢你的解析,但你这个验证可见性的小程序是存在问题的,你的 ...
多线程中共享对象的可见性 -
Gamehu520:
...
java 中的Unsafe -
shanpao1234560:
这个list不是静态的第一种情况下也会有线程安全的问题么,求指 ...
一个看似线程安全的示例
3.3工具
在org.objectweb.asm.commons包中预先定义了一些方法适配器,它们可以辅助你定义你自己的适配器。这个章节将介绍其中的三个适配器,展示如何在AddTimerAdapter示例中使用它们(3.2.4)。它也展示了如何使用前面章节中提到的工具来删除方法或者转换。
3.3.1基本工具
在2.3节中出现的工具也可以针对方法使用。
Type
很多字节码指令,如Xload,xADD或者xRETURN,都依赖于它所处理的类型。Type这个类提供了一个getOpcode方法,针对这些指令,它可以获取一个给定类型的opcode。这个方法以一个int类型的opcode作为参数,然后返回针对调用该方法对象的类型的opcode。例如t.getOpcode(IMUL)返回FMUL,其中t为Type.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);
}
}
3.3.3 LocalVariablesSorter
这个方法适配器对方法中局部变量以它们出现的顺序进行重新编号。例如,一个包含两个参数的方法,第一个局部变量的索引值大于或者等于3,那么前面还应该有三个局部变量,以及两个方法参数,它们是不能改变的,因此第一个局部变量的索引值应该是3,第二个就应该是4(有疑问?),后面依次类推.这个适配器对于想在方法中插入新的变量会有帮助。如果没有这个适配器,虽然也可以实现添加新的局部变量到代码的最后,但是不幸地是,它们的编号直到visitMaxs方法的末尾才可知。
为了展示如何使用这个适配器,假设我们希望通过添加一个局部变量来实现AddTimerAdapter:
public class C {
public static long timer;
public void m() throws Exception {
long t = System.currentTimeMillis();
Thread.sleep(100);
timer += System.currentTimeMillis() - t;
}
}
可以通过继承LocalVariablesSorter,并使用其中的newLocal方法,很容易就实现上面的功能。
class AddTimerMethodAdapter4 extends LocalVariablesSorter {
private int time;
public AddTimerMethodAdapter4(int access, String desc,
MethodVisitor mv) {
super(access, desc, mv);
}
@Override public void visitCode() {
super.visitCode();
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
time = newLocal(Type.LONG_TYPE);
mv.visitVarInsn(LSTORE, time);
}
@Override public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitVarInsn(LLOAD, time);
mv.visitInsn(LSUB);
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitInsn(LADD);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
super.visitInsn(opcode);
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 4, maxLocals);
}
}
注意,当局部变量被重新编号以后,之前与方法关联的帧就变为无效了,更不用说插入新的局部变量了。让人感到有希望的是,我们可以避免从头计算这些帧:事实上没有帧被删除或者被添加,只需要对原始帧中的局部变量进行重排序就能获得转换后的帧了。LocalVariablesSorter将会自动进行这些操作。如果你需要对栈映射帧进行增量更新,你可以从这个类的源码中获取灵感。
如你所见,使用一个局部变量并不能解决在之前版本中遇到的问题,就是计算maxStack在最坏情况下的值。如果你希望使用AnalyzerAdapter来解决这个问题,那么除了LocalVariablesSorter,你必须通过委托的方式来使用这些适配器而不是继承(因为多继承是不可能的):
class AddTimerMethodAdapter5 extends MethodAdapter {
public LocalVariablesSorter lvs;
public AnalyzerAdapter aa;
private int time;
private int maxStack;
public AddTimerMethodAdapter5(MethodVisitor mv) {
super(mv);
}
@Override public void visitCode() {
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
time = lvs.newLocal(Type.LONG_TYPE);
mv.visitVarInsn(LSTORE, time);
maxStack = 4;
}
@Override public void visitInsn(int opcode) {
if ((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitVarInsn(LLOAD, time);
mv.visitInsn(LSUB);
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitInsn(LADD);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
maxStack = Math.max(aa.stack.size() + 4, maxStack);
}
mv.visitInsn(opcode);
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
mv.visitMaxs(Math.max(this.maxStack, maxStack), maxLocals);
}
}
为了使用上面的适配器,你必要将LocalVariablesSorter和AnalyzerAdapter链接起来,然后链接你的适配器:第一个适配器将对局部变量进行排序并更新帧,AnalyzerAdapter适配器将根据前一步骤中执行结果计算中间帧,这样你的适配器就可以访问那些已经重编号的帧了。这个链可以在visitMethod方法中如下构造:
mv = cv.visitMethod(access, name, desc, signature, exceptions);
if (!isInterface && mv != null && !name.equals("<init>")) {
AddTimerMethodAdapter5 at = new AddTimerMethodAdapter5(mv);
at.aa = new AnalyzerAdapter(owner, access, name, desc, at);
at.lvs = new LocalVariablesSorter(access, desc, at.aa);
return at.lvs;
}
3.3.4 AdviceAdapter
这个方法适配器是一个抽象的类,可以用来在方法的开始插入代码,也可以在RETURN和ATHROW指令之前。它最大的优势就是可以适用于构造方法,在构造方法中,代码不能直接插入到方法的开始,而是插入到调用父类构造方法之后。事实上,这个适配器的大部分代码都是用来检测调用父类的构造方法的。
如果你仔细的查看3.2.4章节中的AddTimerAdapter类,你会发现AddTimerMethodAdapter适配器没有用在构造方法上,就是因为这个问题。通过继承AdviceAdapter这个适配器,可以让这个适配器也能够很好的工作在构造方法上(注意,AdviceAdapter是继承自LocalVariablesSorter,因此我们也可以很容易地使用一个局部变量):
class AddTimerMethodAdapter6 extends AdviceAdapter {
public AddTimerMethodAdapter6(int access, String name, String desc,
MethodVisitor mv) {
super(mv, access, name, desc);
}
@Override protected void onMethodEnter() {
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LSUB);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
@Override protected void onMethodExit(int opcode) {
mv.visitFieldInsn(GETSTATIC, owner, "timer", "J");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System",
"currentTimeMillis", "()J");
mv.visitInsn(LADD);
mv.visitFieldInsn(PUTSTATIC, owner, "timer", "J");
}
@Override public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 4, maxLocals);
}
}
发表评论
-
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(二)
2014-11-21 22:23 3216要求:使用Oracle Streams捕获某个用户下部 ... -
基于Oracle Streams + Oracle AQ 捕获变更,发布变更(一)
2014-11-20 22:23 2806要求:使用Oracle Streams捕获某个用户下部分表 ... -
如何去掉在浏览器中打开java applet时的警告对话框
2013-08-24 12:10 6999好久没更新博客了! 最近,由于项目要求,需要将sw ... -
Android 内存泄露笔记
2013-03-05 23:10 01、大部分内存泄露都是错误的持有了Activity或者Con ... -
java 虚拟机总结 【思维导图】
2012-12-22 20:11 2098java虚拟机总结思维导图: 参考《深入理解jav ... -
Java虚拟机字节码执行引擎 【思维导图】
2012-12-22 19:51 1691java虚拟机字节码执行引擎思维导图总结: 参考《深入理 ... -
java 垃圾回收相关总结 【思维导图】
2012-12-21 19:03 3289java垃圾回收相关总结: 参考《深入理解java ... -
java并发中的延迟初始化
2012-12-12 19:17 4709在《java并发编程实战 ... -
java同步容器与并发容器
2012-12-09 18:07 4365何为同步容器:可以简 ... -
一个看似线程安全的示例
2012-12-09 14:56 5083在《java并发编程实战》第四章4.4.1节给出了一个程序示 ... -
多线程中的long和double
2012-12-08 19:26 4210在看一些代码的时候,会发现在定义long型和double型的 ... -
多线程中共享对象的可见性
2012-12-08 18:52 5161在阅读《java并发编程实战》的第三章的时候,看到书中的一个 ... -
一个快速、轻量级 Collection 库 Trove
2012-12-07 09:35 3769Trove一个快速、轻量级针对java原子类型(byte,i ... -
java 中的Unsafe
2012-12-05 22:25 32764在阅读AtomicInteger的源码时,看到了这个类:su ... -
在ubuntu10上编译Thrift0.8.0
2012-08-01 15:34 2160下载thrift0.8.0 ,地址: http:// ... -
生活小工具--记账小助手1.0发布
2012-05-31 15:08 1762因为自己平时喜欢记账,把每日的消费情况都记录下来,所以希望找 ... -
话费速查升级版v1.3发布
2012-05-04 12:47 1443前段时间开发了一个话费速查的小应用,最近一直保持每周更新一个 ... -
编译zeromq的java绑定:jzmq
2012-05-03 22:47 135521、 下载zeromq源码:http://www.ze ... -
Android软件包静默安装小应用 - 附源码
2012-04-21 20:50 12424老早之前,写了一个android软件包静默安装的应用,放在工 ... -
最方便的联通话费,余额查询软件来了! --- 联通话费速查v1.2
2012-04-12 18:01 4049软件介绍: 联通话费速查是一款针对联通 ...
相关推荐
### ASM3.0指南翻译——深入理解Java字节码引擎库 #### 1. 引言与背景 在软件开发领域,程序分析、生成及转换技术的应用极为广泛,覆盖从语法解析到语义分析,再到代码优化、混淆、调试、性能监控及面向切面编程等...
ASM的主要动机在于提供一套高效的工具来支持程序分析、程序生成和程序转换。这些技术可以用于多种应用环境,包括但不限于: - 程序分析:分析程序以查找潜在的bug、检测未使用的代码以及对代码执行逆向工程。 - 程序...
ASM4是针对Java语言设计的一个工具库,用于在运行时动态生成和转换已编译的Java类。它在程序分析、生成和转换技术中有着广泛的应用,可以用于多种场景,比如查找潜在bug、性能优化、面向方面的程序设计(AOP)等。本...
- Adding and removing class members:说明了如何使用ASM库向类中添加或移除成员(字段、方法等)。 - Generating classes:描述了如何使用ASM生成新的类。 - Parsing classes:说明了ASM如何解析已有的类文件。 ...
- **高速与小型化**:ASM被设计为快速且占用空间小的库,这对于运行时需要动态生成或转换类的应用程序尤为重要。 - **兼容性**:支持最新的Java版本,如Java 7。 - **广泛的用户支持**:拥有庞大的用户社区,能够...
**ASM**(Abstract Syntax Model)是一款专门针对Java字节码操作的强大工具库,它主要用于程序分析、生成及转换等场景。根据提供的文档信息来看,尽管标题为“ASM7使用指南”,但文档内容却提到了“ASM4使用指南”。...
标题《asm4使用指南(强烈推荐)》以及描述《java使用asm4操作字节码技术,详细介绍了asm如何对java的字节码进行操作,强烈推荐》指明了文档的核心内容是介绍ASM4这一工具在Java字节码层面操作的指南,并且给出了...
在阅读《ASM指南》的过程中,你将逐步掌握ASM的基本用法,包括类、字段和方法的解析,以及如何生成和修改字节码。同时,书中还会提供丰富的示例代码,帮助你快速上手并应用于实际项目中。 总之,《ASM指南》是一本...
2. **字节码生成**:除了解析,ASM还支持生成新的字节码,允许开发者动态创建或修改类和方法。这对于实现Java代理、AOP(面向切面编程)或者动态语言的JVM实现是非常关键的。 3. **高性能**:ASM设计时注重性能,其...
C32Asm是一款专为处理C32格式代码的反汇编工具,它能够将C32编译后的二进制程序转换成汇编语言代码,帮助开发者理解和分析程序的内部工作原理。在IT领域,反汇编是逆向工程的一部分,常用于软件调试、漏洞分析、代码...
《ASM4 使用指南》是一本深入探讨ASM库的著作,ASM是专为Java设计的类生成和转换工具。这本书详细介绍了ASM如何在运行时对已编译的Java类进行分析、生成和转换,同时强调了ASM库的高效、小巧和易用性。以下是ASM4的...
- **接口与组件**:介绍用于解析、生成、转换类的各种接口和工具类。 - **工具**:提供一些实用工具类,如 Type 和 TraceClassVisitor。 ##### 1.4 致谢 感谢 Eric Bruneton 对 Asm 项目的贡献和支持,以及所有...
**ASM**(Adapter Software Model)是一款专门针对Java字节码操作的强大工具库,它主要用于实现类的生成与转换功能,并支持对编译后的Java类进行分析。该库特别适用于在运行时环境中对Java类进行操作的需求场景。 *...
在ASM 3.1的压缩包中,`asm-3.1`文件包含了所有必要的类库和文档,包括ASM的主要API类、辅助工具类以及相关的用户指南和API文档。通过这些资源,开发者可以快速上手并熟练掌握ASM的用法。 使用ASM 3.1时,开发者...
- Asm.exe 可能是一个单独的汇编器或者与AsmToE相关的辅助工具。 - AsmToE.ini 是配置文件,存储用户的设置和工具的默认参数。 - 8086.txt 可能包含8086处理器的指令集参考,帮助用户了解如何编写针对该架构的汇编...
ASM是一个Java字节码操控和分析框架,常用于动态代理、代码分析以及转换等场景。它的核心在于提供了一种能够直接操作和修改Java类文件的底层API,这使得开发者能够深入到Java字节码级别进行编程,实现一些高级功能,...
ASM(Abstract Syntax Tree Manipulation)是一个Java字节码操作工具,它提供了一种方式,允许开发者在运行时动态地生成和转换Java类。ASM库提供了一种轻量级的方法,用来分析和修改已编译的Java类文件,而无需重新...
"jb51.net.txt"可能包含了一些与ASM相关的链接或者教程,"电子书大全.url"和"PDF阅读器下载.url"可能是推荐的电子书资源网站或PDF阅读器下载地址,对于深入学习和查找更多资料非常有帮助。"脚本之家.url"可能是一个...