Index: src/conversions.cc |
=================================================================== |
--- src/conversions.cc (revision 4248) |
+++ src/conversions.cc (working copy) |
@@ -26,7 +26,6 @@ |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#include <stdarg.h> |
-#include <limits.h> |
#include "v8.h" |
@@ -93,66 +92,74 @@ |
} |
-namespace { |
+static inline void ReleaseCString(const char* original, const char* str) { |
+} |
-// C++-style iterator adaptor for StringInputBuffer |
-// (unlike C++ iterators the end-marker has different type). |
-class StringInputBufferIterator { |
- public: |
- class EndMarker {}; |
- explicit StringInputBufferIterator(StringInputBuffer* buffer); |
+static inline void ReleaseCString(String* original, const char* str) { |
+ DeleteArray(const_cast<char *>(str)); |
+} |
- 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_; |
-}; |
+static inline bool IsSpace(const char* str, int index) { |
+ ASSERT(index >= 0 && index < StrLength(str)); |
+ return Scanner::kIsWhiteSpace.get(str[index]); |
+} |
-StringInputBufferIterator::StringInputBufferIterator( |
- StringInputBuffer* buffer) : buffer_(buffer) { |
- ++(*this); |
+static inline bool IsSpace(String* str, int index) { |
+ return Scanner::kIsWhiteSpace.get(str->Get(index)); |
} |
-int StringInputBufferIterator::operator*() const { |
- return current_; |
-} |
- |
-void StringInputBufferIterator::operator++() { |
- end_ = !buffer_->has_more(); |
- if (!end_) { |
- current_ = buffer_->GetNext(); |
- } |
+static inline bool SubStringEquals(const char* str, |
+ int index, |
+ const char* other) { |
+ return strncmp(str + index, other, strlen(other)) != 0; |
} |
-} |
-static inline void ReleaseCString(const char* original, const char* str) { |
+static inline bool SubStringEquals(String* str, int index, const char* other) { |
+ HandleScope scope; |
+ int str_length = str->length(); |
+ int other_length = StrLength(other); |
+ int end = index + other_length < str_length ? |
+ index + other_length : |
+ str_length; |
+ Handle<String> substring = |
+ Factory::NewSubString(Handle<String>(str), index, end); |
+ return substring->IsEqualTo(Vector<const char>(other, other_length)); |
} |
-static inline void ReleaseCString(String* original, const char* str) { |
- DeleteArray(const_cast<char *>(str)); |
-} |
+// Check if a string should be parsed as an octal number. The string |
+// can be either a char* or a String*. |
+template<class S> |
+static bool ShouldParseOctal(S* s, int i) { |
+ int index = i; |
+ int len = GetLength(s); |
+ if (index < len && GetChar(s, index) != '0') return false; |
+ // If the first real character (following '0') is not an octal |
+ // digit, bail out early. This also takes care of numbers of the |
+ // forms 0.xxx and 0exxx by not allowing the first 0 to be |
+ // interpreted as an octal. |
+ index++; |
+ if (index < len) { |
+ int d = GetChar(s, index) - '0'; |
+ if (d < 0 || d > 7) return false; |
+ } else { |
+ return false; |
+ } |
-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; |
+ // Traverse all digits (including the first). If there is an octal |
+ // prefix which is not a part of a longer decimal prefix, we return |
+ // true. Otherwise, false is returned. |
+ while (index < len) { |
+ int d = GetChar(s, index++) - '0'; |
+ if (d == 8 || d == 9) return false; |
+ if (d < 0 || d > 7) return true; |
} |
- ++*current; |
return true; |
} |
@@ -255,364 +262,95 @@ |
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(Iterator* current, EndMark end) { |
- while (*current != end) { |
- if (!Scanner::kIsWhiteSpace.get(**current)) return true; |
- ++*current; |
- } |
- return false; |
-} |
- |
- |
-template <class Iterator, class EndMark> |
-static double InternalHexidecimalStringToDouble(Iterator current, |
- EndMark end, |
- char* buffer, |
- bool allow_trailing_junk) { |
- ASSERT(current != end); |
- // We reuse the buffer of InternalStringToDouble. Since hexidecimal |
- // numbers may have much less digits than decimal the buffer won't overflow. |
- int significant_digits = 0; |
- int insignificant_digits = 0; |
- bool leading_zero = false; |
- // Hexidecomal may have (52) / 4 + 1 significant digit. Mean of 2 |
- // hexidecimal may have n + 1. |
- const int max_significant_digits = (52) / 4 + 2; |
- int buffer_pos = 0; |
- bool nonzero_digit_dropped = false; |
- |
- // Skip leading 0s. |
- while (*current == '0') { |
- leading_zero = true; |
- ++current; |
- if (current == end) return 0; |
- } |
- |
- int begin_pos = buffer_pos; |
- while ((*current >= '0' && *current <= '9') |
- || (*current >= 'a' && *current <= 'f') |
- || (*current >= 'A' && *current <= 'F')) { |
- if (significant_digits <= max_significant_digits) { |
- buffer[buffer_pos++] = static_cast<char>(*current); |
- significant_digits++; |
- } else { |
- insignificant_digits++; |
- nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; |
- } |
- ++current; |
- if (current == end) break; |
- } |
- |
- if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
- return JUNK_STRING_VALUE; |
- } |
- |
- if (significant_digits == 0) { |
- return leading_zero ? 0 : JUNK_STRING_VALUE; |
- } |
- |
- if (nonzero_digit_dropped) { |
- ASSERT(insignificant_digits > 0); |
- insignificant_digits--; |
- buffer[buffer_pos++] = '1'; |
- } |
- |
- buffer[buffer_pos] = '\0'; |
- |
- double result; |
- StringToInt(buffer, begin_pos, 16, &result); |
- if (insignificant_digits > 0) { |
- // Multiplying by power of 2 doesn't cause a loss of precision. |
- result *= pow(16.0, insignificant_digits); |
- } |
- return result; |
-} |
- |
- |
-// 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(Iterator current, |
- EndMark end, |
+// Convert a string to a double value. The string can be either a |
+// char* or a String*. |
+template<class S> |
+static double InternalStringToDouble(S* str, |
int flags, |
double empty_string_val) { |
- // To make sure that iterator unreferencing 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 unreferenced after the 'parsing_done' label. |
- // 5. Code before 'parsing_done' may rely on 'current != end'. |
- if (!AdvanceToNonspace(¤t, end)) return empty_string_val; |
+ double result = 0.0; |
+ int index = 0; |
- const bool allow_trailing_junk = (flags & ALLOW_TRAILING_JUNK) != 0; |
+ int len = GetLength(str); |
- // Insignificant digits will be removed. |
- const int max_significant_digits = 772; |
- // The longest form of simplified number is: "-<significant digits>'.1eXXX\0". |
- const int buffer_size = max_significant_digits + 10; |
- char buffer[buffer_size]; // NOLINT: size is known at compile time. |
- int buffer_pos = 0; |
+ // Skip leading spaces. |
+ while ((index < len) && IsSpace(str, index)) index++; |
- // 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; |
+ // Is the string empty? |
+ if (index >= len) return empty_string_val; |
- double signed_zero = 0.0; |
+ // Get the first character. |
+ uint16_t first = GetChar(str, index); |
- if (*current == '+') { |
- // Ignore leading sign; Skip following spaces. |
- ++current; |
- if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; |
- } else if (*current == '-') { |
- buffer[buffer_pos++] = '-'; |
- ++current; |
- if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; |
- signed_zero = -0.0; |
+ // Numbers can only start with '-', '+', '.', 'I' (Infinity), or a digit. |
+ if (first != '-' && first != '+' && first != '.' && first != 'I' && |
+ (first > '9' || first < '0')) { |
+ return JUNK_STRING_VALUE; |
} |
- static const char infinity_symbol[] = "Infinity"; |
- if (*current == infinity_symbol[0]) { |
- if (!SubStringEquals(¤t, end, infinity_symbol)) { |
- return JUNK_STRING_VALUE; |
- } |
- |
- if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { |
- return JUNK_STRING_VALUE; |
- } |
- |
- return (buffer_pos > 0 && buffer[0] == '-') ? -V8_INFINITY : V8_INFINITY; |
+ // Compute sign of result based on first character. |
+ int sign = 1; |
+ if (first == '-') { |
+ sign = -1; |
+ index++; |
+ // String only containing a '-' are junk chars. |
+ if (index == len) return JUNK_STRING_VALUE; |
} |
- bool leading_zero = false; |
- if (*current == '0') { |
- ++current; |
- if (current == end) return signed_zero; |
- |
- leading_zero = true; |
- |
- // It could be hexadecimal value. |
- if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { |
- ++current; |
- if (current == end) return JUNK_STRING_VALUE; // "0x". |
- |
- double result = InternalHexidecimalStringToDouble(current, |
- end, |
- buffer + buffer_pos, |
- allow_trailing_junk); |
- return (buffer_pos > 0 && buffer[0] == '-') ? -result : result; |
- } |
- |
- // Ignore leading zeros in the integer part. |
- while (*current == '0') { |
- ++current; |
- if (current == end) return signed_zero; |
- } |
- } |
- |
- 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 < max_significant_digits) { |
- ASSERT(buffer_pos < buffer_size); |
- buffer[buffer_pos++] = static_cast<char>(*current); |
- significant_digits++; |
- // Will later check if it's an octal in the buffer. |
+ // do we have a hex number? |
+ // (since the string is 0-terminated, it's ok to look one char beyond the end) |
+ if ((flags & ALLOW_HEX) != 0 && |
+ (index + 1) < len && |
+ GetChar(str, index) == '0' && |
+ (GetChar(str, index + 1) == 'x' || GetChar(str, index + 1) == 'X')) { |
+ index += 2; |
+ index = StringToInt(str, index, 16, &result); |
+ } else if ((flags & ALLOW_OCTALS) != 0 && ShouldParseOctal(str, index)) { |
+ // NOTE: We optimistically try to parse the number as an octal (if |
+ // we're allowed to), even though this is not as dictated by |
+ // ECMA-262. The reason for doing this is compatibility with IE and |
+ // Firefox. |
+ index = StringToInt(str, index, 8, &result); |
+ } else { |
+ const char* cstr = GetCString(str, index); |
+ const char* end; |
+ // Optimistically parse the number and then, if that fails, |
+ // check if it might have been {+,-,}Infinity. |
+ result = gay_strtod(cstr, &end); |
+ ReleaseCString(str, cstr); |
+ if (result != 0.0 || end != cstr) { |
+ // It appears that strtod worked |
+ index += static_cast<int>(end - cstr); |
} 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 (*current == '.') { |
- ASSERT(buffer_pos < buffer_size); |
- ++current; |
- if (current == end) { |
- if (significant_digits == 0 && !leading_zero) { |
+ // Check for {+,-,}Infinity |
+ bool is_negative = (GetChar(str, index) == '-'); |
+ if (GetChar(str, index) == '+' || GetChar(str, index) == '-') |
+ index++; |
+ if (!SubStringEquals(str, index, "Infinity")) |
return JUNK_STRING_VALUE; |
- } else { |
- goto parsing_done; |
- } |
+ result = is_negative ? -V8_INFINITY : V8_INFINITY; |
+ index += 8; |
} |
- buffer[buffer_pos++] = '.'; |
- |
- 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 signed_zero; |
- exponent--; // Move this 0 into the exponent. |
- } |
- } |
- |
- // There is the fractional part. |
- while (*current >= '0' && *current <= '9') { |
- if (significant_digits < max_significant_digits) { |
- ASSERT(buffer_pos < buffer_size); |
- buffer[buffer_pos++] = static_cast<char>(*current); |
- significant_digits++; |
- } 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 is no digits in the string. |
- return JUNK_STRING_VALUE; |
+ if ((flags & ALLOW_TRAILING_JUNK) == 0) { |
+ // skip trailing spaces |
+ while ((index < len) && IsSpace(str, index)) index++; |
+ // string ending with junk? |
+ if (index < len) 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(¤t, end)) { |
- return JUNK_STRING_VALUE; |
- } |
- |
- parsing_done: |
- exponent += insignificant_digits; |
- |
- if (octal) { |
- buffer[buffer_pos] = '\0'; |
- // ALLOW_OCTALS has set and there is no '8' and '9' in insignificant |
- // digits. Check significant digits now. |
- char sign = '+'; |
- const char* s = buffer; |
- if (*s == '-' || *s == '+') sign = *s++; |
- |
- double result; |
- s += StringToInt(s, 0, 8, &result); |
- if (!allow_trailing_junk && *s != '\0') return JUNK_STRING_VALUE; |
- |
- if (sign == '-') result = -result; |
- if (insignificant_digits > 0) { |
- result *= pow(8.0, insignificant_digits); |
- } |
- return result; |
- } |
- |
- if (nonzero_digit_dropped) { |
- if (insignificant_digits > 0) buffer[buffer_pos++] = '.'; |
- buffer[buffer_pos++] = '1'; |
- } |
- |
- if (exponent != 0) { |
- ASSERT(buffer_pos < buffer_size); |
- buffer[buffer_pos++] = 'e'; |
- if (exponent < 0) { |
- ASSERT(buffer_pos < buffer_size); |
- buffer[buffer_pos++] = '-'; |
- exponent = -exponent; |
- } |
- if (exponent > 999) exponent = 999; // Result will be Infinity or 0 or -0. |
- |
- const int exp_digits = 3; |
- for (int i = 0; i < exp_digits; i++) { |
- buffer[buffer_pos + exp_digits - 1 - i] = '0' + exponent % 10; |
- exponent /= 10; |
- } |
- ASSERT(exponent == 0); |
- buffer_pos += exp_digits; |
- } |
- |
- ASSERT(buffer_pos < buffer_size); |
- buffer[buffer_pos] = '\0'; |
- |
- return gay_strtod(buffer, NULL); |
+ return sign * result; |
} |
+ |
double StringToDouble(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(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(begin, end, flags, empty_string_val); |
- } else { |
- StringInputBuffer buffer(str); |
- return InternalStringToDouble(StringInputBufferIterator(&buffer), |
- StringInputBufferIterator::EndMarker(), |
- flags, |
- empty_string_val); |
- } |
+ return InternalStringToDouble(str, flags, empty_string_val); |
} |
double StringToDouble(const char* str, int flags, double empty_string_val) { |
- const char* end = str + StrLength(str); |
- |
- return InternalStringToDouble(str, end, flags, empty_string_val); |
+ return InternalStringToDouble(str, flags, empty_string_val); |
} |