cfc6a5c2c6
There is no good reason to have the meat of most objects' initialization
logic in heap.cc, all wrapped by the CALL_HEAP_FUNCTION macro. Instead,
this CL changes the protocol between Heap and Factory to be AllocateRaw,
and all object initialization work after (possibly retried) successful
raw allocation happens in the Factory.
This saves about 20KB of binary size on x64.
Original review: https://chromium-review.googlesource.com/c/v8/v8/+/959533
Originally landed as r52416 / f9a2e24bbc
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: Id072cbe6b3ed30afd339c7e502844b99ca12a647
Reviewed-on: https://chromium-review.googlesource.com/1000540
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52492}
1410 lines
45 KiB
C++
1410 lines
45 KiB
C++
// Copyright 2011 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "src/conversions.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <cmath>
|
|
|
|
#include "src/allocation.h"
|
|
#include "src/assert-scope.h"
|
|
#include "src/char-predicates-inl.h"
|
|
#include "src/dtoa.h"
|
|
#include "src/handles.h"
|
|
#include "src/heap/factory.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/objects/bigint.h"
|
|
#include "src/strtod.h"
|
|
#include "src/unicode-cache-inl.h"
|
|
#include "src/utils.h"
|
|
|
|
#if defined(_STLP_VENDOR_CSTD)
|
|
// STLPort doesn't import fpclassify into the std namespace.
|
|
#define FPCLASSIFY_NAMESPACE
|
|
#else
|
|
#define FPCLASSIFY_NAMESPACE std
|
|
#endif
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
namespace {
|
|
|
|
inline double JunkStringValue() {
|
|
return bit_cast<double, uint64_t>(kQuietNaNMask);
|
|
}
|
|
|
|
inline double SignedZero(bool negative) {
|
|
return negative ? uint64_to_double(Double::kSignMask) : 0.0;
|
|
}
|
|
|
|
inline bool isDigit(int x, int radix) {
|
|
return (x >= '0' && x <= '9' && x < '0' + radix) ||
|
|
(radix > 10 && x >= 'a' && x < 'a' + radix - 10) ||
|
|
(radix > 10 && x >= 'A' && x < 'A' + radix - 10);
|
|
}
|
|
|
|
inline bool isBinaryDigit(int x) { return x == '0' || x == '1'; }
|
|
|
|
template <class Iterator, class EndMark>
|
|
bool SubStringEquals(Iterator* current, EndMark end, const char* substring) {
|
|
DCHECK(**current == *substring);
|
|
for (substring++; *substring != '\0'; substring++) {
|
|
++*current;
|
|
if (*current == end || **current != *substring) return false;
|
|
}
|
|
++*current;
|
|
return true;
|
|
}
|
|
|
|
// Returns true if a nonspace character has been found and false if the
|
|
// end was been reached before finding a nonspace character.
|
|
template <class Iterator, class EndMark>
|
|
inline bool AdvanceToNonspace(UnicodeCache* unicode_cache, Iterator* current,
|
|
EndMark end) {
|
|
while (*current != end) {
|
|
if (!unicode_cache->IsWhiteSpaceOrLineTerminator(**current)) return true;
|
|
++*current;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
|
|
template <int radix_log_2, class Iterator, class EndMark>
|
|
double InternalStringToIntDouble(UnicodeCache* unicode_cache, Iterator current,
|
|
EndMark end, bool negative,
|
|
bool allow_trailing_junk) {
|
|
DCHECK(current != end);
|
|
|
|
// Skip leading 0s.
|
|
while (*current == '0') {
|
|
++current;
|
|
if (current == end) return SignedZero(negative);
|
|
}
|
|
|
|
int64_t number = 0;
|
|
int exponent = 0;
|
|
const int radix = (1 << radix_log_2);
|
|
|
|
int lim_0 = '0' + (radix < 10 ? radix : 10);
|
|
int lim_a = 'a' + (radix - 10);
|
|
int lim_A = 'A' + (radix - 10);
|
|
|
|
do {
|
|
int digit;
|
|
if (*current >= '0' && *current < lim_0) {
|
|
digit = static_cast<char>(*current) - '0';
|
|
} else if (*current >= 'a' && *current < lim_a) {
|
|
digit = static_cast<char>(*current) - 'a' + 10;
|
|
} else if (*current >= 'A' && *current < lim_A) {
|
|
digit = static_cast<char>(*current) - 'A' + 10;
|
|
} else {
|
|
if (allow_trailing_junk ||
|
|
!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
break;
|
|
} else {
|
|
return JunkStringValue();
|
|
}
|
|
}
|
|
|
|
number = number * radix + digit;
|
|
int overflow = static_cast<int>(number >> 53);
|
|
if (overflow != 0) {
|
|
// Overflow occurred. Need to determine which direction to round the
|
|
// result.
|
|
int overflow_bits_count = 1;
|
|
while (overflow > 1) {
|
|
overflow_bits_count++;
|
|
overflow >>= 1;
|
|
}
|
|
|
|
int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
|
|
int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
|
|
number >>= overflow_bits_count;
|
|
exponent = overflow_bits_count;
|
|
|
|
bool zero_tail = true;
|
|
while (true) {
|
|
++current;
|
|
if (current == end || !isDigit(*current, radix)) break;
|
|
zero_tail = zero_tail && *current == '0';
|
|
exponent += radix_log_2;
|
|
}
|
|
|
|
if (!allow_trailing_junk &&
|
|
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
return JunkStringValue();
|
|
}
|
|
|
|
int middle_value = (1 << (overflow_bits_count - 1));
|
|
if (dropped_bits > middle_value) {
|
|
number++; // Rounding up.
|
|
} else if (dropped_bits == middle_value) {
|
|
// Rounding to even to consistency with decimals: half-way case rounds
|
|
// up if significant part is odd and down otherwise.
|
|
if ((number & 1) != 0 || !zero_tail) {
|
|
number++; // Rounding up.
|
|
}
|
|
}
|
|
|
|
// Rounding up may cause overflow.
|
|
if ((number & (static_cast<int64_t>(1) << 53)) != 0) {
|
|
exponent++;
|
|
number >>= 1;
|
|
}
|
|
break;
|
|
}
|
|
++current;
|
|
} while (current != end);
|
|
|
|
DCHECK(number < ((int64_t)1 << 53));
|
|
DCHECK(static_cast<int64_t>(static_cast<double>(number)) == number);
|
|
|
|
if (exponent == 0) {
|
|
if (negative) {
|
|
if (number == 0) return -0.0;
|
|
number = -number;
|
|
}
|
|
return static_cast<double>(number);
|
|
}
|
|
|
|
DCHECK_NE(number, 0);
|
|
return std::ldexp(static_cast<double>(negative ? -number : number), exponent);
|
|
}
|
|
|
|
// ES6 18.2.5 parseInt(string, radix) (with NumberParseIntHelper subclass);
|
|
// and BigInt parsing cases from https://tc39.github.io/proposal-bigint/
|
|
// (with StringToBigIntHelper subclass).
|
|
class StringToIntHelper {
|
|
public:
|
|
StringToIntHelper(Isolate* isolate, Handle<String> subject, int radix)
|
|
: isolate_(isolate), subject_(subject), radix_(radix) {
|
|
DCHECK(subject->IsFlat());
|
|
}
|
|
|
|
// Used for the StringToBigInt operation.
|
|
StringToIntHelper(Isolate* isolate, Handle<String> subject)
|
|
: isolate_(isolate), subject_(subject) {
|
|
DCHECK(subject->IsFlat());
|
|
}
|
|
|
|
// Used for parsing BigInt literals, where the input is a Zone-allocated
|
|
// buffer of one-byte digits, along with an optional radix prefix.
|
|
StringToIntHelper(Isolate* isolate, const uint8_t* subject, int length)
|
|
: isolate_(isolate), raw_one_byte_subject_(subject), length_(length) {}
|
|
virtual ~StringToIntHelper() {}
|
|
|
|
protected:
|
|
// Subclasses must implement these:
|
|
virtual void AllocateResult() = 0;
|
|
virtual void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) = 0;
|
|
|
|
// Subclasses must call this to do all the work.
|
|
void ParseInt();
|
|
|
|
// Subclasses may override this.
|
|
virtual void HandleSpecialCases() {}
|
|
|
|
// Subclass constructors should call these for configuration before calling
|
|
// ParseInt().
|
|
void set_allow_binary_and_octal_prefixes() {
|
|
allow_binary_and_octal_prefixes_ = true;
|
|
}
|
|
void set_disallow_trailing_junk() { allow_trailing_junk_ = false; }
|
|
|
|
bool IsOneByte() const {
|
|
return raw_one_byte_subject_ != nullptr ||
|
|
subject_->IsOneByteRepresentationUnderneath();
|
|
}
|
|
|
|
Vector<const uint8_t> GetOneByteVector() {
|
|
if (raw_one_byte_subject_ != nullptr) {
|
|
return Vector<const uint8_t>(raw_one_byte_subject_, length_);
|
|
}
|
|
return subject_->GetFlatContent().ToOneByteVector();
|
|
}
|
|
|
|
Vector<const uc16> GetTwoByteVector() {
|
|
return subject_->GetFlatContent().ToUC16Vector();
|
|
}
|
|
|
|
// Subclasses get access to internal state:
|
|
enum State { kRunning, kError, kJunk, kEmpty, kZero, kDone };
|
|
|
|
enum class Sign { kNegative, kPositive, kNone };
|
|
|
|
Isolate* isolate() { return isolate_; }
|
|
int radix() { return radix_; }
|
|
int cursor() { return cursor_; }
|
|
int length() { return length_; }
|
|
bool negative() { return sign_ == Sign::kNegative; }
|
|
Sign sign() { return sign_; }
|
|
State state() { return state_; }
|
|
void set_state(State state) { state_ = state; }
|
|
|
|
private:
|
|
template <class Char>
|
|
void DetectRadixInternal(Char current, int length);
|
|
template <class Char>
|
|
void ParseInternal(Char start);
|
|
|
|
Isolate* isolate_;
|
|
Handle<String> subject_;
|
|
const uint8_t* raw_one_byte_subject_ = nullptr;
|
|
int radix_ = 0;
|
|
int cursor_ = 0;
|
|
int length_ = 0;
|
|
Sign sign_ = Sign::kNone;
|
|
bool leading_zero_ = false;
|
|
bool allow_binary_and_octal_prefixes_ = false;
|
|
bool allow_trailing_junk_ = true;
|
|
State state_ = kRunning;
|
|
};
|
|
|
|
void StringToIntHelper::ParseInt() {
|
|
{
|
|
DisallowHeapAllocation no_gc;
|
|
if (IsOneByte()) {
|
|
Vector<const uint8_t> vector = GetOneByteVector();
|
|
DetectRadixInternal(vector.start(), vector.length());
|
|
} else {
|
|
Vector<const uc16> vector = GetTwoByteVector();
|
|
DetectRadixInternal(vector.start(), vector.length());
|
|
}
|
|
}
|
|
if (state_ != kRunning) return;
|
|
AllocateResult();
|
|
HandleSpecialCases();
|
|
if (state_ != kRunning) return;
|
|
{
|
|
DisallowHeapAllocation no_gc;
|
|
if (IsOneByte()) {
|
|
Vector<const uint8_t> vector = GetOneByteVector();
|
|
DCHECK_EQ(length_, vector.length());
|
|
ParseInternal(vector.start());
|
|
} else {
|
|
Vector<const uc16> vector = GetTwoByteVector();
|
|
DCHECK_EQ(length_, vector.length());
|
|
ParseInternal(vector.start());
|
|
}
|
|
}
|
|
DCHECK_NE(state_, kRunning);
|
|
}
|
|
|
|
template <class Char>
|
|
void StringToIntHelper::DetectRadixInternal(Char current, int length) {
|
|
Char start = current;
|
|
length_ = length;
|
|
Char end = start + length;
|
|
UnicodeCache* unicode_cache = isolate_->unicode_cache();
|
|
|
|
if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
return set_state(kEmpty);
|
|
}
|
|
|
|
if (*current == '+') {
|
|
// Ignore leading sign; skip following spaces.
|
|
++current;
|
|
if (current == end) {
|
|
return set_state(kJunk);
|
|
}
|
|
sign_ = Sign::kPositive;
|
|
} else if (*current == '-') {
|
|
++current;
|
|
if (current == end) {
|
|
return set_state(kJunk);
|
|
}
|
|
sign_ = Sign::kNegative;
|
|
}
|
|
|
|
if (radix_ == 0) {
|
|
// Radix detection.
|
|
radix_ = 10;
|
|
if (*current == '0') {
|
|
++current;
|
|
if (current == end) return set_state(kZero);
|
|
if (*current == 'x' || *current == 'X') {
|
|
radix_ = 16;
|
|
++current;
|
|
if (current == end) return set_state(kJunk);
|
|
} else if (allow_binary_and_octal_prefixes_ &&
|
|
(*current == 'o' || *current == 'O')) {
|
|
radix_ = 8;
|
|
++current;
|
|
if (current == end) return set_state(kJunk);
|
|
} else if (allow_binary_and_octal_prefixes_ &&
|
|
(*current == 'b' || *current == 'B')) {
|
|
radix_ = 2;
|
|
++current;
|
|
if (current == end) return set_state(kJunk);
|
|
} else {
|
|
leading_zero_ = true;
|
|
}
|
|
}
|
|
} else if (radix_ == 16) {
|
|
if (*current == '0') {
|
|
// Allow "0x" prefix.
|
|
++current;
|
|
if (current == end) return set_state(kZero);
|
|
if (*current == 'x' || *current == 'X') {
|
|
++current;
|
|
if (current == end) return set_state(kJunk);
|
|
} else {
|
|
leading_zero_ = true;
|
|
}
|
|
}
|
|
}
|
|
// Skip leading zeros.
|
|
while (*current == '0') {
|
|
leading_zero_ = true;
|
|
++current;
|
|
if (current == end) return set_state(kZero);
|
|
}
|
|
|
|
if (!leading_zero_ && !isDigit(*current, radix_)) {
|
|
return set_state(kJunk);
|
|
}
|
|
|
|
DCHECK(radix_ >= 2 && radix_ <= 36);
|
|
STATIC_ASSERT(String::kMaxLength <= INT_MAX);
|
|
cursor_ = static_cast<int>(current - start);
|
|
}
|
|
|
|
template <class Char>
|
|
void StringToIntHelper::ParseInternal(Char start) {
|
|
Char current = start + cursor_;
|
|
Char end = start + length_;
|
|
|
|
// The following code causes accumulating rounding error for numbers greater
|
|
// than ~2^56. It's explicitly allowed in the spec: "if R is not 2, 4, 8, 10,
|
|
// 16, or 32, then mathInt may be an implementation-dependent approximation to
|
|
// the mathematical integer value" (15.1.2.2).
|
|
|
|
int lim_0 = '0' + (radix_ < 10 ? radix_ : 10);
|
|
int lim_a = 'a' + (radix_ - 10);
|
|
int lim_A = 'A' + (radix_ - 10);
|
|
|
|
// NOTE: The code for computing the value may seem a bit complex at
|
|
// first glance. It is structured to use 32-bit multiply-and-add
|
|
// loops as long as possible to avoid losing precision.
|
|
|
|
bool done = false;
|
|
do {
|
|
// Parse the longest part of the string starting at {current}
|
|
// possible while keeping the multiplier, and thus the part
|
|
// itself, within 32 bits.
|
|
uint32_t part = 0, multiplier = 1;
|
|
while (true) {
|
|
uint32_t d;
|
|
if (*current >= '0' && *current < lim_0) {
|
|
d = *current - '0';
|
|
} else if (*current >= 'a' && *current < lim_a) {
|
|
d = *current - 'a' + 10;
|
|
} else if (*current >= 'A' && *current < lim_A) {
|
|
d = *current - 'A' + 10;
|
|
} else {
|
|
done = true;
|
|
break;
|
|
}
|
|
|
|
// Update the value of the part as long as the multiplier fits
|
|
// in 32 bits. When we can't guarantee that the next iteration
|
|
// will not overflow the multiplier, we stop parsing the part
|
|
// by leaving the loop.
|
|
const uint32_t kMaximumMultiplier = 0xFFFFFFFFU / 36;
|
|
uint32_t m = multiplier * static_cast<uint32_t>(radix_);
|
|
if (m > kMaximumMultiplier) break;
|
|
part = part * radix_ + d;
|
|
multiplier = m;
|
|
DCHECK(multiplier > part);
|
|
|
|
++current;
|
|
if (current == end) {
|
|
done = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update the value and skip the part in the string.
|
|
ResultMultiplyAdd(multiplier, part);
|
|
} while (!done);
|
|
|
|
if (!allow_trailing_junk_ &&
|
|
AdvanceToNonspace(isolate_->unicode_cache(), ¤t, end)) {
|
|
return set_state(kJunk);
|
|
}
|
|
|
|
return set_state(kDone);
|
|
}
|
|
|
|
class NumberParseIntHelper : public StringToIntHelper {
|
|
public:
|
|
NumberParseIntHelper(Isolate* isolate, Handle<String> string, int radix)
|
|
: StringToIntHelper(isolate, string, radix) {}
|
|
|
|
double GetResult() {
|
|
ParseInt();
|
|
switch (state()) {
|
|
case kJunk:
|
|
case kEmpty:
|
|
return JunkStringValue();
|
|
case kZero:
|
|
return SignedZero(negative());
|
|
case kDone:
|
|
return negative() ? -result_ : result_;
|
|
case kError:
|
|
case kRunning:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
protected:
|
|
virtual void AllocateResult() {}
|
|
virtual void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) {
|
|
result_ = result_ * multiplier + part;
|
|
}
|
|
|
|
private:
|
|
virtual void HandleSpecialCases() {
|
|
bool is_power_of_two = base::bits::IsPowerOfTwo(radix());
|
|
if (!is_power_of_two && radix() != 10) return;
|
|
DisallowHeapAllocation no_gc;
|
|
if (IsOneByte()) {
|
|
Vector<const uint8_t> vector = GetOneByteVector();
|
|
DCHECK_EQ(length(), vector.length());
|
|
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start())
|
|
: HandleBaseTenCase(vector.start());
|
|
} else {
|
|
Vector<const uc16> vector = GetTwoByteVector();
|
|
DCHECK_EQ(length(), vector.length());
|
|
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start())
|
|
: HandleBaseTenCase(vector.start());
|
|
}
|
|
set_state(kDone);
|
|
}
|
|
|
|
template <class Char>
|
|
double HandlePowerOfTwoCase(Char start) {
|
|
Char current = start + cursor();
|
|
Char end = start + length();
|
|
UnicodeCache* unicode_cache = isolate()->unicode_cache();
|
|
const bool allow_trailing_junk = true;
|
|
// GetResult() will take care of the sign bit, so ignore it for now.
|
|
const bool negative = false;
|
|
switch (radix()) {
|
|
case 2:
|
|
return InternalStringToIntDouble<1>(unicode_cache, current, end,
|
|
negative, allow_trailing_junk);
|
|
case 4:
|
|
return InternalStringToIntDouble<2>(unicode_cache, current, end,
|
|
negative, allow_trailing_junk);
|
|
case 8:
|
|
return InternalStringToIntDouble<3>(unicode_cache, current, end,
|
|
negative, allow_trailing_junk);
|
|
|
|
case 16:
|
|
return InternalStringToIntDouble<4>(unicode_cache, current, end,
|
|
negative, allow_trailing_junk);
|
|
|
|
case 32:
|
|
return InternalStringToIntDouble<5>(unicode_cache, current, end,
|
|
negative, allow_trailing_junk);
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
template <class Char>
|
|
double HandleBaseTenCase(Char start) {
|
|
// Parsing with strtod.
|
|
Char current = start + cursor();
|
|
Char end = start + length();
|
|
const int kMaxSignificantDigits = 309; // Doubles are less than 1.8e308.
|
|
// The buffer may contain up to kMaxSignificantDigits + 1 digits and a zero
|
|
// end.
|
|
const int kBufferSize = kMaxSignificantDigits + 2;
|
|
char buffer[kBufferSize];
|
|
int buffer_pos = 0;
|
|
while (*current >= '0' && *current <= '9') {
|
|
if (buffer_pos <= kMaxSignificantDigits) {
|
|
// If the number has more than kMaxSignificantDigits it will be parsed
|
|
// as infinity.
|
|
DCHECK_LT(buffer_pos, kBufferSize);
|
|
buffer[buffer_pos++] = static_cast<char>(*current);
|
|
}
|
|
++current;
|
|
if (current == end) break;
|
|
}
|
|
|
|
SLOW_DCHECK(buffer_pos < kBufferSize);
|
|
buffer[buffer_pos] = '\0';
|
|
Vector<const char> buffer_vector(buffer, buffer_pos);
|
|
return Strtod(buffer_vector, 0);
|
|
}
|
|
|
|
double result_ = 0;
|
|
};
|
|
|
|
// Converts a string to a double value. Assumes the Iterator supports
|
|
// the following operations:
|
|
// 1. current == end (other ops are not allowed), current != end.
|
|
// 2. *current - gets the current character in the sequence.
|
|
// 3. ++current (advances the position).
|
|
template <class Iterator, class EndMark>
|
|
double InternalStringToDouble(UnicodeCache* unicode_cache, Iterator current,
|
|
EndMark end, int flags, double empty_string_val) {
|
|
// To make sure that iterator dereferencing is valid the following
|
|
// convention is used:
|
|
// 1. Each '++current' statement is followed by check for equality to 'end'.
|
|
// 2. If AdvanceToNonspace returned false then current == end.
|
|
// 3. If 'current' becomes be equal to 'end' the function returns or goes to
|
|
// 'parsing_done'.
|
|
// 4. 'current' is not dereferenced after the 'parsing_done' label.
|
|
// 5. Code before 'parsing_done' may rely on 'current != end'.
|
|
if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
return empty_string_val;
|
|
}
|
|
|
|
const bool allow_trailing_junk = (flags & ALLOW_TRAILING_JUNK) != 0;
|
|
|
|
// Maximum number of significant digits in decimal representation.
|
|
// The longest possible double in decimal representation is
|
|
// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
|
|
// (768 digits). If we parse a number whose first digits are equal to a
|
|
// mean of 2 adjacent doubles (that could have up to 769 digits) the result
|
|
// must be rounded to the bigger one unless the tail consists of zeros, so
|
|
// we don't need to preserve all the digits.
|
|
const int kMaxSignificantDigits = 772;
|
|
|
|
// The longest form of simplified number is: "-<significant digits>'.1eXXX\0".
|
|
const int kBufferSize = kMaxSignificantDigits + 10;
|
|
char buffer[kBufferSize]; // NOLINT: size is known at compile time.
|
|
int buffer_pos = 0;
|
|
|
|
// Exponent will be adjusted if insignificant digits of the integer part
|
|
// or insignificant leading zeros of the fractional part are dropped.
|
|
int exponent = 0;
|
|
int significant_digits = 0;
|
|
int insignificant_digits = 0;
|
|
bool nonzero_digit_dropped = false;
|
|
|
|
enum Sign { NONE, NEGATIVE, POSITIVE };
|
|
|
|
Sign sign = NONE;
|
|
|
|
if (*current == '+') {
|
|
// Ignore leading sign.
|
|
++current;
|
|
if (current == end) return JunkStringValue();
|
|
sign = POSITIVE;
|
|
} else if (*current == '-') {
|
|
++current;
|
|
if (current == end) return JunkStringValue();
|
|
sign = NEGATIVE;
|
|
}
|
|
|
|
static const char kInfinityString[] = "Infinity";
|
|
if (*current == kInfinityString[0]) {
|
|
if (!SubStringEquals(¤t, end, kInfinityString)) {
|
|
return JunkStringValue();
|
|
}
|
|
|
|
if (!allow_trailing_junk &&
|
|
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
return JunkStringValue();
|
|
}
|
|
|
|
DCHECK_EQ(buffer_pos, 0);
|
|
return (sign == NEGATIVE) ? -V8_INFINITY : V8_INFINITY;
|
|
}
|
|
|
|
bool leading_zero = false;
|
|
if (*current == '0') {
|
|
++current;
|
|
if (current == end) return SignedZero(sign == NEGATIVE);
|
|
|
|
leading_zero = true;
|
|
|
|
// It could be hexadecimal value.
|
|
if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
|
|
++current;
|
|
if (current == end || !isDigit(*current, 16) || sign != NONE) {
|
|
return JunkStringValue(); // "0x".
|
|
}
|
|
|
|
return InternalStringToIntDouble<4>(unicode_cache, current, end, false,
|
|
allow_trailing_junk);
|
|
|
|
// It could be an explicit octal value.
|
|
} else if ((flags & ALLOW_OCTAL) && (*current == 'o' || *current == 'O')) {
|
|
++current;
|
|
if (current == end || !isDigit(*current, 8) || sign != NONE) {
|
|
return JunkStringValue(); // "0o".
|
|
}
|
|
|
|
return InternalStringToIntDouble<3>(unicode_cache, current, end, false,
|
|
allow_trailing_junk);
|
|
|
|
// It could be a binary value.
|
|
} else if ((flags & ALLOW_BINARY) && (*current == 'b' || *current == 'B')) {
|
|
++current;
|
|
if (current == end || !isBinaryDigit(*current) || sign != NONE) {
|
|
return JunkStringValue(); // "0b".
|
|
}
|
|
|
|
return InternalStringToIntDouble<1>(unicode_cache, current, end, false,
|
|
allow_trailing_junk);
|
|
}
|
|
|
|
// Ignore leading zeros in the integer part.
|
|
while (*current == '0') {
|
|
++current;
|
|
if (current == end) return SignedZero(sign == NEGATIVE);
|
|
}
|
|
}
|
|
|
|
bool octal = leading_zero && (flags & ALLOW_IMPLICIT_OCTAL) != 0;
|
|
|
|
// Copy significant digits of the integer part (if any) to the buffer.
|
|
while (*current >= '0' && *current <= '9') {
|
|
if (significant_digits < kMaxSignificantDigits) {
|
|
DCHECK_LT(buffer_pos, kBufferSize);
|
|
buffer[buffer_pos++] = static_cast<char>(*current);
|
|
significant_digits++;
|
|
// Will later check if it's an octal in the buffer.
|
|
} else {
|
|
insignificant_digits++; // Move the digit into the exponential part.
|
|
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
|
|
}
|
|
octal = octal && *current < '8';
|
|
++current;
|
|
if (current == end) goto parsing_done;
|
|
}
|
|
|
|
if (significant_digits == 0) {
|
|
octal = false;
|
|
}
|
|
|
|
if (*current == '.') {
|
|
if (octal && !allow_trailing_junk) return JunkStringValue();
|
|
if (octal) goto parsing_done;
|
|
|
|
++current;
|
|
if (current == end) {
|
|
if (significant_digits == 0 && !leading_zero) {
|
|
return JunkStringValue();
|
|
} else {
|
|
goto parsing_done;
|
|
}
|
|
}
|
|
|
|
if (significant_digits == 0) {
|
|
// octal = false;
|
|
// Integer part consists of 0 or is absent. Significant digits start after
|
|
// leading zeros (if any).
|
|
while (*current == '0') {
|
|
++current;
|
|
if (current == end) return SignedZero(sign == NEGATIVE);
|
|
exponent--; // Move this 0 into the exponent.
|
|
}
|
|
}
|
|
|
|
// There is a fractional part. We don't emit a '.', but adjust the exponent
|
|
// instead.
|
|
while (*current >= '0' && *current <= '9') {
|
|
if (significant_digits < kMaxSignificantDigits) {
|
|
DCHECK_LT(buffer_pos, kBufferSize);
|
|
buffer[buffer_pos++] = static_cast<char>(*current);
|
|
significant_digits++;
|
|
exponent--;
|
|
} else {
|
|
// Ignore insignificant digits in the fractional part.
|
|
nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
|
|
}
|
|
++current;
|
|
if (current == end) goto parsing_done;
|
|
}
|
|
}
|
|
|
|
if (!leading_zero && exponent == 0 && significant_digits == 0) {
|
|
// If leading_zeros is true then the string contains zeros.
|
|
// If exponent < 0 then string was [+-]\.0*...
|
|
// If significant_digits != 0 the string is not equal to 0.
|
|
// Otherwise there are no digits in the string.
|
|
return JunkStringValue();
|
|
}
|
|
|
|
// Parse exponential part.
|
|
if (*current == 'e' || *current == 'E') {
|
|
if (octal) return JunkStringValue();
|
|
++current;
|
|
if (current == end) {
|
|
if (allow_trailing_junk) {
|
|
goto parsing_done;
|
|
} else {
|
|
return JunkStringValue();
|
|
}
|
|
}
|
|
char sign = '+';
|
|
if (*current == '+' || *current == '-') {
|
|
sign = static_cast<char>(*current);
|
|
++current;
|
|
if (current == end) {
|
|
if (allow_trailing_junk) {
|
|
goto parsing_done;
|
|
} else {
|
|
return JunkStringValue();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (current == end || *current < '0' || *current > '9') {
|
|
if (allow_trailing_junk) {
|
|
goto parsing_done;
|
|
} else {
|
|
return JunkStringValue();
|
|
}
|
|
}
|
|
|
|
const int max_exponent = INT_MAX / 2;
|
|
DCHECK(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
|
|
int num = 0;
|
|
do {
|
|
// Check overflow.
|
|
int digit = *current - '0';
|
|
if (num >= max_exponent / 10 &&
|
|
!(num == max_exponent / 10 && digit <= max_exponent % 10)) {
|
|
num = max_exponent;
|
|
} else {
|
|
num = num * 10 + digit;
|
|
}
|
|
++current;
|
|
} while (current != end && *current >= '0' && *current <= '9');
|
|
|
|
exponent += (sign == '-' ? -num : num);
|
|
}
|
|
|
|
if (!allow_trailing_junk && AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
|
return JunkStringValue();
|
|
}
|
|
|
|
parsing_done:
|
|
exponent += insignificant_digits;
|
|
|
|
if (octal) {
|
|
return InternalStringToIntDouble<3>(unicode_cache, buffer,
|
|
buffer + buffer_pos, sign == NEGATIVE,
|
|
allow_trailing_junk);
|
|
}
|
|
|
|
if (nonzero_digit_dropped) {
|
|
buffer[buffer_pos++] = '1';
|
|
exponent--;
|
|
}
|
|
|
|
SLOW_DCHECK(buffer_pos < kBufferSize);
|
|
buffer[buffer_pos] = '\0';
|
|
|
|
double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
|
|
return (sign == NEGATIVE) ? -converted : converted;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
double StringToDouble(UnicodeCache* unicode_cache,
|
|
const char* str, int flags, double empty_string_val) {
|
|
// We cast to const uint8_t* here to avoid instantiating the
|
|
// InternalStringToDouble() template for const char* as well.
|
|
const uint8_t* start = reinterpret_cast<const uint8_t*>(str);
|
|
const uint8_t* end = start + StrLength(str);
|
|
return InternalStringToDouble(unicode_cache, start, end, flags,
|
|
empty_string_val);
|
|
}
|
|
|
|
|
|
double StringToDouble(UnicodeCache* unicode_cache,
|
|
Vector<const uint8_t> str,
|
|
int flags,
|
|
double empty_string_val) {
|
|
// We cast to const uint8_t* here to avoid instantiating the
|
|
// InternalStringToDouble() template for const char* as well.
|
|
const uint8_t* start = reinterpret_cast<const uint8_t*>(str.start());
|
|
const uint8_t* end = start + str.length();
|
|
return InternalStringToDouble(unicode_cache, start, end, flags,
|
|
empty_string_val);
|
|
}
|
|
|
|
|
|
double StringToDouble(UnicodeCache* unicode_cache,
|
|
Vector<const uc16> str,
|
|
int flags,
|
|
double empty_string_val) {
|
|
const uc16* end = str.start() + str.length();
|
|
return InternalStringToDouble(unicode_cache, str.start(), end, flags,
|
|
empty_string_val);
|
|
}
|
|
|
|
double StringToInt(Isolate* isolate, Handle<String> string, int radix) {
|
|
NumberParseIntHelper helper(isolate, string, radix);
|
|
return helper.GetResult();
|
|
}
|
|
|
|
class StringToBigIntHelper : public StringToIntHelper {
|
|
public:
|
|
enum class Behavior { kStringToBigInt, kLiteral };
|
|
|
|
// Used for StringToBigInt operation (BigInt constructor and == operator).
|
|
StringToBigIntHelper(Isolate* isolate, Handle<String> string)
|
|
: StringToIntHelper(isolate, string),
|
|
behavior_(Behavior::kStringToBigInt) {
|
|
set_allow_binary_and_octal_prefixes();
|
|
set_disallow_trailing_junk();
|
|
}
|
|
|
|
// Used for parsing BigInt literals, where the input is a buffer of
|
|
// one-byte ASCII digits, along with an optional radix prefix.
|
|
StringToBigIntHelper(Isolate* isolate, const uint8_t* string, int length)
|
|
: StringToIntHelper(isolate, string, length),
|
|
behavior_(Behavior::kLiteral) {
|
|
set_allow_binary_and_octal_prefixes();
|
|
}
|
|
|
|
MaybeHandle<BigInt> GetResult() {
|
|
ParseInt();
|
|
if (behavior_ == Behavior::kStringToBigInt && sign() != Sign::kNone &&
|
|
radix() != 10) {
|
|
return MaybeHandle<BigInt>();
|
|
}
|
|
if (state() == kEmpty) {
|
|
if (behavior_ == Behavior::kStringToBigInt) {
|
|
set_state(kZero);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
switch (state()) {
|
|
case kJunk:
|
|
if (should_throw() == kThrowOnError) {
|
|
THROW_NEW_ERROR(isolate(),
|
|
NewSyntaxError(MessageTemplate::kBigIntInvalidString),
|
|
BigInt);
|
|
} else {
|
|
DCHECK_EQ(should_throw(), kDontThrow);
|
|
return MaybeHandle<BigInt>();
|
|
}
|
|
case kZero:
|
|
return BigInt::Zero(isolate());
|
|
case kError:
|
|
DCHECK_EQ(should_throw() == kThrowOnError,
|
|
isolate()->has_pending_exception());
|
|
return MaybeHandle<BigInt>();
|
|
case kDone:
|
|
return BigInt::Finalize(result_, negative());
|
|
case kEmpty:
|
|
case kRunning:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
protected:
|
|
virtual void AllocateResult() {
|
|
// We have to allocate a BigInt that's big enough to fit the result.
|
|
// Conseratively assume that all remaining digits are significant.
|
|
// Optimization opportunity: Would it makes sense to scan for trailing
|
|
// junk before allocating the result?
|
|
int charcount = length() - cursor();
|
|
// For literals, we pretenure the allocated BigInt, since it's about
|
|
// to be stored in the interpreter's constants array.
|
|
PretenureFlag pretenure =
|
|
behavior_ == Behavior::kLiteral ? TENURED : NOT_TENURED;
|
|
MaybeHandle<FreshlyAllocatedBigInt> maybe = BigInt::AllocateFor(
|
|
isolate(), radix(), charcount, should_throw(), pretenure);
|
|
if (!maybe.ToHandle(&result_)) {
|
|
set_state(kError);
|
|
}
|
|
}
|
|
|
|
virtual void ResultMultiplyAdd(uint32_t multiplier, uint32_t part) {
|
|
BigInt::InplaceMultiplyAdd(result_, static_cast<uintptr_t>(multiplier),
|
|
static_cast<uintptr_t>(part));
|
|
}
|
|
|
|
private:
|
|
ShouldThrow should_throw() const { return kDontThrow; }
|
|
|
|
Handle<FreshlyAllocatedBigInt> result_;
|
|
Behavior behavior_;
|
|
};
|
|
|
|
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string) {
|
|
string = String::Flatten(string);
|
|
StringToBigIntHelper helper(isolate, string);
|
|
return helper.GetResult();
|
|
}
|
|
|
|
MaybeHandle<BigInt> BigIntLiteral(Isolate* isolate, const char* string) {
|
|
StringToBigIntHelper helper(isolate, reinterpret_cast<const uint8_t*>(string),
|
|
static_cast<int>(strlen(string)));
|
|
return helper.GetResult();
|
|
}
|
|
|
|
const char* DoubleToCString(double v, Vector<char> buffer) {
|
|
switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
|
|
case FP_NAN: return "NaN";
|
|
case FP_INFINITE: return (v < 0.0 ? "-Infinity" : "Infinity");
|
|
case FP_ZERO: return "0";
|
|
default: {
|
|
SimpleStringBuilder builder(buffer.start(), buffer.length());
|
|
int decimal_point;
|
|
int sign;
|
|
const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1;
|
|
char decimal_rep[kV8DtoaBufferCapacity];
|
|
int length;
|
|
|
|
DoubleToAscii(v, DTOA_SHORTEST, 0,
|
|
Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
|
|
&sign, &length, &decimal_point);
|
|
|
|
if (sign) builder.AddCharacter('-');
|
|
|
|
if (length <= decimal_point && decimal_point <= 21) {
|
|
// ECMA-262 section 9.8.1 step 6.
|
|
builder.AddString(decimal_rep);
|
|
builder.AddPadding('0', decimal_point - length);
|
|
|
|
} else if (0 < decimal_point && decimal_point <= 21) {
|
|
// ECMA-262 section 9.8.1 step 7.
|
|
builder.AddSubstring(decimal_rep, decimal_point);
|
|
builder.AddCharacter('.');
|
|
builder.AddString(decimal_rep + decimal_point);
|
|
|
|
} else if (decimal_point <= 0 && decimal_point > -6) {
|
|
// ECMA-262 section 9.8.1 step 8.
|
|
builder.AddString("0.");
|
|
builder.AddPadding('0', -decimal_point);
|
|
builder.AddString(decimal_rep);
|
|
|
|
} else {
|
|
// ECMA-262 section 9.8.1 step 9 and 10 combined.
|
|
builder.AddCharacter(decimal_rep[0]);
|
|
if (length != 1) {
|
|
builder.AddCharacter('.');
|
|
builder.AddString(decimal_rep + 1);
|
|
}
|
|
builder.AddCharacter('e');
|
|
builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
|
|
int exponent = decimal_point - 1;
|
|
if (exponent < 0) exponent = -exponent;
|
|
builder.AddDecimalInteger(exponent);
|
|
}
|
|
return builder.Finalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const char* IntToCString(int n, Vector<char> buffer) {
|
|
bool negative = false;
|
|
if (n < 0) {
|
|
// We must not negate the most negative int.
|
|
if (n == kMinInt) return DoubleToCString(n, buffer);
|
|
negative = true;
|
|
n = -n;
|
|
}
|
|
// Build the string backwards from the least significant digit.
|
|
int i = buffer.length();
|
|
buffer[--i] = '\0';
|
|
do {
|
|
buffer[--i] = '0' + (n % 10);
|
|
n /= 10;
|
|
} while (n);
|
|
if (negative) buffer[--i] = '-';
|
|
return buffer.start() + i;
|
|
}
|
|
|
|
|
|
char* DoubleToFixedCString(double value, int f) {
|
|
const int kMaxDigitsBeforePoint = 21;
|
|
const double kFirstNonFixed = 1e21;
|
|
DCHECK_GE(f, 0);
|
|
DCHECK_LE(f, kMaxFractionDigits);
|
|
|
|
bool negative = false;
|
|
double abs_value = value;
|
|
if (value < 0) {
|
|
abs_value = -value;
|
|
negative = true;
|
|
}
|
|
|
|
// If abs_value has more than kMaxDigitsBeforePoint digits before the point
|
|
// use the non-fixed conversion routine.
|
|
if (abs_value >= kFirstNonFixed) {
|
|
char arr[kMaxFractionDigits];
|
|
Vector<char> buffer(arr, arraysize(arr));
|
|
return StrDup(DoubleToCString(value, buffer));
|
|
}
|
|
|
|
// Find a sufficiently precise decimal representation of n.
|
|
int decimal_point;
|
|
int sign;
|
|
// Add space for the '\0' byte.
|
|
const int kDecimalRepCapacity =
|
|
kMaxDigitsBeforePoint + kMaxFractionDigits + 1;
|
|
char decimal_rep[kDecimalRepCapacity];
|
|
int decimal_rep_length;
|
|
DoubleToAscii(value, DTOA_FIXED, f,
|
|
Vector<char>(decimal_rep, kDecimalRepCapacity),
|
|
&sign, &decimal_rep_length, &decimal_point);
|
|
|
|
// Create a representation that is padded with zeros if needed.
|
|
int zero_prefix_length = 0;
|
|
int zero_postfix_length = 0;
|
|
|
|
if (decimal_point <= 0) {
|
|
zero_prefix_length = -decimal_point + 1;
|
|
decimal_point = 1;
|
|
}
|
|
|
|
if (zero_prefix_length + decimal_rep_length < decimal_point + f) {
|
|
zero_postfix_length = decimal_point + f - decimal_rep_length -
|
|
zero_prefix_length;
|
|
}
|
|
|
|
unsigned rep_length =
|
|
zero_prefix_length + decimal_rep_length + zero_postfix_length;
|
|
SimpleStringBuilder rep_builder(rep_length + 1);
|
|
rep_builder.AddPadding('0', zero_prefix_length);
|
|
rep_builder.AddString(decimal_rep);
|
|
rep_builder.AddPadding('0', zero_postfix_length);
|
|
char* rep = rep_builder.Finalize();
|
|
|
|
// Create the result string by appending a minus and putting in a
|
|
// decimal point if needed.
|
|
unsigned result_size = decimal_point + f + 2;
|
|
SimpleStringBuilder builder(result_size + 1);
|
|
if (negative) builder.AddCharacter('-');
|
|
builder.AddSubstring(rep, decimal_point);
|
|
if (f > 0) {
|
|
builder.AddCharacter('.');
|
|
builder.AddSubstring(rep + decimal_point, f);
|
|
}
|
|
DeleteArray(rep);
|
|
return builder.Finalize();
|
|
}
|
|
|
|
|
|
static char* CreateExponentialRepresentation(char* decimal_rep,
|
|
int exponent,
|
|
bool negative,
|
|
int significant_digits) {
|
|
bool negative_exponent = false;
|
|
if (exponent < 0) {
|
|
negative_exponent = true;
|
|
exponent = -exponent;
|
|
}
|
|
|
|
// Leave room in the result for appending a minus, for a period, the
|
|
// letter 'e', a minus or a plus depending on the exponent, and a
|
|
// three digit exponent.
|
|
unsigned result_size = significant_digits + 7;
|
|
SimpleStringBuilder builder(result_size + 1);
|
|
|
|
if (negative) builder.AddCharacter('-');
|
|
builder.AddCharacter(decimal_rep[0]);
|
|
if (significant_digits != 1) {
|
|
builder.AddCharacter('.');
|
|
builder.AddString(decimal_rep + 1);
|
|
int rep_length = StrLength(decimal_rep);
|
|
builder.AddPadding('0', significant_digits - rep_length);
|
|
}
|
|
|
|
builder.AddCharacter('e');
|
|
builder.AddCharacter(negative_exponent ? '-' : '+');
|
|
builder.AddDecimalInteger(exponent);
|
|
return builder.Finalize();
|
|
}
|
|
|
|
|
|
char* DoubleToExponentialCString(double value, int f) {
|
|
// f might be -1 to signal that f was undefined in JavaScript.
|
|
DCHECK(f >= -1 && f <= kMaxFractionDigits);
|
|
|
|
bool negative = false;
|
|
if (value < 0) {
|
|
value = -value;
|
|
negative = true;
|
|
}
|
|
|
|
// Find a sufficiently precise decimal representation of n.
|
|
int decimal_point;
|
|
int sign;
|
|
// f corresponds to the digits after the point. There is always one digit
|
|
// before the point. The number of requested_digits equals hence f + 1.
|
|
// And we have to add one character for the null-terminator.
|
|
const int kV8DtoaBufferCapacity = kMaxFractionDigits + 1 + 1;
|
|
// Make sure that the buffer is big enough, even if we fall back to the
|
|
// shortest representation (which happens when f equals -1).
|
|
DCHECK_LE(kBase10MaximalLength, kMaxFractionDigits + 1);
|
|
char decimal_rep[kV8DtoaBufferCapacity];
|
|
int decimal_rep_length;
|
|
|
|
if (f == -1) {
|
|
DoubleToAscii(value, DTOA_SHORTEST, 0,
|
|
Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
|
|
&sign, &decimal_rep_length, &decimal_point);
|
|
f = decimal_rep_length - 1;
|
|
} else {
|
|
DoubleToAscii(value, DTOA_PRECISION, f + 1,
|
|
Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
|
|
&sign, &decimal_rep_length, &decimal_point);
|
|
}
|
|
DCHECK_GT(decimal_rep_length, 0);
|
|
DCHECK(decimal_rep_length <= f + 1);
|
|
|
|
int exponent = decimal_point - 1;
|
|
char* result =
|
|
CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
char* DoubleToPrecisionCString(double value, int p) {
|
|
const int kMinimalDigits = 1;
|
|
DCHECK(p >= kMinimalDigits && p <= kMaxFractionDigits);
|
|
USE(kMinimalDigits);
|
|
|
|
bool negative = false;
|
|
if (value < 0) {
|
|
value = -value;
|
|
negative = true;
|
|
}
|
|
|
|
// Find a sufficiently precise decimal representation of n.
|
|
int decimal_point;
|
|
int sign;
|
|
// Add one for the terminating null character.
|
|
const int kV8DtoaBufferCapacity = kMaxFractionDigits + 1;
|
|
char decimal_rep[kV8DtoaBufferCapacity];
|
|
int decimal_rep_length;
|
|
|
|
DoubleToAscii(value, DTOA_PRECISION, p,
|
|
Vector<char>(decimal_rep, kV8DtoaBufferCapacity),
|
|
&sign, &decimal_rep_length, &decimal_point);
|
|
DCHECK(decimal_rep_length <= p);
|
|
|
|
int exponent = decimal_point - 1;
|
|
|
|
char* result = nullptr;
|
|
|
|
if (exponent < -6 || exponent >= p) {
|
|
result =
|
|
CreateExponentialRepresentation(decimal_rep, exponent, negative, p);
|
|
} else {
|
|
// Use fixed notation.
|
|
//
|
|
// Leave room in the result for appending a minus, a period and in
|
|
// the case where decimal_point is not positive for a zero in
|
|
// front of the period.
|
|
unsigned result_size = (decimal_point <= 0)
|
|
? -decimal_point + p + 3
|
|
: p + 2;
|
|
SimpleStringBuilder builder(result_size + 1);
|
|
if (negative) builder.AddCharacter('-');
|
|
if (decimal_point <= 0) {
|
|
builder.AddString("0.");
|
|
builder.AddPadding('0', -decimal_point);
|
|
builder.AddString(decimal_rep);
|
|
builder.AddPadding('0', p - decimal_rep_length);
|
|
} else {
|
|
const int m = Min(decimal_rep_length, decimal_point);
|
|
builder.AddSubstring(decimal_rep, m);
|
|
builder.AddPadding('0', decimal_point - decimal_rep_length);
|
|
if (decimal_point < p) {
|
|
builder.AddCharacter('.');
|
|
const int extra = negative ? 2 : 1;
|
|
if (decimal_rep_length > decimal_point) {
|
|
const int len = StrLength(decimal_rep + decimal_point);
|
|
const int n = Min(len, p - (builder.position() - extra));
|
|
builder.AddSubstring(decimal_rep + decimal_point, n);
|
|
}
|
|
builder.AddPadding('0', extra + (p - builder.position()));
|
|
}
|
|
}
|
|
result = builder.Finalize();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
char* DoubleToRadixCString(double value, int radix) {
|
|
DCHECK(radix >= 2 && radix <= 36);
|
|
DCHECK(std::isfinite(value));
|
|
DCHECK_NE(0.0, value);
|
|
// Character array used for conversion.
|
|
static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
// Temporary buffer for the result. We start with the decimal point in the
|
|
// middle and write to the left for the integer part and to the right for the
|
|
// fractional part. 1024 characters for the exponent and 52 for the mantissa
|
|
// either way, with additional space for sign, decimal point and string
|
|
// termination should be sufficient.
|
|
static const int kBufferSize = 2200;
|
|
char buffer[kBufferSize];
|
|
int integer_cursor = kBufferSize / 2;
|
|
int fraction_cursor = integer_cursor;
|
|
|
|
bool negative = value < 0;
|
|
if (negative) value = -value;
|
|
|
|
// Split the value into an integer part and a fractional part.
|
|
double integer = std::floor(value);
|
|
double fraction = value - integer;
|
|
// We only compute fractional digits up to the input double's precision.
|
|
double delta = 0.5 * (Double(value).NextDouble() - value);
|
|
delta = std::max(Double(0.0).NextDouble(), delta);
|
|
DCHECK_GT(delta, 0.0);
|
|
if (fraction > delta) {
|
|
// Insert decimal point.
|
|
buffer[fraction_cursor++] = '.';
|
|
do {
|
|
// Shift up by one digit.
|
|
fraction *= radix;
|
|
delta *= radix;
|
|
// Write digit.
|
|
int digit = static_cast<int>(fraction);
|
|
buffer[fraction_cursor++] = chars[digit];
|
|
// Calculate remainder.
|
|
fraction -= digit;
|
|
// Round to even.
|
|
if (fraction > 0.5 || (fraction == 0.5 && (digit & 1))) {
|
|
if (fraction + delta > 1) {
|
|
// We need to back trace already written digits in case of carry-over.
|
|
while (true) {
|
|
fraction_cursor--;
|
|
if (fraction_cursor == kBufferSize / 2) {
|
|
CHECK_EQ('.', buffer[fraction_cursor]);
|
|
// Carry over to the integer part.
|
|
integer += 1;
|
|
break;
|
|
}
|
|
char c = buffer[fraction_cursor];
|
|
// Reconstruct digit.
|
|
int digit = c > '9' ? (c - 'a' + 10) : (c - '0');
|
|
if (digit + 1 < radix) {
|
|
buffer[fraction_cursor++] = chars[digit + 1];
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} while (fraction > delta);
|
|
}
|
|
|
|
// Compute integer digits. Fill unrepresented digits with zero.
|
|
while (Double(integer / radix).Exponent() > 0) {
|
|
integer /= radix;
|
|
buffer[--integer_cursor] = '0';
|
|
}
|
|
do {
|
|
double remainder = Modulo(integer, radix);
|
|
buffer[--integer_cursor] = chars[static_cast<int>(remainder)];
|
|
integer = (integer - remainder) / radix;
|
|
} while (integer > 0);
|
|
|
|
// Add sign and terminate string.
|
|
if (negative) buffer[--integer_cursor] = '-';
|
|
buffer[fraction_cursor++] = '\0';
|
|
DCHECK_LT(fraction_cursor, kBufferSize);
|
|
DCHECK_LE(0, integer_cursor);
|
|
// Allocate new string as return value.
|
|
char* result = NewArray<char>(fraction_cursor - integer_cursor);
|
|
memcpy(result, buffer + integer_cursor, fraction_cursor - integer_cursor);
|
|
return result;
|
|
}
|
|
|
|
|
|
// ES6 18.2.4 parseFloat(string)
|
|
double StringToDouble(UnicodeCache* unicode_cache, Handle<String> string,
|
|
int flags, double empty_string_val) {
|
|
Handle<String> flattened = String::Flatten(string);
|
|
{
|
|
DisallowHeapAllocation no_gc;
|
|
String::FlatContent flat = flattened->GetFlatContent();
|
|
DCHECK(flat.IsFlat());
|
|
if (flat.IsOneByte()) {
|
|
return StringToDouble(unicode_cache, flat.ToOneByteVector(), flags,
|
|
empty_string_val);
|
|
} else {
|
|
return StringToDouble(unicode_cache, flat.ToUC16Vector(), flags,
|
|
empty_string_val);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool IsSpecialIndex(UnicodeCache* unicode_cache, String* string) {
|
|
// Max length of canonical double: -X.XXXXXXXXXXXXXXXXX-eXXX
|
|
const int kBufferSize = 24;
|
|
const int length = string->length();
|
|
if (length == 0 || length > kBufferSize) return false;
|
|
uint16_t buffer[kBufferSize];
|
|
String::WriteToFlat(string, buffer, 0, length);
|
|
// If the first char is not a digit or a '-' or we can't match 'NaN' or
|
|
// '(-)Infinity', bailout immediately.
|
|
int offset = 0;
|
|
if (!IsDecimalDigit(buffer[0])) {
|
|
if (buffer[0] == '-') {
|
|
if (length == 1) return false; // Just '-' is bad.
|
|
if (!IsDecimalDigit(buffer[1])) {
|
|
if (buffer[1] == 'I' && length == 9) {
|
|
// Allow matching of '-Infinity' below.
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
offset++;
|
|
} else if (buffer[0] == 'I' && length == 8) {
|
|
// Allow matching of 'Infinity' below.
|
|
} else if (buffer[0] == 'N' && length == 3) {
|
|
// Match NaN.
|
|
return buffer[1] == 'a' && buffer[2] == 'N';
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
// Expected fast path: key is an integer.
|
|
static const int kRepresentableIntegerLength = 15; // (-)XXXXXXXXXXXXXXX
|
|
if (length - offset <= kRepresentableIntegerLength) {
|
|
const int initial_offset = offset;
|
|
bool matches = true;
|
|
for (; offset < length; offset++) {
|
|
matches &= IsDecimalDigit(buffer[offset]);
|
|
}
|
|
if (matches) {
|
|
// Match 0 and -0.
|
|
if (buffer[initial_offset] == '0') return initial_offset == length - 1;
|
|
return true;
|
|
}
|
|
}
|
|
// Slow path: test DoubleToString(StringToDouble(string)) == string.
|
|
Vector<const uint16_t> vector(buffer, length);
|
|
double d = StringToDouble(unicode_cache, vector, NO_FLAGS);
|
|
if (std::isnan(d)) return false;
|
|
// Compute reverse string.
|
|
char reverse_buffer[kBufferSize + 1]; // Result will be /0 terminated.
|
|
Vector<char> reverse_vector(reverse_buffer, arraysize(reverse_buffer));
|
|
const char* reverse_string = DoubleToCString(d, reverse_vector);
|
|
for (int i = 0; i < length; ++i) {
|
|
if (static_cast<uint16_t>(reverse_string[i]) != buffer[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace internal
|
|
} // namespace v8
|