来源:http://nickcen.spaces.live.com/?_c11_BlogPart_BlogPart=summary&_c=BlogPart&partqs=amonth%3D3%26ayear%3D2006
1.
流程控制:
a)
说明:JVM提供了基本的流程控制结构,这些结构都是基于Label而实现的。这些跳转指令,包括基于比较结果的有条件跳转和无条件的GOTO指令。另外,JVM也提供了实现switch结构的LOOKUPSWITCH和TABLESWITCH指令,其中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. :
1. 创建:
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,
"", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",
"", ()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
说明:构造函数在执行时,需要首先执行父类的构造函数或者类内部其他构造
函数。
2. 调用:
mv.visitTypeInsn(NEW, "asm/A");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "asm/A",
"", "()V");
mv.visitVarInsn(ASTORE, 1);
等价于:
A a = new A();
说明:在初始化一般对象时,我们需要先调用NEW指令,来创建该对象实例。而由于
后续的INVOKESPECIAL指令是调用类的构造函数,而该指令执行完以后,对对象的引
用将从栈中弹出,所以在NEW指令执行完以后,INVOKESPECIAL指令执行以前,我们
需要调用DUP指令,来增加对象引用的副本。
ii. :
1. 创建:
MethodVisitor mv = cw.visitMethod(ACC_STATIC,
"", "()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. 调用:在类被加载时自动调用。
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.
使用:通过ClassVisitor,FieldVisitor,MethodVisitor上的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",
"<:lasm>Ljava/lang/Object;Ljava/lang/Comparable;",
"java/lang/Object", new String[] {
"java/lang/Comparable" });
等价于:
public class A
extends B> implements Comparable
{
…
}
说明:在类定义当中使用范型时,需要增加Signature字段来添加范型信息。该Signature的组成是“父类描述
接口描述”
b)
范型字段:
i.
定义:
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "l",
"Ljava/util/List;",
"Ljava/util/List;",
null);
fv.visitEnd();
等价于:
private List
l;
说明:在声明范型字段时,需要增加Signature来增加范型信息。该Signature的组成是
“基类型描述”
ii.
使用:由于范型信息只是供编译器在编译时进行类型检查,而在编译以后该信息将会被擦除,因此在使用时与没有范型的情况一致。
c)
范型方法:
i.
定义:
mv = cw.visitMethod(ACC_PUBLIC, "getList",
"(Ljava/util/Map;)Ljava/util/List;", "(Ljava/util/Map;)Ljava/util/List;",
null);
等价于:
public List
getList(Map 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;",
"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,
"", "()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",
"",
"(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",
"",
"(Ljava/lang/String;I)V");
mv.visitFieldInsn(PUTSTATIC, "asm/E", "E2",
"Lasm/E;");
// 初始化ENUM$VALUES,将E1,E2存入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,
"", "(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",
"",
(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编译器来生成枚举。