自己动手写Java虚拟机
上QQ阅读APP看书,第一时间看更新

3.2.2 整体结构

有了ClassReader,可以开始解析class文件了。在ch03\classfile目录下创建class_file. go文件,在其中定义ClassFile结构体,代码如下:

package classfile

import "fmt"

type ClassFile struct {
//magic          uint32
minorVersion    uint16
majorVersion    uint16
constantPool    ConstantPool
accessFlags     uint16
thisClass       uint16
superClass      uint16
interfaces      []uint16
fields             []*MemberInfo
methods          []*MemberInfo
attributes      []AttributeInfo
}

ClassFile结构体如实反映了Java虚拟机规范定义的class文件格式。还会在class_file. go文件中实现一系列函数和方法,列举如下:

func Parse(classData []byte) (cf *ClassFile, err error) {...}
func (self *ClassFile) read(reader *ClassReader) {...}
func (self *ClassFile) readAndCheckMagic(reader *ClassReader) {...}
func (self *ClassFile) readAndCheckVersion(reader *ClassReader) {...}
func (self *ClassFile) MinorVersion() uint16 {...} // getter
func (self *ClassFile) MajorVersion() uint16 {...} // getter
func (self *ClassFile) ConstantPool() ConstantPool {...} // getter
func (self *ClassFile) AccessFlags() uint16 {...} // getter
func (self *ClassFile) Fields() []*MemberInfo {...} // getter
func (self *ClassFile) Methods() []*MemberInfo {...} // getter
func (self *ClassFile) ClassName() string {...}
func (self *ClassFile) SuperClassName() string {...}
func (self *ClassFile) InterfaceNames() []string {...}

相比Java语言,Go的访问控制非常简单:只有公开和私有两种。所有首字母大写的类型、结构体、字段、变量、函数、方法等都是公开的,可供其他包使用。首字母小写则是私有的,只能在包内部使用。在本书的代码中,尽量只公开必要的变量、字段、函数和方法等。但是为了提高代码可读性,所有的结构体都是公开的,也就是首字母是大写的。

Parse()函数把[]byte解析成ClassFile结构体,代码如下:

func Parse(classData []byte) (cf *ClassFile, err error) {
defer func() {
  if r := recover(); r ! = nil {
      var ok bool
      err, ok = r.(error)
      if ! ok {
        err = fmt.Errorf("%v", r)
      }
  }
}()

cr := &ClassReader{classData}
cf = &ClassFile{}
cf.read(cr)
return
}

Go语言没有异常处理机制,只有一个panic-recover机制。read()方法依次调用其他方法解析class文件,代码如下:

func (self *ClassFile) read(reader *ClassReader) {
self.readAndCheckMagic(reader) // 见3.2.3
self.readAndCheckVersion(reader) // 见3.2.4
self.constantPool = readConstantPool(reader) // 见3.3
self.accessFlags = reader.readUint16()
self.thisClass = reader.readUint16()
self.superClass = reader.readUint16()
self.interfaces = reader.readUint16s()
self.fields = readMembers(reader, self.constantPool) // 见3.2.8
self.methods = readMembers(reader, self.constantPool)
self.attributes = readAttributes(reader, self.constantPool) //见3.4
}

MajorVersion()等6个方法是Getter方法,把结构体的字段暴露给其他包使用。MajorVersion()的代码如下:

func (self *ClassFile) MajorVersion() uint16 {
return self.majorVersion
}

和Java有所不同,Go的Getter方法不以“get”开头。由于Getter方法非常简单,只是返回字段而已,为了节约篇幅,后文中不再给出Getter方法的代码。ClassName()从常量池查找类名,代码如下:

func (self *ClassFile) ClassName() string {
return self.constantPool.getClassName(self.thisClass)
}

SuperClassName()从常量池查找超类名,代码如下:

func (self *ClassFile) SuperClassName() string {
if self.superClass > 0 {
  return self.constantPool.getClassName(self.superClass)
}
return "" // 只有java.lang.Object没有超类
}

InterfaceNames()从常量池查找接口名,代码如下:

func (self *ClassFile) InterfaceNames() []string {
interfaceNames := make([]string, len(self.interfaces))
for i, cpIndex := range self.interfaces {
  interfaceNames[i] = self.constantPool.getClassName(cpIndex)
}
return interfaceNames
}

下面详细介绍class文件的各个部分(常量池和属性表比较复杂,放到3.3和3.4节单独讨论)。