1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * JS number type and wrapper class.
13 #include "mozilla/double-conversion.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/PodOperations.h"
16 #include "mozilla/RangedPtr.h"
18 #ifdef HAVE_LOCALECONV
31 #include "js/Conversions.h"
32 #include "vm/GlobalObject.h"
33 #include "vm/StringBuffer.h"
35 #include "jsatominlines.h"
37 #include "vm/NativeObject-inl.h"
38 #include "vm/NumberObject-inl.h"
39 #include "vm/String-inl.h"
42 using namespace js::types
;
45 using mozilla::ArrayLength
;
46 using mozilla::MinNumberValue
;
47 using mozilla::NegativeInfinity
;
48 using mozilla::PodCopy
;
49 using mozilla::PositiveInfinity
;
50 using mozilla::RangedPtr
;
52 using JS::AutoCheckCannotGC
;
60 * If we're accumulating a decimal number and the number is >= 2^53, then the
61 * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
62 * Call js_strtod_harder to get the correct answer.
64 template <typename CharT
>
66 ComputeAccurateDecimalInteger(ExclusiveContext
* cx
, const CharT
* start
, const CharT
* end
,
69 size_t length
= end
- start
;
70 ScopedJSFreePtr
<char> cstr(cx
->pod_malloc
<char>(length
+ 1));
74 for (size_t i
= 0; i
< length
; i
++) {
75 char c
= char(start
[i
]);
76 MOZ_ASSERT(('0' <= c
&& c
<= '9') || ('a' <= c
&& c
<= 'z') || ('A' <= c
&& c
<= 'Z'));
83 *dp
= js_strtod_harder(cx
->dtoaState(), cstr
, &estr
, &err
);
84 if (err
== JS_DTOA_ENOMEM
) {
85 js_ReportOutOfMemory(cx
);
94 template <typename CharT
>
95 class BinaryDigitReader
97 const int base
; /* Base of number; must be a power of 2 */
98 int digit
; /* Current digit value in radix given by base */
99 int digitMask
; /* Mask to extract the next bit from digit */
100 const CharT
* start
; /* Pointer to the remaining digits */
101 const CharT
* end
; /* Pointer to first non-digit */
104 BinaryDigitReader(int base
, const CharT
* start
, const CharT
* end
)
105 : base(base
), digit(0), digitMask(0), start(start
), end(end
)
109 /* Return the next binary digit from the number, or -1 if done. */
111 if (digitMask
== 0) {
116 MOZ_ASSERT(('0' <= c
&& c
<= '9') || ('a' <= c
&& c
<= 'z') || ('A' <= c
&& c
<= 'Z'));
117 if ('0' <= c
&& c
<= '9')
119 else if ('a' <= c
&& c
<= 'z')
120 digit
= c
- 'a' + 10;
122 digit
= c
- 'A' + 10;
123 digitMask
= base
>> 1;
126 int bit
= (digit
& digitMask
) != 0;
132 } /* anonymous namespace */
135 * The fast result might also have been inaccurate for power-of-two bases. This
136 * happens if the addition in value * 2 + digit causes a round-down to an even
137 * least significant mantissa bit when the first dropped bit is a one. If any
138 * of the following digits in the number (which haven't been added in yet) are
139 * nonzero, then the correct action would have been to round up instead of
140 * down. An example occurs when reading the number 0x1000000000000081, which
141 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
143 template <typename CharT
>
145 ComputeAccurateBinaryBaseInteger(const CharT
* start
, const CharT
* end
, int base
)
147 BinaryDigitReader
<CharT
> bdr(base
, start
, end
);
149 /* Skip leading zeroes. */
152 bit
= bdr
.nextDigit();
155 MOZ_ASSERT(bit
== 1); // guaranteed by Get{Prefix,Decimal}Integer
157 /* Gather the 53 significant bits (including the leading 1). */
159 for (int j
= 52; j
> 0; j
--) {
160 bit
= bdr
.nextDigit();
163 value
= value
* 2 + bit
;
166 /* bit2 is the 54th bit (the first dropped from the mantissa). */
167 int bit2
= bdr
.nextDigit();
170 int sticky
= 0; /* sticky is 1 if any bit beyond the 54th is 1 */
173 while ((bit3
= bdr
.nextDigit()) >= 0) {
177 value
+= bit2
& (bit
| sticky
);
184 template <typename CharT
>
186 js::ParseDecimalNumber(const mozilla::Range
<const CharT
> chars
)
188 MOZ_ASSERT(chars
.length() > 0);
190 RangedPtr
<const CharT
> s
= chars
.start(), end
= chars
.end();
193 MOZ_ASSERT('0' <= c
&& c
<= '9');
194 uint8_t digit
= c
- '0';
195 uint64_t next
= dec
* 10 + digit
;
196 MOZ_ASSERT(next
< DOUBLE_INTEGRAL_PRECISION_LIMIT
,
197 "next value won't be an integrally-precise double");
200 return static_cast<double>(dec
);
204 js::ParseDecimalNumber(const mozilla::Range
<const Latin1Char
> chars
);
207 js::ParseDecimalNumber(const mozilla::Range
<const char16_t
> chars
);
209 template <typename CharT
>
211 js::GetPrefixInteger(ExclusiveContext
* cx
, const CharT
* start
, const CharT
* end
, int base
,
212 const CharT
** endp
, double* dp
)
214 MOZ_ASSERT(start
<= end
);
215 MOZ_ASSERT(2 <= base
&& base
<= 36);
217 const CharT
* s
= start
;
219 for (; s
< end
; s
++) {
222 if ('0' <= c
&& c
<= '9')
224 else if ('a' <= c
&& c
<= 'z')
225 digit
= c
- 'a' + 10;
226 else if ('A' <= c
&& c
<= 'Z')
227 digit
= c
- 'A' + 10;
232 d
= d
* base
+ digit
;
238 /* If we haven't reached the limit of integer precision, we're done. */
239 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
)
243 * Otherwise compute the correct integer from the prefix of valid digits
244 * if we're computing for base ten or a power of two. Don't worry about
245 * other bases; see 15.1.2.2 step 13.
248 return ComputeAccurateDecimalInteger(cx
, start
, s
, dp
);
250 if ((base
& (base
- 1)) == 0)
251 *dp
= ComputeAccurateBinaryBaseInteger(start
, s
, base
);
257 js::GetPrefixInteger(ExclusiveContext
* cx
, const char16_t
* start
, const char16_t
* end
, int base
,
258 const char16_t
** endp
, double* dp
);
261 js::GetPrefixInteger(ExclusiveContext
* cx
, const Latin1Char
* start
, const Latin1Char
* end
,
262 int base
, const Latin1Char
** endp
, double* dp
);
265 js::GetDecimalInteger(ExclusiveContext
* cx
, const char16_t
* start
, const char16_t
* end
, double* dp
)
267 MOZ_ASSERT(start
<= end
);
269 const char16_t
* s
= start
;
271 for (; s
< end
; s
++) {
273 MOZ_ASSERT('0' <= c
&& c
<= '9');
280 // If we haven't reached the limit of integer precision, we're done.
281 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
)
284 // Otherwise compute the correct integer from the prefix of valid digits.
285 return ComputeAccurateDecimalInteger(cx
, start
, s
, dp
);
289 num_parseFloat(JSContext
* cx
, unsigned argc
, Value
* vp
)
291 CallArgs args
= CallArgsFromVp(argc
, vp
);
293 if (args
.length() == 0) {
294 args
.rval().setNaN();
298 JSString
* str
= ToString
<CanGC
>(cx
, args
[0]);
302 JSLinearString
* linear
= str
->ensureLinear(cx
);
307 AutoCheckCannotGC nogc
;
308 if (linear
->hasLatin1Chars()) {
309 const Latin1Char
* begin
= linear
->latin1Chars(nogc
);
310 const Latin1Char
* end
;
311 if (!js_strtod(cx
, begin
, begin
+ linear
->length(), &end
, &d
))
316 const char16_t
* begin
= linear
->twoByteChars(nogc
);
318 if (!js_strtod(cx
, begin
, begin
+ linear
->length(), &end
, &d
))
324 args
.rval().setDouble(d
);
328 template <typename CharT
>
330 ParseIntImpl(JSContext
* cx
, const CharT
* chars
, size_t length
, bool stripPrefix
, int32_t radix
,
334 const CharT
* end
= chars
+ length
;
335 const CharT
* s
= SkipSpace(chars
, end
);
337 MOZ_ASSERT(chars
<= s
);
338 MOZ_ASSERT(s
<= end
);
341 bool negative
= (s
!= end
&& s
[0] == '-');
344 if (s
!= end
&& (s
[0] == '-' || s
[0] == '+'))
349 if (end
- s
>= 2 && s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X')) {
356 const CharT
* actualEnd
;
358 if (!GetPrefixInteger(cx
, s
, end
, radix
, &actualEnd
, &d
))
364 *res
= negative
? -d
: d
;
370 js::num_parseInt(JSContext
* cx
, unsigned argc
, Value
* vp
)
372 CallArgs args
= CallArgsFromVp(argc
, vp
);
374 /* Fast paths and exceptional cases. */
375 if (args
.length() == 0) {
376 args
.rval().setNaN();
380 if (args
.length() == 1 ||
381 (args
[1].isInt32() && (args
[1].toInt32() == 0 || args
[1].toInt32() == 10))) {
382 if (args
[0].isInt32()) {
383 args
.rval().set(args
[0]);
388 * Step 1 is |inputString = ToString(string)|. When string >=
389 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
390 * the word, which would mean the result of parseInt(string) should be |N|.
392 * To preserve this behaviour, we can't use the fast-path when string >
393 * 1e21, or else the result would be |NeM|.
395 * The same goes for values smaller than 1.0e-6, because the string would be in
396 * the form of "Ne-M".
398 if (args
[0].isDouble()) {
399 double d
= args
[0].toDouble();
400 if (1.0e-6 < d
&& d
< 1.0e21
) {
401 args
.rval().setNumber(floor(d
));
404 if (-1.0e21
< d
&& d
< -1.0e-6) {
405 args
.rval().setNumber(-floor(-d
));
409 args
.rval().setInt32(0);
416 RootedString
inputString(cx
, ToString
<CanGC
>(cx
, args
[0]));
419 args
[0].setString(inputString
);
422 bool stripPrefix
= true;
424 if (!args
.hasDefined(1)) {
427 if (!ToInt32(cx
, args
[1], &radix
))
432 if (radix
< 2 || radix
> 36) {
433 args
.rval().setNaN();
441 JSLinearString
* linear
= inputString
->ensureLinear(cx
);
445 AutoCheckCannotGC nogc
;
446 size_t length
= inputString
->length();
448 if (linear
->hasLatin1Chars()) {
449 if (!ParseIntImpl(cx
, linear
->latin1Chars(nogc
), length
, stripPrefix
, radix
, &number
))
452 if (!ParseIntImpl(cx
, linear
->twoByteChars(nogc
), length
, stripPrefix
, radix
, &number
))
456 args
.rval().setNumber(number
);
460 static const JSFunctionSpec number_functions
[] = {
461 JS_SELF_HOSTED_FN(js_isNaN_str
, "Global_isNaN", 1,0),
462 JS_SELF_HOSTED_FN(js_isFinite_str
, "Global_isFinite", 1,0),
463 JS_FN(js_parseFloat_str
, num_parseFloat
, 1,0),
464 JS_FN(js_parseInt_str
, num_parseInt
, 2,0),
468 const Class
NumberObject::class_
= {
470 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number
)
474 Number(JSContext
* cx
, unsigned argc
, Value
* vp
)
476 CallArgs args
= CallArgsFromVp(argc
, vp
);
478 /* Sample JS_CALLEE before clobbering. */
479 bool isConstructing
= args
.isConstructing();
481 if (args
.length() > 0) {
482 if (!ToNumber(cx
, args
[0]))
484 args
.rval().set(args
[0]);
486 args
.rval().setInt32(0);
492 JSObject
* obj
= NumberObject::create(cx
, args
.rval().toNumber());
495 args
.rval().setObject(*obj
);
499 MOZ_ALWAYS_INLINE
bool
500 IsNumber(HandleValue v
)
502 return v
.isNumber() || (v
.isObject() && v
.toObject().is
<NumberObject
>());
506 Extract(const Value
& v
)
510 return v
.toObject().as
<NumberObject
>().unbox();
514 MOZ_ALWAYS_INLINE
bool
515 num_toSource_impl(JSContext
* cx
, CallArgs args
)
517 double d
= Extract(args
.thisv());
520 if (!sb
.append("(new Number(") ||
521 !NumberValueToStringBuffer(cx
, NumberValue(d
), sb
) ||
527 JSString
* str
= sb
.finishString();
530 args
.rval().setString(str
);
535 num_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
)
537 CallArgs args
= CallArgsFromVp(argc
, vp
);
538 return CallNonGenericMethod
<IsNumber
, num_toSource_impl
>(cx
, args
);
542 ToCStringBuf::ToCStringBuf() : dbuf(nullptr)
544 static_assert(sbufSize
>= DTOSTR_STANDARD_BUFFER_SIZE
,
545 "builtin space must be large enough to store even the "
546 "longest string produced by a conversion");
549 ToCStringBuf::~ToCStringBuf()
556 LookupDtoaCache(ExclusiveContext
* cx
, double d
)
558 if (JSCompartment
* comp
= cx
->compartment()) {
559 if (JSFlatString
* str
= comp
->dtoaCache
.lookup(10, d
))
568 CacheNumber(ExclusiveContext
* cx
, double d
, JSFlatString
* str
)
570 if (JSCompartment
* comp
= cx
->compartment())
571 comp
->dtoaCache
.cache(10, d
, str
);
576 LookupInt32ToString(ExclusiveContext
* cx
, int32_t si
)
578 if (si
>= 0 && StaticStrings::hasInt(si
))
579 return cx
->staticStrings().getInt(si
);
581 return LookupDtoaCache(cx
, si
);
584 template <typename T
>
587 BackfillInt32InBuffer(int32_t si
, T
* buffer
, size_t size
, size_t* length
)
589 uint32_t ui
= Abs(si
);
590 MOZ_ASSERT_IF(si
== INT32_MIN
, ui
== uint32_t(INT32_MAX
) + 1);
592 RangedPtr
<T
> end(buffer
+ size
- 1, buffer
, size
);
594 RangedPtr
<T
> start
= BackfillIndexInCharBuffer(ui
, end
);
598 *length
= end
- start
;
602 template <AllowGC allowGC
>
604 js::Int32ToString(ExclusiveContext
* cx
, int32_t si
)
606 if (JSFlatString
* str
= LookupInt32ToString(cx
, si
))
609 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
611 Latin1Char
* start
= BackfillInt32InBuffer(si
, buffer
, ArrayLength(buffer
), &length
);
613 mozilla::Range
<const Latin1Char
> chars(start
, length
);
614 JSInlineString
* str
= NewFatInlineString
<allowGC
>(cx
, chars
);
618 CacheNumber(cx
, si
, str
);
622 template JSFlatString
*
623 js::Int32ToString
<CanGC
>(ExclusiveContext
* cx
, int32_t si
);
625 template JSFlatString
*
626 js::Int32ToString
<NoGC
>(ExclusiveContext
* cx
, int32_t si
);
629 js::Int32ToAtom(ExclusiveContext
* cx
, int32_t si
)
631 if (JSFlatString
* str
= LookupInt32ToString(cx
, si
))
632 return js::AtomizeString(cx
, str
);
634 char buffer
[JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1];
636 char* start
= BackfillInt32InBuffer(si
, buffer
, JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1, &length
);
638 JSAtom
* atom
= Atomize(cx
, start
, length
);
642 CacheNumber(cx
, si
, atom
);
646 /* Returns a non-nullptr pointer to inside cbuf. */
648 Int32ToCString(ToCStringBuf
* cbuf
, int32_t i
, size_t* len
, int base
= 10)
652 RangedPtr
<char> cp(cbuf
->sbuf
+ ToCStringBuf::sbufSize
- 1, cbuf
->sbuf
, ToCStringBuf::sbufSize
);
653 char* end
= cp
.get();
656 /* Build the string from behind. */
659 cp
= BackfillIndexInCharBuffer(u
, cp
);
663 unsigned newu
= u
/ 16;
664 *--cp
= "0123456789abcdef"[u
- newu
* 16];
669 MOZ_ASSERT(base
>= 2 && base
<= 36);
671 unsigned newu
= u
/ base
;
672 *--cp
= "0123456789abcdefghijklmnopqrstuvwxyz"[u
- newu
* base
];
680 *len
= end
- cp
.get();
684 template <AllowGC allowGC
>
685 static JSString
* JS_FASTCALL
686 js_NumberToStringWithBase(ExclusiveContext
* cx
, double d
, int base
);
688 MOZ_ALWAYS_INLINE
bool
689 num_toString_impl(JSContext
* cx
, CallArgs args
)
691 MOZ_ASSERT(IsNumber(args
.thisv()));
693 double d
= Extract(args
.thisv());
696 if (args
.hasDefined(0)) {
698 if (!ToInteger(cx
, args
[0], &d2
))
701 if (d2
< 2 || d2
> 36) {
702 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_BAD_RADIX
);
708 JSString
* str
= js_NumberToStringWithBase
<CanGC
>(cx
, d
, base
);
710 JS_ReportOutOfMemory(cx
);
713 args
.rval().setString(str
);
718 js_num_toString(JSContext
* cx
, unsigned argc
, Value
* vp
)
720 CallArgs args
= CallArgsFromVp(argc
, vp
);
721 return CallNonGenericMethod
<IsNumber
, num_toString_impl
>(cx
, args
);
725 MOZ_ALWAYS_INLINE
bool
726 num_toLocaleString_impl(JSContext
* cx
, CallArgs args
)
728 MOZ_ASSERT(IsNumber(args
.thisv()));
730 double d
= Extract(args
.thisv());
732 Rooted
<JSString
*> str(cx
, js_NumberToStringWithBase
<CanGC
>(cx
, d
, 10));
734 JS_ReportOutOfMemory(cx
);
739 * Create the string, move back to bytes to make string twiddling
740 * a bit easier and so we can insert platform charset seperators.
742 JSAutoByteString
numBytes(cx
, str
);
745 const char* num
= numBytes
.ptr();
750 * Find the first non-integer value, whether it be a letter as in
751 * 'Infinity', a decimal point, or an 'e' from exponential notation.
753 const char* nint
= num
;
756 while (*nint
>= '0' && *nint
<= '9')
758 int digits
= nint
- num
;
759 const char* end
= num
+ digits
;
761 args
.rval().setString(str
);
765 JSRuntime
* rt
= cx
->runtime();
766 size_t thousandsLength
= strlen(rt
->thousandsSeparator
);
767 size_t decimalLength
= strlen(rt
->decimalSeparator
);
769 /* Figure out how long resulting string will be. */
770 int buflen
= strlen(num
);
772 buflen
+= decimalLength
- 1; /* -1 to account for existing '.' */
774 const char* numGrouping
;
775 const char* tmpGroup
;
776 numGrouping
= tmpGroup
= rt
->numGrouping
;
777 int remainder
= digits
;
781 while (*tmpGroup
!= CHAR_MAX
&& *tmpGroup
!= '\0') {
782 if (*tmpGroup
>= remainder
)
784 buflen
+= thousandsLength
;
785 remainder
-= *tmpGroup
;
790 if (*tmpGroup
== '\0' && *numGrouping
!= '\0') {
791 nrepeat
= (remainder
- 1) / tmpGroup
[-1];
792 buflen
+= thousandsLength
* nrepeat
;
793 remainder
-= nrepeat
* tmpGroup
[-1];
799 char* buf
= cx
->pod_malloc
<char>(buflen
+ 1);
804 const char* tmpSrc
= num
;
806 while (*tmpSrc
== '-' || remainder
--) {
807 MOZ_ASSERT(tmpDest
- buf
< buflen
);
808 *tmpDest
++ = *tmpSrc
++;
810 while (tmpSrc
< end
) {
811 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(thousandsLength
) <= buflen
);
812 strcpy(tmpDest
, rt
->thousandsSeparator
);
813 tmpDest
+= thousandsLength
;
814 MOZ_ASSERT(tmpDest
- buf
+ *tmpGroup
<= buflen
);
815 js_memcpy(tmpDest
, tmpSrc
, *tmpGroup
);
816 tmpDest
+= *tmpGroup
;
823 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(decimalLength
) <= buflen
);
824 strcpy(tmpDest
, rt
->decimalSeparator
);
825 tmpDest
+= decimalLength
;
826 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(strlen(nint
+ 1)) <= buflen
);
827 strcpy(tmpDest
, nint
+ 1);
829 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(strlen(nint
)) <= buflen
);
830 strcpy(tmpDest
, nint
);
833 if (cx
->runtime()->localeCallbacks
&& cx
->runtime()->localeCallbacks
->localeToUnicode
) {
834 Rooted
<Value
> v(cx
, StringValue(str
));
835 bool ok
= !!cx
->runtime()->localeCallbacks
->localeToUnicode(cx
, buf
, &v
);
842 str
= NewStringCopyN
<CanGC
>(cx
, buf
, buflen
);
847 args
.rval().setString(str
);
852 num_toLocaleString(JSContext
* cx
, unsigned argc
, Value
* vp
)
854 CallArgs args
= CallArgsFromVp(argc
, vp
);
855 return CallNonGenericMethod
<IsNumber
, num_toLocaleString_impl
>(cx
, args
);
857 #endif /* !EXPOSE_INTL_API */
859 MOZ_ALWAYS_INLINE
bool
860 num_valueOf_impl(JSContext
* cx
, CallArgs args
)
862 MOZ_ASSERT(IsNumber(args
.thisv()));
863 args
.rval().setNumber(Extract(args
.thisv()));
868 js_num_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
)
870 CallArgs args
= CallArgsFromVp(argc
, vp
);
871 return CallNonGenericMethod
<IsNumber
, num_valueOf_impl
>(cx
, args
);
874 static const unsigned MAX_PRECISION
= 100;
877 ComputePrecisionInRange(JSContext
* cx
, int minPrecision
, int maxPrecision
, HandleValue v
,
881 if (!ToInteger(cx
, v
, &prec
))
883 if (minPrecision
<= prec
&& prec
<= maxPrecision
) {
884 *precision
= int(prec
);
889 if (char* numStr
= NumberToCString(cx
, &cbuf
, prec
, 10))
890 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_PRECISION_RANGE
, numStr
);
895 DToStrResult(JSContext
* cx
, double d
, JSDToStrMode mode
, int precision
, CallArgs args
)
897 char buf
[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION
+ 1)];
898 char* numStr
= js_dtostr(cx
->mainThread().dtoaState
, buf
, sizeof buf
, mode
, precision
, d
);
900 JS_ReportOutOfMemory(cx
);
903 JSString
* str
= NewStringCopyZ
<CanGC
>(cx
, numStr
);
906 args
.rval().setString(str
);
911 * In the following three implementations, we allow a larger range of precision
912 * than ECMA requires; this is permitted by ECMA-262.
914 MOZ_ALWAYS_INLINE
bool
915 num_toFixed_impl(JSContext
* cx
, CallArgs args
)
917 MOZ_ASSERT(IsNumber(args
.thisv()));
920 if (args
.length() == 0) {
923 if (!ComputePrecisionInRange(cx
, -20, MAX_PRECISION
, args
[0], &precision
))
927 return DToStrResult(cx
, Extract(args
.thisv()), DTOSTR_FIXED
, precision
, args
);
931 num_toFixed(JSContext
* cx
, unsigned argc
, Value
* vp
)
933 CallArgs args
= CallArgsFromVp(argc
, vp
);
934 return CallNonGenericMethod
<IsNumber
, num_toFixed_impl
>(cx
, args
);
937 MOZ_ALWAYS_INLINE
bool
938 num_toExponential_impl(JSContext
* cx
, CallArgs args
)
940 MOZ_ASSERT(IsNumber(args
.thisv()));
944 if (!args
.hasDefined(0)) {
945 mode
= DTOSTR_STANDARD_EXPONENTIAL
;
948 mode
= DTOSTR_EXPONENTIAL
;
949 if (!ComputePrecisionInRange(cx
, 0, MAX_PRECISION
, args
[0], &precision
))
953 return DToStrResult(cx
, Extract(args
.thisv()), mode
, precision
+ 1, args
);
957 num_toExponential(JSContext
* cx
, unsigned argc
, Value
* vp
)
959 CallArgs args
= CallArgsFromVp(argc
, vp
);
960 return CallNonGenericMethod
<IsNumber
, num_toExponential_impl
>(cx
, args
);
963 MOZ_ALWAYS_INLINE
bool
964 num_toPrecision_impl(JSContext
* cx
, CallArgs args
)
966 MOZ_ASSERT(IsNumber(args
.thisv()));
968 double d
= Extract(args
.thisv());
970 if (!args
.hasDefined(0)) {
971 JSString
* str
= js_NumberToStringWithBase
<CanGC
>(cx
, d
, 10);
973 JS_ReportOutOfMemory(cx
);
976 args
.rval().setString(str
);
981 if (!ComputePrecisionInRange(cx
, 1, MAX_PRECISION
, args
[0], &precision
))
984 return DToStrResult(cx
, d
, DTOSTR_PRECISION
, precision
, args
);
988 num_toPrecision(JSContext
* cx
, unsigned argc
, Value
* vp
)
990 CallArgs args
= CallArgsFromVp(argc
, vp
);
991 return CallNonGenericMethod
<IsNumber
, num_toPrecision_impl
>(cx
, args
);
994 static const JSFunctionSpec number_methods
[] = {
996 JS_FN(js_toSource_str
, num_toSource
, 0, 0),
998 JS_FN(js_toString_str
, js_num_toString
, 1, 0),
1000 JS_SELF_HOSTED_FN(js_toLocaleString_str
, "Number_toLocaleString", 0,0),
1002 JS_FN(js_toLocaleString_str
, num_toLocaleString
, 0,0),
1004 JS_FN(js_valueOf_str
, js_num_valueOf
, 0, 0),
1005 JS_FN("toFixed", num_toFixed
, 1, 0),
1006 JS_FN("toExponential", num_toExponential
, 1, 0),
1007 JS_FN("toPrecision", num_toPrecision
, 1, 0),
1011 // ES6 draft ES6 15.7.3.12
1013 Number_isInteger(JSContext
* cx
, unsigned argc
, Value
* vp
)
1015 CallArgs args
= CallArgsFromVp(argc
, vp
);
1016 if (args
.length() < 1 || !args
[0].isNumber()) {
1017 args
.rval().setBoolean(false);
1020 Value val
= args
[0];
1021 args
.rval().setBoolean(val
.isInt32() ||
1022 (mozilla::IsFinite(val
.toDouble()) &&
1023 JS::ToInteger(val
.toDouble()) == val
.toDouble()));
1028 static const JSFunctionSpec number_static_methods
[] = {
1029 JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1,0),
1030 JS_FN("isInteger", Number_isInteger
, 1, 0),
1031 JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1,0),
1032 JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
1033 JS_FN("parseFloat", num_parseFloat
, 1, 0),
1034 JS_FN("parseInt", num_parseInt
, 2, 0),
1039 /* NB: Keep this in synch with number_constants[]. */
1042 NC_POSITIVE_INFINITY
,
1043 NC_NEGATIVE_INFINITY
,
1046 NC_MAX_SAFE_INTEGER
,
1047 NC_MIN_SAFE_INTEGER
,
1053 * Some to most C compilers forbid spelling these at compile time, or barf
1054 * if you try, so all but MAX_VALUE are set up by InitRuntimeNumberState
1055 * using union jsdpun.
1057 static JSConstDoubleSpec number_constants
[] = {
1059 {"POSITIVE_INFINITY", 0 },
1060 {"NEGATIVE_INFINITY", 0 },
1061 {"MAX_VALUE", 1.7976931348623157E+308 },
1063 /* ES6 (April 2014 draft) 20.1.2.6 */
1064 {"MAX_SAFE_INTEGER", 9007199254740991 },
1065 /* ES6 (April 2014 draft) 20.1.2.10 */
1066 {"MIN_SAFE_INTEGER", -9007199254740991, },
1067 /* ES6 (May 2013 draft) 15.7.3.7 */
1068 {"EPSILON", 2.2204460492503130808472633361816e-16},
1073 * Set the exception mask to mask all exceptions and set the FPU precision
1074 * to 53 bit mantissa (64 bit doubles).
1079 #if (defined __GNUC__ && defined __i386__) || \
1080 (defined __SUNPRO_CC && defined __i386)
1082 asm("fstcw %0" : "=m" (control
) : );
1083 control
&= ~0x300; // Lower bits 8 and 9 (precision control).
1084 control
|= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
1085 asm("fldcw %0" : : "m" (control
) );
1090 js::InitRuntimeNumberState(JSRuntime
* rt
)
1095 * Our NaN must be one particular canonical value, because we rely on NaN
1096 * encoding for our value representation. See Value.h.
1098 number_constants
[NC_NaN
].val
= GenericNaN();
1100 number_constants
[NC_POSITIVE_INFINITY
].val
= mozilla::PositiveInfinity
<double>();
1101 number_constants
[NC_NEGATIVE_INFINITY
].val
= mozilla::NegativeInfinity
<double>();
1103 number_constants
[NC_MIN_VALUE
].val
= MinNumberValue
<double>();
1105 // XXX If EXPOSE_INTL_API becomes true all the time at some point,
1106 // js::InitRuntimeNumberState is no longer fallible, and we should
1107 // change its return type.
1108 #if !EXPOSE_INTL_API
1109 /* Copy locale-specific separators into the runtime strings. */
1110 const char* thousandsSeparator
, *decimalPoint
, *grouping
;
1111 #ifdef HAVE_LOCALECONV
1112 struct lconv
* locale
= localeconv();
1113 thousandsSeparator
= locale
->thousands_sep
;
1114 decimalPoint
= locale
->decimal_point
;
1115 grouping
= locale
->grouping
;
1117 thousandsSeparator
= getenv("LOCALE_THOUSANDS_SEP");
1118 decimalPoint
= getenv("LOCALE_DECIMAL_POINT");
1119 grouping
= getenv("LOCALE_GROUPING");
1121 if (!thousandsSeparator
)
1122 thousandsSeparator
= "'";
1129 * We use single malloc to get the memory for all separator and grouping
1132 size_t thousandsSeparatorSize
= strlen(thousandsSeparator
) + 1;
1133 size_t decimalPointSize
= strlen(decimalPoint
) + 1;
1134 size_t groupingSize
= strlen(grouping
) + 1;
1136 char* storage
= js_pod_malloc
<char>(thousandsSeparatorSize
+
1142 js_memcpy(storage
, thousandsSeparator
, thousandsSeparatorSize
);
1143 rt
->thousandsSeparator
= storage
;
1144 storage
+= thousandsSeparatorSize
;
1146 js_memcpy(storage
, decimalPoint
, decimalPointSize
);
1147 rt
->decimalSeparator
= storage
;
1148 storage
+= decimalPointSize
;
1150 js_memcpy(storage
, grouping
, groupingSize
);
1151 rt
->numGrouping
= grouping
;
1152 #endif /* !EXPOSE_INTL_API */
1156 #if !EXPOSE_INTL_API
1158 js::FinishRuntimeNumberState(JSRuntime
* rt
)
1161 * The free also releases the memory for decimalSeparator and numGrouping
1164 char* storage
= const_cast<char*>(rt
->thousandsSeparator
);
1170 js_InitNumberClass(JSContext
* cx
, HandleObject obj
)
1172 MOZ_ASSERT(obj
->isNative());
1174 /* XXX must do at least once per new thread, so do it per JSContext... */
1177 Rooted
<GlobalObject
*> global(cx
, &obj
->as
<GlobalObject
>());
1179 RootedObject
numberProto(cx
, global
->createBlankPrototype(cx
, &NumberObject::class_
));
1182 numberProto
->as
<NumberObject
>().setPrimitiveValue(0);
1184 RootedFunction
ctor(cx
);
1185 ctor
= global
->createConstructor(cx
, Number
, cx
->names().Number
, 1);
1189 if (!LinkConstructorAndPrototype(cx
, ctor
, numberProto
))
1192 /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */
1193 if (!JS_DefineConstDoubles(cx
, ctor
, number_constants
))
1196 if (!DefinePropertiesAndFunctions(cx
, ctor
, nullptr, number_static_methods
))
1199 if (!DefinePropertiesAndFunctions(cx
, numberProto
, nullptr, number_methods
))
1202 if (!JS_DefineFunctions(cx
, global
, number_functions
))
1205 RootedValue
valueNaN(cx
, cx
->runtime()->NaNValue
);
1206 RootedValue
valueInfinity(cx
, cx
->runtime()->positiveInfinityValue
);
1208 /* ES5 15.1.1.1, 15.1.1.2 */
1209 if (!DefineNativeProperty(cx
, global
, cx
->names().NaN
, valueNaN
, nullptr, nullptr,
1210 JSPROP_PERMANENT
| JSPROP_READONLY
) ||
1211 !DefineNativeProperty(cx
, global
, cx
->names().Infinity
, valueInfinity
, nullptr, nullptr,
1212 JSPROP_PERMANENT
| JSPROP_READONLY
))
1217 if (!GlobalObject::initBuiltinConstructor(cx
, global
, JSProto_Number
, ctor
, numberProto
))
1224 FracNumberToCString(ExclusiveContext
* cx
, ToCStringBuf
* cbuf
, double d
, int base
= 10)
1229 MOZ_ASSERT(!mozilla::NumberIsInt32(d
, &_
));
1236 * This is V8's implementation of the algorithm described in the
1239 * Printing floating-point numbers quickly and accurately with integers.
1240 * Florian Loitsch, PLDI 2010.
1242 const double_conversion::DoubleToStringConverter
& converter
1243 = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1244 double_conversion::StringBuilder
builder(cbuf
->sbuf
, cbuf
->sbufSize
);
1245 converter
.ToShortest(d
, &builder
);
1246 numStr
= builder
.Finalize();
1248 numStr
= cbuf
->dbuf
= js_dtobasestr(cx
->dtoaState(), base
, d
);
1254 js::NumberToCString(JSContext
* cx
, ToCStringBuf
* cbuf
, double d
, int base
/* = 10*/)
1258 return mozilla::NumberIsInt32(d
, &i
)
1259 ? Int32ToCString(cbuf
, i
, &len
, base
)
1260 : FracNumberToCString(cx
, cbuf
, d
, base
);
1263 template <AllowGC allowGC
>
1264 static JSString
* JS_FASTCALL
1265 js_NumberToStringWithBase(ExclusiveContext
* cx
, double d
, int base
)
1271 * Caller is responsible for error reporting. When called from trace,
1272 * returning nullptr here will cause us to fall of trace and then retry
1273 * from the interpreter (which will report the error).
1275 if (base
< 2 || base
> 36)
1278 JSCompartment
* comp
= cx
->compartment();
1281 if (mozilla::NumberIsInt32(d
, &i
)) {
1282 if (base
== 10 && StaticStrings::hasInt(i
))
1283 return cx
->staticStrings().getInt(i
);
1284 if (unsigned(i
) < unsigned(base
)) {
1286 return cx
->staticStrings().getInt(i
);
1287 char16_t c
= 'a' + i
- 10;
1288 MOZ_ASSERT(StaticStrings::hasUnit(c
));
1289 return cx
->staticStrings().getUnit(c
);
1292 if (JSFlatString
* str
= comp
->dtoaCache
.lookup(base
, d
))
1296 numStr
= Int32ToCString(&cbuf
, i
, &len
, base
);
1297 MOZ_ASSERT(!cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&& numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1299 if (JSFlatString
* str
= comp
->dtoaCache
.lookup(base
, d
))
1302 numStr
= FracNumberToCString(cx
, &cbuf
, d
, base
);
1304 js_ReportOutOfMemory(cx
);
1307 MOZ_ASSERT_IF(base
== 10,
1308 !cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&& numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1309 MOZ_ASSERT_IF(base
!= 10,
1310 cbuf
.dbuf
&& cbuf
.dbuf
== numStr
);
1313 JSFlatString
* s
= NewStringCopyZ
<allowGC
>(cx
, numStr
);
1315 comp
->dtoaCache
.cache(base
, d
, s
);
1319 template <AllowGC allowGC
>
1321 js::NumberToString(ExclusiveContext
* cx
, double d
)
1323 return js_NumberToStringWithBase
<allowGC
>(cx
, d
, 10);
1327 js::NumberToString
<CanGC
>(ExclusiveContext
* cx
, double d
);
1330 js::NumberToString
<NoGC
>(ExclusiveContext
* cx
, double d
);
1333 js::NumberToAtom(ExclusiveContext
* cx
, double d
)
1336 if (mozilla::NumberIsInt32(d
, &si
))
1337 return Int32ToAtom(cx
, si
);
1339 if (JSFlatString
* str
= LookupDtoaCache(cx
, d
))
1340 return AtomizeString(cx
, str
);
1343 char* numStr
= FracNumberToCString(cx
, &cbuf
, d
);
1345 js_ReportOutOfMemory(cx
);
1348 MOZ_ASSERT(!cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&& numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1350 size_t length
= strlen(numStr
);
1351 JSAtom
* atom
= Atomize(cx
, numStr
, length
);
1355 CacheNumber(cx
, d
, atom
);
1361 js::NumberToString(JSContext
* cx
, double d
)
1363 if (JSString
* str
= js_NumberToStringWithBase
<CanGC
>(cx
, d
, 10))
1364 return &str
->asFlat();
1369 js::IndexToString(JSContext
* cx
, uint32_t index
)
1371 if (StaticStrings::hasUint(index
))
1372 return cx
->staticStrings().getUint(index
);
1374 JSCompartment
* c
= cx
->compartment();
1375 if (JSFlatString
* str
= c
->dtoaCache
.lookup(10, index
))
1378 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
1379 RangedPtr
<Latin1Char
> end(buffer
+ JSFatInlineString::MAX_LENGTH_LATIN1
,
1380 buffer
, JSFatInlineString::MAX_LENGTH_LATIN1
+ 1);
1382 RangedPtr
<Latin1Char
> start
= BackfillIndexInCharBuffer(index
, end
);
1384 mozilla::Range
<const Latin1Char
> chars(start
.get(), end
- start
);
1385 JSInlineString
* str
= NewFatInlineString
<CanGC
>(cx
, chars
);
1389 c
->dtoaCache
.cache(10, index
, str
);
1394 js::NumberValueToStringBuffer(JSContext
* cx
, const Value
& v
, StringBuffer
& sb
)
1396 /* Convert to C-string. */
1401 cstr
= Int32ToCString(&cbuf
, v
.toInt32(), &cstrlen
);
1402 MOZ_ASSERT(cstrlen
== strlen(cstr
));
1404 cstr
= NumberToCString(cx
, &cbuf
, v
.toDouble());
1406 JS_ReportOutOfMemory(cx
);
1409 cstrlen
= strlen(cstr
);
1413 * Inflate to char16_t string. The input C-string characters are < 127, so
1414 * even if char16_t units are UTF-8, all chars should map to one char16_t.
1416 MOZ_ASSERT(!cbuf
.dbuf
&& cstrlen
< cbuf
.sbufSize
);
1417 return sb
.append(cstr
, cstrlen
);
1420 template <typename CharT
>
1422 CharsToNumber(ExclusiveContext
* cx
, const CharT
* chars
, size_t length
, double* result
)
1426 if ('0' <= c
&& c
<= '9')
1428 else if (unicode::IsSpace(c
))
1431 *result
= GenericNaN();
1435 const CharT
* end
= chars
+ length
;
1436 const CharT
* bp
= SkipSpace(chars
, end
);
1438 /* ECMA doesn't allow signed non-decimal numbers (bug 273467). */
1439 if (end
- bp
>= 2 && bp
[0] == '0') {
1441 if (bp
[1] == 'b' || bp
[1] == 'B')
1443 else if (bp
[1] == 'o' || bp
[1] == 'O')
1445 else if (bp
[1] == 'x' || bp
[1] == 'X')
1450 * It's probably a non-decimal number. Accept if there's at least one digit after
1451 * the 0b|0o|0x, and if no non-whitespace characters follow all the digits.
1453 const CharT
* endptr
;
1455 if (!GetPrefixInteger(cx
, bp
+ 2, end
, radix
, &endptr
, &d
) ||
1457 SkipSpace(endptr
, end
) != end
)
1459 *result
= GenericNaN();
1468 * Note that ECMA doesn't treat a string beginning with a '0' as
1469 * an octal number here. This works because all such numbers will
1470 * be interpreted as decimal by js_strtod. Also, any hex numbers
1471 * that have made it here (which can only be negative ones) will
1472 * be treated as 0 without consuming the 'x' by js_strtod.
1476 if (!js_strtod(cx
, bp
, end
, &ep
, &d
)) {
1477 *result
= GenericNaN();
1481 if (SkipSpace(ep
, end
) != end
)
1482 *result
= GenericNaN();
1490 js::StringToNumber(ExclusiveContext
* cx
, JSString
* str
, double* result
)
1492 AutoCheckCannotGC nogc
;
1493 JSLinearString
* linearStr
= str
->ensureLinear(cx
);
1497 return linearStr
->hasLatin1Chars()
1498 ? CharsToNumber(cx
, linearStr
->latin1Chars(nogc
), str
->length(), result
)
1499 : CharsToNumber(cx
, linearStr
->twoByteChars(nogc
), str
->length(), result
);
1503 js::ToNumberSlow(ExclusiveContext
* cx
, Value v
, double* out
)
1505 MOZ_ASSERT(!v
.isNumber());
1506 goto skip_int_double
;
1509 *out
= v
.toNumber();
1514 if (!v
.isObject()) {
1516 return StringToNumber(cx
, v
.toString(), out
);
1517 if (v
.isBoolean()) {
1518 *out
= v
.toBoolean() ? 1.0 : 0.0;
1526 if (cx
->isJSContext()) {
1527 JS_ReportErrorNumber(cx
->asJSContext(), js_GetErrorMessage
, nullptr,
1528 JSMSG_SYMBOL_TO_NUMBER
);
1533 MOZ_ASSERT(v
.isUndefined());
1534 *out
= GenericNaN();
1538 if (!cx
->isJSContext())
1541 RootedValue
v2(cx
, v
);
1542 if (!ToPrimitive(cx
->asJSContext(), JSTYPE_NUMBER
, &v2
))
1549 *out
= GenericNaN();
1554 js::ToNumberSlow(JSContext
* cx
, Value v
, double* out
)
1556 return ToNumberSlow(static_cast<ExclusiveContext
*>(cx
), v
, out
);
1560 * Convert a value to an int64_t, according to the WebIDL rules for long long
1561 * conversion. Return converted value in *out on success, false on failure.
1564 js::ToInt64Slow(JSContext
* cx
, const HandleValue v
, int64_t* out
)
1566 MOZ_ASSERT(!v
.isInt32());
1571 if (!ToNumberSlow(cx
, v
, &d
))
1579 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
1580 * conversion. Return converted value in *out on success, false on failure.
1583 js::ToUint64Slow(JSContext
* cx
, const HandleValue v
, uint64_t* out
)
1585 MOZ_ASSERT(!v
.isInt32());
1590 if (!ToNumberSlow(cx
, v
, &d
))
1598 js::ToInt32Slow(JSContext
* cx
, const HandleValue v
, int32_t* out
)
1600 MOZ_ASSERT(!v
.isInt32());
1605 if (!ToNumberSlow(cx
, v
, &d
))
1613 js::ToUint32Slow(JSContext
* cx
, const HandleValue v
, uint32_t* out
)
1615 MOZ_ASSERT(!v
.isInt32());
1620 if (!ToNumberSlow(cx
, v
, &d
))
1628 js::ToUint16Slow(JSContext
* cx
, const HandleValue v
, uint16_t* out
)
1630 MOZ_ASSERT(!v
.isInt32());
1634 } else if (!ToNumberSlow(cx
, v
, &d
)) {
1638 if (d
== 0 || !mozilla::IsFinite(d
)) {
1643 uint16_t u
= (uint16_t) d
;
1644 if ((double)u
== d
) {
1650 d
= floor(neg
? -d
: d
);
1652 unsigned m
= JS_BIT(16);
1653 d
= fmod(d
, (double) m
);
1656 *out
= (uint16_t) d
;
1660 template<typename T
>
1662 js::ToLengthClamped(T
* cx
, HandleValue v
, uint32_t* out
, bool* overflow
)
1665 int32_t i
= v
.toInt32();
1666 *out
= i
< 0 ? 0 : i
;
1673 if (!ToNumber(cx
, v
, &d
)) {
1678 d
= JS::ToInteger(d
);
1683 if (d
>= (double)0xFFFFFFFEU
) {
1692 js::ToLengthClamped
<JSContext
>(JSContext
*, HandleValue
, uint32_t*, bool*);
1694 js::ToLengthClamped
<ExclusiveContext
>(ExclusiveContext
*, HandleValue
, uint32_t*, bool*);
1696 template <typename CharT
>
1698 js_strtod(ExclusiveContext
* cx
, const CharT
* begin
, const CharT
* end
, const CharT
** dEnd
,
1701 const CharT
* s
= SkipSpace(begin
, end
);
1702 size_t length
= end
- s
;
1704 Vector
<char, 32> chars(cx
);
1705 if (!chars
.growByUninitialized(length
+ 1))
1709 for (; i
< length
; i
++) {
1717 /* Try to parse +Infinity, -Infinity or Infinity. */
1719 char* afterSign
= chars
.begin();
1720 bool negative
= (*afterSign
== '-');
1721 if (negative
|| *afterSign
== '+')
1724 if (*afterSign
== 'I' && !strncmp(afterSign
, "Infinity", 8)) {
1725 *d
= negative
? NegativeInfinity
<double>() : PositiveInfinity
<double>();
1726 *dEnd
= s
+ (afterSign
- chars
.begin()) + 8;
1731 /* Everything else. */
1734 *d
= js_strtod_harder(cx
->dtoaState(), chars
.begin(), &ep
, &err
);
1736 MOZ_ASSERT(ep
>= chars
.begin());
1738 if (ep
== chars
.begin())
1741 *dEnd
= s
+ (ep
- chars
.begin());
1747 js_strtod(ExclusiveContext
* cx
, const char16_t
* begin
, const char16_t
* end
, const char16_t
** dEnd
,
1751 js_strtod(ExclusiveContext
* cx
, const Latin1Char
* begin
, const Latin1Char
* end
,
1752 const Latin1Char
** dEnd
, double* d
);