瀏覽代碼

Throw `ERRBadAwait` instead of `ERRnoSemic` if applicable (#6955)

* Throw `ERRBadAwait` instead of `ERRnoSemic` if applicable
Lukas Kurz 1 年之前
父節點
當前提交
469795bb68
共有 4 個文件被更改,包括 32 次插入14 次删除
  1. 8 2
      lib/Parser/Parse.cpp
  2. 2 1
      lib/Parser/Scan.cpp
  3. 4 2
      lib/Parser/Scan.h
  4. 18 9
      test/es7/asyncawait-syntax.js

+ 8 - 2
lib/Parser/Parse.cpp

@@ -5483,7 +5483,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c
             {
                 // Class member methods have optional separators. We need to check whether we are
                 // getting the IchLim of the correct token.
-                Assert(this->GetScanner()->m_tkPrevious == tkRCurly && needScanRCurly);
+                Assert(this->GetScanner()->GetPrevious() == tkRCurly && needScanRCurly);
 
                 this->m_funcInArray += this->GetScanner()->IchMinTok() - /*tkRCurly*/ 1 - ichMin;
             }
@@ -6529,7 +6529,7 @@ bool Parser::FastScanFormalsAndBody()
         {
             int opl;
             OpCode nop;
-            tokens tkPrev = this->GetScanner()->m_tkPrevious;
+            tokens tkPrev = this->GetScanner()->GetPrevious();
             if ((this->GetHashTbl()->TokIsBinop(tkPrev, &opl, &nop) && nop != knopNone) ||
                 (this->GetHashTbl()->TokIsUnop(tkPrev, &opl, &nop) &&
                     nop != knopNone &&
@@ -11267,6 +11267,12 @@ LNeedTerminator:
     default:
         if (!this->GetScanner()->FHadNewLine())
         {
+            Token previous = this->GetScanner()->GetPreviousToken();
+            if (tkID == previous.tk && wellKnownPropertyPids.await == previous.GetIdentifier(this->GetHashTbl()))
+            {
+                Error(ERRBadAwait);
+            }
+
             Error(ERRnoSemic);
         }
         else

+ 2 - 1
lib/Parser/Scan.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "ParserPch.h"
@@ -1592,7 +1593,7 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
     bool seenDelimitedCommentEnd = false;
 
     // store the last token
-    m_tkPrevious = m_ptoken->tk;
+    m_tokenPrevious = *m_ptoken;
     m_iecpLimTokPrevious = IecpLimTok();    // Introduced for use by lambda parsing to find correct span of expression lambdas
     m_ichLimTokPrevious = IchLimTok();
     size_t savedMultiUnits = this->m_cMultiUnits;

+ 4 - 2
lib/Parser/Scan.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #pragma once
@@ -705,7 +706,8 @@ public:
         }
     };
 
-    tokens GetPrevious() { return m_tkPrevious; }
+    tokens GetPrevious() { return m_tokenPrevious.tk; }
+    Token GetPreviousToken() { return m_tokenPrevious; }
     void Capture(_Out_ RestorePoint* restorePoint);
     void SeekTo(const RestorePoint& restorePoint);
     void SeekToForcingPid(const RestorePoint& restorePoint);
@@ -756,7 +758,7 @@ private:
     Js::ScriptContext* m_scriptContext;
     const Js::CharClassifier *charClassifier;
 
-    tokens m_tkPrevious;
+    Token m_tokenPrevious;
     size_t m_iecpLimTokPrevious;
     charcount_t m_ichLimTokPrevious;
 

+ 18 - 9
test/es7/asyncawait-syntax.js

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
@@ -58,26 +59,26 @@ var tests = [
     {
         name: "Await in eval global scope",
         body: function () {
-            assert.throws(function () { eval("var result = await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
-            assert.throws(function () { eval("await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
+            assert.throws(function () { eval("var result = await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
+            assert.throws(function () { eval("await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
 
-            assert.throws(function () { eval("await a;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
-            assert.throws(function () { eval("await a[0];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
-            assert.throws(function () { eval("await o.p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
+            assert.throws(function () { eval("await a;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
+            assert.throws(function () { eval("await a[0];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
+            assert.throws(function () { eval("await o.p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
             assert.throws(function () { eval("a[await p];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ']'");
-            assert.throws(function () { eval("a + await p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
-            assert.throws(function () { eval("await p + await q;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'");
+            assert.throws(function () { eval("a + await p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
+            assert.throws(function () { eval("await p + await q;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`);
             assert.throws(function () { eval("foo(await p, await q);"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ')'");
 
             assert.throws(function () { eval("var lambdaParenNoArg = await () => x < y;"); }, SyntaxError, "'await' keyword is not allowed with a non-async lambda expression", "Syntax error");
-            assert.throws(function () { eval("var lambdaArgs = await async (a, b ,c) => a + b + c;"); }, SyntaxError, "There miss parenthises", "Expected ';'");
+            assert.throws(function () { eval("var lambdaArgs = await async (a, b ,c) => a + b + c;"); }, SyntaxError, "There miss parenthises", `'await' expression not allowed in this context`);
             assert.throws(function () { eval("var lambdaArgs = await (async (a, b ,c) => a + b + c);"); }, ReferenceError, "The 'await' function doesn't exists in this scope", "'await' is not defined");
         }
     },
     {
         name: "Await in a non-async function",
         body: function () {
-            assert.throws(function () { eval("function method() { var x = await call(); }"); }, SyntaxError, "'await' cannot be used in a non-async function.", "Expected ';'");
+            assert.throws(function () { eval("function method() { var x = await call(); }"); }, SyntaxError, "'await' cannot be used in a non-async function.", `'await' expression not allowed in this context`);
         }
     },
     {
@@ -216,6 +217,14 @@ var tests = [
             assert.throws(function () { eval("async function af() { (b = (c = await => {}) => {}) => {}; }"); }, SyntaxError, "await cannot appear as the formal name of an unparathensized arrow function in a nested case too", "Unexpected token '=>' after 'await'");
         }
     },
+    {
+        name: "Specific error message when using 'await' as a keyword outside 'async' context",
+        body: function () {
+            assert.throws(function () {
+                eval(`await new Promise(() => {});`);
+            }, SyntaxError, "await is not a keyword here", `'await' expression not allowed in this context`);
+        }
+    }
 ];
 
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });