`

使用ASM来书写Java代码(转)

 
阅读更多


要了解ASM,参见:http://www.ibm.com/developerworks/cn/java/j-lo-asm30/

来源:http://nickcen.spaces.live.com/?_c11_BlogPart_BlogPart=summary&_c=BlogPart&partqs=amonth%3D3%26ayear%3D2006

1.   流程控制:

a)        说明:JVM提供了基本的流程控制结构,这些结构都是基于Label而实现的。这些跳转指令,包括基于比较结果的有条件跳转和无条件的GOTO指令。另外,JVM也提供了实现switch结构的LOOKUPSWITCHTABLESWITCH指令,其中LOOKUPSWITCH是基于键比较的,而TABLESWITCH则是基于键索引的,因此后者的匹配速度更快。

2.   接口声明:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "asm/IA", null,

"java/lang/Object", null);

cw.visitSource("IA.java", null);

cw.visitEnd();

 

等价于:

public interface IA{}

 

3.   类声明:

a)        抽象类:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER + ACC_ABSTRACT, "asm/A", null,

"java/lang/Object", null);

cw.visitSource("A.java", null);

 

等价于:

public abstract class A{}

 

b)        具体类:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "asm/A", null, "java/lang/Object", null);

cw.visitSource("A.java", null);

cw.visitEnd();

 

等价于:

public class A{}

 

1.   类:
a)        数组:
                      i.              创建:
mv.visitInsn(ICONST_3);
mv.visitIntInsn(NEWARRAY, T_INT);
mv.visitVarInsn(ASTORE, 1);      //   将数组引用存到局部变量栈1号的位置
 
等价于:
int[] a = new int[3];
 
                   ii.              取值:
mv.visitVarInsn(ALOAD, 1);       //   数组引用在局部变量栈1号的位置
mv.visitInsn(ICONST_1);
mv.visitInsn(IALOAD);
mv.visitVarInsn(ISTORE, 2);
 
等价于:
int b = a[1];
 
                iii.              赋值:
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ICONST_1);
mv.visitInsn(ICONST_2);
mv.visitInsn(IASTORE);
 
等价于:
a[1] = 2;
 
b)        构造函数:
                      i.              <init>:
1.        创建:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", ()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
 
说明:构造函数<init>在执行时,需要首先执行父类的构造函数或者类内部其他构造
函数。
 
2.        调用:
mv.visitTypeInsn(NEW, "asm/A");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "asm/A", "<init>", "()V");
mv.visitVarInsn(ASTORE, 1);
 
等价于:
A a = new A();
 
说明:在初始化一般对象时,我们需要先调用NEW指令,来创建该对象实例。而由于
后续的INVOKESPECIAL指令是调用类的构造函数,而该指令执行完以后,对对象的引
用将从栈中弹出,所以在NEW指令执行完以后,INVOKESPECIAL指令执行以前,我们
需要调用DUP指令,来增加对象引用的副本。
 
                   ii.              <clinit>:
1.        创建:
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("hello world");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
"(Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 0);
mv.visitEnd();
 
等价于:
static {
System.out.println("hello world");
}
 
2.        调用:<clinit>在类被加载时自动调用。
 
c)        字段:
                      i.              一般字段:
1.        创建:
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "a", "I", null, null);
fv.visitEnd();
 
等价于:
private int a;
 
2.        读取:读取类当中名字为a,类型为int的字段的值。
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "asm/A", "a", "I");
 
3.        设置:
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTFIELD, "asm/A", "a", "I");
 
等价于:
a = 2;
 
                   ii.              静态字段:
1.        创建:
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "a", "I", null,
null);
fv.visitEnd();
 
等价于:
private static int a;
 
2.        读取:
mv.visitFieldInsn(GETSTATIC, "asm/A", "a", "I");
 
3.        设置:
mv.visitInsn(ICONST_2);
mv.visitFieldInsn(PUTSTATIC, "asm/A", "a", "I");
 
等价于:
a = 2;
 
d)        方法:
                      i.              接口方法:
1.        定义:
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "getA", "()V", null, null);
mv.visitEnd();
 
2.        调用:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "asm/IA", "getA", "()V");
 
等价于:
public interface IA{
     public void geA();
}
public class A implements IA{
     public void geA(){…}
}
 
IA a = new A();
a.getA();
 
                   ii.              一般方法:
1.        定义:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getA", "()V", null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 1);
mv.visitEnd();
 
等价于:
public void getA() {}
 
2.        调用:
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "asm/A", "getA", "()V");
 
等价于:
A a = new A():
a.getA();
 
                iii.              静态方法:
1.        定义:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getA", "()V",
null, null);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
 
等价于:
public static void getA() {}
 
2.        调用:
mv.visitMethodInsn(INVOKESTATIC, "asm/A", "getB", "()V");
 
等价于:
A.getB();
    
                   iv.              说明:一般方法比静态方法在声明和调用时均要多传入一个this引用作为参数。另外,当使用INVOKESPECIAL来调用方法时,虚拟机将直接根据指令当中所指明的类类型来调用方法;而当使用INVOKEVIRTUAL来调用方法时,虚拟机将根据实例的实际类型来调用方法。
 
e)        异常处理:
                      i.              声明:
mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");
mv.visitLabel(l0);
mv.visitLabel(l1);
等价于:
try {
} catch (Exception e) {
}
 
说明:在visitTryCatchBlock()当中,第一,二,三个参数均是Label实例,其中一,二表示try块的范围,三则是catch块的开始位置。而第四个参数则是异常类型。而当异常发生时,JVM将会将异常实例置于运行栈的栈顶。
 

1.   Signature

a)        说明:J2SE 5.0为了支持范型,参数化参数,Annotation和枚举等新增特性,因此增加了一个Signature属性,作为类,字段,方法的Description之外的一个辅助机制。

 

2.   Annotation

a)        Annotation

                      i.              定义:

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

"asm/AN", null, "java/lang/Object", new String[]

{ "java/lang/annotation/Annotation" });

 

等价于:

public @interface AN {}

 

                   ii.              使用:通过ClassVisitorFieldVisitorMethodVisitor上的visitAnnotation()方法,来获取一个AnnotationVisitor实例,从而为类,字段,方法设置Annotation

AnnotationVisitor av0 = cw.visitAnnotation("Lasm/AN;", false);

av0.visitEnd();

 

@AN

public class A{}

 

b)        属性:

                      i.              定义:

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "age", "()I", null, null);

mv.visitEnd();

 

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "name", "()Ljava/lang/String;",

null, null);

av0 = mv.visitAnnotationDefault();

av0.visit(null, "A");

av0.visitEnd();

mv.visitEnd();

 

等价于:

public @interface AN {

     int age();

     String name() default "A";

}

 

                   ii.              使用:

av0 = cw.visitAnnotation("Lasm/AN;", false);

av0.visit("age", new Integer(1));

av0.visit("name", "B");

av0.visitEnd();

 

等价于:

@AN(age = 1, name = "B")

public class A {

}

 

3.   范型:

a)        定义:

cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "asm/A", "<T::Lasm/IA;G:Lasm/B;>Ljava/lang/Object;Ljava/lang/Comparable;", "java/lang/Object", new String[] { "java/lang/Comparable" });

 

等价于:

public class A<T extends IA, G extends B> implements Comparable {

}

 

说明:在类定义当中使用范型时,需要增加Signature字段来添加范型信息。该Signature的组成是“<范型参数名范型扩展的类范型扩展的接口>父类描述 接口描述”

 

b)        范型字段:

                      i.              定义:

FieldVisitor fv = cw.visitField(ACC_PRIVATE, "l", "Ljava/util/List;",

"Ljava/util/List<Ljava/lang/String;>;", null);

fv.visitEnd();

 

等价于:

private List<String> l;

 

说明:在声明范型字段时,需要增加Signature来增加范型信息。该Signature的组成是

“基类型描述<参数类型描述>

 

                   ii.              使用:由于范型信息只是供编译器在编译时进行类型检查,而在编译以后该信息将会被擦除,因此在使用时与没有范型的情况一致。

 

c)        范型方法:

                      i.              定义:

mv = cw.visitMethod(ACC_PUBLIC, "getList", "(Ljava/util/Map;)Ljava/util/List;", "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;)Ljava/util/List<Ljava/lang/String;>;", null);

 

等价于:

public List<String> getList(Map<String, Integer> maps) {…}

 

                   ii.              使用:由于范型信息只是供编译器在编译时进行类型检查,而在编译以后该信息将会被擦除,因此在使用时与没有范型的情况一致。

 

4.   枚举:

a)        定义:

ClassWriter cw = new ClassWriter(false);

FieldVisitor fv;

MethodVisitor mv;

AnnotationVisitor av0;

 

cw.visit(V1_5, ACC_PUBLIC + ACC_FINAL + ACC_SUPER + ACC_ENUM, "asm/E",

"Ljava/lang/Enum<Lasm/E;>;", "java/lang/Enum", null);

cw.visitSource("E.java", null);

 

fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_ENUM, "E1", "Lasm/E;",

null, null);

fv.visitEnd();         //   定义静态字段E1

 

fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_ENUM, "E2", "Lasm/E;",

ull, null);

fv.visitEnd();         //   定义静态字段E2

 

fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC, "ENUM$VALUES",

"[Lasm/E;", null, null);

fv.visitEnd();         //   定义存储所有枚举值的静态字段ENUM$VALUES

 

mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);

mv.visitCode();

 

//   初始化E1

mv.visitTypeInsn(NEW, "asm/E");

mv.visitInsn(DUP);

mv.visitLdcInsn("E1");

mv.visitInsn(ICONST_0);

mv.visitMethodInsn(INVOKESPECIAL, "asm/E", "<init>", "(Ljava/lang/String;I)V");

mv.visitFieldInsn(PUTSTATIC, "asm/E", "E1", "Lasm/E;");

 

//   初始化E2

mv.visitTypeInsn(NEW, "asm/E");

mv.visitInsn(DUP);

mv.visitLdcInsn("E2");

mv.visitInsn(ICONST_1);

mv.visitMethodInsn(INVOKESPECIAL, "asm/E", "<init>", "(Ljava/lang/String;I)V");

mv.visitFieldInsn(PUTSTATIC, "asm/E", "E2", "Lasm/E;");

 

//   初始化ENUM$VALUES,将E1E2存入ENUM$VALUES当中

mv.visitInsn(ICONST_2);

mv.visitTypeInsn(ANEWARRAY, "asm/E");

mv.visitInsn(DUP);

mv.visitInsn(ICONST_0);

mv.visitFieldInsn(GETSTATIC, "asm/E", "E1", "Lasm/E;");

mv.visitInsn(AASTORE);

mv.visitInsn(DUP);

mv.visitInsn(ICONST_1);

mv.visitFieldInsn(GETSTATIC, "asm/E", "E2", "Lasm/E;");

mv.visitInsn(AASTORE);

mv.visitFieldInsn(PUTSTATIC, "asm/E", "ENUM$VALUES", "[Lasm/E;");

mv.visitInsn(RETURN);

mv.visitMaxs(8, 0);

mv.visitEnd();

 

mv = cw.visitMethod(ACC_PRIVATE, "<init>", "(Ljava/lang/String;I)V", null, null);

mv.visitCode();

mv.visitVarInsn(ALOAD, 0);

mv.visitVarInsn(ALOAD, 1);

mv.visitVarInsn(ILOAD, 2);

mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "<init>",

(Ljava/lang/String;I)V");

mv.visitInsn(RETURN);

mv.visitMaxs(3, 3);

mv.visitEnd();

 

//   使用arraycopy()方法,将ENUM$VALUES的值存入一个新数组当中,并返回。

mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "values", "()[Lasm/E;", null,

null);

...

mv.visitEnd();

 

//   返回某个枚举值的字符表示

mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "valueOf",

"(Ljava/lang/String;)Lasm/E;", null, null);

...

mv.visitEnd();

 

cw.visitEnd();

 

等价于:

public enum E {

     E1, E2

}

 

b)        使用:

mv.visitFieldInsn(GETSTATIC, "asm/E", "E1", "Lasm/E;");

 

等价于:

E e = E.E1;

 

c)        说明:从上面的代码可以看到,即使是一个简单的枚举,也需要使用很多的代码才能定义,因此更可行的办法是使用Java编译器来生成枚举。

分享到:
评论

相关推荐

    基于JavaParser的代码调用链分析,可以用于分析Java代码的方法调用链.zip

    总的来说,这个压缩包提供的资源对于理解和实践Java代码的调用链分析非常有帮助,无论是初学者还是经验丰富的开发者,都能从中受益。通过深入学习和应用JavaParser,我们可以更好地理解和优化Java代码,提升软件的...

    asm_java.rar_asm_java

    总的来说,这个`asm_java.rar_asm_java`压缩包提供了一个完整的ASM模型的Java实现,包括了理论介绍、源代码、可能的额外资源以及可能使用的ASM库。通过深入研究这个项目,用户不仅可以了解ASM模型的工作原理,还能...

    ASM操作字节码,动态生成Java类class文件

    通过这样的方式,ASM可以帮助我们深入理解Java字节码,同时也为我们提供了强大的工具来实现动态代码生成,这对于实现诸如AOP这样的高级编程模式至关重要。在实际项目中,ASM通常与其他工具结合使用,如CGLIB或Byte...

    ASM4使用指南 - ASM 4 Guide 中文版

    ASM 4 使用指南中文版。ASM是Java字节码的工业级库。长期以来一直没有中文版。这下好了,中文版横空出世。感兴趣的同学可以投递简历 mars # oneapm . com 常年有效 ASM 4.0 A Java bytecode engineering library

    JavaEE源代码 asm-attrs

    JavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-attrsJavaEE源代码 asm-...

    asm操作指南(中文)

    - **代码生成**:ASM可以用于自动生成Java类,特别适用于需要动态生成大量代码的情况。 - **代码优化**:通过对字节码进行修改,可以实现代码优化,例如去除冗余代码、减少循环次数等。 - **安全增强**:通过在代码...

    JAVA代码优化工具

    Java代码优化是提升应用程序性能的关键步骤,尤其是在大型企业级应用或者高性能服务中。优化能够减少内存消耗,提高程序运行速度,降低CPU使用率,并改善整体的用户体验。在Java开发领域,有多种工具可以帮助开发者...

    asm with java and matlab

    本文档详细描述了活跃形状模型(Active Shape Model,ASM)算法在JAVA和MATLAB上的实现过程,并包含了相关的代码实现。ASM是一种用于检测和建模图像中形状的技术,尤其是在人脸检测和分析领域内得到了广泛的应用。本...

    Mixin,MIXIN是使用ASM的Java的特性/混合框架.zip

    总的来说,Mixin是一个强大的工具,它利用ASM库为Java开发者提供了一种优雅且高效的代码扩展方式。通过学习和使用Mixin,开发者可以更好地理解和控制程序的运行时行为,同时保持代码的清晰和模块化。开源的特性也让...

    Java字节码和asm入门资料

    Java字节码是Java虚拟机...总的来说,Java字节码和ASM是Java开发中的高级主题,它们对于理解Java运行机制、实现高性能的代码以及进行动态编程至关重要。通过学习和实践,开发者可以掌握这些技术,提升自己的编程能力。

    Asm汇编代码转换器

    就是 Asm汇编语言转易语言的置入代码的工具 此工具有双引擎 ------ Masm引擎 跟 OD引擎 Masm引擎兼较好 OD引擎兼容性(我都不想吐槽) 汇编功能支持 代码高亮 (亮瞎我的狗眼啊..魂淡) 此版本为Ver1.0 如果可能的...

    51HEX转ASM软件

    总的来说,"51HEX转ASM软件"是MCS51单片机开发中的一款实用工具,它填补了HEX文件与源代码之间的鸿沟,使得代码研究和调试工作更加高效。对于从事或学习51单片机编程的人员,掌握这款软件的使用无疑会提高工作效率,...

    ASM算法源代码

    这个库可能提供了ASM算法的接口和示例代码,帮助开发者快速集成ASM到自己的项目中。 总结来说,ASM算法是一种强大的形状建模和对象检测方法,特别适用于人脸识别。通过理解其核心原理和步骤,结合OpenCV这样的工具...

    初学者必看!ASM如何转换为HEX文件).rar

    4. **链接器的作用**:如果ASM源代码引用了其他模块的函数或数据,就需要链接器(Linker)来合并这些单独的OBJ文件,解决地址冲突,生成最终的可执行文件或库。 5. **HEX文件格式**:Intel HEX格式是一种标准的二...

    JavaEE源代码 asm-2.2.2

    JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2JavaEE源代码 asm-2.2.2...

    学习asm汇编的代码

    CIH病毒的作者使用了汇编语言编写,这些文件可能是分析或逆向工程CIH病毒的代码片段,帮助学习者理解病毒的工作原理和如何防止类似的攻击。 2. **CMOSBOOT.asm**: CMOS是计算机中的非易失性存储器,用于存储BIOS...

    ASM4使用指南

    综上所述,《ASM4使用指南》是一份全面的文档,它为Java开发人员提供了一个强大的工具来处理运行时的字节码操作。通过学习和使用 ASM4,开发者可以更加灵活地控制Java字节码,进而实现复杂的编程任务,提高应用程序...

    ASM4手册中文版.pdf.zip

    ASM4是中国Java开发者常用的一款字节码操作框架ASM的第四个主要版本,它主要用于动态生成和分析Java字节码。ASM是一个低级别的库,可以直接操作和生成类的字节码,这在创建编译器、代码分析工具以及运行时代码修改等...

    asm2com asm转com程序

    【标题解析】:“asm2com asm转com程序”指的是一个特定的软件工具,它的主要功能是将汇编语言(Assembly)编写的源代码转换成COM(Command Output Module)格式的可执行文件。COM文件是早期DOS操作系统下常见的...

    asm官方代码文档

    综上所述,《ASM官方代码文档》是学习和使用ASM不可或缺的资料,无论你是Java字节码的初学者还是资深开发者,都能从中获取宝贵的知识和实践经验。通过对ASM的深入理解和运用,开发者可以实现更多底层功能,提高代码...

Global site tag (gtag.js) - Google Analytics