diff --git a/source/quickjs.c b/source/quickjs.c index b71405a7..40d92729 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -1885,9 +1885,7 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); JSValue JS_ThrowOutOfMemory (JSContext *ctx); -static JSVarRef *get_var_ref (JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg); static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx); -static void free_var_ref (JSRuntime *rt, JSVarRef *var_ref); static int js_string_compare (JSContext *ctx, const JSText *p1, const JSText *p2); static JSValue JS_ToNumber (JSContext *ctx, JSValue val); @@ -6794,81 +6792,20 @@ static JSValue JS_GetActiveFunction (JSContext *ctx) { return ctx->rt->current_stack_frame->cur_func; } -static JSVarRef *get_var_ref (JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg) { - JSVarRef *var_ref; - struct list_head *el; - JSValue *pvalue; - - if (is_arg) - pvalue = &sf->arg_buf[var_idx]; - else - pvalue = &sf->var_buf[var_idx]; - - list_for_each (el, &sf->var_ref_list) { - var_ref = list_entry (el, JSVarRef, var_ref_link); - if (var_ref->pvalue == pvalue) { - /* With copying GC, no need to increment ref_count */ - return var_ref; - } - } - /* create a new one - use regular heap, not GC heap, to avoid pointer invalidation */ - var_ref = pjs_mallocz (sizeof (JSVarRef)); - if (!var_ref) return NULL; - var_ref->is_detached = FALSE; - list_add_tail (&var_ref->var_ref_link, &sf->var_ref_list); - var_ref->pvalue = pvalue; - return var_ref; -} - -static JSValue js_closure2 (JSContext *ctx, JSValue func_obj, JSFunctionBytecode *b, JSVarRef **cur_var_refs, JSStackFrame *sf) { +static JSValue js_closure2 (JSContext *ctx, JSValue func_obj, JSFunctionBytecode *b, JSStackFrame *sf) { JSFunction *f; - JSVarRef **var_refs; - JSGCRef func_obj_ref; - int i; - f = JS_VALUE_GET_FUNCTION (func_obj); f->u.func.function_bytecode = b; f->u.func.var_refs = NULL; - f->u.func.outer_frame = JS_NULL; /* Will be set after GC-safe operations */ - /* Also populate var_refs for backward compatibility with existing bytecode - that uses OP_get_var_ref/OP_put_var_ref opcodes. */ - if (b->closure_var_count) { - /* Use pjs_mallocz for var_refs - regular heap, not GC-managed */ - var_refs = pjs_mallocz(sizeof(var_refs[0]) * b->closure_var_count); - if (!var_refs) goto fail; - 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; - if (cv->is_local) { - /* reuse the existing variable reference if it already exists */ - /* Protect func_obj from GC during get_var_ref */ - JS_PUSH_VALUE(ctx, func_obj); - var_ref = get_var_ref (ctx, sf, cv->var_idx, cv->is_arg); - JS_POP_VALUE(ctx, func_obj); - if (!var_ref) goto fail; - /* Re-fetch f and var_refs after potential GC */ - f = JS_VALUE_GET_FUNCTION (func_obj); - var_refs = f->u.func.var_refs; - } else { - var_ref = cur_var_refs[cv->var_idx]; - } - var_refs[i] = var_ref; - } - } - - /* Set outer_frame AFTER all GC-triggering operations. - sf->js_frame may have been updated by GC, so we read it here. */ - f = JS_VALUE_GET_FUNCTION (func_obj); + /* Set outer_frame to parent's JSFrame for the new closure model. + This allows OP_get_up/OP_set_up to access captured variables via frame chain. */ f->u.func.outer_frame = sf ? sf->js_frame : JS_NULL; return func_obj; -fail: - return JS_EXCEPTION; } -static JSValue js_closure (JSContext *ctx, JSValue bfunc, JSVarRef **cur_var_refs, JSStackFrame *sf) { +static JSValue js_closure (JSContext *ctx, JSValue bfunc, JSStackFrame *sf) { JSFunctionBytecode *b; JSValue func_obj; JSFunction *f; @@ -6886,7 +6823,7 @@ static JSValue js_closure (JSContext *ctx, JSValue bfunc, JSVarRef **cur_var_ref JS_POP_VALUE (ctx, bfunc); b = JS_VALUE_GET_PTR (bfunc); - func_obj = js_closure2 (ctx, func_obj, b, cur_var_refs, sf); + func_obj = js_closure2 (ctx, func_obj, b, sf); if (JS_IsException (func_obj)) { /* bfunc has been freed */ goto fail; @@ -6902,38 +6839,6 @@ fail: return JS_EXCEPTION; } -static void close_var_refs (JSRuntime *rt, JSStackFrame *sf) { - struct list_head *el, *el1; - JSVarRef *var_ref; - - list_for_each_safe (el, el1, &sf->var_ref_list) { - var_ref = list_entry (el, JSVarRef, var_ref_link); - /* no need to unlink var_ref->var_ref_link as the list is never used - * afterwards */ - var_ref->value = *var_ref->pvalue; - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - } -} - -static void close_lexical_var (JSContext *ctx, JSStackFrame *sf, int var_idx) { - JSValue *pvalue; - struct list_head *el, *el1; - JSVarRef *var_ref; - - pvalue = &sf->var_buf[var_idx]; - list_for_each_safe (el, el1, &sf->var_ref_list) { - var_ref = list_entry (el, JSVarRef, var_ref_link); - if (var_ref->pvalue == pvalue) { - list_del (&var_ref->var_ref_link); - var_ref->value = *var_ref->pvalue; - var_ref->pvalue = &var_ref->value; - /* the reference is no longer to a local variable */ - var_ref->is_detached = TRUE; - } - } -} #define JS_CALL_FLAG_COPY_ARGV (1 << 1) @@ -7079,7 +6984,6 @@ static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj, JSValue const uint8_t *pc; int opcode, arg_allocated_size, i; JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; - JSVarRef **var_refs; size_t alloca_size; #if !DIRECT_DISPATCH @@ -7157,8 +7061,6 @@ static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj, JSValue sf->js_mode = b->js_mode; sf->arg_count = argc; sf->cur_func = (JSValue)func_obj; - init_list_head (&sf->var_ref_list); - var_refs = f->u.func.var_refs; /* Allocate JSFrame for args + vars on regular heap (stable pointers). This enables the outer_frame closure model. */ @@ -7236,7 +7138,7 @@ restart: CASE (OP_push_const8) : *sp++ = b->cpool[*pc++]; BREAK; CASE (OP_fclosure8) - : *sp++ = js_closure (ctx, b->cpool[*pc++], var_refs, sf); + : *sp++ = js_closure (ctx, b->cpool[*pc++], sf); if (unlikely (JS_IsException (sp[-1]))) goto exception; BREAK; CASE (OP_push_empty_string) : *sp++ = JS_KEY_empty; @@ -7430,7 +7332,7 @@ restart: CASE (OP_fclosure) : { JSValue bfunc = b->cpool[get_u32 (pc)]; pc += 4; - *sp++ = js_closure (ctx, bfunc, var_refs, sf); + *sp++ = js_closure (ctx, bfunc, sf); if (unlikely (JS_IsException (sp[-1]))) goto exception; } BREAK; @@ -7746,98 +7648,32 @@ restart: CASE (OP_set_arg3) : set_value (ctx, &arg_buf[3], sp[-1]); BREAK; - CASE (OP_get_var_ref0) : *sp++ = *var_refs[0]->pvalue; - BREAK; - CASE (OP_get_var_ref1) : *sp++ = *var_refs[1]->pvalue; - BREAK; - CASE (OP_get_var_ref2) : *sp++ = *var_refs[2]->pvalue; - BREAK; - CASE (OP_get_var_ref3) : *sp++ = *var_refs[3]->pvalue; - BREAK; - CASE (OP_put_var_ref0) : set_value (ctx, var_refs[0]->pvalue, *--sp); - BREAK; - CASE (OP_put_var_ref1) : set_value (ctx, var_refs[1]->pvalue, *--sp); - BREAK; - CASE (OP_put_var_ref2) : set_value (ctx, var_refs[2]->pvalue, *--sp); - BREAK; - CASE (OP_put_var_ref3) : set_value (ctx, var_refs[3]->pvalue, *--sp); - BREAK; - CASE (OP_set_var_ref0) - : set_value (ctx, var_refs[0]->pvalue, sp[-1]); - BREAK; - CASE (OP_set_var_ref1) - : set_value (ctx, var_refs[1]->pvalue, sp[-1]); - BREAK; - CASE (OP_set_var_ref2) - : set_value (ctx, var_refs[2]->pvalue, sp[-1]); - BREAK; - CASE (OP_set_var_ref3) - : set_value (ctx, var_refs[3]->pvalue, sp[-1]); + /* Legacy var_ref opcodes - deprecated, replaced by OP_get_up/OP_set_up */ + CASE (OP_get_var_ref0) : + CASE (OP_get_var_ref1) : + CASE (OP_get_var_ref2) : + CASE (OP_get_var_ref3) : + CASE (OP_put_var_ref0) : + CASE (OP_put_var_ref1) : + CASE (OP_put_var_ref2) : + CASE (OP_put_var_ref3) : + CASE (OP_set_var_ref0) : + CASE (OP_set_var_ref1) : + CASE (OP_set_var_ref2) : + CASE (OP_set_var_ref3) : + JS_ThrowInternalError(ctx, "deprecated var_ref opcode (use OP_get_up/OP_set_up)"); + goto exception; BREAK; #endif - - CASE (OP_get_var_ref) : { - int idx; - JSValue val; - idx = get_u16 (pc); - pc += 2; - val = *var_refs[idx]->pvalue; - sp[0] = val; - sp++; - } - BREAK; - CASE (OP_put_var_ref) : { - int idx; - idx = get_u16 (pc); - pc += 2; - set_value (ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE (OP_set_var_ref) : { - int idx; - idx = get_u16 (pc); - pc += 2; - set_value (ctx, var_refs[idx]->pvalue, sp[-1]); - } - BREAK; - CASE (OP_get_var_ref_check) : { - int idx; - JSValue val; - idx = get_u16 (pc); - pc += 2; - val = *var_refs[idx]->pvalue; - if (unlikely (JS_IsUninitialized (val))) { - JS_ThrowReferenceErrorUninitialized2 (ctx, b, idx, TRUE); - goto exception; - } - sp[0] = val; - sp++; - } - BREAK; - CASE (OP_put_var_ref_check) : { - int idx; - idx = get_u16 (pc); - pc += 2; - if (unlikely (JS_IsUninitialized (*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2 (ctx, b, idx, TRUE); - goto exception; - } - set_value (ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } - BREAK; - CASE (OP_put_var_ref_check_init) : { - int idx; - idx = get_u16 (pc); - pc += 2; - if (unlikely (!JS_IsUninitialized (*var_refs[idx]->pvalue))) { - JS_ThrowReferenceErrorUninitialized2 (ctx, b, idx, TRUE); - goto exception; - } - set_value (ctx, var_refs[idx]->pvalue, sp[-1]); - sp--; - } + CASE (OP_get_var_ref) : + CASE (OP_put_var_ref) : + CASE (OP_set_var_ref) : + CASE (OP_get_var_ref_check) : + CASE (OP_put_var_ref_check) : + CASE (OP_put_var_ref_check_init) : + pc += 2; /* skip the index */ + JS_ThrowInternalError(ctx, "deprecated var_ref opcode (use OP_get_up/OP_set_up)"); + goto exception; BREAK; CASE (OP_set_loc_uninitialized) : { int idx; @@ -7894,49 +7730,23 @@ restart: sp--; } BREAK; - CASE (OP_close_loc) : { - int idx; - idx = get_u16 (pc); + /* Legacy var_ref reference creation - deprecated, replaced by OP_get_up/OP_set_up */ + CASE (OP_close_loc) : pc += 2; - close_lexical_var (ctx, sf, idx); - } + JS_ThrowInternalError(ctx, "deprecated close_loc opcode"); + goto exception; BREAK; - - CASE (OP_make_loc_ref) - : CASE (OP_make_arg_ref) : CASE (OP_make_var_ref_ref) : { - JSVarRef *var_ref; - JSValue key; - int idx; - key = b->cpool[get_u32 (pc)]; - idx = get_u16 (pc + 4); + CASE (OP_make_loc_ref) : + CASE (OP_make_arg_ref) : + CASE (OP_make_var_ref_ref) : pc += 6; - - /* Create var ref object with dedicated class */ - *sp++ = JS_NewObjectClass (ctx, JS_CLASS_VAR_REF_OBJECT); - if (unlikely (JS_IsException (sp[-1]))) goto exception; - - if (opcode == OP_make_var_ref_ref) { - var_ref = var_refs[idx]; - /* No ref_count increment needed with copying GC */ - } else { - var_ref = get_var_ref (ctx, sf, idx, opcode == OP_make_arg_ref); - if (!var_ref) goto exception; - } - - /* Store var_ref as opaque data */ - JS_SetOpaque (sp[-1], var_ref); - *sp++ = key; - } + JS_ThrowInternalError(ctx, "deprecated make_*_ref opcode"); + goto exception; BREAK; - CASE (OP_make_var_ref) : { - JSValue key; - key = b->cpool[get_u32 (pc)]; + CASE (OP_make_var_ref) : pc += 4; - sf->cur_pc = pc; - - if (JS_GetGlobalVarRef (ctx, key, sp)) goto exception; - sp += 2; - } + JS_ThrowInternalError(ctx, "deprecated make_var_ref opcode"); + goto exception; BREAK; CASE (OP_goto) : pc += (int32_t)get_u32 (pc); @@ -9094,10 +8904,6 @@ exception: } ret_val = JS_EXCEPTION; done: - if (unlikely (!list_empty (&sf->var_ref_list))) { - /* variable references reference the stack: must close them */ - close_var_refs (rt, sf); - } /* free the local variables and stack */ for (pval = local_buf; pval < sp; pval++) { } @@ -18302,7 +18108,7 @@ static JSValue JS_EvalFunctionInternal (JSContext *ctx, JSValue fun_obj, JSValue tag = JS_VALUE_GET_TAG (fun_obj); /* JSFunctionBytecode uses OBJ_CODE type with JS_TAG_PTR */ if (tag == JS_TAG_PTR && objhdr_type (*(objhdr_t *)JS_VALUE_GET_PTR (fun_obj)) == OBJ_CODE) { - fun_obj = js_closure (ctx, fun_obj, var_refs, sf); + fun_obj = js_closure (ctx, fun_obj, sf); ret_val = JS_Call (ctx, fun_obj, this_obj, 0, NULL); } else { ret_val = JS_ThrowTypeError (ctx, "bytecode function expected");