1. 程式人生 > >《自己動手寫java虛擬機器》學習筆記(六)-----解析class檔案(java)

《自己動手寫java虛擬機器》學習筆記(六)-----解析class檔案(java)

專案地址: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 !!");
    }
}

執行命令

結果

參考資料:https://zachaxy.github.io