Extract string->double and double->string conversions for use in the preparser.
Review URL: http://codereview.chromium.org/7308004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8534 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
e3b1fe2c14
commit
877d6198e5
@ -126,7 +126,9 @@ SOURCES = {
|
||||
utils.cc
|
||||
v8-counters.cc
|
||||
v8.cc
|
||||
v8conversions.cc
|
||||
v8threads.cc
|
||||
v8utils.cc
|
||||
variables.cc
|
||||
version.cc
|
||||
zone.cc
|
||||
@ -229,13 +231,18 @@ SOURCES = {
|
||||
PREPARSER_SOURCES = {
|
||||
'all': Split("""
|
||||
allocation.cc
|
||||
bignum.cc
|
||||
cached-powers.cc
|
||||
conversions.cc
|
||||
hashmap.cc
|
||||
preparse-data.cc
|
||||
preparser.cc
|
||||
preparser-api.cc
|
||||
scanner-base.cc
|
||||
strtod.cc
|
||||
token.cc
|
||||
unicode.cc
|
||||
utils.cc
|
||||
""")
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -25,10 +25,9 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "bignum.h"
|
||||
#include "../include/v8stdint.h"
|
||||
#include "utils.h"
|
||||
#include "bignum.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -26,10 +26,12 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "../include/v8stdint.h"
|
||||
#include "globals.h"
|
||||
#include "checks.h"
|
||||
#include "cached-powers.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -147,7 +149,9 @@ void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
|
||||
DiyFp* power,
|
||||
int* decimal_exponent) {
|
||||
int kQ = DiyFp::kSignificandSize;
|
||||
double k = ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
|
||||
// Some platforms return incorrect sign on 0 result. We can ignore that here,
|
||||
// which means we can avoid depending on platform.h.
|
||||
double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
|
||||
int foo = kCachedPowersOffset;
|
||||
int index =
|
||||
(foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -28,14 +28,16 @@
|
||||
#ifndef V8_CONVERSIONS_INL_H_
|
||||
#define V8_CONVERSIONS_INL_H_
|
||||
|
||||
#include <limits.h> // Required for INT_MAX etc.
|
||||
#include <math.h>
|
||||
#include <float.h> // required for DBL_MAX and on Win32 for finite()
|
||||
#include <float.h> // Required for DBL_MAX and on Win32 for finite()
|
||||
#include <stdarg.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Extra POSIX/ANSI functions for Win32/MSVC.
|
||||
|
||||
#include "conversions.h"
|
||||
#include "strtod.h"
|
||||
#include "platform.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -77,18 +79,6 @@ static inline double DoubleToInteger(double x) {
|
||||
}
|
||||
|
||||
|
||||
int32_t NumberToInt32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToInt32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
uint32_t NumberToUint32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToUint32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
int32_t DoubleToInt32(double x) {
|
||||
int32_t i = FastD2I(x);
|
||||
if (FastI2D(i) == x) return i;
|
||||
@ -101,6 +91,572 @@ int32_t DoubleToInt32(double x) {
|
||||
}
|
||||
|
||||
|
||||
template <class Iterator, class EndMark>
|
||||
static bool SubStringEquals(Iterator* current,
|
||||
EndMark end,
|
||||
const char* substring) {
|
||||
ASSERT(**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>
|
||||
static inline bool AdvanceToNonspace(UnicodeCache* unicode_cache,
|
||||
Iterator* current,
|
||||
EndMark end) {
|
||||
while (*current != end) {
|
||||
if (!unicode_cache->IsWhiteSpace(**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>
|
||||
static double InternalStringToIntDouble(UnicodeCache* unicode_cache,
|
||||
Iterator current,
|
||||
EndMark end,
|
||||
bool negative,
|
||||
bool allow_trailing_junk) {
|
||||
ASSERT(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);
|
||||
|
||||
do {
|
||||
int digit;
|
||||
if (*current >= '0' && *current <= '9' && *current < '0' + radix) {
|
||||
digit = static_cast<char>(*current) - '0';
|
||||
} else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) {
|
||||
digit = static_cast<char>(*current) - 'a' + 10;
|
||||
} else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) {
|
||||
digit = static_cast<char>(*current) - 'A' + 10;
|
||||
} else {
|
||||
if (allow_trailing_junk ||
|
||||
!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
break;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
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 & ((int64_t)1 << 53)) != 0) {
|
||||
exponent++;
|
||||
number >>= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++current;
|
||||
} while (current != end);
|
||||
|
||||
ASSERT(number < ((int64_t)1 << 53));
|
||||
ASSERT(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);
|
||||
}
|
||||
|
||||
ASSERT(number != 0);
|
||||
// The double could be constructed faster from number (mantissa), exponent
|
||||
// and sign. Assuming it's a rare case more simple code is used.
|
||||
return static_cast<double>(negative ? -number : number) * pow(2.0, exponent);
|
||||
}
|
||||
|
||||
|
||||
template <class Iterator, class EndMark>
|
||||
static double InternalStringToInt(UnicodeCache* unicode_cache,
|
||||
Iterator current,
|
||||
EndMark end,
|
||||
int radix) {
|
||||
const bool allow_trailing_junk = true;
|
||||
const double empty_string_val = JUNK_STRING_VALUE;
|
||||
|
||||
if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return empty_string_val;
|
||||
}
|
||||
|
||||
bool negative = false;
|
||||
bool leading_zero = false;
|
||||
|
||||
if (*current == '+') {
|
||||
// Ignore leading sign; skip following spaces.
|
||||
++current;
|
||||
if (current == end) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
} else if (*current == '-') {
|
||||
++current;
|
||||
if (current == end) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
negative = true;
|
||||
}
|
||||
|
||||
if (radix == 0) {
|
||||
// Radix detection.
|
||||
if (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
if (*current == 'x' || *current == 'X') {
|
||||
radix = 16;
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else {
|
||||
radix = 8;
|
||||
leading_zero = true;
|
||||
}
|
||||
} else {
|
||||
radix = 10;
|
||||
}
|
||||
} else if (radix == 16) {
|
||||
if (*current == '0') {
|
||||
// Allow "0x" prefix.
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
if (*current == 'x' || *current == 'X') {
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else {
|
||||
leading_zero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (radix < 2 || radix > 36) return JUNK_STRING_VALUE;
|
||||
|
||||
// Skip leading zeros.
|
||||
while (*current == '0') {
|
||||
leading_zero = true;
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
}
|
||||
|
||||
if (!leading_zero && !isDigit(*current, radix)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
if (IsPowerOf2(radix)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
if (radix == 10) {
|
||||
// Parsing with strtod.
|
||||
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.
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||
}
|
||||
++current;
|
||||
if (current == end) break;
|
||||
}
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
Vector<const char> buffer_vector(buffer, buffer_pos);
|
||||
return negative ? -Strtod(buffer_vector, 0) : Strtod(buffer_vector, 0);
|
||||
}
|
||||
|
||||
// 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 loosing precision.
|
||||
|
||||
double v = 0.0;
|
||||
bool done = false;
|
||||
do {
|
||||
// Parse the longest part of the string starting at index j
|
||||
// possible while keeping the multiplier, and thus the part
|
||||
// itself, within 32 bits.
|
||||
unsigned int part = 0, multiplier = 1;
|
||||
while (true) {
|
||||
int 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 unsigned int kMaximumMultiplier = 0xffffffffU / 36;
|
||||
uint32_t m = multiplier * radix;
|
||||
if (m > kMaximumMultiplier) break;
|
||||
part = part * radix + d;
|
||||
multiplier = m;
|
||||
ASSERT(multiplier > part);
|
||||
|
||||
++current;
|
||||
if (current == end) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the value and skip the part in the string.
|
||||
v = v * multiplier + part;
|
||||
} while (!done);
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
return negative ? -v : v;
|
||||
}
|
||||
|
||||
|
||||
// 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>
|
||||
static 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;
|
||||
|
||||
// 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;
|
||||
bool fractional_part = false;
|
||||
|
||||
bool negative = false;
|
||||
|
||||
if (*current == '+') {
|
||||
// Ignore leading sign.
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else if (*current == '-') {
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
negative = true;
|
||||
}
|
||||
|
||||
static const char kInfinitySymbol[] = "Infinity";
|
||||
if (*current == kInfinitySymbol[0]) {
|
||||
if (!SubStringEquals(¤t, end, kInfinitySymbol)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos == 0);
|
||||
return negative ? -V8_INFINITY : V8_INFINITY;
|
||||
}
|
||||
|
||||
bool leading_zero = false;
|
||||
if (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
|
||||
leading_zero = true;
|
||||
|
||||
// It could be hexadecimal value.
|
||||
if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
|
||||
++current;
|
||||
if (current == end || !isDigit(*current, 16)) {
|
||||
return JUNK_STRING_VALUE; // "0x".
|
||||
}
|
||||
|
||||
return InternalStringToIntDouble<4>(unicode_cache,
|
||||
current,
|
||||
end,
|
||||
negative,
|
||||
allow_trailing_junk);
|
||||
}
|
||||
|
||||
// Ignore leading zeros in the integer part.
|
||||
while (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
}
|
||||
}
|
||||
|
||||
bool octal = leading_zero && (flags & ALLOW_OCTALS) != 0;
|
||||
|
||||
// Copy significant digits of the integer part (if any) to the buffer.
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (significant_digits < kMaxSignificantDigits) {
|
||||
ASSERT(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 JUNK_STRING_VALUE;
|
||||
if (octal) goto parsing_done;
|
||||
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (significant_digits == 0 && !leading_zero) {
|
||||
return JUNK_STRING_VALUE;
|
||||
} 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(negative);
|
||||
exponent--; // Move this 0 into the exponent.
|
||||
}
|
||||
}
|
||||
|
||||
// We don't emit a '.', but adjust the exponent instead.
|
||||
fractional_part = true;
|
||||
|
||||
// There is a fractional part.
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (significant_digits < kMaxSignificantDigits) {
|
||||
ASSERT(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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
// Parse exponential part.
|
||||
if (*current == 'e' || *current == 'E') {
|
||||
if (octal) return JUNK_STRING_VALUE;
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
char sign = '+';
|
||||
if (*current == '+' || *current == '-') {
|
||||
sign = static_cast<char>(*current);
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current == end || *current < '0' || *current > '9') {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
const int max_exponent = INT_MAX / 2;
|
||||
ASSERT(-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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
parsing_done:
|
||||
exponent += insignificant_digits;
|
||||
|
||||
if (octal) {
|
||||
return InternalStringToIntDouble<3>(unicode_cache,
|
||||
buffer,
|
||||
buffer + buffer_pos,
|
||||
negative,
|
||||
allow_trailing_junk);
|
||||
}
|
||||
|
||||
if (nonzero_digit_dropped) {
|
||||
buffer[buffer_pos++] = '1';
|
||||
exponent--;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
|
||||
double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
|
||||
return negative ? -converted : converted;
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_CONVERSIONS_INL_H_
|
||||
|
@ -28,693 +28,15 @@
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "conversions-inl.h"
|
||||
#include "dtoa.h"
|
||||
#include "factory.h"
|
||||
#include "scanner-base.h"
|
||||
#include "strtod.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// C++-style iterator adaptor for StringInputBuffer
|
||||
// (unlike C++ iterators the end-marker has different type).
|
||||
class StringInputBufferIterator {
|
||||
public:
|
||||
class EndMarker {};
|
||||
|
||||
explicit StringInputBufferIterator(StringInputBuffer* buffer);
|
||||
|
||||
int operator*() const;
|
||||
void operator++();
|
||||
bool operator==(EndMarker const&) const { return end_; }
|
||||
bool operator!=(EndMarker const& m) const { return !end_; }
|
||||
|
||||
private:
|
||||
StringInputBuffer* const buffer_;
|
||||
int current_;
|
||||
bool end_;
|
||||
};
|
||||
|
||||
|
||||
StringInputBufferIterator::StringInputBufferIterator(
|
||||
StringInputBuffer* buffer) : buffer_(buffer) {
|
||||
++(*this);
|
||||
}
|
||||
|
||||
int StringInputBufferIterator::operator*() const {
|
||||
return current_;
|
||||
}
|
||||
|
||||
|
||||
void StringInputBufferIterator::operator++() {
|
||||
end_ = !buffer_->has_more();
|
||||
if (!end_) {
|
||||
current_ = buffer_->GetNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Iterator, class EndMark>
|
||||
static bool SubStringEquals(Iterator* current,
|
||||
EndMark end,
|
||||
const char* substring) {
|
||||
ASSERT(**current == *substring);
|
||||
for (substring++; *substring != '\0'; substring++) {
|
||||
++*current;
|
||||
if (*current == end || **current != *substring) return false;
|
||||
}
|
||||
++*current;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
static const double JUNK_STRING_VALUE = OS::nan_value();
|
||||
|
||||
|
||||
// Returns true if a nonspace found and false if the end has reached.
|
||||
template <class Iterator, class EndMark>
|
||||
static inline bool AdvanceToNonspace(UnicodeCache* unicode_cache,
|
||||
Iterator* current,
|
||||
EndMark end) {
|
||||
while (*current != end) {
|
||||
if (!unicode_cache->IsWhiteSpace(**current)) return true;
|
||||
++*current;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
|
||||
static double SignedZero(bool negative) {
|
||||
return negative ? -0.0 : 0.0;
|
||||
}
|
||||
|
||||
|
||||
// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
|
||||
template <int radix_log_2, class Iterator, class EndMark>
|
||||
static double InternalStringToIntDouble(UnicodeCache* unicode_cache,
|
||||
Iterator current,
|
||||
EndMark end,
|
||||
bool negative,
|
||||
bool allow_trailing_junk) {
|
||||
ASSERT(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);
|
||||
|
||||
do {
|
||||
int digit;
|
||||
if (*current >= '0' && *current <= '9' && *current < '0' + radix) {
|
||||
digit = static_cast<char>(*current) - '0';
|
||||
} else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) {
|
||||
digit = static_cast<char>(*current) - 'a' + 10;
|
||||
} else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) {
|
||||
digit = static_cast<char>(*current) - 'A' + 10;
|
||||
} else {
|
||||
if (allow_trailing_junk ||
|
||||
!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
break;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
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 & ((int64_t)1 << 53)) != 0) {
|
||||
exponent++;
|
||||
number >>= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++current;
|
||||
} while (current != end);
|
||||
|
||||
ASSERT(number < ((int64_t)1 << 53));
|
||||
ASSERT(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);
|
||||
}
|
||||
|
||||
ASSERT(number != 0);
|
||||
// The double could be constructed faster from number (mantissa), exponent
|
||||
// and sign. Assuming it's a rare case more simple code is used.
|
||||
return static_cast<double>(negative ? -number : number) * pow(2.0, exponent);
|
||||
}
|
||||
|
||||
|
||||
template <class Iterator, class EndMark>
|
||||
static double InternalStringToInt(UnicodeCache* unicode_cache,
|
||||
Iterator current,
|
||||
EndMark end,
|
||||
int radix) {
|
||||
const bool allow_trailing_junk = true;
|
||||
const double empty_string_val = JUNK_STRING_VALUE;
|
||||
|
||||
if (!AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return empty_string_val;
|
||||
}
|
||||
|
||||
bool negative = false;
|
||||
bool leading_zero = false;
|
||||
|
||||
if (*current == '+') {
|
||||
// Ignore leading sign; skip following spaces.
|
||||
++current;
|
||||
if (current == end) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
} else if (*current == '-') {
|
||||
++current;
|
||||
if (current == end) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
negative = true;
|
||||
}
|
||||
|
||||
if (radix == 0) {
|
||||
// Radix detection.
|
||||
if (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
if (*current == 'x' || *current == 'X') {
|
||||
radix = 16;
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else {
|
||||
radix = 8;
|
||||
leading_zero = true;
|
||||
}
|
||||
} else {
|
||||
radix = 10;
|
||||
}
|
||||
} else if (radix == 16) {
|
||||
if (*current == '0') {
|
||||
// Allow "0x" prefix.
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
if (*current == 'x' || *current == 'X') {
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else {
|
||||
leading_zero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (radix < 2 || radix > 36) return JUNK_STRING_VALUE;
|
||||
|
||||
// Skip leading zeros.
|
||||
while (*current == '0') {
|
||||
leading_zero = true;
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
}
|
||||
|
||||
if (!leading_zero && !isDigit(*current, radix)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
if (IsPowerOf2(radix)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
if (radix == 10) {
|
||||
// Parsing with strtod.
|
||||
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.
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos++] = static_cast<char>(*current);
|
||||
}
|
||||
++current;
|
||||
if (current == end) break;
|
||||
}
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
Vector<const char> buffer_vector(buffer, buffer_pos);
|
||||
return negative ? -Strtod(buffer_vector, 0) : Strtod(buffer_vector, 0);
|
||||
}
|
||||
|
||||
// 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 loosing precision.
|
||||
|
||||
double v = 0.0;
|
||||
bool done = false;
|
||||
do {
|
||||
// Parse the longest part of the string starting at index j
|
||||
// possible while keeping the multiplier, and thus the part
|
||||
// itself, within 32 bits.
|
||||
unsigned int part = 0, multiplier = 1;
|
||||
while (true) {
|
||||
int 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 unsigned int kMaximumMultiplier = 0xffffffffU / 36;
|
||||
uint32_t m = multiplier * radix;
|
||||
if (m > kMaximumMultiplier) break;
|
||||
part = part * radix + d;
|
||||
multiplier = m;
|
||||
ASSERT(multiplier > part);
|
||||
|
||||
++current;
|
||||
if (current == end) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the value and skip the part in the string.
|
||||
v = v * multiplier + part;
|
||||
} while (!done);
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
return negative ? -v : v;
|
||||
}
|
||||
|
||||
|
||||
// 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>
|
||||
static 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;
|
||||
|
||||
// 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;
|
||||
bool fractional_part = false;
|
||||
|
||||
bool negative = false;
|
||||
|
||||
if (*current == '+') {
|
||||
// Ignore leading sign.
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
} else if (*current == '-') {
|
||||
++current;
|
||||
if (current == end) return JUNK_STRING_VALUE;
|
||||
negative = true;
|
||||
}
|
||||
|
||||
static const char kInfinitySymbol[] = "Infinity";
|
||||
if (*current == kInfinitySymbol[0]) {
|
||||
if (!SubStringEquals(¤t, end, kInfinitySymbol)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
if (!allow_trailing_junk &&
|
||||
AdvanceToNonspace(unicode_cache, ¤t, end)) {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos == 0);
|
||||
return negative ? -V8_INFINITY : V8_INFINITY;
|
||||
}
|
||||
|
||||
bool leading_zero = false;
|
||||
if (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
|
||||
leading_zero = true;
|
||||
|
||||
// It could be hexadecimal value.
|
||||
if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
|
||||
++current;
|
||||
if (current == end || !isDigit(*current, 16)) {
|
||||
return JUNK_STRING_VALUE; // "0x".
|
||||
}
|
||||
|
||||
return InternalStringToIntDouble<4>(unicode_cache,
|
||||
current,
|
||||
end,
|
||||
negative,
|
||||
allow_trailing_junk);
|
||||
}
|
||||
|
||||
// Ignore leading zeros in the integer part.
|
||||
while (*current == '0') {
|
||||
++current;
|
||||
if (current == end) return SignedZero(negative);
|
||||
}
|
||||
}
|
||||
|
||||
bool octal = leading_zero && (flags & ALLOW_OCTALS) != 0;
|
||||
|
||||
// Copy significant digits of the integer part (if any) to the buffer.
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (significant_digits < kMaxSignificantDigits) {
|
||||
ASSERT(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 JUNK_STRING_VALUE;
|
||||
if (octal) goto parsing_done;
|
||||
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (significant_digits == 0 && !leading_zero) {
|
||||
return JUNK_STRING_VALUE;
|
||||
} 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(negative);
|
||||
exponent--; // Move this 0 into the exponent.
|
||||
}
|
||||
}
|
||||
|
||||
// We don't emit a '.', but adjust the exponent instead.
|
||||
fractional_part = true;
|
||||
|
||||
// There is a fractional part.
|
||||
while (*current >= '0' && *current <= '9') {
|
||||
if (significant_digits < kMaxSignificantDigits) {
|
||||
ASSERT(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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
// Parse exponential part.
|
||||
if (*current == 'e' || *current == 'E') {
|
||||
if (octal) return JUNK_STRING_VALUE;
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
char sign = '+';
|
||||
if (*current == '+' || *current == '-') {
|
||||
sign = static_cast<char>(*current);
|
||||
++current;
|
||||
if (current == end) {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current == end || *current < '0' || *current > '9') {
|
||||
if (allow_trailing_junk) {
|
||||
goto parsing_done;
|
||||
} else {
|
||||
return JUNK_STRING_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
const int max_exponent = INT_MAX / 2;
|
||||
ASSERT(-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 JUNK_STRING_VALUE;
|
||||
}
|
||||
|
||||
parsing_done:
|
||||
exponent += insignificant_digits;
|
||||
|
||||
if (octal) {
|
||||
return InternalStringToIntDouble<3>(unicode_cache,
|
||||
buffer,
|
||||
buffer + buffer_pos,
|
||||
negative,
|
||||
allow_trailing_junk);
|
||||
}
|
||||
|
||||
if (nonzero_digit_dropped) {
|
||||
buffer[buffer_pos++] = '1';
|
||||
exponent--;
|
||||
}
|
||||
|
||||
ASSERT(buffer_pos < kBufferSize);
|
||||
buffer[buffer_pos] = '\0';
|
||||
|
||||
double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
|
||||
return negative ? -converted : converted;
|
||||
}
|
||||
|
||||
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
String* str, int flags, double empty_string_val) {
|
||||
StringShape shape(str);
|
||||
if (shape.IsSequentialAscii()) {
|
||||
const char* begin = SeqAsciiString::cast(str)->GetChars();
|
||||
const char* end = begin + str->length();
|
||||
return InternalStringToDouble(unicode_cache, begin, end, flags,
|
||||
empty_string_val);
|
||||
} else if (shape.IsSequentialTwoByte()) {
|
||||
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
|
||||
const uc16* end = begin + str->length();
|
||||
return InternalStringToDouble(unicode_cache, begin, end, flags,
|
||||
empty_string_val);
|
||||
} else {
|
||||
StringInputBuffer buffer(str);
|
||||
return InternalStringToDouble(unicode_cache,
|
||||
StringInputBufferIterator(&buffer),
|
||||
StringInputBufferIterator::EndMarker(),
|
||||
flags,
|
||||
empty_string_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double StringToInt(UnicodeCache* unicode_cache,
|
||||
String* str,
|
||||
int radix) {
|
||||
StringShape shape(str);
|
||||
if (shape.IsSequentialAscii()) {
|
||||
const char* begin = SeqAsciiString::cast(str)->GetChars();
|
||||
const char* end = begin + str->length();
|
||||
return InternalStringToInt(unicode_cache, begin, end, radix);
|
||||
} else if (shape.IsSequentialTwoByte()) {
|
||||
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
|
||||
const uc16* end = begin + str->length();
|
||||
return InternalStringToInt(unicode_cache, begin, end, radix);
|
||||
} else {
|
||||
StringInputBuffer buffer(str);
|
||||
return InternalStringToInt(unicode_cache,
|
||||
StringInputBufferIterator(&buffer),
|
||||
StringInputBufferIterator::EndMarker(),
|
||||
radix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
@ -750,7 +72,7 @@ const char* DoubleToCString(double v, Vector<char> buffer) {
|
||||
case FP_INFINITE: return (v < 0.0 ? "-Infinity" : "Infinity");
|
||||
case FP_ZERO: return "0";
|
||||
default: {
|
||||
StringBuilder builder(buffer.start(), buffer.length());
|
||||
SimpleStringBuilder builder(buffer.start(), buffer.length());
|
||||
int decimal_point;
|
||||
int sign;
|
||||
const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1;
|
||||
@ -791,7 +113,7 @@ const char* DoubleToCString(double v, Vector<char> buffer) {
|
||||
builder.AddCharacter((decimal_point >= 0) ? '+' : '-');
|
||||
int exponent = decimal_point - 1;
|
||||
if (exponent < 0) exponent = -exponent;
|
||||
builder.AddFormatted("%d", exponent);
|
||||
builder.AddDecimalInteger(exponent);
|
||||
}
|
||||
return builder.Finalize();
|
||||
}
|
||||
@ -869,7 +191,7 @@ char* DoubleToFixedCString(double value, int f) {
|
||||
|
||||
unsigned rep_length =
|
||||
zero_prefix_length + decimal_rep_length + zero_postfix_length;
|
||||
StringBuilder rep_builder(rep_length + 1);
|
||||
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);
|
||||
@ -878,7 +200,7 @@ char* DoubleToFixedCString(double value, int f) {
|
||||
// Create the result string by appending a minus and putting in a
|
||||
// decimal point if needed.
|
||||
unsigned result_size = decimal_point + f + 2;
|
||||
StringBuilder builder(result_size + 1);
|
||||
SimpleStringBuilder builder(result_size + 1);
|
||||
if (negative) builder.AddCharacter('-');
|
||||
builder.AddSubstring(rep, decimal_point);
|
||||
if (f > 0) {
|
||||
@ -904,7 +226,7 @@ static char* CreateExponentialRepresentation(char* decimal_rep,
|
||||
// letter 'e', a minus or a plus depending on the exponent, and a
|
||||
// three digit exponent.
|
||||
unsigned result_size = significant_digits + 7;
|
||||
StringBuilder builder(result_size + 1);
|
||||
SimpleStringBuilder builder(result_size + 1);
|
||||
|
||||
if (negative) builder.AddCharacter('-');
|
||||
builder.AddCharacter(decimal_rep[0]);
|
||||
@ -917,7 +239,7 @@ static char* CreateExponentialRepresentation(char* decimal_rep,
|
||||
|
||||
builder.AddCharacter('e');
|
||||
builder.AddCharacter(negative_exponent ? '-' : '+');
|
||||
builder.AddFormatted("%d", exponent);
|
||||
builder.AddDecimalInteger(exponent);
|
||||
return builder.Finalize();
|
||||
}
|
||||
|
||||
@ -1009,7 +331,7 @@ char* DoubleToPrecisionCString(double value, int p) {
|
||||
unsigned result_size = (decimal_point <= 0)
|
||||
? -decimal_point + p + 3
|
||||
: p + 2;
|
||||
StringBuilder builder(result_size + 1);
|
||||
SimpleStringBuilder builder(result_size + 1);
|
||||
if (negative) builder.AddCharacter('-');
|
||||
if (decimal_point <= 0) {
|
||||
builder.AddString("0.");
|
||||
@ -1101,7 +423,7 @@ char* DoubleToRadixCString(double value, int radix) {
|
||||
// If the number has a decimal part, leave room for the period.
|
||||
if (decimal_pos > 0) result_size++;
|
||||
// Allocate result and fill in the parts.
|
||||
StringBuilder builder(result_size + 1);
|
||||
SimpleStringBuilder builder(result_size + 1);
|
||||
builder.AddSubstring(integer_buffer + integer_pos + 1, integer_part_size);
|
||||
if (decimal_pos > 0) builder.AddCharacter('.');
|
||||
builder.AddSubstring(decimal_buffer, decimal_pos);
|
||||
|
@ -28,11 +28,36 @@
|
||||
#ifndef V8_CONVERSIONS_H_
|
||||
#define V8_CONVERSIONS_H_
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "scanner-base.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// 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;
|
||||
|
||||
static const double JUNK_STRING_VALUE =
|
||||
std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
|
||||
static double SignedZero(bool negative) {
|
||||
return negative ? -0.0 : 0.0;
|
||||
}
|
||||
|
||||
|
||||
// The fast double-to-(unsigned-)int conversion routine does not guarantee
|
||||
// rounding towards zero.
|
||||
@ -87,16 +112,7 @@ enum ConversionFlags {
|
||||
};
|
||||
|
||||
|
||||
// Convert from Number object to C integer.
|
||||
static inline int32_t NumberToInt32(Object* number);
|
||||
static inline uint32_t NumberToUint32(Object* number);
|
||||
|
||||
|
||||
// Converts a string into a double value according to ECMA-262 9.3.1
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
String* str,
|
||||
int flags,
|
||||
double empty_string_val = 0);
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
Vector<const char> str,
|
||||
int flags,
|
||||
@ -111,9 +127,6 @@ double StringToDouble(UnicodeCache* unicode_cache,
|
||||
int flags,
|
||||
double empty_string_val = 0);
|
||||
|
||||
// Converts a string into an integer.
|
||||
double StringToInt(UnicodeCache* unicode_cache, String* str, int radix);
|
||||
|
||||
// Converts a double to a string value according to ECMA-262 9.8.1.
|
||||
// The buffer should be large enough for any floating point number.
|
||||
// 100 characters is enough.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -25,8 +25,9 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "../include/v8stdint.h"
|
||||
#include "globals.h"
|
||||
#include "checks.h"
|
||||
#include "diy-fp.h"
|
||||
|
||||
namespace v8 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -80,7 +80,7 @@ class DiyFp {
|
||||
|
||||
// This method is mainly called for normalizing boundaries. In general
|
||||
// boundaries need to be shifted by 10 bits. We thus optimize for this case.
|
||||
const uint64_t k10MSBits = V8_2PART_UINT64_C(0xFFC00000, 00000000);
|
||||
const uint64_t k10MSBits = static_cast<uint64_t>(0x3FF) << 54;
|
||||
while ((f & k10MSBits) == 0) {
|
||||
f <<= 10;
|
||||
e -= 10;
|
||||
@ -106,7 +106,7 @@ class DiyFp {
|
||||
void set_e(int new_value) { e_ = new_value; }
|
||||
|
||||
private:
|
||||
static const uint64_t kUint64MSB = V8_2PART_UINT64_C(0x80000000, 00000000);
|
||||
static const uint64_t kUint64MSB = static_cast<uint64_t>(1) << 63;
|
||||
|
||||
uint64_t f_;
|
||||
int e_;
|
||||
|
@ -35,7 +35,8 @@
|
||||
#include "data-flow.h"
|
||||
#include "small-pointer-list.h"
|
||||
#include "string-stream.h"
|
||||
#include "utils.h"
|
||||
#include "v8conversions.h"
|
||||
#include "v8utils.h"
|
||||
#include "zone.h"
|
||||
|
||||
namespace v8 {
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include "v8.h"
|
||||
|
||||
#include "char-predicates-inl.h"
|
||||
#include "conversions.h"
|
||||
#include "v8conversions.h"
|
||||
#include "messages.h"
|
||||
#include "spaces-inl.h"
|
||||
#include "token.h"
|
||||
|
@ -2585,4 +2585,3 @@ void LiveObjectList::VerifyNotInFromSpace() {
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // LIVE_OBJECT_LIST
|
||||
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "preparse-data.h"
|
||||
#include "preparser.h"
|
||||
|
||||
#include "conversions-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace preparser {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -26,9 +26,13 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <limits>
|
||||
|
||||
#include "v8.h"
|
||||
#ifndef V8_INFINITY
|
||||
#define V8_INFINITY std::numeric_limits<double>::infinity()
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "strtod.h"
|
||||
#include "bignum.h"
|
||||
|
333
src/utils.cc
333
src/utils.cc
@ -1,4 +1,4 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -26,211 +26,26 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "sys/stat.h"
|
||||
#include "../include/v8stdint.h"
|
||||
#include "checks.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
void PrintF(const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VPrint(format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void PrintF(FILE* out, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VFPrint(out, format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void Flush(FILE* out) {
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
|
||||
char* ReadLine(const char* prompt) {
|
||||
char* result = NULL;
|
||||
char line_buf[256];
|
||||
int offset = 0;
|
||||
bool keep_going = true;
|
||||
fprintf(stdout, "%s", prompt);
|
||||
fflush(stdout);
|
||||
while (keep_going) {
|
||||
if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
|
||||
// fgets got an error. Just give up.
|
||||
if (result != NULL) {
|
||||
DeleteArray(result);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int len = StrLength(line_buf);
|
||||
if (len > 1 &&
|
||||
line_buf[len - 2] == '\\' &&
|
||||
line_buf[len - 1] == '\n') {
|
||||
// When we read a line that ends with a "\" we remove the escape and
|
||||
// append the remainder.
|
||||
line_buf[len - 2] = '\n';
|
||||
line_buf[len - 1] = 0;
|
||||
len -= 1;
|
||||
} else if ((len > 0) && (line_buf[len - 1] == '\n')) {
|
||||
// Since we read a new line we are done reading the line. This
|
||||
// will exit the loop after copying this buffer into the result.
|
||||
keep_going = false;
|
||||
}
|
||||
if (result == NULL) {
|
||||
// Allocate the initial result and make room for the terminating '\0'
|
||||
result = NewArray<char>(len + 1);
|
||||
} else {
|
||||
// Allocate a new result with enough room for the new addition.
|
||||
int new_len = offset + len + 1;
|
||||
char* new_result = NewArray<char>(new_len);
|
||||
// Copy the existing input into the new array and set the new
|
||||
// array as the result.
|
||||
memcpy(new_result, result, offset * kCharSize);
|
||||
DeleteArray(result);
|
||||
result = new_result;
|
||||
}
|
||||
// Copy the newly read line into the result.
|
||||
memcpy(result + offset, line_buf, len * kCharSize);
|
||||
offset += len;
|
||||
}
|
||||
ASSERT(result != NULL);
|
||||
result[offset] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* ReadCharsFromFile(const char* filename,
|
||||
int* size,
|
||||
int extra_space,
|
||||
bool verbose) {
|
||||
FILE* file = OS::FOpen(filename, "rb");
|
||||
if (file == NULL || fseek(file, 0, SEEK_END) != 0) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot read from file %s.\n", filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the size of the file and rewind it.
|
||||
*size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* result = NewArray<char>(*size + extra_space);
|
||||
for (int i = 0; i < *size;) {
|
||||
int read = static_cast<int>(fread(&result[i], 1, *size - i, file));
|
||||
if (read <= 0) {
|
||||
fclose(file);
|
||||
DeleteArray(result);
|
||||
return NULL;
|
||||
}
|
||||
i += read;
|
||||
}
|
||||
fclose(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
byte* ReadBytes(const char* filename, int* size, bool verbose) {
|
||||
char* chars = ReadCharsFromFile(filename, size, 0, verbose);
|
||||
return reinterpret_cast<byte*>(chars);
|
||||
}
|
||||
|
||||
|
||||
Vector<const char> ReadFile(const char* filename,
|
||||
bool* exists,
|
||||
bool verbose) {
|
||||
int size;
|
||||
char* result = ReadCharsFromFile(filename, &size, 1, verbose);
|
||||
if (!result) {
|
||||
*exists = false;
|
||||
return Vector<const char>::empty();
|
||||
}
|
||||
result[size] = '\0';
|
||||
*exists = true;
|
||||
return Vector<const char>(result, size);
|
||||
}
|
||||
|
||||
|
||||
int WriteCharsToFile(const char* str, int size, FILE* f) {
|
||||
int total = 0;
|
||||
while (total < size) {
|
||||
int write = static_cast<int>(fwrite(str, 1, size - total, f));
|
||||
if (write == 0) {
|
||||
return total;
|
||||
}
|
||||
total += write;
|
||||
str += write;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
int AppendChars(const char* filename,
|
||||
const char* str,
|
||||
int size,
|
||||
bool verbose) {
|
||||
FILE* f = OS::FOpen(filename, "ab");
|
||||
if (f == NULL) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot open file %s for writing.\n", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int written = WriteCharsToFile(str, size, f);
|
||||
fclose(f);
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
int WriteChars(const char* filename,
|
||||
const char* str,
|
||||
int size,
|
||||
bool verbose) {
|
||||
FILE* f = OS::FOpen(filename, "wb");
|
||||
if (f == NULL) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot open file %s for writing.\n", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int written = WriteCharsToFile(str, size, f);
|
||||
fclose(f);
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
int WriteBytes(const char* filename,
|
||||
const byte* bytes,
|
||||
int size,
|
||||
bool verbose) {
|
||||
const char* str = reinterpret_cast<const char*>(bytes);
|
||||
return WriteChars(filename, str, size, verbose);
|
||||
}
|
||||
|
||||
|
||||
StringBuilder::StringBuilder(int size) {
|
||||
SimpleStringBuilder::SimpleStringBuilder(int size) {
|
||||
buffer_ = Vector<char>::New(size);
|
||||
position_ = 0;
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddString(const char* s) {
|
||||
void SimpleStringBuilder::AddString(const char* s) {
|
||||
AddSubstring(s, StrLength(s));
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddSubstring(const char* s, int n) {
|
||||
void SimpleStringBuilder::AddSubstring(const char* s, int n) {
|
||||
ASSERT(!is_finalized() && position_ + n < buffer_.length());
|
||||
ASSERT(static_cast<size_t>(n) <= strlen(s));
|
||||
memcpy(&buffer_[position_], s, n * kCharSize);
|
||||
@ -238,33 +53,32 @@ void StringBuilder::AddSubstring(const char* s, int n) {
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddFormatted(const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
AddFormattedList(format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddFormattedList(const char* format, va_list list) {
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
int n = OS::VSNPrintF(buffer_ + position_, format, list);
|
||||
if (n < 0 || n >= (buffer_.length() - position_)) {
|
||||
position_ = buffer_.length();
|
||||
} else {
|
||||
position_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddPadding(char c, int count) {
|
||||
void SimpleStringBuilder::AddPadding(char c, int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
AddCharacter(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char* StringBuilder::Finalize() {
|
||||
void SimpleStringBuilder::AddDecimalInteger(int32_t value) {
|
||||
uint32_t number = static_cast<uint32_t>(value);
|
||||
if (value < 0) {
|
||||
AddCharacter('-');
|
||||
number = static_cast<uint32_t>(-value);
|
||||
}
|
||||
int digits = 1;
|
||||
for (uint32_t factor = 10; digits < 10; digits++, factor *= 10) {
|
||||
if (factor > number) break;
|
||||
}
|
||||
position_ += digits;
|
||||
for (int i = 1; i <= digits; i++) {
|
||||
buffer_[position_ - i] = '0' + static_cast<char>(number % 10);
|
||||
number /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char* SimpleStringBuilder::Finalize() {
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
buffer_[position_] = '\0';
|
||||
// Make sure nobody managed to add a 0-character to the
|
||||
@ -275,97 +89,4 @@ char* StringBuilder::Finalize() {
|
||||
return buffer_.start();
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::MemoryMappedExternalResource(const char* filename)
|
||||
: filename_(NULL),
|
||||
data_(NULL),
|
||||
length_(0),
|
||||
remove_file_on_cleanup_(false) {
|
||||
Init(filename);
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::
|
||||
MemoryMappedExternalResource(const char* filename,
|
||||
bool remove_file_on_cleanup)
|
||||
: filename_(NULL),
|
||||
data_(NULL),
|
||||
length_(0),
|
||||
remove_file_on_cleanup_(remove_file_on_cleanup) {
|
||||
Init(filename);
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::~MemoryMappedExternalResource() {
|
||||
// Release the resources if we had successfully acquired them:
|
||||
if (file_ != NULL) {
|
||||
delete file_;
|
||||
if (remove_file_on_cleanup_) {
|
||||
OS::Remove(filename_);
|
||||
}
|
||||
DeleteArray<char>(filename_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MemoryMappedExternalResource::Init(const char* filename) {
|
||||
file_ = OS::MemoryMappedFile::open(filename);
|
||||
if (file_ != NULL) {
|
||||
filename_ = StrDup(filename);
|
||||
data_ = reinterpret_cast<char*>(file_->memory());
|
||||
length_ = file_->size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const {
|
||||
bool is_ascii = true;
|
||||
|
||||
int line_no = 1;
|
||||
const char* start_of_line = data_;
|
||||
const char* end = data_ + length_;
|
||||
for (const char* p = data_; p < end; p++) {
|
||||
char c = *p;
|
||||
if ((c & 0x80) != 0) {
|
||||
// Non-ascii detected:
|
||||
is_ascii = false;
|
||||
|
||||
// Report the error and abort if appropriate:
|
||||
if (abort_if_failed) {
|
||||
int char_no = static_cast<int>(p - start_of_line) - 1;
|
||||
|
||||
ASSERT(filename_ != NULL);
|
||||
PrintF("\n\n\n"
|
||||
"Abort: Non-Ascii character 0x%.2x in file %s line %d char %d",
|
||||
c, filename_, line_no, char_no);
|
||||
|
||||
// Allow for some context up to kNumberOfLeadingContextChars chars
|
||||
// before the offending non-ascii char to help the user see where
|
||||
// the offending char is.
|
||||
const int kNumberOfLeadingContextChars = 10;
|
||||
const char* err_context = p - kNumberOfLeadingContextChars;
|
||||
if (err_context < data_) {
|
||||
err_context = data_;
|
||||
}
|
||||
// Compute the length of the error context and print it.
|
||||
int err_context_length = static_cast<int>(p - err_context);
|
||||
if (err_context_length != 0) {
|
||||
PrintF(" after \"%.*s\"", err_context_length, err_context);
|
||||
}
|
||||
PrintF(".\n\n\n");
|
||||
OS::Abort();
|
||||
}
|
||||
|
||||
break; // Non-ascii detected. No need to continue scanning.
|
||||
}
|
||||
if (c == '\n') {
|
||||
start_of_line = p;
|
||||
line_no++;
|
||||
}
|
||||
}
|
||||
|
||||
return is_ascii;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
63
src/utils.h
63
src/utils.h
@ -822,6 +822,69 @@ class EmbeddedContainer<ElementType, 0> {
|
||||
};
|
||||
|
||||
|
||||
// Helper class for building result strings in a character buffer. The
|
||||
// purpose of the class is to use safe operations that checks the
|
||||
// buffer bounds on all operations in debug mode.
|
||||
// This simple base class does not allow formatted output.
|
||||
class SimpleStringBuilder {
|
||||
public:
|
||||
// Create a string builder with a buffer of the given size. The
|
||||
// buffer is allocated through NewArray<char> and must be
|
||||
// deallocated by the caller of Finalize().
|
||||
explicit SimpleStringBuilder(int size);
|
||||
|
||||
SimpleStringBuilder(char* buffer, int size)
|
||||
: buffer_(buffer, size), position_(0) { }
|
||||
|
||||
~SimpleStringBuilder() { if (!is_finalized()) Finalize(); }
|
||||
|
||||
int size() const { return buffer_.length(); }
|
||||
|
||||
// Get the current position in the builder.
|
||||
int position() const {
|
||||
ASSERT(!is_finalized());
|
||||
return position_;
|
||||
}
|
||||
|
||||
// Reset the position.
|
||||
void Reset() { position_ = 0; }
|
||||
|
||||
// Add a single character to the builder. It is not allowed to add
|
||||
// 0-characters; use the Finalize() method to terminate the string
|
||||
// instead.
|
||||
void AddCharacter(char c) {
|
||||
ASSERT(c != '\0');
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
buffer_[position_++] = c;
|
||||
}
|
||||
|
||||
// Add an entire string to the builder. Uses strlen() internally to
|
||||
// compute the length of the input string.
|
||||
void AddString(const char* s);
|
||||
|
||||
// Add the first 'n' characters of the given string 's' to the
|
||||
// builder. The input string must have enough characters.
|
||||
void AddSubstring(const char* s, int n);
|
||||
|
||||
// Add character padding to the builder. If count is non-positive,
|
||||
// nothing is added to the builder.
|
||||
void AddPadding(char c, int count);
|
||||
|
||||
// Add the decimal representation of the value.
|
||||
void AddDecimalInteger(int value);
|
||||
|
||||
// Finalize the string by 0-terminating it and returning the buffer.
|
||||
char* Finalize();
|
||||
|
||||
protected:
|
||||
Vector<char> buffer_;
|
||||
int position_;
|
||||
|
||||
bool is_finalized() const { return position_ < 0; }
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(SimpleStringBuilder);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_UTILS_H_
|
||||
|
129
src/v8conversions.cc
Normal file
129
src/v8conversions.cc
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "conversions-inl.h"
|
||||
#include "v8conversions.h"
|
||||
#include "dtoa.h"
|
||||
#include "factory.h"
|
||||
#include "scanner-base.h"
|
||||
#include "strtod.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
// C++-style iterator adaptor for StringInputBuffer
|
||||
// (unlike C++ iterators the end-marker has different type).
|
||||
class StringInputBufferIterator {
|
||||
public:
|
||||
class EndMarker {};
|
||||
|
||||
explicit StringInputBufferIterator(StringInputBuffer* buffer);
|
||||
|
||||
int operator*() const;
|
||||
void operator++();
|
||||
bool operator==(EndMarker const&) const { return end_; }
|
||||
bool operator!=(EndMarker const& m) const { return !end_; }
|
||||
|
||||
private:
|
||||
StringInputBuffer* const buffer_;
|
||||
int current_;
|
||||
bool end_;
|
||||
};
|
||||
|
||||
|
||||
StringInputBufferIterator::StringInputBufferIterator(
|
||||
StringInputBuffer* buffer) : buffer_(buffer) {
|
||||
++(*this);
|
||||
}
|
||||
|
||||
int StringInputBufferIterator::operator*() const {
|
||||
return current_;
|
||||
}
|
||||
|
||||
|
||||
void StringInputBufferIterator::operator++() {
|
||||
end_ = !buffer_->has_more();
|
||||
if (!end_) {
|
||||
current_ = buffer_->GetNext();
|
||||
}
|
||||
}
|
||||
} // End anonymous namespace.
|
||||
|
||||
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
String* str, int flags, double empty_string_val) {
|
||||
StringShape shape(str);
|
||||
if (shape.IsSequentialAscii()) {
|
||||
const char* begin = SeqAsciiString::cast(str)->GetChars();
|
||||
const char* end = begin + str->length();
|
||||
return InternalStringToDouble(unicode_cache, begin, end, flags,
|
||||
empty_string_val);
|
||||
} else if (shape.IsSequentialTwoByte()) {
|
||||
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
|
||||
const uc16* end = begin + str->length();
|
||||
return InternalStringToDouble(unicode_cache, begin, end, flags,
|
||||
empty_string_val);
|
||||
} else {
|
||||
StringInputBuffer buffer(str);
|
||||
return InternalStringToDouble(unicode_cache,
|
||||
StringInputBufferIterator(&buffer),
|
||||
StringInputBufferIterator::EndMarker(),
|
||||
flags,
|
||||
empty_string_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double StringToInt(UnicodeCache* unicode_cache,
|
||||
String* str,
|
||||
int radix) {
|
||||
StringShape shape(str);
|
||||
if (shape.IsSequentialAscii()) {
|
||||
const char* begin = SeqAsciiString::cast(str)->GetChars();
|
||||
const char* end = begin + str->length();
|
||||
return InternalStringToInt(unicode_cache, begin, end, radix);
|
||||
} else if (shape.IsSequentialTwoByte()) {
|
||||
const uc16* begin = SeqTwoByteString::cast(str)->GetChars();
|
||||
const uc16* end = begin + str->length();
|
||||
return InternalStringToInt(unicode_cache, begin, end, radix);
|
||||
} else {
|
||||
StringInputBuffer buffer(str);
|
||||
return InternalStringToInt(unicode_cache,
|
||||
StringInputBufferIterator(&buffer),
|
||||
StringInputBufferIterator::EndMarker(),
|
||||
radix);
|
||||
}
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
60
src/v8conversions.h
Normal file
60
src/v8conversions.h
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef V8_V8CONVERSIONS_H_
|
||||
#define V8_V8CONVERSIONS_H_
|
||||
|
||||
#include "conversions.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Convert from Number object to C integer.
|
||||
static inline int32_t NumberToInt32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToInt32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t NumberToUint32(Object* number) {
|
||||
if (number->IsSmi()) return Smi::cast(number)->value();
|
||||
return DoubleToUint32(number->Number());
|
||||
}
|
||||
|
||||
|
||||
// Converts a string into a double value according to ECMA-262 9.3.1
|
||||
double StringToDouble(UnicodeCache* unicode_cache,
|
||||
String* str,
|
||||
int flags,
|
||||
double empty_string_val = 0);
|
||||
|
||||
// Converts a string into an integer.
|
||||
double StringToInt(UnicodeCache* unicode_cache, String* str, int radix);
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_V8CONVERSIONS_H_
|
334
src/v8utils.cc
Normal file
334
src/v8utils.cc
Normal file
@ -0,0 +1,334 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "sys/stat.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
void PrintF(const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VPrint(format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void PrintF(FILE* out, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
OS::VFPrint(out, format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void Flush(FILE* out) {
|
||||
fflush(out);
|
||||
}
|
||||
|
||||
|
||||
char* ReadLine(const char* prompt) {
|
||||
char* result = NULL;
|
||||
char line_buf[256];
|
||||
int offset = 0;
|
||||
bool keep_going = true;
|
||||
fprintf(stdout, "%s", prompt);
|
||||
fflush(stdout);
|
||||
while (keep_going) {
|
||||
if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
|
||||
// fgets got an error. Just give up.
|
||||
if (result != NULL) {
|
||||
DeleteArray(result);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int len = StrLength(line_buf);
|
||||
if (len > 1 &&
|
||||
line_buf[len - 2] == '\\' &&
|
||||
line_buf[len - 1] == '\n') {
|
||||
// When we read a line that ends with a "\" we remove the escape and
|
||||
// append the remainder.
|
||||
line_buf[len - 2] = '\n';
|
||||
line_buf[len - 1] = 0;
|
||||
len -= 1;
|
||||
} else if ((len > 0) && (line_buf[len - 1] == '\n')) {
|
||||
// Since we read a new line we are done reading the line. This
|
||||
// will exit the loop after copying this buffer into the result.
|
||||
keep_going = false;
|
||||
}
|
||||
if (result == NULL) {
|
||||
// Allocate the initial result and make room for the terminating '\0'
|
||||
result = NewArray<char>(len + 1);
|
||||
} else {
|
||||
// Allocate a new result with enough room for the new addition.
|
||||
int new_len = offset + len + 1;
|
||||
char* new_result = NewArray<char>(new_len);
|
||||
// Copy the existing input into the new array and set the new
|
||||
// array as the result.
|
||||
memcpy(new_result, result, offset * kCharSize);
|
||||
DeleteArray(result);
|
||||
result = new_result;
|
||||
}
|
||||
// Copy the newly read line into the result.
|
||||
memcpy(result + offset, line_buf, len * kCharSize);
|
||||
offset += len;
|
||||
}
|
||||
ASSERT(result != NULL);
|
||||
result[offset] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char* ReadCharsFromFile(const char* filename,
|
||||
int* size,
|
||||
int extra_space,
|
||||
bool verbose) {
|
||||
FILE* file = OS::FOpen(filename, "rb");
|
||||
if (file == NULL || fseek(file, 0, SEEK_END) != 0) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot read from file %s.\n", filename);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the size of the file and rewind it.
|
||||
*size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* result = NewArray<char>(*size + extra_space);
|
||||
for (int i = 0; i < *size;) {
|
||||
int read = static_cast<int>(fread(&result[i], 1, *size - i, file));
|
||||
if (read <= 0) {
|
||||
fclose(file);
|
||||
DeleteArray(result);
|
||||
return NULL;
|
||||
}
|
||||
i += read;
|
||||
}
|
||||
fclose(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
byte* ReadBytes(const char* filename, int* size, bool verbose) {
|
||||
char* chars = ReadCharsFromFile(filename, size, 0, verbose);
|
||||
return reinterpret_cast<byte*>(chars);
|
||||
}
|
||||
|
||||
|
||||
Vector<const char> ReadFile(const char* filename,
|
||||
bool* exists,
|
||||
bool verbose) {
|
||||
int size;
|
||||
char* result = ReadCharsFromFile(filename, &size, 1, verbose);
|
||||
if (!result) {
|
||||
*exists = false;
|
||||
return Vector<const char>::empty();
|
||||
}
|
||||
result[size] = '\0';
|
||||
*exists = true;
|
||||
return Vector<const char>(result, size);
|
||||
}
|
||||
|
||||
|
||||
int WriteCharsToFile(const char* str, int size, FILE* f) {
|
||||
int total = 0;
|
||||
while (total < size) {
|
||||
int write = static_cast<int>(fwrite(str, 1, size - total, f));
|
||||
if (write == 0) {
|
||||
return total;
|
||||
}
|
||||
total += write;
|
||||
str += write;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
int AppendChars(const char* filename,
|
||||
const char* str,
|
||||
int size,
|
||||
bool verbose) {
|
||||
FILE* f = OS::FOpen(filename, "ab");
|
||||
if (f == NULL) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot open file %s for writing.\n", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int written = WriteCharsToFile(str, size, f);
|
||||
fclose(f);
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
int WriteChars(const char* filename,
|
||||
const char* str,
|
||||
int size,
|
||||
bool verbose) {
|
||||
FILE* f = OS::FOpen(filename, "wb");
|
||||
if (f == NULL) {
|
||||
if (verbose) {
|
||||
OS::PrintError("Cannot open file %s for writing.\n", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int written = WriteCharsToFile(str, size, f);
|
||||
fclose(f);
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
int WriteBytes(const char* filename,
|
||||
const byte* bytes,
|
||||
int size,
|
||||
bool verbose) {
|
||||
const char* str = reinterpret_cast<const char*>(bytes);
|
||||
return WriteChars(filename, str, size, verbose);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StringBuilder::AddFormatted(const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
AddFormattedList(format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
|
||||
void StringBuilder::AddFormattedList(const char* format, va_list list) {
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
int n = OS::VSNPrintF(buffer_ + position_, format, list);
|
||||
if (n < 0 || n >= (buffer_.length() - position_)) {
|
||||
position_ = buffer_.length();
|
||||
} else {
|
||||
position_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::MemoryMappedExternalResource(const char* filename)
|
||||
: filename_(NULL),
|
||||
data_(NULL),
|
||||
length_(0),
|
||||
remove_file_on_cleanup_(false) {
|
||||
Init(filename);
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::
|
||||
MemoryMappedExternalResource(const char* filename,
|
||||
bool remove_file_on_cleanup)
|
||||
: filename_(NULL),
|
||||
data_(NULL),
|
||||
length_(0),
|
||||
remove_file_on_cleanup_(remove_file_on_cleanup) {
|
||||
Init(filename);
|
||||
}
|
||||
|
||||
|
||||
MemoryMappedExternalResource::~MemoryMappedExternalResource() {
|
||||
// Release the resources if we had successfully acquired them:
|
||||
if (file_ != NULL) {
|
||||
delete file_;
|
||||
if (remove_file_on_cleanup_) {
|
||||
OS::Remove(filename_);
|
||||
}
|
||||
DeleteArray<char>(filename_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MemoryMappedExternalResource::Init(const char* filename) {
|
||||
file_ = OS::MemoryMappedFile::open(filename);
|
||||
if (file_ != NULL) {
|
||||
filename_ = StrDup(filename);
|
||||
data_ = reinterpret_cast<char*>(file_->memory());
|
||||
length_ = file_->size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const {
|
||||
bool is_ascii = true;
|
||||
|
||||
int line_no = 1;
|
||||
const char* start_of_line = data_;
|
||||
const char* end = data_ + length_;
|
||||
for (const char* p = data_; p < end; p++) {
|
||||
char c = *p;
|
||||
if ((c & 0x80) != 0) {
|
||||
// Non-ascii detected:
|
||||
is_ascii = false;
|
||||
|
||||
// Report the error and abort if appropriate:
|
||||
if (abort_if_failed) {
|
||||
int char_no = static_cast<int>(p - start_of_line) - 1;
|
||||
|
||||
ASSERT(filename_ != NULL);
|
||||
PrintF("\n\n\n"
|
||||
"Abort: Non-Ascii character 0x%.2x in file %s line %d char %d",
|
||||
c, filename_, line_no, char_no);
|
||||
|
||||
// Allow for some context up to kNumberOfLeadingContextChars chars
|
||||
// before the offending non-ascii char to help the user see where
|
||||
// the offending char is.
|
||||
const int kNumberOfLeadingContextChars = 10;
|
||||
const char* err_context = p - kNumberOfLeadingContextChars;
|
||||
if (err_context < data_) {
|
||||
err_context = data_;
|
||||
}
|
||||
// Compute the length of the error context and print it.
|
||||
int err_context_length = static_cast<int>(p - err_context);
|
||||
if (err_context_length != 0) {
|
||||
PrintF(" after \"%.*s\"", err_context_length, err_context);
|
||||
}
|
||||
PrintF(".\n\n\n");
|
||||
OS::Abort();
|
||||
}
|
||||
|
||||
break; // Non-ascii detected. No need to continue scanning.
|
||||
}
|
||||
if (c == '\n') {
|
||||
start_of_line = p;
|
||||
line_no++;
|
||||
}
|
||||
}
|
||||
|
||||
return is_ascii;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2010 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -190,71 +190,6 @@ Vector<const char> ReadFile(const char* filename,
|
||||
bool verbose = true);
|
||||
|
||||
|
||||
// Helper class for building result strings in a character buffer. The
|
||||
// purpose of the class is to use safe operations that checks the
|
||||
// buffer bounds on all operations in debug mode.
|
||||
class StringBuilder {
|
||||
public:
|
||||
// Create a string builder with a buffer of the given size. The
|
||||
// buffer is allocated through NewArray<char> and must be
|
||||
// deallocated by the caller of Finalize().
|
||||
explicit StringBuilder(int size);
|
||||
|
||||
StringBuilder(char* buffer, int size)
|
||||
: buffer_(buffer, size), position_(0) { }
|
||||
|
||||
~StringBuilder() { if (!is_finalized()) Finalize(); }
|
||||
|
||||
int size() const { return buffer_.length(); }
|
||||
|
||||
// Get the current position in the builder.
|
||||
int position() const {
|
||||
ASSERT(!is_finalized());
|
||||
return position_;
|
||||
}
|
||||
|
||||
// Reset the position.
|
||||
void Reset() { position_ = 0; }
|
||||
|
||||
// Add a single character to the builder. It is not allowed to add
|
||||
// 0-characters; use the Finalize() method to terminate the string
|
||||
// instead.
|
||||
void AddCharacter(char c) {
|
||||
ASSERT(c != '\0');
|
||||
ASSERT(!is_finalized() && position_ < buffer_.length());
|
||||
buffer_[position_++] = c;
|
||||
}
|
||||
|
||||
// Add an entire string to the builder. Uses strlen() internally to
|
||||
// compute the length of the input string.
|
||||
void AddString(const char* s);
|
||||
|
||||
// Add the first 'n' characters of the given string 's' to the
|
||||
// builder. The input string must have enough characters.
|
||||
void AddSubstring(const char* s, int n);
|
||||
|
||||
// Add formatted contents to the builder just like printf().
|
||||
void AddFormatted(const char* format, ...);
|
||||
|
||||
// Add formatted contents like printf based on a va_list.
|
||||
void AddFormattedList(const char* format, va_list list);
|
||||
|
||||
// Add character padding to the builder. If count is non-positive,
|
||||
// nothing is added to the builder.
|
||||
void AddPadding(char c, int count);
|
||||
|
||||
// Finalize the string by 0-terminating it and returning the buffer.
|
||||
char* Finalize();
|
||||
|
||||
private:
|
||||
Vector<char> buffer_;
|
||||
int position_;
|
||||
|
||||
bool is_finalized() const { return position_ < 0; }
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||
};
|
||||
|
||||
|
||||
// Copy from ASCII/16bit chars to ASCII/16bit chars.
|
||||
template <typename sourcechar, typename sinkchar>
|
||||
@ -313,6 +248,19 @@ class MemoryMappedExternalResource: public
|
||||
bool remove_file_on_cleanup_;
|
||||
};
|
||||
|
||||
class StringBuilder : public SimpleStringBuilder {
|
||||
public:
|
||||
explicit StringBuilder(int size) : SimpleStringBuilder(size) { }
|
||||
StringBuilder(char* buffer, int size) : SimpleStringBuilder(buffer, size) { }
|
||||
|
||||
// Add formatted contents to the builder just like printf().
|
||||
void AddFormatted(const char* format, ...);
|
||||
|
||||
// Add formatted contents like printf based on a va_list.
|
||||
void AddFormattedList(const char* format, va_list list);
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user