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