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/. */
6 
7 /*
8  * JS number type and wrapper class.
9  */
10 
11 #include "jsnum.h"
12 
13 #include "mozilla/double-conversion.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/PodOperations.h"
16 #include "mozilla/RangedPtr.h"
17 
18 #ifdef HAVE_LOCALECONV
19 #include <locale.h>
20 #endif
21 #include <math.h>
22 #include <string.h>
23 
24 #include "jsatom.h"
25 #include "jscntxt.h"
26 #include "jsdtoa.h"
27 #include "jsobj.h"
28 #include "jsstr.h"
29 #include "jstypes.h"
30 
31 #include "js/Conversions.h"
32 #include "vm/GlobalObject.h"
33 #include "vm/StringBuffer.h"
34 
35 #include "jsatominlines.h"
36 
37 #include "vm/NativeObject-inl.h"
38 #include "vm/NumberObject-inl.h"
39 #include "vm/String-inl.h"
40 
41 using namespace js;
42 
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;
50 
51 using JS::AutoCheckCannotGC;
52 using JS::GenericNaN;
53 using JS::ToInt8;
54 using JS::ToInt16;
55 using JS::ToInt32;
56 using JS::ToInt64;
57 using JS::ToUint32;
58 using JS::ToUint64;
59 
60 /*
61  * If we're accumulating a decimal number and the number is >= 2^53, then the
62  * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
63  * Call js_strtod_harder to get the correct answer.
64  */
65 template <typename CharT>
66 static bool
ComputeAccurateDecimalInteger(ExclusiveContext * cx,const CharT * start,const CharT * end,double * dp)67 ComputeAccurateDecimalInteger(ExclusiveContext* cx, const CharT* start, const CharT* end,
68                               double* dp)
69 {
70     size_t length = end - start;
71     ScopedJSFreePtr<char> cstr(cx->pod_malloc<char>(length + 1));
72     if (!cstr)
73         return false;
74 
75     for (size_t i = 0; i < length; i++) {
76         char c = char(start[i]);
77         MOZ_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
78         cstr[i] = c;
79     }
80     cstr[length] = 0;
81 
82     char* estr;
83     int err = 0;
84     *dp = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
85     if (err == JS_DTOA_ENOMEM) {
86         ReportOutOfMemory(cx);
87         return false;
88     }
89 
90     return true;
91 }
92 
93 namespace {
94 
95 template <typename CharT>
96 class BinaryDigitReader
97 {
98     const int base;      /* Base of number; must be a power of 2 */
99     int digit;           /* Current digit value in radix given by base */
100     int digitMask;       /* Mask to extract the next bit from digit */
101     const CharT* start;  /* Pointer to the remaining digits */
102     const CharT* end;    /* Pointer to first non-digit */
103 
104   public:
BinaryDigitReader(int base,const CharT * start,const CharT * end)105     BinaryDigitReader(int base, const CharT* start, const CharT* end)
106       : base(base), digit(0), digitMask(0), start(start), end(end)
107     {
108     }
109 
110     /* Return the next binary digit from the number, or -1 if done. */
nextDigit()111     int nextDigit() {
112         if (digitMask == 0) {
113             if (start == end)
114                 return -1;
115 
116             int c = *start++;
117             MOZ_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
118             if ('0' <= c && c <= '9')
119                 digit = c - '0';
120             else if ('a' <= c && c <= 'z')
121                 digit = c - 'a' + 10;
122             else
123                 digit = c - 'A' + 10;
124             digitMask = base >> 1;
125         }
126 
127         int bit = (digit & digitMask) != 0;
128         digitMask >>= 1;
129         return bit;
130     }
131 };
132 
133 } /* anonymous namespace */
134 
135 /*
136  * The fast result might also have been inaccurate for power-of-two bases. This
137  * happens if the addition in value * 2 + digit causes a round-down to an even
138  * least significant mantissa bit when the first dropped bit is a one.  If any
139  * of the following digits in the number (which haven't been added in yet) are
140  * nonzero, then the correct action would have been to round up instead of
141  * down.  An example occurs when reading the number 0x1000000000000081, which
142  * rounds to 0x1000000000000000 instead of 0x1000000000000100.
143  */
144 template <typename CharT>
145 static double
ComputeAccurateBinaryBaseInteger(const CharT * start,const CharT * end,int base)146 ComputeAccurateBinaryBaseInteger(const CharT* start, const CharT* end, int base)
147 {
148     BinaryDigitReader<CharT> bdr(base, start, end);
149 
150     /* Skip leading zeroes. */
151     int bit;
152     do {
153         bit = bdr.nextDigit();
154     } while (bit == 0);
155 
156     MOZ_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
157 
158     /* Gather the 53 significant bits (including the leading 1). */
159     double value = 1.0;
160     for (int j = 52; j > 0; j--) {
161         bit = bdr.nextDigit();
162         if (bit < 0)
163             return value;
164         value = value * 2 + bit;
165     }
166 
167     /* bit2 is the 54th bit (the first dropped from the mantissa). */
168     int bit2 = bdr.nextDigit();
169     if (bit2 >= 0) {
170         double factor = 2.0;
171         int sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */
172         int bit3;
173 
174         while ((bit3 = bdr.nextDigit()) >= 0) {
175             sticky |= bit3;
176             factor *= 2;
177         }
178         value += bit2 & (bit | sticky);
179         value *= factor;
180     }
181 
182     return value;
183 }
184 
185 template <typename CharT>
186 double
ParseDecimalNumber(const mozilla::Range<const CharT> chars)187 js::ParseDecimalNumber(const mozilla::Range<const CharT> chars)
188 {
189     MOZ_ASSERT(chars.length() > 0);
190     uint64_t dec = 0;
191     RangedPtr<const CharT> s = chars.start(), end = chars.end();
192     do {
193         CharT c = *s;
194         MOZ_ASSERT('0' <= c && c <= '9');
195         uint8_t digit = c - '0';
196         uint64_t next = dec * 10 + digit;
197         MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
198                    "next value won't be an integrally-precise double");
199         dec = next;
200     } while (++s < end);
201     return static_cast<double>(dec);
202 }
203 
204 template double
205 js::ParseDecimalNumber(const mozilla::Range<const Latin1Char> chars);
206 
207 template double
208 js::ParseDecimalNumber(const mozilla::Range<const char16_t> chars);
209 
210 template <typename CharT>
211 bool
GetPrefixInteger(ExclusiveContext * cx,const CharT * start,const CharT * end,int base,const CharT ** endp,double * dp)212 js::GetPrefixInteger(ExclusiveContext* cx, const CharT* start, const CharT* end, int base,
213                      const CharT** endp, double* dp)
214 {
215     MOZ_ASSERT(start <= end);
216     MOZ_ASSERT(2 <= base && base <= 36);
217 
218     const CharT* s = start;
219     double d = 0.0;
220     for (; s < end; s++) {
221         int digit;
222         CharT c = *s;
223         if ('0' <= c && c <= '9')
224             digit = c - '0';
225         else if ('a' <= c && c <= 'z')
226             digit = c - 'a' + 10;
227         else if ('A' <= c && c <= 'Z')
228             digit = c - 'A' + 10;
229         else
230             break;
231         if (digit >= base)
232             break;
233         d = d * base + digit;
234     }
235 
236     *endp = s;
237     *dp = d;
238 
239     /* If we haven't reached the limit of integer precision, we're done. */
240     if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
241         return true;
242 
243     /*
244      * Otherwise compute the correct integer from the prefix of valid digits
245      * if we're computing for base ten or a power of two.  Don't worry about
246      * other bases; see 15.1.2.2 step 13.
247      */
248     if (base == 10)
249         return ComputeAccurateDecimalInteger(cx, start, s, dp);
250 
251     if ((base & (base - 1)) == 0)
252         *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
253 
254     return true;
255 }
256 
257 template bool
258 js::GetPrefixInteger(ExclusiveContext* cx, const char16_t* start, const char16_t* end, int base,
259                      const char16_t** endp, double* dp);
260 
261 template bool
262 js::GetPrefixInteger(ExclusiveContext* cx, const Latin1Char* start, const Latin1Char* end,
263                      int base, const Latin1Char** endp, double* dp);
264 
265 bool
GetDecimalInteger(ExclusiveContext * cx,const char16_t * start,const char16_t * end,double * dp)266 js::GetDecimalInteger(ExclusiveContext* cx, const char16_t* start, const char16_t* end, double* dp)
267 {
268     MOZ_ASSERT(start <= end);
269 
270     const char16_t* s = start;
271     double d = 0.0;
272     for (; s < end; s++) {
273         char16_t c = *s;
274         MOZ_ASSERT('0' <= c && c <= '9');
275         int digit = c - '0';
276         d = d * 10 + digit;
277     }
278 
279     *dp = d;
280 
281     // If we haven't reached the limit of integer precision, we're done.
282     if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
283         return true;
284 
285     // Otherwise compute the correct integer from the prefix of valid digits.
286     return ComputeAccurateDecimalInteger(cx, start, s, dp);
287 }
288 
289 static bool
num_parseFloat(JSContext * cx,unsigned argc,Value * vp)290 num_parseFloat(JSContext* cx, unsigned argc, Value* vp)
291 {
292     CallArgs args = CallArgsFromVp(argc, vp);
293 
294     if (args.length() == 0) {
295         args.rval().setNaN();
296         return true;
297     }
298 
299     JSString* str = ToString<CanGC>(cx, args[0]);
300     if (!str)
301         return false;
302 
303     JSLinearString* linear = str->ensureLinear(cx);
304     if (!linear)
305         return false;
306 
307     double d;
308     AutoCheckCannotGC nogc;
309     if (linear->hasLatin1Chars()) {
310         const Latin1Char* begin = linear->latin1Chars(nogc);
311         const Latin1Char* end;
312         if (!js_strtod(cx, begin, begin + linear->length(), &end, &d))
313             return false;
314         if (end == begin)
315             d = GenericNaN();
316     } else {
317         const char16_t* begin = linear->twoByteChars(nogc);
318         const char16_t* end;
319         if (!js_strtod(cx, begin, begin + linear->length(), &end, &d))
320             return false;
321         if (end == begin)
322             d = GenericNaN();
323     }
324 
325     args.rval().setDouble(d);
326     return true;
327 }
328 
329 template <typename CharT>
330 static bool
ParseIntImpl(JSContext * cx,const CharT * chars,size_t length,bool stripPrefix,int32_t radix,double * res)331 ParseIntImpl(JSContext* cx, const CharT* chars, size_t length, bool stripPrefix, int32_t radix,
332              double* res)
333 {
334     /* Step 2. */
335     const CharT* end = chars + length;
336     const CharT* s = SkipSpace(chars, end);
337 
338     MOZ_ASSERT(chars <= s);
339     MOZ_ASSERT(s <= end);
340 
341     /* Steps 3-4. */
342     bool negative = (s != end && s[0] == '-');
343 
344     /* Step 5. */
345     if (s != end && (s[0] == '-' || s[0] == '+'))
346         s++;
347 
348     /* Step 10. */
349     if (stripPrefix) {
350         if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
351             s += 2;
352             radix = 16;
353         }
354     }
355 
356     /* Steps 11-15. */
357     const CharT* actualEnd;
358     double d;
359     if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, &d))
360         return false;
361 
362     if (s == actualEnd)
363         *res = GenericNaN();
364     else
365         *res = negative ? -d : d;
366     return true;
367 }
368 
369 /* ES5 15.1.2.2. */
370 bool
num_parseInt(JSContext * cx,unsigned argc,Value * vp)371 js::num_parseInt(JSContext* cx, unsigned argc, Value* vp)
372 {
373     CallArgs args = CallArgsFromVp(argc, vp);
374 
375     /* Fast paths and exceptional cases. */
376     if (args.length() == 0) {
377         args.rval().setNaN();
378         return true;
379     }
380 
381     if (args.length() == 1 ||
382         (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) {
383         if (args[0].isInt32()) {
384             args.rval().set(args[0]);
385             return true;
386         }
387 
388         /*
389          * Step 1 is |inputString = ToString(string)|. When string >=
390          * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
391          * the word, which would mean the result of parseInt(string) should be |N|.
392          *
393          * To preserve this behaviour, we can't use the fast-path when string >
394          * 1e21, or else the result would be |NeM|.
395          *
396          * The same goes for values smaller than 1.0e-6, because the string would be in
397          * the form of "Ne-M".
398          */
399         if (args[0].isDouble()) {
400             double d = args[0].toDouble();
401             if (1.0e-6 < d && d < 1.0e21) {
402                 args.rval().setNumber(floor(d));
403                 return true;
404             }
405             if (-1.0e21 < d && d < -1.0e-6) {
406                 args.rval().setNumber(-floor(-d));
407                 return true;
408             }
409             if (d == 0.0) {
410                 args.rval().setInt32(0);
411                 return true;
412             }
413         }
414     }
415 
416     /* Step 1. */
417     RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
418     if (!inputString)
419         return false;
420     args[0].setString(inputString);
421 
422     /* Steps 6-9. */
423     bool stripPrefix = true;
424     int32_t radix;
425     if (!args.hasDefined(1)) {
426         radix = 10;
427     } else {
428         if (!ToInt32(cx, args[1], &radix))
429             return false;
430         if (radix == 0) {
431             radix = 10;
432         } else {
433             if (radix < 2 || radix > 36) {
434                 args.rval().setNaN();
435                 return true;
436             }
437             if (radix != 16)
438                 stripPrefix = false;
439         }
440     }
441 
442     JSLinearString* linear = inputString->ensureLinear(cx);
443     if (!linear)
444         return false;
445 
446     AutoCheckCannotGC nogc;
447     size_t length = inputString->length();
448     double number;
449     if (linear->hasLatin1Chars()) {
450         if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix, &number))
451             return false;
452     } else {
453         if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix, radix, &number))
454             return false;
455     }
456 
457     args.rval().setNumber(number);
458     return true;
459 }
460 
461 static const JSFunctionSpec number_functions[] = {
462     JS_SELF_HOSTED_FN(js_isNaN_str, "Global_isNaN", 1, JSPROP_RESOLVING),
463     JS_SELF_HOSTED_FN(js_isFinite_str, "Global_isFinite", 1, JSPROP_RESOLVING),
464     JS_FS_END
465 };
466 
467 const Class NumberObject::class_ = {
468     js_Number_str,
469     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number)
470 };
471 
472 static bool
Number(JSContext * cx,unsigned argc,Value * vp)473 Number(JSContext* cx, unsigned argc, Value* vp)
474 {
475     CallArgs args = CallArgsFromVp(argc, vp);
476 
477     /* Sample JS_CALLEE before clobbering. */
478     bool isConstructing = args.isConstructing();
479 
480     if (args.length() > 0) {
481         if (!ToNumber(cx, args[0]))
482             return false;
483         args.rval().set(args[0]);
484     } else {
485         args.rval().setInt32(0);
486     }
487 
488     if (!isConstructing)
489         return true;
490 
491     RootedObject newTarget(cx, &args.newTarget().toObject());
492     RootedObject proto(cx);
493     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
494         return false;
495     JSObject* obj = NumberObject::create(cx, args.rval().toNumber(), proto);
496     if (!obj)
497         return false;
498     args.rval().setObject(*obj);
499     return true;
500 }
501 
502 MOZ_ALWAYS_INLINE bool
IsNumber(HandleValue v)503 IsNumber(HandleValue v)
504 {
505     return v.isNumber() || (v.isObject() && v.toObject().is<NumberObject>());
506 }
507 
508 static inline double
Extract(const Value & v)509 Extract(const Value& v)
510 {
511     if (v.isNumber())
512         return v.toNumber();
513     return v.toObject().as<NumberObject>().unbox();
514 }
515 
516 #if JS_HAS_TOSOURCE
517 MOZ_ALWAYS_INLINE bool
num_toSource_impl(JSContext * cx,const CallArgs & args)518 num_toSource_impl(JSContext* cx, const CallArgs& args)
519 {
520     double d = Extract(args.thisv());
521 
522     StringBuffer sb(cx);
523     if (!sb.append("(new Number(") ||
524         !NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
525         !sb.append("))"))
526     {
527         return false;
528     }
529 
530     JSString* str = sb.finishString();
531     if (!str)
532         return false;
533     args.rval().setString(str);
534     return true;
535 }
536 
537 static bool
num_toSource(JSContext * cx,unsigned argc,Value * vp)538 num_toSource(JSContext* cx, unsigned argc, Value* vp)
539 {
540     CallArgs args = CallArgsFromVp(argc, vp);
541     return CallNonGenericMethod<IsNumber, num_toSource_impl>(cx, args);
542 }
543 #endif
544 
ToCStringBuf()545 ToCStringBuf::ToCStringBuf() : dbuf(nullptr)
546 {
547     static_assert(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE,
548                   "builtin space must be large enough to store even the "
549                   "longest string produced by a conversion");
550 }
551 
~ToCStringBuf()552 ToCStringBuf::~ToCStringBuf()
553 {
554     js_free(dbuf);
555 }
556 
557 MOZ_ALWAYS_INLINE
558 static JSFlatString*
LookupDtoaCache(ExclusiveContext * cx,double d)559 LookupDtoaCache(ExclusiveContext* cx, double d)
560 {
561     if (JSCompartment* comp = cx->compartment()) {
562         if (JSFlatString* str = comp->dtoaCache.lookup(10, d))
563             return str;
564     }
565 
566     return nullptr;
567 }
568 
569 MOZ_ALWAYS_INLINE
570 static void
CacheNumber(ExclusiveContext * cx,double d,JSFlatString * str)571 CacheNumber(ExclusiveContext* cx, double d, JSFlatString* str)
572 {
573     if (JSCompartment* comp = cx->compartment())
574         comp->dtoaCache.cache(10, d, str);
575 }
576 
577 MOZ_ALWAYS_INLINE
578 static JSFlatString*
LookupInt32ToString(ExclusiveContext * cx,int32_t si)579 LookupInt32ToString(ExclusiveContext* cx, int32_t si)
580 {
581     if (si >= 0 && StaticStrings::hasInt(si))
582         return cx->staticStrings().getInt(si);
583 
584     return LookupDtoaCache(cx, si);
585 }
586 
587 template <typename T>
588 MOZ_ALWAYS_INLINE
589 static T*
BackfillInt32InBuffer(int32_t si,T * buffer,size_t size,size_t * length)590 BackfillInt32InBuffer(int32_t si, T* buffer, size_t size, size_t* length)
591 {
592     uint32_t ui = Abs(si);
593     MOZ_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
594 
595     RangedPtr<T> end(buffer + size - 1, buffer, size);
596     *end = '\0';
597     RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
598     if (si < 0)
599         *--start = '-';
600 
601     *length = end - start;
602     return start.get();
603 }
604 
605 template <AllowGC allowGC>
606 JSFlatString*
Int32ToString(ExclusiveContext * cx,int32_t si)607 js::Int32ToString(ExclusiveContext* cx, int32_t si)
608 {
609     if (JSFlatString* str = LookupInt32ToString(cx, si))
610         return str;
611 
612     Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
613     size_t length;
614     Latin1Char* start = BackfillInt32InBuffer(si, buffer, ArrayLength(buffer), &length);
615 
616     mozilla::Range<const Latin1Char> chars(start, length);
617     JSInlineString* str = NewInlineString<allowGC>(cx, chars);
618     if (!str)
619         return nullptr;
620 
621     CacheNumber(cx, si, str);
622     return str;
623 }
624 
625 template JSFlatString*
626 js::Int32ToString<CanGC>(ExclusiveContext* cx, int32_t si);
627 
628 template JSFlatString*
629 js::Int32ToString<NoGC>(ExclusiveContext* cx, int32_t si);
630 
631 JSAtom*
Int32ToAtom(ExclusiveContext * cx,int32_t si)632 js::Int32ToAtom(ExclusiveContext* cx, int32_t si)
633 {
634     if (JSFlatString* str = LookupInt32ToString(cx, si))
635         return js::AtomizeString(cx, str);
636 
637     char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
638     size_t length;
639     char* start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
640 
641     JSAtom* atom = Atomize(cx, start, length);
642     if (!atom)
643         return nullptr;
644 
645     CacheNumber(cx, si, atom);
646     return atom;
647 }
648 
649 /* Returns a non-nullptr pointer to inside cbuf.  */
650 static char*
Int32ToCString(ToCStringBuf * cbuf,int32_t i,size_t * len,int base=10)651 Int32ToCString(ToCStringBuf* cbuf, int32_t i, size_t* len, int base = 10)
652 {
653     uint32_t u = Abs(i);
654 
655     RangedPtr<char> cp(cbuf->sbuf + ToCStringBuf::sbufSize - 1, cbuf->sbuf, ToCStringBuf::sbufSize);
656     char* end = cp.get();
657     *cp = '\0';
658 
659     /* Build the string from behind. */
660     switch (base) {
661     case 10:
662       cp = BackfillIndexInCharBuffer(u, cp);
663       break;
664     case 16:
665       do {
666           unsigned newu = u / 16;
667           *--cp = "0123456789abcdef"[u - newu * 16];
668           u = newu;
669       } while (u != 0);
670       break;
671     default:
672       MOZ_ASSERT(base >= 2 && base <= 36);
673       do {
674           unsigned newu = u / base;
675           *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
676           u = newu;
677       } while (u != 0);
678       break;
679     }
680     if (i < 0)
681         *--cp = '-';
682 
683     *len = end - cp.get();
684     return cp.get();
685 }
686 
687 template <AllowGC allowGC>
688 static JSString*
689 NumberToStringWithBase(ExclusiveContext* cx, double d, int base);
690 
691 MOZ_ALWAYS_INLINE bool
num_toString_impl(JSContext * cx,const CallArgs & args)692 num_toString_impl(JSContext* cx, const CallArgs& args)
693 {
694     MOZ_ASSERT(IsNumber(args.thisv()));
695 
696     double d = Extract(args.thisv());
697 
698     int32_t base = 10;
699     if (args.hasDefined(0)) {
700         double d2;
701         if (!ToInteger(cx, args[0], &d2))
702             return false;
703 
704         if (d2 < 2 || d2 > 36) {
705             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
706             return false;
707         }
708 
709         base = int32_t(d2);
710     }
711     JSString* str = NumberToStringWithBase<CanGC>(cx, d, base);
712     if (!str) {
713         JS_ReportOutOfMemory(cx);
714         return false;
715     }
716     args.rval().setString(str);
717     return true;
718 }
719 
720 bool
num_toString(JSContext * cx,unsigned argc,Value * vp)721 js::num_toString(JSContext* cx, unsigned argc, Value* vp)
722 {
723     CallArgs args = CallArgsFromVp(argc, vp);
724     return CallNonGenericMethod<IsNumber, num_toString_impl>(cx, args);
725 }
726 
727 #if !EXPOSE_INTL_API
728 MOZ_ALWAYS_INLINE bool
num_toLocaleString_impl(JSContext * cx,const CallArgs & args)729 num_toLocaleString_impl(JSContext* cx, const CallArgs& args)
730 {
731     MOZ_ASSERT(IsNumber(args.thisv()));
732 
733     double d = Extract(args.thisv());
734 
735     RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
736     if (!str) {
737         JS_ReportOutOfMemory(cx);
738         return false;
739     }
740 
741     /*
742      * Create the string, move back to bytes to make string twiddling
743      * a bit easier and so we can insert platform charset seperators.
744      */
745     JSAutoByteString numBytes(cx, str);
746     if (!numBytes)
747         return false;
748     const char* num = numBytes.ptr();
749     if (!num)
750         return false;
751 
752     /*
753      * Find the first non-integer value, whether it be a letter as in
754      * 'Infinity', a decimal point, or an 'e' from exponential notation.
755      */
756     const char* nint = num;
757     if (*nint == '-')
758         nint++;
759     while (*nint >= '0' && *nint <= '9')
760         nint++;
761     int digits = nint - num;
762     const char* end = num + digits;
763     if (!digits) {
764         args.rval().setString(str);
765         return true;
766     }
767 
768     JSRuntime* rt = cx->runtime();
769     size_t thousandsLength = strlen(rt->thousandsSeparator);
770     size_t decimalLength = strlen(rt->decimalSeparator);
771 
772     /* Figure out how long resulting string will be. */
773     int buflen = strlen(num);
774     if (*nint == '.')
775         buflen += decimalLength - 1; /* -1 to account for existing '.' */
776 
777     const char* numGrouping;
778     const char* tmpGroup;
779     numGrouping = tmpGroup = rt->numGrouping;
780     int remainder = digits;
781     if (*num == '-')
782         remainder--;
783 
784     while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
785         if (*tmpGroup >= remainder)
786             break;
787         buflen += thousandsLength;
788         remainder -= *tmpGroup;
789         tmpGroup++;
790     }
791 
792     int nrepeat;
793     if (*tmpGroup == '\0' && *numGrouping != '\0') {
794         nrepeat = (remainder - 1) / tmpGroup[-1];
795         buflen += thousandsLength * nrepeat;
796         remainder -= nrepeat * tmpGroup[-1];
797     } else {
798         nrepeat = 0;
799     }
800     tmpGroup--;
801 
802     char* buf = cx->pod_malloc<char>(buflen + 1);
803     if (!buf)
804         return false;
805 
806     char* tmpDest = buf;
807     const char* tmpSrc = num;
808 
809     while (*tmpSrc == '-' || remainder--) {
810         MOZ_ASSERT(tmpDest - buf < buflen);
811         *tmpDest++ = *tmpSrc++;
812     }
813     while (tmpSrc < end) {
814         MOZ_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
815         strcpy(tmpDest, rt->thousandsSeparator);
816         tmpDest += thousandsLength;
817         MOZ_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
818         js_memcpy(tmpDest, tmpSrc, *tmpGroup);
819         tmpDest += *tmpGroup;
820         tmpSrc += *tmpGroup;
821         if (--nrepeat < 0)
822             tmpGroup--;
823     }
824 
825     if (*nint == '.') {
826         MOZ_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
827         strcpy(tmpDest, rt->decimalSeparator);
828         tmpDest += decimalLength;
829         MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
830         strcpy(tmpDest, nint + 1);
831     } else {
832         MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
833         strcpy(tmpDest, nint);
834     }
835 
836     if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) {
837         Rooted<Value> v(cx, StringValue(str));
838         bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
839         if (ok)
840             args.rval().set(v);
841         js_free(buf);
842         return ok;
843     }
844 
845     str = NewStringCopyN<CanGC>(cx, buf, buflen);
846     js_free(buf);
847     if (!str)
848         return false;
849 
850     args.rval().setString(str);
851     return true;
852 }
853 
854 static bool
num_toLocaleString(JSContext * cx,unsigned argc,Value * vp)855 num_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
856 {
857     CallArgs args = CallArgsFromVp(argc, vp);
858     return CallNonGenericMethod<IsNumber, num_toLocaleString_impl>(cx, args);
859 }
860 #endif /* !EXPOSE_INTL_API */
861 
862 MOZ_ALWAYS_INLINE bool
num_valueOf_impl(JSContext * cx,const CallArgs & args)863 num_valueOf_impl(JSContext* cx, const CallArgs& args)
864 {
865     MOZ_ASSERT(IsNumber(args.thisv()));
866     args.rval().setNumber(Extract(args.thisv()));
867     return true;
868 }
869 
870 bool
num_valueOf(JSContext * cx,unsigned argc,Value * vp)871 js::num_valueOf(JSContext* cx, unsigned argc, Value* vp)
872 {
873     CallArgs args = CallArgsFromVp(argc, vp);
874     return CallNonGenericMethod<IsNumber, num_valueOf_impl>(cx, args);
875 }
876 
877 static const unsigned MAX_PRECISION = 100;
878 
879 static bool
ComputePrecisionInRange(JSContext * cx,int minPrecision,int maxPrecision,HandleValue v,int * precision)880 ComputePrecisionInRange(JSContext* cx, int minPrecision, int maxPrecision, HandleValue v,
881                         int* precision)
882 {
883     double prec;
884     if (!ToInteger(cx, v, &prec))
885         return false;
886     if (minPrecision <= prec && prec <= maxPrecision) {
887         *precision = int(prec);
888         return true;
889     }
890 
891     ToCStringBuf cbuf;
892     if (char* numStr = NumberToCString(cx, &cbuf, prec, 10))
893         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE, numStr);
894     return false;
895 }
896 
897 static bool
DToStrResult(JSContext * cx,double d,JSDToStrMode mode,int precision,const CallArgs & args)898 DToStrResult(JSContext* cx, double d, JSDToStrMode mode, int precision, const CallArgs& args)
899 {
900     char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION + 1)];
901     char* numStr = js_dtostr(cx->mainThread().dtoaState, buf, sizeof buf, mode, precision, d);
902     if (!numStr) {
903         JS_ReportOutOfMemory(cx);
904         return false;
905     }
906     JSString* str = NewStringCopyZ<CanGC>(cx, numStr);
907     if (!str)
908         return false;
909     args.rval().setString(str);
910     return true;
911 }
912 
913 /*
914  * In the following three implementations, we allow a larger range of precision
915  * than ECMA requires; this is permitted by ECMA-262.
916  */
917 MOZ_ALWAYS_INLINE bool
num_toFixed_impl(JSContext * cx,const CallArgs & args)918 num_toFixed_impl(JSContext* cx, const CallArgs& args)
919 {
920     MOZ_ASSERT(IsNumber(args.thisv()));
921 
922     int precision;
923     if (args.length() == 0) {
924         precision = 0;
925     } else {
926         if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, args[0], &precision))
927             return false;
928     }
929 
930     return DToStrResult(cx, Extract(args.thisv()), DTOSTR_FIXED, precision, args);
931 }
932 
933 static bool
num_toFixed(JSContext * cx,unsigned argc,Value * vp)934 num_toFixed(JSContext* cx, unsigned argc, Value* vp)
935 {
936     CallArgs args = CallArgsFromVp(argc, vp);
937     return CallNonGenericMethod<IsNumber, num_toFixed_impl>(cx, args);
938 }
939 
940 MOZ_ALWAYS_INLINE bool
num_toExponential_impl(JSContext * cx,const CallArgs & args)941 num_toExponential_impl(JSContext* cx, const CallArgs& args)
942 {
943     MOZ_ASSERT(IsNumber(args.thisv()));
944 
945     JSDToStrMode mode;
946     int precision;
947     if (!args.hasDefined(0)) {
948         mode = DTOSTR_STANDARD_EXPONENTIAL;
949         precision = 0;
950     } else {
951         mode = DTOSTR_EXPONENTIAL;
952         if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, args[0], &precision))
953             return false;
954     }
955 
956     return DToStrResult(cx, Extract(args.thisv()), mode, precision + 1, args);
957 }
958 
959 static bool
num_toExponential(JSContext * cx,unsigned argc,Value * vp)960 num_toExponential(JSContext* cx, unsigned argc, Value* vp)
961 {
962     CallArgs args = CallArgsFromVp(argc, vp);
963     return CallNonGenericMethod<IsNumber, num_toExponential_impl>(cx, args);
964 }
965 
966 MOZ_ALWAYS_INLINE bool
num_toPrecision_impl(JSContext * cx,const CallArgs & args)967 num_toPrecision_impl(JSContext* cx, const CallArgs& args)
968 {
969     MOZ_ASSERT(IsNumber(args.thisv()));
970 
971     double d = Extract(args.thisv());
972 
973     if (!args.hasDefined(0)) {
974         JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
975         if (!str) {
976             JS_ReportOutOfMemory(cx);
977             return false;
978         }
979         args.rval().setString(str);
980         return true;
981     }
982 
983     int precision;
984     if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, args[0], &precision))
985         return false;
986 
987     return DToStrResult(cx, d, DTOSTR_PRECISION, precision, args);
988 }
989 
990 static bool
num_toPrecision(JSContext * cx,unsigned argc,Value * vp)991 num_toPrecision(JSContext* cx, unsigned argc, Value* vp)
992 {
993     CallArgs args = CallArgsFromVp(argc, vp);
994     return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
995 }
996 
997 static const JSFunctionSpec number_methods[] = {
998 #if JS_HAS_TOSOURCE
999     JS_FN(js_toSource_str,       num_toSource,          0, 0),
1000 #endif
1001     JS_FN(js_toString_str,       num_toString,          1, 0),
1002 #if EXPOSE_INTL_API
1003     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0,0),
1004 #else
1005     JS_FN(js_toLocaleString_str, num_toLocaleString,     0,0),
1006 #endif
1007     JS_FN(js_valueOf_str,        num_valueOf,           0, 0),
1008     JS_FN("toFixed",             num_toFixed,           1, 0),
1009     JS_FN("toExponential",       num_toExponential,     1, 0),
1010     JS_FN("toPrecision",         num_toPrecision,       1, 0),
1011     JS_FS_END
1012 };
1013 
1014 // ES6 draft ES6 15.7.3.12
1015 static bool
Number_isInteger(JSContext * cx,unsigned argc,Value * vp)1016 Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
1017 {
1018     CallArgs args = CallArgsFromVp(argc, vp);
1019     if (args.length() < 1 || !args[0].isNumber()) {
1020         args.rval().setBoolean(false);
1021         return true;
1022     }
1023     Value val = args[0];
1024     args.rval().setBoolean(val.isInt32() ||
1025                            (mozilla::IsFinite(val.toDouble()) &&
1026                             JS::ToInteger(val.toDouble()) == val.toDouble()));
1027     return true;
1028 }
1029 
1030 
1031 static const JSFunctionSpec number_static_methods[] = {
1032     JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1,0),
1033     JS_FN("isInteger", Number_isInteger, 1, 0),
1034     JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1,0),
1035     JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1,0),
1036     JS_FS_END
1037 };
1038 
1039 
1040 /*
1041  * Set the exception mask to mask all exceptions and set the FPU precision
1042  * to 53 bit mantissa (64 bit doubles).
1043  */
1044 void
FIX_FPU()1045 js::FIX_FPU()
1046 {
1047 #if (defined __GNUC__ && defined __i386__) || \
1048     (defined __SUNPRO_CC && defined __i386)
1049     short control;
1050     asm("fstcw %0" : "=m" (control) : );
1051     control &= ~0x300; // Lower bits 8 and 9 (precision control).
1052     control |= 0x2f3;  // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
1053     asm("fldcw %0" : : "m" (control) );
1054 #endif
1055 }
1056 
1057 bool
InitRuntimeNumberState(JSRuntime * rt)1058 js::InitRuntimeNumberState(JSRuntime* rt)
1059 {
1060     FIX_FPU();
1061 
1062     // XXX If EXPOSE_INTL_API becomes true all the time at some point,
1063     //     js::InitRuntimeNumberState is no longer fallible, and we should
1064     //     change its return type.
1065 #if !EXPOSE_INTL_API
1066     /* Copy locale-specific separators into the runtime strings. */
1067     const char* thousandsSeparator;
1068     const char* decimalPoint;
1069     const char* grouping;
1070 #ifdef HAVE_LOCALECONV
1071     struct lconv* locale = localeconv();
1072     thousandsSeparator = locale->thousands_sep;
1073     decimalPoint = locale->decimal_point;
1074     grouping = locale->grouping;
1075 #else
1076     thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
1077     decimalPoint = getenv("LOCALE_DECIMAL_POINT");
1078     grouping = getenv("LOCALE_GROUPING");
1079 #endif
1080     if (!thousandsSeparator)
1081         thousandsSeparator = "'";
1082     if (!decimalPoint)
1083         decimalPoint = ".";
1084     if (!grouping)
1085         grouping = "\3\0";
1086 
1087     /*
1088      * We use single malloc to get the memory for all separator and grouping
1089      * strings.
1090      */
1091     size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
1092     size_t decimalPointSize = strlen(decimalPoint) + 1;
1093     size_t groupingSize = strlen(grouping) + 1;
1094 
1095     char* storage = js_pod_malloc<char>(thousandsSeparatorSize +
1096                                         decimalPointSize +
1097                                         groupingSize);
1098     if (!storage)
1099         return false;
1100 
1101     js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
1102     rt->thousandsSeparator = storage;
1103     storage += thousandsSeparatorSize;
1104 
1105     js_memcpy(storage, decimalPoint, decimalPointSize);
1106     rt->decimalSeparator = storage;
1107     storage += decimalPointSize;
1108 
1109     js_memcpy(storage, grouping, groupingSize);
1110     rt->numGrouping = grouping;
1111 #endif /* !EXPOSE_INTL_API */
1112     return true;
1113 }
1114 
1115 #if !EXPOSE_INTL_API
1116 void
FinishRuntimeNumberState(JSRuntime * rt)1117 js::FinishRuntimeNumberState(JSRuntime* rt)
1118 {
1119     /*
1120      * The free also releases the memory for decimalSeparator and numGrouping
1121      * strings.
1122      */
1123     char* storage = const_cast<char*>(rt->thousandsSeparator);
1124     js_free(storage);
1125 }
1126 #endif
1127 
1128 JSObject*
InitNumberClass(JSContext * cx,HandleObject obj)1129 js::InitNumberClass(JSContext* cx, HandleObject obj)
1130 {
1131     MOZ_ASSERT(obj->isNative());
1132 
1133     /* XXX must do at least once per new thread, so do it per JSContext... */
1134     FIX_FPU();
1135 
1136     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
1137 
1138     RootedObject numberProto(cx, global->createBlankPrototype(cx, &NumberObject::class_));
1139     if (!numberProto)
1140         return nullptr;
1141     numberProto->as<NumberObject>().setPrimitiveValue(0);
1142 
1143     RootedFunction ctor(cx);
1144     ctor = global->createConstructor(cx, Number, cx->names().Number, 1);
1145     if (!ctor)
1146         return nullptr;
1147 
1148     if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
1149         return nullptr;
1150 
1151     /*
1152      * Our NaN must be one particular canonical value, because we rely on NaN
1153      * encoding for our value representation.  See Value.h.
1154      */
1155     static JSConstDoubleSpec number_constants[] = {
1156         {"NaN",               GenericNaN()               },
1157         {"POSITIVE_INFINITY", mozilla::PositiveInfinity<double>() },
1158         {"NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>() },
1159         {"MAX_VALUE",         1.7976931348623157E+308    },
1160         {"MIN_VALUE",         MinNumberValue<double>()   },
1161         /* ES6 (April 2014 draft) 20.1.2.6 */
1162         {"MAX_SAFE_INTEGER",  9007199254740991           },
1163         /* ES6 (April 2014 draft) 20.1.2.10 */
1164         {"MIN_SAFE_INTEGER", -9007199254740991,          },
1165         /* ES6 (May 2013 draft) 15.7.3.7 */
1166         {"EPSILON", 2.2204460492503130808472633361816e-16},
1167         {0,0}
1168     };
1169 
1170     /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */
1171     if (!JS_DefineConstDoubles(cx, ctor, number_constants))
1172         return nullptr;
1173 
1174     if (!DefinePropertiesAndFunctions(cx, ctor, nullptr, number_static_methods))
1175         return nullptr;
1176 
1177     if (!DefinePropertiesAndFunctions(cx, numberProto, nullptr, number_methods))
1178         return nullptr;
1179 
1180     if (!JS_DefineFunctions(cx, global, number_functions))
1181         return nullptr;
1182 
1183     /* Number.parseInt should be the same function object as global parseInt. */
1184     RootedId parseIntId(cx, NameToId(cx->names().parseInt));
1185     JSFunction* parseInt = DefineFunction(cx, global, parseIntId, num_parseInt, 2,
1186                                           JSPROP_RESOLVING);
1187     if (!parseInt)
1188         return nullptr;
1189     RootedValue parseIntValue(cx, ObjectValue(*parseInt));
1190     if (!DefineProperty(cx, ctor, parseIntId, parseIntValue, nullptr, nullptr, 0))
1191         return nullptr;
1192 
1193     /* Number.parseFloat should be the same function object as global parseFloat. */
1194     RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
1195     JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId, num_parseFloat, 1,
1196                                             JSPROP_RESOLVING);
1197     if (!parseFloat)
1198         return nullptr;
1199     RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
1200     if (!DefineProperty(cx, ctor, parseFloatId, parseFloatValue, nullptr, nullptr, 0))
1201         return nullptr;
1202 
1203     RootedValue valueNaN(cx, cx->runtime()->NaNValue);
1204     RootedValue valueInfinity(cx, cx->runtime()->positiveInfinityValue);
1205 
1206     /* ES5 15.1.1.1, 15.1.1.2 */
1207     if (!NativeDefineProperty(cx, global, cx->names().NaN, valueNaN, nullptr, nullptr,
1208                               JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
1209         !NativeDefineProperty(cx, global, cx->names().Infinity, valueInfinity, nullptr, nullptr,
1210                               JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING))
1211     {
1212         return nullptr;
1213     }
1214 
1215     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Number, ctor, numberProto))
1216         return nullptr;
1217 
1218     return numberProto;
1219 }
1220 
1221 static char*
FracNumberToCString(ExclusiveContext * cx,ToCStringBuf * cbuf,double d,int base=10)1222 FracNumberToCString(ExclusiveContext* cx, ToCStringBuf* cbuf, double d, int base = 10)
1223 {
1224 #ifdef DEBUG
1225     {
1226         int32_t _;
1227         MOZ_ASSERT(!mozilla::NumberIsInt32(d, &_));
1228     }
1229 #endif
1230 
1231     char* numStr;
1232     if (base == 10) {
1233         /*
1234          * This is V8's implementation of the algorithm described in the
1235          * following paper:
1236          *
1237          *   Printing floating-point numbers quickly and accurately with integers.
1238          *   Florian Loitsch, PLDI 2010.
1239          */
1240         const double_conversion::DoubleToStringConverter& converter
1241             = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1242         double_conversion::StringBuilder builder(cbuf->sbuf, cbuf->sbufSize);
1243         converter.ToShortest(d, &builder);
1244         numStr = builder.Finalize();
1245     } else {
1246         numStr = cbuf->dbuf = js_dtobasestr(cx->dtoaState(), base, d);
1247     }
1248     return numStr;
1249 }
1250 
1251 char*
NumberToCString(JSContext * cx,ToCStringBuf * cbuf,double d,int base)1252 js::NumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d, int base/* = 10*/)
1253 {
1254     int32_t i;
1255     size_t len;
1256     return mozilla::NumberIsInt32(d, &i)
1257            ? Int32ToCString(cbuf, i, &len, base)
1258            : FracNumberToCString(cx, cbuf, d, base);
1259 }
1260 
1261 template <AllowGC allowGC>
1262 static JSString*
NumberToStringWithBase(ExclusiveContext * cx,double d,int base)1263 NumberToStringWithBase(ExclusiveContext* cx, double d, int base)
1264 {
1265     ToCStringBuf cbuf;
1266     char* numStr;
1267 
1268     /*
1269      * Caller is responsible for error reporting. When called from trace,
1270      * returning nullptr here will cause us to fall of trace and then retry
1271      * from the interpreter (which will report the error).
1272      */
1273     if (base < 2 || base > 36)
1274         return nullptr;
1275 
1276     JSCompartment* comp = cx->compartment();
1277 
1278     int32_t i;
1279     if (mozilla::NumberIsInt32(d, &i)) {
1280         if (base == 10 && StaticStrings::hasInt(i))
1281             return cx->staticStrings().getInt(i);
1282         if (unsigned(i) < unsigned(base)) {
1283             if (i < 10)
1284                 return cx->staticStrings().getInt(i);
1285             char16_t c = 'a' + i - 10;
1286             MOZ_ASSERT(StaticStrings::hasUnit(c));
1287             return cx->staticStrings().getUnit(c);
1288         }
1289 
1290         if (JSFlatString* str = comp->dtoaCache.lookup(base, d))
1291             return str;
1292 
1293         size_t len;
1294         numStr = Int32ToCString(&cbuf, i, &len, base);
1295         MOZ_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1296     } else {
1297         if (JSFlatString* str = comp->dtoaCache.lookup(base, d))
1298             return str;
1299 
1300         numStr = FracNumberToCString(cx, &cbuf, d, base);
1301         if (!numStr) {
1302             ReportOutOfMemory(cx);
1303             return nullptr;
1304         }
1305         MOZ_ASSERT_IF(base == 10,
1306                       !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1307         MOZ_ASSERT_IF(base != 10,
1308                       cbuf.dbuf && cbuf.dbuf == numStr);
1309     }
1310 
1311     JSFlatString* s = NewStringCopyZ<allowGC>(cx, numStr);
1312 
1313     comp->dtoaCache.cache(base, d, s);
1314     return s;
1315 }
1316 
1317 template <AllowGC allowGC>
1318 JSString*
NumberToString(ExclusiveContext * cx,double d)1319 js::NumberToString(ExclusiveContext* cx, double d)
1320 {
1321     return NumberToStringWithBase<allowGC>(cx, d, 10);
1322 }
1323 
1324 template JSString*
1325 js::NumberToString<CanGC>(ExclusiveContext* cx, double d);
1326 
1327 template JSString*
1328 js::NumberToString<NoGC>(ExclusiveContext* cx, double d);
1329 
1330 JSAtom*
NumberToAtom(ExclusiveContext * cx,double d)1331 js::NumberToAtom(ExclusiveContext* cx, double d)
1332 {
1333     int32_t si;
1334     if (mozilla::NumberIsInt32(d, &si))
1335         return Int32ToAtom(cx, si);
1336 
1337     if (JSFlatString* str = LookupDtoaCache(cx, d))
1338         return AtomizeString(cx, str);
1339 
1340     ToCStringBuf cbuf;
1341     char* numStr = FracNumberToCString(cx, &cbuf, d);
1342     if (!numStr) {
1343         ReportOutOfMemory(cx);
1344         return nullptr;
1345     }
1346     MOZ_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1347 
1348     size_t length = strlen(numStr);
1349     JSAtom* atom = Atomize(cx, numStr, length);
1350     if (!atom)
1351         return nullptr;
1352 
1353     CacheNumber(cx, d, atom);
1354 
1355     return atom;
1356 }
1357 
1358 JSFlatString*
NumberToString(JSContext * cx,double d)1359 js::NumberToString(JSContext* cx, double d)
1360 {
1361     if (JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10))
1362         return &str->asFlat();
1363     return nullptr;
1364 }
1365 
1366 JSFlatString*
IndexToString(JSContext * cx,uint32_t index)1367 js::IndexToString(JSContext* cx, uint32_t index)
1368 {
1369     if (StaticStrings::hasUint(index))
1370         return cx->staticStrings().getUint(index);
1371 
1372     JSCompartment* c = cx->compartment();
1373     if (JSFlatString* str = c->dtoaCache.lookup(10, index))
1374         return str;
1375 
1376     Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
1377     RangedPtr<Latin1Char> end(buffer + JSFatInlineString::MAX_LENGTH_LATIN1,
1378                               buffer, JSFatInlineString::MAX_LENGTH_LATIN1 + 1);
1379     *end = '\0';
1380     RangedPtr<Latin1Char> start = BackfillIndexInCharBuffer(index, end);
1381 
1382     mozilla::Range<const Latin1Char> chars(start.get(), end - start);
1383     JSInlineString* str = NewInlineString<CanGC>(cx, chars);
1384     if (!str)
1385         return nullptr;
1386 
1387     c->dtoaCache.cache(10, index, str);
1388     return str;
1389 }
1390 
1391 bool JS_FASTCALL
NumberValueToStringBuffer(JSContext * cx,const Value & v,StringBuffer & sb)1392 js::NumberValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb)
1393 {
1394     /* Convert to C-string. */
1395     ToCStringBuf cbuf;
1396     const char* cstr;
1397     size_t cstrlen;
1398     if (v.isInt32()) {
1399         cstr = Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
1400         MOZ_ASSERT(cstrlen == strlen(cstr));
1401     } else {
1402         cstr = NumberToCString(cx, &cbuf, v.toDouble());
1403         if (!cstr) {
1404             JS_ReportOutOfMemory(cx);
1405             return false;
1406         }
1407         cstrlen = strlen(cstr);
1408     }
1409 
1410     /*
1411      * Inflate to char16_t string.  The input C-string characters are < 127, so
1412      * even if char16_t units are UTF-8, all chars should map to one char16_t.
1413      */
1414     MOZ_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
1415     return sb.append(cstr, cstrlen);
1416 }
1417 
1418 template <typename CharT>
1419 static bool
CharsToNumber(ExclusiveContext * cx,const CharT * chars,size_t length,double * result)1420 CharsToNumber(ExclusiveContext* cx, const CharT* chars, size_t length, double* result)
1421 {
1422     if (length == 1) {
1423         CharT c = chars[0];
1424         if ('0' <= c && c <= '9')
1425             *result = c - '0';
1426         else if (unicode::IsSpace(c))
1427             *result = 0.0;
1428         else
1429             *result = GenericNaN();
1430         return true;
1431     }
1432 
1433     const CharT* end = chars + length;
1434     const CharT* bp = SkipSpace(chars, end);
1435 
1436     /* ECMA doesn't allow signed non-decimal numbers (bug 273467). */
1437     if (end - bp >= 2 && bp[0] == '0') {
1438         int radix = 0;
1439         if (bp[1] == 'b' || bp[1] == 'B')
1440             radix = 2;
1441         else if (bp[1] == 'o' || bp[1] == 'O')
1442             radix = 8;
1443         else if (bp[1] == 'x' || bp[1] == 'X')
1444             radix = 16;
1445 
1446         if (radix != 0) {
1447             /*
1448              * It's probably a non-decimal number. Accept if there's at least one digit after
1449              * the 0b|0o|0x, and if no non-whitespace characters follow all the digits.
1450              */
1451             const CharT* endptr;
1452             double d;
1453             if (!GetPrefixInteger(cx, bp + 2, end, radix, &endptr, &d) ||
1454                 endptr == bp + 2 ||
1455                 SkipSpace(endptr, end) != end)
1456             {
1457                 *result = GenericNaN();
1458             } else {
1459                 *result = d;
1460             }
1461             return true;
1462         }
1463     }
1464 
1465     /*
1466      * Note that ECMA doesn't treat a string beginning with a '0' as
1467      * an octal number here. This works because all such numbers will
1468      * be interpreted as decimal by js_strtod.  Also, any hex numbers
1469      * that have made it here (which can only be negative ones) will
1470      * be treated as 0 without consuming the 'x' by js_strtod.
1471      */
1472     const CharT* ep;
1473     double d;
1474     if (!js_strtod(cx, bp, end, &ep, &d)) {
1475         *result = GenericNaN();
1476         return false;
1477     }
1478 
1479     if (SkipSpace(ep, end) != end)
1480         *result = GenericNaN();
1481     else
1482         *result = d;
1483 
1484     return true;
1485 }
1486 
1487 bool
StringToNumber(ExclusiveContext * cx,JSString * str,double * result)1488 js::StringToNumber(ExclusiveContext* cx, JSString* str, double* result)
1489 {
1490     AutoCheckCannotGC nogc;
1491     JSLinearString* linearStr = str->ensureLinear(cx);
1492     if (!linearStr)
1493         return false;
1494 
1495     return linearStr->hasLatin1Chars()
1496            ? CharsToNumber(cx, linearStr->latin1Chars(nogc), str->length(), result)
1497            : CharsToNumber(cx, linearStr->twoByteChars(nogc), str->length(), result);
1498 }
1499 
1500 bool
ToNumberSlow(ExclusiveContext * cx,Value v,double * out)1501 js::ToNumberSlow(ExclusiveContext* cx, Value v, double* out)
1502 {
1503     MOZ_ASSERT(!v.isNumber());
1504     goto skip_int_double;
1505     for (;;) {
1506         if (v.isNumber()) {
1507             *out = v.toNumber();
1508             return true;
1509         }
1510 
1511       skip_int_double:
1512         if (!v.isObject()) {
1513             if (v.isString())
1514                 return StringToNumber(cx, v.toString(), out);
1515             if (v.isBoolean()) {
1516                 *out = v.toBoolean() ? 1.0 : 0.0;
1517                 return true;
1518             }
1519             if (v.isNull()) {
1520                 *out = 0.0;
1521                 return true;
1522             }
1523             if (v.isSymbol()) {
1524                 if (cx->isJSContext()) {
1525                     JS_ReportErrorNumber(cx->asJSContext(), GetErrorMessage, nullptr,
1526                                          JSMSG_SYMBOL_TO_NUMBER);
1527                 }
1528                 return false;
1529             }
1530 
1531             MOZ_ASSERT(v.isUndefined());
1532             *out = GenericNaN();
1533             return true;
1534         }
1535 
1536         if (!cx->isJSContext())
1537             return false;
1538 
1539         RootedValue v2(cx, v);
1540         if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
1541             return false;
1542         v = v2;
1543         if (v.isObject())
1544             break;
1545     }
1546 
1547     *out = GenericNaN();
1548     return true;
1549 }
1550 
JS_PUBLIC_API(bool)1551 JS_PUBLIC_API(bool)
1552 js::ToNumberSlow(JSContext* cx, Value v, double* out)
1553 {
1554     return ToNumberSlow(static_cast<ExclusiveContext*>(cx), v, out);
1555 }
1556 
1557 /*
1558  * Convert a value to an int8_t, according to the WebIDL rules for byte
1559  * conversion. Return converted value in *out on success, false on failure.
1560  */
JS_PUBLIC_API(bool)1561 JS_PUBLIC_API(bool)
1562 js::ToInt8Slow(JSContext *cx, const HandleValue v, int8_t *out)
1563 {
1564     MOZ_ASSERT(!v.isInt32());
1565     double d;
1566     if (v.isDouble()) {
1567         d = v.toDouble();
1568     } else {
1569         if (!ToNumberSlow(cx, v, &d))
1570             return false;
1571     }
1572     *out = ToInt8(d);
1573     return true;
1574 }
1575 
1576 /*
1577  * Convert a value to an int16_t, according to the WebIDL rules for short
1578  * conversion. Return converted value in *out on success, false on failure.
1579  */
JS_PUBLIC_API(bool)1580 JS_PUBLIC_API(bool)
1581 js::ToInt16Slow(JSContext *cx, const HandleValue v, int16_t *out)
1582 {
1583     MOZ_ASSERT(!v.isInt32());
1584     double d;
1585     if (v.isDouble()) {
1586         d = v.toDouble();
1587     } else {
1588         if (!ToNumberSlow(cx, v, &d))
1589             return false;
1590     }
1591     *out = ToInt16(d);
1592     return true;
1593 }
1594 
1595 /*
1596  * Convert a value to an int64_t, according to the WebIDL rules for long long
1597  * conversion. Return converted value in *out on success, false on failure.
1598  */
JS_PUBLIC_API(bool)1599 JS_PUBLIC_API(bool)
1600 js::ToInt64Slow(JSContext* cx, const HandleValue v, int64_t* out)
1601 {
1602     MOZ_ASSERT(!v.isInt32());
1603     double d;
1604     if (v.isDouble()) {
1605         d = v.toDouble();
1606     } else {
1607         if (!ToNumberSlow(cx, v, &d))
1608             return false;
1609     }
1610     *out = ToInt64(d);
1611     return true;
1612 }
1613 
1614 /*
1615  * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
1616  * conversion. Return converted value in *out on success, false on failure.
1617  */
JS_PUBLIC_API(bool)1618 JS_PUBLIC_API(bool)
1619 js::ToUint64Slow(JSContext* cx, const HandleValue v, uint64_t* out)
1620 {
1621     MOZ_ASSERT(!v.isInt32());
1622     double d;
1623     if (v.isDouble()) {
1624         d = v.toDouble();
1625     } else {
1626         if (!ToNumberSlow(cx, v, &d))
1627             return false;
1628     }
1629     *out = ToUint64(d);
1630     return true;
1631 }
1632 
JS_PUBLIC_API(bool)1633 JS_PUBLIC_API(bool)
1634 js::ToInt32Slow(JSContext* cx, const HandleValue v, int32_t* out)
1635 {
1636     MOZ_ASSERT(!v.isInt32());
1637     double d;
1638     if (v.isDouble()) {
1639         d = v.toDouble();
1640     } else {
1641         if (!ToNumberSlow(cx, v, &d))
1642             return false;
1643     }
1644     *out = ToInt32(d);
1645     return true;
1646 }
1647 
JS_PUBLIC_API(bool)1648 JS_PUBLIC_API(bool)
1649 js::ToUint32Slow(JSContext* cx, const HandleValue v, uint32_t* out)
1650 {
1651     MOZ_ASSERT(!v.isInt32());
1652     double d;
1653     if (v.isDouble()) {
1654         d = v.toDouble();
1655     } else {
1656         if (!ToNumberSlow(cx, v, &d))
1657             return false;
1658     }
1659     *out = ToUint32(d);
1660     return true;
1661 }
1662 
JS_PUBLIC_API(bool)1663 JS_PUBLIC_API(bool)
1664 js::ToUint16Slow(JSContext* cx, const HandleValue v, uint16_t* out)
1665 {
1666     MOZ_ASSERT(!v.isInt32());
1667     double d;
1668     if (v.isDouble()) {
1669         d = v.toDouble();
1670     } else if (!ToNumberSlow(cx, v, &d)) {
1671         return false;
1672     }
1673 
1674     if (d == 0 || !mozilla::IsFinite(d)) {
1675         *out = 0;
1676         return true;
1677     }
1678 
1679     uint16_t u = (uint16_t) d;
1680     if ((double)u == d) {
1681         *out = u;
1682         return true;
1683     }
1684 
1685     bool neg = (d < 0);
1686     d = floor(neg ? -d : d);
1687     d = neg ? -d : d;
1688     unsigned m = JS_BIT(16);
1689     d = fmod(d, (double) m);
1690     if (d < 0)
1691         d += m;
1692     *out = (uint16_t) d;
1693     return true;
1694 }
1695 
1696 template<typename T>
1697 bool
ToLengthClamped(T * cx,HandleValue v,uint32_t * out,bool * overflow)1698 js::ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow)
1699 {
1700     if (v.isInt32()) {
1701         int32_t i = v.toInt32();
1702         *out = i < 0 ? 0 : i;
1703         return true;
1704     }
1705     double d;
1706     if (v.isDouble()) {
1707         d = v.toDouble();
1708     } else {
1709         if (!ToNumber(cx, v, &d)) {
1710             *overflow = false;
1711             return false;
1712         }
1713     }
1714     d = JS::ToInteger(d);
1715     if (d <= 0.0) {
1716         *out = 0;
1717         return true;
1718     }
1719     if (d >= (double)0xFFFFFFFEU) {
1720         *overflow = true;
1721         return false;
1722     }
1723     *out = (uint32_t)d;
1724     return true;
1725 }
1726 
1727 template bool
1728 js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
1729 template bool
1730 js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*);
1731 
1732 template <typename CharT>
1733 bool
js_strtod(ExclusiveContext * cx,const CharT * begin,const CharT * end,const CharT ** dEnd,double * d)1734 js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd,
1735           double* d)
1736 {
1737     const CharT* s = SkipSpace(begin, end);
1738     size_t length = end - s;
1739 
1740     Vector<char, 32> chars(cx);
1741     if (!chars.growByUninitialized(length + 1))
1742         return false;
1743 
1744     size_t i = 0;
1745     for (; i < length; i++) {
1746         char16_t c = s[i];
1747         if (c >> 8)
1748             break;
1749         chars[i] = char(c);
1750     }
1751     chars[i] = 0;
1752 
1753     /* Try to parse +Infinity, -Infinity or Infinity. */
1754     {
1755         char* afterSign = chars.begin();
1756         bool negative = (*afterSign == '-');
1757         if (negative || *afterSign == '+')
1758             afterSign++;
1759 
1760         if (*afterSign == 'I' && !strncmp(afterSign, "Infinity", 8)) {
1761             *d = negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
1762             *dEnd = s + (afterSign - chars.begin()) + 8;
1763             return true;
1764         }
1765     }
1766 
1767     /* Everything else. */
1768     int err;
1769     char* ep;
1770     *d = js_strtod_harder(cx->dtoaState(), chars.begin(), &ep, &err);
1771 
1772     MOZ_ASSERT(ep >= chars.begin());
1773 
1774     if (ep == chars.begin())
1775         *dEnd = begin;
1776     else
1777         *dEnd = s + (ep - chars.begin());
1778 
1779     return true;
1780 }
1781 
1782 template bool
1783 js_strtod(ExclusiveContext* cx, const char16_t* begin, const char16_t* end, const char16_t** dEnd,
1784           double* d);
1785 
1786 template bool
1787 js_strtod(ExclusiveContext* cx, const Latin1Char* begin, const Latin1Char* end,
1788           const Latin1Char** dEnd, double* d);
1789