1 函数调用链
js_new_function_def 会通过运行 JS_NewAtom 函数,来返回一个文件名 JSAtom。JSAtom定义如下:
typedef uint32_t JSAtom;
JSAtom 是 uint32_t 类型整数,用来记录关键字等字符串,作用是提高字符串内存占用和使用的效率。其取值范围如下:
enum {
JS_ATOM_TYPE_STRING = 1,
JS_ATOM_TYPE_GLOBAL_SYMBOL,
JS_ATOM_TYPE_SYMBOL,
JS_ATOM_TYPE_PRIVATE,
};
JS_NewRuntime 函数执行时会把 quickjs-atom.h 里定义的 JS 关键字比如 if、new、try等,保留字比如 class、enum、import 等,标识符 name、get、string 等加到内存中,解析中加的会按需使用。字符串转 JSAtom 是通过 __JS_NewAtom 函数来做的,__JS_NewAtom 函数会先尝试看已注册的 atom 里是否已经有对应的字符串,没有就根据定义的规则算出字符串的 hash 值 i,然后把字符串指针加到 JSAtomStruct 类型的 atom_array 里。
JS_NewAtom 函数定义如下:
JSAtom JS_NewAtom(JSContext *ctx, const char *str){
return JS_NewAtomLen(ctx, str, strlen(str));
}
JS_NewAtomLen函数定义如下:
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len){
JSValue val;
if (len == 0 || !is_digit(*str)) {
JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
if (atom)
return atom;
}
val = JS_NewStringLen(ctx, str, len);
if (JS_IsException(val))
return JS_ATOM_NULL;
return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
}
js_new_function_def 函数执行完创建了顶级函数定义后,会做个判断,如果创建失败会直接使用 goto 语法跳转到
fail1 label处,fail1 label会释放所有 JSModuleDef。
如果没有失败 __JS_EvalInternal 函数会先将 JSParseState 的当前函数设置为 js_new_function_def 创建的函数 fd。然后对 fd 进行字段的设置。使用 push_scope 生成一个作用域,后面解析的内容会放到这个作用域内。接下来就开始执行解析函数 js_parse_program,入参是 JSParseState 类型的 s,s 记录着 fd,用来作为解析内容的输入。
3 补充
补充1
__JS_NewAtom函数定义如下:
/* string case (internal). Return JS_ATOM_NULL if error. 'str' is
freed. */
static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
{
uint32_t h, h1, i;
JSAtomStruct *p;
int len;
#if 0
printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
#endif
if (atom_type < JS_ATOM_TYPE_SYMBOL) {
/* str is not NULL */
if (str->atom_type == atom_type) {
/* str is the atom, return its index */
i = js_get_atom_index(rt, str);
/* reduce string refcount and increase atom's unless constant */
if (__JS_AtomIsConst(i))
str->header.ref_count--;
return i;
}
/* try and locate an already registered atom */
len = str->len;
h = hash_string(str, atom_type);
h &= JS_ATOM_HASH_MASK;
h1 = h & (rt->atom_hash_size - 1);
i = rt->atom_hash[h1];
while (i != 0) {
p = rt->atom_array[i];
if (p->hash == h &&
p->atom_type == atom_type &&
p->len == len &&
js_string_memcmp(p, str, len) == 0) {
if (!__JS_AtomIsConst(i))
p->header.ref_count++;
goto done;
}
i = p->hash_next;
}
} else {
h1 = 0; /* avoid warning */
if (atom_type == JS_ATOM_TYPE_SYMBOL) {
h = JS_ATOM_HASH_SYMBOL;
} else {
h = JS_ATOM_HASH_PRIVATE;
atom_type = JS_ATOM_TYPE_SYMBOL;
}
}
if (rt->atom_free_index == 0) {
/* allow new atom entries */
uint32_t new_size, start;
JSAtomStruct **new_array;
/* alloc new with size progression 3/2:
4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
preallocating space for predefined atoms (at least 195).
*/
new_size = max_int(211, rt->atom_size * 3 / 2);
if (new_size > JS_ATOM_MAX)
goto fail;
/* XXX: should use realloc2 to use slack space */
new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
if (!new_array)
goto fail;
/* Note: the atom 0 is not used */
start = rt->atom_size;
if (start == 0) {
/* JS_ATOM_NULL entry */
p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
if (!p) {
js_free_rt(rt, new_array);
goto fail;
}
p->header.ref_count = 1; /* not refcounted */
p->atom_type = JS_ATOM_TYPE_SYMBOL;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
new_array[0] = p;
rt->atom_count++;
start = 1;
}
rt->atom_size = new_size;
rt->atom_array = new_array;
rt->atom_free_index = start;
for(i = start; i < new_size; i++) {
uint32_t next;
if (i == (new_size - 1))
next = 0;
else
next = i + 1;
rt->atom_array[i] = atom_set_free(next);
}
}
if (str) {
if (str->atom_type == 0) {
p = str;
p->atom_type = atom_type;
} else {
p = js_malloc_rt(rt, sizeof(JSString) +
(str->len << str->is_wide_char) +
1 - str->is_wide_char);
if (unlikely(!p))
goto fail;
p->header.ref_count = 1;
p->is_wide_char = str->is_wide_char;
p->len = str->len;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
1 - str->is_wide_char);
js_free_string(rt, str);
}
} else {
p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
if (!p)
return JS_ATOM_NULL;
p->header.ref_count = 1;
p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
p->len = 0;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
}
/* use an already free entry */
i = rt->atom_free_index;
rt->atom_free_index = atom_get_free(rt->atom_array[i]);
rt->atom_array[i] = p;
p->hash = h;
p->hash_next = i; /* atom_index */
p->atom_type = atom_type;
rt->atom_count++;
if (atom_type != JS_ATOM_TYPE_SYMBOL) {
p->hash_next = rt->atom_hash[h1];
rt->atom_hash[h1] = i;
if (unlikely(rt->atom_count >= rt->atom_count_resize))
JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
}
// JS_DumpAtoms(rt);
return i;
fail:
i = JS_ATOM_NULL;
done:
if (str)
js_free_string(rt, str);
return i;
}