Much faster destructuring at the expense of a slight incompatibility

with the spec when direct evals are present (v8 behaves the same way).
This commit is contained in:
Fabrice Bellard
2025-11-03 17:08:59 +01:00
parent a6816be23a
commit e015918dd8
3 changed files with 103 additions and 41 deletions

2
TODO
View File

@@ -63,4 +63,4 @@ Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Test262: Test262:
Result: 56/83147 errors, 1646 excluded, 5538 skipped Result: 66/83147 errors, 1646 excluded, 5538 skipped

View File

@@ -25143,19 +25143,20 @@ done:
return js_parse_expect(s, ']'); return js_parse_expect(s, ']');
} }
/* XXX: remove */ /* check if scope chain contains a with statement */
static BOOL has_with_scope(JSFunctionDef *s, int scope_level) static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
{ {
/* check if scope chain contains a with statement */
while (s) { while (s) {
/* no with in strict mode */
if (!(s->js_mode & JS_MODE_STRICT)) {
int scope_idx = s->scopes[scope_level].first; int scope_idx = s->scopes[scope_level].first;
while (scope_idx >= 0) { while (scope_idx >= 0) {
JSVarDef *vd = &s->vars[scope_idx]; JSVarDef *vd = &s->vars[scope_idx];
if (vd->var_name == JS_ATOM__with_) if (vd->var_name == JS_ATOM__with_)
return TRUE; return TRUE;
scope_idx = vd->scope_next; scope_idx = vd->scope_next;
} }
}
/* check parent scopes */ /* check parent scopes */
scope_level = s->parent_scope_level; scope_level = s->parent_scope_level;
s = s->parent; s = s->parent;
@@ -25187,7 +25188,11 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
} }
if (name == JS_ATOM_this || name == JS_ATOM_new_target) if (name == JS_ATOM_this || name == JS_ATOM_new_target)
goto invalid_lvalue; goto invalid_lvalue;
if (has_with_scope(fd, scope)) {
depth = 2; /* will generate OP_get_ref_value */ depth = 2; /* will generate OP_get_ref_value */
} else {
depth = 0;
}
break; break;
case OP_get_field: case OP_get_field:
name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
@@ -25224,6 +25229,7 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
/* get the value but keep the object/fields on the stack */ /* get the value but keep the object/fields on the stack */
switch(opcode) { switch(opcode) {
case OP_scope_get_var: case OP_scope_get_var:
if (depth != 0) {
label = new_label(s); label = new_label(s);
if (label < 0) if (label < 0)
return -1; return -1;
@@ -25234,6 +25240,11 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
update_label(fd, label, 1); update_label(fd, label, 1);
emit_op(s, OP_get_ref_value); emit_op(s, OP_get_ref_value);
opcode = OP_get_ref_value; opcode = OP_get_ref_value;
} else {
emit_op(s, OP_scope_get_var);
emit_atom(s, name);
emit_u16(s, scope);
}
break; break;
case OP_get_field: case OP_get_field:
emit_op(s, OP_get_field2); emit_op(s, OP_get_field2);
@@ -25258,6 +25269,7 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
} else { } else {
switch(opcode) { switch(opcode) {
case OP_scope_get_var: case OP_scope_get_var:
if (depth != 0) {
label = new_label(s); label = new_label(s);
if (label < 0) if (label < 0)
return -1; return -1;
@@ -25267,6 +25279,7 @@ static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
emit_u16(s, scope); emit_u16(s, scope);
update_label(fd, label, 1); update_label(fd, label, 1);
opcode = OP_get_ref_value; opcode = OP_get_ref_value;
}
break; break;
default: default:
break; break;
@@ -25300,6 +25313,21 @@ static void put_lvalue(JSParseState *s, int opcode, int scope,
BOOL is_let) BOOL is_let)
{ {
switch(opcode) { switch(opcode) {
case OP_scope_get_var:
/* depth = 0 */
switch(special) {
case PUT_LVALUE_NOKEEP:
case PUT_LVALUE_NOKEEP_DEPTH:
case PUT_LVALUE_KEEP_SECOND:
case PUT_LVALUE_NOKEEP_BOTTOM:
break;
case PUT_LVALUE_KEEP_TOP:
emit_op(s, OP_dup);
break;
default:
abort();
}
break;
case OP_get_field: case OP_get_field:
case OP_scope_get_private_field: case OP_scope_get_private_field:
/* depth = 1 */ /* depth = 1 */
@@ -25371,8 +25399,6 @@ static void put_lvalue(JSParseState *s, int opcode, int scope,
switch(opcode) { switch(opcode) {
case OP_scope_get_var: /* val -- */ case OP_scope_get_var: /* val -- */
assert(special == PUT_LVALUE_NOKEEP ||
special == PUT_LVALUE_NOKEEP_DEPTH);
emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
emit_u32(s, name); /* has refcount */ emit_u32(s, name); /* has refcount */
emit_u16(s, scope); emit_u16(s, scope);
@@ -25726,6 +25752,8 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
/* swap ref and lvalue object if any */ /* swap ref and lvalue object if any */
if (prop_name == JS_ATOM_NULL) { if (prop_name == JS_ATOM_NULL) {
switch(depth_lvalue) { switch(depth_lvalue) {
case 0:
break;
case 1: case 1:
/* source prop x -> x source prop */ /* source prop x -> x source prop */
emit_op(s, OP_rot3r); emit_op(s, OP_rot3r);
@@ -25739,9 +25767,13 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
emit_op(s, OP_rot5l); emit_op(s, OP_rot5l);
emit_op(s, OP_rot5l); emit_op(s, OP_rot5l);
break; break;
default:
abort();
} }
} else { } else {
switch(depth_lvalue) { switch(depth_lvalue) {
case 0:
break;
case 1: case 1:
/* source x -> x source */ /* source x -> x source */
emit_op(s, OP_swap); emit_op(s, OP_swap);
@@ -25754,6 +25786,8 @@ static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
/* source x y z -> x y z source */ /* source x y z -> x y z source */
emit_op(s, OP_rot4l); emit_op(s, OP_rot4l);
break; break;
default:
abort();
} }
} }
} }
@@ -27405,7 +27439,7 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
} }
if (op == '=') { if (op == '=') {
if (opcode == OP_get_ref_value && name == name0) { if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) {
set_object_name(s, name); set_object_name(s, name);
} }
} else { } else {
@@ -27440,11 +27474,14 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
return -1; return -1;
} }
if (opcode == OP_get_ref_value && name == name0) { if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) {
set_object_name(s, name); set_object_name(s, name);
} }
switch(depth_lvalue) { switch(depth_lvalue) {
case 0:
emit_op(s, OP_dup);
break;
case 1: case 1:
emit_op(s, OP_insert2); emit_op(s, OP_insert2);
break; break;
@@ -32202,14 +32239,22 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
} }
} }
break; break;
case OP_scope_put_var:
if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
/* in non strict mode, modifying the function name is ignored */
dbuf_putc(bc, OP_drop);
goto done;
}
goto local_scope_var;
case OP_scope_get_ref: case OP_scope_get_ref:
dbuf_putc(bc, OP_undefined); dbuf_putc(bc, OP_undefined);
/* fall thru */ goto local_scope_var;
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:
case OP_scope_put_var:
case OP_scope_put_var_init: case OP_scope_put_var_init:
local_scope_var:
is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init); is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
if (var_idx & ARGUMENT_VAR_OFFSET) { if (var_idx & ARGUMENT_VAR_OFFSET) {
dbuf_putc(bc, OP_get_arg + is_put); dbuf_putc(bc, OP_get_arg + is_put);
@@ -32497,15 +32542,22 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
dbuf_put_u16(bc, idx); dbuf_put_u16(bc, idx);
} }
break; break;
case OP_scope_put_var:
if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
/* in non strict mode, modifying the function name is ignored */
dbuf_putc(bc, OP_drop);
goto done;
}
goto closure_scope_var;
case OP_scope_get_ref: case OP_scope_get_ref:
/* XXX: should create a dummy object with a named slot that is /* XXX: should create a dummy object with a named slot that is
a reference to the closure variable */ a reference to the closure variable */
dbuf_putc(bc, OP_undefined); dbuf_putc(bc, OP_undefined);
/* fall thru */ goto closure_scope_var;
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_init: case OP_scope_put_var_init:
closure_scope_var:
is_put = (op == OP_scope_put_var || is_put = (op == OP_scope_put_var ||
op == OP_scope_put_var_init); op == OP_scope_put_var_init);
if (is_put) { if (is_put) {

View File

@@ -5,6 +5,19 @@ test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-
test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js:27: SyntaxError: invalid increment/decrement operand test262/test/annexB/language/expressions/assignmenttargettype/callexpression-in-prefix-update.js:27: SyntaxError: invalid increment/decrement operand
test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33: SyntaxError: invalid assignment left-hand side test262/test/annexB/language/expressions/assignmenttargettype/callexpression.js:33: SyntaxError: invalid assignment left-hand side
test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side test262/test/annexB/language/expressions/assignmenttargettype/cover-callexpression-and-asyncarrowhead.js:20: SyntaxError: invalid assignment left-hand side
test262/test/language/expressions/assignment/S11.13.1_A6_T1.js:23: Test262Error: #1: innerX === undefined. Actual: 1
test262/test/language/expressions/assignment/S11.13.1_A6_T2.js:23: Test262Error: #1: innerX === 2. Actual: 1
test262/test/language/expressions/compound-assignment/S11.13.2_A6.1_T1.js:24: Test262Error: #1: innerX === 2. Actual: 12
test262/test/language/expressions/compound-assignment/S11.13.2_A6.2_T1.js:24: Test262Error: #1: innerX === 2. Actual: 5
test262/test/language/expressions/compound-assignment/S11.13.2_A6.3_T1.js:24: Test262Error: #1: innerX === 2. Actual: 3
test262/test/language/expressions/compound-assignment/S11.13.2_A6.4_T1.js:24: Test262Error: #1: innerX === 2. Actual: 4
test262/test/language/expressions/compound-assignment/S11.13.2_A6.5_T1.js:24: Test262Error: #1: innerX === 2. Actual: 4
test262/test/language/expressions/compound-assignment/S11.13.2_A6.6_T1.js:24: Test262Error: #1: innerX === 2. Actual: 8
test262/test/language/expressions/compound-assignment/S11.13.2_A6.7_T1.js:24: Test262Error: #1: innerX === 2. Actual: 4
test262/test/language/expressions/compound-assignment/S11.13.2_A6.8_T1.js:24: Test262Error: #1: innerX === 2. Actual: 4
test262/test/language/expressions/compound-assignment/S11.13.2_A6.9_T1.js:24: Test262Error: #1: innerX === 2. Actual: 1
test262/test/language/expressions/compound-assignment/S11.13.2_A6.10_T1.js:24: Test262Error: #1: innerX === 2. Actual: 5
test262/test/language/expressions/compound-assignment/S11.13.2_A6.11_T1.js:24: Test262Error: #1: innerX === 2. Actual: 5
test262/test/language/identifier-resolution/assign-to-global-undefined.js:20: strict mode: expected error test262/test/language/identifier-resolution/assign-to-global-undefined.js:20: strict mode: expected error
test262/test/staging/sm/Function/arguments-parameter-shadowing.js:14: Test262Error: Expected SameValue(«true», «false») to be true test262/test/staging/sm/Function/arguments-parameter-shadowing.js:14: Test262Error: Expected SameValue(«true», «false») to be true
test262/test/staging/sm/Function/constructor-binding.js:11: Test262Error: Expected SameValue(«"function"», «"undefined"») to be true test262/test/staging/sm/Function/constructor-binding.js:11: Test262Error: Expected SameValue(«"function"», «"undefined"») to be true
@@ -35,9 +48,6 @@ test262/test/staging/sm/class/boundFunctionSubclassing.js:9: strict mode: Test26
test262/test/staging/sm/class/strictExecution.js:13: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/sm/class/strictExecution.js:13: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
test262/test/staging/sm/class/superPropOrdering.js:17: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/sm/class/superPropOrdering.js:17: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
test262/test/staging/sm/class/superPropOrdering.js:17: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all test262/test/staging/sm/class/superPropOrdering.js:17: strict mode: Test262Error: Expected a TypeError to be thrown but no exception was thrown at all
test262/test/staging/sm/expressions/short-circuit-compound-assignment-const.js:96: TypeError: 'a' is read-only
test262/test/staging/sm/expressions/short-circuit-compound-assignment-tdz.js:18: Test262Error: Expected a ReferenceError but got a TypeError
test262/test/staging/sm/expressions/short-circuit-compound-assignment-tdz.js:18: strict mode: Test262Error: Expected a ReferenceError but got a TypeError
test262/test/staging/sm/generators/syntax.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all test262/test/staging/sm/generators/syntax.js:50: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-arguments.js:13: Test262Error: Expected SameValue(«"object"», «"function"») to be true test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-arguments.js:13: Test262Error: Expected SameValue(«"object"», «"function"») to be true
test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-eval.js:11: Test262Error: Expected SameValue(«"outer-gouter-geval-gtruefalseq"», «"outer-geval-gwith-gtruefalseq"») to be true test262/test/staging/sm/lexical-environment/block-scoped-functions-annex-b-eval.js:11: Test262Error: Expected SameValue(«"outer-gouter-geval-gtruefalseq"», «"outer-geval-gwith-gtruefalseq"») to be true