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