专业的JAVA编程教程与资源

网站首页 > java教程 正文

用go编写java虚拟机(三)class文件读取到内存

temp10 2024-09-16 05:24:37 java教程 13 ℃ 0 评论

一、概要

上篇我们定义了class文件的结构体,这节我们主要实现把class文件读取到内存,注意我们的目的很简单,就是读取字节流解析class文。不会现在处理一些过多的内容。

在class.go文件定义ClassBytes结构体

type ClassBytes struct {
	Bytes []byte
	Class *Class
}

Bytes是当前读取class文件的字节,Class是需要解析的Class结构体的指针。

用go编写java虚拟机(三)class文件读取到内存

二、调整结构体

上节我们定义的结构体做一些简单调整

type Class struct {
	Magic             uint32             `json:"magic"`               // 魔数
	MinorVersionMin   uint16             `json:"minor_version_min"`   //    副版本号
	MinorVersion      uint16             `json:"minor_version"`       //主版本号
	ConstantPoolCount uint16             `json:"constant_pool_count"` //常量池计数器
	ConstantPoolInfo  []ConstantPoolInfo `json:"constant_pool_info"`  // 常量池
	AccessFlags       uint16             `json:"access_flags"`        //访问标志
	ThisClass         uint16             `json:"this_class"`          //类索引,
	SuperClass        uint16             `json:"super_class"`         //父类索引,
	InterfaceCount    uint16             `json:"interface_count"`     //接口计数器
	interfaces        []interface{}      `json:"interfaces"`          //接口表
	FieldsCount       uint16             `json:"fields_count"`        //字段计数器
	FieldInfo         []interface{}      `json:"field_info"`          //字段表
	MethodCount       uint16             `json:"method_count"`        //方法计数器
	MethodInfo        []*Method          `json:"method_info"`         //方法表
	AttributesCount   uint16             `json:"attributes_count"`    //属性计数器
	AttributeInfo     []Attribute        `json:"attribute_info"`
}

我们把[]interface{} 类型能确定的替换成了具体的类型,并且加入了json标签,用于打印一下class的内容,另外由于篇幅的原因,此处不粘贴其它结构体的代码了,我会在文章结尾添加当天的代码下载地址,也会在本篇绑定资源下载,读者可以自行下载代码查阅,这里只粘贴Class

三、解析class

1、在class.go文件定义 AnalysisClass方法

` 提示:请读者理解go语言的方法和函数的区别,方法有接收者调用的时候需要变量.func()这样子来调用,类比java可以把方法看成是对象方法,函数看成是静态方法

func (C *ClassBytes) AnalysisClass() {
	class := C.Class
	//解析魔数
	class.Magic = binary.BigEndian.Uint32(C.Bytes[:4])
	//小版本号
	class.MinorVersionMin = binary.BigEndian.Uint16(C.Bytes[4:6])
	//大版本号
	class.MinorVersionMin = binary.BigEndian.Uint16(C.Bytes[6:8])
	//读取常量池

	class.ConstantPoolCount = binary.BigEndian.Uint16(C.Bytes[8:10])
	//去除已经读过的字节
	C.Bytes = C.Bytes[10:]
	if class.ConstantPoolCount != 0 {
		//调用常量池读取方法
		pool := C.AnalysisClassReadeConstantPool(int(class.ConstantPoolCount))
		class.ConstantPoolInfo = pool
	}

	//访问控制
	class.AccessFlags = binary.BigEndian.Uint16(C.Bytes[:2])
	//类索引
	class.ThisClass = binary.BigEndian.Uint16(C.Bytes[2:4])
	//父类
	class.SuperClass = binary.BigEndian.Uint16(C.Bytes[4:6])
	//接口计数器
	class.InterfaceCount = binary.BigEndian.Uint16(C.Bytes[6:8])
	//先去除读过的字节
	C.Bytes = C.Bytes[8:]
	//判断是否用接口
	if class.InterfaceCount != 0 {
		//解析接口
	}
	//字段计数器
	class.FieldsCount = binary.BigEndian.Uint16(C.Bytes[:2])
	//先去除读过的字节
	C.Bytes = C.Bytes[2:]
	//判断是否有字段
	if class.InterfaceCount != 0 {
		//解析字段
	}
	//方法计数器
	class.MethodCount = binary.BigEndian.Uint16(C.Bytes[:2])
	//先去除读过的字节
	C.Bytes = C.Bytes[2:]
	//判断是否有方法
	if class.MethodCount != 0 {
		//解析方法
		method := C.AnalysisClassReadeMethod(int(class.MethodCount))
		class.MethodInfo = method
	}
	//属性计数器
	class.AttributesCount = binary.BigEndian.Uint16(C.Bytes[:2])
	//先去除读过的字节
	C.Bytes = C.Bytes[2:]
	//判断是否有属性
	if class.AttributesCount != 0 {
		//属性解析
		attributes := C.AnalysisClassReadeAttributes(int(class.AttributesCount), class)
		class.AttributeInfo = attributes
	}
}

这里不再赘述,相信我的注释很清晰。

2、在class.go文件定义 AnalysisClassReadeAttributes方法

func (C *ClassBytes) AnalysisClassReadeAttributes(count int, class *Class) []Attribute {
	var attribute []Attribute = make([]Attribute, count)
	for i := 0; i < count; i++ {
		//2字节名称
		nameIndex := binary.BigEndian.Uint16(C.Bytes[:2])
		info := class.ConstantPoolInfo[nameIndex]
		//强转,接口转成结构体
		utf8Info := info.(*ConstantUtf8Info)
		//取出string
		s := string(utf8Info.Bytes)
		length := binary.BigEndian.Uint32(C.Bytes[2:6])
		//取属性信息
		bytes := C.Bytes[6 : 6+length]
		C.Bytes = C.Bytes[6+length:]
		switch s {
		case constan.CODE:
			//j解析code
			codeAttribute := CodeAttribute{}
			codeAttribute.AttributeNameIndex = nameIndex
			codeAttribute.AttributeLength = length

			//栈深
			codeAttribute.MaxStack = binary.BigEndian.Uint16(bytes[:2])
			//空间
			codeAttribute.MaxLocals = binary.BigEndian.Uint16(bytes[2:4])
			//指令长度
			codeAttribute.CodeLengthInfo = binary.BigEndian.Uint32(bytes[4:8])
			//指令
			codeAttribute.CodeInfo = bytes[8 : 8+codeAttribute.CodeLengthInfo]
			//异常
			bytes = bytes[8+codeAttribute.CodeLengthInfo:]
			codeAttribute.ExceptionTableLength = binary.BigEndian.Uint16(bytes[:2])
			//先去除读过的字节
			bytes = bytes[2:]
			//判断是否有异常
			if codeAttribute.ExceptionTableLength != 0 {
				//异常
			}
			//其它属性
			codeAttribute.AttributesCountInfo = binary.BigEndian.Uint16(bytes[:2])
			//先去除读过的字节
			bytes = bytes[2:]
			//判断是否有属性
			if codeAttribute.AttributeLength != 0 {
				//其它属性
				//递归
				//这里注意递归不要改变原来的class的值
				var c ClassBytes
				c.Bytes = bytes
				attributes := c.AnalysisClassReadeAttributes(int(codeAttribute.AttributesCountInfo), class)
				codeAttribute.AttributeInfo = attributes
			}
			attribute[i] = codeAttribute
		case constan.LINENUMBERTABLE:
			//LINENUMBERTABLE
			lineNumberTableAttribute := LineNumberTableAttribute{}
			lineNumberTableAttribute.AttributeNameIndex = nameIndex
			lineNumberTableAttribute.AttributeLength = length
			lineNumberTableAttribute.Infos = bytes
			attribute[i] = lineNumberTableAttribute
		case constan.LOCALVARIABLETABLE:
			//LOCALVARIABLETABLE
			localVariableTable := LocalVariableTable{}
			localVariableTable.AttributeNameIndex = nameIndex
			localVariableTable.AttributeLength = length
			localVariableTable.Infos = bytes
			attribute[i] = localVariableTable
		case constan.SOURCEFILE:
			//SOURCEFILE
			sourceFile := SourceFile{}
			sourceFile.AttributeNameIndex = nameIndex
			sourceFile.AttributeLength = length
			sourceFile.Infos = bytes
			attribute[i] = sourceFile

		}
	}
	return attribute
}

3、在class.go文件定义 AnalysisClassReadeMethod方法

func (C *ClassBytes) AnalysisClassReadeMethod(count int) []*Method {
	var method []*Method = make([]*Method, count)
	for i := 0; i < count; i++ {
		m := Method{}
		访问标识
		m.AccessFlags = binary.BigEndian.Uint16(C.Bytes[:2])
		m.NameIndex = binary.BigEndian.Uint16(C.Bytes[2:4])
		m.DescriptorIndex = binary.BigEndian.Uint16(C.Bytes[4:6])
		m.AttributesCount = binary.BigEndian.Uint16(C.Bytes[6:8])
		//先去除读过的字节
		C.Bytes = C.Bytes[8:]
		//判断是否有属性
		if m.AttributesCount != 0 {
			//属性解析
			C.AnalysisClassReadeAttributes(int(m.AttributesCount), C.Class)
		}
		method[i] = &m
	}
	return method
}

4、在class.go文件定义 AnalysisClassReadeConstantPool方法

func (C *ClassBytes) AnalysisClassReadeConstantPool(count int) []ConstantPoolInfo {
	var constantPoolInfo []ConstantPoolInfo = make([]ConstantPoolInfo, count)
	//这里较为复杂,先循环
	for i := 1; i < count; i++ {
		//读取tag
		tag := C.Bytes[0]
		switch tag {
		case constan.CONSTANT_Class:
			//类信息ConstantClassInfo
			info := &ConstantClassInfo{}
			info.Tag = tag
			info.NameIndex = binary.BigEndian.Uint16(C.Bytes[1:3])
			C.Bytes = C.Bytes[3:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_Fieldref:
			//字段
			info := &ConstantFieldrefInfo{}
			info.Tag = tag
			info.ClassIndex = binary.BigEndian.Uint16(C.Bytes[1:3])
			info.nameAndTypeIndex = binary.BigEndian.Uint16(C.Bytes[3:5])
			C.Bytes = C.Bytes[5:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_Methodref:
			//方法ConstantMethodrefInfo
			info := &ConstantMethodrefInfo{}
			info.Tag = tag
			info.ClassIndex = binary.BigEndian.Uint16(C.Bytes[1:3])
			info.nameAndTypeIndex = binary.BigEndian.Uint16(C.Bytes[3:5])
			C.Bytes = C.Bytes[5:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_InterfaceMethodref:
		case constan.CONSTANT_String:
			//方法ConstantStringInfo
			info := &ConstantStringInfo{}
			info.Tag = tag
			info.StringIndex = binary.BigEndian.Uint16(C.Bytes[1:3])
			C.Bytes = C.Bytes[3:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_Integer:
		case constan.CONSTANT_Float:
		case constan.CONSTANT_Long:
		case constan.CONSTANT_Double:
		case constan.CONSTANT_NameAndType:
			//名称类型ConstantNameAndTypeInfo
			info := &ConstantNameAndTypeInfo{}
			info.Tag = tag
			info.NameIndex = binary.BigEndian.Uint16(C.Bytes[1:3])
			info.DescriptorIndex = binary.BigEndian.Uint16(C.Bytes[3:5])
			C.Bytes = C.Bytes[5:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_Utf8:
			//ConstantUtf8Info
			info := &ConstantUtf8Info{}
			info.Tag = tag
			info.Length = binary.BigEndian.Uint16(C.Bytes[1:3])
			info.Bytes = C.Bytes[3 : 3+info.Length]
			C.Bytes = C.Bytes[3+info.Length:]
			constantPoolInfo[i] = info
		case constan.CONSTANT_MethodHandle:
		case constan.CONSTANT_MethodType:
		case constan.CONSTANT_InvokeDynamic:

		}
	}

	return constantPoolInfo
}

5、定义常量

我们新建哥目录constan用来定义一些常量,新建constanTag.go用于定义常量池的tag,attributeTag.go用来定义属性的名称,我们需要按属性名称来解析

constanTag的信息

package constan

const (
	CONSTANT_Class              = 7
	CONSTANT_Fieldref           = 9
	CONSTANT_Methodref          = 10
	CONSTANT_InterfaceMethodref = 11
	CONSTANT_String             = 8
	CONSTANT_Integer            = 3
	CONSTANT_Float              = 4
	CONSTANT_Long               = 5
	CONSTANT_Double             = 6
	CONSTANT_NameAndType        = 12
	CONSTANT_Utf8               = 1
	CONSTANT_MethodHandle       = 15
	CONSTANT_MethodType         = 16
	CONSTANT_InvokeDynamic      = 18
)

attributeTag的内容

package constan

const (
	CODE            = "Code"
	LINENUMBERTABLE = "LineNumberTable"

	LOCALVARIABLETABLE = "LocalVariableTable"

	SOURCEFILE = "SourceFile"
)

四、编写main.go

终于在第三篇文章我们要初次运行我们的jvm了,编写代码

func main() {
	file, err := os.ReadFile("main.class")
	if err != nil {
		panic(err)
	}
	class := Class{}
	bytes := &ClassBytes{file, &class}
	bytes.AnalysisClass()
	class.PrintClassInfo()
	//for _, o := range file {
	//	sprintf := fmt.Sprintf("%02X ", o)
	//	fmt.Print(sprintf)
	//	fmt.Print(" ")
	//
	//}
}

这里定义一个class的方法用于打印class的信息PrintClassInfo()

func (c Class) PrintClassInfo() {
	buf, err := json.MarshalIndent(c, "", " ") //格式化编码
	if err != nil {
		fmt.Println("err = ", err)
	}
	fmt.Println("buf = ", string(buf))
}

然后重新让ConstantUtf8Info这个常量结构体实现一下MarshalJSON这样我们就能临时打印string类型的常量了

func (constantUtf8Info ConstantUtf8Info) MarshalJSON() ([]byte, error) {
	fmt.Println(string(constantUtf8Info.Bytes))
	buffer := bytes.NewBufferString("{")
	s := string(constantUtf8Info.Bytes)
	buffer.WriteString(fmt.Sprintf(
		"\"%s\" %s%s%s%s",
		"key",
		":",
		"\"",
		s,
		"\""))

	buffer.WriteString("}")
	return buffer.Bytes(), nil
}

运行main.go,我用的是idea如果大家和我一样,建议右键项目运行,否则你可能遇到报错运行不起来

运行结果

 {
 "magic": 3405691582,
 "minor_version_min": 52,
 "minor_version": 0,
 "constant_pool_count": 34,
 "constant_pool_info": [
  null,
  {
   "tag": 10,
   "class_index": 6
  },
  {
   "tag": 9,
   "class_index": 21
  },
  {
   "tag": 8,
   "string_index": 23
  },
  {
   "tag": 10,
   "class_index": 24
  },
  {
   "tag": 7,
   "name_index": 26
  },
  {
   "tag": 7,
   "name_index": 27
  },
  {
   "key": "\u003cinit\u003e"
  },
  {
   "key": "()V"
  },
  {
   "key": "Code"
  },
  {
   "key": "LineNumberTable"
  },
  {
   "key": "LocalVariableTable"
  },
  {
   "key": "this"
  },
  {
   "key": "LMain;"
  },
  {
   "key": "main"
  },
  {
   "key": "([Ljava/lang/String;)V"
  },
  {
   "key": "args"
  },
  {
   "key": "[Ljava/lang/String;"
  },
  {
   "key": "SourceFile"
  },
  {
   "key": "Main.java"
  },
  {
   "tag": 12,
   "name_index": 7,
   "descriptor_index": 8
  },
  {
   "tag": 7,
   "name_index": 28
  },
  {
   "tag": 12,
   "name_index": 29,
   "descriptor_index": 30
  },
  {
   "key": "Hello world!"
  },
  {
   "tag": 7,
   "name_index": 31
  },
  {
   "tag": 12,
   "name_index": 32,
   "descriptor_index": 33
  },
  {
   "key": "Main"
  },
  {
   "key": "java/lang/Object"
  },
  {
   "key": "java/lang/System"
  },
  {
   "key": "out"
  },
  {
   "key": "Ljava/io/PrintStream;"
  },
  {
   "key": "java/io/PrintStream"
  },
  {
   "key": "println"
  },
  {
   "key": "(Ljava/lang/String;)V"
  }
 ],
 "access_flags": 33,
 "this_class": 5,
 "super_class": 6,
 "interface_count": 0,
 "fields_count": 0,
 "field_info": null,
 "method_count": 2,
 "method_info": [
  {
   "access_flags": 1,
   "name_index": 7,
   "descriptor_index": 8,
   "attributes_count": 1,
   "attribute_info": null
  },
  {
   "access_flags": 9,
   "name_index": 14,
   "descriptor_index": 15,
   "attributes_count": 1,
   "attribute_info": null
  }
 ],
 "attributes_count": 1,
 "attribute_info": [
  {
   "attribute_name_index": 18,
   "attribute_length": 2,
   "infos": "ABM="
  }
 ]
}

五、小结

本篇我第一次运行了我们自己的jvm真是可喜可贺,
本姐代码地址:https://download.csdn.net/download/wangkai031/88792241

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表