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