Skip to content

Commit 4748971

Browse files
committedJul 10, 2024
[JSC] Disallow yield/await expressions in class field initializers
https://bugs.webkit.org/show_bug.cgi?id=276438 rdar://119044881 Reviewed by NOBODY (OOPS!). The language spec doesn't explictly disallow yield and await expressions in class field initializers, however it implicitly does given that the expression is effectively evaluated as if it's inside a method. Additionally, the consensus seems to be that these expressions should not be allowed, see: tc39/ecma262#3333 The yield and await expressions are now handled differently, but similar to how they are each handled in other contexts. This also seems to be the least disruptive way to handle existing scripts, as well as consistent with other engines: yield: raise syntax error await: parse as an identifier Also adding a new test that generates and verifies scripts containing a class with a field initializer containing yield and await, where the class is either not nested or nested inside generator or async functions to verify the behavior of various combinations. * Source/JavaScriptCore/parser/Parser.cpp: (JSC::Parser<LexerType>::parseYieldExpression): (JSC::Parser<LexerType>::parseAwaitExpression): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseUnaryExpression):
1 parent 0f7c9d3 commit 4748971

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Tests for 'yield' and 'await' inside class field initializers, where the class is declared inside
2+
// async and generator functions.
3+
const verbose = false;
4+
5+
const wrappers = ['none', 'generator', 'async'];
6+
const fieldModifiers = ['', 'static'];
7+
const fieldInitExprs = ['yield', 'yield 42', 'await', 'await 3'];
8+
9+
function genTestCases() {
10+
let cases = [];
11+
for (const wrapper of wrappers) {
12+
for (const fieldModifier of fieldModifiers) {
13+
for (const fieldInitExpr of fieldInitExprs) {
14+
cases.push({ wrapper, fieldModifier, fieldInitExpr });
15+
}
16+
}
17+
}
18+
return cases;
19+
}
20+
21+
function genTestScript(c) {
22+
let tabs = 0;
23+
let script = "";
24+
25+
function append(line) {
26+
for (t = 0; t < tabs; t++)
27+
script += ' ';
28+
script += line + '\n';
29+
}
30+
31+
switch (c.wrapper) {
32+
case 'generator':
33+
append('function * g() {');
34+
break;
35+
case 'async':
36+
append('async function f() {');
37+
break;
38+
case 'none':
39+
break;
40+
}
41+
tabs++;
42+
append('class C {');
43+
tabs++;
44+
append(`${c.fieldModifier} f = ${c.fieldInitExpr};`);
45+
tabs--;
46+
append('}');
47+
tabs--;
48+
if (c.wrapper !== 'none') append('}');
49+
return script;
50+
}
51+
52+
function expected(c, result, error) {
53+
if (c.fieldInitExpr === 'await') {
54+
// 'await' will parse as an identifier.
55+
if (c.wrapper === 'none' && c.fieldModifier === 'static') {
56+
// In this case, 'await' as identifier produces a ReferenceError.
57+
return result === null && error instanceof ReferenceError;
58+
}
59+
// In these cases, 'await' as identifier has value 'undefined').
60+
return result === undefined && error === null;
61+
}
62+
// All other cases should result in a SyntaxError.
63+
return result === null && error instanceof SyntaxError;
64+
}
65+
66+
cases = genTestCases();
67+
68+
for (const c of cases) {
69+
let script = genTestScript(c);
70+
let result = null;
71+
let error = null;
72+
try {
73+
result = eval(script);
74+
} catch (e) {
75+
error = e;
76+
}
77+
78+
if (verbose || !expected(c, result, error)) {
79+
print(`Case: ${c.wrapper}:${c.fieldModifier}:${c.fieldInitExpr}`);
80+
print(`Script:\n${script}`);
81+
if (result != null) {
82+
print("Result: " + result);
83+
} else if (error != null) {
84+
print("Error: " + error);
85+
} else {
86+
print("Expecting either result or error!")
87+
}
88+
}
89+
}

‎Source/JavaScriptCore/parser/Parser.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -4326,6 +4326,9 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseYieldExpress
43264326
// http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
43274327
failIfTrue(m_parserState.functionParsePhase == FunctionParsePhase::Parameters, "Cannot use yield expression within parameters");
43284328

4329+
// https://github.com/tc39/ecma262/issues/3333
4330+
failIfTrue(m_parserState.isParsingClassFieldInitializer, "Cannot use yield expression inside class field initializer expression");
4331+
43294332
JSTokenLocation location(tokenLocation());
43304333
JSTextPosition divotStart = tokenStartPosition();
43314334
ASSERT(match(YIELD));
@@ -4352,6 +4355,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseAwaitExpress
43524355
ASSERT(currentScope()->isAsyncFunction() || isModuleParseMode(sourceParseMode()));
43534356
ASSERT(isAsyncFunctionParseMode(sourceParseMode()) || isModuleParseMode(sourceParseMode()));
43544357
ASSERT(m_parserState.functionParsePhase != FunctionParsePhase::Parameters);
4358+
ASSERT(!m_parserState.isParsingClassFieldInitializer);
43554359
JSTokenLocation location(tokenLocation());
43564360
JSTextPosition divotStart = tokenStartPosition();
43574361
next();
@@ -5086,7 +5090,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
50865090
semanticFailIfTrue(currentScope()->isStaticBlock(), "The 'await' keyword is disallowed in the IdentifierReference position within static block");
50875091
if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters)
50885092
semanticFailIfFalse(m_parserState.allowAwait, "Cannot use 'await' within a parameter default expression");
5089-
else if (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode()))
5093+
else if (!m_parserState.isParsingClassFieldInitializer && (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())))
50905094
return parseAwaitExpression(context);
50915095

50925096
goto identifierExpression;
@@ -5583,7 +5587,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseUnaryExpress
55835587
bool hasPrefixUpdateOp = false;
55845588
unsigned lastOperator = 0;
55855589

5586-
if (UNLIKELY(match(AWAIT) && (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())))) {
5590+
if (UNLIKELY(match(AWAIT) && !m_parserState.isParsingClassFieldInitializer && (currentFunctionScope()->isAsyncFunctionBoundary() || isModuleParseMode(sourceParseMode())))) {
55875591
semanticFailIfTrue(currentScope()->isStaticBlock(), "Cannot use 'await' within static block");
55885592
return parseAwaitExpression(context);
55895593
}

0 commit comments

Comments
 (0)
Please sign in to comment.