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