当前位置: 首页 > 工具软件 > QuickJS > 使用案例 >

7.1 QuickJS核心代码流程-新建Runtime

楚翰
2023-12-01

1 前言

quickjs.c有五万多行代码。我们可以通过QuickJS解析执行JS代码的过程出发来分析QuickJS源码:

JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
js_std_add_helpers(ctx, 0, NULL);

const char *scripts = "console.log('hello quickjs')";
JS_Eval(ctx, scripts, strlen(scripts), "main", 0);

上面代码中rt和ctx是先构造一个JS运行时和上下文环境,js_std_add_helpers是调用C的std方法帮助在控制台输出调试信息。

2 JSRuntime结构体介绍

JS_NewRuntime函数会新建一个JSRuntime *rt。结构体JSRuntime定义如下:

struct JSRuntime {
	//内存分配函数
    JSMallocFunctions mf;
    //定义了内存分配的一系列信息
    JSMallocState malloc_state;
    const char *rt_info;

    int atom_hash_size; /* power of two */
    int atom_count;
    int atom_size;
    int atom_count_resize; /* resize hash table at this count */
    uint32_t *atom_hash;
    JSAtomStruct **atom_array;
    int atom_free_index; /* 0 = none */

    int class_count;    /* size of class_array */
    JSClass *class_array;

    struct list_head context_list; /* list of JSContext.link */
    /* list of JSGCObjectHeader.link. List of allocated GC objects (used
       by the garbage collector) */
    struct list_head gc_obj_list;
    /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
    struct list_head gc_zero_ref_count_list; 
    struct list_head tmp_obj_list; /* used during GC */
    JSGCPhaseEnum gc_phase : 8;
    size_t malloc_gc_threshold;
#ifdef DUMP_LEAKS
    struct list_head string_list; /* list of JSString.link */
#endif
    /* stack limitation */
    uintptr_t stack_size; /* in bytes, 0 if no limit */
    uintptr_t stack_top;
    uintptr_t stack_limit; /* lower stack limit */
    
    JSValue current_exception;
    /* true if inside an out of memory error, to avoid recursing */
    BOOL in_out_of_memory : 8;

    struct JSStackFrame *current_stack_frame;

    JSInterruptHandler *interrupt_handler;
    void *interrupt_opaque;

    JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
    void *host_promise_rejection_tracker_opaque;
    
    struct list_head job_list; /* list of JSJobEntry.link */

    JSModuleNormalizeFunc *module_normalize_func;
    JSModuleLoaderFunc *module_loader_func;
    void *module_loader_opaque;

    BOOL can_block : 8; /* TRUE if Atomics.wait can block */
    /* used to allocate, free and clone SharedArrayBuffers */
    JSSharedArrayBufferFunctions sab_funcs;
    
    /* Shape hash table */
    int shape_hash_bits;
    int shape_hash_size;
    int shape_hash_count; /* number of hashed shapes */
    JSShape **shape_hash;
#ifdef CONFIG_BIGNUM
    bf_context_t bf_ctx;
    JSNumericOperations bigint_ops;
    JSNumericOperations bigfloat_ops;
    JSNumericOperations bigdecimal_ops;
    uint32_t operator_count;
#endif
    void *user_opaque;
};

JSMallocState结构体定义如下:

typedef struct JSMallocState {
    size_t malloc_count;
    size_t malloc_size;
    size_t malloc_limit;
    void *opaque; /* user opaque */
} JSMallocState;

JSMallocFunctions结构体定义如下:

typedef struct JSMallocFunctions {
    void *(*js_malloc)(JSMallocState *s, size_t size);
    void (*js_free)(JSMallocState *s, void *ptr);
    void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
    size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;

其中,包含如下内容:

  • 原子大小int atom_size和原子结构数组指针JSAtomStruct **atom_array
  • 记录类的数组JSClass *class_array
  • 用于GC的一些链表head
  • 栈头uintptr_t stack_top
  • 栈空间大小uintptr_t stack_size(大小以字节进行衡量)
  • 避免重复出现内存超出错误的BOOL in_out_of_memory : 8
  • 当前栈帧struct JSStackFrame *current_stack_frame
  • 中断处理JSInterruptHandler *interrupt_handler
  • module读取函数JSModuleLoaderFunc *module_loader_func
  • 用于分配、释放和克隆SharedArrayBuffers的JSSharedArrayBufferFunctions sab_funcs
  • Shape的哈希表JSShape **shape_hash

3 JS_NewRuntime函数介绍

其函数主体如下:

JSRuntime *JS_NewRuntime(void){
    return JS_NewRuntime2(&def_malloc_funcs, NULL);
}

JS_NewRuntime2函数定义如下:

JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque){
    JSRuntime *rt;
    JSMallocState ms;

    memset(&ms, 0, sizeof(ms));
    ms.opaque = opaque;
    ms.malloc_limit = -1;

    rt = mf->js_malloc(&ms, sizeof(JSRuntime));
    if (!rt)
        return NULL;
    memset(rt, 0, sizeof(*rt));
    rt->mf = *mf;
    if (!rt->mf.js_malloc_usable_size) {
        /* use dummy function if none provided */
        rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
    }
    rt->malloc_state = ms;
    rt->malloc_gc_threshold = 256 * 1024;

#ifdef CONFIG_BIGNUM
    bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
    set_dummy_numeric_ops(&rt->bigint_ops);
    set_dummy_numeric_ops(&rt->bigfloat_ops);
    set_dummy_numeric_ops(&rt->bigdecimal_ops);
#endif

    init_list_head(&rt->context_list);
    init_list_head(&rt->gc_obj_list);
    init_list_head(&rt->gc_zero_ref_count_list);
    rt->gc_phase = JS_GC_PHASE_NONE;
    
#ifdef DUMP_LEAKS
    init_list_head(&rt->string_list);
#endif
    init_list_head(&rt->job_list);

    if (JS_InitAtoms(rt))
        goto fail;

    /* create the object, array and function classes */
    if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
                         countof(js_std_class_def)) < 0)
        goto fail;
    rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
    rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
    rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;

    rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
    rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
    rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
    rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
    if (init_shape_hash(rt))
        goto fail;

    rt->stack_size = JS_DEFAULT_STACK_SIZE;
    JS_UpdateStackTop(rt);

    rt->current_exception = JS_NULL;

    return rt;
 fail:
    JS_FreeRuntime(rt);
    return NULL;
}

JS_NewRuntime2 函数对 JSRuntime 和 JSMallocState 初始化使用的是 memset 函数,这个函数一般是对比较大的结构体进行初始化,因为是直接操作内存,所以很快。

JS_NewRuntime2 函数使用 JSMallocFunctions 结构体来记录 JSMallocState 分配内存状态(?)。

JSRuntime2 里用 context_list 记录所有的上下文,gc_obj_list 记录分配的 GC 对象,在调用 JS_FreeValueRT 函数时如果 JSValue 的 tag 是字节码,同时不在 GC 清理周期内时,会将 GC 对象加到 gc_zero_ref_count_list 里。

JS_NewRuntime函数最后会用init_class_range函数创建对象、数组和函数类,每个类新建用的是JS_NewClass1函数,函数定义如下:

static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, int start, int count){
    JSClassDef cm_s, *cm = &cm_s;
    int i, class_id;

    for(i = 0; i < count; i++) {
        class_id = i + start;
        memset(cm, 0, sizeof(*cm));
        cm->finalizer = tab[i].finalizer;
        cm->gc_mark = tab[i].gc_mark;
        if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
            return -1;
    }
    return 0;
}

3 malloc系列函数解析

/* Throw out of memory in case of error */
void *js_malloc(JSContext *ctx, size_t size)
{
    void *ptr;
    ptr = js_malloc_rt(ctx->rt, size);
    if (unlikely(!ptr)) {
        JS_ThrowOutOfMemory(ctx);
        return NULL;
    }
    return ptr;
}
void *js_malloc_rt(JSRuntime *rt, size_t size){
    return rt->mf.js_malloc(&rt->malloc_state, size);
}
 类似资料: