项目地址: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 !!");
}
}
运行命令
结果