forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[JSC] Disallow yield/await expressions in class field initializers
https://bugs.webkit.org/show_bug.cgi?id=278589 rdar://132338331 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 However, if the field initializer expression is an async function expression, then 'await' is allowed within that expression. 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. * JSTests/stress/yield-await-class-field-initializer-expr.js: Added. (genTestCases): (genTestScript.append): (genTestScript): (expected): (testNegativeCases): (testPositiveCases.assertEq): (testPositiveCases.yieldFn0): * Source/JavaScriptCore/parser/Parser.cpp: (JSC::Parser<LexerType>::parseFunctionBody): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseYieldExpression): (JSC::Parser<LexerType>::parseAwaitExpression): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseUnaryExpression): * Source/JavaScriptCore/parser/Parser.h:
- Loading branch information
Showing
3 changed files
with
126 additions
and
2 deletions.
There are no files selected for viewing
117 changes: 117 additions & 0 deletions
117
JSTests/stress/yield-await-class-field-initializer-expr.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// Tests for 'yield' and 'await' inside class field initializers, where the class is declared inside | ||
// async and generator functions. | ||
const verbose = false; | ||
|
||
const wrappers = ['none', 'generator', 'async']; | ||
const fieldModifiers = ['', 'static']; | ||
const fieldInitExprs = ['yield', 'yield 42', 'function() { yield 21; }', 'await', 'await 3', '() => await 7', 'function() { await 9; }']; | ||
|
||
function genTestCases() { | ||
let cases = []; | ||
for (const wrapper of wrappers) { | ||
for (const fieldModifier of fieldModifiers) { | ||
for (const fieldInitExpr of fieldInitExprs) { | ||
cases.push({ wrapper, fieldModifier, fieldInitExpr }); | ||
} | ||
} | ||
} | ||
return cases; | ||
} | ||
|
||
function genTestScript(c) { | ||
let tabs = 0; | ||
let script = ""; | ||
|
||
function append(line) { | ||
for (t = 0; t < tabs; t++) | ||
script += ' '; | ||
script += line + '\n'; | ||
} | ||
|
||
switch (c.wrapper) { | ||
case 'generator': | ||
append('function * g() {'); | ||
break; | ||
case 'async': | ||
append('async function f() {'); | ||
break; | ||
case 'none': | ||
break; | ||
} | ||
tabs++; | ||
append('class C {'); | ||
tabs++; | ||
append(`${c.fieldModifier} f = ${c.fieldInitExpr};`); | ||
tabs--; | ||
append('}'); | ||
tabs--; | ||
if (c.wrapper !== 'none') append('}'); | ||
return script; | ||
} | ||
|
||
function expected(c, result, error) { | ||
if (c.fieldInitExpr === 'await') { | ||
// 'await' will parse as an identifier. | ||
if (c.wrapper === 'none' && c.fieldModifier === 'static') { | ||
// In this case, 'await' as identifier produces a ReferenceError. | ||
return result === null && error instanceof ReferenceError; | ||
} | ||
// In these cases, 'await' as identifier has value 'undefined'). | ||
return result === undefined && error === null; | ||
} | ||
// All other cases should result in a SyntaxError. | ||
return result === null && error instanceof SyntaxError; | ||
} | ||
|
||
// Verify that 'await' and 'yield' do not parse as keywords (directly) inside class field initializers. | ||
function testNegativeCases() { | ||
cases = genTestCases(); | ||
|
||
for (const c of cases) { | ||
let script = genTestScript(c); | ||
let result = null; | ||
let error = null; | ||
try { | ||
result = eval(script); | ||
} catch (e) { | ||
error = e; | ||
} | ||
|
||
if (verbose || !expected(c, result, error)) { | ||
print(`Case: ${c.wrapper}:${c.fieldModifier}:${c.fieldInitExpr}`); | ||
print(`Script:\n${script}`); | ||
print(`Result: ${result}`); | ||
print(`Error: ${error}\n`); | ||
} | ||
} | ||
} | ||
|
||
// Verify that 'await' and 'yield' work inside anonymous async / generator functions | ||
// used as class field initializers. | ||
function testPositiveCases() { | ||
function assertEq(got, expected) { | ||
if (got !== expected) { | ||
throw Error(`Got: ${got}, Expected: ${expected}`); | ||
} | ||
} | ||
|
||
class C { | ||
asyncFn0 = async y => await y; | ||
asyncFn1 = async () => { return await 5 }; | ||
asyncFn2 = async function(x) { return await x; }; | ||
|
||
yieldFn0 = function* () { yield 9; }; | ||
yieldFn1 = async function* () { yield await 11; }; | ||
}; | ||
let c = new C(); | ||
|
||
c.asyncFn0(3).then(x => assertEq(x, 3)); | ||
c.asyncFn1().then(x => assertEq(x, 5)); | ||
c.asyncFn2(7).then(x => assertEq(x, 7)); | ||
|
||
assertEq(c.yieldFn0().next().value, 9); | ||
c.yieldFn1().next().then(x => assertEq(x.value, 11)); | ||
} | ||
|
||
testNegativeCases(); | ||
testPositiveCases(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters