网站首页 > java教程 正文
一、概要
上篇我们定义了class文件的结构体,这节我们主要实现把class文件读取到内存,注意我们的目的很简单,就是读取字节流解析class文。不会现在处理一些过多的内容。
在class.go文件定义ClassBytes结构体
type ClassBytes struct {
Bytes []byte
Class *Class
}
Bytes是当前读取class文件的字节,Class是需要解析的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
猜你喜欢
- 2024-09-16 【开发工具】VMwareInstall23.11安装程序
- 2024-09-16 Kubernetes核心原理和搭建(kubernetes底层原理)
- 2024-09-16 Java 虚拟机基础:内存管理,类的加载机制,分析字节码执行过程
- 2024-09-16 类装载器实现Java虚拟机安全(三),看完100%的人都收藏了
- 2024-09-16 免费全能虚拟机VirtualBox 4.3.28中文版下载
- 2024-09-16 七爪源码:虚拟机:定义、类型、优势和优势
- 2024-09-16 VirtualBox5.2.6虚拟机下载安装基础教程(1)
- 2024-09-16 全面支持Win10:免费虚拟机VMware Player 12下载
- 2024-09-16 最强免费虚拟机:VirtualBox 5.1.28下载
- 2024-09-16 面试时最怕问到 Java 虚拟机内存模型?这篇文章我发晚了
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)