Type 解析

作者: Xcdf | 来源:发表于2019-02-08 18:43 被阅读20次

    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);
    
    

    相关文章

      网友评论

        本文标题:Type 解析

        本文链接:https://www.haomeiwen.com/subject/cconsqtx.html