在ASM的代码中,有一个Type类(org.objectweb.asm.Type)。为什么会有这样一个Type类呢?
大家知道,在JDK当中有一个java.lang.reflect.Type类。对于java.lang.reflect.Type类来说,它是一个接口,它有一个我们经常使用的子类,即java.lang.Class;相应的,在ASM当中有一个org.objectweb.asm.Type类。
JDK | ASM | |
---|---|---|
类名 | java.lang.reflect.Type | org.objectweb.asm.Type |
位置 | rt.jar | asm.jar |
在编写代码层面,如果我们不能区分出java.lang.reflect.Type类和org.objectweb.asm.Type类,我们也不能很好的使用它们。
- Java File:具体表现为.java文件,在里面使用Java语言编写代码,它是属于Java Language Specification的范畴。
- Class File:具体表现为.class文件,它里面的内容遵循ClassFile的结构,它是属于JVM Specification的范畴。
- ASM:它是一个类库。我们在编写ASM代码的时候,是在.java文件中编写,使用的是Java语言,而它所 *** 作的对象却是.class文件。
换句话说,ASM实现,从本质上来说,是一只脚踩在Java Language Specification的范畴,而另一只脚却踩在JVM Specification的范畴。ASM,在这两个范畴中,扮演的一个非常重要的角色,就是将Java Language Specification范畴的概念和JVM Specification范畴的概念进行转换。
这两个范畴,是相关的,但是又不是那种密不可分的关系。比如说,Java语言编写的程序可以运行在JVM上,Scala语言编写的程序也可以运行在JVM上,甚至Python语言编写的程序也可以编写在JVM上;也就是说,某一种编程语言和JVM之间,并不是一种非常强的依赖关系。
Java Language Specification | ASM | JVM Specification |
---|---|---|
int | <--- 向左转换 ---Type--- 向右转换 ---> | I |
float | <--- 向左转换 ---Type--- 向右转换 ---> | F |
java.lang.String | <--- 向左转换 ---Type--- 向右转换 ---> | java/lang/String |
在.java文件中,我们经常使用java.lang.Class类;而在.class文件中,需要经常用到internal name、type descriptor和method descriptor;而在ASM中,org.objectweb.asm.Type类就是帮助我们进行两者之间的转换。
2. Type类 2.1. class info第一个部分,Type类继承自Object类,而且带有final标识,所以不会存在子类。
public final class Type {
}
2.2. fields
第二个部分,Type类定义的字段有哪些。这里我们列出了4个字段,这4个字段可以分成两组。
- 第一组,只包括sort字段,是int类型,它标识了Type类的类别。
- 第二组,包括valueBuffer、valueBegin和valueEnd字段,这3个字段组合到一起表示一个value值,本质上就是一个字符串。
public final class Type {
// 标识类型
private final int sort;
// 标识内容
private final String valueBuffer;
private final int valueBegin;
private final int valueEnd;
}
2.3. constructors
第三个部分,Type类定义的构造方法有哪些。由于Type类的构造方法用private修饰,因此“外界”不能使用new关键字创建Type对象。
public final class Type {
private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) {
this.sort = sort;
this.valueBuffer = valueBuffer;
this.valueBegin = valueBegin;
this.valueEnd = valueEnd;
}
}
2.4. methods
第四个部分,Type类定义的方法有哪些。在Type类里,定义了一些方法,这些方法是与字段有直接关系的。
public final class Type {
public int getSort() {
return sort == INTERNAL ? OBJECT : sort;
}
public String getClassName() {
switch (sort) {
case VOID:
return "void";
case BOOLEAN:
return "boolean";
case CHAR:
return "char";
case BYTE:
return "byte";
case SHORT:
return "short";
case INT:
return "int";
case FLOAT:
return "float";
case LONG:
return "long";
case DOUBLE:
return "double";
case ARRAY:
StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName());
for (int i = getDimensions(); i > 0; --i) {
stringBuilder.append("[]");
}
return stringBuilder.toString();
case OBJECT:
case INTERNAL:
return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.');
default:
throw new AssertionError();
}
}
public String getInternalName() {
return valueBuffer.substring(valueBegin, valueEnd);
}
public String getDescriptor() {
if (sort == OBJECT) {
return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
} else if (sort == INTERNAL) {
return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
} else {
return valueBuffer.substring(valueBegin, valueEnd);
}
}
}
关于这些方法的使用,示例如下:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.getType("Ljava/lang/String;");
int sort = t.getSort(); // ASM
String className = t.getClassName(); // Java File
String internalName = t.getInternalName(); // Class File
String descriptor = t.getDescriptor(); // Class File
System.out.println(sort); // 10,它对应于Type.OBJECT字段
System.out.println(className); // java.lang.String 注意,分隔符是“.”
System.out.println(internalName); // java/lang/String 注意,分隔符是“/”
System.out.println(descriptor); // Ljava/lang/String; 注意,分隔符是“/”,前有“L”,后有“;”
}
}
3. 静态成员
3.1. 静态字段
在Type类里,定义了一些常量字段,有int类型,也有String类型。
public final class Type {
public static final int VOID = 0;
public static final int BOOLEAN = 1;
public static final int CHAR = 2;
public static final int BYTE = 3;
public static final int SHORT = 4;
public static final int INT = 5;
public static final int FLOAT = 6;
public static final int LONG = 7;
public static final int DOUBLE = 8;
public static final int ARRAY = 9;
public static final int OBJECT = 10;
public static final int METHOD = 11;
private static final int INTERNAL = 12;
private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
}
在Type类里,也定义了一些Type类型的字段,这些字段是由上面的int和String类型的字段组合得到。
public final class Type {
public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);
public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1);
public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1);
public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1);
public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1);
public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1);
public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1);
public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1);
public static final Type DOUBLE_TYPE = new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1);
}
3.2. 静态方法
这里介绍的几个get*Type()方法,是静态(static)方法。这几个方法的主要目的就是得到一个Type对象。
public final class Type {
public static Type getType(final Class clazz) {
if (clazz.isPrimitive()) {
if (clazz == Integer.TYPE) {
return INT_TYPE;
} else if (clazz == Void.TYPE) {
return VOID_TYPE;
} else if (clazz == Boolean.TYPE) {
return BOOLEAN_TYPE;
} else if (clazz == Byte.TYPE) {
return BYTE_TYPE;
} else if (clazz == Character.TYPE) {
return CHAR_TYPE;
} else if (clazz == Short.TYPE) {
return SHORT_TYPE;
} else if (clazz == Double.TYPE) {
return DOUBLE_TYPE;
} else if (clazz == Float.TYPE) {
return FLOAT_TYPE;
} else if (clazz == Long.TYPE) {
return LONG_TYPE;
} else {
throw new AssertionError();
}
} else {
return getType(getDescriptor(clazz));
}
}
public static Type getType(final Constructor<?> constructor) {
return getType(getConstructorDescriptor(constructor));
}
public static Type getType(final Method method) {
return getType(getMethodDescriptor(method));
}
public static Type getType(final String typeDescriptor) {
return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
}
public static Type getMethodType(final String methodDescriptor) {
return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length());
}
public static Type getObjectType(final String internalName) {
return new Type(internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length());
}
}
3.3. 获取Type对象
Type类有一个private的构造方法,因此Type对象实例不能通过new关键字来创建。但是,Type类提供了static method和static field来获取对象。
3.3.1. 方式一:java.lang.Class从一个java.lang.Class对象来获取Type对象:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.getType(String.class);
System.out.println(t);
}
}
3.3.2. 方式二:descriptor
从一个描述符(descriptor)来获取Type对象:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t1 = Type.getType("Ljava/lang/String;");
System.out.println(t1);
// 这里是方法的描述符
Type t2 = Type.getMethodType("(II)I");
System.out.println(t2);
}
}
3.3.3. 方式三:internal name
从一个internal name来获取Type对象:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.getObjectType("java/lang/String");
System.out.println(t);
}
}
3.3.4. 方式四:static field
从一个Type类的静态字段来获取Type对象:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.INT_TYPE;
System.out.println(t);
}
}
4. 特殊的方法
4.1. array-related methods
这里介绍的两个方法与数组类型相关:
- getDimensions()方法,用于获取数组的维度
- getElementType()方法,用于获取数组的元素的类型
public final class Type {
public int getDimensions() {
int numDimensions = 1;
while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
numDimensions++;
}
return numDimensions;
}
public Type getElementType() {
final int numDimensions = getDimensions();
return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd);
}
}
示例代码:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.getType("[[[[[Ljava/lang/String;");
int dimensions = t.getDimensions();
Type elementType = t.getElementType();
System.out.println(dimensions); // 5
System.out.println(elementType); // Ljava/lang/String;
}
}
4.2. method-related methods
这里介绍的两个方法与“方法”相关:
- getArgumentTypes()方法,用于获取“方法”接收的参数类型
- getReturnType()方法,用于获取“方法”返回值的类型
public final class Type {
public Type[] getArgumentTypes() {
return getArgumentTypes(getDescriptor());
}
public Type getReturnType() {
return getReturnType(getDescriptor());
}
}
示例代码:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type methodType = Type.getMethodType("(Ljava/lang/String;I)V");
String descriptor = methodType.getDescriptor();
Type[] argumentTypes = methodType.getArgumentTypes();
Type returnType = methodType.getReturnType();
System.out.println("Descriptor: " + descriptor);
System.out.println("Argument Types:");
for (Type t : argumentTypes) {
System.out.println(" " + t);
}
System.out.println("Return Type: " + returnType);
}
}
输出结果:
Descriptor: (Ljava/lang/String;I)V
Argument Types:
Ljava/lang/String;
I
Return Type: V
4.3. size-related methods
这里列举的3个方法是与“类型占用slot空间的大小”相关:
getSize()
方法,用于返回某一个类型所占用的slot空间的大小。getArgumentsAndReturnSizes()
方法,用于返回方法所对应的slot空间的大……- 其中,参数大小
int argumentsSize = argumentsAndReturnSizes >> 2;
。注意,在argumentsSize
当中包含了隐藏的this
变量。 - 其中,返回值大小
int returnSize = argumentsAndReturnSizes 0x03)
- 其中,参数大小
public final class Type {
public int getSize() {
switch (sort) {
case VOID:
return 0;
case BOOLEAN:
case CHAR:
case BYTE:
case SHORT:
case INT:
case FLOAT:
case ARRAY:
case OBJECT:
case INTERNAL:
return 1;
case LONG:
case DOUBLE:
return 2;
default:
throw new AssertionError();
}
}
public int getArgumentsAndReturnSizes() {
return getArgumentsAndReturnSizes(getDescriptor());
}
public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
int argumentsSize = 1;
// Skip the first character, which is always a '('.
int currentOffset = 1;
int currentChar = methodDescriptor.charAt(currentOffset);
// Parse the argument types and compute their size, one at a each loop iteration.
while (currentChar != ')') {
if (currentChar == 'J' || currentChar == 'D') {
currentOffset++;
argumentsSize += 2;
} else {
while (methodDescriptor.charAt(currentOffset) == '[') {
currentOffset++;
}
if (methodDescriptor.charAt(currentOffset++) == 'L') {
// Skip the argument descriptor content.
int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
}
argumentsSize += 1;
}
currentChar = methodDescriptor.charAt(currentOffset);
}
currentChar = methodDescriptor.charAt(currentOffset + 1);
if (currentChar == 'V') {
return argumentsSize << 2;
} else {
int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
return argumentsSize << 2 | returnSize;
}
}
}
示例代码:
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.INT_TYPE;
System.out.println(t.getSize()); // 1
}
}
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.LONG_TYPE;
System.out.println(t.getSize()); // 2
}
}
import org.objectweb.asm.Type;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.getMethodType("(II)I");
int value = t.getArgumentsAndReturnSizes();
int argumentsSize = value >> 2;
int returnSize = value & 0b11;
System.out.println(argumentsSize); // 3
System.out.println(returnSize); // 1
}
}
4.4. opcode-related methods
这里介绍的方法与opcode相关:
- getOpcode()方法,会让我们写代码的过程中更加方便。
public final class Type {
public int getOpcode(final int opcode) {
if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
switch (sort) {
case BOOLEAN:
case BYTE:
return opcode + (Opcodes.BALOAD - Opcodes.IALOAD);
case CHAR:
return opcode + (Opcodes.CALOAD - Opcodes.IALOAD);
case SHORT:
return opcode + (Opcodes.SALOAD - Opcodes.IALOAD);
case INT:
return opcode;
case FLOAT:
return opcode + (Opcodes.FALOAD - Opcodes.IALOAD);
case LONG:
return opcode + (Opcodes.LALOAD - Opcodes.IALOAD);
case DOUBLE:
return opcode + (Opcodes.DALOAD - Opcodes.IALOAD);
case ARRAY:
case OBJECT:
case INTERNAL:
return opcode + (Opcodes.AALOAD - Opcodes.IALOAD);
case METHOD:
case VOID:
throw new UnsupportedOperationException();
default:
throw new AssertionError();
}
} else {
switch (sort) {
case VOID:
if (opcode != Opcodes.IRETURN) {
throw new UnsupportedOperationException();
}
return Opcodes.RETURN;
case BOOLEAN:
case BYTE:
case CHAR:
case SHORT:
case INT:
return opcode;
case FLOAT:
return opcode + (Opcodes.FRETURN - Opcodes.IRETURN);
case LONG:
return opcode + (Opcodes.LRETURN - Opcodes.IRETURN);
case DOUBLE:
return opcode + (Opcodes.DRETURN - Opcodes.IRETURN);
case ARRAY:
case OBJECT:
case INTERNAL:
if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) {
throw new UnsupportedOperationException();
}
return opcode + (Opcodes.ARETURN - Opcodes.IRETURN);
case METHOD:
throw new UnsupportedOperationException();
default:
throw new AssertionError();
}
}
}
}
示例代码:
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.Printer;
public class HelloWorldRun {
public static void main(String[] args) throws Exception {
Type t = Type.FLOAT_TYPE;
int[] opcodes = new int[]{
Opcodes.IALOAD,
Opcodes.IASTORE,
Opcodes.ILOAD,
Opcodes.ISTORE,
Opcodes.IADD,
Opcodes.ISUB,
Opcodes.IRETURN,
};
for (int oldOpcode : opcodes) {
int newOpcode = t.getOpcode(oldOpcode);
String oldName = Printer.OPCODES[oldOpcode];
String newName = Printer.OPCODES[newOpcode];
System.out.printf("%-7s --- %-7s%n", oldName, newName);
}
}
}
输出结果:
IALOAD --- FALOAD
IASTORE --- FASTORE
ILOAD --- FLOAD
ISTORE --- FSTORE
IADD --- FADD
ISUB --- FSUB
IRETURN --- FRETURN
5. 总结
- 第一点,Type类的作用是什么?Type类是一个工具类,它的一个主要目的是将Java语言当中的概念转换成ClassFile当中的概念。
- 第二点,学习Type类的方式就是“分而治之”。在Type类当中,定义了许多的字段和方法,它们是一个整体,内容也很繁杂;于是,我们将Type类分成不同的部分来讲解,就是希望大家能循序渐进的理解这个类的各个部分,方便以后对该类的使用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)