Skip to content

Commit

Permalink
Parse octal and binary literals.
Browse files Browse the repository at this point in the history
Summary:
Allow the parsing of `0o` and `0b` numeric literals,
which are ES standard formats.

Reviewed By: tmikov

Differential Revision: D20878163

fbshipit-source-id: d6201945a0cf0071e6bd2b451b2d5c16d006cf46
  • Loading branch information
avp authored and facebook-github-bot committed Apr 6, 2020
1 parent b6d9e16 commit 14e99e2
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 13 deletions.
22 changes: 18 additions & 4 deletions lib/Parser/JSLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1028,12 +1028,23 @@ void JSLexer::scanNumber() {
bool ok = true;
const char *start = curCharPtr_;

// True when we encounter a legacy octal number (starts with '0').
bool legacyOctal = false;

// Detect the radix
if (*curCharPtr_ == '0') {
if ((curCharPtr_[1] | 32) == 'x') {
radix = 16;
curCharPtr_ += 2;
start += 2;
} else if ((curCharPtr_[1] | 32) == 'o') {
radix = 8;
curCharPtr_ += 2;
start += 2;
} else if ((curCharPtr_[1] | 32) == 'b') {
radix = 2;
curCharPtr_ += 2;
start += 2;
} else if (curCharPtr_[1] == '.') {
curCharPtr_ += 2;
goto fraction;
Expand All @@ -1042,6 +1053,7 @@ void JSLexer::scanNumber() {
goto exponent;
} else {
radix = 8;
legacyOctal = true;
++curCharPtr_;
}
}
Expand Down Expand Up @@ -1130,10 +1142,10 @@ void JSLexer::scanNumber() {
val = std::numeric_limits<double>::quiet_NaN();
}
} else {
if (radix == 8 && strictMode_ && curCharPtr_ - start > 1) {
if (legacyOctal && strictMode_ && curCharPtr_ - start > 1) {
if (!error(
token_.getSourceRange(),
"Octal literals are not allowed in strict mode")) {
"Octal literals must use '0o' in strict mode")) {
val = std::numeric_limits<double>::quiet_NaN();
goto done;
}
Expand All @@ -1142,11 +1154,13 @@ void JSLexer::scanNumber() {
// Handle the zero-radix case. This could only happen with radix 16 because
// otherwise start wouldn't have been changed.
if (curCharPtr_ == start) {
error(token_.getSourceRange(), "No hexadecimal digits after 0x");
error(
token_.getSourceRange(),
llvm::Twine("No digits after ") + StringRef(start - 2, 2));
val = std::numeric_limits<double>::quiet_NaN();
} else {
// Parse the rest of the number:
if (radix == 8) {
if (legacyOctal) {
// ES6.0 B.1.1
// If we encounter a "legacy" octal number (starting with a '0') but it
// contains '8' or '9' we interpret it as decimal.
Expand Down
6 changes: 6 additions & 0 deletions lib/VM/Operations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,12 @@ static inline double stringToNumber(
if (str16[0] == u'0' && letterToLower(str16[1]) == u'x') {
return parseIntWithRadix(str16.slice(2), 16);
}
if (str16[0] == u'0' && letterToLower(str16[1]) == u'o') {
return parseIntWithRadix(str16.slice(2), 8);
}
if (str16[0] == u'0' && letterToLower(str16[1]) == u'b') {
return parseIntWithRadix(str16.slice(2), 2);
}
}

// Finally, copy 16 bit chars into 8 bit chars and call dtoa.
Expand Down
2 changes: 1 addition & 1 deletion test/Parser/directives-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"use the force"
"use strict"
010
//CHECK: {{.*}}directives-2.js:14:1: error: Octal literals are not allowed in strict mode
//CHECK: {{.*}}directives-2.js:14:1: error: Octal literals must use '0o' in strict mode
//CHECK-NEXT: 010
//CHECK-NEXT: ^~~
2 changes: 1 addition & 1 deletion test/Parser/literal.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
"use strict";
0x

//CHECK: No hexadecimal digits after 0x
//CHECK: No digits after 0x
2 changes: 1 addition & 1 deletion test/Parser/octal.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@

"\001"; // this should be ok
x = 010;
//CHECK: {{.*}}octal.js:13:5: error: Octal literals are not allowed in strict mode
//CHECK: {{.*}}octal.js:13:5: error: Octal literals must use '0o' in strict mode
//CHECK-NEXT: x = 010;
//CHECK-NEXT: ^~~
32 changes: 31 additions & 1 deletion unittests/Parser/JSLexerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ TEST(JSLexerTest, OctalLiteralTest) {
SourceErrorManager sm;
DiagContext diag(sm);

JSLexer lex("01 010 09 019", sm, alloc);
JSLexer lex("01 010 09 019 0o11 0O11", sm, alloc);

auto tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
Expand All @@ -290,6 +290,36 @@ TEST(JSLexerTest, OctalLiteralTest) {
ASSERT_EQ(tok->getNumericLiteral(), 19.0);
ASSERT_EQ(diag.getWarnCountClear(), 1);

tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
ASSERT_EQ(tok->getNumericLiteral(), 9.0);

tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
ASSERT_EQ(tok->getNumericLiteral(), 9.0);

ASSERT_EQ(TokenKind::eof, lex.advance()->getKind());
}

TEST(JSLexerTest, BinaryLiteralTest) {
JSLexer::Allocator alloc;
SourceErrorManager sm;
DiagContext diag(sm);

JSLexer lex("0b1 0B1 0b101", sm, alloc);

auto tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
ASSERT_EQ(tok->getNumericLiteral(), 1.0);

tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
ASSERT_EQ(tok->getNumericLiteral(), 1.0);

tok = lex.advance();
ASSERT_EQ(tok->getKind(), TokenKind::numeric_literal);
ASSERT_EQ(tok->getNumericLiteral(), 5.0);

ASSERT_EQ(TokenKind::eof, lex.advance()->getKind());
}

Expand Down
9 changes: 9 additions & 0 deletions unittests/VMRuntime/OperationsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,15 @@ TEST_F(OperationsLargeHeapTest, ToNumberTest) {
StringToNumberTest(1152921504606847200, u"0x1000000000000081");
StringToNumberTest(1152921504606847200, u"0x1000000000000084");
StringToNumberTest(1152921504606847000, u"0x1000000000000079");

// Octal
StringToNumberTest(0, u"0o0");
StringToNumberTest(9, u"0o11");

// Binary
StringToNumberTest(0, u"0b0");
StringToNumberTest(3, u"0b11");
StringToNumberTest(1024, u"0b10000000000");
}

// Big hex number
Expand Down
5 changes: 0 additions & 5 deletions utils/testsuite/testsuite_blacklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,6 @@
"test262/test/language/expressions/new.target/",
"test262/test/language/expressions/object/method-definition/",
"test262/test/language/expressions/super/",
"test262/test/language/literals/numeric/binary.js",
"test262/test/language/literals/numeric/octal.js",
"test262/test/language/module-code/",
"test262/test/language/rest-parameters/",
"test262/test/language/statements/async-function/",
Expand Down Expand Up @@ -557,9 +555,6 @@
# TODO(T60938585): Make these conditional on the choice of build.
"test262/test/built-ins/decodeURI/S15.1.3.1_A2.5_T1.js",
"test262/test/built-ins/decodeURIComponent/S15.1.3.2_A2.5_T1.js",
# number literals
"test262/test/built-ins/Number/string-binary-literal.js",
"test262/test/built-ins/Number/string-octal-literal.js",
# default arguments
"esprima/test_fixtures/expression/primary/object/migrated_0038.js",
"mjsunit/es6/default-parameters-debug.js",
Expand Down

0 comments on commit 14e99e2

Please sign in to comment.