Source code

Revision control

Other Tools

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