写了三个月,这是一次较大的更新。
本次进行了一些测试,并且修复了代码上的一些小瑕疵(由于测试很杂,我不提交测试代码)
我没有添加新的库文件,但是在原有的基础上给一些库增加了函数,这些函数方便了我的开发。
这才是这次更新的重点。
本次更新的主要文件是src/data_type目录下的Variable.cpp和src/vm目录下的ObjectManager.cpp。
其中,ObjectManager.cpp基于Variable.cpp。
Variable是一个非常简单的类,它有一个名为data
的DataType*
成员,所以对于任意左值,我们只需要得到这个左值的Variable*
值,就可以对这个左值进行赋值(而不需要知道这个左值的具体信息)。
比如,对于一个名为var
的Variable*
对象和一个名为dat
的DataType*
对象,想要把var
(也就是左值)赋值为dat
(也就是右值),只需要这么做:
var->data = dat;
换句话说,每个左值都对应着一个Variable*
对象,而可以通过给这个对象的data成员赋值,从而达到给该左值赋值的目的
由于我之前对src/data_type目录下的框架没有清楚的认识。导致了data_type的一部分代码需要修改,我在这次的更新当中修复了它。(修复的主要内容是把代码中的DataType*
改成Variable*
,具体见源码)
ObjectManager.cpp编写了对象管理器的本体。对象管理器包含了GC机制。从今以后,如果你要新建虚拟机的对象,应该向对象管理器申请。
GC所采用的算法是标记-清除算法,我参考了清华大学出版社的《编译原理(第二版)》里的伪代码。我用一个栈来维护运行时所有的作用域。新建或退出一个作用域时只需要入栈或出栈即可。寻找某个变量也只需要从栈顶找到栈底就行。
以下是ObjectManager.cpp的使用方法(我只讲述用户应该了解的接口,内部接口请见源码):
ObjectManager(unsigned long long mem_limit)
:构造函数,mem_limit是虚拟机对象可以占用的最大内存大小。MallocObject<申请的类>(申请的类的构造函数参数...)
:用户应该通过这个函数来申请对象。SequenceType* d = man.MallocObject<SequenceType>(10);
//注意:这个函数的模板类型不是指针类型,但是函数的返回值的是指针类型
申请对象时,该函数会调用GCConditions函数,来查看申请此对象时是否需要执行GC。
Variable* GetLeftVariable(int id, datatype::DataType* val)
:这个函数用于获取左值变量。
因为和右值不同,在左值当中的变量可能是第一次出现的,所以我编写了这个函数,当获取的变量第一次出现时,就创建它,并返回这个变量。
这个GetLeftVariable与GetVariable的区别在于:如果要获取的变量是第一次出现的,那么GetLeftVariable函数会创建这个变量并返回;而GetVariable函数会直接报错。
Variable* GetVariable(int id)
:这个函数用于获取变量。这个函数通常用于获取右值变量。
PushScope()
:这个函数用于新建一个作用域。PopScope()
:这个函数用于退出一个作用域。GC()
:这个函数用于GC,和JVM等虚拟机不同,调用这个函数就一定会触发GC。(这个函数看似云淡风轻,实则我花了大把精力实现了这个函数,这是我人生中第一次编写GC)开发者同样可以自定义虚拟机平时执行GC的条件:只需改变ObjectManager.cpp里的GCConditions函数即可,十分简单。
虚拟机默认:
当对象占用内存+GC预留内存大等于内存限制时,触发GC。
写了三个月,可以说是完成了一件大事,真累,但是感觉好爽。
我一直致力于把文档写简单、干练、透彻。但是接下来项目难度骤增,文档也会更加复杂。(其实这篇文档就已经开始变复杂了)
写完文档之后,对着它审而又审,最后准备郑重地提交。