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