zoukankan      html  css  js  c++  java
  • JVM Class信息分析 规格严格

    看了JVM虚拟机规范,之前看ClassFile的描述,总是觉得很模糊,这次周末又一次看了一遍,决定写点代码分析一下,Oracle的JDK提供了

    javap,参照这个写了简单的分析类,就是为了辅助学习用,因为规范里面都是u类型的,而且是Big-Indian,使用DataInput正好符合。

    代码很简单,我也没有按照什么面向对象来分析,就是走一步算一步,写到哪算到哪,很多可能都重复了,望见谅,就是个练手。

    看代码的时候,请参照虚拟机规范看,其实这个就是个理论,关键还是JVM的实现以及API的类库,再一次感慨一下。

    注意一下,Attribute的分析我没写,其实也是分析字节流,实在懒的写的,以后在补上吧。整个代码就是读流,读文件内容按照预先定义的格式进行解析。

    import java.io.DataInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.text.DecimalFormat;
    import java.text.NumberFormat;

    public class SimpleClassAnalizer {

     static NumberFormat formatter = new DecimalFormat("00");
     static final int ACC_PUBLIC = 0x0001;
     static final int ACC_FINAL = 0x0010;
     static final int ACC_SUPER = 0x0020;
     static final int ACC_INTERFACE = 0x0200;
     static final int ACC_ABSTRACT = 0x0400;
     static final int ACC_PRIVATE = 0x0002;
     static final int ACC_PROTECTED = 0x0004;
     static final int ACC_STATIC = 0x0008;
     static final int ACC_VOLATILE = 0x0040;
     static final int ACC_TRANSIENT = 0x0080;
     static final int ACC_SYNCHRONIZED = 0x0020;
     static final int ACC_NATIVE = 0x0100;
     static final int ACC_STRICT = 0x0800;

     static enum ConstantPoolTag {
      CONSTANT_Class(7), CONSTANT_Fieldref(9), CONSTANT_Methodref(10), CONSTANT_InterfaceMethodref(
        11), CONSTANT_String(8), CONSTANT_Integer(3), CONSTANT_Float(4), CONSTANT_Long(
        5), CONSTANT_Double(6), CONSTANT_NameAndType(12), CONSTANT_Utf8(
        1);

      private int tag;

      ConstantPoolTag(int tagN) {
       this.tag = tagN;
      }

      public int getTag() {
       return tag;
      }

      public static ConstantPoolTag getConstantPoolTag(int tagN) {
       switch (tagN) {
       case 7:
        return CONSTANT_Class;
       case 9:
        return CONSTANT_Fieldref;
       case 10:
        return CONSTANT_Methodref;
       case 11:
        return CONSTANT_InterfaceMethodref;
       case 8:
        return CONSTANT_String;
       case 3:
        return CONSTANT_Integer;
       case 4:
        return CONSTANT_Float;
       case 5:
        return CONSTANT_Long;
       case 6:
        return CONSTANT_Double;
       case 12:
        return CONSTANT_NameAndType;
       case 1:
        return CONSTANT_Utf8;
       default:
        return null;
       }
      }
     }

     public static void main(String[] args) {
      InputStream fin = SimpleClassAnalizer.class
        .getResourceAsStream("Demo.class");
      DataInputStream din = new DataInputStream(fin);
      printMagic(din);
      printVersion(din);
      printConstantPool(din);
      printAccessFlag(din, true);
      printClassName("this", din);
      printClassName("super", din);
      printInterfaceInfo(din);
      printFieldInfo(din);
      printMethodInfo(din);
      printClassAttributes(din);
     }

     private static void printClassAttributes(DataInputStream din) {
      try {
       // 符合Big-Indian
       int attriCnt = din.readUnsignedShort();
       System.out.println("Class Attributes: " + attriCnt);
       for (int i = 0; i < attriCnt; i++) {
        printAttributeInfo(din, i);
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printMethodInfo(DataInputStream din) {
      try {
       // 符合Big-Indian
       int methodCnt = din.readUnsignedShort();
       System.out.println("MethodCnt: " + methodCnt);
       for (int i = 0; i < methodCnt; i++) {
        printAccessFlag(din, false);
        int name_index = din.readUnsignedShort();
        int descriptor_index = din.readUnsignedShort();
        System.out.println("Method name \t#" + name_index);
        System.out.println("Method descriptor \t#" + descriptor_index);
        int attributes_count = din.readUnsignedShort();
        System.out.println("Method AttributesCnt: " + attributes_count);
        for (int j = 0; j < attributes_count; j++) {
         printAttributeInfo(din, j);
        }
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printClassName(String name, DataInputStream din) {
      try {
       // 符合Big-Indian
       int index = din.readUnsignedShort();
       System.out.println(name + "\t" + " #" + index);
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printFieldInfo(DataInputStream din) {
      try {
       // 符合Big-Indian
       int fieldCnt = din.readUnsignedShort();
       System.out.println("FieldCnt: " + fieldCnt);
       for (int i = 0; i < fieldCnt; i++) {
        printAccessFlag(din, false);
        int name_index = din.readUnsignedShort();
        int descriptor_index = din.readUnsignedShort();
        System.out.println("Field name \t#" + name_index);
        System.out.println("Field descriptor \t#" + descriptor_index);
        int attributes_count = din.readUnsignedShort();
        System.out.println("Field AttributesCnt: " + attributes_count);
        for (int j = 0; j < attributes_count; j++) {
         printAttributeInfo(din, j);
        }
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     // Not implement
     private static void printAttributeInfo(DataInputStream din, int j) {
      // Method
      try {
       // 符合Big-Indian
       int attribute_name_index = din.readUnsignedShort();
       System.out.println("AttributNameIndex: #" + attribute_name_index);
       // u4 <-> int can be replaced?
       int attribute_length = din.readInt();
       byte[] tmp = new byte[attribute_length];
       din.readFully(tmp);
       // System.out.println("AttributeInfo: " + new String(tmp));
       // Not Analysis now ,later mended
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printInterfaceInfo(DataInputStream din) {
      try {
       // 符合Big-Indian
       int interfaceCnt = din.readUnsignedShort();
       System.out.println("InterfaceCnt: " + interfaceCnt);
       for (int i = 0; i < interfaceCnt; i++) {
        System.out.println("Interface: \t#" + din.readUnsignedShort());
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printAccessFlag(DataInputStream din,
       boolean classOrInterface) {
      try {
       int accessFlag = din.readUnsignedShort();
       System.out.print("AccessFlagInfo: ");
       StringBuilder buf = new StringBuilder();

       if ((accessFlag & ACC_FINAL) > 0) {
        buf.append("Final ");
       }

       if (classOrInterface) {
        if ((accessFlag & ACC_SUPER) > 0) {
         // set
         buf.append("Super ");
        }

        if ((accessFlag & ACC_INTERFACE) > 0) {
         buf.append("Interface ");
        } else {
         buf.append("Class ");
        }
       }

       if ((accessFlag & ACC_ABSTRACT) > 0) {
        buf.append("Abstract ");
       }

       // Field/Method/Class must one of this
       if ((accessFlag & ACC_PUBLIC) > 0) {
        buf.append("Public ");
       } else if ((accessFlag & ACC_PRIVATE) > 0) {
        buf.append("Private ");
       } else if ((accessFlag & ACC_PROTECTED) > 0) {
        buf.append("Protected ");
       } else {
        buf.append("Package ");
       }

       if (!classOrInterface) {
        // Field
        if ((accessFlag & ACC_STATIC) > 0) {
         buf.append("Static ");
        }

        if ((accessFlag & ACC_VOLATILE) > 0) {
         buf.append("Volatile ");
        }

        if ((accessFlag & ACC_TRANSIENT) > 0) {
         buf.append("Transient ");
        }

        if ((accessFlag & ACC_SYNCHRONIZED) > 0) {
         buf.append("Synchronized ");
        }
        if ((accessFlag & ACC_NATIVE) > 0) {
         buf.append("Native ");
        }
        if ((accessFlag & ACC_STRICT) > 0) {
         buf.append("FPStrict ");
        }
       }

       System.out.println(buf.toString().trim());
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printConstantPool(DataInputStream din) {
      try {
       // 符合Big-Indian
       int poolSize = din.readUnsignedShort();
       System.out.println("ConstantPoolSize: " + poolSize);
       for (int i = 0; i < poolSize - 1; i++) {
        byte tag = din.readByte();
        readConstPool(ConstantPoolTag.getConstantPoolTag(tag), din,
          i + 1);
        if ((ConstantPoolTag.getConstantPoolTag(tag) == ConstantPoolTag.CONSTANT_Double)
          || (ConstantPoolTag.getConstantPoolTag(tag) == ConstantPoolTag.CONSTANT_Long)) {
         i++;
        }
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void readConstPool(ConstantPoolTag constantPoolTag,
       DataInputStream din, int index) {
      // only deal with several situation
      try {
       switch (constantPoolTag) {
       case CONSTANT_Class:
        int name_index = din.readUnsignedShort();
        System.out.println("#" + index + " = " + constantPoolTag
          + "\tNameIndex: #" + name_index);
        break;
       case CONSTANT_Utf8:
        // readUtf also read length
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + din.readUTF());
        break;
       case CONSTANT_NameAndType:
        name_index = din.readUnsignedShort();
        int descriptor_index = din.readUnsignedShort();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + "#" + name_index + ":#" + descriptor_index);
        break;
       case CONSTANT_String:
        name_index = din.readUnsignedShort();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + "#" + name_index);
        break;
       case CONSTANT_Methodref:
       case CONSTANT_Fieldref:
       case CONSTANT_InterfaceMethodref:
        name_index = din.readUnsignedShort();
        descriptor_index = din.readUnsignedShort();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + "#" + name_index + ":#" + descriptor_index);
        break;
       case CONSTANT_Integer:
        int val = din.readInt();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + " v=" + val);
        break;
       case CONSTANT_Float:
        float fval = din.readFloat();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + " fv=" + fval);
        break;
       case CONSTANT_Double:
        double dval = din.readDouble();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + " dv=" + dval);
        break;
       case CONSTANT_Long:
        long lval = din.readLong();
        System.out.println("#" + index + " = " + constantPoolTag + "\t"
          + " lv=" + lval);
        break;
       default:
        return;
       }
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printVersion(DataInputStream din) {
      try {
       int version = din.readInt();
       System.out.print("Version: ");
       for (int i = 2; i > 0; i--) {
        System.out.print(formatter
          .format((version >>> (2 - i) * 16) & 0xFFFF));
        System.out.print(i == 2 ? "." : "");
       }
       System.out.println();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

     private static void printMagic(DataInputStream din) {
      try {
       int magic = din.readInt();
       System.out.print("Magic String: ");
       for (int i = 0; i < 4; i++) {
        System.out.print(Integer
          .toHexString(((magic >>> (3 - i) * 8) & 0xFF)));
       }
       System.out.println();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }
    }

    写完之后,我发现Google上一个国人写的分析常量池的代码,当然是他整体代码的一部分,我觉得写的很好,好在结构很清晰,其实思路都是一样,所以说当思路一样的时候,人的解决方案建模能力能够看出人的水平,我还是建模和模式能力差啊。代码也附上,有兴趣的可以看看,版权不在我,望周知:

    import java.io.DataInputStream;
    import java.util.ArrayList;
    import java.util.HashMap;

    public class ConsantPool {
        static int count = 0;
        static int index = 1;

        static ArrayList<Object> l = new ArrayList<Object>();
        static byte tag_main;

        public static HashMap<Integer, String> consantPool = new HashMap<Integer, String>();

        public static void parseConstant_pool(int count2, DataInputStream in)
                throws Exception {
            count = count2 - 1;
            System.out.println("start parse count = " + count);

            while (count-- > 0) {
                tag_main = in.readByte();
                CONSTANT_Class c = new CONSTANT_Class();
                CONSTANT_Utf8_info u = new CONSTANT_Utf8_info();
                CONSTANT_Fieldref_info f = new CONSTANT_Fieldref_info();
                CONSTANT_Methodref_info m = new CONSTANT_Methodref_info();
                CONSTANT_InterfaceMethodref_info itf = new CONSTANT_InterfaceMethodref_info();
                CONSTANT_String_info s = new CONSTANT_String_info();
                CONSTANT_Integer_info i = new CONSTANT_Integer_info();
                CONSTANT_Float_info fl = new CONSTANT_Float_info();
                CONSTANT_Long_info l = new CONSTANT_Long_info();
                CONSTANT_Double_info d = new CONSTANT_Double_info();
                CONSTANT_NameAndType_info n = new CONSTANT_NameAndType_info();

                c.parse(in);

                u.parse(in);

                f.parse(in);

                m.parse(in);

                itf.parse(in);

                s.parse(in);

                i.parse(in);

                fl.parse(in);

                l.parse(in);

                d.parse(in);

                n.parse(in);
            }
        }
    }

    interface constant {
        void parse(DataInputStream in) throws Exception;
    }

    class CONSTANT_Class implements constant {
        static byte tag = 0x07;
        short name_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                name_index = in.readShort();

                ConsantPool.l.add(this);
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Class name_index = "
                        + name_index);
            }
        }
    }

    class CONSTANT_Utf8_info implements constant {
        static byte tag = 0x01;
        short length;
        String value;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                length = in.readShort();
                byte[] b = new byte[length];
                in.read(b);
                value = new String(b);

                ConsantPool.consantPool.put(ConsantPool.index, value);// 添加到常量池

                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Utf8_info = " + value);
            }
        }
    }

    class CONSTANT_String_info implements constant {
        static byte tag = 0x08;
        short string_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                string_index = in.readShort();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_String_info string_index = "
                        + string_index);
            }
        }
    }

    class CONSTANT_Fieldref_info implements constant {
        static byte tag = 0x9;
        short class_index;
        short name_and_type_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                class_index = in.readShort();
                name_and_type_index = in.readShort();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Fieldref_info class_index = "
                        + class_index + " name_and_type_index="
                        + name_and_type_index);
            }
        }
    }

    class CONSTANT_Methodref_info implements constant {
        static byte tag = 0x0A;
        short class_index;
        short name_and_type_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                class_index = in.readShort();
                name_and_type_index = in.readShort();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Methodref_info class_index = "
                        + class_index + " name_and_type_index="
                        + name_and_type_index);
            }
        }
    }

    class CONSTANT_InterfaceMethodref_info implements constant {
        static byte tag = 0x0B;
        short class_index;
        short name_and_type_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                class_index = in.readShort();
                name_and_type_index = in.readShort();

                System.out.print(ConsantPool.index++ + " ");
                System.out
                        .println("parse CONSTANT_InterfaceMethodref_info class_index = "
                                + class_index
                                + " name_and_type_index = "
                                + name_and_type_index);
            }
        }
    }

    class CONSTANT_Integer_info implements constant {
        static byte tag = 0x03;
        int bytes;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                bytes = in.readInt();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Integer_info int = " + bytes);
            }
        }
    }

    class CONSTANT_Float_info implements constant {
        static byte tag = 0x04;
        float bytes;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                bytes = in.readFloat();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Float_info float = " + bytes);
            }
        }
    }

    class CONSTANT_Long_info implements constant {
        static byte tag = 0x05;
        long l;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                l = in.readLong();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Long_info long = " + l);
            }
        }
    }

    class CONSTANT_Double_info implements constant {
        static byte tag = 0x06;
        double value;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                value = in.readDouble();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_Double_info double = " + value);
            }
        }
    }

    class CONSTANT_NameAndType_info implements constant {
        static byte tag = 0x0C;
        short name_index;
        short descriptor_index;

        @Override
        public void parse(DataInputStream in) throws Exception {
            if (tag == ConsantPool.tag_main) {
                name_index = in.readShort();
                descriptor_index = in.readShort();
                System.out.print(ConsantPool.index++ + " ");
                System.out.println("parse CONSTANT_NameAndType_info name_index = "
                        + name_index + " descriptor_index = " + descriptor_index);
            }
        }
    }

    具体大家可以google Java ConstantPool就能在第一页看见这个代码,托管在Google Code下面的。

    附注:修改了几个Bug,添加了对所有常量池的支持,另外我贴的别人的代码,貌似和我之前的代码犯一个毛病,就是解析Long和Double时候,没有增加常量池索引。

    All 8-byte constants take up two entries in the constant_pool table of the class file. If a CONSTANT_Long_info or CONSTANT_Double_info structure is the item in the constant_pool table at index n, then the next usable item in the pool is located at index n+2. The constant_pool index n+1 must be valid but is considered unusable.2

  • 相关阅读:
    php 时间问题
    php语言
    高级查询
    数据库的查询详情
    数据库的创建和增删改查,外键和主键的创建
    数据库
    js的基本语句和语法
    JS的脚本语言
    样式、格式布局
    表单的元素和样式表
  • 原文地址:https://www.cnblogs.com/diyunpeng/p/2465486.html
Copyright © 2011-2022 走看看