Skip to content

Commit 8536968

Browse files
committed
Assertion when scanning invalid numeric literal after multi-unit characters
We can throw an error when scanning numeric literals. If the numeric literal is preceeded by multi-unit whitespace (eaten as part of the numeric literal token) we will count up those multi-units into Scanner::m_cMultiUnits. However, if the numeric literal ends up throwing, we should reset the multi-unit counter in scanner to whatever it was before we started to scan the whitespace and numeric literal token. If we don't reset the multi-unit counter, the character offset for the error object will be wrong and debug build will assert. Fixes #5571
1 parent f2d1409 commit 8536968

File tree

3 files changed

+17
-4
lines changed

3 files changed

+17
-4
lines changed

lib/Parser/Scan.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ IdentPtr Scanner<EncodingPolicy>::PidOfIdentiferAt(EncodedCharPtr p, EncodedChar
587587
}
588588

589589
template <typename EncodingPolicy>
590-
typename Scanner<EncodingPolicy>::EncodedCharPtr Scanner<EncodingPolicy>::FScanNumber(EncodedCharPtr p, double *pdbl, bool& likelyInt)
590+
typename Scanner<EncodingPolicy>::EncodedCharPtr Scanner<EncodingPolicy>::FScanNumber(EncodedCharPtr p, double *pdbl, bool& likelyInt, size_t saveMultiUnits)
591591
{
592592
EncodedCharPtr last = m_pchLast;
593593
EncodedCharPtr pchT = nullptr;
@@ -686,6 +686,7 @@ typename Scanner<EncodingPolicy>::EncodedCharPtr Scanner<EncodingPolicy>::FScanN
686686
}
687687
if (this->charClassifier->IsIdStart(outChar))
688688
{
689+
this->RestoreMultiUnits(saveMultiUnits);
689690
Error(ERRIdAfterLit);
690691
}
691692

@@ -695,12 +696,14 @@ typename Scanner<EncodingPolicy>::EncodedCharPtr Scanner<EncodingPolicy>::FScanN
695696
startingLocation++; // TryReadEscape expects us to point to the 'u', and since it is by reference we need to do it beforehand.
696697
if (TryReadEscape(startingLocation, m_pchLast, &outChar))
697698
{
699+
this->RestoreMultiUnits(saveMultiUnits);
698700
Error(ERRIdAfterLit);
699701
}
700702
}
701703

702704
if (Js::NumberUtilities::IsDigit(*startingLocation))
703705
{
706+
this->RestoreMultiUnits(saveMultiUnits);
704707
Error(ERRbadNumber);
705708
}
706709

@@ -1616,6 +1619,8 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
16161619
}
16171620
}
16181621

1622+
size_t saveMultiUnits = this->m_cMultiUnits;
1623+
16191624
for (;;)
16201625
{
16211626
LLoop:
@@ -1720,9 +1725,10 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
17201725
p = m_pchMinTok;
17211726
this->RestoreMultiUnits(m_cMinTokMultiUnits);
17221727
bool likelyInt = true;
1723-
pchT = FScanNumber(p, &dbl, likelyInt);
1728+
pchT = FScanNumber(p, &dbl, likelyInt, saveMultiUnits);
17241729
if (p == pchT)
17251730
{
1731+
this->RestoreMultiUnits(saveMultiUnits);
17261732
Assert(this->PeekFirst(p, last) != '.');
17271733
Error(ERRbadNumber);
17281734
}

lib/Parser/Scan.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,6 @@ class Scanner : public IScanner, public EncodingPolicy
500500
// character of the token would have if the entire file was converted to Unicode (UTF16-LE).
501501
charcount_t IchLimTok(void) const
502502
{
503-
504503
Assert(m_currentCharacter - m_pchBase >= 0);
505504
Assert(m_currentCharacter - m_pchBase <= LONG_MAX);
506505
Assert(static_cast<charcount_t>(m_currentCharacter - m_pchBase) >= this->m_cMultiUnits);
@@ -788,7 +787,7 @@ class Scanner : public IScanner, public EncodingPolicy
788787
tokens SkipComment(EncodedCharPtr *pp, /* out */ bool* containTypeDef);
789788
tokens ScanRegExpConstant(ArenaAllocator* alloc);
790789
tokens ScanRegExpConstantNoAST(ArenaAllocator* alloc);
791-
EncodedCharPtr FScanNumber(EncodedCharPtr p, double *pdbl, bool& likelyInt);
790+
EncodedCharPtr FScanNumber(EncodedCharPtr p, double *pdbl, bool& likelyInt, size_t saveMultiUnits);
792791
IdentPtr PidOfIdentiferAt(EncodedCharPtr p, EncodedCharPtr last, bool fHadEscape, bool fHasMultiChar);
793792
IdentPtr PidOfIdentiferAt(EncodedCharPtr p, EncodedCharPtr last);
794793
uint32 UnescapeToTempBuf(EncodedCharPtr p, EncodedCharPtr last);

test/Scanner/NumericLiteralSuffix.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ var tests = [
8282
eval("\u2028var\u2028x\u2028=\u20281234\u2028; result = x;");
8383
assert.areEqual(1234, result, "Mutli-unit whitespace after numeric literal does not affect literal value");
8484
}
85+
},
86+
{
87+
name: "Multi-unit count updated in the middle of a token",
88+
body: function () {
89+
assert.throws(() => eval('\u20091a'), SyntaxError, 'Multi-unit whitespace followed by numeric literal followed by identifier', 'Unexpected identifier after numeric literal');
90+
assert.throws(() => eval('\u20091\\u0065'), SyntaxError, 'Multi-unit whitespace followed by numeric literal followed by unicode escape sequence', 'Unexpected identifier after numeric literal');
91+
assert.throws(() => eval('\u20090o1239'), SyntaxError, 'Multi-unit whitespace followed by invalid octal numeric literal', 'Invalid number');
92+
}
8593
}
8694
];
8795

0 commit comments

Comments
 (0)