`
aswang
  • 浏览: 849835 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

ASM指南翻译-13 有状态转换

 
阅读更多

3.2.5有状态转换

在前面章节看到的转换都是局部的,它们都没有依赖于之前已经访问过的代码:在方法最开始添加的代码始终是一样的并且总是会被添加,同样,在RETURN之前添加的代码也是如此。这样的转换称之为无状态转换。这些转换较容易实现,但是也只有这种最简单的转换才满足这个属性。

 

更复杂的转换,需要记住之前访问过代码的一些状态。考虑这样的一个转换,移除所有ICONST_0 IADD指令序列,这些指令的效果类似于加零。当一个IADD指令被访问时,只有当最后一个被访问指令是ICONST_0时,这个指令序列才可以被移除。这需要在方法适配器内部保存一些状态。类似于这样的转换称之为有状态转换。

 

让我们详细地了解下这个示例。当一个ICONST_0指令被访问时,如果其下一条指令是IADD时,才可以将这两条指令删除。问题是在访问ICONST_0指令时,下一条指令还未知。解决方案就是推迟最初这个判断知道下一条指令被访问:如果访问到IADD指令,那么就移除这两个指令,否则忽略ICONST_0指令和当前的指令。

 

为了实现这样的一个转换:移除或者替换某些指令序列,简便的方式就是引入一个方法适配器MethodAdapter,这个类的visitXxxInsn方法会调用一个共同的方法visitInsn方法:

public abstract class PatternMethodAdapter extends MethodAdapter {

         protected final static int SEEN_NOTHING = 0;

         protected int state;

         public PatternMethodAdapter(MethodVisitor mv) {

                   super(mv);

         }

         @Overrid public void visitInsn(int opcode) {

                  visitInsn();

                   mv.visitInsn(opcode);

         }

         @Override public void visitIntInsn(int opcode, int operand) {

                  visitInsn();

                   mv.visitIntInsn(opcode, operand);

         }

         ...

         protected abstract void visitInsn();

}

 

上面的转换可以像下面这样实现:

public class RemoveAddZeroAdapter extends PatternMethodAdapter {

         private static int SEEN_ICONST_0 = 1;

         public RemoveAddZeroAdapter(MethodVisitor mv) {

                   super(mv);

         }

         @Override public void visitInsn(int opcode) {

                   if (state == SEEN_ICONST_0) {

                            if (opcode == IADD) {

                                     state = SEEN_NOTHING;

                                     return;

                            }

                   }

                   visitInsn();

                   if (opcode == ICONST_0) {

                            state = SEEN_ICONST_0;

                            return;

                   }

                   mv.visitInsn(opcode);

         }

         @Override protected void visitInsn() {

                   if (state == SEEN_ICONST_0) {

                            mv.visitInsn(ICONST_0);

                   }

                   state = SEEN_NOTHING;

         }

}

 

visitInsn方法首先会判断指令序列是否被检测到。在这个例子中,它初始化state然后立即返回,它具有删除指令序列的效果。在另外的情形中,它会调用visitInsn方法,这个方法会

忽略ICONST_0指令。如果当前的指令是ICONST_0,它会记录下来,然后返回,这样就可以在下一条指令到来时判断了。在所有其它的情形中,当前的指令直接被传递给了下一个visitor

 

标签和帧

 

如我们在前面章节中见到的一样,标签和帧会先于它们关联的指令被访问。换句话说,它们会和指令同时被访问,尽管它们本身不是指令。这会对检测指令序列这样的转换造成一定的影响,但这种影响事实上是有利的。如果我们移除的指令是跳转指令的目标,那会发生什么了?如果有指令会跳转到ICONST_0指令,这意味着这里有一个标签来代表这个指令。在移除这两个指令后,这个标签就会代表IADD之后的指令,这正是我们想要的。但是如果一些指令要跳转到IADD指令,那我们就不能移除这个指令序列了(我们不能确保在跳转之前,一个0值入栈了)。让人有希望的是,在这种情况下,在ICONST_0IADD之间必须有一个标签,并且这很容易检测到。

 

这里的原因也同样适用于栈映射帧:如果一个栈映射帧在这两个指令之间被访问,我们也不能移除这两个指令。这两种情形,我们都可以把标签和帧当做指令使用模式匹配算法来处理。这可以使用PatternMethodAdapter类完成(注意visitMaxs方法也会调用visitInsn方法,这是用来处理这样的情形:方法的结尾可能是这个序列的前缀,它们也应该被检测到):

public abstract class PatternMethodAdapter extends MethodAdapter {

         ...

         @Override public void visitFrame(int type, int nLocal, Object[] local,

         int nStack, Object[] stack) {

                   visitInsn();

                   mv.visitFrame(type, nLocal, local, nStack, stack);

         }

         @Override public void visitLabel(Label label) {

                   visitInsn();

                   mv.visitLabel(label);

         }

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

                  visitInsn();

                   mv.visitMaxs(maxStack, maxLocals);

         }

}

 

我们在下一章节中会看到,一个编译后的类可能会包含源文件行号等信息,可以用来进行异常追踪。这些信息通过visitLineNumber方法来访问,它们也会在指令被访问的同时被调用。尽管如此,出现在指令序列中间的行号也不会对转换或者删除行号有任何影响。相应的解决办法就是在模式匹配算法中完全忽略它们。

 

一个更复杂的例子

 

之前的例子可以很容易地扩展到更复杂的指令序列的情形。考虑这样转换示例,删除所有字段的自我赋值,如f=f,或者,以字节码的形式就是,ALOAD 0 ALOAD 0  GETFIELD f  PUTFIELD f。在实现这个转换之前,我们最好来设计一个识别这种序列的状态机:




 

每个转换都会以一个条件作为标签(就是当前的指令)以及一个动作(一个必须被执行的指令序列,以粗体表示)。例如从S1S0的转换,只有当前的指令不是ALOAD 0时才进行。在这种情形下,ALOAD 0会被访问然到达初始状态。来看看S2到自身的转换:如果找到3个或者更多的连续的ALOAD 0指令,那么将进行S2到自身的转换。在这种情形下,我们会停留在这样的状态:两条ALOAD 0指令已经被访问,同时我们将再执行一个ALOAD 0指令。一旦这个状态机模型被找到,那么编写对应的方法适配器就很直接了(下面的8switch case就对应着上面的8个转换):

class RemoveGetFieldPutFieldAdapter extends PatternMethodAdapter {

         private final static int SEEN_ALOAD_0 = 1;

         private final static int SEEN_ALOAD_0ALOAD_0 = 2;

         private final static int SEEN_ALOAD_0ALOAD_0GETFIELD = 3;

         private String fieldOwner;

         private String fieldName;

         private String fieldDesc;

        

         public RemoveGetFieldPutFieldAdapter(MethodVisitor mv) {

                   super(mv);

         }

         @Override

         public void visitVarInsn(int opcode, int var) {

                   switch (state) {

                   case SEEN_NOTHING: // S0 -> S1

                            if (opcode == ALOAD && var == 0) {

                            state = SEEN_ALOAD_0;

                            return;

                            }

                            break;

                   case SEEN_ALOAD_0: // S1 -> S2

                            if (opcode == ALOAD && var == 0) {

                            state = SEEN_ALOAD_0ALOAD_0;

                            return;

                            }

                            break;

                   case SEEN_ALOAD_0ALOAD_0: // S2 -> S2

                            if (opcode == ALOAD && var == 0) {

                            mv.visitVarInsn(ALOAD, 0);

                            return;

                            }

                            break;

                   }

                   visitInsn();

                   mv.visitVarInsn(opcode, var);

         }

         @Override

         public void visitFieldInsn(int opcode, String owner, String name,

                   String desc) {

                   switch (state) {

                   case SEEN_ALOAD_0ALOAD_0: // S2 -> S3

                            if (opcode == GETFIELD) {

                            state = SEEN_ALOAD_0ALOAD_0GETFIELD;

                            fieldOwner = owner;

                            fieldName = name;

                            fieldDesc = desc;

                            return;

                            }

                            break;

                   case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0

                            if (opcode == PUTFIELD && name.equals(fieldName)) {

                            state = SEEN_NOTHING;

                            return;

                            }

                            break;

                   }

                   visitInsn();

                   mv.visitFieldInsn(opcode, owner, name, desc);

         }

         @Override protected void visitInsn() {

                   switch (state) {

                   case SEEN_ALOAD_0: // S1 -> S0

                            mv.visitVarInsn(ALOAD, 0);

                            break;

                   case SEEN_ALOAD_0ALOAD_0: // S2 -> S0

                            mv.visitVarInsn(ALOAD, 0);

                            mv.visitVarInsn(ALOAD, 0);

                            break;

                   case SEEN_ALOAD_0ALOAD_0GETFIELD: // S3 -> S0

                            mv.visitVarInsn(ALOAD, 0);

                            mv.visitVarInsn(ALOAD, 0);

                            mv.visitFieldInsn(GETFIELD, fieldOwner, fieldName, fieldDesc);

                            break;

                   }

                   state = SEEN_NOTHING;

         }

}

 

注意,因为和3.2.4节中AddTimerAdapter相似的原因,本节中出现的有状态转换不需要转换栈映射帧:原始的帧仍在转换之后然有效。也不需要转换局部变量和操作数栈的大小。最后,注意有状态转换不局限于检测和转换指令序列,还有很多其它类型的转换也是状态转换。下一节出现的有关方法适配器就是这样的例子。

  • 大小: 27.6 KB
0
0
分享到:
评论

相关推荐

    ASM1153E_固件刷写.zip

    ASM1153E是一款广泛应用在电子设备中的串行ATA(SATA)桥接芯片,它主要用于将SATA接口转换为其他类型的接口,如PCI-E,USB等,以便于数据传输和存储。固件刷写是更新该芯片内嵌软件的过程,以改善性能、修复错误或...

    绿联ASM1153E芯片硬盘盒关闭休眠工具+教程

    总结来说,这个主题涵盖了硬件组件(ASM1153E芯片)、外设优化(关闭休眠)、以及用户操作指南,是关于提升硬盘盒性能和效率的一个实践性教程。如果你需要关闭硬盘盒的休眠功能,这个工具和教程将非常有用。在操作前...

    asm.rar_asm电机是什么_步进电机_步进电机调速

    标题中的“asm.rar_asm电机是什么_步进电机_步进电机调速”表明这是一个关于ASM电机,...ASM电机作为一个特定的电机类型,其调速技术和应用可能有其独特之处,深入学习相关资料能帮助我们掌握其工作原理和优化方法。

    C32Asm.exe

    1. C32ASM.chm:这是帮助文档,通常包含软件的使用指南、功能介绍和常见问题解答。用户可以通过这个文件快速学习如何使用C32Asm.exe。 2. C32asm.dll:这是一个动态链接库文件,包含了C32Asm.exe运行所需的部分功能...

    C32Asm编辑器

    2. **调试功能**:C32Asm编辑器内置了调试工具,允许用户设置断点、单步执行、查看寄存器状态和内存内容,这对于理解程序运行过程和查找错误至关重要。 3. **反汇编和二进制查看**:编辑器可能包含反汇编器功能,...

    训练asm 英文文档

    ### 训练ASM英文文档知识点概述 #### 一、引言 - **Stasm**:一个C++软件包,主要用于定位面部图像中的特征点(即面部标志)。用户提供一张人脸图像,该软件能够返回这些特征点的位置。 - **适用场景**:Stasm主要...

    ASM汇编语言全集,很经典的,希望大家能喜欢

    ASM汇编语言全集是一份宝贵的资源,涵盖了汇编语言的各个方面,对于学习和深入理解计算机底层原理极其有用。汇编语言是计算机编程的一种低级语言,它与机器语言紧密相关,每一行汇编指令几乎都能直接转换为特定的...

    Moving your SAP database to Oracle 11gR2 ASM

    ### 将SAP数据库迁移至Oracle 11g R2 ASM的最佳实践指南 #### 概述 本指南旨在提供一个全面的技术文档,介绍如何将现有的SAP数据库迁移至Oracle 11g R2 ASM(自动存储管理)环境,并强调最佳实践以确保数据完整性和...

    C32Asm纯正无毒

    - `C32ASM.chm`:这通常是一个CHM(Compiled Help Manual)文件,即编译后的帮助文档,包含C32Asm的使用指南和参考信息。 - `C32asm.dll`:动态链接库文件,可能包含了C32Asm运行时需要的一些函数或服务。 - `C32...

    反汇编工具 win32asm

    描述中提到的“汉化了的,好用”,意味着这个win32asm工具已经被翻译成了中文,这对于中文用户来说是一个极大的便利,因为它消除了语言障碍,使得不懂英文的开发者也能轻松使用。通常,反汇编器的界面和文档都是英文...

    MCS51_ASM_IDE_2812

    《MCS51_ASM_IDE_2812:全面解析单片机开发的强大工具》 在电子工程和嵌入式系统的世界里,MCS51系列单片机以其高效能和广泛应用而闻名。为了更好地支持这些单片机的开发工作,出现了"MCS51_ASM_IDE_2812"这样的...

    杭电微机原理AD转换DA转换实验实验报告.pdf

    2. 在PC机和实验系统联机状态下,新建实验程序,编辑完成后进行保存(保存后缀为.asm 文件)。 3. 编译下载。 4. 全速运行,运行程序。 5. 按 RST 键退出。 七、实验程序 实验程序主要包括两个部分:数据段和代码...

    HairOracle数据库运维手册.docx

    - **视图V$ASM_DISK**:列出ASM实例中的所有磁盘及其状态。 - **常用方法** - **如何确定ASM实例的编号**:解释如何查找ASM实例的标识符。 - **查询DG-RAW-磁盘的对应关系**:查询diskgroup与磁盘之间的关联。 ...

    ds18.rar_DS18B20 ASM_ds18

    标题中的“ds18.rar_DS18B20 ASM_ds18”暗示了这个压缩包主要涉及DS18B20数字温度传感器的程序代码,其中可能包含了使用汇编语言(ASM)编写的代码。DS18B20是一款单线数字温度传感器,由达拉斯半导体(现为Maxim ...

    ARM编译器参考指南

    ### ARM编译器参考指南知识点总结 #### 一、ARM RealView 编译工具 (RVCT) 概述 **1.1 关于ARM编译器** - **RealView 编译工具 (RVCT)**:这是一套由ARM公司开发的专业编译工具,主要用于ARM架构下的软件开发。 -...

    MCS51_ASM_IDE

    【MCS51_ASM_IDE】是一款专为51系列微控制器设计的汇编语言集成开发环境(IDE),它提供了全面的功能,使开发者能够高效地编写、编译和调试51汇编代码。51汇编器是这个IDE的核心部分,它支持8051、8052等51家族的微...

    OTIS-电梯系统文件

    这份OTIS电梯系统的文档对于工程技术人员来说至关重要,因为它不仅提供了详细的调试指南,还包含了必要的软件基础知识和服务工具的操作方法。熟练掌握这些内容能够帮助技术人员更好地完成现场调试任务,确保电梯系统...

    Intel® 64 and IA-32 Architectures Software Developer’s Manual

    《Intel® 64 and IA-32 Architectures Software Developer’s Manual》是Intel公司为开发者提供的权威指南,详细阐述了Intel 64(也称为x86-64)和IA-32架构的硬件特性、指令集以及编程模型。这份手册对于深入理解...

Global site tag (gtag.js) - Google Analytics