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