NumberPrototype.cpp
1 /* 2 * Copyright (C) 1999-2000,2003 Harri Porten (porten@kde.org) 3 * Copyright (C) 2007-2020 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 18 * USA 19 * 20 */ 21 22 #include "config.h" 23 #include "NumberPrototype.h" 24 25 #include "BigInteger.h" 26 #include "IntegrityInlines.h" 27 #include "IntlNumberFormat.h" 28 #include "JSCInlines.h" 29 #include "Operations.h" 30 #include "ParseInt.h" 31 #include "Uint16WithFraction.h" 32 #include <wtf/dtoa.h> 33 #include <wtf/Assertions.h> 34 #include <wtf/dtoa/double-conversion.h> 35 36 using DoubleToStringConverter = WTF::double_conversion::DoubleToStringConverter; 37 38 // To avoid conflict with WTF::StringBuilder. 39 typedef WTF::double_conversion::StringBuilder DoubleConversionStringBuilder; 40 41 namespace JSC { 42 43 static JSC_DECLARE_HOST_FUNCTION(numberProtoFuncToLocaleString); 44 static JSC_DECLARE_HOST_FUNCTION(numberProtoFuncToFixed); 45 static JSC_DECLARE_HOST_FUNCTION(numberProtoFuncToExponential); 46 static JSC_DECLARE_HOST_FUNCTION(numberProtoFuncToPrecision); 47 48 } 49 50 #include "NumberPrototype.lut.h" 51 52 namespace JSC { 53 54 const ClassInfo NumberPrototype::s_info = { "Number", &NumberObject::s_info, &numberPrototypeTable, nullptr, CREATE_METHOD_TABLE(NumberPrototype) }; 55 56 /* Source for NumberPrototype.lut.h 57 @begin numberPrototypeTable 58 toLocaleString numberProtoFuncToLocaleString DontEnum|Function 0 59 valueOf numberProtoFuncValueOf DontEnum|Function 0 60 toFixed numberProtoFuncToFixed DontEnum|Function 1 61 toExponential numberProtoFuncToExponential DontEnum|Function 1 62 toPrecision numberProtoFuncToPrecision DontEnum|Function 1 63 @end 64 */ 65 66 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberPrototype); 67 68 NumberPrototype::NumberPrototype(VM& vm, Structure* structure) 69 : NumberObject(vm, structure) 70 { 71 } 72 73 void NumberPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) 74 { 75 Base::finishCreation(vm); 76 setInternalValue(vm, jsNumber(0)); 77 putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->numberProtoToStringFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum)); 78 ASSERT(inherits(vm, info())); 79 globalObject->installNumberPrototypeWatchpoint(this); 80 } 81 82 // ------------------------------ Functions --------------------------- 83 84 static ALWAYS_INLINE bool toThisNumber(VM& vm, JSValue thisValue, double& x) 85 { 86 if (thisValue.isInt32()) { 87 x = thisValue.asInt32(); 88 return true; 89 } 90 91 if (thisValue.isDouble()) { 92 x = thisValue.asDouble(); 93 return true; 94 } 95 96 if (auto* numberObject = jsDynamicCast<NumberObject*>(vm, thisValue)) { 97 Integrity::auditStructureID(vm, numberObject->structureID()); 98 x = numberObject->internalValue().asNumber(); 99 return true; 100 } 101 102 return false; 103 } 104 105 static ALWAYS_INLINE EncodedJSValue throwVMToThisNumberError(JSGlobalObject* globalObject, ThrowScope& scope, JSValue thisValue) 106 { 107 auto typeString = jsTypeStringForValue(globalObject, thisValue)->value(globalObject); 108 scope.assertNoException(); 109 return throwVMTypeError(globalObject, scope, WTF::makeString("thisNumberValue called on incompatible ", typeString)); 110 } 111 112 // The largest finite floating point number is 1.mantissa * 2^(0x7fe-0x3ff). 113 // Since 2^N in binary is a one bit followed by N zero bits. 1 * 2^3ff requires 114 // at most 1024 characters to the left of a decimal point, in base 2 (1025 if 115 // we include a minus sign). For the fraction, a value with an exponent of 0 116 // has up to 52 bits to the right of the decimal point. Each decrement of the 117 // exponent down to a minimum of -0x3fe adds an additional digit to the length 118 // of the fraction. As such the maximum fraction size is 1075 (1076 including 119 // a point). We pick a buffer size such that can simply place the point in the 120 // center of the buffer, and are guaranteed to have enough space in each direction 121 // fo any number of digits an IEEE number may require to represent. 122 typedef char RadixBuffer[2180]; 123 124 static inline char* int52ToStringWithRadix(char* startOfResultString, int64_t int52Value, unsigned radix) 125 { 126 bool negative = false; 127 uint64_t positiveNumber = int52Value; 128 if (int52Value < 0) { 129 negative = true; 130 positiveNumber = -int52Value; 131 } 132 133 do { 134 uint64_t index = positiveNumber % radix; 135 ASSERT(index < sizeof(radixDigits)); 136 *--startOfResultString = radixDigits[index]; 137 positiveNumber /= radix; 138 } while (positiveNumber); 139 if (negative) 140 *--startOfResultString = '-'; 141 142 return startOfResultString; 143 } 144 145 static char* toStringWithRadixInternal(RadixBuffer& buffer, double originalNumber, unsigned radix) 146 { 147 ASSERT(std::isfinite(originalNumber)); 148 ASSERT(radix >= 2 && radix <= 36); 149 150 // Position the decimal point at the center of the string, set 151 // the startOfResultString pointer to point at the decimal point. 152 char* decimalPoint = buffer + sizeof(buffer) / 2; 153 char* startOfResultString = decimalPoint; 154 155 // Extract the sign. 156 bool isNegative = originalNumber < 0; 157 double number = originalNumber; 158 if (std::signbit(originalNumber)) 159 number = -originalNumber; 160 double integerPart = floor(number); 161 162 // Check if the value has a fractional part to convert. 163 double fractionPart = number - integerPart; 164 if (!fractionPart) { 165 *decimalPoint = '\0'; 166 // We do not need to care the negative zero (-0) since it is also converted to "0" in all the radix. 167 if (integerPart < (static_cast<int64_t>(1) << (JSValue::numberOfInt52Bits - 1))) 168 return int52ToStringWithRadix(startOfResultString, static_cast<int64_t>(originalNumber), radix); 169 } else { 170 // We use this to test for odd values in odd radix bases. 171 // Where the base is even, (e.g. 10), to determine whether a value is even we need only 172 // consider the least significant digit. For example, 124 in base 10 is even, because '4' 173 // is even. if the radix is odd, then the radix raised to an integer power is also odd. 174 // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value 175 // is multiplied by an odd number, the result is even if the sum of all digits is even. 176 // 177 // For the integer portion of the result, we only need test whether the integer value is 178 // even or odd. For each digit of the fraction added, we should invert our idea of whether 179 // the number is odd if the new digit is odd. 180 // 181 // Also initialize digit to this value; for even radix values we only need track whether 182 // the last individual digit was odd. 183 bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1; 184 ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2))); 185 bool isOddInOddRadix = integerPartIsOdd; 186 uint32_t digit = integerPartIsOdd; 187 188 // Write the decimal point now. 189 *decimalPoint = '.'; 190 191 // Higher precision representation of the fractional part. 192 Uint16WithFraction fraction(fractionPart); 193 194 bool needsRoundingUp = false; 195 char* endOfResultString = decimalPoint + 1; 196 197 // Calculate the delta from the current number to the next & previous possible IEEE numbers. 198 double nextNumber = nextafter(number, std::numeric_limits<double>::infinity()); 199 double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity()); 200 ASSERT(std::isfinite(nextNumber) && !std::signbit(nextNumber)); 201 ASSERT(std::isfinite(lastNumber) && !std::signbit(lastNumber)); 202 double deltaNextDouble = nextNumber - number; 203 double deltaLastDouble = number - lastNumber; 204 ASSERT(std::isfinite(deltaNextDouble) && !std::signbit(deltaNextDouble)); 205 ASSERT(std::isfinite(deltaLastDouble) && !std::signbit(deltaLastDouble)); 206 207 // We track the delta from the current value to the next, to track how many digits of the 208 // fraction we need to write. For example, if the value we are converting is precisely 209 // 1.2345, so far we have written the digits "1.23" to a string leaving a remainder of 210 // 0.45, and we want to determine whether we can round off, or whether we need to keep 211 // appending digits ('4'). We can stop adding digits provided that then next possible 212 // lower IEEE value is further from 1.23 than the remainder we'd be rounding off (0.45), 213 // which is to say, less than 1.2255. Put another way, the delta between the prior 214 // possible value and this number must be more than 2x the remainder we'd be rounding off 215 // (or more simply half the delta between numbers must be greater than the remainder). 216 // 217 // Similarly we need track the delta to the next possible value, to dertermine whether 218 // to round up. In almost all cases (other than at exponent boundaries) the deltas to 219 // prior and subsequent values are identical, so we don't need track then separately. 220 if (deltaNextDouble != deltaLastDouble) { 221 // Since the deltas are different track them separately. Pre-multiply by 0.5. 222 Uint16WithFraction halfDeltaNext(deltaNextDouble, 1); 223 Uint16WithFraction halfDeltaLast(deltaLastDouble, 1); 224 225 while (true) { 226 // examine the remainder to determine whether we should be considering rounding 227 // up or down. If remainder is precisely 0.5 rounding is to even. 228 int dComparePoint5 = fraction.comparePoint5(); 229 if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { 230 // Check for rounding up; are we closer to the value we'd round off to than 231 // the next IEEE value would be? 232 if (fraction.sumGreaterThanOne(halfDeltaNext)) { 233 needsRoundingUp = true; 234 break; 235 } 236 } else { 237 // Check for rounding down; are we closer to the value we'd round off to than 238 // the prior IEEE value would be? 239 if (fraction < halfDeltaLast) 240 break; 241 } 242 243 ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); 244 // Write a digit to the string. 245 fraction *= radix; 246 digit = fraction.floorAndSubtract(); 247 *endOfResultString++ = radixDigits[digit]; 248 // Keep track whether the portion written is currently even, if the radix is odd. 249 if (digit & 1) 250 isOddInOddRadix = !isOddInOddRadix; 251 252 // Shift the fractions by radix. 253 halfDeltaNext *= radix; 254 halfDeltaLast *= radix; 255 } 256 } else { 257 // This code is identical to that above, except since deltaNextDouble != deltaLastDouble 258 // we don't need to track these two values separately. 259 Uint16WithFraction halfDelta(deltaNextDouble, 1); 260 261 while (true) { 262 int dComparePoint5 = fraction.comparePoint5(); 263 if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) { 264 if (fraction.sumGreaterThanOne(halfDelta)) { 265 needsRoundingUp = true; 266 break; 267 } 268 } else if (fraction < halfDelta) 269 break; 270 271 ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1)); 272 fraction *= radix; 273 digit = fraction.floorAndSubtract(); 274 if (digit & 1) 275 isOddInOddRadix = !isOddInOddRadix; 276 *endOfResultString++ = radixDigits[digit]; 277 278 halfDelta *= radix; 279 } 280 } 281 282 // Check if the fraction needs rounding off (flag set in the loop writing digits, above). 283 if (needsRoundingUp) { 284 // Whilst the last digit is the maximum in the current radix, remove it. 285 // e.g. rounding up the last digit in "12.3999" is the same as rounding up the 286 // last digit in "12.3" - both round up to "12.4". 287 while (endOfResultString[-1] == radixDigits[radix - 1]) 288 --endOfResultString; 289 290 // Radix digits are sequential in ascii/unicode, except for '9' and 'a'. 291 // E.g. the first 'if' case handles rounding 67.89 to 67.8a in base 16. 292 // The 'else if' case handles rounding of all other digits. 293 if (endOfResultString[-1] == '9') 294 endOfResultString[-1] = 'a'; 295 else if (endOfResultString[-1] != '.') 296 ++endOfResultString[-1]; 297 else { 298 // One other possibility - there may be no digits to round up in the fraction 299 // (or all may be been rounded off already), in which case we may need to 300 // round into the integer portion of the number. Remove the decimal point. 301 --endOfResultString; 302 // In order to get here there must have been a non-zero fraction, in which case 303 // there must be at least one bit of the value's mantissa not in use in the 304 // integer part of the number. As such, adding to the integer part should not 305 // be able to lose precision. 306 ASSERT((integerPart + 1) - integerPart == 1); 307 ++integerPart; 308 } 309 } else { 310 // We only need to check for trailing zeros if the value does not get rounded up. 311 while (endOfResultString[-1] == '0') 312 --endOfResultString; 313 } 314 315 *endOfResultString = '\0'; 316 ASSERT(endOfResultString < buffer + sizeof(buffer)); 317 } 318 319 BigInteger units(integerPart); 320 321 // Always loop at least once, to emit at least '0'. 322 do { 323 ASSERT(buffer < startOfResultString); 324 325 // Read a single digit and write it to the front of the string. 326 // Divide by radix to remove one digit from the value. 327 uint32_t digit = units.divide(radix); 328 *--startOfResultString = radixDigits[digit]; 329 } while (!!units); 330 331 // If the number is negative, prepend '-'. 332 if (isNegative) 333 *--startOfResultString = '-'; 334 ASSERT(buffer <= startOfResultString); 335 336 return startOfResultString; 337 } 338 339 static String toStringWithRadixInternal(int32_t number, unsigned radix) 340 { 341 LChar buf[1 + 32]; // Worst case is radix == 2, which gives us 32 digits + sign. 342 LChar* end = std::end(buf); 343 LChar* p = end; 344 345 bool negative = false; 346 uint32_t positiveNumber = number; 347 if (number < 0) { 348 negative = true; 349 positiveNumber = static_cast<uint32_t>(-static_cast<int64_t>(number)); 350 } 351 352 // Always loop at least once, to emit at least '0'. 353 do { 354 uint32_t index = positiveNumber % radix; 355 ASSERT(index < sizeof(radixDigits)); 356 *--p = static_cast<LChar>(radixDigits[index]); 357 positiveNumber /= radix; 358 } while (positiveNumber); 359 360 if (negative) 361 *--p = '-'; 362 363 return String(p, static_cast<unsigned>(end - p)); 364 } 365 366 String toStringWithRadix(double doubleValue, int32_t radix) 367 { 368 ASSERT(2 <= radix && radix <= 36); 369 370 int32_t integerValue = static_cast<int32_t>(doubleValue); 371 if (integerValue == doubleValue) 372 return toStringWithRadixInternal(integerValue, radix); 373 374 if (radix == 10 || !std::isfinite(doubleValue)) 375 return String::number(doubleValue); 376 377 RadixBuffer buffer; 378 return toStringWithRadixInternal(buffer, doubleValue, radix); 379 } 380 381 // toExponential converts a number to a string, always formatting as an exponential. 382 // This method takes an optional argument specifying a number of *decimal places* 383 // to round the significand to (or, put another way, this method optionally rounds 384 // to argument-plus-one significant figures). 385 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncToExponential, (JSGlobalObject* globalObject, CallFrame* callFrame)) 386 { 387 VM& vm = globalObject->vm(); 388 auto scope = DECLARE_THROW_SCOPE(vm); 389 390 double x; 391 if (!toThisNumber(vm, callFrame->thisValue(), x)) 392 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 393 394 JSValue arg = callFrame->argument(0); 395 // Perform ToInteger on the argument before remaining steps. 396 int decimalPlaces = static_cast<int>(arg.toInteger(globalObject)); 397 RETURN_IF_EXCEPTION(scope, { }); 398 399 // Handle NaN and Infinity. 400 if (!std::isfinite(x)) 401 return JSValue::encode(jsNontrivialString(vm, String::number(x))); 402 403 if (decimalPlaces < 0 || decimalPlaces > 100) 404 return throwVMRangeError(globalObject, scope, "toExponential() argument must be between 0 and 100"_s); 405 406 // Round if the argument is not undefined, always format as exponential. 407 NumberToStringBuffer buffer; 408 DoubleConversionStringBuilder builder { &buffer[0], sizeof(buffer) }; 409 const DoubleToStringConverter& converter = DoubleToStringConverter::EcmaScriptConverter(); 410 builder.Reset(); 411 if (arg.isUndefined()) 412 converter.ToExponential(x, -1, &builder); 413 else 414 converter.ToExponential(x, decimalPlaces, &builder); 415 return JSValue::encode(jsString(vm, builder.Finalize())); 416 } 417 418 // toFixed converts a number to a string, always formatting as an a decimal fraction. 419 // This method takes an argument specifying a number of decimal places to round the 420 // significand to. However when converting large values (1e+21 and above) this 421 // method will instead fallback to calling ToString. 422 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncToFixed, (JSGlobalObject* globalObject, CallFrame* callFrame)) 423 { 424 VM& vm = globalObject->vm(); 425 auto scope = DECLARE_THROW_SCOPE(vm); 426 427 double x; 428 if (!toThisNumber(vm, callFrame->thisValue(), x)) 429 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 430 431 int decimalPlaces = static_cast<int>(callFrame->argument(0).toInteger(globalObject)); 432 RETURN_IF_EXCEPTION(scope, { }); 433 if (decimalPlaces < 0 || decimalPlaces > 100) 434 return throwVMRangeError(globalObject, scope, "toFixed() argument must be between 0 and 100"_s); 435 436 // 15.7.4.5.7 states "If x >= 10^21, then let m = ToString(x)" 437 // This also covers Ininity, and structure the check so that NaN 438 // values are also handled by numberToString 439 if (!(fabs(x) < 1e+21)) 440 return JSValue::encode(jsString(vm, String::number(x))); 441 442 // The check above will return false for NaN or Infinity, these will be 443 // handled by numberToString. 444 ASSERT(std::isfinite(x)); 445 446 return JSValue::encode(jsString(vm, String::numberToStringFixedWidth(x, decimalPlaces))); 447 } 448 449 // toPrecision converts a number to a string, taking an argument specifying a 450 // number of significant figures to round the significand to. For positive 451 // exponent, all values that can be represented using a decimal fraction will 452 // be, e.g. when rounding to 3 s.f. any value up to 999 will be formated as a 453 // decimal, whilst 1000 is converted to the exponential representation 1.00e+3. 454 // For negative exponents values >= 1e-6 are formated as decimal fractions, 455 // with smaller values converted to exponential representation. 456 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncToPrecision, (JSGlobalObject* globalObject, CallFrame* callFrame)) 457 { 458 VM& vm = globalObject->vm(); 459 auto scope = DECLARE_THROW_SCOPE(vm); 460 461 double x; 462 if (!toThisNumber(vm, callFrame->thisValue(), x)) 463 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 464 465 JSValue arg = callFrame->argument(0); 466 // To precision called with no argument is treated as ToString. 467 if (arg.isUndefined()) 468 return JSValue::encode(jsString(vm, String::number(x))); 469 470 // Perform ToInteger on the argument before remaining steps. 471 int significantFigures = static_cast<int>(arg.toInteger(globalObject)); 472 RETURN_IF_EXCEPTION(scope, { }); 473 474 // Handle NaN and Infinity. 475 if (!std::isfinite(x)) 476 return JSValue::encode(jsNontrivialString(vm, String::number(x))); 477 478 if (significantFigures < 1 || significantFigures > 100) 479 return throwVMRangeError(globalObject, scope, "toPrecision() argument must be between 1 and 100"_s); 480 481 return JSValue::encode(jsString(vm, String::numberToStringFixedPrecision(x, significantFigures, KeepTrailingZeros))); 482 } 483 484 static ALWAYS_INLINE JSString* int32ToStringInternal(VM& vm, int32_t value, int32_t radix) 485 { 486 ASSERT(!(radix < 2 || radix > 36)); 487 // A negative value casted to unsigned would be bigger than 36 (the max radix). 488 if (static_cast<unsigned>(value) < static_cast<unsigned>(radix)) { 489 ASSERT(value <= 36); 490 ASSERT(value >= 0); 491 return vm.smallStrings.singleCharacterString(radixDigits[value]); 492 } 493 494 if (radix == 10) 495 return jsNontrivialString(vm, vm.numericStrings.add(value)); 496 497 return jsNontrivialString(vm, toStringWithRadixInternal(value, radix)); 498 499 } 500 501 static ALWAYS_INLINE JSString* numberToStringInternal(VM& vm, double doubleValue, int32_t radix) 502 { 503 ASSERT(!(radix < 2 || radix > 36)); 504 505 int32_t integerValue = static_cast<int32_t>(doubleValue); 506 if (integerValue == doubleValue) 507 return int32ToStringInternal(vm, integerValue, radix); 508 509 if (radix == 10) 510 return jsString(vm, vm.numericStrings.add(doubleValue)); 511 512 if (!std::isfinite(doubleValue)) 513 return jsNontrivialString(vm, String::number(doubleValue)); 514 515 RadixBuffer buffer; 516 return jsString(vm, toStringWithRadixInternal(buffer, doubleValue, radix)); 517 } 518 519 JSString* int32ToString(VM& vm, int32_t value, int32_t radix) 520 { 521 return int32ToStringInternal(vm, value, radix); 522 } 523 524 JSString* int52ToString(VM& vm, int64_t value, int32_t radix) 525 { 526 ASSERT(!(radix < 2 || radix > 36)); 527 // A negative value casted to unsigned would be bigger than 36 (the max radix). 528 if (static_cast<uint64_t>(value) < static_cast<uint64_t>(radix)) { 529 ASSERT(value <= 36); 530 ASSERT(value >= 0); 531 return vm.smallStrings.singleCharacterString(radixDigits[value]); 532 } 533 534 if (radix == 10) 535 return jsNontrivialString(vm, vm.numericStrings.add(static_cast<double>(value))); 536 537 // Position the decimal point at the center of the string, set 538 // the startOfResultString pointer to point at the decimal point. 539 RadixBuffer buffer; 540 char* decimalPoint = buffer + sizeof(buffer) / 2; 541 char* startOfResultString = decimalPoint; 542 *decimalPoint = '\0'; 543 544 return jsNontrivialString(vm, int52ToStringWithRadix(startOfResultString, value, radix)); 545 } 546 547 JSString* numberToString(VM& vm, double doubleValue, int32_t radix) 548 { 549 return numberToStringInternal(vm, doubleValue, radix); 550 } 551 552 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame)) 553 { 554 VM& vm = globalObject->vm(); 555 auto scope = DECLARE_THROW_SCOPE(vm); 556 557 double doubleValue; 558 if (!toThisNumber(vm, callFrame->thisValue(), doubleValue)) 559 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 560 561 auto radix = extractToStringRadixArgument(globalObject, callFrame->argument(0), scope); 562 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 563 564 return JSValue::encode(numberToStringInternal(vm, doubleValue, radix)); 565 } 566 567 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncToLocaleString, (JSGlobalObject* globalObject, CallFrame* callFrame)) 568 { 569 VM& vm = globalObject->vm(); 570 auto scope = DECLARE_THROW_SCOPE(vm); 571 572 double x; 573 if (!toThisNumber(vm, callFrame->thisValue(), x)) 574 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 575 576 auto* numberFormat = IntlNumberFormat::create(vm, globalObject->numberFormatStructure()); 577 numberFormat->initializeNumberFormat(globalObject, callFrame->argument(0), callFrame->argument(1)); 578 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 579 RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->format(globalObject, x))); 580 } 581 582 JSC_DEFINE_HOST_FUNCTION(numberProtoFuncValueOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) 583 { 584 VM& vm = globalObject->vm(); 585 auto scope = DECLARE_THROW_SCOPE(vm); 586 587 double x; 588 JSValue thisValue = callFrame->thisValue(); 589 if (!toThisNumber(vm, thisValue, x)) 590 return throwVMToThisNumberError(globalObject, scope, callFrame->thisValue()); 591 return JSValue::encode(jsNumber(x)); 592 } 593 594 int32_t extractToStringRadixArgument(JSGlobalObject* globalObject, JSValue radixValue, ThrowScope& throwScope) 595 { 596 if (radixValue.isUndefined()) 597 return 10; 598 599 if (radixValue.isInt32()) { 600 int32_t radix = radixValue.asInt32(); 601 if (radix >= 2 && radix <= 36) 602 return radix; 603 } else { 604 double radixDouble = radixValue.toInteger(globalObject); 605 RETURN_IF_EXCEPTION(throwScope, 0); 606 if (radixDouble >= 2 && radixDouble <= 36) 607 return static_cast<int32_t>(radixDouble); 608 } 609 610 throwRangeError(globalObject, throwScope, "toString() radix argument must be between 2 and 36"_s); 611 return 0; 612 } 613 614 } // namespace JSC