/ runtime / NumberPrototype.cpp
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