Implement static class initializer blocks (#144)
Spec compliance bug: "await" is illegal inside initializer blocks _except_ when used as an identifier in a function expression, like so: class C { static { var f = function await() {} } } It is somewhat complicated to make the parser understand the distinction and such code is probably rare or non-existent so I decided to leave well enough alone for now.
This commit is contained in:
parent
51633afe56
commit
b5148b212e
3 changed files with 72 additions and 7 deletions
71
quickjs.c
71
quickjs.c
|
@ -17820,6 +17820,7 @@ typedef enum JSParseFunctionEnum {
|
|||
JS_PARSE_FUNC_GETTER,
|
||||
JS_PARSE_FUNC_SETTER,
|
||||
JS_PARSE_FUNC_METHOD,
|
||||
JS_PARSE_FUNC_CLASS_STATIC_INIT,
|
||||
JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
|
||||
JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
|
||||
} JSParseFunctionEnum;
|
||||
|
@ -18688,7 +18689,13 @@ static __exception int next_token(JSParseState *s)
|
|||
s->token.u.ident.atom = atom;
|
||||
s->token.u.ident.has_escape = ident_has_escape;
|
||||
s->token.u.ident.is_reserved = FALSE;
|
||||
if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
|
||||
// TODO(bnoordhuis) accept await when used in a function expression
|
||||
// inside the static initializer block
|
||||
if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT &&
|
||||
(atom == JS_ATOM_arguments || atom == JS_ATOM_await)) {
|
||||
s->token.u.ident.is_reserved = TRUE;
|
||||
s->token.val = TOK_IDENT;
|
||||
} else if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
|
||||
(s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
|
||||
(s->cur_func->js_mode & JS_MODE_STRICT)) ||
|
||||
(s->token.u.ident.atom == JS_ATOM_yield &&
|
||||
|
@ -20836,6 +20843,44 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
if (is_static) {
|
||||
if (next_token(s))
|
||||
goto fail;
|
||||
if (s->token.val == '{') {
|
||||
ClassFieldsDef *cf = &class_fields[is_static];
|
||||
if (!cf->fields_init_fd)
|
||||
if (emit_class_init_start(s, cf))
|
||||
goto fail;
|
||||
s->cur_func = cf->fields_init_fd;
|
||||
// stack is now: <empty>
|
||||
JSFunctionDef *init;
|
||||
if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
|
||||
JS_FUNC_NORMAL, JS_ATOM_NULL,
|
||||
s->token.ptr, s->token.line_num,
|
||||
JS_PARSE_EXPORT_NONE, &init) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
// stack is now: fclosure
|
||||
push_scope(s);
|
||||
emit_op(s, OP_scope_get_var);
|
||||
emit_atom(s, JS_ATOM_this);
|
||||
emit_u16(s, 0);
|
||||
// stack is now: fclosure this
|
||||
if (class_name != JS_ATOM_NULL) {
|
||||
// TODO(bnoordhuis) pass as argument to init method?
|
||||
emit_op(s, OP_dup);
|
||||
emit_op(s, OP_scope_put_var_init);
|
||||
emit_atom(s, class_name);
|
||||
emit_u16(s, s->cur_func->scope_level);
|
||||
}
|
||||
emit_op(s, OP_swap);
|
||||
// stack is now: this fclosure
|
||||
emit_op(s, OP_call_method);
|
||||
emit_u16(s, 0);
|
||||
// stack is now: returnvalue
|
||||
emit_op(s, OP_drop);
|
||||
// stack is now: <empty>
|
||||
pop_scope(s);
|
||||
s->cur_func = s->cur_func->parent;
|
||||
continue;
|
||||
}
|
||||
/* allow "static" field name */
|
||||
if (s->token.val == ';' || s->token.val == '=') {
|
||||
is_static = FALSE;
|
||||
|
@ -24085,6 +24130,10 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
|
|||
js_parse_error(s, "return not in a function");
|
||||
goto fail;
|
||||
}
|
||||
if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
|
||||
js_parse_error(s, "return in a static initializer block");
|
||||
goto fail;
|
||||
}
|
||||
if (next_token(s))
|
||||
goto fail;
|
||||
if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
|
||||
|
@ -30646,7 +30695,9 @@ static __exception int js_parse_function_decl2(JSParseState *s,
|
|||
(func_kind & JS_FUNC_GENERATOR)) ||
|
||||
(s->token.u.ident.atom == JS_ATOM_await &&
|
||||
func_type == JS_PARSE_FUNC_EXPR &&
|
||||
(func_kind & JS_FUNC_ASYNC))) {
|
||||
(func_kind & JS_FUNC_ASYNC)) ||
|
||||
(s->token.u.ident.atom == JS_ATOM_await &&
|
||||
func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT)) {
|
||||
return js_parse_error_reserved_identifier(s);
|
||||
}
|
||||
}
|
||||
|
@ -30740,7 +30791,8 @@ static __exception int js_parse_function_decl2(JSParseState *s,
|
|||
func_type == JS_PARSE_FUNC_SETTER ||
|
||||
func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
|
||||
func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
|
||||
fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
|
||||
fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
|
||||
func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
|
||||
fd->has_this_binding = fd->has_arguments_binding;
|
||||
fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
|
||||
if (func_type == JS_PARSE_FUNC_ARROW) {
|
||||
|
@ -30748,6 +30800,11 @@ static __exception int js_parse_function_decl2(JSParseState *s,
|
|||
fd->super_call_allowed = fd->parent->super_call_allowed;
|
||||
fd->super_allowed = fd->parent->super_allowed;
|
||||
fd->arguments_allowed = fd->parent->arguments_allowed;
|
||||
} else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
|
||||
fd->new_target_allowed = TRUE; // although new.target === undefined
|
||||
fd->super_call_allowed = FALSE;
|
||||
fd->super_allowed = TRUE;
|
||||
fd->arguments_allowed = FALSE;
|
||||
} else {
|
||||
fd->new_target_allowed = TRUE;
|
||||
fd->super_call_allowed = fd->is_derived_class_constructor;
|
||||
|
@ -30785,7 +30842,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
|
|||
if (add_arg(ctx, fd, name) < 0)
|
||||
goto fail;
|
||||
fd->defined_arg_count = 1;
|
||||
} else {
|
||||
} else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
|
||||
if (s->token.val == '(') {
|
||||
int skip_bits;
|
||||
/* if there is an '=' inside the parameter list, we
|
||||
|
@ -31005,8 +31062,10 @@ static __exception int js_parse_function_decl2(JSParseState *s,
|
|||
}
|
||||
}
|
||||
|
||||
if (js_parse_expect(s, '{'))
|
||||
goto fail;
|
||||
// js_parse_class() already consumed the '{'
|
||||
if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT)
|
||||
if (js_parse_expect(s, '{'))
|
||||
goto fail;
|
||||
|
||||
if (js_parse_directives(s))
|
||||
goto fail;
|
||||
|
|
|
@ -79,7 +79,7 @@ class-fields-private
|
|||
class-fields-private-in=skip
|
||||
class-fields-public
|
||||
class-methods-private
|
||||
class-static-block=skip
|
||||
class-static-block
|
||||
class-static-fields-private
|
||||
class-static-fields-public
|
||||
class-static-methods-private
|
||||
|
|
|
@ -92,6 +92,8 @@ test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds
|
|||
test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js:22: strict mode: Test262Error: Reflect.set(sample, "-1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
|
||||
test262/test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js:39: Test262Error: Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
|
||||
test262/test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js:39: strict mode: Test262Error: Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
|
||||
test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: unexpected error type: Test262: This statement should not be evaluated.
|
||||
test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: strict mode: unexpected error type: Test262: This statement should not be evaluated.
|
||||
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: Test262Error: Expected a DummyError but got a TypeError
|
||||
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError
|
||||
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: Test262Error: Expected a DummyError but got a TypeError
|
||||
|
@ -124,6 +126,10 @@ test262/test/language/expressions/delete/super-property-null-base.js:26: Test262
|
|||
test262/test/language/expressions/delete/super-property-null-base.js:26: strict mode: Test262Error: Expected a ReferenceError but got a TypeError
|
||||
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
|
||||
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
|
||||
test262/test/language/expressions/function/static-init-await-binding.js:16: SyntaxError: 'await' is a reserved identifier
|
||||
test262/test/language/expressions/function/static-init-await-binding.js:16: strict mode: SyntaxError: 'await' is a reserved identifier
|
||||
test262/test/language/expressions/generators/static-init-await-binding.js:16: SyntaxError: 'await' is a reserved identifier
|
||||
test262/test/language/expressions/generators/static-init-await-binding.js:16: strict mode: SyntaxError: 'await' is a reserved identifier
|
||||
test262/test/language/expressions/logical-assignment/left-hand-side-private-reference-accessor-property-and.js:60: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «false») to be true
|
||||
test262/test/language/expressions/logical-assignment/left-hand-side-private-reference-accessor-property-and.js:60: strict mode: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «false») to be true
|
||||
test262/test/language/expressions/logical-assignment/left-hand-side-private-reference-accessor-property-nullish.js:59: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «1») to be true
|
||||
|
|
Loading…
Reference in a new issue