LibJS: Fix await boundaries in class elements
Treat ordinary and generator function expressions inside class static blocks as boundaries for await binding names, while still rejecting await as an arrow parameter in the static block itself. Parse field initializers without inheriting an enclosing async function's await-expression context, so script field initializers can resolve await as an identifier while computed field names still use the enclosing expression context. Cover static block function expressions, class field initializers, and the AST shape for an await arrow inside a nested function.
This commit is contained in:
parent
0c6f86c80d
commit
99596d68ab
6 changed files with 65 additions and 16 deletions
|
|
@ -555,7 +555,7 @@ impl Parser<'_> {
|
|||
"async generator function is not allowed to be called '{name_str}'"
|
||||
));
|
||||
}
|
||||
if self.flags.in_class_static_init_block && fn_name == utf16!("await") {
|
||||
if self.flags.in_class_static_init_block && is_async && fn_name == utf16!("await") {
|
||||
self.syntax_error("'await' is a reserved word");
|
||||
}
|
||||
}
|
||||
|
|
@ -593,16 +593,15 @@ impl Parser<'_> {
|
|||
self.scope_collector.close_scope();
|
||||
self.pattern_bound_names = saved_pattern_bound_names;
|
||||
|
||||
self.flags.in_class_static_init_block = saved_static_init;
|
||||
self.flags.in_class_field_initializer = saved_field_init;
|
||||
self.flags.new_target_is_valid = saved_new_target;
|
||||
|
||||
if name.is_some() {
|
||||
self.check_identifier_name_for_assignment_validity(fn_name, has_use_strict);
|
||||
}
|
||||
if has_use_strict || kind != FunctionKind::Normal {
|
||||
self.check_parameters_post_body(&parsed.parameter_info, has_use_strict, kind);
|
||||
}
|
||||
self.flags.in_class_static_init_block = saved_static_init;
|
||||
self.flags.in_class_field_initializer = saved_field_init;
|
||||
self.flags.new_target_is_valid = saved_new_target;
|
||||
|
||||
insights.might_need_arguments_object = self.flags.function_might_need_arguments_object;
|
||||
self.flags.function_might_need_arguments_object = saved_might_need_arguments;
|
||||
|
|
@ -1143,13 +1142,16 @@ impl Parser<'_> {
|
|||
|
||||
let init = if self.match_token(TokenType::Equals) {
|
||||
self.consume();
|
||||
let saved_await = self.flags.await_expression_is_valid;
|
||||
let saved_field_init = self.flags.in_class_field_initializer;
|
||||
let saved_super_lookup = self.flags.allow_super_property_lookup;
|
||||
self.flags.await_expression_is_valid = false;
|
||||
self.flags.in_class_field_initializer = true;
|
||||
self.flags.allow_super_property_lookup = true;
|
||||
self.scope_collector.open_class_field_scope(None);
|
||||
let expression = self.parse_assignment_expression();
|
||||
self.scope_collector.close_scope();
|
||||
self.flags.await_expression_is_valid = saved_await;
|
||||
self.flags.in_class_field_initializer = saved_field_init;
|
||||
self.flags.allow_super_property_lookup = saved_super_lookup;
|
||||
Some(Box::new(expression))
|
||||
|
|
|
|||
|
|
@ -2300,8 +2300,10 @@ impl Parser<'_> {
|
|||
} else if self.match_identifier() || self.match_token(TokenType::Await) {
|
||||
let token = self.consume();
|
||||
let value = self.token_value(&token).to_vec();
|
||||
if is_async && value == utf16!("await") {
|
||||
self.syntax_error("'await' is a reserved identifier in async functions");
|
||||
if value == utf16!("await")
|
||||
&& (is_async || self.program_type == ProgramType::Module || self.flags.in_class_static_init_block)
|
||||
{
|
||||
self.syntax_error("'await' is not allowed as an identifier in this context");
|
||||
}
|
||||
// C++ uses rule_start (arrow function start, which is `async` for async arrows).
|
||||
let value_id = self.arena.strings.intern(&value);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@ Program (script) @1:1
|
|||
└─ elements
|
||||
└─ StaticInitializer @2:12
|
||||
└─ FunctionBody @1:1
|
||||
└─ ExpressionStatement @3:9
|
||||
└─ FunctionExpression "" [strict] [arrow] @3:9
|
||||
├─ parameters
|
||||
│ └─ Identifier "await" [argument:0] @3:9
|
||||
└─ body
|
||||
└─ FunctionBody @3:9
|
||||
└─ ReturnStatement @3:9
|
||||
└─ NumericLiteral 0 @3:18
|
||||
└─ FunctionDeclaration "f" [strict] @3:9
|
||||
└─ body
|
||||
└─ FunctionBody @4:13
|
||||
└─ ExpressionStatement @4:13
|
||||
└─ FunctionExpression "" [strict] [arrow] @4:13
|
||||
├─ parameters
|
||||
│ └─ Identifier "await" [argument:0] @4:13
|
||||
└─ body
|
||||
└─ FunctionBody @4:13
|
||||
└─ ReturnStatement @4:13
|
||||
└─ NumericLiteral 0 @4:22
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
class C {
|
||||
static {
|
||||
await => 0;
|
||||
function f() {
|
||||
await => 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,23 @@ test("initializer has correct this value", () => {
|
|||
expect(a.this_name).toBe(a);
|
||||
});
|
||||
|
||||
test("field initializers do not inherit await expressions", () => {
|
||||
var await = 42;
|
||||
|
||||
async function getClass() {
|
||||
return class {
|
||||
field = await;
|
||||
};
|
||||
}
|
||||
|
||||
expect("async () => class { [await] = 1 };").not.toEval();
|
||||
expect("async () => class { field = await 1 };").not.toEval();
|
||||
|
||||
return getClass().then(A => {
|
||||
expect(new A().field).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
test("static fields", () => {
|
||||
class A {
|
||||
static simple = 1;
|
||||
|
|
|
|||
|
|
@ -89,3 +89,26 @@ test("declaring variables", () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("await binding names in nested functions", () => {
|
||||
class A {
|
||||
static {
|
||||
const makeGenerator = function* await(await) {
|
||||
yield await;
|
||||
};
|
||||
|
||||
const generator = makeGenerator(42);
|
||||
expect(generator.next()).toEqual({ value: 42, done: false });
|
||||
expect(generator.next()).toEqual({ value: undefined, done: true });
|
||||
|
||||
(function await(await) {
|
||||
expect(await).toBe(42);
|
||||
})(42);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("await binding names in static blocks without a function boundary", () => {
|
||||
expect(`class A { static { await => 0; } }`).not.toEval();
|
||||
expect(`class A { static { async function await() {} } }`).not.toEval();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue