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

  • 相关阅读:
    Android 编程下 Eclipse 恢复被删除的文件
    Android 编程下背景图片适配工具类
    Android 编程下 Managing Your App's Memory
    Android 编程下代码之(QQ消息列表滑动删除)
    Android 编程下 Canvas and Drawables
    Android 编程下 AlarmManager
    Android 编程下去除 ListView 上下边界蓝色或黄色阴影
    Java 编程下字符串的 16 位、32位 MD5 加密
    C#枚举类型和int类型相互转换
    MVC和普通三层架构的区别
  • 原文地址:https://www.cnblogs.com/diyunpeng/p/2465486.html
Copyright © 2011-2022 走看看