ASM 字节码框架源码解析

Type 解析

2019-02-08  本文已影响20人  Xcdf

1.java类型和类型描述符

  在已编译的类中,描述字段和方法都有特定的描述符,对于普通类型每个类型编译之后对应的都是一个大写字母;对于对象类型使用L开头反斜杠做分隔符描述对象;对于数组类型,以[开头,结合内部使用的类型构成数组的描述。JVM的类型描述符有:


java类型的描述符

Integer类型的名称、内部名称和描述符的区别:

public static void main(String[] args){
    String name=Integer.class.getName();
    System.out.println("name: "+name);
    String internalName = Type.getInternalName(Integer.class);
    System.out.println("internalName: "+internalName);
    String descriptor = Type.getDescriptor(Integer.class);
    System.out.println("descriptor: "+descriptor);
 }
运行结果:
name: java.lang.Integer
internalName: java/lang/Integer
descriptor: Ljava/lang/Integer;

name和internal name 的关系:将name种的“.”换成“/”,代码如下:

  public static String getInternalName(final Class<?> clazz) {
    return clazz.getName().replace('.', '/');
  }

2.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;

  从Type类源码看出其中的方法都是公共静态方法,也就是说Type是ASM包下的工具类
属性:

  // Type属性的值,用来区别不同的Type类型  
  private final int sort;
  // 包含此字段或方法类型值的字符串。
  private final String valueBuffer;
  // 字符串的开始索引
  private final int valueBegin;
  // 字符串的结束索引
  private final int valueEnd;

构造器:构造器为私有,因此不能在类的外部创建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;
  }

常用方法有
  Type getType()

getType():Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc.

Type type = Type.getType(String.class);  
System.out.println(type.getDescriptor()); // Ljava/lang/String

方法的调用顺序:
1.getType(clazz)

  public static Type getType(final Class<?> clazz) {
    if (clazz.isPrimitive()) { // Java的基本类型
      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));
    }
  }

2.如果不是基本类型,则调用getType(getDescriptor(clazz))

  public static String getDescriptor(final Class<?> clazz) {
    StringBuilder stringBuilder = new StringBuilder();
    appendDescriptor(clazz, stringBuilder);
    return stringBuilder.toString();
  }

3.在getDescriptor(clazz) 中调用appendDescriptor(),将Clazz.getName()中的."."转换为"/",如果是数组则加"[",如果是对象则加"L"。

 private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
    Class<?> currentClass = clazz;
    // 多维数组
    while (currentClass.isArray()) {
      stringBuilder.append('[');
      // 获取组成数组元素的对象类型
      // 例如:String[][] array,最终结果是String.class
      currentClass = currentClass.getComponentType();
    }
    if (currentClass.isPrimitive()) {
      // 基本类型
      char descriptor;
      if (currentClass == Integer.TYPE) {
        descriptor = 'I';
      } else if (currentClass == Void.TYPE) {
        descriptor = 'V';
      } else if (currentClass == Boolean.TYPE) {
        descriptor = 'Z';
      } else if (currentClass == Byte.TYPE) {
        descriptor = 'B';
      } else if (currentClass == Character.TYPE) {
        descriptor = 'C';
      } else if (currentClass == Short.TYPE) {
        descriptor = 'S';
      } else if (currentClass == Double.TYPE) {
        descriptor = 'D';
      } else if (currentClass == Float.TYPE) {
        descriptor = 'F';
      } else if (currentClass == Long.TYPE) {
        descriptor = 'J';
      } else {
        throw new AssertionError();
      }
      stringBuilder.append(descriptor);
    } else {
      stringBuilder.append('L');
      String name = currentClass.getName();
      int nameLength = name.length();
      for (int i = 0; i < nameLength; ++i) {
        char car = name.charAt(i);
        // 将'.'换成'/',例如: java.lang.String --> java/lang/String
        stringBuilder.append(car == '.' ? '/' : car);
      }
      /** 将java/util/String  --> java/util/String;,这也是为什么
         getTypeInternal(typeDescriptor, 0, typeDescriptor.length()); 中的数组的长度,而不是length-1
      */
      stringBuilder.append(';');
    }
  }

4.如果获取了类的描述符,则调用getType的重构方法getType(final String typeDescriptor)

  public static Type getType(final String typeDescriptor) {
    // 注意 由于第第三部中的 stringBuilder.append(';'); 多加了一个";"
   // 这里的字符串的分割的索引下线为typeDescriptor.length(),而不是typeDescriptor.length()-1
    return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
  }
  1. 再根据字符串的特征来返回不同Type 实例,如果字符串的第一个字母为“VZCBSIFJD[L(”中的一个,则可以直接判断Type的类型。
  private static Type getTypeInternal(
      final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
    switch (descriptorBuffer.charAt(descriptorBegin)) {
      case 'V':
        return VOID_TYPE;
      case 'Z':
        return BOOLEAN_TYPE;
      case 'C':
        return CHAR_TYPE;
      case 'B':
        return BYTE_TYPE;
      case 'S':
        return SHORT_TYPE;
      case 'I':
        return INT_TYPE;
      case 'F':
        return FLOAT_TYPE;
      case 'J':
        return LONG_TYPE;
      case 'D':
        return DOUBLE_TYPE;
      case '[':
        return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd);
      case 'L':
        return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1);
      case '(':
        return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd);
      default:
        throw new IllegalArgumentException();
    }
  }

3.Type工具类的功能?

1.Type类的作用是什么?
  从方法的角度来看,getXXXType()获取java中字符串描述符、类(Class)、方法(Method)和构造器(Constructor)等的Type。
2.Type在其他类中的使用?
  SymbolTable.java:

    if (value instanceof Integer) {
      return addConstantInteger(((Integer) value).intValue());
    }......
    else if (value instanceof Type) {
      Type type = (Type) value;
      int typeSort = type.getSort();
      if (typeSort == Type.OBJECT) {
        return addConstantClass(type.getInternalName());
      } else if (typeSort == Type.METHOD) {
        return addConstantMethodType(type.getDescriptor());
      } else { // type is a primitive or array type.
        return addConstantClass(type.getDescriptor());
      }
    }

  从以上代码可以看出,Type 用来区分对象是基本类型还是对象类型,方法,还是数组,以更好的修改字节码文件。是从字节码文件的角度来区分。

  Symbol.java

  int getArgumentsAndReturnSizes() {
    if (info == 0) {
      info = Type.getArgumentsAndReturnSizes(value);
    }
    return info;
  }

3.为什么使用字符串分割的形式?
  比如要获取"V"这个字符串,使用了[VOID,VOID+1) 来限定字符串的范围

private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);

上一篇下一篇

猜你喜欢

热点阅读