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

 

posted @ 2012-04-22 21:25  规格严格-功夫到家  阅读(1157)  评论(3编辑  收藏  举报