手写JVM(二十二)-数组实现

代码、内容参考来自于张秀宏大佬的自己动手写Java虚拟机 (Java核心技术系列)以及尚硅谷宋红康:JVM全套教程。

如果数组的元素是基本类型,就把它叫作基本类型数组 ,数组的元素是引用类型,把它叫作引用类型数组 。基本类型数组肯定都是一维数组 ,如果引用类型数组的元素也是数组,那么它就是多维数组 。

 

1.数组概述

数组类和普通的类是不同的。普通的类从class文件中加载,但是数组类由Java虚拟机在运行时生成。数组的类名是左方括号([)+数组元素的类型描述符;数组的类型描述符就是类名本身。

还有指令等的不同,这些将在下面说。

将在类和对象的基础上实现数组类和数组对象,先来看数组对象。

 

2.数组对象

和普通对象一样,数组也是分配在堆中的,通过引用来使用。所以需要改造Object结构体,让它既可以表示普通的对象,也可以表示数组。打开ch08\rtda\heap\object.go,修改Object结构体,把fields字段改为data,类型也从Slots变成了interface{}。改动如下:

type Object struct {
    class *Class
    data interface{} // Slots for Object, []int32 for int[] ...
}

 

Go语言的interface{}该类型的变量可以容纳任何类型的值。对于普通对象来说,data字段中存放的仍然还是Slots变量。但是对于数组,可以在其中放各种类型的数组。

newObject()用来创建普通对象,因此需要做相应的调整,改动如下:

// create normal (non-array) object
func newObject(class *Class) *Object {
    return &Object{
        class: class,
        data: newSlots(class.instanceSlotCount),
    }
}

因为Fields()方法也仍然只针对普通对象,所以它的代码也需要做相应调整,如下所示:

func (self *Object) Fields() Slots {
    return self.data.(Slots)
}

 

需要给Object结构体添加几个数组特有的方法,在ch08/rtda/heap目录下创建array_object.go文件,在其中实现8个方法,分别针对引用类型数组和7种基本类型数组返回具体的数组数据。在其中添加一个ArrayLength()方法,<t>aload和<t>astore系 列指令各有8条,所以针对每种类型都提供一个方法,返回相应的 数组数据。因为arraylength指令只有一条,所以ArrayLength()方法 需要自己判断数组类型,而Booleans()方法将使用[]int8来表示布尔数组,所以只需要Bytes()方法即可。

package heap

func (self *Object) Bytes() []int8 {
    return self.data.([]int8)
}
func (self *Object) Shorts() []int16 {
    return self.data.([]int16)
}
func (self *Object) Ints() []int32 {
    return self.data.([]int32)
}
func (self *Object) Longs() []int64 {
    return self.data.([]int64)
}
func (self *Object) Chars() []uint16 {
   return self.data.([]uint16)
}
func (self *Object) Floats() []float32 {
    return self.data.([]float32)
}
func (self *Object) Doubles() []float64 {
    return self.data.([]float64)
}
func (self *Object) Refs() []*Object {
    return self.data.([]*Object)
}

func (self *Object) ArrayLength() int32 {
    switch self.data.(type) {
    case []int8:
        return int32(len(self.data.([]int8)))
    case []int16:
        return int32(len(self.data.([]int16)))
    case []int32:
        return int32(len(self.data.([]int32)))
    case []int64:
        return int32(len(self.data.([]int64)))
    case []uint16:
        return int32(len(self.data.([]uint16)))
    case []float32:
        return int32(len(self.data.([]float32)))
    case []float64:
        return int32(len(self.data.([]float64)))
    case []*Object:
        return int32(len(self.data.([]*Object)))
    default:
        panic("Not array!")
    }
}

 

3.数组类

在/rtda/heap目录下创建array_class.go文件,在其中定义NewArray()方法,NewArray()方法专门用来创建数组对象。如果类并不是数组类,就调用panic()函数终止程序执行,否则根据数组类型创建数组对象。代码如下:

package heap

// 判断是否是数组类
func (self *Class) IsArray() bool {
    return self.name[0] == '['
}

// 用来创建数组对象
func (self *Class) NewArray(count uint) *Object {
    if !self.IsArray() {
        panic("Not array class: " + self.name)
    }
    switch self.Name() {
    case "[Z":
        //布尔数组是使用字节数组来表示
        return &Object{self, make([]int8, count)}
    case "[B":
        return &Object{self, make([]int8, count)}
    case "[C":
        return &Object{self, make([]uint16, count)}
    case "[S":
        return &Object{self, make([]int16, count)}
    case "[I":
        return &Object{self, make([]int32, count)}
    case "[J":
        return &Object{self, make([]int64, count)}
    case "[F":
        return &Object{self, make([]float32, count)}
    case "[D":
        return &Object{self, make([]float64, count)}
    default:
        return &Object{self, make([]*Object, count)}
    }
}

注意,布尔数组是使用字节数组来表示的:

 

4.加载数组类

打开\rtda\heap\class_loader.go文件,修改LoadClass()方法,增加了类型判断,如果要加载的类是数组类,则调用新的loadArrayClass()方法,否则还按照原来的逻辑。loadArrayClass()方法需要生成一个Class结构体实例,代码如下:

func (self *ClassLoader) LoadClass(name string) *Class {
    if class, ok := self.classMap[name]; ok {
        // already loaded
        return class
    }
    // 若类名的第一个字符是'[',表示该类是数组类
    if name[0] == '[' {
        // array class
        return self.loadArrayClass(name)
    }

    return self.loadNonArrayClass(name)
}

func (self *ClassLoader) loadArrayClass(name string) *Class {
    //生成Class结构体,数组类由Java虚拟机在运行时生成所以没有像loadNonArrayClass()那样读取class文件
    class := &Class{
        // 访问标识
        accessFlags: ACC_PUBLIC, // todo
        // 类名
        name: name,
        // 类加载器
        loader: self,
        // 数组类不需要初始化,所以设成true
        initStarted: true,
        // 数组类的超类是java/lang/Object
        superClass: self.LoadClass("java/lang/Object"),
        interfaces: []*Class{
            // 数组类实现了java.lang.Cloneable接口
            self.LoadClass("java/lang/Cloneable"),
            // 数组类实现了java.io.Serializable
            self.LoadClass("java/io/Serializable"),
        },
    }
    self.classMap[name] = class
    return class
}

注意:因为数组类不需要初始化,所以把initStarted字段设置成true。数组类的超类是java.lang.Object,并且实现了java.lang.Cloneable和java.io.Serializable接口。

 

5.参考

尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ

周志明:深入理解java虚拟机

张秀宏:自己动手写Java虚拟机 (Java核心技术系列)

GO语言官网:Standard library – Go Packages

Java虚拟机规范:Chapter 4. The class File Format (oracle.com)

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇