Skip to content

Commit ab68b02

Browse files
author
Kevin Smith
committed
[MERGE #6285 @zenparsing] [test262] Contextual keywords cannot have unicode escape sequences
Merge pull request #6285 from zenparsing:test262-escaped-keywords Example from test262: https://github.com/tc39/test262/blob/master/test/language/export/escaped-as-export-specifier.js
2 parents d57bf66 + 69e17f6 commit ab68b02

File tree

7 files changed

+81
-45
lines changed

7 files changed

+81
-45
lines changed

lib/Parser/Parse.cpp

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,7 +2410,7 @@ void Parser::ParseNamedImportOrExportClause(ModuleImportOrExportEntryList* impor
24102410
if (m_token.tk == tkID)
24112411
{
24122412
// We have the pattern "IdentifierName as"
2413-
if (wellKnownPropertyPids.as != m_token.GetIdentifier(this->GetHashTbl()))
2413+
if (!CheckContextualKeyword(wellKnownPropertyPids.as))
24142414
{
24152415
Error(ERRsyntax);
24162416
}
@@ -2666,7 +2666,7 @@ void Parser::ParseImportClause(ModuleImportOrExportEntryList* importEntryList, b
26662666

26672667
// Token following * must be the identifier 'as'
26682668
this->GetScanner()->Scan();
2669-
if (m_token.tk != tkID || wellKnownPropertyPids.as != m_token.GetIdentifier(this->GetHashTbl()))
2669+
if (!CheckContextualKeyword(wellKnownPropertyPids.as))
26702670
{
26712671
Error(ERRsyntax);
26722672
}
@@ -2828,7 +2828,7 @@ IdentPtr Parser::ParseImportOrExportFromClause(bool throwIfNotFound)
28282828
{
28292829
IdentPtr moduleSpecifier = nullptr;
28302830

2831-
if (m_token.tk == tkID && wellKnownPropertyPids.from == m_token.GetIdentifier(this->GetHashTbl()))
2831+
if (CheckContextualKeyword(wellKnownPropertyPids.from))
28322832
{
28332833
this->GetScanner()->Scan();
28342834

@@ -2901,7 +2901,7 @@ ParseNodePtr Parser::ParseDefaultExportClause()
29012901
// function token) or it could be an identifier (let async = 0; export default async;).
29022902
// To handle both cases, when we parse an async token we need to keep the parser state
29032903
// and rewind if the next token is not function.
2904-
if (wellKnownPropertyPids.async == m_token.GetIdentifier(this->GetHashTbl()))
2904+
if (CheckContextualKeyword(wellKnownPropertyPids.async))
29052905
{
29062906
RestorePoint parsedAsync;
29072907
this->GetScanner()->Capture(&parsedAsync);
@@ -3023,26 +3023,22 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
30233023

30243024
if (m_scriptContext->GetConfig()->IsESExportNsAsEnabled())
30253025
{
3026-
// export * as name
3027-
if (m_token.tk == tkID)
3026+
// check for 'as'
3027+
if (CheckContextualKeyword(wellKnownPropertyPids.as))
30283028
{
3029-
// check for 'as'
3030-
if (wellKnownPropertyPids.as == m_token.GetIdentifier(this->GetHashTbl()))
3029+
// scan to the next token
3030+
this->GetScanner()->Scan();
3031+
3032+
// token after as must be an identifier
3033+
if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
30313034
{
3032-
// scan to the next token
3033-
this->GetScanner()->Scan();
3035+
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
3036+
}
30343037

3035-
// token after as must be an identifier
3036-
if (!(m_token.IsIdentifier() || m_token.IsReservedWord()))
3037-
{
3038-
Error(ERRValidIfFollowedBy, _u("'as'"), _u("an identifier."));
3039-
}
3038+
exportName = m_token.GetIdentifier(this->GetHashTbl());
30403039

3041-
exportName = m_token.GetIdentifier(this->GetHashTbl());
3042-
3043-
// scan to next token
3044-
this->GetScanner()->Scan();
3045-
}
3040+
// scan to next token
3041+
this->GetScanner()->Scan();
30463042
}
30473043
}
30483044

@@ -3122,14 +3118,18 @@ ParseNodePtr Parser::ParseExportDeclaration(bool *needTerminator)
31223118

31233119
case tkID:
31243120
{
3125-
IdentPtr pid = m_token.GetIdentifier(this->GetHashTbl());
3121+
IdentPtr pid = nullptr;
3122+
if (!this->GetScanner()->LastIdentifierHasEscape())
3123+
{
3124+
pid = m_token.GetIdentifier(this->GetHashTbl());
3125+
}
31263126

3127-
if (wellKnownPropertyPids.let == pid)
3127+
if (pid == wellKnownPropertyPids.let)
31283128
{
31293129
declarationType = tkLET;
31303130
goto ParseVarDecl;
31313131
}
3132-
if (wellKnownPropertyPids.async == pid && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
3132+
if (pid == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
31333133
{
31343134
// In module export statements, async token is only valid if it's followed by function.
31353135
// We need to check here because ParseStatement would think 'async = 20' is a var decl.
@@ -3267,6 +3267,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
32673267
iecpLim = this->GetScanner()->IecpLimTok();
32683268

32693269
if (pid == wellKnownPropertyPids.async &&
3270+
!this->GetScanner()->LastIdentifierHasEscape() &&
32703271
m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
32713272
{
32723273
isAsyncExpr = true;
@@ -4747,7 +4748,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
47474748
bool isAsyncMethod = false;
47484749
charcount_t ichMin = this->GetScanner()->IchMinTok();
47494750
size_t iecpMin = this->GetScanner()->IecpMinTok();
4750-
if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
4751+
if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
47514752
{
47524753
RestorePoint parsedAsync;
47534754
this->GetScanner()->Capture(&parsedAsync);
@@ -4789,6 +4790,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
47894790
charcount_t idHintIchLim = static_cast<charcount_t>(this->GetScanner()->IecpLimTok());
47904791
bool wrapInBrackets = false;
47914792
bool seenEllipsis = false;
4793+
bool maybeKeyword = false;
47924794
switch (m_token.tk)
47934795
{
47944796
default:
@@ -4801,6 +4803,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
48014803
// fall-through
48024804
case tkID:
48034805
pidHint = m_token.GetIdentifier(this->GetHashTbl());
4806+
maybeKeyword = !this->GetScanner()->LastIdentifierHasEscape();
48044807
if (buildAST)
48054808
{
48064809
pnodeName = CreateStrNode(pidHint);
@@ -5066,9 +5069,10 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
50665069
{
50675070
Assert(pidHint->Psz() != nullptr);
50685071

5072+
// get/set are only pseudo keywords when they are identifiers (i.e. not strings)
50695073
if ((pidHint == wellKnownPropertyPids.get || pidHint == wellKnownPropertyPids.set) &&
5070-
// get/set are only pseudo keywords when they are identifiers (i.e. not strings)
5071-
tkHint.tk == tkID && NextTokenIsPropertyNameStart())
5074+
maybeKeyword &&
5075+
NextTokenIsPropertyNameStart())
50725076
{
50735077
if (isObjectPattern)
50745078
{
@@ -7471,7 +7475,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn)
74717475
for (;;)
74727476
{
74737477
this->GetScanner()->Scan();
7474-
if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async)
7478+
if (CheckContextualKeyword(wellKnownPropertyPids.async))
74757479
{
74767480
Assert(pnodeFnc->IsAsync());
74777481
continue;
@@ -7502,7 +7506,7 @@ void Parser::FinishFncNode(ParseNodeFnc * pnodeFnc, bool fAllowIn)
75027506
Assert(pnodeFnc->IsGenerator());
75037507
this->GetScanner()->ScanNoKeywords();
75047508
}
7505-
if (fLambda && m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async)
7509+
if (fLambda && CheckContextualKeyword(wellKnownPropertyPids.async))
75067510
{
75077511
Assert(pnodeFnc->IsAsync());
75087512
this->GetScanner()->ScanNoKeywords();
@@ -7960,13 +7964,14 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
79607964
ParseNodePtr pnodeMemberName = nullptr;
79617965
IdentPtr pidHint = nullptr;
79627966
IdentPtr memberPid = nullptr;
7967+
bool maybeAccessor = false;
79637968
LPCOLESTR pMemberNameHint = nullptr;
7964-
uint32 memberNameHintLength = 0;
7965-
uint32 memberNameOffset = 0;
7969+
uint32 memberNameHintLength = 0;
7970+
uint32 memberNameOffset = 0;
79667971
bool isComputedName = false;
79677972
bool isAsyncMethod = false;
79687973

7969-
if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
7974+
if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
79707975
{
79717976
RestorePoint parsedAsync;
79727977
this->GetScanner()->Capture(&parsedAsync);
@@ -8002,6 +8007,7 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
80028007
}
80038008
else // not computed name
80048009
{
8010+
maybeAccessor = !this->GetScanner()->LastIdentifierHasEscape();
80058011
memberPid = this->ParseClassPropertyName(&pidHint);
80068012
if (pidHint)
80078013
{
@@ -8063,21 +8069,21 @@ ParseNodeClass * Parser::ParseClassDecl(BOOL isDeclaration, LPCOLESTR pNameHint,
80638069
else
80648070
{
80658071
ParseNodePtr pnodeMember = nullptr;
8066-
8067-
bool isMemberNamedGetOrSet = false;
80688072
RestorePoint beginMethodName;
80698073
this->GetScanner()->Capture(&beginMethodName);
8070-
if (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set)
8074+
8075+
if (maybeAccessor && (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set))
80718076
{
80728077
this->GetScanner()->ScanForcingPid();
80738078
}
8079+
80748080
if (m_token.tk == tkLParen)
80758081
{
80768082
this->GetScanner()->SeekTo(beginMethodName);
8077-
isMemberNamedGetOrSet = true;
8083+
maybeAccessor = false;
80788084
}
80798085

8080-
if ((memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set) && !isMemberNamedGetOrSet)
8086+
if (maybeAccessor && (memberPid == wellKnownPropertyPids.get || memberPid == wellKnownPropertyPids.set))
80818087
{
80828088
bool isGetter = (memberPid == wellKnownPropertyPids.get);
80838089

@@ -9244,7 +9250,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
92449250
RestoreStateFrom(&parserState);
92459251

92469252
this->GetScanner()->SeekTo(termStart);
9247-
if (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
9253+
if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
92489254
{
92499255
ichMin = this->GetScanner()->IchMinTok();
92509256
iecpMin = this->GetScanner()->IecpMinTok();
@@ -10216,7 +10222,7 @@ ParseNodePtr Parser::ParseStatement()
1021610222

1021710223
case tkID:
1021810224
case tkLET:
10219-
if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.let)
10225+
if (tok == tkLET || CheckContextualKeyword(wellKnownPropertyPids.let))
1022010226
{
1022110227
// We see "let" at the start of a statement. This could either be a declaration or an identifier
1022210228
// reference. The next token determines which.
@@ -10241,7 +10247,7 @@ ParseNodePtr Parser::ParseStatement()
1024110247
}
1024210248
this->GetScanner()->SeekTo(parsedLet);
1024310249
}
10244-
else if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.async && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
10250+
else if (CheckContextualKeyword(wellKnownPropertyPids.async) && m_scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
1024510251
{
1024610252
RestorePoint parsedAsync;
1024710253
this->GetScanner()->Capture(&parsedAsync);
@@ -10286,7 +10292,7 @@ ParseNodePtr Parser::ParseStatement()
1028610292
ichMin = this->GetScanner()->IchMinTok();
1028710293

1028810294
this->GetScanner()->Scan();
10289-
if (m_token.tk == tkAWAIT || (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.await))
10295+
if (m_token.tk == tkAWAIT || CheckContextualKeyword(wellKnownPropertyPids.await))
1029010296
{
1029110297
if (!this->GetScanner()->AwaitIsKeywordRegion())
1029210298
{
@@ -10317,7 +10323,7 @@ ParseNodePtr Parser::ParseStatement()
1031710323
switch (tok)
1031810324
{
1031910325
case tkID:
10320-
if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.let)
10326+
if (CheckContextualKeyword(wellKnownPropertyPids.let))
1032110327
{
1032210328
// We see "let" in the init part of a for loop. This could either be a declaration or an identifier
1032310329
// reference. The next token determines which.
@@ -10426,7 +10432,7 @@ ParseNodePtr Parser::ParseStatement()
1042610432
if (TokIsForInOrForOf())
1042710433
{
1042810434
bool isForOf = (m_token.tk != tkIN);
10429-
Assert(!isForOf || (m_token.tk == tkID && m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.of));
10435+
Assert(!isForOf || CheckContextualKeyword(wellKnownPropertyPids.of));
1043010436

1043110437
if (isForAwait && !isForOf)
1043210438
{
@@ -11321,9 +11327,7 @@ ParseNodePtr Parser::ParseStatement()
1132111327
BOOL
1132211328
Parser::TokIsForInOrForOf()
1132311329
{
11324-
return m_token.tk == tkIN ||
11325-
(m_token.tk == tkID &&
11326-
m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.of);
11330+
return m_token.tk == tkIN || CheckContextualKeyword(wellKnownPropertyPids.of);
1132711331
}
1132811332

1132911333
/***************************************************************************

lib/Parser/Parse.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ class Parser
328328
bool CheckStrictModeStrPid(IdentPtr pid);
329329
bool CheckAsmjsModeStrPid(IdentPtr pid);
330330

331+
bool CheckContextualKeyword(IdentPtr keywordPid)
332+
{
333+
if (m_token.tk == tkID && !GetScanner()->LastIdentifierHasEscape())
334+
{
335+
IdentPtr pid = m_token.GetIdentifier(GetHashTbl());
336+
return pid == keywordPid;
337+
}
338+
return false;
339+
}
340+
331341
bool IsCurBlockInLoop() const;
332342

333343
void InitPids();

lib/Parser/Scan.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ tokens Scanner<EncodingPolicy>::ScanIdentifierContinue(bool identifyKwds, bool f
492492
break;
493493
}
494494

495+
m_lastIdentifierHasEscape = fHasEscape;
496+
495497
Assert(p - pchMin > 0 && p - pchMin <= LONG_MAX);
496498

497499
*pp = p;

lib/Parser/Scan.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,10 @@ class Scanner : public IScanner, public EncodingPolicy
493493
return m_EscapeOnLastTkStrCon;
494494
}
495495

496+
bool LastIdentifierHasEscape()
497+
{
498+
return m_lastIdentifierHasEscape;
499+
}
496500

497501
bool IsOctOrLeadingZeroOnLastTKNumber()
498502
{
@@ -730,6 +734,7 @@ class Scanner : public IScanner, public EncodingPolicy
730734
BOOL m_doubleQuoteOnLastTkStrCon :1;
731735
bool m_OctOrLeadingZeroOnLastTKNumber :1;
732736
bool m_EscapeOnLastTkStrCon:1;
737+
bool m_lastIdentifierHasEscape:1;
733738
BOOL m_fNextStringTemplateIsTagged:1; // the next string template scanned has a tag (must create raw strings)
734739
BYTE m_DeferredParseFlags:2; // suppressStrPid and suppressIdPid
735740
bool es6UnicodeMode; // True if ES6Unicode Extensions are enabled.

test/es5/setget.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,7 @@ test(false);
220220
test(true);
221221
})();
222222

223+
try {
224+
eval("({ g\\u0065t foo() {} })");
225+
write("Get and set cannot contain unicode escapes");
226+
} catch {}

test/es6module/module-syntax.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ var tests = [
117117
testModuleScript('function () { export default null; }', 'Syntax error export in function', true);
118118
testModuleScript('export {foo}', 'Syntax error undefined export', true);
119119
testModuleScript('export {Array}', 'Syntax error exporting a global name with no local definition', true);
120+
testModuleScript('var x; export { x \\u0061s y }', 'Syntax error if as keyword has unicode escape', true);
121+
testModuleScript('export { x } \\u0066rom "module";', 'Syntax error if as keyword has unicode escape', true);
120122
}
121123
},
122124
{
@@ -164,6 +166,7 @@ var tests = [
164166
testModuleScript(`do import { default } from "module"
165167
while (false);`, 'Syntax error export in while', true);
166168
testModuleScript('function () { import { default } from "module"; }', 'Syntax error export in function', true);
169+
testModuleScript('import { default \\u0061s y } from "module";', 'Syntax error if as keyword has unicode escape', true);
167170
}
168171
},
169172
{

test/es7/asyncawait-syntax.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ var tests = [
134134
assert.doesNotThrow(function () { eval("(async function (z) {})[0]"); }, "Should not throw when async function occurs a type conversion");
135135
}
136136
},
137+
{
138+
name: "Async keyword cannot contain unicode escapes",
139+
body: function () {
140+
assert.throws(function () { eval("\\u0061sync function foo() {} }"); }, SyntaxError, "async keyword cannot contain unicode escapes");
141+
assert.throws(function () { eval("({ \\u0061sync foo() {} })"); }, SyntaxError, "async keyword cannot contain unicode escapes");
142+
assert.throws(function () { eval("class A { \\u0061sync foo() {} }"); }, SyntaxError, "async keyword cannot contain unicode escapes");
143+
}
144+
},
137145
{
138146
name: "It is a Syntax Error if FormalParameters Contains AwaitExpression is true",
139147
body: function () {

0 commit comments

Comments
 (0)