《自己动手写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

猜你喜欢

转载自blog.csdn.net/qq_33543634/article/details/84252191