diff --git a/internal/engine.cm b/internal/engine.cm index 3a34f204..5620f4ba 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -162,6 +162,10 @@ globalThis.log = function(name, args) { } } +log.console( + 'hello' +) + function disrupt(err) { if (is_function(err.toString)) { diff --git a/source/quickjs.c b/source/quickjs.c index 4ff4f19f..417d13cf 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -367,6 +367,7 @@ typedef enum { JS_GC_OBJ_TYPE_VAR_REF, JS_GC_OBJ_TYPE_JS_CONTEXT, JS_GC_OBJ_TYPE_ARRAY, + JS_GC_OBJ_TYPE_FUNCTION, } JSGCObjectTypeEnum; /* header for GC objects. GC objects are C data structures with a @@ -679,11 +680,37 @@ typedef struct JSRecord { } JSRecord; -typedef struct JSFunction { +typedef enum { + JS_FUNC_KIND_C, + JS_FUNC_KIND_BYTECODE, + JS_FUNC_KIND_BOUND, + JS_FUNC_KIND_C_DATA, +} JSFunctionKind; +typedef struct JSFunction { + JSGCObjectHeader header; /* must come first */ + JSAtom name; + uint8_t length; + uint8_t kind; + uint8_t free_mark : 1; + union { + struct { + JSContext *realm; + JSCFunctionType c_function; + uint8_t cproto; + int16_t magic; + } cfunc; + struct { + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + } func; + struct JSBoundFunction *bound_function; + struct JSCFunctionDataRecord *c_function_data_record; + } u; } JSFunction; #define JS_VALUE_GET_ARRAY(v) ((JSArray *)JS_VALUE_GET_PTR(v)) +#define JS_VALUE_GET_FUNCTION(v) ((JSFunction *)JS_VALUE_GET_PTR(v)) typedef struct JSClosureVar { uint8_t is_local : 1; @@ -1021,6 +1048,16 @@ static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); static void js_regexp_finalizer(JSRuntime *rt, JSValue val); +static JSValue js_new_function(JSContext *ctx, JSFunctionKind kind); +static void free_function(JSRuntime *rt, JSFunction *func); +static void mark_function_children(JSRuntime *rt, JSFunction *func, JS_MarkFunc *mark_func); +static void mark_function_children_decref(JSRuntime *rt, JSFunction *func); +#ifdef RC_TRACE +static void gc_decref_child_dbg(JSRuntime *rt, JSGCObjectHeader *parent, + const char *edge, JSAtom atom, int prop_index, + JSGCObjectHeader *child, + const char *file, int line); +#endif int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val); @@ -4907,25 +4944,19 @@ static BOOL js_class_has_bytecode(JSClassID class_id) /* Check if a value is a function that uses proxy-call syntax (fn.a -> fn("a")) */ static BOOL js_is_proxy_callable(JSValueConst v) { - JSObject *p; - if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(v); - return p->class_id == JS_CLASS_BYTECODE_FUNCTION || - p->class_id == JS_CLASS_C_FUNCTION || - p->class_id == JS_CLASS_C_FUNCTION_DATA; + return JS_VALUE_GET_TAG(v) == JS_TAG_FUNCTION; } /* return NULL without exception if not a function or no bytecode */ static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) { - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + JSFunction *f; + if (JS_VALUE_GET_TAG(val) != JS_TAG_FUNCTION) return NULL; - p = JS_VALUE_GET_OBJ(val); - if (!js_class_has_bytecode(p->class_id)) + f = JS_VALUE_GET_FUNCTION(val); + if (f->kind != JS_FUNC_KIND_BYTECODE) return NULL; - return p->u.func.function_bytecode; + return f->u.func.function_bytecode; } static JSValue js_get_function_name(JSContext *ctx, JSAtom name) @@ -4963,18 +4994,20 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, JSValueConst proto_val) { JSValue func_obj; - JSObject *p; + JSFunction *f; JSAtom name_atom; - func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + (void)proto_val; /* unused - intrinsic functions have no prototype */ + + func_obj = js_new_function(ctx, JS_FUNC_KIND_C); if (JS_IsException(func_obj)) return func_obj; - p = JS_VALUE_GET_OBJ(func_obj); - p->u.cfunc.realm = JS_DupContext(ctx); - p->u.cfunc.c_function.generic = func; - p->u.cfunc.length = length; - p->u.cfunc.cproto = cproto; - p->u.cfunc.magic = magic; + f = JS_VALUE_GET_FUNCTION(func_obj); + f->u.cfunc.realm = JS_DupContext(ctx); + f->u.cfunc.c_function.generic = func; + f->u.cfunc.cproto = cproto; + f->u.cfunc.magic = magic; + f->length = length; if (!name) name = ""; name_atom = JS_NewAtom(ctx, name); @@ -4982,8 +5015,7 @@ static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, JS_FreeValue(ctx, func_obj); return JS_EXCEPTION; } - js_function_set_properties(ctx, func_obj, name_atom, length); - JS_FreeAtom(ctx, name_atom); + f->name = name_atom; return func_obj; } @@ -5034,10 +5066,13 @@ static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst *argv, int flags) { - JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + JSFunction *f = JS_VALUE_GET_FUNCTION(func_obj); + JSCFunctionDataRecord *s = f->u.c_function_data_record; JSValueConst *arg_buf; int i; + (void)flags; /* unused */ + /* XXX: could add the function on the stack for debug */ if (unlikely(argc < s->length)) { arg_buf = alloca(sizeof(arg_buf[0]) * s->length); @@ -5058,10 +5093,10 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, { JSCFunctionDataRecord *s; JSValue func_obj; + JSFunction *f; int i; - func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, - JS_CLASS_C_FUNCTION_DATA); + func_obj = js_new_function(ctx, JS_FUNC_KIND_C_DATA); if (JS_IsException(func_obj)) return func_obj; s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); @@ -5075,9 +5110,10 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, s->magic = magic; for(i = 0; i < data_len; i++) s->data[i] = JS_DupValue(ctx, data[i]); - JS_SetOpaque(func_obj, s); - js_function_set_properties(ctx, func_obj, - JS_ATOM_empty_string, length); + f = JS_VALUE_GET_FUNCTION(func_obj); + f->u.c_function_data_record = s; + f->length = length; + f->name = JS_ATOM_empty_string; return func_obj; } @@ -5258,6 +5294,187 @@ static void js_c_function_mark(JSRuntime *rt, JSValueConst val, mark_func(rt, &p->u.cfunc.realm->header); } +/* Allocate intrinsic function (JS_TAG_FUNCTION) */ +static JSValue js_new_function(JSContext *ctx, JSFunctionKind kind) +{ + JSRuntime *rt = ctx->rt; + JSFunction *func = js_mallocz(ctx, sizeof(JSFunction)); + if (!func) + return JS_EXCEPTION; + func->header.ref_count = 1; + func->kind = kind; + func->name = JS_ATOM_NULL; + func->length = 0; + func->free_mark = 0; + add_gc_object(rt, &func->header, JS_GC_OBJ_TYPE_FUNCTION); + return JS_MKPTR(JS_TAG_FUNCTION, func); +} + +/* Free intrinsic function (JS_TAG_FUNCTION) */ +static void free_function(JSRuntime *rt, JSFunction *func) +{ + assert(func->header.gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION); + + func->free_mark = 1; /* used to tell the function is invalid when + freeing cycles */ + + JS_FreeAtomRT(rt, func->name); + + switch (func->kind) { + case JS_FUNC_KIND_C: + if (func->u.cfunc.realm) + JS_FreeContext(func->u.cfunc.realm); + break; + case JS_FUNC_KIND_BYTECODE: + if (func->u.func.function_bytecode) { + JSFunctionBytecode *b = func->u.func.function_bytecode; + JSVarRef **var_refs = func->u.func.var_refs; + if (var_refs) { + for (int i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } + break; + case JS_FUNC_KIND_BOUND: + if (func->u.bound_function) { + JSBoundFunction *bf = func->u.bound_function; + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for (int i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); + } + break; + case JS_FUNC_KIND_C_DATA: + if (func->u.c_function_data_record) { + JSCFunctionDataRecord *s = func->u.c_function_data_record; + for (int i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } + break; + default: + break; + } + + func->u.c_function_data_record = NULL; + func->length = 0; + func->name = JS_ATOM_NULL; + + remove_gc_object(&func->header); + if (func->header.ref_count == 0) + js_free_rt(rt, func); + else + list_add_tail(&func->header.link, &rt->gc_zero_ref_count_list); +} + +static void mark_function_children(JSRuntime *rt, JSFunction *func, + JS_MarkFunc *mark_func) +{ + switch (func->kind) { + case JS_FUNC_KIND_C: + if (func->u.cfunc.realm) + mark_func(rt, &func->u.cfunc.realm->header); + break; + case JS_FUNC_KIND_BYTECODE: + if (func->u.func.function_bytecode) { + JSFunctionBytecode *b = func->u.func.function_bytecode; + if (func->u.func.var_refs) { + for (int i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = func->u.func.var_refs[i]; + if (var_ref) { + mark_func(rt, &var_ref->header); + } + } + } + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } + break; + case JS_FUNC_KIND_BOUND: + if (func->u.bound_function) { + JSBoundFunction *bf = func->u.bound_function; + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for (int i = 0; i < bf->argc; i++) { + JS_MarkValue(rt, bf->argv[i], mark_func); + } + } + break; + case JS_FUNC_KIND_C_DATA: + if (func->u.c_function_data_record) { + JSCFunctionDataRecord *s = func->u.c_function_data_record; + for (int i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } + break; + default: + break; + } +} + +static void mark_function_children_decref(JSRuntime *rt, JSFunction *func) +{ + switch (func->kind) { + case JS_FUNC_KIND_C: + if (func->u.cfunc.realm) { +#ifdef RC_TRACE + gc_decref_child_dbg(rt, &func->header, "func.realm", 0, -1, + &func->u.cfunc.realm->header, + __FILE__, __LINE__); +#else + gc_decref_child(rt, &func->u.cfunc.realm->header); +#endif + } + break; + case JS_FUNC_KIND_BYTECODE: + if (func->u.func.function_bytecode) { + JSFunctionBytecode *b = func->u.func.function_bytecode; + if (func->u.func.var_refs) { + for (int i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = func->u.func.var_refs[i]; + if (var_ref) { +#ifdef RC_TRACE + gc_decref_child_dbg(rt, &func->header, "func.var_ref", + 0, i, &var_ref->header, + __FILE__, __LINE__); +#else + gc_decref_child(rt, &var_ref->header); +#endif + } + } + } + JS_MarkValueEdge(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), &func->header, + "func.bytecode"); + } + break; + case JS_FUNC_KIND_BOUND: + if (func->u.bound_function) { + JSBoundFunction *bf = func->u.bound_function; + JS_MarkValueEdge(rt, bf->func_obj, &func->header, "func.bound.func_obj"); + JS_MarkValueEdge(rt, bf->this_val, &func->header, "func.bound.this_val"); + for (int i = 0; i < bf->argc; i++) { + JS_MarkValueEdge(rt, bf->argv[i], &func->header, "func.bound.argv"); + } + } + break; + case JS_FUNC_KIND_C_DATA: + if (func->u.c_function_data_record) { + JSCFunctionDataRecord *s = func->u.c_function_data_record; + for (int i = 0; i < s->data_len; i++) { + JS_MarkValueEdge(rt, s->data[i], &func->header, "func.cdata"); + } + } + break; + default: + break; + } +} + static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -5396,6 +5613,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) case JS_GC_OBJ_TYPE_ARRAY: free_array(rt, (JSArray *)gp); break; + case JS_GC_OBJ_TYPE_FUNCTION: + free_function(rt, (JSFunction *)gp); + break; default: abort(); } @@ -5461,6 +5681,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) case JS_TAG_ARRAY: case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: + case JS_TAG_FUNCTION: { JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { @@ -5511,6 +5732,7 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) case JS_TAG_ARRAY: case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: + case JS_TAG_FUNCTION: mark_func(rt, JS_VALUE_GET_PTR(val)); break; default: @@ -5580,6 +5802,12 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, mark_func(rt, &b->realm->header); } break; + case JS_GC_OBJ_TYPE_FUNCTION: + { + JSFunction *func = (JSFunction *)gp; + mark_function_children(rt, func, mark_func); + } + break; case JS_GC_OBJ_TYPE_VAR_REF: { JSVarRef *var_ref = (JSVarRef *)gp; @@ -5690,6 +5918,7 @@ static inline void JS_MarkValueEdgeEx(JSRuntime *rt, JSValueConst val, case JS_TAG_ARRAY: case JS_TAG_OBJECT: case JS_TAG_FUNCTION_BYTECODE: + case JS_TAG_FUNCTION: { JSGCObjectHeader *child = JS_VALUE_GET_PTR(val); #ifdef RC_TRACE @@ -5808,6 +6037,12 @@ static void mark_children_decref(JSRuntime *rt, JSGCObjectHeader *gp) } } break; + case JS_GC_OBJ_TYPE_FUNCTION: + { + JSFunction *func = (JSFunction *)gp; + mark_function_children_decref(rt, func); + } + break; case JS_GC_OBJ_TYPE_VAR_REF: { JSVarRef *var_ref = (JSVarRef *)gp; @@ -5935,6 +6170,7 @@ static void gc_free_cycles(JSRuntime *rt) case JS_GC_OBJ_TYPE_JS_OBJECT: case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: case JS_GC_OBJ_TYPE_ARRAY: + case JS_GC_OBJ_TYPE_FUNCTION: #ifdef DUMP_GC_FREE if (!header_done) { printf("Freeing cycles:\n"); @@ -5957,7 +6193,8 @@ static void gc_free_cycles(JSRuntime *rt) p = list_entry(el, JSGCObjectHeader, link); assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || - p->gc_obj_type == JS_GC_OBJ_TYPE_ARRAY); + p->gc_obj_type == JS_GC_OBJ_TYPE_ARRAY || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION); js_free_rt(rt, p); } @@ -6612,23 +6849,27 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_obj, dbuf_printf(&dbuf, " at %s", str1); JS_FreeCString(ctx, func_name_str); - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b; - const char *atom_str; - int line_num1, col_num1; + if (JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_FUNCTION) { + JSFunction *fn = JS_VALUE_GET_FUNCTION(sf->cur_func); + if (fn->kind == JS_FUNC_KIND_BYTECODE) { + JSFunctionBytecode *b; + const char *atom_str; + int line_num1, col_num1; - b = p->u.func.function_bytecode; - if (b->has_debug) { - line_num1 = find_line_num(ctx, b, - sf->cur_pc - b->byte_code_buf - 1, &col_num1); - atom_str = JS_AtomToCString(ctx, b->debug.filename); - dbuf_printf(&dbuf, " (%s", - atom_str ? atom_str : ""); - JS_FreeCString(ctx, atom_str); - if (line_num1 != 0) - dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); - dbuf_putc(&dbuf, ')'); + b = fn->u.func.function_bytecode; + if (b->has_debug) { + line_num1 = find_line_num(ctx, b, + sf->cur_pc - b->byte_code_buf - 1, &col_num1); + atom_str = JS_AtomToCString(ctx, b->debug.filename); + dbuf_printf(&dbuf, " (%s", + atom_str ? atom_str : ""); + JS_FreeCString(ctx, atom_str); + if (line_num1 != 0) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); + dbuf_putc(&dbuf, ')'); + } + } else { + dbuf_printf(&dbuf, " (native)"); } } else { dbuf_printf(&dbuf, " (native)"); @@ -7953,26 +8194,19 @@ int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) { - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) - return FALSE; - p = JS_VALUE_GET_OBJ(val); - switch(p->class_id) { - case JS_CLASS_BYTECODE_FUNCTION: - return TRUE; - default: - return (ctx->rt->class_array[p->class_id].call != NULL); - } + (void)ctx; /* unused */ + return JS_VALUE_GET_TAG(val) == JS_TAG_FUNCTION; } BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) { - JSObject *p; - if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + JSFunction *f; + (void)ctx; /* unused */ + if (JS_VALUE_GET_TAG(val) != JS_TAG_FUNCTION) return FALSE; - p = JS_VALUE_GET_OBJ(val); - if (p->class_id == JS_CLASS_C_FUNCTION) - return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + f = JS_VALUE_GET_FUNCTION(val); + if (f->kind == JS_FUNC_KIND_C) + return (f->u.cfunc.c_function.generic == func && f->u.cfunc.magic == magic); else return FALSE; } @@ -9276,6 +9510,23 @@ static void js_print_value(JSPrintValueState *s, JSValueConst val) js_putc(s, ']'); } break; + case JS_TAG_FUNCTION: + { + JSFunction *f = JS_VALUE_GET_FUNCTION(val); + js_puts(s, "[Function"); + if (f->name != JS_ATOM_NULL) { + js_putc(s, ' '); + js_print_atom(s, f->name); + } else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) { + JSAtom name = f->u.func.function_bytecode->func_name; + if (name != JS_ATOM_NULL) { + js_putc(s, ' '); + js_print_atom(s, name); + } + } + js_putc(s, ']'); + } + break; case JS_TAG_OBJECT: { JSObject *p = JS_VALUE_GET_OBJ(val); @@ -10085,18 +10336,18 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj, JSVarRef **cur_var_refs, JSStackFrame *sf) { - JSObject *p; + JSFunction *f; JSVarRef **var_refs; int i; - p = JS_VALUE_GET_OBJ(func_obj); - p->u.func.function_bytecode = b; - p->u.func.var_refs = NULL; + f = JS_VALUE_GET_FUNCTION(func_obj); + f->u.func.function_bytecode = b; + f->u.func.var_refs = NULL; if (b->closure_var_count) { var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); if (!var_refs) goto fail; - p->u.func.var_refs = var_refs; + f->u.func.var_refs = var_refs; for(i = 0; i < b->closure_var_count; i++) { JSClosureVar *cv = &b->closure_var[i]; JSVarRef *var_ref; @@ -10125,10 +10376,11 @@ static JSValue js_closure(JSContext *ctx, JSValue bfunc, { JSFunctionBytecode *b; JSValue func_obj; + JSFunction *f; JSAtom name_atom; b = JS_VALUE_GET_PTR(bfunc); - func_obj = JS_NewObjectClass(ctx, JS_CLASS_BYTECODE_FUNCTION); + func_obj = js_new_function(ctx, JS_FUNC_KIND_BYTECODE); if (JS_IsException(func_obj)) { JS_FreeValue(ctx, bfunc); return JS_EXCEPTION; @@ -10138,11 +10390,12 @@ static JSValue js_closure(JSContext *ctx, JSValue bfunc, /* bfunc has been freed */ goto fail; } + f = JS_VALUE_GET_FUNCTION(func_obj); name_atom = b->func_name; if (name_atom == JS_ATOM_NULL) name_atom = JS_ATOM_empty_string; - js_function_set_properties(ctx, func_obj, name_atom, - b->defined_arg_count); + f->name = JS_DupAtom(ctx, name_atom); + f->length = b->defined_arg_count; return func_obj; fail: /* bfunc is freed when func_obj is freed */ @@ -10194,16 +10447,17 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, { JSRuntime *rt = ctx->rt; JSCFunctionType func; - JSObject *p; + JSFunction *f; JSStackFrame sf_s, *sf = &sf_s, *prev_sf; JSValue ret_val; JSValueConst *arg_buf; int arg_count, i; JSCFunctionEnum cproto; - p = JS_VALUE_GET_OBJ(func_obj); - cproto = p->u.cfunc.cproto; - arg_count = p->u.cfunc.length; + (void)flags; /* unused */ + f = JS_VALUE_GET_FUNCTION(func_obj); + cproto = f->u.cfunc.cproto; + arg_count = f->length; /* better to always check stack overflow */ if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) @@ -10212,7 +10466,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, prev_sf = rt->current_stack_frame; sf->prev_frame = prev_sf; rt->current_stack_frame = sf; - ctx = p->u.cfunc.realm; /* change the current realm */ + ctx = f->u.cfunc.realm; /* change the current realm */ sf->js_mode = 0; sf->cur_func = (JSValue)func_obj; sf->arg_count = argc; @@ -10229,7 +10483,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, } sf->arg_buf = (JSValue*)arg_buf; - func = p->u.cfunc.c_function; + func = f->u.cfunc.c_function; if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_CALL)) { js_debug dbg = {0}; @@ -10244,7 +10498,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, break; case JS_CFUNC_generic_magic: ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, - p->u.cfunc.magic); + f->u.cfunc.magic); break; case JS_CFUNC_f_f: { @@ -10292,13 +10546,15 @@ static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv, int flags) { - JSObject *p; + JSFunction *f; JSBoundFunction *bf; JSValueConst *arg_buf; int arg_count, i; - p = JS_VALUE_GET_OBJ(func_obj); - bf = p->u.bound_function; + (void)this_obj; /* unused - bound function uses bf->this_val */ + (void)flags; /* unused */ + f = JS_VALUE_GET_FUNCTION(func_obj); + bf = f->u.bound_function; arg_count = bf->argc + argc; if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) return JS_ThrowStackOverflow(ctx); @@ -10400,14 +10656,14 @@ static struct VMFrame *vm_push_frame(JSContext *ctx, const uint8_t *ret_pc, int ret_sp_offset, int call_argc, int call_has_this) { - JSObject *p; + JSFunction *f; JSFunctionBytecode *b; struct VMFrame *frame; int total_slots, i, arg_allocated_size; JSValue *stack_base, *arg_buf, *var_buf; - p = JS_VALUE_GET_OBJ(func_obj); - b = p->u.func.function_bytecode; + f = JS_VALUE_GET_FUNCTION(func_obj); + b = f->u.func.function_bytecode; /* Check frame stack capacity */ if (ctx->frame_stack_top + 1 >= ctx->frame_stack_capacity) { @@ -10467,7 +10723,7 @@ static struct VMFrame *vm_push_frame(JSContext *ctx, frame->ctx = b->realm; frame->pc = b->byte_code_buf; frame->js_mode = b->js_mode; - frame->var_refs = p->u.func.var_refs; + frame->var_refs = f->u.func.var_refs; init_list_head(&frame->var_ref_list); /* Continuation info for return */ @@ -10612,7 +10868,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSRuntime *rt = caller_ctx->rt; JSContext *ctx; - JSObject *p; + JSFunction *f; JSFunctionBytecode *b; JSStackFrame sf_s, *sf = &sf_s; const uint8_t *pc; @@ -10652,21 +10908,27 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (js_poll_interrupts(caller_ctx)) return JS_EXCEPTION; - if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_FUNCTION)) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); + } + f = JS_VALUE_GET_FUNCTION(func_obj); + switch (f->kind) { + case JS_FUNC_KIND_C: + return js_call_c_function(caller_ctx, func_obj, this_obj, argc, + (JSValueConst *)argv, flags); + case JS_FUNC_KIND_BOUND: + return js_call_bound_function(caller_ctx, func_obj, this_obj, argc, + (JSValueConst *)argv, flags); + case JS_FUNC_KIND_C_DATA: + return js_c_function_data_call(caller_ctx, func_obj, this_obj, argc, + (JSValueConst *)argv, flags); + case JS_FUNC_KIND_BYTECODE: + break; /* continue to bytecode execution below */ + default: goto not_a_function; } - p = JS_VALUE_GET_OBJ(func_obj); - if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { - JSClassCall *call_func; - call_func = rt->class_array[p->class_id].call; - if (!call_func) { - not_a_function: - return JS_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, - (JSValueConst *)argv, flags); - } - b = p->u.func.function_bytecode; + b = f->u.func.function_bytecode; #ifdef DUMP_PROFILE /* Increment function entry count */ @@ -10696,7 +10958,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sf->arg_count = argc; sf->cur_func = (JSValue)func_obj; init_list_head(&sf->var_ref_list); - var_refs = p->u.func.var_refs; + var_refs = f->u.func.var_refs; local_buf = alloca(alloca_size); if (unlikely(arg_allocated_size)) { @@ -13044,29 +13306,30 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o NULL in case of exception (case of revoked proxy only) */ static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) { - JSObject *p; + JSFunction *f; JSContext *realm; - if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_FUNCTION) return ctx; - p = JS_VALUE_GET_OBJ(func_obj); - switch(p->class_id) { - case JS_CLASS_C_FUNCTION: - realm = p->u.cfunc.realm; + f = JS_VALUE_GET_FUNCTION(func_obj); + switch(f->kind) { + case JS_FUNC_KIND_C: + realm = f->u.cfunc.realm; break; - case JS_CLASS_BYTECODE_FUNCTION: + case JS_FUNC_KIND_BYTECODE: { JSFunctionBytecode *b; - b = p->u.func.function_bytecode; + b = f->u.func.function_bytecode; realm = b->realm; } break; - case JS_CLASS_BOUND_FUNCTION: + case JS_FUNC_KIND_BOUND: { - JSBoundFunction *bf = p->u.bound_function; + JSBoundFunction *bf = f->u.bound_function; realm = JS_GetFunctionRealm(ctx, bf->func_obj); } break; + case JS_FUNC_KIND_C_DATA: default: realm = ctx; break; @@ -22859,14 +23122,14 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, eval_type = flags & JS_EVAL_TYPE_MASK; if (eval_type == JS_EVAL_TYPE_DIRECT) { - JSObject *p; + JSFunction *fn; sf = ctx->rt->current_stack_frame; assert(sf != NULL); - assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); - p = JS_VALUE_GET_OBJ(sf->cur_func); - assert(js_class_has_bytecode(p->class_id)); - b = p->u.func.function_bytecode; - var_refs = p->u.func.var_refs; + assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_FUNCTION); + fn = JS_VALUE_GET_FUNCTION(sf->cur_func); + assert(fn->kind == JS_FUNC_KIND_BYTECODE); + b = fn->u.func.function_bytecode; + var_refs = fn->u.func.var_refs; js_mode = b->js_mode; } else { sf = NULL; @@ -27297,9 +27560,9 @@ array_fail: /* Handle function - return source or native stub */ if (JS_IsFunction(ctx, arg)) { - JSObject *p = JS_VALUE_GET_OBJ(arg); - if (js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b = p->u.func.function_bytecode; + JSFunction *fn = JS_VALUE_GET_FUNCTION(arg); + if (fn->kind == JS_FUNC_KIND_BYTECODE) { + JSFunctionBytecode *b = fn->u.func.function_bytecode; if (b->has_debug && b->debug.source) return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); } @@ -27309,12 +27572,15 @@ array_fail: const char *name = ""; const char *name_cstr = NULL; - JSObject *fp = JS_VALUE_GET_OBJ(arg); - if (js_class_has_bytecode(fp->class_id)) { - JSFunctionBytecode *fb = fp->u.func.function_bytecode; + if (fn->kind == JS_FUNC_KIND_BYTECODE) { + JSFunctionBytecode *fb = fn->u.func.function_bytecode; name_cstr = JS_AtomToCString(ctx, fb->func_name); if (name_cstr) name = name_cstr; + } else if (fn->name != JS_ATOM_NULL) { + name_cstr = JS_AtomToCString(ctx, fn->name); + if (name_cstr) + name = name_cstr; } size_t plen = strlen(pref); @@ -30166,17 +30432,8 @@ static JSValue js_cell_length(JSContext *ctx, JSValueConst this_val, /* Functions return arity (accessed directly, not via properties) */ if (JS_IsFunction(ctx, val)) { - JSObject *p = JS_VALUE_GET_OBJ(val); - switch (p->class_id) { - case JS_CLASS_BYTECODE_FUNCTION: - return JS_NewInt32(ctx, p->u.func.function_bytecode->defined_arg_count); - case JS_CLASS_C_FUNCTION: - return JS_NewInt32(ctx, p->u.cfunc.length); - case JS_CLASS_C_FUNCTION_DATA: - return JS_NewInt32(ctx, p->u.c_function_data_record->length); - default: - return JS_NewInt32(ctx, 0); - } + JSFunction *f = JS_VALUE_GET_FUNCTION(val); + return JS_NewInt32(ctx, f->length); } int tag = JS_VALUE_GET_TAG(val); @@ -30667,10 +30924,15 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg) { memset(dbg, 0, sizeof(*dbg)); - if (!JS_IsObject(fn)) return; - JSObject *p = JS_VALUE_GET_OBJ(fn); + if (!JS_IsFunction(js, fn)) return; + JSFunction *f = JS_VALUE_GET_FUNCTION(fn); - const char *fn_name = get_prop_string(js, fn, JS_ATOM_name); + const char *fn_name = NULL; + if (f->name != JS_ATOM_NULL) { + fn_name = JS_AtomToCString(js, f->name); + } else if (f->kind == JS_FUNC_KIND_BYTECODE && f->u.func.function_bytecode) { + fn_name = JS_AtomToCString(js, f->u.func.function_bytecode->func_name); + } if (!fn_name) dbg->name = js_strdup(js, ""); @@ -30682,11 +30944,11 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg) JS_FreeCString(js,fn_name); } - dbg->unique = (int)p; + dbg->unique = (int)(uintptr_t)f; - switch(p->class_id) { - case JS_CLASS_BYTECODE_FUNCTION: { - JSFunctionBytecode *b = p->u.func.function_bytecode; + switch(f->kind) { + case JS_FUNC_KIND_BYTECODE: { + JSFunctionBytecode *b = f->u.func.function_bytecode; // get filename const char *filename = JS_AtomToCString(js, b->debug.filename); if (!filename) @@ -30709,15 +30971,18 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg) dbg->srclen = b->debug.source_len; break; } - case JS_CLASS_C_FUNCTION: + case JS_FUNC_KIND_C: + case JS_FUNC_KIND_C_DATA: dbg->filename = js_strdup(js, ""); dbg->what = "C"; - dbg->nparams = 0; + dbg->nparams = f->length; dbg->vararg = 1; dbg->line = 0; dbg->source = CSTR; dbg->srclen = STRLEN(CSTR); break; + default: + break; } } @@ -30761,7 +31026,7 @@ JSValue js_debugger_build_backtrace(JSContext *ctx, const uint8_t *cur_pc) { JSStackFrame *sf; const char *func_name_str; - JSObject *p; + JSFunction *f; JSValue ret = JS_NewArray(ctx); uint32_t stack_index = 0; @@ -30778,19 +31043,23 @@ JSValue js_debugger_build_backtrace(JSContext *ctx, const uint8_t *cur_pc) JS_SetPropertyStr(ctx, current_frame, "name", JS_NewString(ctx, func_name_str)); JS_FreeCString(ctx, func_name_str); - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (p && js_class_has_bytecode(p->class_id)) { - JSFunctionBytecode *b; - int line_num1; + if (JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_FUNCTION) { + f = JS_VALUE_GET_FUNCTION(sf->cur_func); + if (f->kind == JS_FUNC_KIND_BYTECODE) { + JSFunctionBytecode *b; + int line_num1; - b = p->u.func.function_bytecode; - if (b->has_debug) { - const uint8_t *pc = sf != ctx->rt->current_stack_frame || !cur_pc ? sf->cur_pc : cur_pc; - int col_num; - line_num1 = find_line_num(ctx, b, pc - b->byte_code_buf - 1, &col_num); - JS_SetPropertyStr(ctx, current_frame, "filename", JS_AtomToString(ctx, b->debug.filename)); - if (line_num1 != -1) - JS_SetPropertyStr(ctx, current_frame, "line", JS_NewUint32(ctx, line_num1)); + b = f->u.func.function_bytecode; + if (b->has_debug) { + const uint8_t *pc = sf != ctx->rt->current_stack_frame || !cur_pc ? sf->cur_pc : cur_pc; + int col_num; + line_num1 = find_line_num(ctx, b, pc - b->byte_code_buf - 1, &col_num); + JS_SetPropertyStr(ctx, current_frame, "filename", JS_AtomToString(ctx, b->debug.filename)); + if (line_num1 != -1) + JS_SetPropertyStr(ctx, current_frame, "line", JS_NewUint32(ctx, line_num1)); + } + } else { + JS_SetPropertyStr(ctx, current_frame, "name", JS_NewString(ctx, "(native)")); } } else { JS_SetPropertyStr(ctx, current_frame, "name", JS_NewString(ctx, "(native)")); diff --git a/source/quickjs.h b/source/quickjs.h index 7ffec94d..3885a185 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -79,6 +79,7 @@ enum { JS_TAG_STRING = -8, JS_TAG_STRING_ROPE = -7, JS_TAG_ARRAY = -6, /* intrinsic array type */ + JS_TAG_FUNCTION = -5, /* intrinsic function type */ JS_TAG_MODULE = -3, /* used internally */ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */ JS_TAG_OBJECT = -1,