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