Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsnum.cpp
blob0ed1ed09af1de708c8c4b15bde422cd8e1c62acb
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/. */
7 /*
8 * JS number type and wrapper class.
9 */
11 #include "jsnum.h"
13 #include "mozilla/FloatingPoint.h"
14 #include "mozilla/PodOperations.h"
15 #include "mozilla/RangedPtr.h"
17 #ifdef HAVE_LOCALECONV
18 #include <locale.h>
19 #endif
20 #include <math.h>
21 #include <string.h>
23 #include "double-conversion.h"
24 #include "jsatom.h"
25 #include "jscntxt.h"
26 #include "jsdtoa.h"
27 #include "jsobj.h"
28 #include "jsstr.h"
29 #include "jstypes.h"
31 #include "vm/GlobalObject.h"
32 #include "vm/NumericConversions.h"
33 #include "vm/StringBuffer.h"
35 #include "jsatominlines.h"
37 #include "vm/NumberObject-inl.h"
38 #include "vm/String-inl.h"
40 using namespace js;
41 using namespace js::types;
43 using mozilla::Abs;
44 using mozilla::ArrayLength;
45 using mozilla::MinNumberValue;
46 using mozilla::NegativeInfinity;
47 using mozilla::PodCopy;
48 using mozilla::PositiveInfinity;
49 using mozilla::RangedPtr;
51 using JS::AutoCheckCannotGC;
52 using JS::GenericNaN;
55 * If we're accumulating a decimal number and the number is >= 2^53, then the
56 * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
57 * Call js_strtod_harder to get the correct answer.
59 template <typename CharT>
60 static bool
61 ComputeAccurateDecimalInteger(ThreadSafeContext *cx, const CharT *start, const CharT *end,
62 double *dp)
64 size_t length = end - start;
65 ScopedJSFreePtr<char> cstr(cx->pod_malloc<char>(length + 1));
66 if (!cstr)
67 return false;
69 for (size_t i = 0; i < length; i++) {
70 char c = char(start[i]);
71 JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
72 cstr[i] = c;
74 cstr[length] = 0;
76 char *estr;
77 int err = 0;
78 *dp = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
79 if (err == JS_DTOA_ENOMEM) {
80 js_ReportOutOfMemory(cx);
81 return false;
84 return true;
87 namespace {
89 template <typename CharT>
90 class BinaryDigitReader
92 const int base; /* Base of number; must be a power of 2 */
93 int digit; /* Current digit value in radix given by base */
94 int digitMask; /* Mask to extract the next bit from digit */
95 const CharT *start; /* Pointer to the remaining digits */
96 const CharT *end; /* Pointer to first non-digit */
98 public:
99 BinaryDigitReader(int base, const CharT *start, const CharT *end)
100 : base(base), digit(0), digitMask(0), start(start), end(end)
104 /* Return the next binary digit from the number, or -1 if done. */
105 int nextDigit() {
106 if (digitMask == 0) {
107 if (start == end)
108 return -1;
110 int c = *start++;
111 JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
112 if ('0' <= c && c <= '9')
113 digit = c - '0';
114 else if ('a' <= c && c <= 'z')
115 digit = c - 'a' + 10;
116 else
117 digit = c - 'A' + 10;
118 digitMask = base >> 1;
121 int bit = (digit & digitMask) != 0;
122 digitMask >>= 1;
123 return bit;
127 } /* anonymous namespace */
130 * The fast result might also have been inaccurate for power-of-two bases. This
131 * happens if the addition in value * 2 + digit causes a round-down to an even
132 * least significant mantissa bit when the first dropped bit is a one. If any
133 * of the following digits in the number (which haven't been added in yet) are
134 * nonzero, then the correct action would have been to round up instead of
135 * down. An example occurs when reading the number 0x1000000000000081, which
136 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
138 template <typename CharT>
139 static double
140 ComputeAccurateBinaryBaseInteger(const CharT *start, const CharT *end, int base)
142 BinaryDigitReader<CharT> bdr(base, start, end);
144 /* Skip leading zeroes. */
145 int bit;
146 do {
147 bit = bdr.nextDigit();
148 } while (bit == 0);
150 JS_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
152 /* Gather the 53 significant bits (including the leading 1). */
153 double value = 1.0;
154 for (int j = 52; j > 0; j--) {
155 bit = bdr.nextDigit();
156 if (bit < 0)
157 return value;
158 value = value * 2 + bit;
161 /* bit2 is the 54th bit (the first dropped from the mantissa). */
162 int bit2 = bdr.nextDigit();
163 if (bit2 >= 0) {
164 double factor = 2.0;
165 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
166 int bit3;
168 while ((bit3 = bdr.nextDigit()) >= 0) {
169 sticky |= bit3;
170 factor *= 2;
172 value += bit2 & (bit | sticky);
173 value *= factor;
176 return value;
179 template <typename CharT>
180 double
181 js::ParseDecimalNumber(const mozilla::Range<const CharT> chars)
183 MOZ_ASSERT(chars.length() > 0);
184 uint64_t dec = 0;
185 RangedPtr<const CharT> s = chars.start(), end = chars.end();
186 do {
187 CharT c = *s;
188 MOZ_ASSERT('0' <= c && c <= '9');
189 uint8_t digit = c - '0';
190 uint64_t next = dec * 10 + digit;
191 MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
192 "next value won't be an integrally-precise double");
193 dec = next;
194 } while (++s < end);
195 return static_cast<double>(dec);
198 template double
199 js::ParseDecimalNumber(const mozilla::Range<const Latin1Char> chars);
201 template double
202 js::ParseDecimalNumber(const mozilla::Range<const jschar> chars);
204 template <typename CharT>
205 bool
206 js::GetPrefixInteger(ThreadSafeContext *cx, const CharT *start, const CharT *end, int base,
207 const CharT **endp, double *dp)
209 JS_ASSERT(start <= end);
210 JS_ASSERT(2 <= base && base <= 36);
212 const CharT *s = start;
213 double d = 0.0;
214 for (; s < end; s++) {
215 int digit;
216 CharT c = *s;
217 if ('0' <= c && c <= '9')
218 digit = c - '0';
219 else if ('a' <= c && c <= 'z')
220 digit = c - 'a' + 10;
221 else if ('A' <= c && c <= 'Z')
222 digit = c - 'A' + 10;
223 else
224 break;
225 if (digit >= base)
226 break;
227 d = d * base + digit;
230 *endp = s;
231 *dp = d;
233 /* If we haven't reached the limit of integer precision, we're done. */
234 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
235 return true;
238 * Otherwise compute the correct integer from the prefix of valid digits
239 * if we're computing for base ten or a power of two. Don't worry about
240 * other bases; see 15.1.2.2 step 13.
242 if (base == 10)
243 return ComputeAccurateDecimalInteger(cx, start, s, dp);
245 if ((base & (base - 1)) == 0)
246 *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
248 return true;
251 template bool
252 js::GetPrefixInteger(ThreadSafeContext *cx, const jschar *start, const jschar *end, int base,
253 const jschar **endp, double *dp);
255 template bool
256 js::GetPrefixInteger(ThreadSafeContext *cx, const Latin1Char *start, const Latin1Char *end,
257 int base, const Latin1Char **endp, double *dp);
259 bool
260 js::GetDecimalInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, double *dp)
262 JS_ASSERT(start <= end);
264 const jschar *s = start;
265 double d = 0.0;
266 for (; s < end; s++) {
267 jschar c = *s;
268 JS_ASSERT('0' <= c && c <= '9');
269 int digit = c - '0';
270 d = d * 10 + digit;
273 *dp = d;
275 // If we haven't reached the limit of integer precision, we're done.
276 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
277 return true;
279 // Otherwise compute the correct integer from the prefix of valid digits.
280 return ComputeAccurateDecimalInteger(cx, start, s, dp);
283 static bool
284 num_parseFloat(JSContext *cx, unsigned argc, Value *vp)
286 CallArgs args = CallArgsFromVp(argc, vp);
288 if (args.length() == 0) {
289 args.rval().setNaN();
290 return true;
293 JSString *str = ToString<CanGC>(cx, args[0]);
294 if (!str)
295 return false;
297 JSLinearString *linear = str->ensureLinear(cx);
298 if (!linear)
299 return false;
301 double d;
302 AutoCheckCannotGC nogc;
303 if (linear->hasLatin1Chars()) {
304 const Latin1Char *begin = linear->latin1Chars(nogc);
305 const Latin1Char *end;
306 if (!js_strtod(cx, begin, begin + linear->length(), &end, &d))
307 return false;
308 if (end == begin)
309 d = GenericNaN();
310 } else {
311 const jschar *begin = linear->twoByteChars(nogc);
312 const jschar *end;
313 if (!js_strtod(cx, begin, begin + linear->length(), &end, &d))
314 return false;
315 if (end == begin)
316 d = GenericNaN();
319 args.rval().setDouble(d);
320 return true;
323 template <typename CharT>
324 static bool
325 ParseIntImpl(JSContext *cx, const CharT *chars, size_t length, bool stripPrefix, int32_t radix,
326 double *res)
328 /* Step 2. */
329 const CharT *end = chars + length;
330 const CharT *s = SkipSpace(chars, end);
332 MOZ_ASSERT(chars <= s);
333 MOZ_ASSERT(s <= end);
335 /* Steps 3-4. */
336 bool negative = (s != end && s[0] == '-');
338 /* Step 5. */
339 if (s != end && (s[0] == '-' || s[0] == '+'))
340 s++;
342 /* Step 10. */
343 if (stripPrefix) {
344 if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
345 s += 2;
346 radix = 16;
350 /* Steps 11-15. */
351 const CharT *actualEnd;
352 double d;
353 if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, &d))
354 return false;
356 if (s == actualEnd)
357 *res = GenericNaN();
358 else
359 *res = negative ? -d : d;
360 return true;
363 /* ES5 15.1.2.2. */
364 bool
365 js::num_parseInt(JSContext *cx, unsigned argc, Value *vp)
367 CallArgs args = CallArgsFromVp(argc, vp);
369 /* Fast paths and exceptional cases. */
370 if (args.length() == 0) {
371 args.rval().setNaN();
372 return true;
375 if (args.length() == 1 ||
376 (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) {
377 if (args[0].isInt32()) {
378 args.rval().set(args[0]);
379 return true;
383 * Step 1 is |inputString = ToString(string)|. When string >=
384 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
385 * the word, which would mean the result of parseInt(string) should be |N|.
387 * To preserve this behaviour, we can't use the fast-path when string >
388 * 1e21, or else the result would be |NeM|.
390 * The same goes for values smaller than 1.0e-6, because the string would be in
391 * the form of "Ne-M".
393 if (args[0].isDouble()) {
394 double d = args[0].toDouble();
395 if (1.0e-6 < d && d < 1.0e21) {
396 args.rval().setNumber(floor(d));
397 return true;
399 if (-1.0e21 < d && d < -1.0e-6) {
400 args.rval().setNumber(-floor(-d));
401 return true;
403 if (d == 0.0) {
404 args.rval().setInt32(0);
405 return true;
410 /* Step 1. */
411 RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
412 if (!inputString)
413 return false;
414 args[0].setString(inputString);
416 /* Steps 6-9. */
417 bool stripPrefix = true;
418 int32_t radix;
419 if (!args.hasDefined(1)) {
420 radix = 10;
421 } else {
422 if (!ToInt32(cx, args[1], &radix))
423 return false;
424 if (radix == 0) {
425 radix = 10;
426 } else {
427 if (radix < 2 || radix > 36) {
428 args.rval().setNaN();
429 return true;
431 if (radix != 16)
432 stripPrefix = false;
436 JSLinearString *linear = inputString->ensureLinear(cx);
437 if (!linear)
438 return false;
440 AutoCheckCannotGC nogc;
441 size_t length = inputString->length();
442 double number;
443 if (linear->hasLatin1Chars()) {
444 if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix, &number))
445 return false;
446 } else {
447 if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix, radix, &number))
448 return false;
451 args.rval().setNumber(number);
452 return true;
455 static const JSFunctionSpec number_functions[] = {
456 JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1,0),
457 JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1,0),
458 JS_FN(js_parseFloat_str, num_parseFloat, 1,0),
459 JS_FN(js_parseInt_str, num_parseInt, 2,0),
460 JS_FS_END
463 const Class NumberObject::class_ = {
464 js_Number_str,
465 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
466 JS_PropertyStub, /* addProperty */
467 JS_DeletePropertyStub, /* delProperty */
468 JS_PropertyStub, /* getProperty */
469 JS_StrictPropertyStub, /* setProperty */
470 JS_EnumerateStub,
471 JS_ResolveStub,
472 JS_ConvertStub
475 static bool
476 Number(JSContext *cx, unsigned argc, Value *vp)
478 CallArgs args = CallArgsFromVp(argc, vp);
480 /* Sample JS_CALLEE before clobbering. */
481 bool isConstructing = args.isConstructing();
483 if (args.length() > 0) {
484 if (!ToNumber(cx, args[0]))
485 return false;
486 args.rval().set(args[0]);
487 } else {
488 args.rval().setInt32(0);
491 if (!isConstructing)
492 return true;
494 JSObject *obj = NumberObject::create(cx, args.rval().toNumber());
495 if (!obj)
496 return false;
497 args.rval().setObject(*obj);
498 return true;
501 MOZ_ALWAYS_INLINE bool
502 IsNumber(HandleValue v)
504 return v.isNumber() || (v.isObject() && v.toObject().is<NumberObject>());
507 static inline double
508 Extract(const Value &v)
510 if (v.isNumber())
511 return v.toNumber();
512 return v.toObject().as<NumberObject>().unbox();
515 #if JS_HAS_TOSOURCE
516 MOZ_ALWAYS_INLINE bool
517 num_toSource_impl(JSContext *cx, CallArgs args)
519 double d = Extract(args.thisv());
521 StringBuffer sb(cx);
522 if (!sb.append("(new Number(") ||
523 !NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
524 !sb.append("))"))
526 return false;
529 JSString *str = sb.finishString();
530 if (!str)
531 return false;
532 args.rval().setString(str);
533 return true;
536 static bool
537 num_toSource(JSContext *cx, unsigned argc, Value *vp)
539 CallArgs args = CallArgsFromVp(argc, vp);
540 return CallNonGenericMethod<IsNumber, num_toSource_impl>(cx, args);
542 #endif
544 ToCStringBuf::ToCStringBuf() :dbuf(nullptr)
546 JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
549 ToCStringBuf::~ToCStringBuf()
551 js_free(dbuf);
554 MOZ_ALWAYS_INLINE
555 static JSFlatString *
556 LookupDtoaCache(ThreadSafeContext *cx, double d)
558 if (!cx->isExclusiveContext())
559 return nullptr;
561 if (JSCompartment *comp = cx->asExclusiveContext()->compartment()) {
562 if (JSFlatString *str = comp->dtoaCache.lookup(10, d))
563 return str;
566 return nullptr;
569 MOZ_ALWAYS_INLINE
570 static void
571 CacheNumber(ThreadSafeContext *cx, double d, JSFlatString *str)
573 if (!cx->isExclusiveContext())
574 return;
576 if (JSCompartment *comp = cx->asExclusiveContext()->compartment())
577 comp->dtoaCache.cache(10, d, str);
580 MOZ_ALWAYS_INLINE
581 static JSFlatString *
582 LookupInt32ToString(ThreadSafeContext *cx, int32_t si)
584 if (si >= 0 && StaticStrings::hasInt(si))
585 return cx->staticStrings().getInt(si);
587 return LookupDtoaCache(cx, si);
590 template <typename T>
591 MOZ_ALWAYS_INLINE
592 static T *
593 BackfillInt32InBuffer(int32_t si, T *buffer, size_t size, size_t *length)
595 uint32_t ui = Abs(si);
596 JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
598 RangedPtr<T> end(buffer + size - 1, buffer, size);
599 *end = '\0';
600 RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
601 if (si < 0)
602 *--start = '-';
604 *length = end - start;
605 return start.get();
608 template <AllowGC allowGC>
609 JSFlatString *
610 js::Int32ToString(ThreadSafeContext *cx, int32_t si)
612 if (JSFlatString *str = LookupInt32ToString(cx, si))
613 return str;
615 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
616 size_t length;
617 Latin1Char *start = BackfillInt32InBuffer(si, buffer, ArrayLength(buffer), &length);
619 mozilla::Range<const Latin1Char> chars(start, length);
620 JSInlineString *str = NewFatInlineString<allowGC>(cx, chars);
621 if (!str)
622 return nullptr;
624 CacheNumber(cx, si, str);
625 return str;
628 template JSFlatString *
629 js::Int32ToString<CanGC>(ThreadSafeContext *cx, int32_t si);
631 template JSFlatString *
632 js::Int32ToString<NoGC>(ThreadSafeContext *cx, int32_t si);
634 JSAtom *
635 js::Int32ToAtom(ExclusiveContext *cx, int32_t si)
637 if (JSFlatString *str = LookupInt32ToString(cx, si))
638 return js::AtomizeString(cx, str);
640 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
641 size_t length;
642 char *start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
644 JSAtom *atom = Atomize(cx, start, length);
645 if (!atom)
646 return nullptr;
648 CacheNumber(cx, si, atom);
649 return atom;
652 /* Returns a non-nullptr pointer to inside cbuf. */
653 static char *
654 Int32ToCString(ToCStringBuf *cbuf, int32_t i, size_t *len, int base = 10)
656 uint32_t u = Abs(i);
658 RangedPtr<char> cp(cbuf->sbuf + ToCStringBuf::sbufSize - 1, cbuf->sbuf, ToCStringBuf::sbufSize);
659 char *end = cp.get();
660 *cp = '\0';
662 /* Build the string from behind. */
663 switch (base) {
664 case 10:
665 cp = BackfillIndexInCharBuffer(u, cp);
666 break;
667 case 16:
668 do {
669 unsigned newu = u / 16;
670 *--cp = "0123456789abcdef"[u - newu * 16];
671 u = newu;
672 } while (u != 0);
673 break;
674 default:
675 JS_ASSERT(base >= 2 && base <= 36);
676 do {
677 unsigned newu = u / base;
678 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
679 u = newu;
680 } while (u != 0);
681 break;
683 if (i < 0)
684 *--cp = '-';
686 *len = end - cp.get();
687 return cp.get();
690 template <AllowGC allowGC>
691 static JSString * JS_FASTCALL
692 js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base);
694 MOZ_ALWAYS_INLINE bool
695 num_toString_impl(JSContext *cx, CallArgs args)
697 JS_ASSERT(IsNumber(args.thisv()));
699 double d = Extract(args.thisv());
701 int32_t base = 10;
702 if (args.hasDefined(0)) {
703 double d2;
704 if (!ToInteger(cx, args[0], &d2))
705 return false;
707 if (d2 < 2 || d2 > 36) {
708 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
709 return false;
712 base = int32_t(d2);
714 JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, base);
715 if (!str) {
716 JS_ReportOutOfMemory(cx);
717 return false;
719 args.rval().setString(str);
720 return true;
723 bool
724 js_num_toString(JSContext *cx, unsigned argc, Value *vp)
726 CallArgs args = CallArgsFromVp(argc, vp);
727 return CallNonGenericMethod<IsNumber, num_toString_impl>(cx, args);
730 #if !EXPOSE_INTL_API
731 MOZ_ALWAYS_INLINE bool
732 num_toLocaleString_impl(JSContext *cx, CallArgs args)
734 JS_ASSERT(IsNumber(args.thisv()));
736 double d = Extract(args.thisv());
738 Rooted<JSString*> str(cx, js_NumberToStringWithBase<CanGC>(cx, d, 10));
739 if (!str) {
740 JS_ReportOutOfMemory(cx);
741 return false;
745 * Create the string, move back to bytes to make string twiddling
746 * a bit easier and so we can insert platform charset seperators.
748 JSAutoByteString numBytes(cx, str);
749 if (!numBytes)
750 return false;
751 const char *num = numBytes.ptr();
752 if (!num)
753 return false;
756 * Find the first non-integer value, whether it be a letter as in
757 * 'Infinity', a decimal point, or an 'e' from exponential notation.
759 const char *nint = num;
760 if (*nint == '-')
761 nint++;
762 while (*nint >= '0' && *nint <= '9')
763 nint++;
764 int digits = nint - num;
765 const char *end = num + digits;
766 if (!digits) {
767 args.rval().setString(str);
768 return true;
771 JSRuntime *rt = cx->runtime();
772 size_t thousandsLength = strlen(rt->thousandsSeparator);
773 size_t decimalLength = strlen(rt->decimalSeparator);
775 /* Figure out how long resulting string will be. */
776 int buflen = strlen(num);
777 if (*nint == '.')
778 buflen += decimalLength - 1; /* -1 to account for existing '.' */
780 const char *numGrouping;
781 const char *tmpGroup;
782 numGrouping = tmpGroup = rt->numGrouping;
783 int remainder = digits;
784 if (*num == '-')
785 remainder--;
787 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
788 if (*tmpGroup >= remainder)
789 break;
790 buflen += thousandsLength;
791 remainder -= *tmpGroup;
792 tmpGroup++;
795 int nrepeat;
796 if (*tmpGroup == '\0' && *numGrouping != '\0') {
797 nrepeat = (remainder - 1) / tmpGroup[-1];
798 buflen += thousandsLength * nrepeat;
799 remainder -= nrepeat * tmpGroup[-1];
800 } else {
801 nrepeat = 0;
803 tmpGroup--;
805 char *buf = cx->pod_malloc<char>(buflen + 1);
806 if (!buf)
807 return false;
809 char *tmpDest = buf;
810 const char *tmpSrc = num;
812 while (*tmpSrc == '-' || remainder--) {
813 JS_ASSERT(tmpDest - buf < buflen);
814 *tmpDest++ = *tmpSrc++;
816 while (tmpSrc < end) {
817 JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
818 strcpy(tmpDest, rt->thousandsSeparator);
819 tmpDest += thousandsLength;
820 JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
821 js_memcpy(tmpDest, tmpSrc, *tmpGroup);
822 tmpDest += *tmpGroup;
823 tmpSrc += *tmpGroup;
824 if (--nrepeat < 0)
825 tmpGroup--;
828 if (*nint == '.') {
829 JS_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
830 strcpy(tmpDest, rt->decimalSeparator);
831 tmpDest += decimalLength;
832 JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
833 strcpy(tmpDest, nint + 1);
834 } else {
835 JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
836 strcpy(tmpDest, nint);
839 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) {
840 Rooted<Value> v(cx, StringValue(str));
841 bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
842 if (ok)
843 args.rval().set(v);
844 js_free(buf);
845 return ok;
848 str = NewStringCopyN<CanGC>(cx, buf, buflen);
849 js_free(buf);
850 if (!str)
851 return false;
853 args.rval().setString(str);
854 return true;
857 static bool
858 num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
860 CallArgs args = CallArgsFromVp(argc, vp);
861 return CallNonGenericMethod<IsNumber, num_toLocaleString_impl>(cx, args);
863 #endif /* !EXPOSE_INTL_API */
865 MOZ_ALWAYS_INLINE bool
866 num_valueOf_impl(JSContext *cx, CallArgs args)
868 JS_ASSERT(IsNumber(args.thisv()));
869 args.rval().setNumber(Extract(args.thisv()));
870 return true;
873 bool
874 js_num_valueOf(JSContext *cx, unsigned argc, Value *vp)
876 CallArgs args = CallArgsFromVp(argc, vp);
877 return CallNonGenericMethod<IsNumber, num_valueOf_impl>(cx, args);
880 static const unsigned MAX_PRECISION = 100;
882 static bool
883 ComputePrecisionInRange(JSContext *cx, int minPrecision, int maxPrecision, HandleValue v,
884 int *precision)
886 double prec;
887 if (!ToInteger(cx, v, &prec))
888 return false;
889 if (minPrecision <= prec && prec <= maxPrecision) {
890 *precision = int(prec);
891 return true;
894 ToCStringBuf cbuf;
895 if (char *numStr = NumberToCString(cx, &cbuf, prec, 10))
896 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE, numStr);
897 return false;
900 static bool
901 DToStrResult(JSContext *cx, double d, JSDToStrMode mode, int precision, CallArgs args)
903 char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION + 1)];
904 char *numStr = js_dtostr(cx->mainThread().dtoaState, buf, sizeof buf, mode, precision, d);
905 if (!numStr) {
906 JS_ReportOutOfMemory(cx);
907 return false;
909 JSString *str = NewStringCopyZ<CanGC>(cx, numStr);
910 if (!str)
911 return false;
912 args.rval().setString(str);
913 return true;
917 * In the following three implementations, we allow a larger range of precision
918 * than ECMA requires; this is permitted by ECMA-262.
920 MOZ_ALWAYS_INLINE bool
921 num_toFixed_impl(JSContext *cx, CallArgs args)
923 JS_ASSERT(IsNumber(args.thisv()));
925 int precision;
926 if (args.length() == 0) {
927 precision = 0;
928 } else {
929 if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, args[0], &precision))
930 return false;
933 return DToStrResult(cx, Extract(args.thisv()), DTOSTR_FIXED, precision, args);
936 static bool
937 num_toFixed(JSContext *cx, unsigned argc, Value *vp)
939 CallArgs args = CallArgsFromVp(argc, vp);
940 return CallNonGenericMethod<IsNumber, num_toFixed_impl>(cx, args);
943 MOZ_ALWAYS_INLINE bool
944 num_toExponential_impl(JSContext *cx, CallArgs args)
946 JS_ASSERT(IsNumber(args.thisv()));
948 JSDToStrMode mode;
949 int precision;
950 if (!args.hasDefined(0)) {
951 mode = DTOSTR_STANDARD_EXPONENTIAL;
952 precision = 0;
953 } else {
954 mode = DTOSTR_EXPONENTIAL;
955 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, args[0], &precision))
956 return false;
959 return DToStrResult(cx, Extract(args.thisv()), mode, precision + 1, args);
962 static bool
963 num_toExponential(JSContext *cx, unsigned argc, Value *vp)
965 CallArgs args = CallArgsFromVp(argc, vp);
966 return CallNonGenericMethod<IsNumber, num_toExponential_impl>(cx, args);
969 MOZ_ALWAYS_INLINE bool
970 num_toPrecision_impl(JSContext *cx, CallArgs args)
972 JS_ASSERT(IsNumber(args.thisv()));
974 double d = Extract(args.thisv());
976 if (!args.hasDefined(0)) {
977 JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10);
978 if (!str) {
979 JS_ReportOutOfMemory(cx);
980 return false;
982 args.rval().setString(str);
983 return true;
986 int precision;
987 if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, args[0], &precision))
988 return false;
990 return DToStrResult(cx, d, DTOSTR_PRECISION, precision, args);
993 static bool
994 num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
996 CallArgs args = CallArgsFromVp(argc, vp);
997 return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
1000 static const JSFunctionSpec number_methods[] = {
1001 #if JS_HAS_TOSOURCE
1002 JS_FN(js_toSource_str, num_toSource, 0, 0),
1003 #endif
1004 JS_FN(js_toString_str, js_num_toString, 1, 0),
1005 #if EXPOSE_INTL_API
1006 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0,0),
1007 #else
1008 JS_FN(js_toLocaleString_str, num_toLocaleString, 0,0),
1009 #endif
1010 JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
1011 JS_FN("toFixed", num_toFixed, 1, 0),
1012 JS_FN("toExponential", num_toExponential, 1, 0),
1013 JS_FN("toPrecision", num_toPrecision, 1, 0),
1014 JS_FS_END
1017 // ES6 draft ES6 15.7.3.12
1018 static bool
1019 Number_isInteger(JSContext *cx, unsigned argc, Value *vp)
1021 CallArgs args = CallArgsFromVp(argc, vp);
1022 if (args.length() < 1 || !args[0].isNumber()) {
1023 args.rval().setBoolean(false);
1024 return true;
1026 Value val = args[0];
1027 args.rval().setBoolean(val.isInt32() ||
1028 (mozilla::IsFinite(val.toDouble()) &&
1029 ToInteger(val.toDouble()) == val.toDouble()));
1030 return true;
1034 static const JSFunctionSpec number_static_methods[] = {
1035 JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1,0),
1036 JS_FN("isInteger", Number_isInteger, 1, 0),
1037 JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1,0),
1038 JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
1039 JS_FN("parseFloat", num_parseFloat, 1, 0),
1040 JS_FN("parseInt", num_parseInt, 2, 0),
1041 JS_FS_END
1045 /* NB: Keep this in synch with number_constants[]. */
1046 enum nc_slot {
1047 NC_NaN,
1048 NC_POSITIVE_INFINITY,
1049 NC_NEGATIVE_INFINITY,
1050 NC_MAX_VALUE,
1051 NC_MIN_VALUE,
1052 NC_MAX_SAFE_INTEGER,
1053 NC_MIN_SAFE_INTEGER,
1054 NC_EPSILON,
1055 NC_LIMIT
1059 * Some to most C compilers forbid spelling these at compile time, or barf
1060 * if you try, so all but MAX_VALUE are set up by InitRuntimeNumberState
1061 * using union jsdpun.
1063 static JSConstDoubleSpec number_constants[] = {
1064 {0, "NaN", 0,{0,0,0}},
1065 {0, "POSITIVE_INFINITY", 0,{0,0,0}},
1066 {0, "NEGATIVE_INFINITY", 0,{0,0,0}},
1067 {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}},
1068 {0, "MIN_VALUE", 0,{0,0,0}},
1069 /* ES6 (April 2014 draft) 20.1.2.6 */
1070 {9007199254740991, "MAX_SAFE_INTEGER", 0,{0,0,0}},
1071 /* ES6 (April 2014 draft) 20.1.2.10 */
1072 {-9007199254740991, "MIN_SAFE_INTEGER", 0,{0,0,0}},
1073 /* ES6 (May 2013 draft) 15.7.3.7 */
1074 {2.2204460492503130808472633361816e-16, "EPSILON", 0,{0,0,0}},
1075 {0,0,0,{0,0,0}}
1079 * Set the exception mask to mask all exceptions and set the FPU precision
1080 * to 53 bit mantissa (64 bit doubles).
1082 void
1083 js::FIX_FPU()
1085 #if (defined __GNUC__ && defined __i386__) || \
1086 (defined __SUNPRO_CC && defined __i386)
1087 short control;
1088 asm("fstcw %0" : "=m" (control) : );
1089 control &= ~0x300; // Lower bits 8 and 9 (precision control).
1090 control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
1091 asm("fldcw %0" : : "m" (control) );
1092 #endif
1095 bool
1096 js::InitRuntimeNumberState(JSRuntime *rt)
1098 FIX_FPU();
1101 * Our NaN must be one particular canonical value, because we rely on NaN
1102 * encoding for our value representation. See Value.h.
1104 number_constants[NC_NaN].dval = GenericNaN();
1106 number_constants[NC_POSITIVE_INFINITY].dval = mozilla::PositiveInfinity<double>();
1107 number_constants[NC_NEGATIVE_INFINITY].dval = mozilla::NegativeInfinity<double>();
1109 number_constants[NC_MIN_VALUE].dval = MinNumberValue<double>();
1111 // XXX If EXPOSE_INTL_API becomes true all the time at some point,
1112 // js::InitRuntimeNumberState is no longer fallible, and we should
1113 // change its return type.
1114 #if !EXPOSE_INTL_API
1115 /* Copy locale-specific separators into the runtime strings. */
1116 const char *thousandsSeparator, *decimalPoint, *grouping;
1117 #ifdef HAVE_LOCALECONV
1118 struct lconv *locale = localeconv();
1119 thousandsSeparator = locale->thousands_sep;
1120 decimalPoint = locale->decimal_point;
1121 grouping = locale->grouping;
1122 #else
1123 thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
1124 decimalPoint = getenv("LOCALE_DECIMAL_POINT");
1125 grouping = getenv("LOCALE_GROUPING");
1126 #endif
1127 if (!thousandsSeparator)
1128 thousandsSeparator = "'";
1129 if (!decimalPoint)
1130 decimalPoint = ".";
1131 if (!grouping)
1132 grouping = "\3\0";
1135 * We use single malloc to get the memory for all separator and grouping
1136 * strings.
1138 size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
1139 size_t decimalPointSize = strlen(decimalPoint) + 1;
1140 size_t groupingSize = strlen(grouping) + 1;
1142 char *storage = js_pod_malloc<char>(thousandsSeparatorSize +
1143 decimalPointSize +
1144 groupingSize);
1145 if (!storage)
1146 return false;
1148 js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
1149 rt->thousandsSeparator = storage;
1150 storage += thousandsSeparatorSize;
1152 js_memcpy(storage, decimalPoint, decimalPointSize);
1153 rt->decimalSeparator = storage;
1154 storage += decimalPointSize;
1156 js_memcpy(storage, grouping, groupingSize);
1157 rt->numGrouping = grouping;
1158 #endif /* !EXPOSE_INTL_API */
1159 return true;
1162 #if !EXPOSE_INTL_API
1163 void
1164 js::FinishRuntimeNumberState(JSRuntime *rt)
1167 * The free also releases the memory for decimalSeparator and numGrouping
1168 * strings.
1170 char *storage = const_cast<char *>(rt->thousandsSeparator);
1171 js_free(storage);
1173 #endif
1175 JSObject *
1176 js_InitNumberClass(JSContext *cx, HandleObject obj)
1178 JS_ASSERT(obj->isNative());
1180 /* XXX must do at least once per new thread, so do it per JSContext... */
1181 FIX_FPU();
1183 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
1185 RootedObject numberProto(cx, global->createBlankPrototype(cx, &NumberObject::class_));
1186 if (!numberProto)
1187 return nullptr;
1188 numberProto->as<NumberObject>().setPrimitiveValue(0);
1190 RootedFunction ctor(cx);
1191 ctor = global->createConstructor(cx, Number, cx->names().Number, 1);
1192 if (!ctor)
1193 return nullptr;
1195 if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
1196 return nullptr;
1198 /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */
1199 if (!JS_DefineConstDoubles(cx, ctor, number_constants))
1200 return nullptr;
1202 if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, number_static_methods))
1203 return nullptr;
1205 if (!DefinePropertiesAndFunctions(cx, numberProto, nullptr, number_methods))
1206 return nullptr;
1208 if (!JS_DefineFunctions(cx, global, number_functions))
1209 return nullptr;
1211 RootedValue valueNaN(cx, cx->runtime()->NaNValue);
1212 RootedValue valueInfinity(cx, cx->runtime()->positiveInfinityValue);
1214 /* ES5 15.1.1.1, 15.1.1.2 */
1215 if (!DefineNativeProperty(cx, global, cx->names().NaN, valueNaN,
1216 JS_PropertyStub, JS_StrictPropertyStub,
1217 JSPROP_PERMANENT | JSPROP_READONLY) ||
1218 !DefineNativeProperty(cx, global, cx->names().Infinity, valueInfinity,
1219 JS_PropertyStub, JS_StrictPropertyStub,
1220 JSPROP_PERMANENT | JSPROP_READONLY))
1222 return nullptr;
1225 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Number, ctor, numberProto))
1226 return nullptr;
1228 return numberProto;
1231 static char *
1232 FracNumberToCString(ThreadSafeContext *cx, ToCStringBuf *cbuf, double d, int base = 10)
1234 #ifdef DEBUG
1236 int32_t _;
1237 JS_ASSERT(!mozilla::NumberIsInt32(d, &_));
1239 #endif
1241 char* numStr;
1242 if (base == 10) {
1244 * This is V8's implementation of the algorithm described in the
1245 * following paper:
1247 * Printing floating-point numbers quickly and accurately with integers.
1248 * Florian Loitsch, PLDI 2010.
1250 const double_conversion::DoubleToStringConverter &converter
1251 = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1252 double_conversion::StringBuilder builder(cbuf->sbuf, cbuf->sbufSize);
1253 converter.ToShortest(d, &builder);
1254 numStr = builder.Finalize();
1255 } else {
1256 numStr = cbuf->dbuf = js_dtobasestr(cx->dtoaState(), base, d);
1258 return numStr;
1261 char *
1262 js::NumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base/* = 10*/)
1264 int32_t i;
1265 size_t len;
1266 return mozilla::NumberIsInt32(d, &i)
1267 ? Int32ToCString(cbuf, i, &len, base)
1268 : FracNumberToCString(cx, cbuf, d, base);
1271 template <AllowGC allowGC>
1272 static JSString * JS_FASTCALL
1273 js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base)
1275 ToCStringBuf cbuf;
1276 char *numStr;
1279 * Caller is responsible for error reporting. When called from trace,
1280 * returning nullptr here will cause us to fall of trace and then retry
1281 * from the interpreter (which will report the error).
1283 if (base < 2 || base > 36)
1284 return nullptr;
1286 JSCompartment *comp = cx->isExclusiveContext()
1287 ? cx->asExclusiveContext()->compartment()
1288 : nullptr;
1290 int32_t i;
1291 if (mozilla::NumberIsInt32(d, &i)) {
1292 if (base == 10 && StaticStrings::hasInt(i))
1293 return cx->staticStrings().getInt(i);
1294 if (unsigned(i) < unsigned(base)) {
1295 if (i < 10)
1296 return cx->staticStrings().getInt(i);
1297 jschar c = 'a' + i - 10;
1298 JS_ASSERT(StaticStrings::hasUnit(c));
1299 return cx->staticStrings().getUnit(c);
1302 if (comp) {
1303 if (JSFlatString *str = comp->dtoaCache.lookup(base, d))
1304 return str;
1307 size_t len;
1308 numStr = Int32ToCString(&cbuf, i, &len, base);
1309 JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1310 } else {
1311 if (comp) {
1312 if (JSFlatString *str = comp->dtoaCache.lookup(base, d))
1313 return str;
1316 numStr = FracNumberToCString(cx, &cbuf, d, base);
1317 if (!numStr) {
1318 js_ReportOutOfMemory(cx);
1319 return nullptr;
1321 JS_ASSERT_IF(base == 10,
1322 !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1323 JS_ASSERT_IF(base != 10,
1324 cbuf.dbuf && cbuf.dbuf == numStr);
1327 JSFlatString *s = NewStringCopyZ<allowGC>(cx, numStr);
1329 if (comp)
1330 comp->dtoaCache.cache(base, d, s);
1332 return s;
1335 template <AllowGC allowGC>
1336 JSString *
1337 js::NumberToString(ThreadSafeContext *cx, double d)
1339 return js_NumberToStringWithBase<allowGC>(cx, d, 10);
1342 template JSString *
1343 js::NumberToString<CanGC>(ThreadSafeContext *cx, double d);
1345 template JSString *
1346 js::NumberToString<NoGC>(ThreadSafeContext *cx, double d);
1348 JSAtom *
1349 js::NumberToAtom(ExclusiveContext *cx, double d)
1351 int32_t si;
1352 if (mozilla::NumberIsInt32(d, &si))
1353 return Int32ToAtom(cx, si);
1355 if (JSFlatString *str = LookupDtoaCache(cx, d))
1356 return AtomizeString(cx, str);
1358 ToCStringBuf cbuf;
1359 char *numStr = FracNumberToCString(cx, &cbuf, d);
1360 if (!numStr) {
1361 js_ReportOutOfMemory(cx);
1362 return nullptr;
1364 JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1366 size_t length = strlen(numStr);
1367 JSAtom *atom = Atomize(cx, numStr, length);
1368 if (!atom)
1369 return nullptr;
1371 CacheNumber(cx, d, atom);
1373 return atom;
1376 JSFlatString *
1377 js::NumberToString(JSContext *cx, double d)
1379 if (JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10))
1380 return &str->asFlat();
1381 return nullptr;
1384 JSFlatString *
1385 js::IndexToString(JSContext *cx, uint32_t index)
1387 if (StaticStrings::hasUint(index))
1388 return cx->staticStrings().getUint(index);
1390 JSCompartment *c = cx->compartment();
1391 if (JSFlatString *str = c->dtoaCache.lookup(10, index))
1392 return str;
1394 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
1395 RangedPtr<Latin1Char> end(buffer + JSFatInlineString::MAX_LENGTH_LATIN1,
1396 buffer, JSFatInlineString::MAX_LENGTH_LATIN1 + 1);
1397 *end = '\0';
1398 RangedPtr<Latin1Char> start = BackfillIndexInCharBuffer(index, end);
1400 mozilla::Range<const Latin1Char> chars(start.get(), end - start);
1401 JSInlineString *str = NewFatInlineString<CanGC>(cx, chars);
1402 if (!str)
1403 return nullptr;
1405 c->dtoaCache.cache(10, index, str);
1406 return str;
1409 bool JS_FASTCALL
1410 js::NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
1412 /* Convert to C-string. */
1413 ToCStringBuf cbuf;
1414 const char *cstr;
1415 size_t cstrlen;
1416 if (v.isInt32()) {
1417 cstr = Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
1418 JS_ASSERT(cstrlen == strlen(cstr));
1419 } else {
1420 cstr = NumberToCString(cx, &cbuf, v.toDouble());
1421 if (!cstr) {
1422 JS_ReportOutOfMemory(cx);
1423 return false;
1425 cstrlen = strlen(cstr);
1429 * Inflate to jschar string. The input C-string characters are < 127, so
1430 * even if jschars are UTF-8, all chars should map to one jschar.
1432 JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
1433 return sb.append(cstr, cstrlen);
1436 template <typename CharT>
1437 static bool
1438 CharsToNumber(ThreadSafeContext *cx, const CharT *chars, size_t length, double *result)
1440 if (length == 1) {
1441 CharT c = chars[0];
1442 if ('0' <= c && c <= '9')
1443 *result = c - '0';
1444 else if (unicode::IsSpace(c))
1445 *result = 0.0;
1446 else
1447 *result = GenericNaN();
1448 return true;
1451 const CharT *end = chars + length;
1452 const CharT *bp = SkipSpace(chars, end);
1454 /* ECMA doesn't allow signed hex numbers (bug 273467). */
1455 if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
1457 * It's probably a hex number. Accept if there's at least one hex
1458 * digit after the 0x, and if no non-whitespace characters follow all
1459 * the hex digits.
1461 const CharT *endptr;
1462 double d;
1463 if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
1464 endptr == bp + 2 ||
1465 SkipSpace(endptr, end) != end)
1467 *result = GenericNaN();
1468 } else {
1469 *result = d;
1471 return true;
1475 * Note that ECMA doesn't treat a string beginning with a '0' as
1476 * an octal number here. This works because all such numbers will
1477 * be interpreted as decimal by js_strtod. Also, any hex numbers
1478 * that have made it here (which can only be negative ones) will
1479 * be treated as 0 without consuming the 'x' by js_strtod.
1481 const CharT *ep;
1482 double d;
1483 if (!js_strtod(cx, bp, end, &ep, &d)) {
1484 *result = GenericNaN();
1485 return false;
1488 if (SkipSpace(ep, end) != end)
1489 *result = GenericNaN();
1490 else
1491 *result = d;
1493 return true;
1496 bool
1497 js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
1499 AutoCheckCannotGC nogc;
1500 ScopedThreadSafeStringInspector inspector(str);
1501 if (!inspector.ensureChars(cx, nogc))
1502 return false;
1504 return inspector.hasLatin1Chars()
1505 ? CharsToNumber(cx, inspector.latin1Chars(), str->length(), result)
1506 : CharsToNumber(cx, inspector.twoByteChars(), str->length(), result);
1509 bool
1510 js::NonObjectToNumberSlow(ThreadSafeContext *cx, Value v, double *out)
1512 JS_ASSERT(!v.isNumber());
1513 JS_ASSERT(!v.isObject());
1515 if (v.isString())
1516 return StringToNumber(cx, v.toString(), out);
1517 if (v.isBoolean()) {
1518 *out = v.toBoolean() ? 1.0 : 0.0;
1519 return true;
1521 if (v.isNull()) {
1522 *out = 0.0;
1523 return true;
1525 if (v.isSymbol()) {
1526 JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr, JSMSG_SYMBOL_TO_NUMBER);
1527 return false;
1530 JS_ASSERT(v.isUndefined());
1531 *out = GenericNaN();
1532 return true;
1535 bool
1536 js::ToNumberSlow(ExclusiveContext *cx, Value v, double *out)
1538 JS_ASSERT(!v.isNumber());
1539 goto skip_int_double;
1540 for (;;) {
1541 if (v.isNumber()) {
1542 *out = v.toNumber();
1543 return true;
1546 skip_int_double:
1547 if (!v.isObject())
1548 return NonObjectToNumberSlow(cx, v, out);
1550 if (!cx->isJSContext())
1551 return false;
1553 RootedValue v2(cx, v);
1554 if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
1555 return false;
1556 v = v2;
1557 if (v.isObject())
1558 break;
1561 *out = GenericNaN();
1562 return true;
1565 JS_PUBLIC_API(bool)
1566 js::ToNumberSlow(JSContext *cx, Value v, double *out)
1568 return ToNumberSlow(static_cast<ExclusiveContext *>(cx), v, out);
1572 * Convert a value to an int64_t, according to the WebIDL rules for long long
1573 * conversion. Return converted value in *out on success, false on failure.
1575 JS_PUBLIC_API(bool)
1576 js::ToInt64Slow(JSContext *cx, const HandleValue v, int64_t *out)
1578 JS_ASSERT(!v.isInt32());
1579 double d;
1580 if (v.isDouble()) {
1581 d = v.toDouble();
1582 } else {
1583 if (!ToNumberSlow(cx, v, &d))
1584 return false;
1586 *out = ToInt64(d);
1587 return true;
1591 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
1592 * conversion. Return converted value in *out on success, false on failure.
1594 JS_PUBLIC_API(bool)
1595 js::ToUint64Slow(JSContext *cx, const HandleValue v, uint64_t *out)
1597 JS_ASSERT(!v.isInt32());
1598 double d;
1599 if (v.isDouble()) {
1600 d = v.toDouble();
1601 } else {
1602 if (!ToNumberSlow(cx, v, &d))
1603 return false;
1605 *out = ToUint64(d);
1606 return true;
1609 template <typename ContextType,
1610 bool (*ToNumberSlowFn)(ContextType *, Value, double *),
1611 typename ValueType>
1612 static bool
1613 ToInt32SlowImpl(ContextType *cx, const ValueType v, int32_t *out)
1615 JS_ASSERT(!v.isInt32());
1616 double d;
1617 if (v.isDouble()) {
1618 d = v.toDouble();
1619 } else {
1620 if (!ToNumberSlowFn(cx, v, &d))
1621 return false;
1623 *out = ToInt32(d);
1624 return true;
1627 JS_PUBLIC_API(bool)
1628 js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out)
1630 return ToInt32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
1633 bool
1634 js::NonObjectToInt32Slow(ThreadSafeContext *cx, const Value &v, int32_t *out)
1636 return ToInt32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
1639 template <typename ContextType,
1640 bool (*ToNumberSlowFn)(ContextType *, Value, double *),
1641 typename ValueType>
1642 static bool
1643 ToUint32SlowImpl(ContextType *cx, const ValueType v, uint32_t *out)
1645 JS_ASSERT(!v.isInt32());
1646 double d;
1647 if (v.isDouble()) {
1648 d = v.toDouble();
1649 } else {
1650 if (!ToNumberSlowFn(cx, v, &d))
1651 return false;
1653 *out = ToUint32(d);
1654 return true;
1657 JS_PUBLIC_API(bool)
1658 js::ToUint32Slow(JSContext *cx, const HandleValue v, uint32_t *out)
1660 return ToUint32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
1663 bool
1664 js::NonObjectToUint32Slow(ThreadSafeContext *cx, const Value &v, uint32_t *out)
1666 return ToUint32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
1669 JS_PUBLIC_API(bool)
1670 js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
1672 JS_ASSERT(!v.isInt32());
1673 double d;
1674 if (v.isDouble()) {
1675 d = v.toDouble();
1676 } else if (!ToNumberSlow(cx, v, &d)) {
1677 return false;
1680 if (d == 0 || !mozilla::IsFinite(d)) {
1681 *out = 0;
1682 return true;
1685 uint16_t u = (uint16_t) d;
1686 if ((double)u == d) {
1687 *out = u;
1688 return true;
1691 bool neg = (d < 0);
1692 d = floor(neg ? -d : d);
1693 d = neg ? -d : d;
1694 unsigned m = JS_BIT(16);
1695 d = fmod(d, (double) m);
1696 if (d < 0)
1697 d += m;
1698 *out = (uint16_t) d;
1699 return true;
1702 template <typename CharT>
1703 bool
1704 js_strtod(ThreadSafeContext *cx, const CharT *begin, const CharT *end, const CharT **dEnd,
1705 double *d)
1707 const CharT *s = SkipSpace(begin, end);
1708 size_t length = end - s;
1710 Vector<char, 32> chars(cx);
1711 if (!chars.growByUninitialized(length + 1))
1712 return false;
1714 size_t i = 0;
1715 for (; i < length; i++) {
1716 jschar c = s[i];
1717 if (c >> 8)
1718 break;
1719 chars[i] = char(c);
1721 chars[i] = 0;
1723 /* Try to parse +Infinity, -Infinity or Infinity. */
1725 char *afterSign = chars.begin();
1726 bool negative = (*afterSign == '-');
1727 if (negative || *afterSign == '+')
1728 afterSign++;
1730 if (*afterSign == 'I' && !strncmp(afterSign, "Infinity", 8)) {
1731 *d = negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
1732 *dEnd = s + (afterSign - chars.begin()) + 8;
1733 return true;
1737 /* Everything else. */
1738 int err;
1739 char *ep;
1740 *d = js_strtod_harder(cx->dtoaState(), chars.begin(), &ep, &err);
1742 MOZ_ASSERT(ep >= chars.begin());
1744 if (ep == chars.begin())
1745 *dEnd = begin;
1746 else
1747 *dEnd = s + (ep - chars.begin());
1749 return true;
1752 template bool
1753 js_strtod(ThreadSafeContext *cx, const jschar *begin, const jschar *end, const jschar **dEnd,
1754 double *d);
1756 template bool
1757 js_strtod(ThreadSafeContext *cx, const Latin1Char *begin, const Latin1Char *end,
1758 const Latin1Char **dEnd, double *d);