《自己動手寫java虛擬機器》學習筆記(六)-----解析class檔案(java)
阿新 • • 發佈:2018-11-23
專案地址:https://github.com/gongxianshengjiadexiaohuihui
註釋都寫的很清楚,有一些概念問題,請參考go版本的實現
目錄結構
首先是位元組轉換工具,因為java和go的類庫不同,另外需注意class檔案是大端儲存方式(高位元組放低地址,低位元組放高地址)
package classfile; /** * @ClassName classfile.ByteUtils * @Description TODO * @Author Mr.G * @Date 2018/11/5 15:52 * @Version 1.0 */ public class ByteUtils { public static String bytesToHexString(byte[] src){ return bytesToHexString(src,src.length); } public static String bytesToHexString(byte [] src,int len){ StringBuilder stringBuilder = new StringBuilder(""); if(src == null || len <= 0){ return null; } for(int i = 0; i < len; i++){ /** * jvm有符號位擴充套件機制,會把高24位補1,為了保持二進位制資料一致性,進行下面操作,把搞24位變0,低8位不變,詳細可看我的一篇部落格 */ int v = src[i]&0xFF; String hv = Integer.toHexString(v).toUpperCase(); /** * 一個位元組8位表示最大的16進位制是兩位,但是也有一位的情況,如果是一位,就補0 */ if(hv.length() < 2){ stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } public static int bytesToU16(byte[] data){ assert data.length == 2; /** * 至於為什麼不直接用data[0]<<8|data[1]還是因為符號位擴充套件機制,會把data[0]的前24位擴充套件為1,這樣在移位擴充套件的時候就會出錯,為了避免,我們轉換為補碼和它一樣的正數 */ return (data[0] + 256) % 256 * 256 + (data[1] + 256) % 256; } /** * 這裡之所以不用 轉正數,是因為最後需要的剛好是32位,所以不會影響最終結果 * @param data * @return */ public static int bytesToInt32(byte[] data) { assert data.length == 4; int res = 0; for (int i = 0; i < data.length; i++) { res = res << 8 | (data[i] + 256) % 256; } return res; } public static long bytesToLong64(byte[] data){ assert data.length == 8; long res = 0; for (int i=0; i< data.length; i++){ res = res <<8 | (data[i] +256) % 256; } return res; } public static float bytesToFloat32(byte[] data){ assert data.length == 4; int res = bytesToInt32(data); return Float.intBitsToFloat(res); } public static Double bytesToDouble64(byte[] data){ assert data.length == 8; long res = bytesToLong64(data); return Double.longBitsToDouble(res); } }
至於符號位擴充套件機制可以參考我的一篇部落格https://blog.csdn.net/qq_33543634/article/details/83789227
class的讀取工具類
package classfile; /** * @ClassName ClassReader * @Description TODO * @Author Mr.G * @Date 2018/11/5 15:45 * @Version 1.0 */ public class ClassReader { /** * 位元組碼 */ private byte[] data; /** * 表示當前要讀的位元組陣列索引 */ private int index=0; public ClassReader(byte[] data){ this.data = data; } public byte readUint8(){ byte res = data[index++]; return res; } /** * java中沒有無符號16位的數,用int代替 * @return */ public int readUint16(){ byte[] res = new byte[2]; res[0] = readUint8(); res[1] = readUint8(); return ByteUtils.bytesToU16(res); } /** * 根據自己的需求,通過ByteUtils轉換 * 讀取n位元組 * @return */ public byte[] readBytes(int n){ byte[] res = new byte[n]; for(int i = 0; i < n; i++){ res[i] = readUint8(); } return res; } /** * 讀取一個無符號16位的表,表的第一個數是表的大小 * @return */ public int[] readUint16s(){ int n = readUint16(); int[] res = new int[n]; for(int i = 0; i < n ;i++){ res[i] =readUint16(); } return res; } }
接著是class的檔案類
package classfile; import classfile.attribute.SourceFileAttribute; /** * @ClassName ClassFile * @Description TODO * @Author Mr.G * @Date 2018/11/15 20:21 * @Version 1.0 */ public class ClassFile { /** * 小版本號 */ private int minorVersion; /** * 主版本號 */ private int majorVersion; /** * 常量池 */ public ConstantPool constantPool; /** * 類訪問標誌 */ private int accessFlags; /** * 類名的常量池索引 */ private int thisClass; /** * 超類名的常量池索引 */ private int superClass; /** * 介面索引表,存放的也是常量池索引 */ private int[] interfaces; /** * 欄位表 */ private MemberInfo[] fields; /** * 方法表 */ private MemberInfo[] methods; /** * 屬性表 */ private AttributeInfo[] attributes; public ClassFile(byte[] classData){ ClassReader reader = new ClassReader(classData); read(reader); } private void read(ClassReader reader) { readAndCheckMagic(reader); readAndCheckVersion(reader); constantPool = new ConstantPool(reader); accessFlags = reader.readUint16(); thisClass = reader.readUint16(); superClass = reader.readUint16(); interfaces = reader.readUint16s(); fields = MemberInfo.readMembers(reader,constantPool); methods = MemberInfo.readMembers(reader,constantPool); attributes = AttributeInfo.readAttributes(reader,constantPool); } /** * 檢查魔術,也就是檔案格式,CAFEBABE代表的是class檔案 * @param reader */ private void readAndCheckMagic(ClassReader reader){ String magic = ByteUtils.bytesToHexString(reader.readBytes(4)); if(!magic.equals("CAFEBABE")){ throw new RuntimeException("java.lang.ClassFormatError:magic"); } } /** * 小版本號在j2se1.2以前用過,主版本號在j2se之前都是45,從1.2開始,每次有大的java版本釋出,都會加1,我們參考的是jdk8,支援45.0-52.0的classw檔案 * @param reader */ private void readAndCheckVersion(ClassReader reader){ minorVersion = reader.readUint16(); majorVersion = reader.readUint16(); if(majorVersion == 45){ return ; } if(minorVersion == 0 && majorVersion >= 46 && majorVersion <= 52){ return ; } throw new RuntimeException("java.lang.UnsupportedCLassVersionError"); } public int getMinorVersion() { return minorVersion; } public int getMajorVersion() { return majorVersion; } public ConstantPool getConstantPool() { return constantPool; } public int getAccessFlags() { return accessFlags; } public int getThisClass() { return thisClass; } public int getSuperClass() { return superClass; } public int[] getInterfaces() { return interfaces; } public MemberInfo[] getFields() { return fields; } public MemberInfo[] getMethods() { return methods; } public AttributeInfo[] getAttributes() { return attributes; } public String getClassName(){ return constantPool.getClassName(thisClass); } public String getSuperClassName(){ return constantPool.getClassName(superClass); } public String[] getInterfaceNames(){ String[] interfaceNames = new String[interfaces.length]; for(int i=0; i < interfaceNames.length; i++){ interfaceNames[i] = constantPool.getClassName(interfaces[i]); } return interfaceNames; } public String getSourceFile(){ for(AttributeInfo info : attributes){ if(info instanceof SourceFileAttribute){ return ((SourceFileAttribute)info).getFileName(); } } return "unknown"; } }
常量池是jvm很重要的部分,存放了class檔案大部分的資訊
常量池檔案類
package classfile;
import classfile.class_constant.*;
/**
* @ClassName ConstantPool
* @Description 常量池實際上是一個表 ,這裡用陣列來實現,所以常量池這個類中持有一個常量資料,陣列的每一項根據讀取到的tag進行初始化下·
* @Author Mr.G
* @Date 2018/11/16 9:40
* @Version 1.0
*/
public class ConstantPool {
/**
* 儲存常量池中的所有常量,常量分好多型別(tag標籤區分)
*/
ConstantInfo[] infos;
public ConstantInfo[] getInfos() {
return infos;
}
/**
* 常量池中的常量數量
*/
private int constantPoolCount;
/**
* 表頭給出的常量池大小n比實際大1,有效的常量池索引是 1-n-1,0是無效索引,CONSTANT_Long_info和CONSTANT_Double_info各佔兩個位置,而且1-n-1,中的某些數也會變成無效索引
*/
private int realConstantPoolCount;
public ConstantPool( ClassReader reader){
constantPoolCount = reader.readUint16();
infos = new ConstantInfo[constantPoolCount];
for(int i = 1; i < constantPoolCount; i++ ){
infos[i] = ConstantInfo.readConstantInfo(reader,this);
realConstantPoolCount++;
if((infos[i] instanceof ConstantLongInfo)||(infos[i] instanceof ConstantDoubleInfo)){
i++;
}
}
}
/**
* 按照索引查詢常量
* @param index
* @return
*/
private ConstantInfo getConstantInfo(int index){
if(0 < index && index < constantPoolCount){
ConstantInfo info =infos[index];
if(info != null){
return info;
}
}
throw new NullPointerException("Invalid constant pool index!");
}
/**
* 欄位或方法的名字
* @param index
* @return
*/
public String getName(int index){
ConstantNameAndTypeInfo info =(ConstantNameAndTypeInfo) getConstantInfo(index);
return getUtf8(info.nameIndex);
}
/**
* 欄位或方法的描述符
* @param index
* @return
*/
public String getType(int index){
ConstantNameAndTypeInfo info =(ConstantNameAndTypeInfo) getConstantInfo(index);
return getUtf8(info.descriptorIndex);
}
public String[] getNameAndType(int index){
String[] str = new String[2];
ConstantNameAndTypeInfo info = (ConstantNameAndTypeInfo) getConstantInfo(index);
str[0] = getUtf8(info.nameIndex);
str[1] = getUtf8(info.descriptorIndex);
return str;
}
public String getClassName(int index){
ConstantClassInfo info = (ConstantClassInfo)getConstantInfo(index);
return getUtf8(info.nameIndex);
}
/**
* 讀取字串常量的值,直接強制轉換位ConstantUtf8Info,然後返回val值
* @param index
* @return
*/
public String getUtf8(int index){
return ((ConstantUtf8Info)getConstantInfo(index)).val;
}
public int getConstantPoolCount(){
return constantPoolCount;
}
}
欄位表和方法表共用此表
package classfile;
import classfile.attribute.*;
/**
* @ClassName MemberInfo
* @Description 欄位表和方法表共用該類,裡面包含的是類中所定義的成員變數/方法,欄位/方法中可能還有屬性
* @Author Mr.G
* @Date 2018/11/17 10:42
* @Version 1.0
*/
public class MemberInfo {
ConstantPool constantPool;
int accessFlags;
int nameIndex;
int descriptorIndex;
AttributeInfo[] attributeInfos;
public MemberInfo(ClassReader reader, ConstantPool constantPool){
this.constantPool = constantPool;
accessFlags = reader.readUint16();
nameIndex = reader.readUint16();
descriptorIndex = reader.readUint16();
attributeInfos = AttributeInfo.readAttributes(reader,constantPool);
}
public static MemberInfo[] readMembers(ClassReader reader, ConstantPool constantPool){
int memberCount = reader.readUint16();
MemberInfo[] memberInfos = new MemberInfo[memberCount];
for(int i = 0; i < memberCount; i++){
memberInfos[i] = new MemberInfo(reader, constantPool);
}
return memberInfos;
}
public int getAccessFlags() {
return accessFlags;
}
public int getNameIndex() {
return nameIndex;
}
public int getDescriptorIndex() {
return descriptorIndex;
}
public CodeAttribute getCodeAttribute(){
for(AttributeInfo info: attributeInfos){
if (info instanceof CodeAttribute){
return (CodeAttribute)info;
}
}
return null;
}
public ConstantValueAttribute getConstantValueAttribute() {
for (AttributeInfo info : attributeInfos) {
if (info instanceof ConstantValueAttribute) {
return (ConstantValueAttribute) info;
}
}
return null;
}
public ExceptionsAttribute getExceptionsAttribute() {
for (int i = 0; i < attributeInfos.length; i++) {
if (attributeInfos[i] instanceof ExceptionsAttribute) {
return (ExceptionsAttribute) attributeInfos[i];
}
}
return null;
}
}
常量池的組成單元的檔案類,常量池是由一個個常量組成,每個常量都是一個檔案,抽象出一個檔案類,讓各種常量去繼承
package classfile;
import classfile.class_constant.*;
/**
* @ClassName ConstantInfo
* @Description
* @Author Mr.G
* @Date 2018/11/16 10:21
* @Version 1.0
*/
public abstract class ConstantInfo {
public static final int CONSTANT_Utf8 = 1;
public static final int CONSTANT_Integer = 3;
public static final int CONSTANT_Float = 4;
public static final int CONSTANT_Long = 5;
public static final int CONSTANT_Double = 6;
public static final int CONSTANT_Class = 7;
public static final int CONSTANT_String = 8;
public static final int CONSTANT_Fieldref = 9;
public static final int CONSTANT_Methodref = 10;
public static final int CONSTANT_InterfaceMethodref = 11;
public static final int CONSTANT_NameAndType = 12;
public static final int CONSTANT_MethodHandle = 15;
public static final int CONSTANT_MethodType = 16;
public static final int CONSTANT_InvokeDynamic = 18;
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
* @param reader
*/
public abstract void readInfo(ClassReader reader);
/**
* 常量型別tag,判斷是上述常量的哪一種
*/
protected int tag;
public int getTag(){
return tag;
}
public static ConstantInfo readConstantInfo(ClassReader reader,ConstantPool constantPool){
int tag = (reader.readUint8() + 256) % 256;
ConstantInfo info = create(tag ,constantPool);
info.readInfo(reader);
return info;
}
private static ConstantInfo create(int tag, ConstantPool constantPool){
switch (tag){
case CONSTANT_Utf8:
return new ConstantUtf8Info();
case CONSTANT_Integer:
return new ConstantIntegerInfo();
case CONSTANT_Float:
return new ConstantFloatInfo();
case CONSTANT_Long:
return new ConstantLongInfo();
case CONSTANT_Double:
return new ConstantDoubleInfo();
case CONSTANT_String:
return new ConstantStringInfo(constantPool);
case CONSTANT_Class:
return new ConstantClassInfo(constantPool);
case CONSTANT_Fieldref:
return new ConstantFieldRefInfo(constantPool);
case CONSTANT_Methodref:
return new ConstantMethodRefInfo(constantPool);
case CONSTANT_InterfaceMethodref:
return new ConstantInterfaceMethodRefInfo(constantPool);
case CONSTANT_NameAndType:
return new ConstantNameAndTypeInfo();
case CONSTANT_MethodType:
return new ConstantMethodTypeInfo();
case CONSTANT_MethodHandle:
return new ConstantMethodHandleInfo();
case CONSTANT_InvokeDynamic:
return new ConstantInvokeDynamicInfo();
default:
throw new RuntimeException("java.lang.ClassFormatError : constantPool tag not found !");
}
}
}
接著是各種繼承了ConstantInfo類的的常量
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
import classfile.ConstantPool;
/**
* @ClassName ConstantClassInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 14:15
* @Version 1.0
*/
public class ConstantClassInfo extends ConstantInfo {
ConstantPool constantPool;
public int nameIndex;
public ConstantClassInfo(ConstantPool constantPool){
this.constantPool = constantPool;
tag = 7;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
nameIndex = reader.readUint16();
}
public String getName(){
return constantPool.getUtf8(nameIndex);
}
}
package classfile.class_constant;
import classfile.ByteUtils;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantDoubleInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 11:41
* @Version 1.0
*/
public class ConstantDoubleInfo extends ConstantInfo {
double val;
public ConstantDoubleInfo(){
tag = 6;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
byte[] data = reader.readBytes(8);
val = ByteUtils.bytesToDouble64(data);
}
public double getVal(){
return val;
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
import classfile.ConstantPool;
/**
* @ClassName ConstantFieldrefInfo
* @Description 欄位符號引用
* @Author Mr.G
* @Date 2018/11/16 14:23
* @Version 1.0
*/
public class ConstantFieldRefInfo extends ConstantMemberInfo {
public ConstantFieldRefInfo(ConstantPool constantPool) {
super(constantPool, 9);
}
}
package classfile.class_constant;
import classfile.ByteUtils;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantFloatInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 11:33
* @Version 1.0
*/
public class ConstantFloatInfo extends ConstantInfo {
float val;
public ConstantFloatInfo(){
tag = 4;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
byte[] data = reader.readBytes(4);
val = ByteUtils.bytesToFloat32(data);
}
public float getVal(){
return val;
}
}
package classfile.class_constant;
import classfile.ByteUtils;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantIntegerInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 11:23
* @Version 1.0
*/
public class ConstantIntegerInfo extends ConstantInfo {
int val;
public ConstantIntegerInfo(){
tag = 3;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
byte[] data = reader.readBytes(4);
val = ByteUtils.bytesToInt32(data);
}
public int getVal(){
return val;
}
}
package classfile.class_constant;
import classfile.ConstantPool;
/**
* @ClassName ConstantInterfaceMethodRefInfo
* @Description 介面方法符號引用
* @Author Mr.G
* @Date 2018/11/16 14:41
* @Version 1.0
*/
public class ConstantInterfaceMethodRefInfo extends ConstantMemberInfo {
public ConstantInterfaceMethodRefInfo(ConstantPool constantPool) {
super(constantPool, 11);
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantInvokeDynamicInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 15:07
* @Version 1.0
*/
public class ConstantInvokeDynamicInfo extends ConstantInfo {
int bootstrapMethodAttrIndex;
int nameAndTypeIndex;
public ConstantInvokeDynamicInfo() {
tag = 18;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
bootstrapMethodAttrIndex = reader.readUint16();
nameAndTypeIndex = reader.readUint16();
}
}
package classfile.class_constant;
import classfile.ByteUtils;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantLongInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 11:36
* @Version 1.0
*/
public class ConstantLongInfo extends ConstantInfo {
long val;
public ConstantLongInfo(){
tag = 5;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
byte[] data =reader.readBytes(8);
val = ByteUtils.bytesToLong64(data);
}
public long getVal(){
return val;
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
import classfile.ConstantPool;
/**
* @ClassName ConstantMemberInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 14:32
* @Version 1.0
*/
public class ConstantMemberInfo extends ConstantInfo {
ConstantPool constantPool;
int classIndex;
int nameAndTypeIndex;
public ConstantMemberInfo(ConstantPool constantPool, int tag){
this.constantPool = constantPool;
this.tag = tag;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
classIndex = reader.readUint16();
nameAndTypeIndex = reader.readUint16();
}
public String getClassName(){
return constantPool.getClassName(classIndex);
}
public String[] getNameAndDescriptor(){
return constantPool.getNameAndType(nameAndTypeIndex);
}
public String getName(){
return constantPool.getName(nameAndTypeIndex);
}
public String getType(){
return constantPool.getType(nameAndTypeIndex);
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantMethodHandleInfo
* @Description java7的屬性
* @Author Mr.G
* @Date 2018/11/16 14:57
* @Version 1.0
*/
public class ConstantMethodHandleInfo extends ConstantInfo {
private byte referenceKind;
private int referenceIndex;
public ConstantMethodHandleInfo() {
tag = 15;
}
public int getReferenceKind() {
return (referenceKind + 256) % 256;
}
public int getReferenceIndex() {
return referenceIndex;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
referenceKind = reader.readUint8();
referenceIndex = reader.readUint16();
}
}
package classfile.class_constant;
import classfile.ConstantPool;
/**
* @ClassName ConstantMethodRefInfo
* @Description 普通方法符號引用
* @Author Mr.G
* @Date 2018/11/16 14:39
* @Version 1.0
*/
public class ConstantMethodRefInfo extends ConstantMemberInfo {
public ConstantMethodRefInfo(ConstantPool constantPool) {
super(constantPool, 10);
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantMethodTypeInfo
* @Description java7的屬性
* @Author Mr.G
* @Date 2018/11/16 14:55
* @Version 1.0
*/
public class ConstantMethodTypeInfo extends ConstantInfo {
private int decriptorIndex;
public ConstantMethodTypeInfo(){
tag = 16;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
decriptorIndex = reader.readUint16();
}
public int getDecriptorIndex(){
return decriptorIndex;
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantNameAndTypeInfo
* @Description 描述符,描述方法的引數型別,詳情見go版本
* @Author Mr.G
* @Date 2018/11/16 14:42
* @Version 1.0
*/
public class ConstantNameAndTypeInfo extends ConstantInfo {
public int nameIndex;
public int descriptorIndex;
public ConstantNameAndTypeInfo(){
tag = 12;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
nameIndex = reader.readUint16();
descriptorIndex = reader.readUint16();
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
import classfile.ConstantPool;
/**
* @ClassName ConstantStringInfo
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 14:11
* @Version 1.0
*/
public class ConstantStringInfo extends ConstantInfo {
ConstantPool constantPool;
int stringIndex;
public ConstantStringInfo(ConstantPool constantPool){
this.constantPool = constantPool;
tag = 8;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
stringIndex = reader.readUint16();
}
public String getString(){
return constantPool.getUtf8(stringIndex);
}
}
package classfile.class_constant;
import classfile.ClassReader;
import classfile.ConstantInfo;
/**
* @ClassName ConstantUtf8Info
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 11:48
* @Version 1.0
*/
public class ConstantUtf8Info extends ConstantInfo {
public String val;
public ConstantUtf8Info(){
tag = 1;
}
/**
* 讀取資訊,每種常量所佔的大小不同,需要各自去具體實現
*
* @param reader
*/
@Override
public void readInfo(ClassReader reader) {
int len = reader.readUint16();
byte[] data = reader.readBytes(len);
val = decodeMUTF8(data);
}
/**
* 如果字串中不包含null字元或補充字元,下面的函式也是能正常工作的
* @param data
* @return
*/
private String decodeMUTF8(byte[] data) {
return new String(data);
}
}
常量池的工作已經做完,接下來是屬性表,屬性同樣有很多,定義一個抽象類讓各種屬性去繼承
package classfile;
import classfile.attribute.*;
/**
* @ClassName AttributeInfo
* @Description 屬性表和常量池不同,常量是以tag區分,而屬性是通過屬性名區分,所以屬性可以自定義
* @Author Mr.G
* @Date 2018/11/16 15:59
* @Version 1.0
*/
public abstract class AttributeInfo {
public abstract void readInfo(ClassReader reader);
/**
* 讀取單個屬性
* @param reader
* @param constantPool
* @return
*/
private static AttributeInfo readAttribute(ClassReader reader, ConstantPool constantPool){
int attrNameIndex = reader.readUint16();
String attrName = constantPool.getUtf8(attrNameIndex);
int attrLen = ByteUtils.bytesToInt32(reader.readBytes(4));
AttributeInfo attrInfo = create(attrName, attrLen, constantPool);
attrInfo.readInfo(reader);
return attrInfo;
}
/**
* 讀取屬性表
* @param reader
* @param constantPool
* @return
*/
public static AttributeInfo[] readAttributes(ClassReader reader, ConstantPool constantPool){
int attributesCount = reader.readUint16();
AttributeInfo[] attributes = new AttributeInfo[attributesCount];
for(int i =0; i < attributesCount; i++){
attributes[i] = readAttribute(reader, constantPool);
}
return attributes;
}
private static AttributeInfo create(String attrName, int attrLen, ConstantPool constantPool) {
if ("Code".equals(attrName)) {
return new CodeAttribute(constantPool);
} else if ("ConstantValue".equals(attrName)) {
return new ConstantValueAttribute();
} else if ("DeprecatedAttribute".equals(attrName)) {
return new DeprecatedAttribute();
} else if ("Exceptions".equals(attrName)) {
return new ExceptionsAttribute();
} else if ("LineNumberTable".equals(attrName)) {
return new LineNumberTableAttribute();
} else if ("LocalVariableTable".equals(attrName)) {
return new LocalVariableTableAttribute();
} else if ("SourceFile".equals(attrName)) {
return new SourceFileAttribute(constantPool);
} else if ("Synthetic".equals(attrName)) {
return new SyntheticAttribute();
} else {
return new UnparsedAttribute(attrName, attrLen);
}
}
}
各種屬性的實現
package classfile.attribute;
import classfile.*;
/**
* @ClassName CodeAttribute
* @Description TODO
* @Author Mr.G
* @Date 2018/11/16 16:36
* @Version 1.0
*/
public class CodeAttribute extends AttributeInfo {
ConstantPool constantPool;
/**
* 運算元棧的最大深度
*/
public int maxStack;
/**
* 區域性變量表的大小
*/
public int maxLocals;
/**
* 位元組碼
*/
byte[] code;
/**
* 異常表
*/
ExceptionTableEntry[] exceptionTable;
/**
* 屬性表
*/
AttributeInfo[] attributes;
public CodeAttribute(ConstantPool constantPool){
this.constantPool = constantPool;
}
@Override
public void readInfo(ClassReader reader) {
maxStack = reader.readUint16();
maxLocals = reader.readUint16();
int codeLength = ByteUtils.bytesToInt32(reader.readBytes(4));
code = reader.readBytes(codeLength);
exceptionTable = readExceptionTable(reader);
attributes = readAttributes(reader, constantPool);
}
private ExceptionTableEntry[] readExceptionTable(ClassReader reader){
int exceptionTableLength = reader.readUint16();
ExceptionTableEntry[] exceptionTable = new ExceptionTableEntry[exceptionTableLength];
for(int i = 0; i < exceptionTableLength; i++){
exceptionTable[i] = new ExceptionTableEntry(reader);
}
return exceptionTable;
}
public LineNumberTableAttribute lineNumberTableAttribute(){
for(int i = 0; i < attributes.length; i++){
if(attributes[i] instanceof LineNumberTableAttribute){
return (LineNumberTableAttribute)attributes[i];
}
}
return null;
}
public static class ExceptionTableEntry{
/**
* 被try_catch包裹程式碼塊的起始位元組碼(包括)
*/
int startPc;
/**
* 被try_catch包裹程式碼塊的終止位元組碼(不包括)
*/
int endPc;
/**
* catch的起始位置
*/
int handlerPc;
/**
* 指向常量池的一個索引,解析後可以得到一個異常類
*/
int catchType;
public ExceptionTableEntry(ClassReader reader){
this.startPc = reader.readUint16();
this.endPc = reader.readUint16();
this.handlerPc = reader.readUint16();
this.catchType = reader.readUint16();
}
public int getStartPc() {
return startPc;
}
public int getEndPc() {
return endPc;
}
public int getHandlerPc() {
return handlerPc;
}
public int getCatchType() {
return catchType;
}
}
public int getMaxStack() {
return maxStack;
}
public int getMaxLocals() {
return maxLocals;
}
public byte[] getCode() {
return code;
}
public ExceptionTableEntry[] getExceptionTable() {
return exceptionTable;
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName ConstantValueAttribute
* @Description 通知jvm自動為靜態變數賦值,只有被static關鍵字修飾的變數才有這個屬性
* @Author Mr.G
* @Date 2018/11/17 9:39
* @Version 1.0
*/
public class ConstantValueAttribute extends AttributeInfo {
int constantValueIndex;
@Override
public void readInfo(ClassReader reader) {
constantValueIndex = reader.readUint16();
}
public int getConstantValueIndex() {
return constantValueIndex;
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName DeprecatedAttribute
* @Description 僅起標記作用,不包含任何資料。是JDK1.1引入的,可以出現在 ClassFile、field_info和method_info結構中,屬於布林屬性,僅區別存在和不存在
* @Author Mr.G
* @Date 2018/11/17 9:43
* @Version 1.0
*/
public class DeprecatedAttribute extends AttributeInfo {
@Override
public void readInfo(ClassReader reader) {
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName ExceptionsAttribute
* @Description 記錄方法丟擲的異常表
* @Author Mr.G
* @Date 2018/11/17 9:45
* @Version 1.0
*/
public class ExceptionsAttribute extends AttributeInfo {
int[] exceptionIndexTable;
@Override
public void readInfo(ClassReader reader) {
exceptionIndexTable = reader.readUint16s();
}
public int[] getExceptionIndexTable() {
return exceptionIndexTable;
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName LineNumberTableAttribute
* @Description LineNumberTable屬性表存放方法的行號資訊,和SourceFile屬性都屬於除錯資訊,都不是執行時必需
* @Author Mr.G
* @Date 2018/11/17 9:47
* @Version 1.0
*/
public class LineNumberTableAttribute extends AttributeInfo {
LineNumberTableEntry[] lineNumberTable;
@Override
public void readInfo(ClassReader reader) {
int lineNumberTableLength = reader.readUint16();
this.lineNumberTable =new LineNumberTableEntry[lineNumberTableLength];
for(int i = 0; i < lineNumberTableLength; i++){
lineNumberTable[i] = new LineNumberTableEntry(reader.readUint16(),reader.readUint16());
}
}
/**
* 可以確保的是位元組碼中的行號遞增的,而對應的原始碼中的行號並不是
* @param pc
* @return
*/
public int getLineNumber(int pc){
for(int i = lineNumberTable.length - 1; i >= 0; i--){
LineNumberTableEntry entry = lineNumberTable[i];
if(pc >= entry.startPc){
return entry.lineNumber;
}
}
return -1;
}
public static class LineNumberTableEntry{
/**
* 位元組碼行號
*/
int startPc;
/**
* Java原始碼行號,二者對應
*/
int lineNumber;
public LineNumberTableEntry(int startPc, int lineNumber){
this.startPc = startPc;
this.lineNumber = lineNumber;
}
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName LocalVariableTableAttribute
* @Description 用於描述棧幀中區域性變量表中的變數和Java原始碼中定義的變數之間的關係。
* 這並不是執行時必須的屬性,但預設會生成到Class檔案中,可以在Javac 中使用 -g:none 來取消這項資訊;
* 如果不生成這項,產生的影響是:當其他人引用這個方法時,IDE將會使用諸如arg0,arg1之類的佔位符代替原來的引數名,但對執行毫無影響。
* 只是在除錯期間無法根據引數名從上下文中獲得引數值
* @Author Mr.G
* @Date 2018/11/17 10:15
* @Version 1.0
*/
public class LocalVariableTableAttribute extends AttributeInfo {
LocalVariableTableEntry[] localVariableTable;
@Override
public void readInfo(ClassReader reader) {
int localVariableTableLength = reader.readUint16();
this.localVariableTable = new LocalVariableTableEntry[localVariableTableLength];
for(int i = 0; i < localVariableTableLength; i++){
localVariableTable[i] = new LocalVariableTableEntry(
reader.readUint16(),
reader.readUint16(),
reader.readUint16(),
reader.readUint16(),
reader.readUint16()
);
}
}
public static class LocalVariableTableEntry{
/**
* 代表區域性變數的生命週期開始的位元組碼偏移量
*/
int startPc;
/**
* 代表區域性變數的作用範圍覆蓋的長度
*/
int length;
/**
* 區域性變數的名稱在常量池中的索引
*/
int nameIndex;
/**
* 變數描述符
*/
int descriptorIndex;
/**
* 該區域性變數在棧幀區域性變數包中的位置
*/
int index;
public LocalVariableTableEntry(int startPc, int length, int nameIndex, int descriptorIndex, int index){
this.startPc = startPc;
this.length = length;
this.nameIndex = nameIndex;
this.descriptorIndex =descriptorIndex;
this.index = index;
}
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
import classfile.ConstantPool;
/**
* @ClassName SourceFileAttribute
* @Description 用於指出原始檔名name
* @Author Mr.G
* @Date 2018/11/17 10:30
* @Version 1.0
*/
public class SourceFileAttribute extends AttributeInfo {
int sourceFileIndex;
ConstantPool constantPool;
public SourceFileAttribute(ConstantPool constantPool){
this.constantPool = constantPool;
}
@Override
public void readInfo(ClassReader reader) {
sourceFileIndex = reader.readUint16();
}
public String getFileName(){
return constantPool.getUtf8(sourceFileIndex);
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName SyntheticAttribute
* @Description 僅起標記作用,不包含任何資料。是JDK1.1引入的,可以出現在 ClassFile、field_info和method_info結構中
* 代表詞欄位或方法並不是由Java原始碼生成的,而是由編譯器自行新增的
* @Author Mr.G
* @Date 2018/11/17 10:39
* @Version 1.0
*/
public class SyntheticAttribute extends AttributeInfo {
@Override
public void readInfo(ClassReader reader) {
}
}
package classfile.attribute;
import classfile.AttributeInfo;
import classfile.ClassReader;
/**
* @ClassName UnparsedAttribute
* @Description 未定義的屬性,跳過
* @Author Mr.G
* @Date 2018/11/17 10:40
* @Version 1.0
*/
public class UnparsedAttribute extends AttributeInfo {
private String attrName;
private int attrLen;
private byte[] info;
public UnparsedAttribute(String attrName, int attrLen) {
this.attrName = attrName;
this.attrLen = attrLen;
}
@Override
public void readInfo(ClassReader reader) {
info = reader.readBytes(attrLen);
}
}
屬性表的工作也已經完成,最後就是修改我們的main函式
import classfile.ClassFile;
import classpath.ClassPath;
import java.util.Arrays;
/**
* @ClassName Main
* @Description TODO
* @Author Mr.G
* @Date 2018/10/9 10:43
* @Version 1.0
*/
public class Main {
public static void main(String[] args){
Cmd cmd=new Cmd(args);
if(!cmd.isRightFmt||cmd.helpFlag){
cmd.printUsage();
}else if(cmd.versionFlag){
System.out.println("version 0.0.1");
}else{
startJVM(cmd);
}
}
public static void startJVM(Cmd cmd){
ClassPath cp = new ClassPath(cmd.getXjreOption(),cmd.getCpOption());
System.out.println("classpath:"+cp.printAbsPath()+" class:"+cmd.getClazz()+" args:"+cmd.args);
ClassFile classFile = loadClass(cmd.getClazz(),cp);
printClassInfo(classFile);
}
private static void printClassInfo(ClassFile classFile) {
System.out.println("version:" + classFile.getMajorVersion() + "." + classFile.getMinorVersion());
System.out.println("constants count:" + classFile.getConstantPool().getConstantPoolCount());
System.out.println("access flags:" + classFile.getAccessFlags());
System.out.println("this class:" + classFile.getClassName());
System.out.println("super class" + classFile.getSuperClassName());
System.out.println("interfaces:" + classFile.getInterfaceNames());
System.out.println("fields count:" + classFile.getFields().length);
System.out.println(classFile.getFields());
System.out.println("Methods count:" + classFile.getMethods());
}
private static ClassFile loadClass(String clazz, ClassPath cp) {
try {
byte[] data = cp.readClass(clazz);
return new ClassFile(data);
}catch (Exception e){
e.printStackTrace();
}
throw new RuntimeException("Read class fail !!");
}
}
執行命令
結果