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

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.

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

原文地址:https://www.cnblogs.com/megachen/p/10383643.html

时间: 2024-08-11 04:24:26

面向对象的脚本语言的类的实现的相关文章

Cocos2d-x 脚本语言Lua中的面向对象

Cocos2d-x 脚本语言Lua中的面向对象 面向对象不是针对某一门语言,而是一种思想.在面向过程的语言也能够使用面向对象的思想来进行编程. 在Lua中,并没有面向对象的概念存在,没有类的定义和子类的定义.但相同在Lua中能够利用面向对象的思想来实现面向对象的类继承. 一.复制表的方式面向对象 --Lua中的面向对象 --[[ 复制表方式面向对象 參数为一张表.通过遍历这张表取值,赋给一张空表,最后返回新建的表.来达到克隆表 ]] function clone(tab) local ins =

Ruby(面向对象程序设计的脚本语言)入门

Ruby是一种为简单快捷的面向对象编程(面向对象程序设计)而创的脚本语言. 简介 Ruby 是开源的,在Web上免费提供,但需要一个许可证. Ruby 是一种通用的.解释的编程语言. Ruby 是一种真正的面向对象编程语言. Ruby 是一种类似于 Python 和 Perl 的服务器端脚本语言. Ruby 可以用来编写通用网关接口(CGI)脚本. Ruby 可以被嵌入到超文本标记语言(HTML). Ruby 语法简单,这使得新的开发人员能够快速轻松地学习 Ruby. Ruby 与 C++ 和

java脚本语言学习心得

第一篇技术博客,一定要认真! 第一篇技术博客,一定要认真! 第一篇技术博客,一定要认真! 好了,进入正题: 一 什么是脚本语言? 程序的运行方式有两种:编译运行和解释运行 1.1 前者的典型代表是java, 从文件角度看分为三步: write[编写]: a.java文件(拿个记事本就能写,扩展名是.java), compile[编译]: 编译(cmd命令是java a.java,ide集成了编译器运行之前自动编译)之后产生了a.class文件(是一堆二进制码,人看不懂,是给虚拟机看的) 运行[r

[Java面试九]脚本语言知识总结.

核心内容概述 1.JavaScript加强,涉及到ECMAScript语法.BOM对象.DOM对象以及事件. 2.Ajax传统编程. 3.jQuery框架,九种选择器为核心学习内容 4.JQuery UI插件 5.jQuery Ajax编程 6.jQuery第三方插件 7.反向Ajax编程(彗星) 一.JavaScript基础加强 JavaScript是在浏览器内容运行,无需编译.解释执行动态脚本语言,是一种弱类型语言,所有变量使用var定义. JavaScript的3个组成部分分别为:核心(E

面向对象程序设计及语言

面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术到一定阶段后的产物.如今,面向对象的概念和应用已超越了程序设计和软件开发,扩展到很宽的范围.如数据库系统.交互式界面.应用结构.应用平台.分布式系统.网络管理结构,CAD技术.人工智能等领域. 面向对象语言借鉴了20世纪50年代的人工智能语言LISP,引入动态绑定的概念和交互式开发环境的思想.始于20世纪60年代的离散事件模拟语言SIMULA67,引入了类的要领和继承,成型于20世纪70年代的Smalltalk. 面向对象语言发展的两个方

脚本语言:Xmas(二)

本篇,来谈谈类型系统,以及部分与垃圾收集器相关的内容. 一.基本类型 Xmas的基本类型:Null.Boolean.Label.String.Ref.Function.Integer.Float.Decimal.Array.List.Set.Map.Object:14个,相对于其他的脚本语言是有些多的:但,这些类型都是"原子的",少了其中一种,多少会有些不方便(但并非不完备,如Decimal可以代替所有算数类型:Map则可以可以替代所有容器类型).如果,你看过上一篇,会发现有那么一点点

主流脚本语言的比较和选择

主流脚本语言的比较和选择 —— Hywhy 过去这一年的时间里,我买了不少书,查了很多资料,可以算是认真的学习了几种主流的脚本 语言,因为我一直想搞一个好用的自动化工具,来方便我们的系统维护.虽然这个愿望还没有达成,但是在这个过程中,还是学到了很多东西.今天下午,跟同事们 聊天时,说到了脚本语言,这是比较难得的,也正好借这个契机,把我的一些看法说一说,有不妥和错误的地方,请大家多指正. 为什么选择脚本语言 为什么选择脚本语言,可能每个人面对的实际情况都不一样.语言本身没有好坏之分,只有合适或者不

如何在Java平台上使用脚本语言做Java开发

如何在Java平台上使用脚本语言做Java开发     最近开始流行区分Java平台和Java语言,但很多Java开发者还是不能确定如何在 Java应用程序开发中结合脚本.本篇文章,Gregor Roth给出了在Java平台上使用脚本的方法.通过这篇文章,你可以了解怎样在你的Java应用程序中使用脚本,是否你要通过使用Groovy和 Jython把不同的Java应用程序模块粘合在一起,或者写一个你自己的基于JRuby的应用程序,适用于Java平台. 作为一个Java开发者,你可能已经注意到了,J

L脚本语言语法手册

0.02版 赵亮 简  介 L脚本语言是一个轻量级的,旨在接近自然语言的编程语言,目前支持在中文.英文基础上的编程.并可扩展为任意语种.L脚本语言的语法结构简单,程序结构相对松散,易学易用. 目前L脚本语言仍处于开发初期,功能尚不完善.目前尚未提供源码编辑器,建议使用notepad++或者ultraedit进行源码编辑. 解释器引擎CSDN资源下载地址   http://download.csdn.net/detail/itmes/8656133 目录 一.         注释.. 2 二.