上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节单独讨论)。