- Revision
- 245655
- Author
- [email protected]
- Date
- 2019-05-22 15:43:25 -0700 (Wed, 22 May 2019)
Log Message
[ESNext] Implement support for Numeric Separators
https://bugs.webkit.org/show_bug.cgi?id=196351
Reviewed by Keith Miller.
JSTests:
* stress/numeric-literal-separators.js: Added.
Add tests for feature.
* test262/expectations.yaml:
Mark 60 test cases as passing.
Source/_javascript_Core:
Implement the following proposal, which is now Stage 3:
https://github.com/tc39/proposal-numeric-separator
Specifically, this allows `_` to be used as a separator in numeric literals.
It may be inserted arbitrarily without semantic effect, but it may not occur:
- multiple times in a row
- at the beginning or end of the literal
- adjacent to `0x`, `0b`, `0o`, `.`, `e`, or `n`
- after a leading zero (e.g. `0_123`), even in sloppy mode
* parser/Lexer.cpp:
(JSC::isASCIIDigitOrSeparator): Added.
(JSC::isASCIIHexDigitOrSeparator): Added.
(JSC::isASCIIBinaryDigitOrSeparator): Added.
(JSC::isASCIIOctalDigitOrSeparator): Added.
(JSC::Lexer<T>::parseHex):
(JSC::Lexer<T>::parseBinary):
(JSC::Lexer<T>::parseOctal):
(JSC::Lexer<T>::parseDecimal):
(JSC::Lexer<T>::parseNumberAfterDecimalPoint):
(JSC::Lexer<T>::parseNumberAfterExponentIndicator):
(JSC::Lexer<T>::lexWithoutClearingLineTerminator):
* parser/Lexer.h:
Modified Paths
Added Paths
Diff
Modified: trunk/JSTests/ChangeLog (245654 => 245655)
--- trunk/JSTests/ChangeLog 2019-05-22 22:34:52 UTC (rev 245654)
+++ trunk/JSTests/ChangeLog 2019-05-22 22:43:25 UTC (rev 245655)
@@ -1,3 +1,16 @@
+2019-05-22 Ross Kirsling <[email protected]>
+
+ [ESNext] Implement support for Numeric Separators
+ https://bugs.webkit.org/show_bug.cgi?id=196351
+
+ Reviewed by Keith Miller.
+
+ * stress/numeric-literal-separators.js: Added.
+ Add tests for feature.
+
+ * test262/expectations.yaml:
+ Mark 60 test cases as passing.
+
2019-05-22 Tadeu Zagallo <[email protected]>
llint_slow_path_get_by_id needs to hold the CodeBlock's to update the metadata's mode
Added: trunk/JSTests/stress/numeric-literal-separators.js (0 => 245655)
--- trunk/JSTests/stress/numeric-literal-separators.js (rev 0)
+++ trunk/JSTests/stress/numeric-literal-separators.js 2019-05-22 22:43:25 UTC (rev 245655)
@@ -0,0 +1,96 @@
+//@ runBigIntEnabled
+
+function shouldNotThrow(script) {
+ eval(script);
+}
+
+function shouldThrow(script, errorType) {
+ let error;
+ try {
+ eval(script);
+ } catch (e) {
+ error = e;
+ }
+
+ if (!(error instanceof errorType))
+ throw new Error(`Expected ${errorType.name}!`);
+}
+
+shouldNotThrow('1_1_1');
+shouldNotThrow('0x1_1_1');
+shouldNotThrow('0b1_1_1');
+shouldNotThrow('0o1_1_1');
+shouldNotThrow('10000000000_0');
+shouldNotThrow('0x100000000_0');
+shouldNotThrow('0b100000000000000000000000000000000_0');
+shouldNotThrow('0o10000000000_0');
+
+shouldNotThrow('1_1_1n');
+shouldNotThrow('0x1_1_1n');
+shouldNotThrow('0b1_1_1n');
+shouldNotThrow('0o1_1_1n');
+shouldNotThrow('10000000000_0n');
+shouldNotThrow('0x100000000_0n');
+shouldNotThrow('0b100000000000000000000000000000000_0n');
+shouldNotThrow('0o10000000000_0n');
+
+shouldNotThrow('.1_1_1');
+shouldNotThrow('1_1_1.2_2_2');
+shouldNotThrow('1_1_1.2_2_2e3_3_3');
+shouldNotThrow('1_1_1.2_2_2e+3_3_3');
+shouldNotThrow('1_1_1.2_2_2e-3_3_3');
+shouldNotThrow('1_1_1e2_2_2');
+shouldNotThrow('1_1_1e+2_2_2');
+shouldNotThrow('1_1_1e-2_2_2');
+shouldNotThrow('1_1_1.e2_2_2');
+shouldNotThrow('1_1_1.e+2_2_2');
+shouldNotThrow('1_1_1.e-2_2_2');
+
+shouldThrow('_1', ReferenceError);
+shouldThrow('1_', SyntaxError);
+shouldThrow('1__1', SyntaxError);
+shouldThrow('0x_1', SyntaxError);
+shouldThrow('0x1_', SyntaxError);
+shouldThrow('0x1__1', SyntaxError);
+shouldThrow('0b_1', SyntaxError);
+shouldThrow('0b1_', SyntaxError);
+shouldThrow('0b1__1', SyntaxError);
+shouldThrow('0o_1', SyntaxError);
+shouldThrow('0o1_', SyntaxError);
+shouldThrow('0o1__1', SyntaxError);
+shouldThrow('0_1', SyntaxError);
+shouldThrow('10000000000_', SyntaxError);
+shouldThrow('10000000000__0', SyntaxError);
+shouldThrow('0x100000000_', SyntaxError);
+shouldThrow('0x100000000__0', SyntaxError);
+shouldThrow('0b100000000000000000000000000000000_', SyntaxError);
+shouldThrow('0b100000000000000000000000000000000__0', SyntaxError);
+shouldThrow('0o10000000000_', SyntaxError);
+shouldThrow('0o10000000000__0', SyntaxError);
+
+shouldThrow('1_n', SyntaxError);
+shouldThrow('0x1_n', SyntaxError);
+shouldThrow('0b1_n', SyntaxError);
+shouldThrow('0o1_n', SyntaxError);
+shouldThrow('10000000000_n', SyntaxError);
+shouldThrow('0x100000000_n', SyntaxError);
+shouldThrow('0b100000000000000000000000000000000_n', SyntaxError);
+shouldThrow('0o10000000000_n', SyntaxError);
+
+shouldThrow('._1', SyntaxError);
+shouldThrow('.1_', SyntaxError);
+shouldThrow('.1__1', SyntaxError);
+shouldThrow('1_.2', SyntaxError);
+shouldThrow('1._2', SyntaxError);
+shouldThrow('1.2_', SyntaxError);
+shouldThrow('1.2__2', SyntaxError);
+shouldThrow('1_e2', SyntaxError);
+shouldThrow('1e_2', SyntaxError);
+shouldThrow('1e2_', SyntaxError);
+shouldThrow('1e2__2', SyntaxError);
+shouldThrow('1e+_2', SyntaxError);
+shouldThrow('1e+2_', SyntaxError);
+shouldThrow('1e+2__2', SyntaxError);
+shouldThrow('1e-_2', SyntaxError);
+shouldThrow('1e-2_', SyntaxError);
+shouldThrow('1e-2__2', SyntaxError);
Modified: trunk/JSTests/test262/expectations.yaml (245654 => 245655)
--- trunk/JSTests/test262/expectations.yaml 2019-05-22 22:34:52 UTC (rev 245654)
+++ trunk/JSTests/test262/expectations.yaml 2019-05-22 22:43:25 UTC (rev 245655)
@@ -2586,96 +2586,6 @@
test/language/global-code/script-decl-var-err.js:
default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
-test/language/literals/numeric/numeric-separator-literal-bil-bd-nsl-bd.js:
- default: 'SyntaxError: No space between binary literal and identifier'
- strict mode: 'SyntaxError: No space between binary literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-bil-bd-nsl-bds.js:
- default: 'SyntaxError: No space between binary literal and identifier'
- strict mode: 'SyntaxError: No space between binary literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-bil-bds-nsl-bd.js:
- default: 'SyntaxError: No space between binary literal and identifier'
- strict mode: 'SyntaxError: No space between binary literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-bil-bds-nsl-bds.js:
- default: 'SyntaxError: No space between binary literal and identifier'
- strict mode: 'SyntaxError: No space between binary literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-dd-dot-dd-ep-sign-minus-dd-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dd-dot-dd-ep-sign-minus-dds-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dd-dot-dd-ep-sign-plus-dd-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dd-dot-dd-ep-sign-plus-dds-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dd-nsl-dd-one-of.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dds-dot-dd-nsl-dd-ep-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dds-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dot-dd-nsl-dd-ep.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dot-dd-nsl-dds-ep.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dot-dds-nsl-dd-ep.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-dot-dds-nsl-dds-ep.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-hil-hd-nsl-hd.js:
- default: 'SyntaxError: No space between hexadecimal literal and identifier'
- strict mode: 'SyntaxError: No space between hexadecimal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-hil-hd-nsl-hds.js:
- default: 'SyntaxError: No space between hexadecimal literal and identifier'
- strict mode: 'SyntaxError: No space between hexadecimal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-hil-hds-nsl-hd.js:
- default: 'SyntaxError: No space between hexadecimal literal and identifier'
- strict mode: 'SyntaxError: No space between hexadecimal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-hil-hds-nsl-hds.js:
- default: 'SyntaxError: No space between hexadecimal literal and identifier'
- strict mode: 'SyntaxError: No space between hexadecimal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-hil-od-nsl-od-one-of.js:
- default: 'SyntaxError: No space between hexadecimal literal and identifier'
- strict mode: 'SyntaxError: No space between hexadecimal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-nzd-nsl-dd-one-of.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-nzd-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-nzd-nsl-dds.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-oil-od-nsl-od-one-of.js:
- default: 'SyntaxError: No space between octal literal and identifier'
- strict mode: 'SyntaxError: No space between octal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-oil-od-nsl-od.js:
- default: 'SyntaxError: No space between octal literal and identifier'
- strict mode: 'SyntaxError: No space between octal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-oil-od-nsl-ods.js:
- default: 'SyntaxError: No space between octal literal and identifier'
- strict mode: 'SyntaxError: No space between octal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-oil-ods-nsl-od.js:
- default: 'SyntaxError: No space between octal literal and identifier'
- strict mode: 'SyntaxError: No space between octal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-oil-ods-nsl-ods.js:
- default: 'SyntaxError: No space between octal literal and identifier'
- strict mode: 'SyntaxError: No space between octal literal and identifier'
-test/language/literals/numeric/numeric-separator-literal-sign-minus-dds-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/numeric/numeric-separator-literal-sign-plus-dds-nsl-dd.js:
- default: 'SyntaxError: No identifiers allowed directly after numeric literal'
- strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
test/language/literals/regexp/named-groups/invalid-dangling-groupname-2-u.js:
default: 'Test262: This statement should not be evaluated.'
strict mode: 'Test262: This statement should not be evaluated.'
Modified: trunk/Source/_javascript_Core/ChangeLog (245654 => 245655)
--- trunk/Source/_javascript_Core/ChangeLog 2019-05-22 22:34:52 UTC (rev 245654)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-05-22 22:43:25 UTC (rev 245655)
@@ -1,3 +1,34 @@
+2019-05-22 Ross Kirsling <[email protected]>
+
+ [ESNext] Implement support for Numeric Separators
+ https://bugs.webkit.org/show_bug.cgi?id=196351
+
+ Reviewed by Keith Miller.
+
+ Implement the following proposal, which is now Stage 3:
+ https://github.com/tc39/proposal-numeric-separator
+
+ Specifically, this allows `_` to be used as a separator in numeric literals.
+ It may be inserted arbitrarily without semantic effect, but it may not occur:
+ - multiple times in a row
+ - at the beginning or end of the literal
+ - adjacent to `0x`, `0b`, `0o`, `.`, `e`, or `n`
+ - after a leading zero (e.g. `0_123`), even in sloppy mode
+
+ * parser/Lexer.cpp:
+ (JSC::isASCIIDigitOrSeparator): Added.
+ (JSC::isASCIIHexDigitOrSeparator): Added.
+ (JSC::isASCIIBinaryDigitOrSeparator): Added.
+ (JSC::isASCIIOctalDigitOrSeparator): Added.
+ (JSC::Lexer<T>::parseHex):
+ (JSC::Lexer<T>::parseBinary):
+ (JSC::Lexer<T>::parseOctal):
+ (JSC::Lexer<T>::parseDecimal):
+ (JSC::Lexer<T>::parseNumberAfterDecimalPoint):
+ (JSC::Lexer<T>::parseNumberAfterExponentIndicator):
+ (JSC::Lexer<T>::lexWithoutClearingLineTerminator):
+ * parser/Lexer.h:
+
2019-05-22 Tadeu Zagallo <[email protected]>
llint_slow_path_get_by_id needs to hold the CodeBlock's lock to update the metadata's mode
Modified: trunk/Source/_javascript_Core/parser/Lexer.cpp (245654 => 245655)
--- trunk/Source/_javascript_Core/parser/Lexer.cpp 2019-05-22 22:34:52 UTC (rev 245654)
+++ trunk/Source/_javascript_Core/parser/Lexer.cpp 2019-05-22 22:43:25 UTC (rev 245655)
@@ -813,6 +813,30 @@
return isIdentPartIncludingEscapeTemplate(code, codeEnd);
}
+template<typename CharacterType>
+static inline bool isASCIIDigitOrSeparator(CharacterType character)
+{
+ return isASCIIDigit(character) || character == '_';
+}
+
+template<typename CharacterType>
+static inline bool isASCIIHexDigitOrSeparator(CharacterType character)
+{
+ return isASCIIHexDigit(character) || character == '_';
+}
+
+template<typename CharacterType>
+static inline bool isASCIIBinaryDigitOrSeparator(CharacterType character)
+{
+ return isASCIIBinaryDigit(character) || character == '_';
+}
+
+template<typename CharacterType>
+static inline bool isASCIIOctalDigitOrSeparator(CharacterType character)
+{
+ return isASCIIOctalDigit(character) || character == '_';
+}
+
static inline LChar singleEscape(int c)
{
if (c < 128) {
@@ -1490,20 +1514,29 @@
}
template <typename T>
-ALWAYS_INLINE auto Lexer<T>::parseHex() -> NumberParseResult
+ALWAYS_INLINE auto Lexer<T>::parseHex() -> Optional<NumberParseResult>
{
+ ASSERT(isASCIIHexDigit(m_current));
+
// Optimization: most hexadecimal values fit into 4 bytes.
uint32_t hexValue = 0;
int maximumDigits = 7;
do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIHexDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
hexValue = (hexValue << 4) + toASCIIHexValue(m_current);
shift();
--maximumDigits;
- } while (isASCIIHexDigit(m_current) && maximumDigits >= 0);
+ } while (isASCIIHexDigitOrSeparator(m_current) && maximumDigits >= 0);
if (LIKELY(maximumDigits >= 0 && m_current != 'n'))
- return hexValue;
+ return NumberParseResult { hexValue };
// No more place in the hexValue buffer.
// The values are shifted out and placed into the m_buffer8 vector.
@@ -1516,20 +1549,29 @@
hexValue <<= 4;
}
- while (isASCIIHexDigit(m_current)) {
+ while (isASCIIHexDigitOrSeparator(m_current)) {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIHexDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
record8(m_current);
shift();
}
if (UNLIKELY(Options::useBigInt() && m_current == 'n'))
- return makeIdentifier(m_buffer8.data(), m_buffer8.size());
+ return NumberParseResult { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
- return parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 16);
+ return NumberParseResult { parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 16) };
}
template <typename T>
ALWAYS_INLINE auto Lexer<T>::parseBinary() -> Optional<NumberParseResult>
{
+ ASSERT(isASCIIBinaryDigit(m_current));
+
// Optimization: most binary values fit into 4 bytes.
uint32_t binaryValue = 0;
const unsigned maximumDigits = 32;
@@ -1539,35 +1581,51 @@
LChar digits[maximumDigits];
do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIBinaryDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
binaryValue = (binaryValue << 1) + (m_current - '0');
digits[digit] = m_current;
shift();
--digit;
- } while (isASCIIBinaryDigit(m_current) && digit >= 0);
+ } while (isASCIIBinaryDigitOrSeparator(m_current) && digit >= 0);
- if (LIKELY(!isASCIIDigit(m_current) && digit >= 0 && m_current != 'n'))
- return Variant<double, const Identifier*> { binaryValue };
+ if (LIKELY(!isASCIIDigitOrSeparator(m_current) && digit >= 0 && m_current != 'n'))
+ return NumberParseResult { binaryValue };
for (int i = maximumDigits - 1; i > digit; --i)
record8(digits[i]);
- while (isASCIIBinaryDigit(m_current)) {
+ while (isASCIIBinaryDigitOrSeparator(m_current)) {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIBinaryDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
record8(m_current);
shift();
}
if (UNLIKELY(Options::useBigInt() && m_current == 'n'))
- return Variant<double, const Identifier*> { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
+ return NumberParseResult { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
if (isASCIIDigit(m_current))
return WTF::nullopt;
- return Variant<double, const Identifier*> { parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 2) };
+ return NumberParseResult { parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 2) };
}
template <typename T>
ALWAYS_INLINE auto Lexer<T>::parseOctal() -> Optional<NumberParseResult>
{
+ ASSERT(isASCIIOctalDigit(m_current));
+
// Optimization: most octal values fit into 4 bytes.
uint32_t octalValue = 0;
const unsigned maximumDigits = 10;
@@ -1577,31 +1635,44 @@
LChar digits[maximumDigits];
do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIOctalDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
octalValue = octalValue * 8 + (m_current - '0');
digits[digit] = m_current;
shift();
--digit;
- } while (isASCIIOctalDigit(m_current) && digit >= 0);
+ } while (isASCIIOctalDigitOrSeparator(m_current) && digit >= 0);
- if (LIKELY(!isASCIIDigit(m_current) && digit >= 0 && m_current != 'n'))
- return Variant<double, const Identifier*> { octalValue };
+ if (LIKELY(!isASCIIDigitOrSeparator(m_current) && digit >= 0 && m_current != 'n'))
+ return NumberParseResult { octalValue };
-
for (int i = maximumDigits - 1; i > digit; --i)
record8(digits[i]);
- while (isASCIIOctalDigit(m_current)) {
+ while (isASCIIOctalDigitOrSeparator(m_current)) {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIOctalDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
record8(m_current);
shift();
}
if (UNLIKELY(Options::useBigInt() && m_current == 'n'))
- return Variant<double, const Identifier*> { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
+ return NumberParseResult { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
if (isASCIIDigit(m_current))
return WTF::nullopt;
- return Variant<double, const Identifier*> { parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 8) };
+ return NumberParseResult { parseIntOverflow(m_buffer8.data(), m_buffer8.size(), 8) };
}
template <typename T>
@@ -1620,38 +1691,63 @@
LChar digits[maximumDigits];
do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
decimalValue = decimalValue * 10 + (m_current - '0');
digits[digit] = m_current;
shift();
--digit;
- } while (isASCIIDigit(m_current) && digit >= 0);
+ } while (isASCIIDigitOrSeparator(m_current) && digit >= 0);
if (digit >= 0 && m_current != '.' && !isASCIIAlphaCaselessEqual(m_current, 'e') && m_current != 'n')
- return Variant<double, const Identifier*> { decimalValue };
+ return NumberParseResult { decimalValue };
for (int i = maximumDigits - 1; i > digit; --i)
record8(digits[i]);
}
- while (isASCIIDigit(m_current)) {
+ while (isASCIIDigitOrSeparator(m_current)) {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIDigit(peek(1))))
+ return WTF::nullopt;
+
+ shift();
+ }
+
record8(m_current);
shift();
}
if (UNLIKELY(Options::useBigInt() && m_current == 'n'))
- return Variant<double, const Identifier*> { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
+ return NumberParseResult { makeIdentifier(m_buffer8.data(), m_buffer8.size()) };
return WTF::nullopt;
}
template <typename T>
-ALWAYS_INLINE void Lexer<T>::parseNumberAfterDecimalPoint()
+ALWAYS_INLINE bool Lexer<T>::parseNumberAfterDecimalPoint()
{
+ ASSERT(isASCIIDigit(m_current));
record8('.');
- while (isASCIIDigit(m_current)) {
+
+ do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIDigit(peek(1))))
+ return false;
+
+ shift();
+ }
+
record8(m_current);
shift();
- }
+ } while (isASCIIDigitOrSeparator(m_current));
+
+ return true;
}
template <typename T>
@@ -1668,9 +1764,17 @@
return false;
do {
+ if (m_current == '_') {
+ if (UNLIKELY(!isASCIIDigit(peek(1))))
+ return false;
+
+ shift();
+ }
+
record8(m_current);
shift();
- } while (isASCIIDigit(m_current));
+ } while (isASCIIDigitOrSeparator(m_current));
+
return true;
}
@@ -2090,7 +2194,11 @@
token = DOT;
break;
}
- parseNumberAfterDecimalPoint();
+ if (UNLIKELY(!parseNumberAfterDecimalPoint())) {
+ m_lexErrorMessage = "Non-number found after decimal point"_s;
+ token = INVALID_NUMERIC_LITERAL_ERRORTOK;
+ goto returnError;
+ }
token = DOUBLE;
if (isASCIIAlphaCaselessEqual(m_current, 'e')) {
if (!parseNumberAfterExponentIndicator()) {
@@ -2124,12 +2232,14 @@
shift();
auto parseNumberResult = parseHex();
- if (WTF::holds_alternative<double>(parseNumberResult))
- tokenData->doubleValue = WTF::get<double>(parseNumberResult);
+ if (!parseNumberResult)
+ tokenData->doubleValue = 0;
+ else if (WTF::holds_alternative<double>(*parseNumberResult))
+ tokenData->doubleValue = WTF::get<double>(*parseNumberResult);
else {
token = BIGINT;
shift();
- tokenData->bigIntString = WTF::get<const Identifier*>(parseNumberResult);
+ tokenData->bigIntString = WTF::get<const Identifier*>(*parseNumberResult);
tokenData->radix = 16;
}
@@ -2209,6 +2319,12 @@
break;
}
+ if (UNLIKELY(m_current == '_')) {
+ m_lexErrorMessage = "Numeric literals may not begin with 0_"_s;
+ token = UNTERMINATED_OCTAL_NUMBER_ERRORTOK;
+ goto returnError;
+ }
+
record8('0');
if (strictMode && isASCIIDigit(m_current)) {
m_lexErrorMessage = "Decimal integer literals with a leading zero are forbidden in strict mode"_s;
@@ -2240,7 +2356,11 @@
token = INTEGER;
if (m_current == '.') {
shift();
- parseNumberAfterDecimalPoint();
+ if (UNLIKELY(isASCIIDigit(m_current) && !parseNumberAfterDecimalPoint())) {
+ m_lexErrorMessage = "Non-number found after decimal point"_s;
+ token = INVALID_NUMERIC_LITERAL_ERRORTOK;
+ goto returnError;
+ }
token = DOUBLE;
}
if (isASCIIAlphaCaselessEqual(m_current, 'e')) {
Modified: trunk/Source/_javascript_Core/parser/Lexer.h (245654 => 245655)
--- trunk/Source/_javascript_Core/parser/Lexer.h 2019-05-22 22:34:52 UTC (rev 245654)
+++ trunk/Source/_javascript_Core/parser/Lexer.h 2019-05-22 22:43:25 UTC (rev 245655)
@@ -179,11 +179,11 @@
ALWAYS_INLINE StringParseResult parseTemplateLiteral(JSTokenData*, RawStringsBuildMode);
using NumberParseResult = Variant<double, const Identifier*>;
- ALWAYS_INLINE NumberParseResult parseHex();
+ ALWAYS_INLINE Optional<NumberParseResult> parseHex();
ALWAYS_INLINE Optional<NumberParseResult> parseBinary();
ALWAYS_INLINE Optional<NumberParseResult> parseOctal();
ALWAYS_INLINE Optional<NumberParseResult> parseDecimal();
- ALWAYS_INLINE void parseNumberAfterDecimalPoint();
+ ALWAYS_INLINE bool parseNumberAfterDecimalPoint();
ALWAYS_INLINE bool parseNumberAfterExponentIndicator();
ALWAYS_INLINE bool parseMultilineComment();