Skip to content

Commit 19738b7

Browse files
committed
[MERGE #5601 @boingoing] Assertion when scanning invalid numeric literal after multi-unit characters
Merge pull request #5601 from boingoing:multiunit_assert 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
2 parents f2d1409 + ed1e5e2 commit 19738b7

File tree

3 files changed

+18
-4
lines changed

3 files changed

+18
-4
lines changed

lib/Parser/Scan.cpp

Lines changed: 7 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 savedMultiUnits)
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(savedMultiUnits);
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(savedMultiUnits);
698700
Error(ERRIdAfterLit);
699701
}
700702
}
701703

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

@@ -1587,6 +1590,7 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
15871590
m_tkPrevious = m_ptoken->tk;
15881591
m_iecpLimTokPrevious = IecpLimTok(); // Introduced for use by lambda parsing to find correct span of expression lambdas
15891592
m_ichLimTokPrevious = IchLimTok();
1593+
size_t savedMultiUnits = this->m_cMultiUnits;
15901594

15911595
if (p >= last)
15921596
{
@@ -1720,9 +1724,10 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
17201724
p = m_pchMinTok;
17211725
this->RestoreMultiUnits(m_cMinTokMultiUnits);
17221726
bool likelyInt = true;
1723-
pchT = FScanNumber(p, &dbl, likelyInt);
1727+
pchT = FScanNumber(p, &dbl, likelyInt, savedMultiUnits);
17241728
if (p == pchT)
17251729
{
1730+
this->RestoreMultiUnits(savedMultiUnits);
17261731
Assert(this->PeekFirst(p, last) != '.');
17271732
Error(ERRbadNumber);
17281733
}

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 savedMultiUnits);
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ 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+
if (WScript.Platform.INTL_LIBRARY === "winglob" || WScript.Platform.INTL_LIBRARY === "icu") {
90+
assert.throws(() => eval('\u20091a'), SyntaxError, 'Multi-unit whitespace followed by numeric literal followed by identifier', 'Unexpected identifier after numeric literal');
91+
assert.throws(() => eval('\u20091\\u0065'), SyntaxError, 'Multi-unit whitespace followed by numeric literal followed by unicode escape sequence', 'Unexpected identifier after numeric literal');
92+
assert.throws(() => eval('\u20090o1239'), SyntaxError, 'Multi-unit whitespace followed by invalid octal numeric literal', 'Invalid number');
93+
}
94+
}
8595
}
8696
];
8797

0 commit comments

Comments
 (0)