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