- optimized global variable access

- removed full compliance with the spec for strict mode variable
  assignment so that they are as fast as in non strict mode (V8,
  SpiderMonkey and JavascriptCore do the same, so IMHO the spec should
  be updated).
This commit is contained in:
Fabrice Bellard
2025-10-03 14:26:13 +02:00
parent f4951efd94
commit 2c90110287
4 changed files with 72 additions and 142 deletions

159
quickjs.c
View File

@@ -10175,8 +10175,8 @@ static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
return 0;
}
static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
BOOL throw_ref_error)
static inline JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
BOOL throw_ref_error)
{
JSObject *p;
JSShapeProperty *prs;
@@ -10191,6 +10191,14 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
return JS_DupValue(ctx, pr->u.value);
}
/* fast path */
p = JS_VALUE_GET_OBJ(ctx->global_obj);
prs = find_own_property(&pr, p, prop);
if (prs) {
if (likely((prs->flags & JS_PROP_TMASK) == 0))
return JS_DupValue(ctx, pr->u.value);
}
return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
ctx->global_obj, throw_ref_error);
}
@@ -10232,37 +10240,16 @@ static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
return 0;
}
/* use for strict variable access: test if the variable exists */
static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
{
JSObject *p;
JSShapeProperty *prs;
int ret;
/* no exotic behavior is possible in global_var_obj */
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
prs = find_own_property1(p, prop);
if (prs) {
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, JSAtom prop, JSValue val,
int flag)
static inline int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
int flag)
{
JSObject *p;
JSShapeProperty *prs;
JSProperty *pr;
int flags;
int ret;
/* no exotic behavior is possible in global_var_obj */
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
@@ -10283,13 +10270,30 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
set_value(ctx, &pr->u.value, val);
return 0;
}
/* XXX: add a fast path where the property exists and the object
is not exotic. Otherwise do as in OP_put_ref_value and remove
JS_PROP_NO_ADD which is no longer necessary */
flags = JS_PROP_THROW_STRICT;
if (is_strict_mode(ctx))
flags |= JS_PROP_NO_ADD;
return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags);
p = JS_VALUE_GET_OBJ(ctx->global_obj);
prs = find_own_property(&pr, p, prop);
if (prs) {
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
/* fast path */
set_value(ctx, &pr->u.value, val);
return 0;
}
}
/* slow path */
ret = JS_HasProperty(ctx, ctx->global_obj, prop);
if (ret < 0) {
JS_FreeValue(ctx, val);
return -1;
}
if (ret == 0 && is_strict_mode(ctx)) {
JS_FreeValue(ctx, val);
JS_ThrowReferenceErrorNotDefined(ctx, prop);
return -1;
}
return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj,
JS_PROP_THROW_STRICT);
}
/* return -1, FALSE or TRUE */
@@ -17597,21 +17601,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
}
BREAK;
CASE(OP_check_var):
{
int ret;
JSAtom atom;
atom = get_u32(pc);
pc += 4;
sf->cur_pc = pc;
ret = JS_CheckGlobalVar(ctx, atom);
if (ret < 0)
goto exception;
*sp++ = JS_NewBool(ctx, ret);
}
BREAK;
CASE(OP_get_var_undef):
CASE(OP_get_var):
{
@@ -17644,26 +17633,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
}
BREAK;
CASE(OP_put_var_strict):
{
int ret;
JSAtom atom;
atom = get_u32(pc);
pc += 4;
sf->cur_pc = pc;
/* sp[-2] is JS_TRUE or JS_FALSE */
if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
JS_ThrowReferenceErrorNotDefined(ctx, atom);
goto exception;
}
ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
sp -= 2;
if (unlikely(ret < 0))
goto exception;
}
BREAK;
CASE(OP_check_define_var):
{
JSAtom atom;
@@ -31578,20 +31547,10 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
JSAtom var_name)
{
int label_pos, end_pos, pos, op;
BOOL is_strict;
is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
/* replace the reference get/put with normal variable
accesses */
if (is_strict) {
/* 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, JS_DupAtom(ctx, var_name));
} else {
/* XXX: need 2 extra OP_true if destructuring an array */
}
/* XXX: need 2 extra OP_true if destructuring an array */
if (bc_buf[pos_next] == OP_get_ref_value) {
dbuf_putc(bc, OP_get_var);
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
@@ -31605,34 +31564,10 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
assert(bc_buf[pos] == OP_label);
end_pos = label_pos + 2;
op = bc_buf[label_pos];
if (is_strict) {
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;
}
} else {
if (op == OP_insert3)
bc_buf[pos++] = OP_dup;
}
if (is_strict) {
bc_buf[pos] = OP_put_var_strict;
/* XXX: need 1 extra OP_drop if destructuring an array */
} else {
bc_buf[pos] = OP_put_var;
/* XXX: need 2 extra OP_drop if destructuring an array */
}
if (op == OP_insert3)
bc_buf[pos++] = OP_dup;
bc_buf[pos] = OP_put_var;
/* XXX: need 2 extra OP_drop if destructuring an array */
put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
pos += 5;
/* pad with OP_nop */
@@ -34103,12 +34038,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
if (OPTIMIZE) {
/* Transformation:
insert2 put_field(a) drop -> put_field(a)
insert2 put_var_strict(a) drop -> put_var_strict(a)
*/
if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
if (code_match(&cc, pos_next, OP_put_field, OP_drop, -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info(s, bc_out.size, line_num);
dbuf_putc(&bc_out, cc.op);
dbuf_putc(&bc_out, OP_put_field);
dbuf_put_u32(&bc_out, cc.atom);
pos_next = cc.pos;
break;
@@ -34254,7 +34188,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
/* transformation:
post_inc put_x drop -> inc put_x
post_inc perm3 put_field drop -> inc put_field
post_inc perm3 put_var_strict drop -> inc put_var_strict
post_inc perm4 put_array_el drop -> inc put_array_el
*/
int op1, idx;
@@ -34273,11 +34206,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
put_short_code(&bc_out, op1, idx);
break;
}
if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
if (code_match(&cc, pos_next, OP_perm3, OP_put_field, OP_drop, -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info(s, bc_out.size, line_num);
dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
dbuf_putc(&bc_out, cc.op);
dbuf_putc(&bc_out, OP_put_field);
dbuf_put_u32(&bc_out, cc.atom);
pos_next = cc.pos;
break;