前面已经把类文件加载了出来,并且将字节流转化成了易用的数据结构,下面就是类的验证和准备阶段了。验证阶段在书中忽略了,所以这里只包括准备阶段。
准备阶段主要是给类变量分配空间,赋上默认值,static final变量赋上初始值。如下所示,用slot数组保存静态变量。
type Class struct {
。。。
staticSlotCount uint
staticVars Slots //类静态变量
}Slot定义如下,静态变量都保存在slot中,32位的类型如int,float,boolean,char,引用等都占用一个slot,64位的long和double占用两个slot。Slots是Slot的数组类型,主要封装了Slot的设置和查询操作。
type Slot struct {
num int32
ref *heap.Object
}
type Slots []Slot
func (this Slots) SetInt(index uint, val int32) {
this[index].num = val
}
func (this Slots) GetInt(index uint) int32 {
return this[index].num
}
func (this Slots) SetFloat(index uint, val float32) {
this[index].num = int32(math.Float32bits(val))
}
func (this Slots) GetFloat(index uint) float32 {
return math.Float32frombits(uint32(this[index].num))
}
func (this Slots) SetLong(index uint, val int64) {
this[index].num = int32(val)
this[index+1].num = int32(val >> 32)
}
func (this Slots) GetLong(index uint) int64 {
low := uint32(this[index].num)
high := uint32(this[index+1].num)
return int64(high)<<32 | int64(low)
}
func (this Slots) SetDouble(index uint, val float64) {
bits := int64(math.Float64bits(val))
this.SetLong(index, bits)
}
func (this Slots) GetDouble(index uint) float64 {
bits := uint64(this.GetLong(index))
return math.Float64frombits(bits)
}
func (this Slots) SetRef(index uint, ref *heap.Object) {
this[index].ref = ref
}
func (this Slots) GetRef(index uint) *heap.Object {
return this[index].ref
}上面对Slots的操作需要Slot在Slots中的位置index,即字段占用了第几个Slot,所以在Field结构体中加上SlotId字段表示该字段占用第几个Slot。
type Field struct {
ClassMember
constValueIndex uint16
slotId uint
}那么SlotId怎么确定呢?可以按照field在class中的顺序来分配,需要注意的是,父类也可能有静态变量。所以可以按照继承关系,把父类的字段放到前面,子类的SlotId从父类的最后一个SlotId开始计数,代码如下
// 为每个静态变量分配slotId,并统计slot个数
func calcStaticFieldId(class *Class) {
slotId := uint(0)
if class.superClass != nil {
slotId = class.superClass.staticSlotCount
}
for _, field := range class.fields {
if !field.isStatic() {
continue
}
field.slotId = slotId
slotId++
if field.isLongOrDouble() {
slotId++
}
}
class.staticSlotCount = slotId
}确定好字段在Slot数组中的位置之后,就可以把字段的默认值或初始值放到Slots里了。因为Go默认会分配默认值,所以默认值不需要我们额外赋值,只需要把static final的初始值放到slots即可。常量的初始值的index保存在field的ConstValue的Attribute中。
func initStaticVars(class *Class) {
class.staticVars = newSlots(class.staticSlotCount) // 因为go语言本身就会给slot分配默认值,所以默认值不需要再分配
for _, field := range class.fields {
if field.isStatic() && field.isFinal() { // static final的变量会有初始值,分配在const value常量中
initStaticFinalVars(class, field)
}
}
}
func initStaticFinalVars(class *Class, field *Field) {
staticVars := class.staticVars
poolIndex := field.constValueIndex
constPool := class.constantPool
descriptor := field.descriptor
slotId := field.slotId
if descriptor == "Z" || descriptor == "B" || descriptor == "C" || descriptor == "S" || descriptor == "I" {
staticVars.SetInt(slotId, constPool.GetInteger(poolIndex))
return
}
if descriptor == "J" {
staticVars.SetLong(slotId, constPool.GetLong(poolIndex))
return
}
if descriptor == "D" {
staticVars.SetDouble(slotId, constPool.GetDouble(poolIndex))
return
}
if descriptor == "Ljava/lang/String" {
// TODO 常量池暂不支持string
}
}
版权声明:本文为oBuShanQing原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。