Skip to content

Commit 975467a

Browse files
committed
async should be able to be used as an imported binding name
https://bugs.webkit.org/show_bug.cgi?id=176573 Reviewed by Darin Adler. JSTests: * modules/import-default-async.js: Added. * modules/import-named-async-as.js: Added. * modules/import-named-async.js: Added. * modules/import-named-async/target.js: Added. * modules/import-namespace-async.js: Added. Source/JavaScriptCore: Previously, we have ASYNC keyword in the parser. This is introduced only for performance, and ECMA262 spec does not categorize "async" to keyword. This makes parser code complicated, since ASYNC should be handled as IDENT. If we missed this ASYNC keyword, we cause a bug. For example, import declaration failed to bind imported binding to the name "async" because the parser considered ASYNC as keyword. This patch removes ASYNC keyword from the parser. By carefully handling ASYNC, we can keep the current performance without using this ASYNC keyword. * parser/Keywords.table: * parser/Parser.cpp: (JSC::Parser<LexerType>::parseStatementListItem): (JSC::Parser<LexerType>::parseStatement): (JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement): (JSC::Parser<LexerType>::parseClass): (JSC::Parser<LexerType>::parseExportDeclaration): (JSC::Parser<LexerType>::parseAssignmentExpression): (JSC::Parser<LexerType>::parseProperty): (JSC::Parser<LexerType>::parsePrimaryExpression): (JSC::Parser<LexerType>::parseMemberExpression): (JSC::Parser<LexerType>::printUnexpectedTokenText): * parser/ParserTokens.h: * runtime/CommonIdentifiers.h: Canonical link: https://commits.webkit.org/194293@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223022 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 32af2f7 commit 975467a

11 files changed

Lines changed: 143 additions & 76 deletions

File tree

‎JSTests/ChangeLog‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
2017-10-07 Yusuke Suzuki <utatane.tea@gmail.com>
2+
3+
`async` should be able to be used as an imported binding name
4+
https://bugs.webkit.org/show_bug.cgi?id=176573
5+
6+
Reviewed by Darin Adler.
7+
8+
* modules/import-default-async.js: Added.
9+
* modules/import-named-async-as.js: Added.
10+
* modules/import-named-async.js: Added.
11+
* modules/import-named-async/target.js: Added.
12+
* modules/import-namespace-async.js: Added.
13+
114
2017-09-29 Filip Pizlo <fpizlo@apple.com>
215

316
Enable gigacage on iOS
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { shouldBe } from "./resources/assert.js";
2+
import async from "./import-default-async.js"
3+
4+
export default 42;
5+
6+
shouldBe(async, 42);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { shouldBe } from "./resources/assert.js";
2+
import { Cocoa as async } from "./import-named-async-as.js"
3+
4+
export let Cocoa = 42;
5+
shouldBe(async, 42);
6+
shouldBe(Cocoa, 42);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { shouldBe } from "./resources/assert.js";
2+
import { async } from "./import-named-async/target.js"
3+
4+
shouldBe(async, 42);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export let async = 42;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { shouldBe } from "./resources/assert.js";
2+
import * as async from "./import-namespace-async.js"
3+
4+
export let Cocoa = 42;
5+
shouldBe(async.Cocoa, 42);

‎Source/JavaScriptCore/ChangeLog‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
2017-10-07 Yusuke Suzuki <utatane.tea@gmail.com>
2+
3+
`async` should be able to be used as an imported binding name
4+
https://bugs.webkit.org/show_bug.cgi?id=176573
5+
6+
Reviewed by Darin Adler.
7+
8+
Previously, we have ASYNC keyword in the parser. This is introduced only for performance,
9+
and ECMA262 spec does not categorize "async" to keyword. This makes parser code complicated,
10+
since ASYNC should be handled as IDENT. If we missed this ASYNC keyword, we cause a bug.
11+
For example, import declaration failed to bind imported binding to the name "async" because
12+
the parser considered ASYNC as keyword.
13+
14+
This patch removes ASYNC keyword from the parser. By carefully handling ASYNC, we can keep
15+
the current performance without using this ASYNC keyword.
16+
17+
* parser/Keywords.table:
18+
* parser/Parser.cpp:
19+
(JSC::Parser<LexerType>::parseStatementListItem):
20+
(JSC::Parser<LexerType>::parseStatement):
21+
(JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement):
22+
(JSC::Parser<LexerType>::parseClass):
23+
(JSC::Parser<LexerType>::parseExportDeclaration):
24+
(JSC::Parser<LexerType>::parseAssignmentExpression):
25+
(JSC::Parser<LexerType>::parseProperty):
26+
(JSC::Parser<LexerType>::parsePrimaryExpression):
27+
(JSC::Parser<LexerType>::parseMemberExpression):
28+
(JSC::Parser<LexerType>::printUnexpectedTokenText):
29+
* parser/ParserTokens.h:
30+
* runtime/CommonIdentifiers.h:
31+
132
2017-09-29 Filip Pizlo <fpizlo@apple.com>
233

334
Enable gigacage on iOS

‎Source/JavaScriptCore/parser/Keywords.table‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ true TRUETOKEN
77
false FALSETOKEN
88

99
# Keywords.
10-
async ASYNC
1110
await AWAIT
1211
break BREAK
1312
case CASE

‎Source/JavaScriptCore/parser/Parser.cpp‎

Lines changed: 75 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -657,19 +657,19 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
657657
case FUNCTION:
658658
result = parseFunctionDeclaration(context);
659659
break;
660-
case ASYNC: {
661-
// Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
662-
// but could be mistakenly parsed as an AsyncFunctionExpression.
663-
SavePoint savePoint = createSavePoint();
664-
next();
665-
if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
666-
result = parseAsyncFunctionDeclaration(context);
667-
break;
660+
case IDENT:
661+
if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
662+
// Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
663+
// but could be mistakenly parsed as an AsyncFunctionExpression.
664+
SavePoint savePoint = createSavePoint();
665+
next();
666+
if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
667+
result = parseAsyncFunctionDeclaration(context);
668+
break;
669+
}
670+
restoreSavePoint(savePoint);
668671
}
669-
restoreSavePoint(savePoint);
670672
FALLTHROUGH;
671-
}
672-
case IDENT:
673673
case AWAIT:
674674
case YIELD: {
675675
// This is a convenient place to notice labeled statements
@@ -1899,11 +1899,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
18991899
goto identcase;
19001900
goto defaultCase;
19011901
}
1902-
case ASYNC:
1903-
if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
1904-
break;
1905-
FALLTHROUGH;
19061902
case IDENT:
1903+
if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
1904+
if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
1905+
break;
1906+
}
1907+
FALLTHROUGH;
19071908
case AWAIT:
19081909
case YIELD: {
19091910
identcase:
@@ -1988,7 +1989,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
19881989
template <typename LexerType>
19891990
template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement)
19901991
{
1991-
ASSERT(match(ASYNC));
1992+
ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
19921993
SavePoint savePoint = createSavePoint();
19931994
next();
19941995
if (match(FUNCTION) && !m_lexer->prevTerminator()) {
@@ -2835,20 +2836,21 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
28352836
ASSERT(ident);
28362837
next();
28372838
break;
2838-
case ASYNC:
2839-
if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
2840-
ident = m_token.m_data.ident;
2841-
next();
2842-
if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
2843-
break;
2844-
if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
2845-
parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
2846-
else
2847-
parseMode = SourceParseMode::AsyncMethodMode;
2848-
goto parseMethod;
2839+
case IDENT:
2840+
if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
2841+
if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
2842+
ident = m_token.m_data.ident;
2843+
next();
2844+
if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
2845+
break;
2846+
if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
2847+
parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
2848+
else
2849+
parseMode = SourceParseMode::AsyncMethodMode;
2850+
goto parseMethod;
2851+
}
28492852
}
28502853
FALLTHROUGH;
2851-
case IDENT:
28522854
case AWAIT:
28532855
ident = m_token.m_data.ident;
28542856
ASSERT(ident);
@@ -3369,7 +3371,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
33693371
if (match(IDENT))
33703372
localName = m_token.m_data.ident;
33713373
restoreSavePoint(savePoint);
3372-
} else if (match(ASYNC)) {
3374+
} else if (matchContextualKeyword(m_vm->propertyNames->async)) {
33733375
SavePoint savePoint = createSavePoint();
33743376
next();
33753377
if (match(FUNCTION) && !m_lexer->prevTerminator()) {
@@ -3393,7 +3395,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
33933395
} else if (match(CLASSTOKEN)) {
33943396
result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
33953397
} else {
3396-
ASSERT(match(ASYNC));
3398+
ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
33973399
next();
33983400
DepthManager statementDepth(&m_statementDepth);
33993401
m_statementDepth = 1;
@@ -3514,15 +3516,16 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
35143516
result = parseClassDeclaration(context, ExportType::Exported);
35153517
break;
35163518

3517-
case ASYNC: {
3518-
next();
3519-
semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
3520-
DepthManager statementDepth(&m_statementDepth);
3521-
m_statementDepth = 1;
3522-
result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
3523-
break;
3524-
}
3525-
3519+
case IDENT:
3520+
if (*m_token.m_data.ident == m_vm->propertyNames->async) {
3521+
next();
3522+
semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
3523+
DepthManager statementDepth(&m_statementDepth);
3524+
m_statementDepth = 1;
3525+
result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
3526+
break;
3527+
}
3528+
FALLTHROUGH;
35263529
default:
35273530
failWithMessage("Expected either a declaration or a variable statement");
35283531
break;
@@ -3620,7 +3623,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
36203623
restoreSavePoint(savePoint);
36213624
bool isAsyncArrow = false;
36223625
if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) {
3623-
ASSERT(match(ASYNC));
3626+
ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
36243627
next();
36253628
isAsyncArrow = !m_lexer->prevTerminator();
36263629
}
@@ -3888,26 +3891,27 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
38883891

38893892
parseProperty:
38903893
switch (m_token.m_type) {
3891-
case ASYNC:
3892-
if (parseMode == SourceParseMode::MethodMode) {
3893-
SavePoint savePoint = createSavePoint();
3894-
next();
3894+
case IDENT:
3895+
if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
3896+
if (parseMode == SourceParseMode::MethodMode) {
3897+
SavePoint savePoint = createSavePoint();
3898+
next();
38953899

3896-
if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
3897-
restoreSavePoint(savePoint);
3898-
wasIdent = true;
3899-
goto namedProperty;
3900-
}
3900+
if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
3901+
restoreSavePoint(savePoint);
3902+
wasIdent = true;
3903+
goto namedProperty;
3904+
}
39013905

3902-
failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
3903-
if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
3904-
parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
3905-
else
3906-
parseMode = SourceParseMode::AsyncMethodMode;
3907-
goto parseProperty;
3906+
failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
3907+
if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
3908+
parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
3909+
else
3910+
parseMode = SourceParseMode::AsyncMethodMode;
3911+
goto parseProperty;
3912+
}
39083913
}
39093914
FALLTHROUGH;
3910-
case IDENT:
39113915
case YIELD:
39123916
case AWAIT:
39133917
wasIdent = true;
@@ -4445,22 +4449,22 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
44454449
if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters)
44464450
failIfFalse(m_parserState.allowAwait, "Cannot use await expression within parameters");
44474451
goto identifierExpression;
4448-
case ASYNC: {
4449-
JSTextPosition start = tokenStartPosition();
4450-
const Identifier* ident = m_token.m_data.ident;
4451-
JSTokenLocation location(tokenLocation());
4452-
next();
4453-
if (match(FUNCTION) && !m_lexer->prevTerminator())
4454-
return parseAsyncFunctionExpression(context);
4452+
case IDENT: {
4453+
if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
4454+
JSTextPosition start = tokenStartPosition();
4455+
const Identifier* ident = m_token.m_data.ident;
4456+
JSTokenLocation location(tokenLocation());
4457+
next();
4458+
if (match(FUNCTION) && !m_lexer->prevTerminator())
4459+
return parseAsyncFunctionExpression(context);
44554460

4456-
// Avoid using variable if it is an arrow function parameter
4457-
if (UNLIKELY(match(ARROWFUNCTION)))
4458-
return 0;
4461+
// Avoid using variable if it is an arrow function parameter
4462+
if (UNLIKELY(match(ARROWFUNCTION)))
4463+
return 0;
44594464

4460-
const bool isEval = false;
4461-
return createResolveAndUseVariable(context, ident, isEval, start, location);
4462-
}
4463-
case IDENT: {
4465+
const bool isEval = false;
4466+
return createResolveAndUseVariable(context, ident, isEval, start, location);
4467+
}
44644468
identifierExpression:
44654469
JSTextPosition start = tokenStartPosition();
44664470
const Identifier* ident = m_token.m_data.ident;
@@ -4707,7 +4711,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
47074711
base = context.createImportExpr(location, expr, expressionStart, expressionEnd, lastTokenEndPosition());
47084712
}
47094713
} else if (!baseIsNewTarget) {
4710-
const bool isAsync = match(ASYNC);
4714+
const bool isAsync = matchContextualKeyword(m_vm->propertyNames->async);
47114715

47124716
base = parsePrimaryExpression(context);
47134717
failIfFalse(base, "Cannot parse base expression");
@@ -5064,7 +5068,6 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W
50645068
out.print("Invalid private name '", getToken(), "'");
50655069
return;
50665070

5067-
case ASYNC:
50685071
case AWAIT:
50695072
case IDENT:
50705073
out.print("Unexpected identifier '", getToken(), "'");

‎Source/JavaScriptCore/parser/ParserTokens.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,9 @@ enum JSTokenType {
9393
LET,
9494
YIELD,
9595
AWAIT,
96-
ASYNC,
9796

9897
FirstContextualKeywordToken = LET,
99-
LastContextualKeywordToken = ASYNC,
98+
LastContextualKeywordToken = AWAIT,
10099
FirstSafeContextualKeywordToken = AWAIT,
101100
LastSafeContextualKeywordToken = LastContextualKeywordToken,
102101

0 commit comments

Comments
 (0)