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