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