面向對象的腳本語言的類的實現


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文件

```c
    /*
好好想一下, 一個類中都有什么, 這與我們在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中

    
    // 這里僅僅是定義了字符串對象, 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.

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM