Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

面向对象的脚本语言的类的实现

jin_hao_chen 2019-02-15 14:50:00 阅读数:165 评论数:0 点赞数:0 收藏数:0

2. 面向对象的脚本语言的类的实现

只要是一个对象就要有一个ObjHeader结构体, 该结构体位于该对象的开头

ObjHeader结构

// 以Obj开头的一般为对象, 但是这里ObjHeader仅仅是一个对象头, 不是一个对象, 发现一个规律
// 在结构体中, 如果有定义一个什么type类型的, 则在该脚本语言中就不会定义成对象
typedef struct ObjHeader {
ObjType type; // 对象类型
bool isDark; // 是否可以到达, 如果可以到达, 则GC回收对象
Class *class; // 指向类对象, 在类对象中保存着方法, 这样该对象就可以调用方法了:
struct ObjHeader *next; // 用于链表
} ObjHeader;
// 对象类型
typedef enum ObjType {
ObjTypeList,
ObjTypeMap,
ObjTypeModule,
ObjTypeString,
ObjTypeRange,
ObjTypeFunction,
ObjTypeThread,
ObjTypeClass,
ObjTypeInstance
} ObjType;

Value结构体(Value不是对象, 他在脚本语言层面是一个引用, 因为没有类型, 但是在C语言中需要Value保存属性)


// 它类似于Python中的引用, 在栈中定义, 所以脚本语言模拟的栈就是Value数组, 对象在堆中创建
typedef struct Value {
ValueType type;
union {
double num;
ObjHeader *obj_header;
};
} Value;
// 定义的类型是直接在引用右侧写出来的
// num, true, false这些都能在右侧直接写出来, 而不需要使用其他方法调用
typedef enum {
ValueTypeUndefined,
ValueTypeNull,
ValueTypeObj,
ValueTypeNum,
ValueTypeTrue,
ValueTypeFalse
} ValueType;
// 通过宏将ValueType与Value结构体直接的转换更快捷

Class类对象结构体

/*
好好想一下, 一个类中都有什么, 这与我们在Java和C++编程的类不同, 我们只找所有的类的共同点
1. 对象头
2. 字段个数
3. 方法对象区, 用于存方法
*/
typedef struct Class {
ObjHeader obj_header;
struct Class *superclass;
int field_num;
MethodBuffer methods;
ObjString name;
} Class;
typedef struct Method {
MethodType type;
union {
// C语言实现的方法
Primitive prim_fn;
// 脚本语言将代码编译成ObjClosure对象
ObjClosure *obj;
};
} Method;
typedef num MethodType {
MethodTypeNull,
MethodPrimitive,
MethodScript,
MethodCall // 用于重载
} MethodType;

在构建出上述一个类关系之后, 首先应该定义字符串类(ObjString)

// 这里仅仅是定义了字符串对象, obj_header指向是ObjString类对象
typedef struct ObjString {
ObjHeader obj_header;
long hash_code; // 保存hash值
int len;
char *start[0];
} ObjString;
// 计算字符串的hashcode
int hash_string(const char *str, int length) {
int hashcode = xxxxxxxx;
int idx = 0;
while (idx < length) {
hashcode ^= str[idx++];
hashcode *= yyyyyyyy;
}
return hashcode;
}

元对象


typedef struct {
ObjHeader obj_header;
StringBuffer module_var_name;
ValueBuffer module_var_value;
ObjString *name;
} ObjModule; // 模块不属于任何类, 所有它的obj_header中的class指着指向NULL
typedef struct {
ObjHeader obj_header;
Value field[0]; // 存储属性, 为引用, 这里是在内存中的样子
} ObjInstance;

在脚本中执行过程中最重要的就是代码(存放逻辑的地方, 函数, 方法, 模块中都是)

注意: 接下来的对象结构会比较复杂, 请大致浏览一遍, 在后面会总结他们的关系

  • 统一使用ObjFunc表示还这些代码指令

typedef struct ObjFunc {
ObjHeader obj_header;
ByteBuffer instr_stream; // 保存编译后的代码指令, 这是ObjFunc对象的核心功能
ValueBuffer constants; // 常量, 在模块中会有
Module *mod; // 属于哪个模块
int max_stack_size; // 可用的最大栈个数
int upvalue_num; // 用到外层函数变量的个数, 其中upvalue是一个闭包对象, 对在外层函数中栈中的被内层嵌套函数引用到的引用(Value)的封装[为什么? 因为对象在堆中, Value这种应用类型才在栈中:-)], 可以将upvalue看成一个容器, 里面维护着Value类型的值
// 发现在ObjFunc中没有与其对应的upvalue产生联系, 在后面提到的ObjClosure对象中会进行关联
int arg_num;
} ObjFunc;
  • 与ObjFunc对象相关的与闭包有关的对象结构

typedef struct ObjUpValue {
ObjHeader obj_header;
Value *ptr; // 指向在外层函数中栈中的局部变量
Value closed_value; // 如果外层函数生命周期结束, 则会回收栈, 为了实现闭包, 将ptr指向的值拷贝到closed_value中即可
} ObjUpValue;
typedef struct ObjClosure {
ObjHeader *obj_header;
ObjFunc *func;
// 在这里对func与他的upvalue进行了关联
ObjUpvalue *upvalues[0];
} ObjClosure;

函数要运行就需要一个环境, 这个环境就是一个栈帧(Frame)

// Frame就是一个函数调用框架, 就是一个栈, 但是又是有一点抽象的, 它通过start_stack来访问Value数组
typedef struct Frame {
int *ip; // 模拟CPU的CS:IP
Value *stack_start;
/* 在上面我们提到了很多的结构体对象, 有ObjFunc, ObjUpvalue, ObjClosure, 那么到底那个才是接口, 这里Closure最大, 所以Closure是接口, 在Method结构体对象中可以看到, 在union中primitive与closure是并列的*/
ObjClosure *closure;
}Frame;

关系总结

闭包关系图

  • Frame获取到ObjClosure, 得到ObjFunc中的intr_stream执行指令

提到了这么多的结构体, 那么创建他们的顺序是怎样的呢

  • 创建vm目录

    
    typedef struct vm {
    Parser *cur_parser; // 当前vm使用的parser
    uint32_t allocated_bytes; // 记录已经分配的内存空间
    ObjHeader *all_objects; // 是所有ObjHeader连接成的链表的头
    StringTable all_method_names; // 存放方法的所有名称, 因为从用户中读取到一个对象要调用一个方法, 这个是字符串的层面, 我们需要构建出一张符号表, 通过查找该字符在表中的index, 对应的映射到methods中index调用方法
    ObjMap *allModules; // 通过map管理名称与模块
    ObjThread *cur_thread; // vm支持多线程, cur_thread表示当前的线程(用户态下就是协程)
    // 所有内置类的类对象指针都放在这里
    Class *class_class; // 指向类的类, 是所有元类的基类和元类, 这个需要记住, class_class的元类就是他自己
    Class *object_class; // 除了元类, 是所有类的基类, object_class也是class_class的基类, object_class没有基类
    Class *string_class;
    Class *list_class;
    Class *range_class;
    Class *thread_class;
    Class *map_class;
    /* 下面三个类他们的实现与其他不同, 他们会比较简单, 也没有必要通过复杂的对象来创建 */
    Class *num_class;
    Class *null_class;
    Class *bool_class;
    } VM;
  • 创建object目录
  • 在obj_header.h中创建ObjType枚举, ObjHeader结构体, ValueType枚举, Value结构体
  •  // 对象类型
    typedef enum ObjType {
    ObjTypeList,
    ObjTypeMap,
    ObjTypeModule,
    ObjTypeString,
    ObjTypeRange,
    ObjTypeFunction,
    ObjTypeThread,
    ObjTypeClass,
    ObjTypeInstance
    } ObjType;
    typedef struct ObjHeader {
    ObjType type; // 对象类型
    bool isDark; // 是否可以到达, 如果可以到达, 则GC回收对象
    Class *class; // 指向类对象, 在类对象中保存着方法, 这样该对象就可以调用方法了:
    struct ObjHeader *next; // 用于链表
    } ObjHeader;
    // 定义的类型是直接在引用右侧写出来的
    // num, true, false这些都能在右侧直接写出来, 而不需要使用其他方法调用
    typedef enum {
    ValueTypeUndefined,
    ValueTypeNull,
    ValueTypeObj,
    ValueTypeNum,
    ValueTypeTrue, // true和false主要用于map中的开放定制法
    ValueTypeFalse
    } ValueType;
    // 它类似于Python中的引用, 在栈中定义, 所以脚本语言模拟的栈就是Value数组, 对象在堆中创建
    typedef struct Value {
    ValueType type;
    union {
    double num;
    ObjHeader *obj_header; // obj_header的实体在对象中, 这里只需要指向对象头即可
    };
    } Value;
    // 通过宏将ValueType与Value结构体直接的转换更快捷
    // 此外还要定义Value之间比较的函数
    valueIsEquals
    思路:
    Value的类型不同则false
    Value的类型相同且为数字, 则直接比较数字
    Value的类型相同都为Obj, 则比较里面的ObjHeader的类型, 如果相同则再看ObjHeader的类型是什么, 只能比较字符串, range和Class对象, 因为Class有类名属性, 就相当于比较字符串
  • 紧接着创建类对象, 创建class.h文件

 /*
好好想一下, 一个类中都有什么, 这与我们在Java和C++编程的类不同, 我们只找所有的类的共同点
1. 对象头
2. 字段个数
3. 方法对象区, 用于存方法
*/
typedef struct Class {
ObjHeader obj_header; // 类也是对象, 所以也会有ObjHeader, 但是其中的ObjHeader的class是指向元类的
ObjString name; // 类名
struct Class *superclass;
uint32_t field_num;
MethodBuffer methods; // 存储Method结构体, 主要封装了方法指针
} Class;
newVM的使用需要创建出核心模块coreModule, 并将其添加到allModules的map中
typedef num MethodType {
MethodTypeNull,
MethodPrimitive,
MethodScript,
MethodCall // 用于重载
} MethodType;
typedef struct Method {
MethodType type;
union {
// C语言实现的方法
Primitive prim_fn;
// 脚本语言将代码编译成ObjClosure对象, ObjClosure包含ObjFunc对象, ObjFunc又有指令流
ObjClosure *obj;
};
} Method;
  • 在有了类, 对象头的基础上, 紧接着创建脚本语言第一个内置对象String, 在obj_string.h中

    ```c

    // 这里仅仅是定义了字符串对象, obj_header指向是ObjString类对象
    typedef struct ObjString {
    ObjHeader obj_header;
    long hash_code; // 保存hash值
    int len;
    char *start[0];
    } ObjString;

// 在创建字符串的时候, 传入const char *s, 使用memset拷贝过来, 不要直接用, 可能会有问题

// 计算字符串的hashcode
long hash_string(const char *str, int length) {
int hashcode = xxxxxxxx;
int idx = 0;

while (idx < length) {
hashcode ^= str[idx++];
hashcode *= yyyyyyyy;
}
return hashcode;

}
// 将计算的hash值保存到ObjString对象中
void hashObjString(ObjString &objString) {
objString->hash_code = hash_string(objString->start, objString->len);
}
```

  • 有了第一个ObjString对象之后, 紧接着考虑元对象的创建, 元对象包括ObjModule和ObjInstance, ObjModule不属于任何类, 同时需要执行一个modname, 所以需要ObjString对象, 这就是为什么需要先创建ObjString对象的原因

    
    typedef struct objmodule {
    ObjHeader obj_header; // 因为mod不属于任何类, 所有它里面的ObjHeader中的cls为NULL
    StringBuffer module_names; // 与module_values的长度一样, 用于映射, 因为变量有名字和值
    ValueBuffer module_values;
    ObjString *modname;
    } ObjModule;
    typedef struct objinstance {
    ObjHeader *obj_header;
    Value fields[0]; // 存放属性的
    } ObjInstance;
  • 创建复杂的函数有关的对象, 创建obj_func.h文件

// Class对象为fnClass
typedef struct objfunc {
ObjHeader obj_header;
ByteBuffer inst_stream;
ValueBuffer constants;
int arg_num;
int upvalue_num;
int max_stack_size;
ObjModule *mod;
} ObjFunc;
typedef struct objupvalue {
ObjHeader obj_header;
Value *local_var_ptr;
Value closed_var;
struct objupvalue *next;
} ObjUpvalue;
// class对象也为fnClass
typedef struct objclosure {
ObjHeader obj_header;
ObjFunc *func;
ObjUpvalue *upvalue[0]; // 指向一个ObjUpvalue数组
} ObjClosure;
// 会让线程对象调用
typedef struct frame {
int ip;
ObjClosure *obj_closure;
Value *stack_start;
} Frame;

注意

  • Value非常的重要, 在之后函数与方法的实现都是以Value为参数和返回值得, 可以类比于Python, 但是定义一个对象的时候就不需要了, 直接一个对象上去即可, 如ObjString *objString.

其他类在后面的文章中提到

版权声明
本文为[jin_hao_chen]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/megachen/p/10383643.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领