1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_CONVERSIONS_INL_H_ 6 #define V8_CONVERSIONS_INL_H_ 7 8 #include <float.h> // Required for DBL_MAX and on Win32 for finite() 9 #include <limits.h> // Required for INT_MAX etc. 10 #include <stdarg.h> 11 #include <cmath> 12 #include "src/globals.h" // Required for V8_INFINITY 13 #include "src/unicode-cache-inl.h" 14 15 // ---------------------------------------------------------------------------- 16 // Extra POSIX/ANSI functions for Win32/MSVC. 17 18 #include "src/base/bits.h" 19 #include "src/base/platform/platform.h" 20 #include "src/conversions.h" 21 #include "src/double.h" 22 #include "src/objects-inl.h" 23 #include "src/strtod.h" 24 25 namespace v8 { 26 namespace internal { 27 28 inline double JunkStringValue() { 29 return bit_cast<double, uint64_t>(kQuietNaNMask); 30 } 31 32 33 inline double SignedZero(bool negative) { 34 return negative ? uint64_to_double(Double::kSignMask) : 0.0; 35 } 36 37 38 // The fast double-to-unsigned-int conversion routine does not guarantee 39 // rounding towards zero, or any reasonable value if the argument is larger 40 // than what fits in an unsigned 32-bit integer. 41 inline unsigned int FastD2UI(double x) { 42 // There is no unsigned version of lrint, so there is no fast path 43 // in this function as there is in FastD2I. Using lrint doesn't work 44 // for values of 2^31 and above. 45 46 // Convert "small enough" doubles to uint32_t by fixing the 32 47 // least significant non-fractional bits in the low 32 bits of the 48 // double, and reading them from there. 49 const double k2Pow52 = 4503599627370496.0; 50 bool negative = x < 0; 51 if (negative) { 52 x = -x; 53 } 54 if (x < k2Pow52) { 55 x += k2Pow52; 56 uint32_t result; 57 #ifndef V8_TARGET_BIG_ENDIAN 58 Address mantissa_ptr = reinterpret_cast<Address>(&x); 59 #else 60 Address mantissa_ptr = reinterpret_cast<Address>(&x) + kIntSize; 61 #endif 62 // Copy least significant 32 bits of mantissa. 63 memcpy(&result, mantissa_ptr, sizeof(result)); 64 return negative ? ~result + 1 : result; 65 } 66 // Large number (outside uint32 range), Infinity or NaN. 67 return 0x80000000u; // Return integer indefinite. 68 } 69 70 71 inline float DoubleToFloat32(double x) { 72 // TODO(yangguo): This static_cast is implementation-defined behaviour in C++, 73 // so we may need to do the conversion manually instead to match the spec. 74 volatile float f = static_cast<float>(x); 75 return f; 76 } 77 78 79 inline double DoubleToInteger(double x) { 80 if (std::isnan(x)) return 0; 81 if (!std::isfinite(x) || x == 0) return x; 82 return (x >= 0) ? std::floor(x) : std::ceil(x); 83 } 84 85 86 int32_t DoubleToInt32(double x) { 87 int32_t i = FastD2I(x); 88 if (FastI2D(i) == x) return i; 89 Double d(x); 90 int exponent = d.Exponent(); 91 if (exponent < 0) { 92 if (exponent <= -Double::kSignificandSize) return 0; 93 return d.Sign() * static_cast<int32_t>(d.Significand() >> -exponent); 94 } else { 95 if (exponent > 31) return 0; 96 return d.Sign() * static_cast<int32_t>(d.Significand() << exponent); 97 } 98 } 99 100 101 bool IsSmiDouble(double value) { 102 return !IsMinusZero(value) && value >= Smi::kMinValue && 103 value <= Smi::kMaxValue && value == FastI2D(FastD2I(value)); 104 } 105 106 107 bool IsInt32Double(double value) { 108 return !IsMinusZero(value) && value >= kMinInt && value <= kMaxInt && 109 value == FastI2D(FastD2I(value)); 110 } 111 112 113 bool IsUint32Double(double value) { 114 return !IsMinusZero(value) && value >= 0 && value <= kMaxUInt32 && 115 value == FastUI2D(FastD2UI(value)); 116 } 117 118 119 int32_t NumberToInt32(Object* number) { 120 if (number->IsSmi()) return Smi::cast(number)->value(); 121 return DoubleToInt32(number->Number()); 122 } 123 124 125 uint32_t NumberToUint32(Object* number) { 126 if (number->IsSmi()) return Smi::cast(number)->value(); 127 return DoubleToUint32(number->Number()); 128 } 129 130 131 bool TryNumberToSize(Isolate* isolate, Object* number, size_t* result) { 132 SealHandleScope shs(isolate); 133 if (number->IsSmi()) { 134 int value = Smi::cast(number)->value(); 135 DCHECK(static_cast<unsigned>(Smi::kMaxValue) <= 136 std::numeric_limits<size_t>::max()); 137 if (value >= 0) { 138 *result = static_cast<size_t>(value); 139 return true; 140 } 141 return false; 142 } else { 143 DCHECK(number->IsHeapNumber()); 144 double value = HeapNumber::cast(number)->value(); 145 if (value >= 0 && value <= std::numeric_limits<size_t>::max()) { 146 *result = static_cast<size_t>(value); 147 return true; 148 } else { 149 return false; 150 } 151 } 152 } 153 154 155 size_t NumberToSize(Isolate* isolate, Object* number) { 156 size_t result = 0; 157 bool is_valid = TryNumberToSize(isolate, number, &result); 158 CHECK(is_valid); 159 return result; 160 } 161 162 163 uint32_t DoubleToUint32(double x) { 164 return static_cast<uint32_t>(DoubleToInt32(x)); 165 } 166 167 168 template <class Iterator, class EndMark> 169 bool SubStringEquals(Iterator* current, 170 EndMark end, 171 const char* substring) { 172 DCHECK(**current == *substring); 173 for (substring++; *substring != '\0'; substring++) { 174 ++*current; 175 if (*current == end || **current != *substring) return false; 176 } 177 ++*current; 178 return true; 179 } 180 181 182 // Returns true if a nonspace character has been found and false if the 183 // end was been reached before finding a nonspace character. 184 template <class Iterator, class EndMark> 185 inline bool AdvanceToNonspace(UnicodeCache* unicode_cache, 186 Iterator* current, 187 EndMark end) { 188 while (*current != end) { 189 if (!unicode_cache->IsWhiteSpaceOrLineTerminator(**current)) return true; 190 ++*current; 191 } 192 return false; 193 } 194 195 196 // Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end. 197 template <int radix_log_2, class Iterator, class EndMark> 198 double InternalStringToIntDouble(UnicodeCache* unicode_cache, 199 Iterator current, 200 EndMark end, 201 bool negative, 202 bool allow_trailing_junk) { 203 DCHECK(current != end); 204 205 // Skip leading 0s. 206 while (*current == '0') { 207 ++current; 208 if (current == end) return SignedZero(negative); 209 } 210 211 int64_t number = 0; 212 int exponent = 0; 213 const int radix = (1 << radix_log_2); 214 215 do { 216 int digit; 217 if (*current >= '0' && *current <= '9' && *current < '0' + radix) { 218 digit = static_cast<char>(*current) - '0'; 219 } else if (radix > 10 && *current >= 'a' && *current < 'a' + radix - 10) { 220 digit = static_cast<char>(*current) - 'a' + 10; 221 } else if (radix > 10 && *current >= 'A' && *current < 'A' + radix - 10) { 222 digit = static_cast<char>(*current) - 'A' + 10; 223 } else { 224 if (allow_trailing_junk || 225 !AdvanceToNonspace(unicode_cache, ¤t, end)) { 226 break; 227 } else { 228 return JunkStringValue(); 229 } 230 } 231 232 number = number * radix + digit; 233 int overflow = static_cast<int>(number >> 53); 234 if (overflow != 0) { 235 // Overflow occurred. Need to determine which direction to round the 236 // result. 237 int overflow_bits_count = 1; 238 while (overflow > 1) { 239 overflow_bits_count++; 240 overflow >>= 1; 241 } 242 243 int dropped_bits_mask = ((1 << overflow_bits_count) - 1); 244 int dropped_bits = static_cast<int>(number) & dropped_bits_mask; 245 number >>= overflow_bits_count; 246 exponent = overflow_bits_count; 247 248 bool zero_tail = true; 249 while (true) { 250 ++current; 251 if (current == end || !isDigit(*current, radix)) break; 252 zero_tail = zero_tail && *current == '0'; 253 exponent += radix_log_2; 254 } 255 256 if (!allow_trailing_junk && 257 AdvanceToNonspace(unicode_cache, ¤t, end)) { 258 return JunkStringValue(); 259 } 260 261 int middle_value = (1 << (overflow_bits_count - 1)); 262 if (dropped_bits > middle_value) { 263 number++; // Rounding up. 264 } else if (dropped_bits == middle_value) { 265 // Rounding to even to consistency with decimals: half-way case rounds 266 // up if significant part is odd and down otherwise. 267 if ((number & 1) != 0 || !zero_tail) { 268 number++; // Rounding up. 269 } 270 } 271 272 // Rounding up may cause overflow. 273 if ((number & (static_cast<int64_t>(1) << 53)) != 0) { 274 exponent++; 275 number >>= 1; 276 } 277 break; 278 } 279 ++current; 280 } while (current != end); 281 282 DCHECK(number < ((int64_t)1 << 53)); 283 DCHECK(static_cast<int64_t>(static_cast<double>(number)) == number); 284 285 if (exponent == 0) { 286 if (negative) { 287 if (number == 0) return -0.0; 288 number = -number; 289 } 290 return static_cast<double>(number); 291 } 292 293 DCHECK(number != 0); 294 return std::ldexp(static_cast<double>(negative ? -number : number), exponent); 295 } 296 297 // ES6 18.2.5 parseInt(string, radix) 298 template <class Iterator, class EndMark> 299 double InternalStringToInt(UnicodeCache* unicode_cache, 300 Iterator current, 301 EndMark end, 302 int radix) { 303 const bool allow_trailing_junk = true; 304 const double empty_string_val = JunkStringValue(); 305 306 if (!AdvanceToNonspace(unicode_cache, ¤t, end)) { 307 return empty_string_val; 308 } 309 310 bool negative = false; 311 bool leading_zero = false; 312 313 if (*current == '+') { 314 // Ignore leading sign; skip following spaces. 315 ++current; 316 if (current == end) { 317 return JunkStringValue(); 318 } 319 } else if (*current == '-') { 320 ++current; 321 if (current == end) { 322 return JunkStringValue(); 323 } 324 negative = true; 325 } 326 327 if (radix == 0) { 328 // Radix detection. 329 radix = 10; 330 if (*current == '0') { 331 ++current; 332 if (current == end) return SignedZero(negative); 333 if (*current == 'x' || *current == 'X') { 334 radix = 16; 335 ++current; 336 if (current == end) return JunkStringValue(); 337 } else { 338 leading_zero = true; 339 } 340 } 341 } else if (radix == 16) { 342 if (*current == '0') { 343 // Allow "0x" prefix. 344 ++current; 345 if (current == end) return SignedZero(negative); 346 if (*current == 'x' || *current == 'X') { 347 ++current; 348 if (current == end) return JunkStringValue(); 349 } else { 350 leading_zero = true; 351 } 352 } 353 } 354 355 if (radix < 2 || radix > 36) return JunkStringValue(); 356 357 // Skip leading zeros. 358 while (*current == '0') { 359 leading_zero = true; 360 ++current; 361 if (current == end) return SignedZero(negative); 362 } 363 364 if (!leading_zero && !isDigit(*current, radix)) { 365 return JunkStringValue(); 366 } 367 368 if (base::bits::IsPowerOfTwo32(radix)) { 369 switch (radix) { 370 case 2: 371 return InternalStringToIntDouble<1>( 372 unicode_cache, current, end, negative, allow_trailing_junk); 373 case 4: 374 return InternalStringToIntDouble<2>( 375 unicode_cache, current, end, negative, allow_trailing_junk); 376 case 8: 377 return InternalStringToIntDouble<3>( 378 unicode_cache, current, end, negative, allow_trailing_junk); 379 380 case 16: 381 return InternalStringToIntDouble<4>( 382 unicode_cache, current, end, negative, allow_trailing_junk); 383 384 case 32: 385 return InternalStringToIntDouble<5>( 386 unicode_cache, current, end, negative, allow_trailing_junk); 387 default: 388 UNREACHABLE(); 389 } 390 } 391 392 if (radix == 10) { 393 // Parsing with strtod. 394 const int kMaxSignificantDigits = 309; // Doubles are less than 1.8e308. 395 // The buffer may contain up to kMaxSignificantDigits + 1 digits and a zero 396 // end. 397 const int kBufferSize = kMaxSignificantDigits + 2; 398 char buffer[kBufferSize]; 399 int buffer_pos = 0; 400 while (*current >= '0' && *current <= '9') { 401 if (buffer_pos <= kMaxSignificantDigits) { 402 // If the number has more than kMaxSignificantDigits it will be parsed 403 // as infinity. 404 DCHECK(buffer_pos < kBufferSize); 405 buffer[buffer_pos++] = static_cast<char>(*current); 406 } 407 ++current; 408 if (current == end) break; 409 } 410 411 if (!allow_trailing_junk && 412 AdvanceToNonspace(unicode_cache, ¤t, end)) { 413 return JunkStringValue(); 414 } 415 416 SLOW_DCHECK(buffer_pos < kBufferSize); 417 buffer[buffer_pos] = '\0'; 418 Vector<const char> buffer_vector(buffer, buffer_pos); 419 return negative ? -Strtod(buffer_vector, 0) : Strtod(buffer_vector, 0); 420 } 421 422 // The following code causes accumulating rounding error for numbers greater 423 // than ~2^56. It's explicitly allowed in the spec: "if R is not 2, 4, 8, 10, 424 // 16, or 32, then mathInt may be an implementation-dependent approximation to 425 // the mathematical integer value" (15.1.2.2). 426 427 int lim_0 = '0' + (radix < 10 ? radix : 10); 428 int lim_a = 'a' + (radix - 10); 429 int lim_A = 'A' + (radix - 10); 430 431 // NOTE: The code for computing the value may seem a bit complex at 432 // first glance. It is structured to use 32-bit multiply-and-add 433 // loops as long as possible to avoid loosing precision. 434 435 double v = 0.0; 436 bool done = false; 437 do { 438 // Parse the longest part of the string starting at index j 439 // possible while keeping the multiplier, and thus the part 440 // itself, within 32 bits. 441 unsigned int part = 0, multiplier = 1; 442 while (true) { 443 int d; 444 if (*current >= '0' && *current < lim_0) { 445 d = *current - '0'; 446 } else if (*current >= 'a' && *current < lim_a) { 447 d = *current - 'a' + 10; 448 } else if (*current >= 'A' && *current < lim_A) { 449 d = *current - 'A' + 10; 450 } else { 451 done = true; 452 break; 453 } 454 455 // Update the value of the part as long as the multiplier fits 456 // in 32 bits. When we can't guarantee that the next iteration 457 // will not overflow the multiplier, we stop parsing the part 458 // by leaving the loop. 459 const unsigned int kMaximumMultiplier = 0xffffffffU / 36; 460 uint32_t m = multiplier * radix; 461 if (m > kMaximumMultiplier) break; 462 part = part * radix + d; 463 multiplier = m; 464 DCHECK(multiplier > part); 465 466 ++current; 467 if (current == end) { 468 done = true; 469 break; 470 } 471 } 472 473 // Update the value and skip the part in the string. 474 v = v * multiplier + part; 475 } while (!done); 476 477 if (!allow_trailing_junk && 478 AdvanceToNonspace(unicode_cache, ¤t, end)) { 479 return JunkStringValue(); 480 } 481 482 return negative ? -v : v; 483 } 484 485 486 // Converts a string to a double value. Assumes the Iterator supports 487 // the following operations: 488 // 1. current == end (other ops are not allowed), current != end. 489 // 2. *current - gets the current character in the sequence. 490 // 3. ++current (advances the position). 491 template <class Iterator, class EndMark> 492 double InternalStringToDouble(UnicodeCache* unicode_cache, 493 Iterator current, 494 EndMark end, 495 int flags, 496 double empty_string_val) { 497 // To make sure that iterator dereferencing is valid the following 498 // convention is used: 499 // 1. Each '++current' statement is followed by check for equality to 'end'. 500 // 2. If AdvanceToNonspace returned false then current == end. 501 // 3. If 'current' becomes be equal to 'end' the function returns or goes to 502 // 'parsing_done'. 503 // 4. 'current' is not dereferenced after the 'parsing_done' label. 504 // 5. Code before 'parsing_done' may rely on 'current != end'. 505 if (!AdvanceToNonspace(unicode_cache, ¤t, end)) { 506 return empty_string_val; 507 } 508 509 const bool allow_trailing_junk = (flags & ALLOW_TRAILING_JUNK) != 0; 510 511 // The longest form of simplified number is: "-<significant digits>'.1eXXX\0". 512 const int kBufferSize = kMaxSignificantDigits + 10; 513 char buffer[kBufferSize]; // NOLINT: size is known at compile time. 514 int buffer_pos = 0; 515 516 // Exponent will be adjusted if insignificant digits of the integer part 517 // or insignificant leading zeros of the fractional part are dropped. 518 int exponent = 0; 519 int significant_digits = 0; 520 int insignificant_digits = 0; 521 bool nonzero_digit_dropped = false; 522 523 enum Sign { 524 NONE, 525 NEGATIVE, 526 POSITIVE 527 }; 528 529 Sign sign = NONE; 530 531 if (*current == '+') { 532 // Ignore leading sign. 533 ++current; 534 if (current == end) return JunkStringValue(); 535 sign = POSITIVE; 536 } else if (*current == '-') { 537 ++current; 538 if (current == end) return JunkStringValue(); 539 sign = NEGATIVE; 540 } 541 542 static const char kInfinityString[] = "Infinity"; 543 if (*current == kInfinityString[0]) { 544 if (!SubStringEquals(¤t, end, kInfinityString)) { 545 return JunkStringValue(); 546 } 547 548 if (!allow_trailing_junk && 549 AdvanceToNonspace(unicode_cache, ¤t, end)) { 550 return JunkStringValue(); 551 } 552 553 DCHECK(buffer_pos == 0); 554 return (sign == NEGATIVE) ? -V8_INFINITY : V8_INFINITY; 555 } 556 557 bool leading_zero = false; 558 if (*current == '0') { 559 ++current; 560 if (current == end) return SignedZero(sign == NEGATIVE); 561 562 leading_zero = true; 563 564 // It could be hexadecimal value. 565 if ((flags & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { 566 ++current; 567 if (current == end || !isDigit(*current, 16) || sign != NONE) { 568 return JunkStringValue(); // "0x". 569 } 570 571 return InternalStringToIntDouble<4>(unicode_cache, 572 current, 573 end, 574 false, 575 allow_trailing_junk); 576 577 // It could be an explicit octal value. 578 } else if ((flags & ALLOW_OCTAL) && (*current == 'o' || *current == 'O')) { 579 ++current; 580 if (current == end || !isDigit(*current, 8) || sign != NONE) { 581 return JunkStringValue(); // "0o". 582 } 583 584 return InternalStringToIntDouble<3>(unicode_cache, 585 current, 586 end, 587 false, 588 allow_trailing_junk); 589 590 // It could be a binary value. 591 } else if ((flags & ALLOW_BINARY) && (*current == 'b' || *current == 'B')) { 592 ++current; 593 if (current == end || !isBinaryDigit(*current) || sign != NONE) { 594 return JunkStringValue(); // "0b". 595 } 596 597 return InternalStringToIntDouble<1>(unicode_cache, 598 current, 599 end, 600 false, 601 allow_trailing_junk); 602 } 603 604 // Ignore leading zeros in the integer part. 605 while (*current == '0') { 606 ++current; 607 if (current == end) return SignedZero(sign == NEGATIVE); 608 } 609 } 610 611 bool octal = leading_zero && (flags & ALLOW_IMPLICIT_OCTAL) != 0; 612 613 // Copy significant digits of the integer part (if any) to the buffer. 614 while (*current >= '0' && *current <= '9') { 615 if (significant_digits < kMaxSignificantDigits) { 616 DCHECK(buffer_pos < kBufferSize); 617 buffer[buffer_pos++] = static_cast<char>(*current); 618 significant_digits++; 619 // Will later check if it's an octal in the buffer. 620 } else { 621 insignificant_digits++; // Move the digit into the exponential part. 622 nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; 623 } 624 octal = octal && *current < '8'; 625 ++current; 626 if (current == end) goto parsing_done; 627 } 628 629 if (significant_digits == 0) { 630 octal = false; 631 } 632 633 if (*current == '.') { 634 if (octal && !allow_trailing_junk) return JunkStringValue(); 635 if (octal) goto parsing_done; 636 637 ++current; 638 if (current == end) { 639 if (significant_digits == 0 && !leading_zero) { 640 return JunkStringValue(); 641 } else { 642 goto parsing_done; 643 } 644 } 645 646 if (significant_digits == 0) { 647 // octal = false; 648 // Integer part consists of 0 or is absent. Significant digits start after 649 // leading zeros (if any). 650 while (*current == '0') { 651 ++current; 652 if (current == end) return SignedZero(sign == NEGATIVE); 653 exponent--; // Move this 0 into the exponent. 654 } 655 } 656 657 // There is a fractional part. We don't emit a '.', but adjust the exponent 658 // instead. 659 while (*current >= '0' && *current <= '9') { 660 if (significant_digits < kMaxSignificantDigits) { 661 DCHECK(buffer_pos < kBufferSize); 662 buffer[buffer_pos++] = static_cast<char>(*current); 663 significant_digits++; 664 exponent--; 665 } else { 666 // Ignore insignificant digits in the fractional part. 667 nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; 668 } 669 ++current; 670 if (current == end) goto parsing_done; 671 } 672 } 673 674 if (!leading_zero && exponent == 0 && significant_digits == 0) { 675 // If leading_zeros is true then the string contains zeros. 676 // If exponent < 0 then string was [+-]\.0*... 677 // If significant_digits != 0 the string is not equal to 0. 678 // Otherwise there are no digits in the string. 679 return JunkStringValue(); 680 } 681 682 // Parse exponential part. 683 if (*current == 'e' || *current == 'E') { 684 if (octal) return JunkStringValue(); 685 ++current; 686 if (current == end) { 687 if (allow_trailing_junk) { 688 goto parsing_done; 689 } else { 690 return JunkStringValue(); 691 } 692 } 693 char sign = '+'; 694 if (*current == '+' || *current == '-') { 695 sign = static_cast<char>(*current); 696 ++current; 697 if (current == end) { 698 if (allow_trailing_junk) { 699 goto parsing_done; 700 } else { 701 return JunkStringValue(); 702 } 703 } 704 } 705 706 if (current == end || *current < '0' || *current > '9') { 707 if (allow_trailing_junk) { 708 goto parsing_done; 709 } else { 710 return JunkStringValue(); 711 } 712 } 713 714 const int max_exponent = INT_MAX / 2; 715 DCHECK(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); 716 int num = 0; 717 do { 718 // Check overflow. 719 int digit = *current - '0'; 720 if (num >= max_exponent / 10 721 && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { 722 num = max_exponent; 723 } else { 724 num = num * 10 + digit; 725 } 726 ++current; 727 } while (current != end && *current >= '0' && *current <= '9'); 728 729 exponent += (sign == '-' ? -num : num); 730 } 731 732 if (!allow_trailing_junk && 733 AdvanceToNonspace(unicode_cache, ¤t, end)) { 734 return JunkStringValue(); 735 } 736 737 parsing_done: 738 exponent += insignificant_digits; 739 740 if (octal) { 741 return InternalStringToIntDouble<3>(unicode_cache, 742 buffer, 743 buffer + buffer_pos, 744 sign == NEGATIVE, 745 allow_trailing_junk); 746 } 747 748 if (nonzero_digit_dropped) { 749 buffer[buffer_pos++] = '1'; 750 exponent--; 751 } 752 753 SLOW_DCHECK(buffer_pos < kBufferSize); 754 buffer[buffer_pos] = '\0'; 755 756 double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); 757 return (sign == NEGATIVE) ? -converted : converted; 758 } 759 760 } // namespace internal 761 } // namespace v8 762 763 #endif // V8_CONVERSIONS_INL_H_ 764