Skip to content

Commit 13847da

Browse files
committed
Enable early reference errors for most assignments to invalid LHS
1 parent 5775ba1 commit 13847da

File tree

9 files changed

+40
-20
lines changed

9 files changed

+40
-20
lines changed

lib/Common/ConfigFlagsList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ PHASE(All)
3030
PHASE(ScanAhead)
3131
PHASE_DEFAULT_OFF(ParallelParse)
3232
PHASE(EarlyReferenceErrors)
33+
PHASE(EarlyErrorOnAssignToCall)
3334
PHASE(BgParse)
3435
PHASE(ByteCode)
3536
PHASE(CachedScope)

lib/Parser/Parse.cpp

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,7 +2488,7 @@ ParseNodePtr Parser::ParseImport()
24882488
ParseNodePtr pnode = ParseImportCall<buildAST>();
24892489
BOOL fCanAssign;
24902490
IdentToken token;
2491-
return ParsePostfixOperators<buildAST>(pnode, TRUE, FALSE, FALSE, &fCanAssign, &token);
2491+
return ParsePostfixOperators<buildAST>(pnode, TRUE, FALSE, FALSE, TRUE, &fCanAssign, &token);
24922492
}
24932493

24942494
this->GetScanner()->SeekTo(parsedImport);
@@ -2917,6 +2917,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
29172917
uint32 *pShortNameOffset,
29182918
_Inout_opt_ IdentToken* pToken /*= nullptr*/,
29192919
bool fUnaryOrParen /*= false*/,
2920+
BOOL fCanAssignToCall /*= TRUE*/,
29202921
_Out_opt_ BOOL* pfCanAssign /*= nullptr*/,
29212922
_Inout_opt_ BOOL* pfLikelyPattern /*= nullptr*/,
29222923
_Out_opt_ bool* pfIsDotOrIndex /*= nullptr*/,
@@ -3255,7 +3256,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
32553256
}
32563257
else
32573258
{
3258-
ParseNodePtr pnodeExpr = ParseTerm<buildAST>(FALSE, pNameHint, pHintLength, pShortNameOffset, nullptr, false, nullptr, nullptr, nullptr, plastRParen);
3259+
ParseNodePtr pnodeExpr = ParseTerm<buildAST>(FALSE, pNameHint, pHintLength, pShortNameOffset, nullptr, false, TRUE, nullptr, nullptr, nullptr, plastRParen);
32593260
if (buildAST)
32603261
{
32613262
pnode = CreateCallNode(knopNew, pnodeExpr, nullptr);
@@ -3413,7 +3414,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
34133414
break;
34143415
}
34153416

3416-
pnode = ParsePostfixOperators<buildAST>(pnode, fAllowCall, fInNew, isAsyncExpr, &fCanAssign, &term, pfIsDotOrIndex);
3417+
pnode = ParsePostfixOperators<buildAST>(pnode, fAllowCall, fInNew, isAsyncExpr, fCanAssignToCall, &fCanAssign, &term, pfIsDotOrIndex);
34173418

34183419
if (savedTopAsyncRef != nullptr &&
34193420
this->m_token.tk == tkDArrow)
@@ -3528,6 +3529,7 @@ ParseNodePtr Parser::ParsePostfixOperators(
35283529
BOOL fAllowCall,
35293530
BOOL fInNew,
35303531
BOOL isAsyncExpr,
3532+
BOOL fCanAssignToCallResult,
35313533
BOOL *pfCanAssign,
35323534
_Inout_ IdentToken* pToken,
35333535
_Out_opt_ bool* pfIsDotOrIndex /*= nullptr */)
@@ -3671,7 +3673,10 @@ ParseNodePtr Parser::ParsePostfixOperators(
36713673
}
36723674
if (pfCanAssign)
36733675
{
3674-
*pfCanAssign = FALSE;
3676+
*pfCanAssign = fCanAssignToCallResult &&
3677+
(m_sourceContextInfo ?
3678+
!PHASE_ON_RAW(Js::EarlyErrorOnAssignToCallPhase, m_sourceContextInfo->sourceContextId, GetCurrentFunctionNode()->functionId) :
3679+
!PHASE_ON1(Js::EarlyErrorOnAssignToCallPhase));
36753680
}
36763681
if (pfIsDotOrIndex)
36773682
{
@@ -4547,7 +4552,7 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
45474552
// First identify that the current expression is indeed the object/array literal. Otherwise we will just use the ParsrExpr to parse that.
45484553

45494554
ParseTerm<buildAST>(/* fAllowCall */ m_token.tk != tkSUPER, nullptr /*pNameHint*/, nullptr /*pHintLength*/, nullptr /*pShortNameOffset*/, &token, false /*fUnaryOrParen*/,
4550-
nullptr /*pfCanAssign*/, &fLikelyPattern);
4555+
TRUE, nullptr /*pfCanAssign*/, &fLikelyPattern);
45514556

45524557
this->GetScanner()->SeekTo(atExpression);
45534558

@@ -8408,6 +8413,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
84088413
RestorePoint termStart;
84098414
uint32 hintLength = 0;
84108415
uint32 hintOffset = 0;
8416+
BOOL fLikelyPattern = FALSE;
84118417

84128418
ParserState parserState;
84138419

@@ -8508,7 +8514,10 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
85088514
{
85098515
if ((nop == knopIncPre || nop == knopDecPre) && (m_token.tk != tkDArrow))
85108516
{
8511-
if (!fCanAssign && PHASE_ON1(Js::EarlyReferenceErrorsPhase))
8517+
if (!fCanAssign &&
8518+
(m_sourceContextInfo
8519+
? !PHASE_OFF_RAW(Js::EarlyReferenceErrorsPhase, m_sourceContextInfo->sourceContextId, GetCurrentFunctionNode()->functionId)
8520+
: !PHASE_OFF1(Js::EarlyReferenceErrorsPhase)))
85128521
{
85138522
Error(JSERR_CantAssignTo);
85148523
}
@@ -8604,8 +8613,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
86048613
else
86058614
{
86068615
ichMin = this->GetScanner()->IchMinTok();
8607-
BOOL fLikelyPattern = FALSE;
8608-
pnode = ParseTerm<buildAST>(TRUE, pNameHint, &hintLength, &hintOffset, &term, fUnaryOrParen, &fCanAssign, IsES6DestructuringEnabled() ? &fLikelyPattern : nullptr, &fIsDotOrIndex, plastRParen);
8616+
pnode = ParseTerm<buildAST>(TRUE, pNameHint, &hintLength, &hintOffset, &term, fUnaryOrParen, TRUE, &fCanAssign, IsES6DestructuringEnabled() ? &fLikelyPattern : nullptr, &fIsDotOrIndex, plastRParen);
86098617
if (pfLikelyPattern != nullptr)
86108618
{
86118619
*pfLikelyPattern = !!fLikelyPattern;
@@ -8680,7 +8688,10 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
86808688
if (!this->GetScanner()->FHadNewLine() &&
86818689
(tkInc == m_token.tk || tkDec == m_token.tk))
86828690
{
8683-
if (!fCanAssign && PHASE_ON1(Js::EarlyReferenceErrorsPhase))
8691+
if (!fCanAssign &&
8692+
(m_sourceContextInfo
8693+
? !PHASE_OFF_RAW(Js::EarlyReferenceErrorsPhase, m_sourceContextInfo->sourceContextId, GetCurrentFunctionNode()->functionId)
8694+
: !PHASE_OFF1(Js::EarlyReferenceErrorsPhase)))
86848695
{
86858696
Error(JSERR_CantAssignTo);
86868697
}
@@ -8764,7 +8775,10 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
87648775
{
87658776
break;
87668777
}
8767-
if (m_token.tk != tkDArrow && !fCanAssign && PHASE_ON1(Js::EarlyReferenceErrorsPhase))
8778+
if (m_token.tk != tkDArrow && !fCanAssign &&
8779+
(m_sourceContextInfo
8780+
? !PHASE_OFF_RAW(Js::EarlyReferenceErrorsPhase, m_sourceContextInfo->sourceContextId, GetCurrentFunctionNode()->functionId)
8781+
: !PHASE_OFF1(Js::EarlyReferenceErrorsPhase)))
87688782
{
87698783
Error(JSERR_CantAssignTo);
87708784
// No recovery necessary since this is a semantic, not structural, error.
@@ -8789,7 +8803,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
87898803

87908804
// Precedence is high enough. Consume the operator token.
87918805
this->GetScanner()->Scan();
8792-
fCanAssign = FALSE;
8806+
fCanAssign = !!fLikelyPattern;
87938807

87948808
// Special case the "?:" operator
87958809
if (nop == knopQmark)
@@ -9896,7 +9910,10 @@ ParseNodePtr Parser::ParseStatement()
98969910
Error(ERRForInNoInitAllowed);
98979911
}
98989912
}
9899-
if (!fCanAssign && PHASE_ON1(Js::EarlyReferenceErrorsPhase))
9913+
if (!fCanAssign &&
9914+
(m_sourceContextInfo
9915+
? !PHASE_OFF_RAW(Js::EarlyReferenceErrorsPhase, m_sourceContextInfo->sourceContextId, GetCurrentFunctionNode()->functionId)
9916+
: !PHASE_OFF1(Js::EarlyReferenceErrorsPhase)))
99009917
{
99019918
Error(JSERR_CantAssignTo);
99029919
}
@@ -12723,7 +12740,7 @@ ParseNodePtr Parser::ParseDestructuredVarDecl(tokens declarationType, bool isDec
1272312740
BOOL fCanAssign;
1272412741
IdentToken token;
1272512742
// Look for postfix operator
12726-
pnodeElem = ParsePostfixOperators<buildAST>(pnodeElem, TRUE, FALSE, FALSE, &fCanAssign, &token);
12743+
pnodeElem = ParsePostfixOperators<buildAST>(pnodeElem, TRUE, FALSE, FALSE, TRUE, &fCanAssign, &token);
1272712744
}
1272812745
}
1272912746
else if (m_token.tk == tkSUPER || m_token.tk == tkID || m_token.tk == tkTHIS)
@@ -12741,7 +12758,7 @@ ParseNodePtr Parser::ParseDestructuredVarDecl(tokens declarationType, bool isDec
1274112758
IdentToken token;
1274212759
// We aren't declaring anything, so scan the ID reference manually.
1274312760
pnodeElem = ParseTerm<buildAST>(/* fAllowCall */ m_token.tk != tkSUPER, nullptr /*pNameHint*/, nullptr /*pHintLength*/, nullptr /*pShortNameOffset*/, &token, false,
12744-
&fCanAssign);
12761+
FALSE, &fCanAssign);
1274512762

1274612763
// In this destructuring case we can force error here as we cannot assign.
1274712764

lib/Parser/Parse.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ class Parser
876876
uint32 *pShortNameOffset = nullptr,
877877
_Inout_opt_ IdentToken* pToken = nullptr,
878878
bool fUnaryOrParen = false,
879+
BOOL fCanAssignToCall = TRUE,
879880
_Out_opt_ BOOL* pfCanAssign = nullptr,
880881
_Inout_opt_ BOOL* pfLikelyPattern = nullptr,
881882
_Out_opt_ bool* pfIsDotOrIndex = nullptr,
@@ -886,6 +887,7 @@ class Parser
886887
BOOL fAllowCall,
887888
BOOL fInNew,
888889
BOOL isAsyncExpr,
890+
BOOL fCanAssignToCallResult,
889891
BOOL *pfCanAssign,
890892
_Inout_ IdentToken* pToken,
891893
_Out_opt_ bool* pfIsDotOrIndex = nullptr);

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6893,7 +6893,6 @@ void EmitAssignment(
68936893
break;
68946894

68956895
default:
6896-
Assert(!PHASE_ON1(Js::EarlyReferenceErrorsPhase));
68976896
byteCodeGenerator->Writer()->W1(Js::OpCode::RuntimeReferenceError, SCODE_CODE(JSERR_CantAssignTo));
68986897
break;
68996898
}

test/Bugs/rlexe.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
<default>
5656
<files>randombug.js</files>
5757
<baseline>randombug.baseline</baseline>
58-
<compile-flags>-CollectGarbage -ExtendedErrorStackForTestHost</compile-flags>
58+
<compile-flags>-CollectGarbage -ExtendedErrorStackForTestHost -off:earlyreferenceerrors</compile-flags>
5959
</default>
6060
</test>
6161
<test>

test/Optimizer/rlexe.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@
14621462
<test>
14631463
<default>
14641464
<files>test146.js</files>
1465-
<compile-flags>-off:bailonnoprofile</compile-flags>
1465+
<compile-flags>-off:bailonnoprofile -off:earlyreferenceerrors</compile-flags>
14661466
</default>
14671467
</test>
14681468
<test>

test/bailout/rlexe.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
<test>
207207
<default>
208208
<files>implicit_nosideeffect.js</files>
209+
<compile-flags>-off:earlyreferenceerrors</compile-flags>
209210
<baseline>implicit_nosideeffect.baseline</baseline>
210211
</default>
211212
</test>

test/es6/destructuring_obj.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ var tests = [
142142
assert.doesNotThrow(function () { eval("let a; ({a:((((a1))))} = {a:20})"); }, "Object expression pattern with parens is valid syntax");
143143
assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1} = {})"); }, "Object expression pattern with defaults as reference is valid syntax");
144144
assert.doesNotThrow(function () { eval("let a, r1; ({a:a1 = r1 = 44} = {})"); }, "Object expression pattern with chained assignments as defaults is valid syntax");
145-
assert.throws(function () { eval("let a, r1; ({a:(a1 = r1) = 44} = {})"); }, SyntaxError, "Object expression pattern with chained assignments but paren in between is not valid syntax", "Expected ')'");
145+
assert.throws(function () { eval("let a, r1; ({a:(a1 = r1) = 44} = {})"); }, ReferenceError, "Object expression pattern with chained assignments but paren in between is not valid syntax", "Invalid left-hand side in assignment");
146146
assert.doesNotThrow(function () { eval("var a; `${({a} = {})}`"); }, "Object expression pattern inside a string template is valid syntax");
147147
assert.throws(function () { eval("for (let {x} = {} of []) {}"); }, SyntaxError, "for.of has declaration pattern with initializer is not valid syntax", "for-of loop head declarations cannot have an initializer");
148148
assert.throws(function () { eval("for (let {x} = {} in []) {}"); }, SyntaxError, "for.in has declaration pattern with initializer is not valid syntax", "for-in loop head declarations cannot have an initializer");

test/loop/rlexe.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
<test>
44
<default>
55
<files>loop.js</files>
6-
<compile-flags>-forcejitloopbody</compile-flags>
6+
<compile-flags>-forcejitloopbody -off:earlyreferenceerrors</compile-flags>
77
<baseline>loop.baseline</baseline>
88
</default>
99
</test>
1010
<test>
1111
<default>
1212
<files>loop.js</files>
13-
<compile-flags>-forcejitloopbody -on:interruptprobe</compile-flags>
13+
<compile-flags>-forcejitloopbody -on:interruptprobe -off:earlyreferenceerrors</compile-flags>
1414
<baseline>loop.baseline</baseline>
1515
</default>
1616
</test>

0 commit comments

Comments
 (0)