Bladeren bron

Contextual keywords cannot have unicode escape sequences

Kevin Smith 6 jaren geleden
bovenliggende
commit
69e17f6d0a
7 gewijzigde bestanden met toevoegingen van 81 en 45 verwijderingen
  1. 49 45
      lib/Parser/Parse.cpp
  2. 10 0
      lib/Parser/Parse.h
  3. 2 0
      lib/Parser/Scan.cpp
  4. 5 0
      lib/Parser/Scan.h
  5. 4 0
      test/es5/setget.js
  6. 3 0
      test/es6module/module-syntax.js
  7. 8 0
      test/es7/asyncawait-syntax.js

+ 49 - 45
lib/Parser/Parse.cpp

@@ -2410,7 +2410,7 @@ void Parser::ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* impor
         if (m_token.tk == tkID)
         {
             // We have the pattern "IdentifierName as"
-            if (wellKnownPropertyPids.as != m_token.GetIdentifier(this->GetHashTbl()))
+            if (!CheckContextualKeyword(wellKnownPropertyPids.as))
             {
                 Error(ERRsyntax);
             }
@@ -2666,7 +2666,7 @@ void Parser::ParseImportClause(ModuleImportOrExportEntryList* importEntryList, b
 
         // Token following * must be the identifier 'as'
         this->GetScanner()->Scan();
-        if (m_token.tk != tkID || wellKnownPropertyPids.as != m_token.GetIdentifier(this->GetHashTbl()))
+        if (!CheckContextualKeyword(wellKnownPropertyPids.as))
         {
             Error(ERRsyntax);
         }
@@ -2828,7 +2828,7 @@ IdentPtr Parser::ParseImportOrExportFromClause(bool throwIfNotFound)
 {
     IdentPtr moduleSpecifier = nullptr;
 
-    if (m_token.tk == tkID && wellKnownPropertyPids.from == m_token.GetIdentifier(this->GetHashTbl()))
+    if (CheckContextualKeyword(wellKnownPropertyPids.from))
     {
         this->GetScanner()->Scan();
 
@@ -2901,7 +2901,7 @@ ParseNodePtr Parser::ParseDefaultExportClause()
         // function token) or it could be an identifier (let async = 0; export default async;).
         // To handle both cases, when we parse an async token we need to keep the parser state
         // and rewind if the next token is not function.
-        if (wellKnownPropertyPids.async == m_token.GetIdentifier(this->GetHashTbl()))
+        if (CheckContextualKeyword(wellKnownPropertyPids.async))
         {
             RestorePoint parsedAsync;
             this->GetScanner()->Capture(&parsedAsync);
@@ -3023,26 +3023,22 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
 
         if (m_scriptContext->GetConfig()->IsESExportNsAsEnabled())
         {
-            // export * as name
-            if (m_token.tk == tkID)
+            // check for 'as'
+            if (CheckContextualKeyword(wellKnownPropertyPids.as))
             {
-                // check for 'as'
-                if (wellKnownPropertyPids.as == m_token.GetIdentifier(this->GetHashTbl()))
+                // scan to the next token
+                this->GetScanner()->Scan();
+
+                // token after as must be an identifier
+                if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
                 {
-                    // scan to the next token
-                    this->GetScanner()->Scan();
+                    Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
+                }
 
-                    // token after as must be an identifier
-                    if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
-                    {
-                        Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
-                    }
+                exportName = m_token.GetIdentifier(this->GetHashTbl());
 
-                    exportName = m_token.GetIdentifier(this->GetHashTbl());
-                    
-                    // scan to next token
-                    this->GetScanner()->Scan();
-                }
+                // scan to next token
+                this->GetScanner()->Scan();
             }
         }
 
@@ -3122,14 +3118,18 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
 
     case tkID:
     {
-        IdentPtr pid = m_token.GetIdentifier(this->GetHashTbl());
+        IdentPtr pid = nullptr;
+        if (!this->GetScanner()->LastIdentifierHasEscape())
+        {
+            pid = m_token.GetIdentifier(this->GetHashTbl());
+        }
 
-        if (wellKnownPropertyPids.let == pid)
+        if (pid == wellKnownPropertyPids.let)
         {
             declarationType = tkLET;
             goto ParseVarDecl;
         }
-        if (wellKnownPropertyPids.async == pid && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
+        if (pid == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
         {
             // In module export statements, async token is only valid if it's followed by function.
             // We need to check here because ParseStatement would think 'async = 20' is a var decl.
@@ -3267,6 +3267,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
         iecpLim = this->GetScanner()->IecpLimTok();
 
         if (pid == wellKnownPropertyPids.async &&
+            !this->GetScanner()->LastIdentifierHasEscape() &&
             m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
         {
             isAsyncExpr = true;
@@ -4747,7 +4748,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
         bool isAsyncMethod = false;
         charcount_t ichMin = this->GetScanner()->IchMinTok();
         size_t iecpMin = this->GetScanner()->IecpMinTok();
-        if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
+        if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
         {
             RestorePoint parsedAsync;
             this->GetScanner()->Capture(&parsedAsync);
@@ -4789,6 +4790,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
         charcount_t idHintIchLim = static_cast<charcount_t>(this->GetScanner()->IecpLimTok());
         bool wrapInBrackets = false;
         bool seenEllipsis = false;
+        bool maybeKeyword = false;
         switch (m_token.tk)
         {
         default:
@@ -4801,6 +4803,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
             // fall-through
         case tkID:
             pidHint = m_token.GetIdentifier(this->GetHashTbl());
+            maybeKeyword = !this->GetScanner()->LastIdentifierHasEscape();
             if (buildAST)
             {
                 pnodeName = CreateStrNode(pidHint);
@@ -5066,9 +5069,10 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
         {
             Assert(pidHint->Psz() != nullptr);
 
+            // get/set are only pseudo keywords when they are identifiers (i.e. not strings)
             if ((pidHint == wellKnownPropertyPids.get || pidHint == wellKnownPropertyPids.set) &&
-                // get/set are only pseudo keywords when they are identifiers (i.e. not strings)
-                tkHint.tk == tkID && NextTokenIsPropertyNameStart())
+                maybeKeyword &&
+                NextTokenIsPropertyNameStart())
             {
                 if (isObjectPattern)
                 {
@@ -7471,7 +7475,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn)
             for (;;)
             {
                 this->GetScanner()->Scan();
-                if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async)
+                if (CheckContextualKeyword(wellKnownPropertyPids.async))
                 {
                     Assert(pnodeFnc->IsAsync());
                     continue;
@@ -7502,7 +7506,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn)
         Assert(pnodeFnc->IsGenerator());
         this->GetScanner()->ScanNoKeywords();
     }
-    if (fLambda && m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async)
+    if (fLambda && CheckContextualKeyword(wellKnownPropertyPids.async))
     {
         Assert(pnodeFnc->IsAsync());
         this->GetScanner()->ScanNoKeywords();
@@ -7960,13 +7964,14 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
         ParseNodePtr pnodeMemberName = nullptr;
         IdentPtr pidHint = nullptr;
         IdentPtr memberPid = nullptr;
+        bool maybeAccessor = false;
         LPCOLESTR pMemberNameHint = nullptr;
-        uint32     memberNameHintLength = 0;
-        uint32     memberNameOffset = 0;
+        uint32 memberNameHintLength = 0;
+        uint32 memberNameOffset = 0;
         bool isComputedName = false;
         bool isAsyncMethod = false;
 
-        if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
+        if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
         {
             RestorePoint parsedAsync;
             this->GetScanner()->Capture(&parsedAsync);
@@ -8002,6 +8007,7 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
         }
         else // not computed name
         {
+            maybeAccessor = !this->GetScanner()->LastIdentifierHasEscape();
             memberPid = this->ParseClassPropertyName(&pidHint);
             if (pidHint)
             {
@@ -8063,21 +8069,21 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
         else
         {
             ParseNodePtr pnodeMember = nullptr;
-
-            bool isMemberNamedGetOrSet = false;
             RestorePoint beginMethodName;
             this->GetScanner()->Capture(&beginMethodName);
-            if (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set)
+
+            if (maybeAccessor && (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set))
             {
                 this->GetScanner()->ScanForcingPid();
             }
+
             if (m_token.tk == tkLParen)
             {
                 this->GetScanner()->SeekTo(beginMethodName);
-                isMemberNamedGetOrSet = true;
+                maybeAccessor = false;
             }
 
-            if ((memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set) && !isMemberNamedGetOrSet)
+            if (maybeAccessor && (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set))
             {
                 bool isGetter = (memberPid == wellKnownPropertyPids.get);
 
@@ -9244,7 +9250,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
             RestoreStateFrom(&parserState);
 
             this->GetScanner()->SeekTo(termStart);
-            if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
+            if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
             {
                 ichMin = this->GetScanner()->IchMinTok();
                 iecpMin = this->GetScanner()->IecpMinTok();
@@ -10216,7 +10222,7 @@ LRestart:
 
     case tkID:
     case tkLET:
-        if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.let)
+        if (tok == tkLET || CheckContextualKeyword(wellKnownPropertyPids.let))
         {
             // We see "let" at the start of a statement. This could either be a declaration or an identifier
             // reference. The next token determines which.
@@ -10241,7 +10247,7 @@ LRestart:
             }
             this->GetScanner()->SeekTo(parsedLet);
         }
-        else if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
+        else if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
         {
             RestorePoint parsedAsync;
             this->GetScanner()->Capture(&parsedAsync);
@@ -10286,7 +10292,7 @@ LRestart:
         ichMin = this->GetScanner()->IchMinTok();
 
         this->GetScanner()->Scan();
-        if (m_token.tk == tkAWAIT || (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.await))
+        if (m_token.tk == tkAWAIT || CheckContextualKeyword(wellKnownPropertyPids.await))
         {
             if (!this->GetScanner()->AwaitIsKeywordRegion())
             {
@@ -10317,7 +10323,7 @@ LRestart:
         switch (tok)
         {
         case tkID:
-            if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.let)
+            if (CheckContextualKeyword(wellKnownPropertyPids.let))
             {
                 // We see "let" in the init part of a for loop. This could either be a declaration or an identifier
                 // reference. The next token determines which.
@@ -10426,7 +10432,7 @@ LRestart:
         if (TokIsForInOrForOf())
         {
             bool isForOf = (m_token.tk != tkIN);
-            Assert(!isForOf || (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.of));
+            Assert(!isForOf || CheckContextualKeyword(wellKnownPropertyPids.of));
 
             if (isForAwait && !isForOf)
             {
@@ -11321,9 +11327,7 @@ LNeedTerminator:
 BOOL
 Parser::TokIsForInOrForOf()
 {
-    return m_token.tk == tkIN ||
-        (m_token.tk == tkID &&
-            m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.of);
+    return m_token.tk == tkIN || CheckContextualKeyword(wellKnownPropertyPids.of);
 }
 
 /***************************************************************************

+ 10 - 0
lib/Parser/Parse.h

@@ -328,6 +328,16 @@ private:
     bool CheckStrictModeStrPid(IdentPtr pid);
     bool CheckAsmjsModeStrPid(IdentPtr pid);
 
+    bool CheckContextualKeyword(IdentPtr keywordPid)
+    {
+        if (m_token.tk == tkID && !GetScanner()->LastIdentifierHasEscape())
+        {
+            IdentPtr pid = m_token.GetIdentifier(GetHashTbl());
+            return pid == keywordPid;
+        }
+        return false;
+    }
+
     bool IsCurBlockInLoop() const;
 
     void InitPids();

+ 2 - 0
lib/Parser/Scan.cpp

@@ -492,6 +492,8 @@ tokens Scanner<EncodingPolicy>::ScanIdentifierContinue(bool identifyKwds, bool f
         break;
     }
 
+    m_lastIdentifierHasEscape = fHasEscape;
+
     Assert(p - pchMin > 0 && p - pchMin <= LONG_MAX);
 
     *pp = p;

+ 5 - 0
lib/Parser/Scan.h

@@ -493,6 +493,10 @@ public:
       return m_EscapeOnLastTkStrCon;
     }
 
+    bool LastIdentifierHasEscape()
+    {
+        return m_lastIdentifierHasEscape;
+    }
 
     bool IsOctOrLeadingZeroOnLastTKNumber()
     {
@@ -730,6 +734,7 @@ private:
     BOOL m_doubleQuoteOnLastTkStrCon :1;
     bool m_OctOrLeadingZeroOnLastTKNumber :1;
     bool m_EscapeOnLastTkStrCon:1;
+    bool m_lastIdentifierHasEscape:1;
     BOOL m_fNextStringTemplateIsTagged:1;   // the next string template scanned has a tag (must create raw strings)
     BYTE m_DeferredParseFlags:2;            // suppressStrPid and suppressIdPid    
     bool es6UnicodeMode;                // True if ES6Unicode Extensions are enabled.

+ 4 - 0
test/es5/setget.js

@@ -220,3 +220,7 @@ test(false);
 test(true);
 })();
 
+try {
+  eval("({ g\\u0065t foo() {} })");
+  write("Get and set cannot contain unicode escapes");
+} catch {}

+ 3 - 0
test/es6module/module-syntax.js

@@ -117,6 +117,8 @@ var tests = [
             testModuleScript('function () { export default null; }', 'Syntax error export in function', true);
             testModuleScript('export {foo}', 'Syntax error undefined export', true);
             testModuleScript('export {Array}', 'Syntax error exporting a global name with no local definition', true);
+            testModuleScript('var x; export { x \\u0061s y }', 'Syntax error if as keyword has unicode escape', true);
+            testModuleScript('export { x } \\u0066rom "module";', 'Syntax error if as keyword has unicode escape', true);
         }
     },
     {
@@ -164,6 +166,7 @@ var tests = [
             testModuleScript(`do import { default } from "module"
                                 while (false);`, 'Syntax error export in while', true);
             testModuleScript('function () { import { default } from "module"; }', 'Syntax error export in function', true);
+            testModuleScript('import { default \\u0061s y } from "module";', 'Syntax error if as keyword has unicode escape', true);
         }
     },
     {

+ 8 - 0
test/es7/asyncawait-syntax.js

@@ -134,6 +134,14 @@ var tests = [
             assert.doesNotThrow(function () { eval("(async function (z) {})[0]"); }, "Should not throw when async function occurs a type conversion");
         }
     },
+    {
+        name: "Async keyword cannot contain unicode escapes",
+        body: function () {
+            assert.throws(function () { eval("\\u0061sync function foo() {} }"); }, SyntaxError, "async keyword cannot contain unicode escapes");
+            assert.throws(function () { eval("({ \\u0061sync foo() {} })"); }, SyntaxError, "async keyword cannot contain unicode escapes");
+            assert.throws(function () { eval("class A { \\u0061sync foo() {} }"); }, SyntaxError, "async keyword cannot contain unicode escapes");
+        }
+    },
     {
         name: "It is a Syntax Error if FormalParameters Contains AwaitExpression is true",
         body: function () {