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
#ifndef jsnum_h
8
#define jsnum_h
9
10
#include "mozilla/Compiler.h"
11
#include "mozilla/FloatingPoint.h"
12
#include "mozilla/Range.h"
13
#include "mozilla/Utf8.h"
14
15
#include "NamespaceImports.h"
16
17
#include "js/Conversions.h"
18
19
#include "vm/StringType.h"
20
21
// This macro is should be `one' if current compiler supports builtin functions
22
// like __builtin_sadd_overflow.
23
#if MOZ_IS_GCC
24
// GCC supports these functions.
25
# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1
26
#else
27
// For CLANG, we use its own function to check for this.
28
# ifdef __has_builtin
29
# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x)
30
# endif
31
#endif
32
#ifndef BUILTIN_CHECKED_ARITHMETIC_SUPPORTED
33
# define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 0
34
#endif
35
36
namespace js {
37
38
class GlobalObject;
39
class StringBuffer;
40
41
extern MOZ_MUST_USE bool InitRuntimeNumberState(JSRuntime* rt);
42
43
#if !ENABLE_INTL_API
44
extern void FinishRuntimeNumberState(JSRuntime* rt);
45
#endif
46
47
/* Initialize the Number class, returning its prototype object. */
48
extern JSObject* InitNumberClass(JSContext* cx, Handle<GlobalObject*> global);
49
50
/*
51
* When base == 10, this function implements ToString() as specified by
52
* ECMA-262-5 section 9.8.1; but note that it handles integers specially for
53
* performance. See also js::NumberToCString().
54
*/
55
template <AllowGC allowGC>
56
extern JSString* NumberToString(JSContext* cx, double d);
57
58
extern JSString* NumberToStringHelperPure(JSContext* cx, double d);
59
60
extern JSAtom* NumberToAtom(JSContext* cx, double d);
61
62
template <AllowGC allowGC>
63
extern JSLinearString* Int32ToString(JSContext* cx, int32_t i);
64
65
extern JSLinearString* Int32ToStringHelperPure(JSContext* cx, int32_t i);
66
67
extern JSAtom* Int32ToAtom(JSContext* cx, int32_t si);
68
69
// ES6 15.7.3.12
70
extern bool IsInteger(const Value& val);
71
72
extern bool IsInteger(double d);
73
74
/*
75
* Convert an integer or double (contained in the given value) to a string and
76
* append to the given buffer.
77
*/
78
extern MOZ_MUST_USE bool JS_FASTCALL
79
NumberValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
80
81
extern JSLinearString* IndexToString(JSContext* cx, uint32_t index);
82
83
/*
84
* Usually a small amount of static storage is enough, but sometimes we need
85
* to dynamically allocate much more. This struct encapsulates that.
86
* Dynamically allocated memory will be freed when the object is destroyed.
87
*/
88
struct ToCStringBuf {
89
/*
90
* The longest possible result that would need to fit in sbuf is
91
* (-0x80000000).toString(2), which has length 33. Longer cases are
92
* possible, but they'll go in dbuf.
93
*/
94
static const size_t sbufSize = 34;
95
char sbuf[sbufSize];
96
char* dbuf;
97
98
ToCStringBuf();
99
~ToCStringBuf();
100
};
101
102
/*
103
* Convert a number to a C string. When base==10, this function implements
104
* ToString() as specified by ECMA-262-5 section 9.8.1. It handles integral
105
* values cheaply. Return nullptr if we ran out of memory. See also
106
* NumberToCString().
107
*/
108
extern char* NumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d,
109
int base = 10);
110
111
/*
112
* The largest positive integer such that all positive integers less than it
113
* may be precisely represented using the IEEE-754 double-precision format.
114
*/
115
constexpr double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64_t(1) << 53;
116
117
/*
118
* Parse a decimal number encoded in |chars|. The decimal number must be
119
* sufficiently small that it will not overflow the integrally-precise range of
120
* the double type -- that is, the number will be smaller than
121
* DOUBLE_INTEGRAL_PRECISION_LIMIT
122
*/
123
template <typename CharT>
124
extern double ParseDecimalNumber(const mozilla::Range<const CharT> chars);
125
126
enum class IntegerSeparatorHandling : bool { None, SkipUnderscore };
127
128
/*
129
* Compute the positive integer of the given base described immediately at the
130
* start of the range [start, end) -- no whitespace-skipping, no magical
131
* leading-"0" octal or leading-"0x" hex behavior, no "+"/"-" parsing, just
132
* reading the digits of the integer. Return the index one past the end of the
133
* digits of the integer in *endp, and return the integer itself in *dp. If
134
* base is 10 or a power of two the returned integer is the closest possible
135
* double; otherwise extremely large integers may be slightly inaccurate.
136
*
137
* The |separatorHandling| controls whether or not numeric separators can be
138
* part of integer string. If the option is enabled, all '_' characters in the
139
* string are ignored. Underscore characters must not appear directly next to
140
* each other, e.g. '1__2' will lead to an assertion.
141
*
142
* If [start, end) does not begin with a number with the specified base,
143
* *dp == 0 and *endp == start upon return.
144
*/
145
template <typename CharT>
146
extern MOZ_MUST_USE bool GetPrefixInteger(
147
JSContext* cx, const CharT* start, const CharT* end, int base,
148
IntegerSeparatorHandling separatorHandling, const CharT** endp, double* dp);
149
150
inline const char16_t* ToRawChars(const char16_t* units) { return units; }
151
152
inline const unsigned char* ToRawChars(const unsigned char* units) {
153
return units;
154
}
155
156
inline const unsigned char* ToRawChars(const mozilla::Utf8Unit* units) {
157
return mozilla::Utf8AsUnsignedChars(units);
158
}
159
160
/**
161
* Like GetPrefixInteger, but [start, end) must all be digits in the given
162
* base (and so this function doesn't take a useless outparam).
163
*/
164
template <typename CharT>
165
extern MOZ_MUST_USE bool GetFullInteger(
166
JSContext* cx, const CharT* start, const CharT* end, int base,
167
IntegerSeparatorHandling separatorHandling, double* dp) {
168
decltype(ToRawChars(start)) realEnd;
169
if (GetPrefixInteger(cx, ToRawChars(start), ToRawChars(end), base,
170
separatorHandling, &realEnd, dp)) {
171
MOZ_ASSERT(end == static_cast<const void*>(realEnd));
172
return true;
173
}
174
return false;
175
}
176
177
/*
178
* This is like GetPrefixInteger, but only deals with base 10, always ignores
179
* '_', and doesn't have an |endp| outparam. It should only be used when the
180
* characters are known to match |DecimalIntegerLiteral|, cf. ES2020, 11.8.3
181
* Numeric Literals.
182
*/
183
template <typename CharT>
184
extern MOZ_MUST_USE bool GetDecimalInteger(JSContext* cx, const CharT* start,
185
const CharT* end, double* dp);
186
187
/*
188
* This is like GetDecimalInteger, but also allows non-integer numbers. It
189
* should only be used when the characters are known to match |DecimalLiteral|,
190
* cf. ES2020, 11.8.3 Numeric Literals.
191
*/
192
template <typename CharT>
193
extern MOZ_MUST_USE bool GetDecimalNonInteger(JSContext* cx, const CharT* start,
194
const CharT* end, double* dp);
195
196
extern MOZ_MUST_USE bool StringToNumber(JSContext* cx, JSString* str,
197
double* result);
198
199
extern MOZ_MUST_USE bool StringToNumberPure(JSContext* cx, JSString* str,
200
double* result);
201
202
/* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
203
MOZ_ALWAYS_INLINE MOZ_MUST_USE bool ToNumber(JSContext* cx,
204
JS::MutableHandleValue vp) {
205
if (vp.isNumber()) {
206
return true;
207
}
208
double d;
209
extern JS_PUBLIC_API bool ToNumberSlow(JSContext * cx, HandleValue v,
210
double* dp);
211
if (!ToNumberSlow(cx, vp, &d)) {
212
return false;
213
}
214
215
vp.setNumber(d);
216
return true;
217
}
218
219
bool ToNumericSlow(JSContext* cx, JS::MutableHandleValue vp);
220
221
// BigInt proposal section 3.1.6
222
MOZ_ALWAYS_INLINE MOZ_MUST_USE bool ToNumeric(JSContext* cx,
223
JS::MutableHandleValue vp) {
224
if (vp.isNumeric()) {
225
return true;
226
}
227
return ToNumericSlow(cx, vp);
228
}
229
230
bool ToInt32OrBigIntSlow(JSContext* cx, JS::MutableHandleValue vp);
231
232
MOZ_ALWAYS_INLINE MOZ_MUST_USE bool ToInt32OrBigInt(JSContext* cx,
233
JS::MutableHandleValue vp) {
234
if (vp.isInt32()) {
235
return true;
236
}
237
return ToInt32OrBigIntSlow(cx, vp);
238
}
239
240
MOZ_MUST_USE bool num_parseInt(JSContext* cx, unsigned argc, Value* vp);
241
242
} /* namespace js */
243
244
/*
245
* Similar to strtod except that it replaces overflows with infinities of the
246
* correct sign, and underflows with zeros of the correct sign. Guaranteed to
247
* return the closest double number to the given input in dp.
248
*
249
* Also allows inputs of the form [+|-]Infinity, which produce an infinity of
250
* the appropriate sign. The case of the "Infinity" string must match exactly.
251
* If the string does not contain a number, set *dEnd to begin and return 0.0
252
* in *d.
253
*
254
* Return false if out of memory.
255
*/
256
template <typename CharT>
257
extern MOZ_MUST_USE bool js_strtod(JSContext* cx, const CharT* begin,
258
const CharT* end, const CharT** dEnd,
259
double* d);
260
261
namespace js {
262
263
/**
264
* Like js_strtod, but for when the number always constitutes the entire range
265
* (and so |dEnd| would be a value already known).
266
*/
267
template <typename CharT>
268
extern MOZ_MUST_USE bool FullStringToDouble(JSContext* cx, const CharT* begin,
269
const CharT* end, double* d) {
270
decltype(ToRawChars(begin)) realEnd;
271
if (js_strtod(cx, ToRawChars(begin), ToRawChars(end), &realEnd, d)) {
272
MOZ_ASSERT(end == static_cast<const void*>(realEnd));
273
return true;
274
}
275
return false;
276
}
277
278
extern MOZ_MUST_USE bool num_toString(JSContext* cx, unsigned argc, Value* vp);
279
280
extern MOZ_MUST_USE bool num_valueOf(JSContext* cx, unsigned argc, Value* vp);
281
282
/*
283
* Returns true if the given value is definitely an index: that is, the value
284
* is a number that's an unsigned 32-bit integer.
285
*
286
* This method prioritizes common-case speed over accuracy in every case. It
287
* can produce false negatives (but not false positives): some values which are
288
* indexes will be reported not to be indexes by this method. Users must
289
* consider this possibility when using this method.
290
*/
291
static MOZ_ALWAYS_INLINE bool IsDefinitelyIndex(const Value& v,
292
uint32_t* indexp) {
293
if (v.isInt32() && v.toInt32() >= 0) {
294
*indexp = v.toInt32();
295
return true;
296
}
297
298
int32_t i;
299
if (v.isDouble() && mozilla::NumberEqualsInt32(v.toDouble(), &i) && i >= 0) {
300
*indexp = uint32_t(i);
301
return true;
302
}
303
304
if (v.isString() && v.toString()->hasIndexValue()) {
305
*indexp = v.toString()->getIndexValue();
306
return true;
307
}
308
309
return false;
310
}
311
312
/* ES5 9.4 ToInteger. */
313
static MOZ_MUST_USE inline bool ToInteger(JSContext* cx, HandleValue v,
314
double* dp) {
315
if (v.isInt32()) {
316
*dp = v.toInt32();
317
return true;
318
}
319
if (v.isDouble()) {
320
*dp = v.toDouble();
321
} else if (v.isString() && v.toString()->hasIndexValue()) {
322
*dp = v.toString()->getIndexValue();
323
return true;
324
} else {
325
extern JS_PUBLIC_API bool ToNumberSlow(JSContext * cx, HandleValue v,
326
double* dp);
327
if (!ToNumberSlow(cx, v, dp)) {
328
return false;
329
}
330
}
331
*dp = JS::ToInteger(*dp);
332
return true;
333
}
334
335
/* ES2017 draft 7.1.17 ToIndex
336
*
337
* Return true and set |*index| to the integer value if |v| is a valid
338
* integer index value. Otherwise report a RangeError and return false.
339
*
340
* The returned index will always be in the range 0 <= *index <= 2^53-1.
341
*/
342
extern MOZ_MUST_USE bool ToIndexSlow(JSContext* cx, JS::HandleValue v,
343
const unsigned errorNumber,
344
uint64_t* index);
345
346
static MOZ_MUST_USE inline bool ToIndex(JSContext* cx, JS::HandleValue v,
347
const unsigned errorNumber,
348
uint64_t* index) {
349
if (v.isInt32()) {
350
int32_t i = v.toInt32();
351
if (i >= 0) {
352
*index = uint64_t(i);
353
return true;
354
}
355
}
356
return ToIndexSlow(cx, v, errorNumber, index);
357
}
358
359
static MOZ_MUST_USE inline bool ToIndex(JSContext* cx, JS::HandleValue v,
360
uint64_t* index) {
361
return ToIndex(cx, v, JSMSG_BAD_INDEX, index);
362
}
363
364
MOZ_MUST_USE inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) {
365
#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow)
366
// Using compiler's builtin function.
367
return !__builtin_sadd_overflow(one, two, res);
368
#else
369
// Use unsigned for the 32-bit operation since signed overflow gets
370
// undefined behavior.
371
*res = uint32_t(one) + uint32_t(two);
372
int64_t ores = (int64_t)one + (int64_t)two;
373
return ores == (int64_t)*res;
374
#endif
375
}
376
377
MOZ_MUST_USE inline bool SafeSub(int32_t one, int32_t two, int32_t* res) {
378
#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_ssub_overflow)
379
return !__builtin_ssub_overflow(one, two, res);
380
#else
381
*res = uint32_t(one) - uint32_t(two);
382
int64_t ores = (int64_t)one - (int64_t)two;
383
return ores == (int64_t)*res;
384
#endif
385
}
386
387
MOZ_MUST_USE inline bool SafeMul(int32_t one, int32_t two, int32_t* res) {
388
#if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_smul_overflow)
389
return !__builtin_smul_overflow(one, two, res);
390
#else
391
*res = uint32_t(one) * uint32_t(two);
392
int64_t ores = (int64_t)one * (int64_t)two;
393
return ores == (int64_t)*res;
394
#endif
395
}
396
397
} /* namespace js */
398
399
#endif /* jsnum_h */