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
40using DoubleToStringConverter = WTF::double_conversion::DoubleToStringConverter;
41
42// To avoid conflict with WTF::StringBuilder.
43typedef WTF::double_conversion::StringBuilder DoubleConversionStringBuilder;
44
45namespace JSC {
46
47static EncodedJSValue JSC_HOST_CALL numberProtoFuncToString(ExecState*);
48static EncodedJSValue JSC_HOST_CALL numberProtoFuncToLocaleString(ExecState*);
49static EncodedJSValue JSC_HOST_CALL numberProtoFuncToFixed(ExecState*);
50static EncodedJSValue JSC_HOST_CALL numberProtoFuncToExponential(ExecState*);
51static EncodedJSValue JSC_HOST_CALL numberProtoFuncToPrecision(ExecState*);
52
53}
54
55#include "NumberPrototype.lut.h"
56
57namespace JSC {
58
59const 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
71STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(NumberPrototype);
72
73NumberPrototype::NumberPrototype(VM& vm, Structure* structure)
74 : NumberObject(vm, structure)
75{
76}
77
78void 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
89static 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
109static 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
116static 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.
145typedef char RadixBuffer[2180];
146
147static 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
168static 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
362static 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
389String 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).
408EncodedJSValue 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 NumberToStringBuffer buffer;
432 DoubleConversionStringBuilder builder { &buffer[0], sizeof(buffer) };
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, 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.
445EncodedJSValue 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.
482EncodedJSValue 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::numberToStringFixedPrecision(x, significantFigures, KeepTrailingZeros)));
509}
510
511static 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
528static 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
546JSString* int32ToString(VM& vm, int32_t value, int32_t radix)
547{
548 return int32ToStringInternal(vm, value, radix);
549}
550
551JSString* 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
574JSString* numberToString(VM& vm, double doubleValue, int32_t radix)
575{
576 return numberToStringInternal(vm, doubleValue, radix);
577}
578
579EncodedJSValue 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
594EncodedJSValue 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->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
614EncodedJSValue 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
626int32_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
647