`async` should be able to be used as an imported binding name
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 7 Oct 2017 21:16:24 +0000 (21:16 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 7 Oct 2017 21:16:24 +0000 (21:16 +0000)
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:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223022 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/modules/import-default-async.js [new file with mode: 0644]
JSTests/modules/import-named-async-as.js [new file with mode: 0644]
JSTests/modules/import-named-async.js [new file with mode: 0644]
JSTests/modules/import-named-async/target.js [new file with mode: 0644]
JSTests/modules/import-namespace-async.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Keywords.table
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h

index 89f10a5..0d9068a 100644 (file)
@@ -1,3 +1,16 @@
+2017-10-07  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        `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.
+
+        * 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.
+
 2017-09-29  Filip Pizlo  <fpizlo@apple.com>
 
         Enable gigacage on iOS
diff --git a/JSTests/modules/import-default-async.js b/JSTests/modules/import-default-async.js
new file mode 100644 (file)
index 0000000..eac598e
--- /dev/null
@@ -0,0 +1,6 @@
+import { shouldBe } from "./resources/assert.js";
+import async from "./import-default-async.js"
+
+export default 42;
+
+shouldBe(async, 42);
diff --git a/JSTests/modules/import-named-async-as.js b/JSTests/modules/import-named-async-as.js
new file mode 100644 (file)
index 0000000..854b2b2
--- /dev/null
@@ -0,0 +1,6 @@
+import { shouldBe } from "./resources/assert.js";
+import { Cocoa as async } from "./import-named-async-as.js"
+
+export let Cocoa = 42;
+shouldBe(async, 42);
+shouldBe(Cocoa, 42);
diff --git a/JSTests/modules/import-named-async.js b/JSTests/modules/import-named-async.js
new file mode 100644 (file)
index 0000000..9e30dca
--- /dev/null
@@ -0,0 +1,4 @@
+import { shouldBe } from "./resources/assert.js";
+import { async } from "./import-named-async/target.js"
+
+shouldBe(async, 42);
diff --git a/JSTests/modules/import-named-async/target.js b/JSTests/modules/import-named-async/target.js
new file mode 100644 (file)
index 0000000..a7944c6
--- /dev/null
@@ -0,0 +1 @@
+export let async = 42;
diff --git a/JSTests/modules/import-namespace-async.js b/JSTests/modules/import-namespace-async.js
new file mode 100644 (file)
index 0000000..1e27767
--- /dev/null
@@ -0,0 +1,5 @@
+import { shouldBe } from "./resources/assert.js";
+import * as async from "./import-namespace-async.js"
+
+export let Cocoa = 42;
+shouldBe(async.Cocoa, 42);
index 4b2f902..8dba867 100644 (file)
@@ -1,3 +1,34 @@
+2017-10-07  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        `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.
+
+        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:
+
 2017-09-29  Filip Pizlo  <fpizlo@apple.com>
 
         Enable gigacage on iOS
index a0e5163..2f7dfc5 100644 (file)
@@ -7,7 +7,6 @@ true            TRUETOKEN
 false          FALSETOKEN
 
 # Keywords.
-async       ASYNC
 await       AWAIT
 break          BREAK
 case           CASE
index 7cd8dac..c30c416 100644 (file)
@@ -657,19 +657,19 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
     case FUNCTION:
         result = parseFunctionDeclaration(context);
         break;
-    case ASYNC: {
-        // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
-        // but could be mistakenly parsed as an AsyncFunctionExpression.
-        SavePoint savePoint = createSavePoint();
-        next();
-        if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
-            result = parseAsyncFunctionDeclaration(context);
-            break;
+    case IDENT:
+        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
+            // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
+            // but could be mistakenly parsed as an AsyncFunctionExpression.
+            SavePoint savePoint = createSavePoint();
+            next();
+            if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
+                result = parseAsyncFunctionDeclaration(context);
+                break;
+            }
+            restoreSavePoint(savePoint);
         }
-        restoreSavePoint(savePoint);
         FALLTHROUGH;
-    }
-    case IDENT:
     case AWAIT:
     case YIELD: {
         // This is a convenient place to notice labeled statements
@@ -1899,11 +1899,12 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
             goto identcase;
         goto defaultCase;
     }
-    case ASYNC:
-        if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
-            break;
-        FALLTHROUGH;
     case IDENT:
+        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
+            if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
+                break;
+        }
+        FALLTHROUGH;
     case AWAIT:
     case YIELD: {
         identcase:
@@ -1988,7 +1989,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
 template <typename LexerType>
 template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement)
 {
-    ASSERT(match(ASYNC));
+    ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
     SavePoint savePoint = createSavePoint();
     next();
     if (match(FUNCTION) && !m_lexer->prevTerminator()) {
@@ -2835,20 +2836,21 @@ parseMethod:
             ASSERT(ident);
             next();
             break;
-        case ASYNC:
-            if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
-                ident = m_token.m_data.ident;
-                next();
-                if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
-                    break;
-                if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
-                    parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
-                else
-                    parseMode = SourceParseMode::AsyncMethodMode;
-                goto parseMethod;
+        case IDENT:
+            if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
+                if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
+                    ident = m_token.m_data.ident;
+                    next();
+                    if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
+                        break;
+                    if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
+                        parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
+                    else
+                        parseMode = SourceParseMode::AsyncMethodMode;
+                    goto parseMethod;
+                }
             }
             FALLTHROUGH;
-        case IDENT:
         case AWAIT:
             ident = m_token.m_data.ident;
             ASSERT(ident);
@@ -3369,7 +3371,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
             if (match(IDENT))
                 localName = m_token.m_data.ident;
             restoreSavePoint(savePoint);
-        } else if (match(ASYNC)) {
+        } else if (matchContextualKeyword(m_vm->propertyNames->async)) {
             SavePoint savePoint = createSavePoint();
             next();
             if (match(FUNCTION) && !m_lexer->prevTerminator()) {
@@ -3393,7 +3395,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
             } else if (match(CLASSTOKEN)) {
                 result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
             } else {
-                ASSERT(match(ASYNC));
+                ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
                 next();
                 DepthManager statementDepth(&m_statementDepth);
                 m_statementDepth = 1;
@@ -3514,15 +3516,16 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
             result = parseClassDeclaration(context, ExportType::Exported);
             break;
 
-        case ASYNC: {
-            next();
-            semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
-            DepthManager statementDepth(&m_statementDepth);
-            m_statementDepth = 1;
-            result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
-            break;
-        }
-
+        case IDENT:
+            if (*m_token.m_data.ident == m_vm->propertyNames->async) {
+                next();
+                semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
+                DepthManager statementDepth(&m_statementDepth);
+                m_statementDepth = 1;
+                result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
+                break;
+            }
+            FALLTHROUGH;
         default:
             failWithMessage("Expected either a declaration or a variable statement");
             break;
@@ -3620,7 +3623,7 @@ template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
             restoreSavePoint(savePoint);
             bool isAsyncArrow = false;
             if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) {
-                ASSERT(match(ASYNC));
+                ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
                 next();
                 isAsyncArrow = !m_lexer->prevTerminator();
             }
@@ -3888,26 +3891,27 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
 
 parseProperty:
     switch (m_token.m_type) {
-    case ASYNC:
-        if (parseMode == SourceParseMode::MethodMode) {
-            SavePoint savePoint = createSavePoint();
-            next();
+    case IDENT:
+        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
+            if (parseMode == SourceParseMode::MethodMode) {
+                SavePoint savePoint = createSavePoint();
+                next();
 
-            if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
-                restoreSavePoint(savePoint);
-                wasIdent = true;
-                goto namedProperty;
-            }
+                if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
+                    restoreSavePoint(savePoint);
+                    wasIdent = true;
+                    goto namedProperty;
+                }
 
-            failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
-            if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
-                parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
-            else
-                parseMode = SourceParseMode::AsyncMethodMode;
-            goto parseProperty;
+                failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
+                if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
+                    parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
+                else
+                    parseMode = SourceParseMode::AsyncMethodMode;
+                goto parseProperty;
+            }
         }
         FALLTHROUGH;
-    case IDENT:
     case YIELD:
     case AWAIT:
         wasIdent = true;
@@ -4445,22 +4449,22 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
         if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters)
             failIfFalse(m_parserState.allowAwait, "Cannot use await expression within parameters");
         goto identifierExpression;
-    case ASYNC: {
-        JSTextPosition start = tokenStartPosition();
-        const Identifier* ident = m_token.m_data.ident;
-        JSTokenLocation location(tokenLocation());
-        next();
-        if (match(FUNCTION) && !m_lexer->prevTerminator())
-            return parseAsyncFunctionExpression(context);
+    case IDENT: {
+        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
+            JSTextPosition start = tokenStartPosition();
+            const Identifier* ident = m_token.m_data.ident;
+            JSTokenLocation location(tokenLocation());
+            next();
+            if (match(FUNCTION) && !m_lexer->prevTerminator())
+                return parseAsyncFunctionExpression(context);
 
-        // Avoid using variable if it is an arrow function parameter
-        if (UNLIKELY(match(ARROWFUNCTION)))
-            return 0;
+            // Avoid using variable if it is an arrow function parameter
+            if (UNLIKELY(match(ARROWFUNCTION)))
+                return 0;
 
-        const bool isEval = false;
-        return createResolveAndUseVariable(context, ident, isEval, start, location);
-    }
-    case IDENT: {
+            const bool isEval = false;
+            return createResolveAndUseVariable(context, ident, isEval, start, location);
+        }
     identifierExpression:
         JSTextPosition start = tokenStartPosition();
         const Identifier* ident = m_token.m_data.ident;
@@ -4707,7 +4711,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
             base = context.createImportExpr(location, expr, expressionStart, expressionEnd, lastTokenEndPosition());
         }
     } else if (!baseIsNewTarget) {
-        const bool isAsync = match(ASYNC);
+        const bool isAsync = matchContextualKeyword(m_vm->propertyNames->async);
 
         base = parsePrimaryExpression(context);
         failIfFalse(base, "Cannot parse base expression");
@@ -5064,7 +5068,6 @@ template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W
         out.print("Invalid private name '", getToken(), "'");
         return;
             
-    case ASYNC:
     case AWAIT:
     case IDENT:
         out.print("Unexpected identifier '", getToken(), "'");
index 0817fa2..fa12f3c 100644 (file)
@@ -93,10 +93,9 @@ enum JSTokenType {
     LET,
     YIELD,
     AWAIT,
-    ASYNC,
 
     FirstContextualKeywordToken = LET,
-    LastContextualKeywordToken = ASYNC,
+    LastContextualKeywordToken = AWAIT,
     FirstSafeContextualKeywordToken = AWAIT,
     LastSafeContextualKeywordToken = LastContextualKeywordToken,
 
index b3e3af0..193c91e 100644 (file)
@@ -79,6 +79,7 @@
     macro(arguments) \
     macro(as) \
     macro(assign) \
+    macro(async) \
     macro(back) \
     macro(bind) \
     macro(buffer) \
     macro(year)
 
 #define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \
-    macro(async) \
     macro(await) \
     macro(break) \
     macro(case) \