clean up var ref

This commit is contained in:
2026-02-03 21:54:58 -06:00
parent dc348d023f
commit c08249b6f1
2 changed files with 46 additions and 613 deletions

View File

@@ -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_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( 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 */ /* Global variable opcodes - resolved by linker to get/set_global_slot */
DEF( define_var, 6, 0, 0, key_u8) DEF( define_var, 6, 0, 0, key_u8)
DEF(check_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( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_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 */ 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(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, 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, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc) DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, 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_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */ DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */ 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) 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 */ /* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none) DEF( neg, 1, 1, 1, none)
DEF( plus, 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_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_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_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_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(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 */ def(get_field_opt_chain, 5, 1, 1, key) /* emitted in phase 1, removed in phase 2 */

View File

@@ -5060,175 +5060,6 @@ static JSValue JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx,
return JS_ThrowSyntaxError (ctx, "redeclaration of '%s'", JS_KeyGetStr (ctx, buf, sizeof (buf), prop)); 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) { int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop) {
JSRecord *rec; JSRecord *rec;
int slot; 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) { static inline int to_digit (int c) {
if (c >= '0' && c <= '9') if (c >= '0' && c <= '9')
return c - '0'; return c - '0';
@@ -7641,24 +7453,6 @@ restart:
} }
BREAK; 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); CASE (OP_goto) : pc += (int32_t)get_u32 (pc);
if (unlikely (js_poll_interrupts (ctx))) goto exception; if (unlikely (js_poll_interrupts (ctx))) goto exception;
BREAK; BREAK;
@@ -8013,32 +7807,6 @@ restart:
} }
BREAK; 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) : { CASE (OP_put_array_el) : {
int ret; int ret;
@@ -8055,30 +7823,6 @@ restart:
} }
BREAK; 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) : { CASE (OP_define_array_el) : {
int ret; int ret;
ret = JS_SetPropertyValue (ctx, sp[-3], sp[-2], sp[-1]); ret = JS_SetPropertyValue (ctx, sp[-3], sp[-2], sp[-1]);
@@ -8647,20 +8391,6 @@ restart:
} }
BREAK; 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) : { CASE (OP_template_concat) : {
int n, i; int n, i;
JSValue out; 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; if (next_token (s)) goto var_error;
emit_op (s, OP_drop); emit_op (s, OP_drop);
if (js_parse_assign_expr (s)) goto var_error; 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); set_object_name (s, var_name);
emit_label (s, label_hasval); 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 statement can resolve to a method call of the
`with` context object `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; drop_count = 1;
} break; } break;
@@ -12737,7 +12460,6 @@ static __exception int js_parse_postfix_expr (JSParseState *s,
switch (opcode) { switch (opcode) {
case OP_get_field: case OP_get_field:
case OP_get_array_el: case OP_get_array_el:
case OP_scope_get_ref:
emit_op (s, OP_call_method); emit_op (s, OP_call_method);
emit_u16 (s, arg_count); emit_u16 (s, arg_count);
break; break;
@@ -14744,112 +14466,6 @@ static int compute_closure_depth_slot(JSFunctionDef *s, int closure_idx, int *ou
return depth; 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) { static int add_var_this (JSContext *ctx, JSFunctionDef *fd) {
return add_var (ctx, fd, JS_KEY_this); 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;) { for (idx = s->scopes[scope_level].first; idx >= 0;) {
vd = &s->vars[idx]; vd = &s->vars[idx];
if (js_key_equal (vd->var_name, var_name)) { 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) { if (vd->is_const) {
int cpool_idx = fd_cpool_add (ctx, s, var_name); int cpool_idx = fd_cpool_add (ctx, s, var_name);
dbuf_putc (bc, OP_throw_error); 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 (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) { && !(var_idx & ARGUMENT_VAR_OFFSET) && s->vars[var_idx].is_const) {
/* only happens when assigning a function expression name /* only happens when assigning a function expression name
in strict mode */ 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 lexical variable, so it is never used in a with or var object. It
can be used with a closure (module global variable case). */ can be used with a closure (module global variable case). */
switch (op) { 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_checkthis:
case OP_scope_get_var_undef: case OP_scope_get_var_undef:
case OP_scope_get_var: 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;) { for (idx = fd->scopes[scope_level].first; idx >= 0;) {
vd = &fd->vars[idx]; vd = &fd->vars[idx];
if (js_key_equal (vd->var_name, var_name)) { 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) { if (vd->is_const) {
int cpool_idx = fd_cpool_add (ctx, s, var_name); int cpool_idx = fd_cpool_add (ctx, s, var_name);
dbuf_putc (bc, OP_throw_error); 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 */ /* check eval object */
if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
int slot, depth;
vd = &fd->vars[fd->var_object_idx]; vd = &fd->vars[fd->var_object_idx];
vd->is_captured = 1; vd->is_captured = 1;
idx = get_closure_var (ctx, s, fd, FALSE, fd->var_object_idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); 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); depth = compute_closure_depth_slot(s, idx, &slot);
dbuf_put_u16 (bc, idx); 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); var_object_test (ctx, s, var_name, op, bc, &label_done, 0);
} }
/* check eval object in argument scope */ /* check eval object in argument scope */
if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
int slot, depth;
vd = &fd->vars[fd->arg_var_object_idx]; vd = &fd->vars[fd->arg_var_object_idx];
vd->is_captured = 1; 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); 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); depth = compute_closure_depth_slot(s, idx, &slot);
dbuf_put_u16 (bc, idx); 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); 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_") } else if ((js_key_equal_str (cv->var_name, "_var_")
|| js_key_equal_str (cv->var_name, "_arg_var_")) || js_key_equal_str (cv->var_name, "_arg_var_"))
&& !is_pseudo_var) { && !is_pseudo_var) {
int slot, depth;
if (fd != s) { if (fd != s) {
idx = get_closure_var2 (ctx, s, fd, FALSE, cv->is_arg, idx1, cv->var_name, FALSE, FALSE, JS_VAR_NORMAL); idx = get_closure_var2 (ctx, s, fd, FALSE, cv->is_arg, idx1, cv->var_name, FALSE, FALSE, JS_VAR_NORMAL);
} else { } else {
idx = idx1; idx = idx1;
} }
dbuf_putc (bc, OP_get_var_ref); depth = compute_closure_depth_slot(s, idx, &slot);
dbuf_put_u16 (bc, idx); 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); 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) { if (idx >= 0) {
has_idx: has_idx:
if ((op == OP_scope_put_var || op == OP_scope_make_ref) if (op == OP_scope_put_var && s->closure_var[idx].is_const) {
&& s->closure_var[idx].is_const) {
int cpool_idx = fd_cpool_add (ctx, s, var_name); int cpool_idx = fd_cpool_add (ctx, s, var_name);
dbuf_putc (bc, OP_throw_error); dbuf_putc (bc, OP_throw_error);
dbuf_put_u32 (bc, cpool_idx); dbuf_put_u32 (bc, cpool_idx);
@@ -15163,69 +14744,6 @@ static int resolve_scope_var (JSContext *ctx, JSFunctionDef *s, JSValue var_name
goto done; goto done;
} }
switch (op) { 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_undef:
case OP_scope_get_var: case OP_scope_get_var:
case OP_scope_put_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 */ /* Use OP_get_up/OP_set_up with (depth, slot) encoding */
int slot; int slot;
int depth = compute_closure_depth_slot(s, idx, &slot); int depth = compute_closure_depth_slot(s, idx, &slot);
if (depth > 0 && depth <= 255 && slot <= 65535) { assert(depth > 0 && depth <= 255 && slot <= 65535);
/* Can use the new outer_frame model */ if (is_put) {
if (is_put) { dbuf_putc(bc, OP_set_up);
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);
} else { } else {
/* Fall back to old var_ref model */ dbuf_putc(bc, OP_get_up);
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, (uint8_t)depth);
dbuf_put_u16(bc, (uint16_t)slot);
} }
break; break;
case OP_scope_delete_var: 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); int cpool_idx = fd_cpool_add (ctx, s, var_name);
switch (op) { 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_undef:
case OP_scope_get_var: case OP_scope_get_var:
case OP_scope_put_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_") if (js_key_equal_str (cv->var_name, "_var_")
|| js_key_equal_str (cv->var_name, "_arg_var_")) { || js_key_equal_str (cv->var_name, "_arg_var_")) {
dbuf_putc (bc, OP_get_var_ref); int slot, depth = compute_closure_depth_slot(s, idx, &slot);
dbuf_put_u16 (bc, idx); dbuf_putc (bc, OP_get_up);
dbuf_putc (bc, (uint8_t)depth);
dbuf_put_u16 (bc, (uint16_t)slot);
has_closure = 1; has_closure = 1;
force_init = TRUE; force_init = TRUE;
break; break;
@@ -15716,8 +15196,10 @@ static void instantiate_hoisted_definitions (JSContext *ctx, JSFunctionDef *s, D
dbuf_putc (bc, OP_null); dbuf_putc (bc, OP_null);
} }
if (has_closure == 2) { if (has_closure == 2) {
dbuf_putc (bc, OP_put_var_ref); int slot, depth = compute_closure_depth_slot(s, idx, &slot);
dbuf_put_u16 (bc, idx); dbuf_putc (bc, OP_set_up);
dbuf_putc (bc, (uint8_t)depth);
dbuf_put_u16 (bc, (uint16_t)slot);
} else if (has_closure == 1) { } else if (has_closure == 1) {
int key_idx = fd_cpool_add (ctx, s, hf->var_name); int key_idx = fd_cpool_add (ctx, s, hf->var_name);
dbuf_putc (bc, OP_define_field); 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_get_var:
case OP_scope_put_var: case OP_scope_put_var:
case OP_scope_delete_var: case OP_scope_delete_var:
case OP_scope_get_ref:
case OP_scope_put_var_init: case OP_scope_put_var_init:
cpool_idx = get_u32 (bc_buf + pos + 1); cpool_idx = get_u32 (bc_buf + pos + 1);
var_name = s->cpool[cpool_idx]; var_name = s->cpool[cpool_idx];
scope = get_u16 (bc_buf + pos + 5); scope = get_u16 (bc_buf + pos + 5);
pos_next = resolve_scope_var (ctx, s, var_name, scope, op, &bc_out, NULL, NULL, pos_next); pos_next = resolve_scope_var (ctx, s, var_name, scope, op, &bc_out, NULL, NULL, pos_next);
break; 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: case OP_gosub:
s->jump_size++; s->jump_size++;
if (OPTIMIZE) { if (OPTIMIZE) {
@@ -15937,9 +15407,8 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
goto no_change; goto no_change;
case OP_insert3: case OP_insert3:
if (OPTIMIZE) { if (OPTIMIZE) {
/* Transformation: insert3 put_array_el|put_ref_value drop -> /* Transformation: insert3 put_array_el drop -> put_array_el */
* put_array_el|put_ref_value */ if (code_match (&cc, pos_next, OP_put_array_el, OP_drop, -1)) {
if (code_match (&cc, pos_next, M2 (OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
dbuf_putc (&bc_out, cc.op); dbuf_putc (&bc_out, cc.op);
pos_next = cc.pos; pos_next = cc.pos;
if (cc.line_num != -1 && cc.line_num != line_num) { if (cc.line_num != -1 && cc.line_num != line_num) {
@@ -16019,22 +15488,9 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
} }
} break; } break;
case OP_leave_scope: { case OP_leave_scope:
int scope_idx, scope = get_u16 (bc_buf + pos + 1); /* With outer_frame model, captured variables don't need closing */
break;
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_set_name: { case OP_set_name: {
/* remove dummy set_name opcodes */ /* remove dummy set_name opcodes */
@@ -16740,7 +16196,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
case OP_to_propkey: case OP_to_propkey:
if (OPTIMIZE) { if (OPTIMIZE) {
/* remove redundant to_propkey opcodes when storing simple data */ /* 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, 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)) { || code_match (&cc, pos_next, M4 (OP_null, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
break; break;
@@ -16769,7 +16225,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
/* Transformation: dup put_x(n) drop -> put_x(n) */ /* Transformation: dup put_x(n) drop -> put_x(n) */
int op1, line2 = -1; int op1, line2 = -1;
/* Transformation: dup put_x(n) -> set_x(n) */ /* 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; if (cc.line_num >= 0) line_num = cc.line_num;
op1 = cc.op + 1; /* put_x -> set_x */ op1 = cc.op + 1; /* put_x -> set_x */
pos_next = cc.pos; pos_next = cc.pos;
@@ -16824,13 +16280,11 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
pos_next = cc.pos; pos_next = cc.pos;
break; break;
} }
/* transformation: XXX: also do these: /* transformation:
get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
add_loc(n) get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
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)
*/ */
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; if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info (s, bc_out.size, line_num); add_pc2line_info (s, bc_out.size, line_num);
put_short_code (&bc_out, cc.op, cc.idx); 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; goto no_change;
#if SHORT_OPCODES #if SHORT_OPCODES
case OP_get_arg: case OP_get_arg:
case OP_get_var_ref:
if (OPTIMIZE) { if (OPTIMIZE) {
int idx; int idx;
idx = get_u16 (bc_buf + pos + 1); idx = get_u16 (bc_buf + pos + 1);
@@ -16858,7 +16311,6 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
#endif #endif
case OP_put_loc: case OP_put_loc:
case OP_put_arg: case OP_put_arg:
case OP_put_var_ref:
if (OPTIMIZE) { if (OPTIMIZE) {
/* transformation: put_x(n) get_x(n) -> set_x(n) */ /* transformation: put_x(n) get_x(n) -> set_x(n) */
int idx; 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 post_inc perm4 put_array_el drop -> inc put_array_el
*/ */
int op1, idx; 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; if (cc.line_num >= 0) line_num = cc.line_num;
op1 = cc.op; op1 = cc.op;
idx = cc.idx; idx = cc.idx;