Skip to content

Commit 9a9cf89

Browse files
author
Kenji Fukuda
committed
Fixing labels, should throw syntax errors when labels are followed by lexical declarations
Fixes #352
1 parent 6b5190f commit 9a9cf89

File tree

4 files changed

+121
-1
lines changed

4 files changed

+121
-1
lines changed

lib/Parser/Parse.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9631,6 +9631,7 @@ ParseNodePtr Parser::ParseStatement()
96319631
uint fnop;
96329632
bool expressionStmt = false;
96339633
bool isAsyncMethod = false;
9634+
bool labelledStatement = false;
96349635
tokens tok;
96359636
#if EXCEPTION_RECOVERY
96369637
ParseNodeTryCatch * pParentTryCatch = nullptr;
@@ -9704,6 +9705,21 @@ ParseNodePtr Parser::ParseStatement()
97049705
{
97059706
pnode = ParseFncDeclCheckScope<buildAST>(fFncDeclaration | (isAsyncMethod ? fFncAsync : fFncNoFlgs));
97069707
}
9708+
9709+
Assert(pnode != nullptr);
9710+
ParseNodeFnc* pNodeFnc = (ParseNodeFnc*)pnode;
9711+
if (labelledStatement)
9712+
{
9713+
if (pNodeFnc->IsAsync())
9714+
{
9715+
Error(ERRLabelBeforeAsyncFncDeclaration);
9716+
}
9717+
else if (pNodeFnc->IsGenerator())
9718+
{
9719+
Error(ERRLabelBeforeGeneratorDeclaration);
9720+
}
9721+
}
9722+
97079723
if (isAsyncMethod)
97089724
{
97099725
pnode->AsParseNodeFnc()->cbMin = iecpMin;
@@ -9713,7 +9729,11 @@ ParseNodePtr Parser::ParseStatement()
97139729
}
97149730

97159731
case tkCLASS:
9716-
if (m_scriptContext->GetConfig()->IsES6ClassAndExtendsEnabled())
9732+
if (labelledStatement)
9733+
{
9734+
Error(ERRLabelBeforeClassDeclaration);
9735+
}
9736+
else if (m_scriptContext->GetConfig()->IsES6ClassAndExtendsEnabled())
97179737
{
97189738
pnode = ParseClassDecl<buildAST>(TRUE, nullptr, nullptr, nullptr);
97199739
}
@@ -9726,6 +9746,10 @@ ParseNodePtr Parser::ParseStatement()
97269746
case tkID:
97279747
if (m_token.GetIdentifier(this->GetHashTbl()) == wellKnownPropertyPids.let)
97289748
{
9749+
if (labelledStatement)
9750+
{
9751+
Error(ERRLabelBeforeLexicalDeclaration);
9752+
}
97299753
// We see "let" at the start of a statement. This could either be a declaration or an identifier
97309754
// reference. The next token determines which.
97319755
RestorePoint parsedLet;
@@ -9759,6 +9783,10 @@ ParseNodePtr Parser::ParseStatement()
97599783

97609784
case tkCONST:
97619785
case tkLET:
9786+
if (labelledStatement)
9787+
{
9788+
Error(ERRLabelBeforeLexicalDeclaration);
9789+
}
97629790
ichMin = this->GetScanner()->IchMinTok();
97639791

97649792
this->GetScanner()->Scan();
@@ -10628,6 +10656,7 @@ ParseNodePtr Parser::ParseStatement()
1062810656
pLabelId->next = pLabelIdList;
1062910657
pLabelIdList = pLabelId;
1063010658
this->GetScanner()->Scan();
10659+
labelledStatement = true;
1063110660
goto LRestart;
1063210661
}
1063310662

lib/Parser/perrors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,7 @@ LSC_ERROR_MSG(1087, ERRInvalidExportName, "Unable to resolve module export name"
102102
LSC_ERROR_MSG(1088, ERRLetIDInLexicalDecl, "'let' is not an allowed identifier in lexical declarations")
103103

104104
LSC_ERROR_MSG(1089, ERRInvalidLHSInFor, "Invalid left-hand side in for loop")
105+
LSC_ERROR_MSG(1090, ERRLabelBeforeLexicalDeclaration, "Labels not allowed before lexical declaration")
106+
LSC_ERROR_MSG(1091, ERRLabelBeforeGeneratorDeclaration, "Labels not allowed before generator declaration")
107+
LSC_ERROR_MSG(1092, ERRLabelBeforeAsyncFncDeclaration, "Labels not allowed before async function declaration")
108+
LSC_ERROR_MSG(1093, ERRLabelBeforeClassDeclaration, "Labels not allowed before class declaration")

test/Error/Labels.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//-------------------------------------------------------------------------------------------------------
2+
// Copyright (C) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4+
//-------------------------------------------------------------------------------------------------------
5+
6+
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
7+
8+
9+
testFunctionAfterLabel =
10+
`a:
11+
function x()
12+
{
13+
}`
14+
testVarAfterLabel =
15+
`b:
16+
var a;`
17+
testForLoopAfterLabel =
18+
`c:
19+
for(i = 0; i < 10; i++)
20+
{
21+
d:
22+
for(j = 0; j < 10; j++)
23+
{
24+
if((i%j)%3 === 0)
25+
break d;
26+
}
27+
}`
28+
testWhileLoopAfterLabel =
29+
`i = 1, j = 1, product = 1
30+
c:
31+
while(i <= 20)
32+
{
33+
d:
34+
while(j <= 5)
35+
{
36+
j = j+2
37+
product *= i*j;
38+
break c;
39+
}
40+
i = i+1
41+
}
42+
assert.areEqual(3, product)
43+
`
44+
testNonLexicalDeclarationAfterLabel=
45+
`d:
46+
g = "Hola, Mundo."`
47+
var tests = [
48+
{
49+
name : "Labels followed by declarations which should all be syntax errors.",
50+
body : function ()
51+
{
52+
assert.throws(() => eval("a: let x;"), SyntaxError,
53+
"A let declaration must not be labelled", "Labels not allowed before lexical declaration");
54+
assert.throws(() => eval("b: const y = 0;"), SyntaxError,
55+
"A const declaration must not be labelled", "Labels not allowed before lexical declaration");
56+
assert.throws(() => eval("c: class z {}"), SyntaxError,
57+
"An class declaration must not be labelled", "Labels not allowed before class declaration");
58+
assert.throws(() => eval("d: function* w() {}"), SyntaxError,
59+
"A generator must not be labelled", "Labels not allowed before generator declaration");
60+
assert.throws(() => eval("e: async function u() { }"), SyntaxError,
61+
"An async function must not be labelled", "Labels not allowed before async function declaration");
62+
/*assert.throws(() => eval("e: async function* u() { }"), SyntaxError, //TODO: Uncomment when async generators supported.
63+
"An async generator must not be labelled", "Labels not allowed before async function declaration");*/
64+
}
65+
},
66+
{
67+
name : "Labels followed by non-declarations are valid syntax (non-generator non-async function declarations and non-lexical variable declarations are allowed)",
68+
body : function ()
69+
{
70+
assert.doesNotThrow(() => eval(testFunctionAfterLabel), "Function declarations should be allowed after a label.");
71+
assert.doesNotThrow(() => eval(testVarAfterLabel), "var declarations should be allowed after a label.");
72+
assert.doesNotThrow(() => eval(testForLoopAfterLabel), "for loop statements should be allowed after a label.");
73+
assert.doesNotThrow(() => eval(testNonLexicalDeclarationAfterLabel), "non-lexical declarations should be allowed after a label.");
74+
assert.doesNotThrow(() => eval(testWhileLoopAfterLabel), "while loops should be allowed after a label");
75+
}
76+
}
77+
];
78+
79+
testRunner.runTests(tests, {
80+
verbose : WScript.Arguments[0] != "summary"
81+
});

test/Error/rlexe.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,10 @@
178178
<compile-flags>-ExtendedErrorStackForTestHost -force:DeferParse</compile-flags>
179179
</default>
180180
</test>
181+
<test>
182+
<default>
183+
<files>Labels.js</files>
184+
<compile-flags>-args summary -endargs</compile-flags>
185+
</default>
186+
</test>
181187
</regress-exe>

0 commit comments

Comments
 (0)