Commit 9ffe0670 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Fix StringToBigInt("") to return 0n.

Whitespace-only strings count as empty too.

This behavior is different from BigInt.parseInt(""),
which throws a SyntaxError.

Bug: v8:6791, v8:6957
Change-Id: I6671c803f3ba83e23c3e0cad81d3af29dba61c9f
Reviewed-on: https://chromium-review.googlesource.com/727301Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48797}
parent 5917d5c1
...@@ -261,7 +261,7 @@ void AstValue::Internalize(Isolate* isolate) { ...@@ -261,7 +261,7 @@ void AstValue::Internalize(Isolate* isolate) {
case BIGINT: case BIGINT:
// TODO(adamk): Don't check-fail on conversion failure; instead // TODO(adamk): Don't check-fail on conversion failure; instead
// check for errors during parsing and throw at that point. // check for errors during parsing and throw at that point.
set_value(StringToBigInt(isolate, bigint_buffer_).ToHandleChecked()); set_value(BigIntLiteral(isolate, bigint_buffer_).ToHandleChecked());
break; break;
case BOOLEAN: case BOOLEAN:
if (bool_) { if (bool_) {
......
...@@ -65,7 +65,7 @@ BUILTIN(BigIntParseInt) { ...@@ -65,7 +65,7 @@ BUILTIN(BigIntParseInt) {
THROW_NEW_ERROR_RETURN_FAILURE( THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewSyntaxError(MessageTemplate::kToRadixFormatRange)); isolate, NewSyntaxError(MessageTemplate::kToRadixFormatRange));
} }
RETURN_RESULT_OR_FAILURE(isolate, StringToBigInt(isolate, subject, radix32)); RETURN_RESULT_OR_FAILURE(isolate, BigIntParseInt(isolate, subject, radix32));
} }
BUILTIN(BigIntAsUintN) { BUILTIN(BigIntAsUintN) {
......
...@@ -218,7 +218,7 @@ class StringToIntHelper { ...@@ -218,7 +218,7 @@ class StringToIntHelper {
} }
// Subclasses get access to internal state: // Subclasses get access to internal state:
enum State { kRunning, kError, kJunk, kZero, kDone }; enum State { kRunning, kError, kJunk, kEmpty, kZero, kDone };
Isolate* isolate() { return isolate_; } Isolate* isolate() { return isolate_; }
int radix() { return radix_; } int radix() { return radix_; }
...@@ -291,7 +291,7 @@ void StringToIntHelper::DetectRadixInternal(Char current, int length) { ...@@ -291,7 +291,7 @@ void StringToIntHelper::DetectRadixInternal(Char current, int length) {
UnicodeCache* unicode_cache = isolate_->unicode_cache(); UnicodeCache* unicode_cache = isolate_->unicode_cache();
if (!AdvanceToNonspace(unicode_cache, &current, end)) { if (!AdvanceToNonspace(unicode_cache, &current, end)) {
return set_state(kJunk); return set_state(kEmpty);
} }
if (*current == '+') { if (*current == '+') {
...@@ -432,6 +432,7 @@ class NumberParseIntHelper : public StringToIntHelper { ...@@ -432,6 +432,7 @@ class NumberParseIntHelper : public StringToIntHelper {
ParseInt(); ParseInt();
switch (state()) { switch (state()) {
case kJunk: case kJunk:
case kEmpty:
return JunkStringValue(); return JunkStringValue();
case kZero: case kZero:
return SignedZero(negative()); return SignedZero(negative());
...@@ -837,17 +838,32 @@ double StringToInt(Isolate* isolate, Handle<String> string, int radix) { ...@@ -837,17 +838,32 @@ double StringToInt(Isolate* isolate, Handle<String> string, int radix) {
class BigIntParseIntHelper : public StringToIntHelper { class BigIntParseIntHelper : public StringToIntHelper {
public: public:
// Configures what to return for empty or whitespace-only input strings.
enum class EmptyStringResult { kSyntaxError, kZero, kUnreachable };
// Used for BigInt.parseInt API, where the input is a Heap-allocated String. // Used for BigInt.parseInt API, where the input is a Heap-allocated String.
BigIntParseIntHelper(Isolate* isolate, Handle<String> string, int radix) BigIntParseIntHelper(Isolate* isolate, Handle<String> string, int radix,
: StringToIntHelper(isolate, string, radix) {} EmptyStringResult empty_string_result)
: StringToIntHelper(isolate, string, radix),
empty_string_result_(empty_string_result) {}
// Used for parsing BigInt literals, where the input is a buffer of // Used for parsing BigInt literals, where the input is a buffer of
// one-byte ASCII digits, along with an optional radix prefix. // one-byte ASCII digits, along with an optional radix prefix.
BigIntParseIntHelper(Isolate* isolate, const uint8_t* string, int length) BigIntParseIntHelper(Isolate* isolate, const uint8_t* string, int length)
: StringToIntHelper(isolate, string, length) {} : StringToIntHelper(isolate, string, length),
empty_string_result_(EmptyStringResult::kUnreachable) {}
MaybeHandle<BigInt> GetResult() { MaybeHandle<BigInt> GetResult() {
ParseInt(); ParseInt();
if (state() == kEmpty) {
if (empty_string_result_ == EmptyStringResult::kSyntaxError) {
set_state(kJunk);
} else if (empty_string_result_ == EmptyStringResult::kZero) {
set_state(kZero);
} else {
UNREACHABLE();
}
}
switch (state()) { switch (state()) {
case kJunk: case kJunk:
THROW_NEW_ERROR(isolate(), THROW_NEW_ERROR(isolate(),
...@@ -861,6 +877,7 @@ class BigIntParseIntHelper : public StringToIntHelper { ...@@ -861,6 +877,7 @@ class BigIntParseIntHelper : public StringToIntHelper {
result_->set_sign(negative()); result_->set_sign(negative());
result_->RightTrim(); result_->RightTrim();
return result_; return result_;
case kEmpty:
case kRunning: case kRunning:
break; break;
} }
...@@ -888,15 +905,24 @@ class BigIntParseIntHelper : public StringToIntHelper { ...@@ -888,15 +905,24 @@ class BigIntParseIntHelper : public StringToIntHelper {
private: private:
Handle<BigInt> result_; Handle<BigInt> result_;
EmptyStringResult empty_string_result_;
}; };
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string, MaybeHandle<BigInt> BigIntParseInt(Isolate* isolate, Handle<String> string,
int radix) { int radix) {
BigIntParseIntHelper helper(isolate, string, radix); BigIntParseIntHelper helper(
isolate, string, radix,
BigIntParseIntHelper::EmptyStringResult::kSyntaxError);
return helper.GetResult();
}
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string) {
BigIntParseIntHelper helper(isolate, string, 10,
BigIntParseIntHelper::EmptyStringResult::kZero);
return helper.GetResult(); return helper.GetResult();
} }
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, const char* string) { MaybeHandle<BigInt> BigIntLiteral(Isolate* isolate, const char* string) {
BigIntParseIntHelper helper(isolate, reinterpret_cast<const uint8_t*>(string), BigIntParseIntHelper helper(isolate, reinterpret_cast<const uint8_t*>(string),
static_cast<int>(strlen(string))); static_cast<int>(strlen(string)));
return helper.GetResult(); return helper.GetResult();
......
...@@ -105,17 +105,20 @@ double StringToDouble(UnicodeCache* unicode_cache, ...@@ -105,17 +105,20 @@ double StringToDouble(UnicodeCache* unicode_cache,
double StringToInt(Isolate* isolate, Handle<String> string, int radix); double StringToInt(Isolate* isolate, Handle<String> string, int radix);
V8_EXPORT_PRIVATE MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, // This follows BigInt.parseInt semantics: "" => SyntaxError.
Handle<String> string, MaybeHandle<BigInt> BigIntParseInt(Isolate* isolate, Handle<String> string,
int radix); int radix);
// This follows https://tc39.github.io/proposal-bigint/#sec-string-to-bigint
// semantics: "" => 0n.
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string);
// This version expects a zero-terminated character array. Radix will // This version expects a zero-terminated character array. Radix will
// be inferred from string prefix (case-insensitive): // be inferred from string prefix (case-insensitive):
// 0x -> hex // 0x -> hex
// 0o -> octal // 0o -> octal
// 0b -> binary // 0b -> binary
V8_EXPORT_PRIVATE MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, V8_EXPORT_PRIVATE MaybeHandle<BigInt> BigIntLiteral(Isolate* isolate,
const char* string); const char* string);
const int kDoubleToCStringMinBufferSize = 100; const int kDoubleToCStringMinBufferSize = 100;
......
...@@ -305,7 +305,7 @@ MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) { ...@@ -305,7 +305,7 @@ MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) {
bool BigInt::EqualToString(Handle<BigInt> x, Handle<String> y) { bool BigInt::EqualToString(Handle<BigInt> x, Handle<String> y) {
Isolate* isolate = x->GetIsolate(); Isolate* isolate = x->GetIsolate();
// a. Let n be StringToBigInt(y). // a. Let n be StringToBigInt(y).
MaybeHandle<BigInt> maybe_n = StringToBigInt(isolate, y, 10); MaybeHandle<BigInt> maybe_n = StringToBigInt(isolate, y);
// b. If n is NaN, return false. // b. If n is NaN, return false.
Handle<BigInt> n; Handle<BigInt> n;
if (!maybe_n.ToHandle(&n)) { if (!maybe_n.ToHandle(&n)) {
......
...@@ -90,9 +90,8 @@ const six = BigInt(6); ...@@ -90,9 +90,8 @@ const six = BigInt(6);
assertFalse(%Equal(one, 1.0000000000001)); assertFalse(%Equal(one, 1.0000000000001));
assertFalse(%Equal(1.0000000000001, one)); assertFalse(%Equal(1.0000000000001, one));
// TODO(neis): Empty string must get converted to 0n. assertTrue(%Equal(zero, ""));
//assertTrue(%Equal(zero, "")); assertTrue(%Equal("", zero));
//assertTrue(%Equal("", zero));
assertTrue(%Equal(one, "1")); assertTrue(%Equal(one, "1"));
assertTrue(%Equal("1", one)); assertTrue(%Equal("1", one));
...@@ -141,10 +140,12 @@ const six = BigInt(6); ...@@ -141,10 +140,12 @@ const six = BigInt(6);
assertFalse(one == 1.0000000000001); assertFalse(one == 1.0000000000001);
assertFalse(1.0000000000001 == one); assertFalse(1.0000000000001 == one);
//assertTrue(zero == ""); assertTrue(zero == "");
//assertTrue("" == zero); assertTrue("" == zero);
assertTrue(zero == " \t\r\n");
assertTrue(one == "1"); assertTrue(one == "1");
assertTrue("1" == one); assertTrue("1" == one);
assertFalse(" \t\r\n" == one);
assertTrue(one == {valueOf() { return true }}); assertTrue(one == {valueOf() { return true }});
assertTrue({valueOf() { return true }} == one); assertTrue({valueOf() { return true }} == one);
...@@ -191,8 +192,8 @@ const six = BigInt(6); ...@@ -191,8 +192,8 @@ const six = BigInt(6);
assertTrue(%NotEqual(one, 1.0000000000001)); assertTrue(%NotEqual(one, 1.0000000000001));
assertTrue(%NotEqual(1.0000000000001, one)); assertTrue(%NotEqual(1.0000000000001, one));
//assertFalse(%NotEqual(zero, "")); assertFalse(%NotEqual(zero, ""));
//assertFalse(%NotEqual("", zero)); assertFalse(%NotEqual("", zero));
assertFalse(%NotEqual(one, "1")); assertFalse(%NotEqual(one, "1"));
assertFalse(%NotEqual("1", one)); assertFalse(%NotEqual("1", one));
...@@ -241,8 +242,8 @@ const six = BigInt(6); ...@@ -241,8 +242,8 @@ const six = BigInt(6);
assertTrue(one != 1.0000000000001); assertTrue(one != 1.0000000000001);
assertTrue(1.0000000000001 != one); assertTrue(1.0000000000001 != one);
//assertFalse(zero != ""); assertFalse(zero != "");
//assertFalse("" != zero); assertFalse("" != zero);
assertFalse(one != "1"); assertFalse(one != "1");
assertFalse("1" != one); assertFalse("1" != one);
......
...@@ -66,25 +66,25 @@ TEST_F(BigIntWithIsolate, CompareToDouble) { ...@@ -66,25 +66,25 @@ TEST_F(BigIntWithIsolate, CompareToDouble) {
// Same bit length, difference in first digit. // Same bit length, difference in first digit.
double big_double = 4428155326412785451008.0; double big_double = 4428155326412785451008.0;
Handle<BigInt> big = Handle<BigInt> big =
StringToBigInt(isolate(), "0xF10D00000000000000").ToHandleChecked(); BigIntLiteral(isolate(), "0xF10D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kGreaterThan); Compare(big, big_double, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0xE00D00000000000000").ToHandleChecked(); big = BigIntLiteral(isolate(), "0xE00D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kLessThan); Compare(big, big_double, ComparisonResult::kLessThan);
double other_double = -13758438578910658560.0; double other_double = -13758438578910658560.0;
Handle<BigInt> other = Handle<BigInt> other =
StringToBigInt(isolate(), "-0xBEEFC1FE00000000").ToHandleChecked(); BigIntLiteral(isolate(), "-0xBEEFC1FE00000000").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kGreaterThan); Compare(other, other_double, ComparisonResult::kGreaterThan);
other = StringToBigInt(isolate(), "-0xBEEFCBFE00000000").ToHandleChecked(); other = BigIntLiteral(isolate(), "-0xBEEFCBFE00000000").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kLessThan); Compare(other, other_double, ComparisonResult::kLessThan);
// Same bit length, difference in non-first digit. // Same bit length, difference in non-first digit.
big = StringToBigInt(isolate(), "0xF00D00000000000001").ToHandleChecked(); big = BigIntLiteral(isolate(), "0xF00D00000000000001").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kGreaterThan); Compare(big, big_double, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0xF00A00000000000000").ToHandleChecked(); big = BigIntLiteral(isolate(), "0xF00A00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kLessThan); Compare(big, big_double, ComparisonResult::kLessThan);
other = StringToBigInt(isolate(), "-0xBEEFCAFE00000001").ToHandleChecked(); other = BigIntLiteral(isolate(), "-0xBEEFCAFE00000001").ToHandleChecked();
Compare(other, other_double, ComparisonResult::kLessThan); Compare(other, other_double, ComparisonResult::kLessThan);
// Same bit length, difference in fractional part. // Same bit length, difference in fractional part.
...@@ -93,17 +93,17 @@ TEST_F(BigIntWithIsolate, CompareToDouble) { ...@@ -93,17 +93,17 @@ TEST_F(BigIntWithIsolate, CompareToDouble) {
big = factory->NewBigIntFromInt(0xF00D00); big = factory->NewBigIntFromInt(0xF00D00);
Compare(big, 15731968.125, ComparisonResult::kLessThan); Compare(big, 15731968.125, ComparisonResult::kLessThan);
Compare(big, 15731967.875, ComparisonResult::kGreaterThan); Compare(big, 15731967.875, ComparisonResult::kGreaterThan);
big = StringToBigInt(isolate(), "0x123456789ab").ToHandleChecked(); big = BigIntLiteral(isolate(), "0x123456789ab").ToHandleChecked();
Compare(big, 1250999896491.125, ComparisonResult::kLessThan); Compare(big, 1250999896491.125, ComparisonResult::kLessThan);
// Equality! // Equality!
Compare(one, 1, ComparisonResult::kEqual); Compare(one, 1, ComparisonResult::kEqual);
Compare(minus_one, -1, ComparisonResult::kEqual); Compare(minus_one, -1, ComparisonResult::kEqual);
big = StringToBigInt(isolate(), "0xF00D00000000000000").ToHandleChecked(); big = BigIntLiteral(isolate(), "0xF00D00000000000000").ToHandleChecked();
Compare(big, big_double, ComparisonResult::kEqual); Compare(big, big_double, ComparisonResult::kEqual);
Handle<BigInt> two_52 = Handle<BigInt> two_52 =
StringToBigInt(isolate(), "0x10000000000000").ToHandleChecked(); BigIntLiteral(isolate(), "0x10000000000000").ToHandleChecked();
Compare(two_52, 4503599627370496.0, ComparisonResult::kEqual); Compare(two_52, 4503599627370496.0, ComparisonResult::kEqual);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment