From c08249b6f1834a87da32e833f7f82be3d4dd846c Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 3 Feb 2026 21:54:58 -0600 Subject: [PATCH] clean up var ref --- source/quickjs-opcode.h | 19 -- source/quickjs.c | 640 +++------------------------------------- 2 files changed, 46 insertions(+), 613 deletions(-) diff --git a/source/quickjs-opcode.h b/source/quickjs-opcode.h index 55643eb9..d6953e70 100644 --- a/source/quickjs-opcode.h +++ b/source/quickjs-opcode.h @@ -114,9 +114,6 @@ DEF( put_var, 5, 1, 0, key) /* resolved by linker to set_global_slot */ DEF( put_var_init, 5, 1, 0, key) /* resolved by linker to set_global_slot */ DEF( put_var_strict, 5, 2, 0, key) /* resolved by linker to set_global_slot */ -DEF( get_ref_value, 1, 2, 3, none) -DEF( put_ref_value, 1, 3, 0, none) - /* Global variable opcodes - resolved by linker to get/set_global_slot */ DEF( define_var, 6, 0, 0, key_u8) DEF(check_define_var, 6, 0, 0, key_u8) @@ -144,19 +141,11 @@ DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ DEF( get_arg, 3, 0, 1, arg) DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ -/* Var ref opcodes - deprecated, resolved to get_up/set_up during scope resolution */ -DEF( get_var_ref, 3, 0, 1, loc) /* deprecated - use get_up */ -DEF( put_var_ref, 3, 1, 0, loc) /* deprecated - use set_up */ -DEF( set_var_ref, 3, 1, 1, loc) /* deprecated - use set_up */ DEF(set_loc_uninitialized, 3, 0, 0, loc) DEF( get_loc_check, 3, 0, 1, loc) DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ DEF( put_loc_check_init, 3, 1, 0, loc) DEF(get_loc_checkthis, 3, 0, 1, loc) -DEF(get_var_ref_check, 3, 0, 1, loc) /* deprecated - use get_up */ -DEF(put_var_ref_check, 3, 1, 0, loc) /* deprecated - use set_up */ -DEF(put_var_ref_check_init, 3, 1, 0, loc) /* deprecated - use set_up */ -DEF( close_loc, 3, 0, 0, loc) /* deprecated - no longer needed */ DEF( if_false, 5, 1, 0, label) DEF( if_true, 5, 1, 0, label) /* must come after if_false */ DEF( goto, 5, 0, 0, label) /* must come after if_true */ @@ -167,12 +156,6 @@ DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ DEF( to_propkey, 1, 1, 1, none) -/* Deprecated make_*_ref opcodes - no longer needed with outer_frame model */ -DEF( make_loc_ref, 7, 0, 2, key_u16) /* deprecated */ -DEF( make_arg_ref, 7, 0, 2, key_u16) /* deprecated */ -DEF(make_var_ref_ref, 7, 0, 2, key_u16) /* deprecated */ -DEF( make_var_ref, 5, 0, 2, key) /* deprecated */ - /* arithmetic/logic operations */ DEF( neg, 1, 1, 1, none) DEF( plus, 1, 1, 1, none) @@ -240,8 +223,6 @@ def(scope_get_var_undef, 7, 0, 1, key_u16) /* emitted in phase 1, removed in pha def( scope_get_var, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2 */ def( scope_put_var, 7, 1, 0, key_u16) /* emitted in phase 1, removed in phase 2 */ def(scope_delete_var, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_make_ref, 11, 0, 2, key_label_u16) /* emitted in phase 1, removed in phase 2 */ -def( scope_get_ref, 7, 0, 2, key_u16) /* emitted in phase 1, removed in phase 2 */ def(scope_put_var_init, 7, 0, 2, key_u16) /* emitted in phase 1, removed in phase 2 */ def(scope_get_var_checkthis, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */ def(get_field_opt_chain, 5, 1, 1, key) /* emitted in phase 1, removed in phase 2 */ diff --git a/source/quickjs.c b/source/quickjs.c index 64bfab83..9b40aac9 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -5060,175 +5060,6 @@ static JSValue JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx, return JS_ThrowSyntaxError (ctx, "redeclaration of '%s'", JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); } -/* GC-SAFE: Only calls rec_find_slot (no allocations). - flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ -static int JS_CheckDefineGlobalVar (JSContext *ctx, JSValue prop, int flags) { - JSRecord *rec; - int slot; - - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj); - slot = rec_find_slot (rec, prop); - if (flags & DEFINE_GLOBAL_LEX_VAR) { - if (slot > 0) goto fail_redeclaration; - } else { - if (slot <= 0 && obj_is_stone (rec)) { - char buf[KEY_GET_STR_BUF_SIZE]; - JS_ThrowTypeError (ctx, "cannot define variable '%s'", JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); - return -1; - } - } - /* check if there already is a lexical declaration */ - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) { - fail_redeclaration: - JS_ThrowSyntaxErrorVarRedeclaration (ctx, prop); - return -1; - } - return 0; -} - -/* CAUTION: rec_set_own is NOT GC-safe if rec_resize is implemented. - Currently safe because rec_resize always fails. - def_flags is (0, DEFINE_GLOBAL_LEX_VAR) */ -static int JS_DefineGlobalVar (JSContext *ctx, JSValue prop, int def_flags) { - JSValue *pobj; - JSValue val; - JSRecord *rec; - int slot; - - if (def_flags & DEFINE_GLOBAL_LEX_VAR) { - pobj = &ctx->global_var_obj; - val = JS_UNINITIALIZED; - } else { - pobj = &ctx->global_obj; - val = JS_NULL; - } - rec = (JSRecord *)JS_VALUE_GET_OBJ (*pobj); - slot = rec_find_slot (rec, prop); - if (slot > 0) return 0; /* already defined */ - if (obj_is_stone (rec)) return 0; - return rec_set_own (ctx, pobj, prop, val); -} - -/* Simplified global function definition */ -static int JS_DefineGlobalFunction (JSContext *ctx, JSValue prop, JSValue func, int def_flags) { - (void)def_flags; - /* JS_SetPropertyInternal consumes the value, so we must dup it */ - if (JS_SetPropertyInternal (ctx, ctx->global_obj, prop, func) - < 0) - return -1; - return 0; -} - -/* GC-SAFE: Only calls rec_find_slot and rec_get (no allocations) */ -static JSValue JS_GetGlobalVar (JSContext *ctx, JSValue prop, BOOL throw_ref_error) { - JSRecord *rec; - int slot; - - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) { - JSValue val = rec->slots[slot].val; - if (unlikely (JS_IsUninitialized (val))) - return JS_ThrowReferenceErrorUninitialized (ctx, prop); - return val; - } - /* Fall back to global object */ - JSValue val - = rec_get (ctx, (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_obj), prop); - if (JS_IsNull (val) && throw_ref_error) - return JS_ThrowReferenceErrorNotDefined (ctx, prop); - return val; -} - -/* GC-SAFE: Only calls rec_find_slot and JS_HasProperty (no allocations). - Construct a reference to a global variable */ -static int JS_GetGlobalVarRef (JSContext *ctx, JSValue prop, JSValue *sp) { - JSRecord *rec; - int slot; - - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) { - JSValue val = rec->slots[slot].val; - if (unlikely (JS_IsUninitialized (val))) { - JS_ThrowReferenceErrorUninitialized (ctx, prop); - return -1; - } - sp[0] = ctx->global_var_obj; - } else { - int ret = JS_HasProperty (ctx, ctx->global_obj, prop); - if (ret < 0) return -1; - if (ret) { - sp[0] = ctx->global_obj; - } else { - sp[0] = JS_NULL; - } - } - sp[1] = prop; - return 0; -} - -/* GC-SAFE: Only calls rec_find_slot and JS_HasProperty (no allocations). - Use for strict variable access: test if the variable exists */ -static int JS_CheckGlobalVar (JSContext *ctx, JSValue prop) { - JSRecord *rec; - int slot, ret; - - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) { - ret = TRUE; - } else { - ret = JS_HasProperty (ctx, ctx->global_obj, prop); - if (ret < 0) return -1; - } - return ret; -} - -/* flag = 0: normal variable write - flag = 1: initialize lexical variable - flag = 2: normal variable write, strict check was done before -*/ -static int JS_SetGlobalVar (JSContext *ctx, JSValue prop, JSValue val, int flag) { - JSRecord *rec; - int slot; - - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) { - if (flag != 1) { - if (unlikely (JS_IsUninitialized (rec->slots[slot].val))) { - JS_ThrowReferenceErrorUninitialized (ctx, prop); - return -1; - } - } - rec->slots[slot].val = val; - return 0; - } - - return JS_SetPropertyInternal (ctx, ctx->global_obj, prop, val); -} - -/* return -1, FALSE or TRUE */ -static int JS_DeleteGlobalVar (JSContext *ctx, JSValue prop) { - JSRecord *rec; - int slot, ret; - - /* 9.1.1.4.7 DeleteBinding ( N ) */ - rec = (JSRecord *)JS_VALUE_GET_OBJ (ctx->global_var_obj); - slot = rec_find_slot (rec, prop); - if (slot > 0) return FALSE; /* lexical variables cannot be deleted */ - ret = JS_HasProperty (ctx, ctx->global_obj, prop); - if (ret < 0) return -1; - if (ret) { - return JS_DeleteProperty (ctx, ctx->global_obj, prop); - } else { - return TRUE; - } -} - int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop) { JSRecord *rec; int slot; @@ -5359,25 +5190,6 @@ int JS_ToBool (JSContext *ctx, JSValue val) { } } -static int skip_spaces (const char *pc) { - const uint8_t *p, *p_next, *p_start; - uint32_t c; - - p = p_start = (const uint8_t *)pc; - for (;;) { - c = *p; - if (c < 128) { - if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) break; - p++; - } else { - c = unicode_from_utf8 (p, UTF8_CHAR_LEN_MAX, &p_next); - if (!lre_is_space (c)) break; - p = p_next; - } - } - return p - p_start; -} - static inline int to_digit (int c) { if (c >= '0' && c <= '9') return c - '0'; @@ -7641,24 +7453,6 @@ restart: } BREAK; - /* Deprecated var_ref opcodes - should be resolved to get_up/set_up during scope resolution */ - 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_close_loc) : - pc += 2; - JS_ThrowInternalError(ctx, "deprecated close_loc opcode"); - goto exception; - BREAK; - CASE (OP_goto) : pc += (int32_t)get_u32 (pc); if (unlikely (js_poll_interrupts (ctx))) goto exception; BREAK; @@ -8013,32 +7807,6 @@ restart: } BREAK; - CASE (OP_get_ref_value) : { - JSValue val; - JSValue key = sp[-1]; - JSValue ref_obj = sp[-2]; - - sf->cur_pc = pc; - - if (JS_IsText (key)) { key = js_key_from_string (ctx, key); } - if (unlikely (JS_IsNull (ref_obj))) { - JS_ThrowReferenceErrorNotDefined (ctx, key); - goto exception; - } - int ret = JS_HasProperty (ctx, ref_obj, key); - if (unlikely (ret <= 0)) { - if (ret < 0) { goto exception; } - JS_ThrowReferenceErrorNotDefined (ctx, key); - goto exception; - } else { - val = JS_GetProperty (ctx, ref_obj, key); - } - if (unlikely (JS_IsException (val))) goto exception; - sp[0] = val; - sp++; - } - BREAK; - CASE (OP_put_array_el) : { int ret; @@ -8055,30 +7823,6 @@ restart: } BREAK; - CASE (OP_put_ref_value) : { - JSValue key = sp[-2]; - JSValue ref_obj = sp[-3]; - JSValue val = sp[-1]; - - sf->cur_pc = pc; - - if (JS_IsText (key)) { key = js_key_from_string (ctx, key); } - if (unlikely (JS_IsNull (ref_obj))) { - JS_ThrowReferenceErrorNotDefined (ctx, key); - goto exception; - } - int ret = JS_HasProperty (ctx, ref_obj, key); - if (unlikely (ret <= 0)) { - if (unlikely (ret < 0)) { goto exception; } - JS_ThrowReferenceErrorNotDefined (ctx, key); - goto exception; - } - ret = JS_SetPropertyInternal (ctx, ref_obj, key, val); - sp -= 3; - if (unlikely (ret < 0)) goto exception; - } - BREAK; - CASE (OP_define_array_el) : { int ret; ret = JS_SetPropertyValue (ctx, sp[-3], sp[-2], sp[-1]); @@ -8647,20 +8391,6 @@ restart: } BREAK; - /* Deprecated make_*_ref opcodes - no longer needed with outer_frame model */ - CASE (OP_make_loc_ref) : - CASE (OP_make_arg_ref) : - CASE (OP_make_var_ref_ref) : - pc += 6; - JS_ThrowInternalError(ctx, "deprecated make_*_ref opcode"); - goto exception; - BREAK; - CASE (OP_make_var_ref) : - pc += 4; - JS_ThrowInternalError(ctx, "deprecated make_var_ref opcode"); - goto exception; - BREAK; - CASE (OP_template_concat) : { int n, i; JSValue out; @@ -12408,7 +12138,7 @@ static int js_parse_destructuring_element (JSParseState *s, int tok, int is_arg, if (next_token (s)) goto var_error; emit_op (s, OP_drop); if (js_parse_assign_expr (s)) goto var_error; - if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) + if (opcode == OP_scope_get_var) set_object_name (s, var_name); emit_label (s, label_hasval); } @@ -12692,13 +12422,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s, statement can resolve to a method call of the `with` context object */ - /* XXX: always generate the OP_scope_get_ref - and remove it in variable resolution - pass ? */ - if (has_with_scope (fd, scope)) { - opcode = OP_scope_get_ref; - fd->byte_code.buf[fd->last_opcode_pos] = opcode; - } } drop_count = 1; } break; @@ -12737,7 +12460,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s, switch (opcode) { case OP_get_field: case OP_get_array_el: - case OP_scope_get_ref: emit_op (s, OP_call_method); emit_u16 (s, arg_count); break; @@ -14744,112 +14466,6 @@ static int compute_closure_depth_slot(JSFunctionDef *s, int closure_idx, int *ou return depth; } -static BOOL can_opt_put_ref_value (const uint8_t *bc_buf, int pos) { - int opcode = bc_buf[pos]; - return (bc_buf[pos + 1] == OP_put_ref_value - && (opcode == OP_insert3 || opcode == OP_perm4 || opcode == OP_nop - || opcode == OP_rot3l)); -} - -static BOOL can_opt_put_global_ref_value (const uint8_t *bc_buf, int pos) { - int opcode = bc_buf[pos]; - return (bc_buf[pos + 1] == OP_put_ref_value - && (opcode == OP_insert3 || opcode == OP_perm4 || opcode == OP_nop - || opcode == OP_rot3l)); -} - -static int optimize_scope_make_ref (JSContext *ctx, JSFunctionDef *s, DynBuf *bc, uint8_t *bc_buf, LabelSlot *ls, int pos_next, int get_op, int var_idx) { - int label_pos, end_pos, pos; - - /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)` - but only if expr does not modify `a`. - should scan the code between pos_next and label_pos - for operations that can potentially change `a`: - OP_scope_make_ref(a), function calls, jumps and gosub. - */ - /* replace the reference get/put with normal variable - accesses */ - if (bc_buf[pos_next] == OP_get_ref_value) { - dbuf_putc (bc, get_op); - dbuf_put_u16 (bc, var_idx); - pos_next++; - } - /* remove the OP_label to make room for replacement */ - /* label should have a refcount of 0 anyway */ - /* XXX: should avoid this patch by inserting nops in phase 1 */ - label_pos = ls->pos; - pos = label_pos - 5; - assert (bc_buf[pos] == OP_label); - /* label points to an instruction pair: - - insert3 / put_ref_value - - perm4 / put_ref_value - - rot3l / put_ref_value - - nop / put_ref_value - */ - end_pos = label_pos + 2; - if (bc_buf[label_pos] == OP_insert3) bc_buf[pos++] = OP_dup; - bc_buf[pos] = get_op + 1; - put_u16 (bc_buf + pos + 1, var_idx); - pos += 3; - /* pad with OP_nop */ - while (pos < end_pos) - bc_buf[pos++] = OP_nop; - return pos_next; -} - -static int optimize_scope_make_global_ref (JSContext *ctx, JSFunctionDef *s, DynBuf *bc, uint8_t *bc_buf, LabelSlot *ls, int pos_next, JSValue var_name) { - int label_pos, end_pos, pos, op; - int cpool_idx = fd_cpool_add (ctx, s, var_name); - - /* replace the reference get/put with normal variable - accesses */ - /* need to check if the variable exists before evaluating the right - expression */ - /* XXX: need an extra OP_true if destructuring an array */ - dbuf_putc (bc, OP_check_var); - dbuf_put_u32 (bc, cpool_idx); - - if (bc_buf[pos_next] == OP_get_ref_value) { - dbuf_putc (bc, OP_get_var); - dbuf_put_u32 (bc, cpool_idx); - pos_next++; - } - /* remove the OP_label to make room for replacement */ - /* label should have a refcount of 0 anyway */ - /* XXX: should have emitted several OP_nop to avoid this kludge */ - label_pos = ls->pos; - pos = label_pos - 5; - assert (bc_buf[pos] == OP_label); - end_pos = label_pos + 2; - op = bc_buf[label_pos]; - if (op != OP_nop) { - switch (op) { - case OP_insert3: - op = OP_insert2; - break; - case OP_perm4: - op = OP_perm3; - break; - case OP_rot3l: - op = OP_swap; - break; - default: - abort (); - } - bc_buf[pos++] = op; - } - - bc_buf[pos] = OP_put_var_strict; - /* XXX: need 1 extra OP_drop if destructuring an array */ - - put_u32 (bc_buf + pos + 1, cpool_idx); - pos += 5; - /* pad with OP_nop */ - while (pos < end_pos) - bc_buf[pos++] = OP_nop; - return pos_next; -} - static int add_var_this (JSContext *ctx, JSFunctionDef *fd) { return add_var (ctx, fd, JS_KEY_this); } @@ -14907,7 +14523,7 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name for (idx = s->scopes[scope_level].first; idx >= 0;) { vd = &s->vars[idx]; if (js_key_equal (vd->var_name, var_name)) { - if (op == OP_scope_put_var || op == OP_scope_make_ref) { + if (op == OP_scope_put_var) { if (vd->is_const) { int cpool_idx = fd_cpool_add (ctx, s, var_name); dbuf_putc (bc, OP_throw_error); @@ -14937,7 +14553,7 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name } } if (var_idx >= 0) { - if ((op == OP_scope_put_var || op == OP_scope_make_ref) + if (op == OP_scope_put_var && !(var_idx & ARGUMENT_VAR_OFFSET) && s->vars[var_idx].is_const) { /* only happens when assigning a function expression name in strict mode */ @@ -14951,49 +14567,6 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name lexical variable, so it is never used in a with or var object. It can be used with a closure (module global variable case). */ switch (op) { - case OP_scope_make_ref: - if (!(var_idx & ARGUMENT_VAR_OFFSET) - && s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { - /* Create a dummy object reference for the func_var */ - int cpool_idx = fd_cpool_add (ctx, s, var_name); - if (cpool_idx < 0) return -1; - dbuf_putc (bc, OP_object); - dbuf_putc (bc, OP_get_loc); - dbuf_put_u16 (bc, var_idx); - dbuf_putc (bc, OP_define_field); - dbuf_put_u32 (bc, cpool_idx); - dbuf_putc (bc, OP_push_const); - dbuf_put_u32 (bc, cpool_idx); - } else if (label_done == -1 && can_opt_put_ref_value (bc_buf, ls->pos)) { - int get_op; - if (var_idx & ARGUMENT_VAR_OFFSET) { - get_op = OP_get_arg; - var_idx -= ARGUMENT_VAR_OFFSET; - } else { - if (s->vars[var_idx].is_lexical) - get_op = OP_get_loc_check; - else - get_op = OP_get_loc; - } - pos_next = optimize_scope_make_ref (ctx, s, bc, bc_buf, ls, pos_next, get_op, var_idx); - } else { - /* Create a dummy object with a named slot that is - a reference to the local variable */ - int cpool_idx = fd_cpool_add (ctx, s, var_name); - if (var_idx & ARGUMENT_VAR_OFFSET) { - dbuf_putc (bc, OP_make_arg_ref); - dbuf_put_u32 (bc, cpool_idx); - dbuf_put_u16 (bc, var_idx - ARGUMENT_VAR_OFFSET); - } else { - dbuf_putc (bc, OP_make_loc_ref); - dbuf_put_u32 (bc, cpool_idx); - dbuf_put_u16 (bc, var_idx); - } - } - break; - case OP_scope_get_ref: - dbuf_putc (bc, OP_null); - /* fall thru */ case OP_scope_get_var_checkthis: case OP_scope_get_var_undef: case OP_scope_get_var: @@ -15059,7 +14632,7 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name for (idx = fd->scopes[scope_level].first; idx >= 0;) { vd = &fd->vars[idx]; if (js_key_equal (vd->var_name, var_name)) { - if (op == OP_scope_put_var || op == OP_scope_make_ref) { + if (op == OP_scope_put_var) { if (vd->is_const) { int cpool_idx = fd_cpool_add (ctx, s, var_name); dbuf_putc (bc, OP_throw_error); @@ -15092,21 +14665,27 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name /* check eval object */ if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { + int slot, depth; vd = &fd->vars[fd->var_object_idx]; vd->is_captured = 1; idx = get_closure_var (ctx, s, fd, FALSE, fd->var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); - dbuf_putc (bc, OP_get_var_ref); - dbuf_put_u16 (bc, idx); + depth = compute_closure_depth_slot(s, idx, &slot); + dbuf_putc (bc, OP_get_up); + dbuf_putc (bc, (uint8_t)depth); + dbuf_put_u16 (bc, (uint16_t)slot); var_object_test (ctx, s, var_name, op, bc, &label_done, 0); } /* check eval object in argument scope */ if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { + int slot, depth; vd = &fd->vars[fd->arg_var_object_idx]; vd->is_captured = 1; idx = get_closure_var (ctx, s, fd, FALSE, fd->arg_var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); - dbuf_putc (bc, OP_get_var_ref); - dbuf_put_u16 (bc, idx); + depth = compute_closure_depth_slot(s, idx, &slot); + dbuf_putc (bc, OP_get_up); + dbuf_putc (bc, (uint8_t)depth); + dbuf_put_u16 (bc, (uint16_t)slot); var_object_test (ctx, s, var_name, op, bc, &label_done, 0); } @@ -15130,13 +14709,16 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name } else if ((js_key_equal_str (cv->var_name, "_var_") || js_key_equal_str (cv->var_name, "_arg_var_")) && !is_pseudo_var) { + int slot, depth; if (fd != s) { idx = get_closure_var2 (ctx, s, fd, FALSE, cv->is_arg, idx1, cv->var_name, FALSE, FALSE, JS_VAR_NORMAL); } else { idx = idx1; } - dbuf_putc (bc, OP_get_var_ref); - dbuf_put_u16 (bc, idx); + depth = compute_closure_depth_slot(s, idx, &slot); + dbuf_putc (bc, OP_get_up); + dbuf_putc (bc, (uint8_t)depth); + dbuf_put_u16 (bc, (uint16_t)slot); var_object_test (ctx, s, var_name, op, bc, &label_done, 0); } } @@ -15154,8 +14736,7 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name } if (idx >= 0) { has_idx: - if ((op == OP_scope_put_var || op == OP_scope_make_ref) - && s->closure_var[idx].is_const) { + if (op == OP_scope_put_var && s->closure_var[idx].is_const) { int cpool_idx = fd_cpool_add (ctx, s, var_name); dbuf_putc (bc, OP_throw_error); dbuf_put_u32 (bc, cpool_idx); @@ -15163,69 +14744,6 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name goto done; } switch (op) { - case OP_scope_make_ref: - if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { - /* Create a dummy object reference for the func_var */ - int cpool_idx = fd_cpool_add (ctx, s, var_name); - if (cpool_idx < 0) return -1; - dbuf_putc (bc, OP_object); - dbuf_putc (bc, OP_get_var_ref); - dbuf_put_u16 (bc, idx); - dbuf_putc (bc, OP_define_field); - dbuf_put_u32 (bc, cpool_idx); - dbuf_putc (bc, OP_push_const); - dbuf_put_u32 (bc, cpool_idx); - } else if (label_done == -1 - && can_opt_put_ref_value (bc_buf, ls->pos)) { - /* Check if we can use new OP_set_up model for closure variables */ - int slot; - int depth = compute_closure_depth_slot(s, idx, &slot); - if (depth > 0 && depth <= 255 && slot <= 65535) { - /* Use OP_get_up/OP_set_up for the make_ref optimization */ - int label_pos = ls->pos; - int pos = label_pos - 5; - int end_pos = label_pos + 2; - if (bc_buf[pos_next] == OP_get_ref_value) { - /* Get part - emit to bc output buffer */ - dbuf_putc (bc, OP_get_up); - dbuf_putc (bc, (uint8_t)depth); - dbuf_put_u16 (bc, (uint16_t)slot); - pos_next++; - } - /* Put part - patch in-place in bc_buf */ - if (bc_buf[label_pos] == OP_insert3) { - bc_buf[pos++] = OP_dup; - } - bc_buf[pos] = OP_set_up; - bc_buf[pos + 1] = (uint8_t)depth; - put_u16(bc_buf + pos + 2, (uint16_t)slot); - pos += 4; - /* Pad with nops */ - while (pos < end_pos) - bc_buf[pos++] = OP_nop; - } else { - /* Fall back to old var_ref model */ - int get_op; - if (s->closure_var[idx].is_lexical) - get_op = OP_get_var_ref_check; - else - get_op = OP_get_var_ref; - pos_next = optimize_scope_make_ref (ctx, s, bc, bc_buf, ls, pos_next, get_op, idx); - } - } else { - /* Create a dummy object with a named slot that is - a reference to the closure variable */ - int cpool_idx = fd_cpool_add (ctx, s, var_name); - dbuf_putc (bc, OP_make_var_ref_ref); - dbuf_put_u32 (bc, cpool_idx); - dbuf_put_u16 (bc, idx); - } - break; - case OP_scope_get_ref: - /* XXX: should create a dummy object with a named slot that is - a reference to the closure variable */ - dbuf_putc (bc, OP_null); - /* fall thru */ case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -15235,39 +14753,14 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name /* Use OP_get_up/OP_set_up with (depth, slot) encoding */ int slot; int depth = compute_closure_depth_slot(s, idx, &slot); - if (depth > 0 && depth <= 255 && slot <= 65535) { - /* Can use the new outer_frame model */ - if (is_put) { - dbuf_putc(bc, OP_set_up); - } else { - dbuf_putc(bc, OP_get_up); - } - dbuf_putc(bc, (uint8_t)depth); - dbuf_put_u16(bc, (uint16_t)slot); + assert(depth > 0 && depth <= 255 && slot <= 65535); + if (is_put) { + dbuf_putc(bc, OP_set_up); } else { - /* Fall back to old var_ref model */ - if (is_put) { - if (s->closure_var[idx].is_lexical) { - if (op == OP_scope_put_var_init) { - if (js_key_equal (var_name, JS_KEY_this)) - dbuf_putc (bc, OP_put_var_ref_check_init); - else - dbuf_putc (bc, OP_put_var_ref); - } else { - dbuf_putc (bc, OP_put_var_ref_check); - } - } else { - dbuf_putc (bc, OP_put_var_ref); - } - } else { - if (s->closure_var[idx].is_lexical) { - dbuf_putc (bc, OP_get_var_ref_check); - } else { - dbuf_putc (bc, OP_get_var_ref); - } - } - dbuf_put_u16 (bc, idx); + dbuf_putc(bc, OP_get_up); } + dbuf_putc(bc, (uint8_t)depth); + dbuf_put_u16(bc, (uint16_t)slot); } break; case OP_scope_delete_var: @@ -15283,21 +14776,6 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name int cpool_idx = fd_cpool_add (ctx, s, var_name); switch (op) { - case OP_scope_make_ref: - if (label_done == -1 && can_opt_put_global_ref_value (bc_buf, ls->pos)) { - pos_next = optimize_scope_make_global_ref (ctx, s, bc, bc_buf, ls, pos_next, var_name); - } else { - dbuf_putc (bc, OP_make_var_ref); - dbuf_put_u32 (bc, cpool_idx); - } - break; - case OP_scope_get_ref: - /* XXX: should create a dummy object with a named slot that is - a reference to the global variable */ - dbuf_putc (bc, OP_null); - dbuf_putc (bc, OP_get_var); - dbuf_put_u32 (bc, cpool_idx); - break; case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -15670,8 +15148,10 @@ static void instantiate_hoisted_definitions (JSContext *ctx, JSFunctionDef *s, D } if (js_key_equal_str (cv->var_name, "_var_") || js_key_equal_str (cv->var_name, "_arg_var_")) { - dbuf_putc (bc, OP_get_var_ref); - dbuf_put_u16 (bc, idx); + int slot, depth = compute_closure_depth_slot(s, idx, &slot); + dbuf_putc (bc, OP_get_up); + dbuf_putc (bc, (uint8_t)depth); + dbuf_put_u16 (bc, (uint16_t)slot); has_closure = 1; force_init = TRUE; break; @@ -15716,8 +15196,10 @@ static void instantiate_hoisted_definitions (JSContext *ctx, JSFunctionDef *s, D dbuf_putc (bc, OP_null); } if (has_closure == 2) { - dbuf_putc (bc, OP_put_var_ref); - dbuf_put_u16 (bc, idx); + int slot, depth = compute_closure_depth_slot(s, idx, &slot); + dbuf_putc (bc, OP_set_up); + dbuf_putc (bc, (uint8_t)depth); + dbuf_put_u16 (bc, (uint16_t)slot); } else if (has_closure == 1) { int key_idx = fd_cpool_add (ctx, s, hf->var_name); dbuf_putc (bc, OP_define_field); @@ -15878,24 +15360,12 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) { case OP_scope_get_var: case OP_scope_put_var: case OP_scope_delete_var: - case OP_scope_get_ref: case OP_scope_put_var_init: cpool_idx = get_u32 (bc_buf + pos + 1); var_name = s->cpool[cpool_idx]; scope = get_u16 (bc_buf + pos + 5); pos_next = resolve_scope_var (ctx, s, var_name, scope, op, &bc_out, NULL, NULL, pos_next); break; - case OP_scope_make_ref: { - int label; - LabelSlot *ls; - cpool_idx = get_u32 (bc_buf + pos + 1); - var_name = s->cpool[cpool_idx]; - label = get_u32 (bc_buf + pos + 5); - scope = get_u16 (bc_buf + pos + 9); - ls = &s->label_slots[label]; - ls->ref_count--; /* always remove label reference */ - pos_next = resolve_scope_var (ctx, s, var_name, scope, op, &bc_out, bc_buf, ls, pos_next); - } break; case OP_gosub: s->jump_size++; if (OPTIMIZE) { @@ -15937,9 +15407,8 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) { goto no_change; case OP_insert3: if (OPTIMIZE) { - /* Transformation: insert3 put_array_el|put_ref_value drop -> - * put_array_el|put_ref_value */ - if (code_match (&cc, pos_next, M2 (OP_put_array_el, OP_put_ref_value), OP_drop, -1)) { + /* Transformation: insert3 put_array_el drop -> put_array_el */ + if (code_match (&cc, pos_next, OP_put_array_el, OP_drop, -1)) { dbuf_putc (&bc_out, cc.op); pos_next = cc.pos; if (cc.line_num != -1 && cc.line_num != line_num) { @@ -16019,22 +15488,9 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) { } } break; - case OP_leave_scope: { - int scope_idx, scope = get_u16 (bc_buf + pos + 1); - - for (scope_idx = s->scopes[scope].first; scope_idx >= 0;) { - JSVarDef *vd = &s->vars[scope_idx]; - if (vd->scope_level == scope) { - if (vd->is_captured) { - dbuf_putc (&bc_out, OP_close_loc); - dbuf_put_u16 (&bc_out, scope_idx); - } - scope_idx = vd->scope_next; - } else { - break; - } - } - } break; + case OP_leave_scope: + /* With outer_frame model, captured variables don't need closing */ + break; case OP_set_name: { /* remove dummy set_name opcodes */ @@ -16740,7 +16196,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { case OP_to_propkey: if (OPTIMIZE) { /* remove redundant to_propkey opcodes when storing simple data */ - if (code_match (&cc, pos_next, M3 (OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) + if (code_match (&cc, pos_next, M2 (OP_get_loc, OP_get_arg), -1, OP_put_array_el, -1) || code_match (&cc, pos_next, M2 (OP_push_i32, OP_push_const), OP_put_array_el, -1) || code_match (&cc, pos_next, M4 (OP_null, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { break; @@ -16769,7 +16225,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { /* Transformation: dup put_x(n) drop -> put_x(n) */ int op1, line2 = -1; /* Transformation: dup put_x(n) -> set_x(n) */ - if (code_match (&cc, pos_next, M3 (OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) { + if (code_match (&cc, pos_next, M2 (OP_put_loc, OP_put_arg), -1, -1)) { if (cc.line_num >= 0) line_num = cc.line_num; op1 = cc.op + 1; /* put_x -> set_x */ pos_next = cc.pos; @@ -16824,13 +16280,11 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { pos_next = cc.pos; break; } - /* transformation: XXX: also do these: - get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) - add_loc(n) get_loc(n) get_arg(x) add dup put_loc(n) drop -> - get_arg(x) add_loc(n) get_loc(n) get_var_ref(x) add dup put_loc(n) - drop -> get_var_ref(x) add_loc(n) + /* transformation: + get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n) + get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n) */ - if (code_match (&cc, pos_next, M3 (OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { + if (code_match (&cc, pos_next, M2 (OP_get_loc, OP_get_arg), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { if (cc.line_num >= 0) line_num = cc.line_num; add_pc2line_info (s, bc_out.size, line_num); put_short_code (&bc_out, cc.op, cc.idx); @@ -16846,7 +16300,6 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { goto no_change; #if SHORT_OPCODES case OP_get_arg: - case OP_get_var_ref: if (OPTIMIZE) { int idx; idx = get_u16 (bc_buf + pos + 1); @@ -16858,7 +16311,6 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { #endif case OP_put_loc: case OP_put_arg: - case OP_put_var_ref: if (OPTIMIZE) { /* transformation: put_x(n) get_x(n) -> set_x(n) */ int idx; @@ -16886,7 +16338,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) { post_inc perm4 put_array_el drop -> inc put_array_el */ int op1, idx; - if (code_match (&cc, pos_next, M3 (OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) { + if (code_match (&cc, pos_next, M2 (OP_put_loc, OP_put_arg), -1, OP_drop, -1)) { if (cc.line_num >= 0) line_num = cc.line_num; op1 = cc.op; idx = cc.idx;