Skip to content

Commit 2c90110

Browse files
author
Fabrice Bellard
committed
- 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).
1 parent f4951ef commit 2c90110

File tree

4 files changed

+72
-142
lines changed

4 files changed

+72
-142
lines changed

quickjs-opcode.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,10 @@ DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern a
123123
DEF( get_super, 1, 1, 1, none)
124124
DEF( import, 1, 2, 1, none) /* dynamic module import */
125125

126-
DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
127126
DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
128127
DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
129128
DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
130129
DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
131-
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
132130

133131
DEF( get_ref_value, 1, 2, 3, none)
134132
DEF( put_ref_value, 1, 3, 0, none)

quickjs.c

Lines changed: 46 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -10175,8 +10175,8 @@ static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
1017510175
return 0;
1017610176
}
1017710177

10178-
static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
10179-
BOOL throw_ref_error)
10178+
static inline JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
10179+
BOOL throw_ref_error)
1018010180
{
1018110181
JSObject *p;
1018210182
JSShapeProperty *prs;
@@ -10191,6 +10191,14 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
1019110191
return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
1019210192
return JS_DupValue(ctx, pr->u.value);
1019310193
}
10194+
10195+
/* fast path */
10196+
p = JS_VALUE_GET_OBJ(ctx->global_obj);
10197+
prs = find_own_property(&pr, p, prop);
10198+
if (prs) {
10199+
if (likely((prs->flags & JS_PROP_TMASK) == 0))
10200+
return JS_DupValue(ctx, pr->u.value);
10201+
}
1019410202
return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
1019510203
ctx->global_obj, throw_ref_error);
1019610204
}
@@ -10232,37 +10240,16 @@ static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
1023210240
return 0;
1023310241
}
1023410242

10235-
/* use for strict variable access: test if the variable exists */
10236-
static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
10237-
{
10238-
JSObject *p;
10239-
JSShapeProperty *prs;
10240-
int ret;
10241-
10242-
/* no exotic behavior is possible in global_var_obj */
10243-
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
10244-
prs = find_own_property1(p, prop);
10245-
if (prs) {
10246-
ret = TRUE;
10247-
} else {
10248-
ret = JS_HasProperty(ctx, ctx->global_obj, prop);
10249-
if (ret < 0)
10250-
return -1;
10251-
}
10252-
return ret;
10253-
}
10254-
1025510243
/* flag = 0: normal variable write
1025610244
flag = 1: initialize lexical variable
10257-
flag = 2: normal variable write, strict check was done before
1025810245
*/
10259-
static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
10260-
int flag)
10246+
static inline int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
10247+
int flag)
1026110248
{
1026210249
JSObject *p;
1026310250
JSShapeProperty *prs;
1026410251
JSProperty *pr;
10265-
int flags;
10252+
int ret;
1026610253

1026710254
/* no exotic behavior is possible in global_var_obj */
1026810255
p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
@@ -10283,13 +10270,30 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
1028310270
set_value(ctx, &pr->u.value, val);
1028410271
return 0;
1028510272
}
10286-
/* XXX: add a fast path where the property exists and the object
10287-
is not exotic. Otherwise do as in OP_put_ref_value and remove
10288-
JS_PROP_NO_ADD which is no longer necessary */
10289-
flags = JS_PROP_THROW_STRICT;
10290-
if (is_strict_mode(ctx))
10291-
flags |= JS_PROP_NO_ADD;
10292-
return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags);
10273+
10274+
p = JS_VALUE_GET_OBJ(ctx->global_obj);
10275+
prs = find_own_property(&pr, p, prop);
10276+
if (prs) {
10277+
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
10278+
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
10279+
/* fast path */
10280+
set_value(ctx, &pr->u.value, val);
10281+
return 0;
10282+
}
10283+
}
10284+
/* slow path */
10285+
ret = JS_HasProperty(ctx, ctx->global_obj, prop);
10286+
if (ret < 0) {
10287+
JS_FreeValue(ctx, val);
10288+
return -1;
10289+
}
10290+
if (ret == 0 && is_strict_mode(ctx)) {
10291+
JS_FreeValue(ctx, val);
10292+
JS_ThrowReferenceErrorNotDefined(ctx, prop);
10293+
return -1;
10294+
}
10295+
return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj,
10296+
JS_PROP_THROW_STRICT);
1029310297
}
1029410298

1029510299
/* return -1, FALSE or TRUE */
@@ -17597,21 +17601,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
1759717601
}
1759817602
BREAK;
1759917603

17600-
CASE(OP_check_var):
17601-
{
17602-
int ret;
17603-
JSAtom atom;
17604-
atom = get_u32(pc);
17605-
pc += 4;
17606-
sf->cur_pc = pc;
17607-
17608-
ret = JS_CheckGlobalVar(ctx, atom);
17609-
if (ret < 0)
17610-
goto exception;
17611-
*sp++ = JS_NewBool(ctx, ret);
17612-
}
17613-
BREAK;
17614-
1761517604
CASE(OP_get_var_undef):
1761617605
CASE(OP_get_var):
1761717606
{
@@ -17644,26 +17633,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
1764417633
}
1764517634
BREAK;
1764617635

17647-
CASE(OP_put_var_strict):
17648-
{
17649-
int ret;
17650-
JSAtom atom;
17651-
atom = get_u32(pc);
17652-
pc += 4;
17653-
sf->cur_pc = pc;
17654-
17655-
/* sp[-2] is JS_TRUE or JS_FALSE */
17656-
if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
17657-
JS_ThrowReferenceErrorNotDefined(ctx, atom);
17658-
goto exception;
17659-
}
17660-
ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
17661-
sp -= 2;
17662-
if (unlikely(ret < 0))
17663-
goto exception;
17664-
}
17665-
BREAK;
17666-
1766717636
CASE(OP_check_define_var):
1766817637
{
1766917638
JSAtom atom;
@@ -31578,20 +31547,10 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
3157831547
JSAtom var_name)
3157931548
{
3158031549
int label_pos, end_pos, pos, op;
31581-
BOOL is_strict;
31582-
is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
3158331550

3158431551
/* replace the reference get/put with normal variable
3158531552
accesses */
31586-
if (is_strict) {
31587-
/* need to check if the variable exists before evaluating the right
31588-
expression */
31589-
/* XXX: need an extra OP_true if destructuring an array */
31590-
dbuf_putc(bc, OP_check_var);
31591-
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
31592-
} else {
31593-
/* XXX: need 2 extra OP_true if destructuring an array */
31594-
}
31553+
/* XXX: need 2 extra OP_true if destructuring an array */
3159531554
if (bc_buf[pos_next] == OP_get_ref_value) {
3159631555
dbuf_putc(bc, OP_get_var);
3159731556
dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
@@ -31605,34 +31564,10 @@ static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
3160531564
assert(bc_buf[pos] == OP_label);
3160631565
end_pos = label_pos + 2;
3160731566
op = bc_buf[label_pos];
31608-
if (is_strict) {
31609-
if (op != OP_nop) {
31610-
switch(op) {
31611-
case OP_insert3:
31612-
op = OP_insert2;
31613-
break;
31614-
case OP_perm4:
31615-
op = OP_perm3;
31616-
break;
31617-
case OP_rot3l:
31618-
op = OP_swap;
31619-
break;
31620-
default:
31621-
abort();
31622-
}
31623-
bc_buf[pos++] = op;
31624-
}
31625-
} else {
31626-
if (op == OP_insert3)
31627-
bc_buf[pos++] = OP_dup;
31628-
}
31629-
if (is_strict) {
31630-
bc_buf[pos] = OP_put_var_strict;
31631-
/* XXX: need 1 extra OP_drop if destructuring an array */
31632-
} else {
31633-
bc_buf[pos] = OP_put_var;
31634-
/* XXX: need 2 extra OP_drop if destructuring an array */
31635-
}
31567+
if (op == OP_insert3)
31568+
bc_buf[pos++] = OP_dup;
31569+
bc_buf[pos] = OP_put_var;
31570+
/* XXX: need 2 extra OP_drop if destructuring an array */
3163631571
put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
3163731572
pos += 5;
3163831573
/* pad with OP_nop */
@@ -34103,12 +34038,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
3410334038
if (OPTIMIZE) {
3410434039
/* Transformation:
3410534040
insert2 put_field(a) drop -> put_field(a)
34106-
insert2 put_var_strict(a) drop -> put_var_strict(a)
3410734041
*/
34108-
if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
34042+
if (code_match(&cc, pos_next, OP_put_field, OP_drop, -1)) {
3410934043
if (cc.line_num >= 0) line_num = cc.line_num;
3411034044
add_pc2line_info(s, bc_out.size, line_num);
34111-
dbuf_putc(&bc_out, cc.op);
34045+
dbuf_putc(&bc_out, OP_put_field);
3411234046
dbuf_put_u32(&bc_out, cc.atom);
3411334047
pos_next = cc.pos;
3411434048
break;
@@ -34254,7 +34188,6 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
3425434188
/* transformation:
3425534189
post_inc put_x drop -> inc put_x
3425634190
post_inc perm3 put_field drop -> inc put_field
34257-
post_inc perm3 put_var_strict drop -> inc put_var_strict
3425834191
post_inc perm4 put_array_el drop -> inc put_array_el
3425934192
*/
3426034193
int op1, idx;
@@ -34273,11 +34206,11 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
3427334206
put_short_code(&bc_out, op1, idx);
3427434207
break;
3427534208
}
34276-
if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
34209+
if (code_match(&cc, pos_next, OP_perm3, OP_put_field, OP_drop, -1)) {
3427734210
if (cc.line_num >= 0) line_num = cc.line_num;
3427834211
add_pc2line_info(s, bc_out.size, line_num);
3427934212
dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
34280-
dbuf_putc(&bc_out, cc.op);
34213+
dbuf_putc(&bc_out, OP_put_field);
3428134214
dbuf_put_u32(&bc_out, cc.atom);
3428234215
pos_next = cc.pos;
3428334216
break;

test262_errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-
55
test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js:27: SyntaxError: invalid increment/decrement operand
66
test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33: SyntaxError: invalid assignment left-hand side
77
test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side
8+
test262/test/language/identifier-resolution/assign-to-global-undefined.js:20: strict mode: expected error
89
test262/test/staging/sm/Date/UTC-convert-all-arguments.js:13: Test262Error: index 1: expected 42, got Error: didn't throw Expected SameValue(«Error: didn't throw», «42») to be true
910
test262/test/staging/sm/Date/UTC-convert-all-arguments.js:13: strict mode: Test262Error: index 1: expected 42, got Error: didn't throw Expected SameValue(«Error: didn't throw», «42») to be true
1011
test262/test/staging/sm/Date/constructor-convert-all-arguments.js:13: Test262Error: index undefined: expected 42, got Error: didn't throw Expected SameValue(«Error: didn't throw», «42») to be true

tests/microbench.js

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -527,22 +527,21 @@ function global_read(n)
527527
return n * 4;
528528
}
529529

530-
// non strict version
531-
var global_write =
532-
(1, eval)(`(function global_write(n)
533-
{
534-
var j;
535-
for(j = 0; j < n; j++) {
536-
global_var0 = j;
537-
global_var0 = j;
538-
global_var0 = j;
539-
global_var0 = j;
540-
}
541-
return n * 4;
542-
})`);
530+
function global_write(n)
531+
{
532+
var j;
533+
for(j = 0; j < n; j++) {
534+
global_var0 = j;
535+
global_var0 = j;
536+
global_var0 = j;
537+
global_var0 = j;
538+
}
539+
return n * 4;
540+
}
543541

544542
function global_write_strict(n)
545543
{
544+
"use strict";
546545
var j;
547546
for(j = 0; j < n; j++) {
548547
global_var0 = j;
@@ -570,23 +569,22 @@ function local_destruct(n)
570569
var global_v1, global_v2, global_v3, global_v4;
571570
var global_a, global_b, global_c, global_d;
572571

573-
// non strict version
574-
var global_destruct =
575-
(1, eval)(`(function global_destruct(n)
576-
{
577-
var j, v1, v2, v3, v4;
578-
var array = [ 1, 2, 3, 4, 5 ];
579-
var o = { a:1, b:2, c:3, d:4 };
580-
var a, b, c, d;
581-
for(j = 0; j < n; j++) {
582-
[ global_v1, global_v2,, global_v3, ...global_v4] = array;
583-
({ a: global_a, b: global_b, c: global_c, d: global_d } = o);
584-
}
585-
return n * 8;
586-
})`);
572+
function global_destruct(n)
573+
{
574+
var j, v1, v2, v3, v4;
575+
var array = [ 1, 2, 3, 4, 5 ];
576+
var o = { a:1, b:2, c:3, d:4 };
577+
var a, b, c, d;
578+
for(j = 0; j < n; j++) {
579+
[ global_v1, global_v2,, global_v3, ...global_v4] = array;
580+
({ a: global_a, b: global_b, c: global_c, d: global_d } = o);
581+
}
582+
return n * 8;
583+
}
587584

588585
function global_destruct_strict(n)
589586
{
587+
"use strict";
590588
var j, v1, v2, v3, v4;
591589
var array = [ 1, 2, 3, 4, 5 ];
592590
var o = { a:1, b:2, c:3, d:4 };

0 commit comments

Comments
 (0)