看了JVM虚拟机规范,之前看ClassFile的描述,总是觉得很模糊,这次周末又一次看了一遍,决定写点代码分析一下,Oracle的JDK提供了
javap,参照这个写了简单的分析类,就是为了辅助学习用,因为规范里面都是u类型的,而且是Big-Indian,使用DataInput正好符合。
代码很简单,我也没有按照什么面向对象来分析,就是走一步算一步,写到哪算到哪,很多可能都重复了,望见谅,就是个练手。
看代码的时候,请参照虚拟机规范看,其实这个就是个理论,关键还是JVM的实现以及API的类库,再一次感慨一下。
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上一个国人写的分析常量池的代码,当然是他整体代码的一部分,我觉得写的很好,好在结构很清晰,其实思路都是一样,所以说当思路一样的时候,人的解决方案建模能力能够看出人的水平,我还是建模和模式能力差啊。代码也附上,有兴趣的可以看看,版权不在我,望周知:
具体大家可以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