Skip to content

Commit b5148b2

Browse files
authored
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.
1 parent 51633af commit b5148b2

File tree

3 files changed

+72
-7
lines changed

3 files changed

+72
-7
lines changed

quickjs.c

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17820,6 +17820,7 @@ typedef enum JSParseFunctionEnum {
1782017820
JS_PARSE_FUNC_GETTER,
1782117821
JS_PARSE_FUNC_SETTER,
1782217822
JS_PARSE_FUNC_METHOD,
17823+
JS_PARSE_FUNC_CLASS_STATIC_INIT,
1782317824
JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
1782417825
JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
1782517826
} JSParseFunctionEnum;
@@ -18688,7 +18689,13 @@ static __exception int next_token(JSParseState *s)
1868818689
s->token.u.ident.atom = atom;
1868918690
s->token.u.ident.has_escape = ident_has_escape;
1869018691
s->token.u.ident.is_reserved = FALSE;
18691-
if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
18692+
// TODO(bnoordhuis) accept await when used in a function expression
18693+
// inside the static initializer block
18694+
if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT &&
18695+
(atom == JS_ATOM_arguments || atom == JS_ATOM_await)) {
18696+
s->token.u.ident.is_reserved = TRUE;
18697+
s->token.val = TOK_IDENT;
18698+
} else if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
1869218699
(s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
1869318700
(s->cur_func->js_mode & JS_MODE_STRICT)) ||
1869418701
(s->token.u.ident.atom == JS_ATOM_yield &&
@@ -20836,6 +20843,44 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
2083620843
if (is_static) {
2083720844
if (next_token(s))
2083820845
goto fail;
20846+
if (s->token.val == '{') {
20847+
ClassFieldsDef *cf = &class_fields[is_static];
20848+
if (!cf->fields_init_fd)
20849+
if (emit_class_init_start(s, cf))
20850+
goto fail;
20851+
s->cur_func = cf->fields_init_fd;
20852+
// stack is now: <empty>
20853+
JSFunctionDef *init;
20854+
if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
20855+
JS_FUNC_NORMAL, JS_ATOM_NULL,
20856+
s->token.ptr, s->token.line_num,
20857+
JS_PARSE_EXPORT_NONE, &init) < 0) {
20858+
goto fail;
20859+
}
20860+
// stack is now: fclosure
20861+
push_scope(s);
20862+
emit_op(s, OP_scope_get_var);
20863+
emit_atom(s, JS_ATOM_this);
20864+
emit_u16(s, 0);
20865+
// stack is now: fclosure this
20866+
if (class_name != JS_ATOM_NULL) {
20867+
// TODO(bnoordhuis) pass as argument to init method?
20868+
emit_op(s, OP_dup);
20869+
emit_op(s, OP_scope_put_var_init);
20870+
emit_atom(s, class_name);
20871+
emit_u16(s, s->cur_func->scope_level);
20872+
}
20873+
emit_op(s, OP_swap);
20874+
// stack is now: this fclosure
20875+
emit_op(s, OP_call_method);
20876+
emit_u16(s, 0);
20877+
// stack is now: returnvalue
20878+
emit_op(s, OP_drop);
20879+
// stack is now: <empty>
20880+
pop_scope(s);
20881+
s->cur_func = s->cur_func->parent;
20882+
continue;
20883+
}
2083920884
/* allow "static" field name */
2084020885
if (s->token.val == ';' || s->token.val == '=') {
2084120886
is_static = FALSE;
@@ -24085,6 +24130,10 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
2408524130
js_parse_error(s, "return not in a function");
2408624131
goto fail;
2408724132
}
24133+
if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
24134+
js_parse_error(s, "return in a static initializer block");
24135+
goto fail;
24136+
}
2408824137
if (next_token(s))
2408924138
goto fail;
2409024139
if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
@@ -30646,7 +30695,9 @@ static __exception int js_parse_function_decl2(JSParseState *s,
3064630695
(func_kind & JS_FUNC_GENERATOR)) ||
3064730696
(s->token.u.ident.atom == JS_ATOM_await &&
3064830697
func_type == JS_PARSE_FUNC_EXPR &&
30649-
(func_kind & JS_FUNC_ASYNC))) {
30698+
(func_kind & JS_FUNC_ASYNC)) ||
30699+
(s->token.u.ident.atom == JS_ATOM_await &&
30700+
func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT)) {
3065030701
return js_parse_error_reserved_identifier(s);
3065130702
}
3065230703
}
@@ -30740,14 +30791,20 @@ static __exception int js_parse_function_decl2(JSParseState *s,
3074030791
func_type == JS_PARSE_FUNC_SETTER ||
3074130792
func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
3074230793
func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
30743-
fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
30794+
fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
30795+
func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
3074430796
fd->has_this_binding = fd->has_arguments_binding;
3074530797
fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
3074630798
if (func_type == JS_PARSE_FUNC_ARROW) {
3074730799
fd->new_target_allowed = fd->parent->new_target_allowed;
3074830800
fd->super_call_allowed = fd->parent->super_call_allowed;
3074930801
fd->super_allowed = fd->parent->super_allowed;
3075030802
fd->arguments_allowed = fd->parent->arguments_allowed;
30803+
} else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
30804+
fd->new_target_allowed = TRUE; // although new.target === undefined
30805+
fd->super_call_allowed = FALSE;
30806+
fd->super_allowed = TRUE;
30807+
fd->arguments_allowed = FALSE;
3075130808
} else {
3075230809
fd->new_target_allowed = TRUE;
3075330810
fd->super_call_allowed = fd->is_derived_class_constructor;
@@ -30785,7 +30842,7 @@ static __exception int js_parse_function_decl2(JSParseState *s,
3078530842
if (add_arg(ctx, fd, name) < 0)
3078630843
goto fail;
3078730844
fd->defined_arg_count = 1;
30788-
} else {
30845+
} else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
3078930846
if (s->token.val == '(') {
3079030847
int skip_bits;
3079130848
/* if there is an '=' inside the parameter list, we
@@ -31005,8 +31062,10 @@ static __exception int js_parse_function_decl2(JSParseState *s,
3100531062
}
3100631063
}
3100731064

31008-
if (js_parse_expect(s, '{'))
31009-
goto fail;
31065+
// js_parse_class() already consumed the '{'
31066+
if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT)
31067+
if (js_parse_expect(s, '{'))
31068+
goto fail;
3101031069

3101131070
if (js_parse_directives(s))
3101231071
goto fail;

test262.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class-fields-private
7979
class-fields-private-in=skip
8080
class-fields-public
8181
class-methods-private
82-
class-static-block=skip
82+
class-static-block
8383
class-static-fields-private
8484
class-static-fields-public
8585
class-static-methods-private

test262_errors.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds
9292
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.)
9393
test262/test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js:39: Test262Error: Expected SameValue(«false», «true») to be true (Testing with Float64Array.)
9494
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.)
95+
test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: unexpected error type: Test262: This statement should not be evaluated.
96+
test262/test/language/expressions/arrow-function/static-init-await-reference.js:12: strict mode: unexpected error type: Test262: This statement should not be evaluated.
9597
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: Test262Error: Expected a DummyError but got a TypeError
9698
test262/test/language/expressions/assignment/target-member-computed-reference-null.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError
9799
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
124126
test262/test/language/expressions/delete/super-property-null-base.js:26: strict mode: Test262Error: Expected a ReferenceError but got a TypeError
125127
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
126128
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
129+
test262/test/language/expressions/function/static-init-await-binding.js:16: SyntaxError: 'await' is a reserved identifier
130+
test262/test/language/expressions/function/static-init-await-binding.js:16: strict mode: SyntaxError: 'await' is a reserved identifier
131+
test262/test/language/expressions/generators/static-init-await-binding.js:16: SyntaxError: 'await' is a reserved identifier
132+
test262/test/language/expressions/generators/static-init-await-binding.js:16: strict mode: SyntaxError: 'await' is a reserved identifier
127133
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
128134
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
129135
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

0 commit comments

Comments
 (0)