Begin removal of varref

This commit is contained in:
2026-02-03 19:13:17 -06:00
parent 94f1645be1
commit e734353722

View File

@@ -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");