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