鹦鹉虚拟机/PMC 系统
我们已经讨论过 PMCs——在鹦鹉虚拟机/多态容器 (PMCs)章节中——包括如何使用 PMC 编译器定义新的 PMC 类型,以及如何在 PIR 程序中使用它们。本章将更详细地介绍 PMCs 在 Parrot 中的实际使用方式,包括 PMCs 的内存管理、变形 PMCs 以及与 PMCs 的交互。
PMC 数据结构看似简单,并且被设计成足够可扩展,以供通用数据和功能使用。以下是 PMC 结构和相关的 PMC_EXT 结构的定义
struct PMC {
Parrot_UInt flags;
VTABLE *vtable;
DPOINTER *data;
struct PMC_EXT *pmc_ext;
};
typedef struct PMC_EXT {
DPOINTER *data;
PMC *_metadata;
struct _Sync *_synchronize;
PMC *_next_for_GC;
} PMC_EXT;
从这些定义中我们可以看到,PMC 实际上非常小。关于 PMC 的大部分信息,包括它所有不同的方法和 VTABLE 接口都存储在 ->vtable
指针中。VTABLE 结构是一个非常大的结构,包含所有不同 VTABLE 接口的函数指针。
每个 PMC 类型还包含一个指向特定于该 PMC 的数据结构的指针。这些数据结构是根据 PMC 的继承层次结构及其定义的所有属性来定义的。例如,这个 PMC 定义
pmclass MyPmc {
ATTR INTVAL a;
ATTR FLOATVAL b;
ATTR STRING *c;
ATTR PMC *d;
...
}
将转换为这个 C 数据结构定义
typedef struct Parrot_MyPmc_attributes {
INTVAL a;
FLOATVAL b;
STRING *c;
PMC *d;
} Parrot_MyPmc_attributes;
这个结构应该包含在 ->data
指针中,应该始终使用 PMC_data
宏访问它。这样,如果 PMC 结构定义最终发生了变化,所有正确使用宏的代码都将自动更新,因为宏也会更新。以下是一个使用这些属性的初始化 VTABLE 的示例
VTABLE void init () {
Parrot_MyPmc_attributes *p = mem_allocate_typed(Parrot_MyPmc_attributes);
p->a = 0;
p->b = 0.0
p->c = NULL;
p->d = PMCNULL;
PMC_data(SELF) = p;
}
还有一个宏使用单词 PARROT 和 PMC 的全大写字母名称来检索数据结构,并正确地进行类型转换(因此你的编译器不会对使用未类型转换的指针发出警告)
Parrot_MyPmc_attributes *attr = PARROT_MYPMC(SELF);
C 不是一种基于类的(或“面向对象”的)语言,但 OO 编程方法的许多教训已被改编用于 Parrot 的代码库。PMCs、STRINGs 和一些其他数据类型是基于“PObj”的定义,也称为“Buffer”
typedef struct Buffer {
Parrot_UInt flags;
} Buffer;
请注意,Buffer 中的前两个条目与 PMC 中的是一样的?所有以这两个数据项开始的对象都被称为“PObject 同构”。简而言之,我们说所有 pobject 同构都只是“PObjects”,并且有许多类型的 PObjects。例如,内存管理系统可以测试所有 pobjects 的标志以确定内存对象是什么类型的 PObject。
PMC 可以选择包含一个 PMC_EXT 结构,它添加了额外的功能。PMC_EXT 允许一个 PMC 在多个线程之间或多个 Parrot 解释器之间共享,而不会引入数据竞争。PMC_EXT 还允许一个 PMC 包含一个元数据哈希(属性值对),这些元数据通常作为属性添加到 PIR 中。
PMCs 从两个特殊的池中分配,一个 PMC 池和一个常量 PMC 池。常量 PMCs 被认为是不可变且永久存在的,因此它们从不被修改,也不被垃圾收集器收集。STRINGS 在字符串池或常量字符串池中分配。相同的关系适用,常量字符串从不被修改,也不被收集。PMC_EXT 结构目前不受内存管理子系统的管理。但是,由于 PMC_EXT 与 PMCs 之间是一对一的关系,我们总是知道当它的 PMC 被释放时可以释放它。
就垃圾收集而言,PMCs 是 Parrot 中唯一的一种聚合数据类型。STRINGS 不包含指向垃圾收集器感兴趣的其他数据项的指针。堆栈块,在某些结构中内部使用,是 PObjs 也是聚合,但由收集器单独标记,并且不直接作为聚合处理。
VTables 代表对所有类型 PMCs 的标准接口。对于每个 PMC,有一系列可以执行(或尝试)的标准操作。并非所有 PMCs 都支持所有 Vtable 操作
VTables 是复杂的数据项,除了包含大量的函数指针之外,还包含一些数据项来支持 PMCs。其中一个数据项是一个类 PMC,一个代表特定 PMC 类的 PMC。另一个数据项是一个枚举,它区分所有 PMC 类。