# 2023/12/2 工作日志

写了三个月，这是一次较大的更新。

### 提交了代码漏洞修复
本次进行了一些测试，并且修复了代码上的一些小瑕疵（由于测试很杂，我不提交测试代码）

### 丰富了依赖库
我没有添加新的库文件，但是在原有的基础上给一些库增加了函数，这些函数方便了我的开发。

### 编写了对象管理器和GC（垃圾回收器）
这才是这次更新的重点。
<br>
本次更新的主要文件是src/data_type目录下的Variable.cpp和src/vm目录下的ObjectManager.cpp。
<br><br>
其中，ObjectManager.cpp基于Variable.cpp。
<br><br>
Variable是一个非常简单的类，它有一个名为```data```的```DataType*```成员，所以对于任意左值，我们只需要得到这个左值的```Variable*```值，就可以对这个左值进行赋值（而不需要知道这个左值的具体信息）。
<br>
比如，对于一个名为```var```的```Variable*```对象和一个名为```dat```的```DataType*```对象，想要把```var```（也就是左值）赋值为```dat```（也就是右值），只需要这么做：
```C++
var->data = dat;
```
<br>

**换句话说，每个左值都对应着一个```Variable*```对象，而可以通过给这个对象的data成员赋值，从而达到给该左值赋值的目的**
<br><br>
由于我之前对src/data_type目录下的框架没有清楚的认识。导致了data_type的一部分代码需要修改，我在这次的更新当中修复了它。（修复的主要内容是把代码中的``DataType*``改成``Variable*``，具体见源码）
<br><br>
ObjectManager.cpp编写了对象管理器的本体。对象管理器包含了GC机制。从今以后，如果你要新建虚拟机的对象，应该向对象管理器申请。
<br><br>
GC所采用的算法是标记-清除算法，我参考了清华大学出版社的《编译原理（第二版）》里的伪代码。我用一个栈来维护运行时所有的作用域。新建或退出一个作用域时只需要入栈或出栈即可。寻找某个变量也只需要从栈顶找到栈底就行。
<br><br>
以下是ObjectManager.cpp的使用方法（我只讲述用户应该了解的接口，内部接口请见源码）：
<br><br>

* ```ObjectManager(unsigned long long mem_limit)```：构造函数，mem_limit是虚拟机对象可以占用的最大内存大小。
* ```MallocObject<申请的类>(申请的类的构造函数参数...)```：用户应该通过这个函数来申请对象。<br><br>比如有一个名为man的对象管理器，申请一个SequenceType对象（假设构造参数为10）并存放到变量d里，应该这么做：
```C++
SequenceType* d = man.MallocObject<SequenceType>(10);
//注意：这个函数的模板类型不是指针类型，但是函数的返回值的是指针类型
```
申请对象时，该函数会调用GCConditions函数，来查看申请此对象时是否需要执行GC。
<br><br>

* ```Variable* GetLeftVariable(int id, datatype::DataType* val)```：这个函数用于获取左值变量。
<br><br>
因为和右值不同，在左值当中的变量可能是第一次出现的，所以我编写了这个函数，当获取的变量第一次出现时，就创建它，并返回这个变量。
<br><br>
这个GetLeftVariable与GetVariable的区别在于：如果要获取的变量是第一次出现的，那么GetLeftVariable函数会创建这个变量并返回；而GetVariable函数会直接报错。

* ```Variable* GetVariable(int id)```：这个函数用于获取变量。这个函数通常用于获取右值变量。
* ```PushScope()```：这个函数用于新建一个作用域。
* ```PopScope()```：这个函数用于退出一个作用域。
* ```GC()```：这个函数用于GC，和JVM等虚拟机不同，调用这个函数就一定会触发GC。（这个函数看似云淡风轻，实则我花了大把精力实现了这个函数，这是我人生中第一次编写GC）
***
开发者同样可以自定义虚拟机平时执行GC的条件：只需改变ObjectManager.cpp里的GCConditions函数即可，十分简单。
<br><br>
虚拟机默认：
<br>
当对象占用内存+GC预留内存大等于内存限制时，触发GC。



### 接下来要做的事：

1. 定义字节码执行器的数据结构
2. 编写字节码执行器的简单算法
3. 完成虚拟机的SFN功能