1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * IBM Corp.
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS number type and wrapper class.
43 : */
44 : #ifdef XP_OS2
45 : #define _PC_53 PC_53
46 : #define _MCW_EM MCW_EM
47 : #define _MCW_PC MCW_PC
48 : #endif
49 : #include <locale.h>
50 : #include <limits.h>
51 : #include <math.h>
52 : #include <stdlib.h>
53 : #include <string.h>
54 :
55 : #include "mozilla/RangedPtr.h"
56 :
57 : #include "jstypes.h"
58 : #include "jsutil.h"
59 : #include "jsapi.h"
60 : #include "jsatom.h"
61 : #include "jscntxt.h"
62 : #include "jsversion.h"
63 : #include "jsdtoa.h"
64 : #include "jsgc.h"
65 : #include "jsinterp.h"
66 : #include "jsnum.h"
67 : #include "jsobj.h"
68 : #include "jsopcode.h"
69 : #include "jsprf.h"
70 : #include "jsscope.h"
71 : #include "jsstr.h"
72 : #include "jslibmath.h"
73 :
74 : #include "vm/GlobalObject.h"
75 : #include "vm/MethodGuard.h"
76 :
77 : #include "jsatominlines.h"
78 : #include "jsinferinlines.h"
79 : #include "jsnuminlines.h"
80 : #include "jsobjinlines.h"
81 :
82 : #include "vm/MethodGuard-inl.h"
83 : #include "vm/NumberObject-inl.h"
84 : #include "vm/String-inl.h"
85 : #include "vm/StringBuffer-inl.h"
86 :
87 : using namespace js;
88 : using namespace js::types;
89 :
90 : /*
91 : * If we're accumulating a decimal number and the number is >= 2^53, then the
92 : * fast result from the loop in GetPrefixInteger may be inaccurate. Call
93 : * js_strtod_harder to get the correct answer.
94 : */
95 : static bool
96 2009 : ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar *end, double *dp)
97 : {
98 2009 : size_t length = end - start;
99 2009 : char *cstr = static_cast<char *>(cx->malloc_(length + 1));
100 2009 : if (!cstr)
101 0 : return false;
102 :
103 43845 : for (size_t i = 0; i < length; i++) {
104 41836 : char c = char(start[i]);
105 41836 : JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
106 41836 : cstr[i] = c;
107 : }
108 2009 : cstr[length] = 0;
109 :
110 : char *estr;
111 2009 : int err = 0;
112 2009 : *dp = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
113 2009 : if (err == JS_DTOA_ENOMEM) {
114 0 : JS_ReportOutOfMemory(cx);
115 0 : cx->free_(cstr);
116 0 : return false;
117 : }
118 2009 : if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL)
119 0 : *dp = js_PositiveInfinity;
120 2009 : cx->free_(cstr);
121 2009 : return true;
122 : }
123 :
124 : class BinaryDigitReader
125 : {
126 : const int base; /* Base of number; must be a power of 2 */
127 : int digit; /* Current digit value in radix given by base */
128 : int digitMask; /* Mask to extract the next bit from digit */
129 : const jschar *start; /* Pointer to the remaining digits */
130 : const jschar *end; /* Pointer to first non-digit */
131 :
132 : public:
133 24 : BinaryDigitReader(int base, const jschar *start, const jschar *end)
134 24 : : base(base), digit(0), digitMask(0), start(start), end(end)
135 : {
136 24 : }
137 :
138 : /* Return the next binary digit from the number, or -1 if done. */
139 1564 : int nextDigit() {
140 1564 : if (digitMask == 0) {
141 409 : if (start == end)
142 24 : return -1;
143 :
144 385 : int c = *start++;
145 385 : JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
146 385 : if ('0' <= c && c <= '9')
147 209 : digit = c - '0';
148 176 : else if ('a' <= c && c <= 'z')
149 176 : digit = c - 'a' + 10;
150 : else
151 0 : digit = c - 'A' + 10;
152 385 : digitMask = base >> 1;
153 : }
154 :
155 1540 : int bit = (digit & digitMask) != 0;
156 1540 : digitMask >>= 1;
157 1540 : return bit;
158 : }
159 : };
160 :
161 : /*
162 : * The fast result might also have been inaccurate for power-of-two bases. This
163 : * happens if the addition in value * 2 + digit causes a round-down to an even
164 : * least significant mantissa bit when the first dropped bit is a one. If any
165 : * of the following digits in the number (which haven't been added in yet) are
166 : * nonzero, then the correct action would have been to round up instead of
167 : * down. An example occurs when reading the number 0x1000000000000081, which
168 : * rounds to 0x1000000000000000 instead of 0x1000000000000100.
169 : */
170 : static double
171 24 : ComputeAccurateBinaryBaseInteger(JSContext *cx, const jschar *start, const jschar *end, int base)
172 : {
173 24 : BinaryDigitReader bdr(base, start, end);
174 :
175 : /* Skip leading zeroes. */
176 : int bit;
177 40 : do {
178 40 : bit = bdr.nextDigit();
179 : } while (bit == 0);
180 :
181 24 : JS_ASSERT(bit == 1); // guaranteed by GetPrefixInteger
182 :
183 : /* Gather the 53 significant bits (including the leading 1). */
184 24 : double value = 1.0;
185 1272 : for (int j = 52; j > 0; j--) {
186 1248 : bit = bdr.nextDigit();
187 1248 : if (bit < 0)
188 0 : return value;
189 1248 : value = value * 2 + bit;
190 : }
191 :
192 : /* bit2 is the 54th bit (the first dropped from the mantissa). */
193 24 : int bit2 = bdr.nextDigit();
194 24 : if (bit2 >= 0) {
195 24 : double factor = 2.0;
196 24 : int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
197 : int bit3;
198 :
199 276 : while ((bit3 = bdr.nextDigit()) >= 0) {
200 228 : sticky |= bit3;
201 228 : factor *= 2;
202 : }
203 24 : value += bit2 & (bit | sticky);
204 24 : value *= factor;
205 : }
206 :
207 24 : return value;
208 : }
209 :
210 : namespace js {
211 :
212 : bool
213 6130705 : GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base,
214 : const jschar **endp, double *dp)
215 : {
216 6130705 : JS_ASSERT(start <= end);
217 6130705 : JS_ASSERT(2 <= base && base <= 36);
218 :
219 6130705 : const jschar *s = start;
220 6130705 : double d = 0.0;
221 18675206 : for (; s < end; s++) {
222 : int digit;
223 12688238 : jschar c = *s;
224 12688238 : if ('0' <= c && c <= '9')
225 12519828 : digit = c - '0';
226 168410 : else if ('a' <= c && c <= 'z')
227 26045 : digit = c - 'a' + 10;
228 142365 : else if ('A' <= c && c <= 'Z')
229 1662 : digit = c - 'A' + 10;
230 : else
231 140703 : break;
232 12547535 : if (digit >= base)
233 3034 : break;
234 12544501 : d = d * base + digit;
235 : }
236 :
237 6130705 : *endp = s;
238 6130705 : *dp = d;
239 :
240 : /* If we haven't reached the limit of integer precision, we're done. */
241 6130705 : if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
242 6128672 : return true;
243 :
244 : /*
245 : * Otherwise compute the correct integer from the prefix of valid digits
246 : * if we're computing for base ten or a power of two. Don't worry about
247 : * other bases; see 15.1.2.2 step 13.
248 : */
249 2033 : if (base == 10)
250 2009 : return ComputeAccurateDecimalInteger(cx, start, s, dp);
251 24 : if ((base & (base - 1)) == 0)
252 24 : *dp = ComputeAccurateBinaryBaseInteger(cx, start, s, base);
253 :
254 24 : return true;
255 : }
256 :
257 : } // namespace js
258 :
259 : static JSBool
260 7663 : num_isNaN(JSContext *cx, unsigned argc, Value *vp)
261 : {
262 7663 : if (argc == 0) {
263 0 : vp->setBoolean(true);
264 0 : return JS_TRUE;
265 : }
266 : double x;
267 7663 : if (!ToNumber(cx, vp[2], &x))
268 0 : return false;
269 7663 : vp->setBoolean(JSDOUBLE_IS_NaN(x));
270 7663 : return JS_TRUE;
271 : }
272 :
273 : static JSBool
274 0 : num_isFinite(JSContext *cx, unsigned argc, Value *vp)
275 : {
276 0 : if (argc == 0) {
277 0 : vp->setBoolean(false);
278 0 : return JS_TRUE;
279 : }
280 : double x;
281 0 : if (!ToNumber(cx, vp[2], &x))
282 0 : return JS_FALSE;
283 0 : vp->setBoolean(JSDOUBLE_IS_FINITE(x));
284 0 : return JS_TRUE;
285 : }
286 :
287 : static JSBool
288 3431 : num_parseFloat(JSContext *cx, unsigned argc, Value *vp)
289 : {
290 : JSString *str;
291 : double d;
292 : const jschar *bp, *end, *ep;
293 :
294 3431 : if (argc == 0) {
295 0 : vp->setDouble(js_NaN);
296 0 : return JS_TRUE;
297 : }
298 3431 : str = ToString(cx, vp[2]);
299 3431 : if (!str)
300 0 : return JS_FALSE;
301 3431 : bp = str->getChars(cx);
302 3431 : if (!bp)
303 0 : return JS_FALSE;
304 3431 : end = bp + str->length();
305 3431 : if (!js_strtod(cx, bp, end, &ep, &d))
306 0 : return JS_FALSE;
307 3431 : if (ep == bp) {
308 1 : vp->setDouble(js_NaN);
309 1 : return JS_TRUE;
310 : }
311 3430 : vp->setNumber(d);
312 3430 : return JS_TRUE;
313 : }
314 :
315 : static bool
316 164346 : ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int maybeRadix,
317 : bool stripPrefix, double *dp)
318 : {
319 164346 : JS_ASSERT(maybeRadix == 0 || (2 <= maybeRadix && maybeRadix <= 36));
320 164346 : JS_ASSERT(ws <= end);
321 :
322 164346 : const jschar *s = SkipSpace(ws, end);
323 164346 : JS_ASSERT(ws <= s);
324 164346 : JS_ASSERT(s <= end);
325 :
326 : /* 15.1.2.2 steps 3-4. */
327 164346 : bool negative = (s != end && s[0] == '-');
328 :
329 : /* 15.1.2.2 step 5. */
330 164346 : if (s != end && (s[0] == '-' || s[0] == '+'))
331 71598 : s++;
332 :
333 : /* 15.1.2.2 step 9. */
334 164346 : int radix = maybeRadix;
335 164346 : if (radix == 0) {
336 148152 : if (end - s >= 2 && s[0] == '0' && (s[1] != 'x' && s[1] != 'X')) {
337 : /*
338 : * Non-standard: ES5 requires that parseInt interpret leading-zero
339 : * strings not starting with "0x" or "0X" as decimal (absent an
340 : * explicitly specified non-zero radix), but we continue to
341 : * interpret such strings as octal, as per ES3 and web practice.
342 : */
343 517 : radix = 8;
344 : } else {
345 147635 : radix = 10;
346 : }
347 : }
348 :
349 : /* 15.1.2.2 step 10. */
350 164346 : if (stripPrefix) {
351 155227 : if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
352 54 : s += 2;
353 54 : radix = 16;
354 : }
355 : }
356 :
357 : /* 15.1.2.2 steps 11-14. */
358 : const jschar *actualEnd;
359 164346 : if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, dp))
360 0 : return false;
361 164346 : if (s == actualEnd)
362 752 : *dp = js_NaN;
363 163594 : else if (negative)
364 71580 : *dp = -*dp;
365 164346 : return true;
366 : }
367 :
368 : /* See ECMA 15.1.2.2. */
369 : JSBool
370 219736 : js::num_parseInt(JSContext *cx, unsigned argc, Value *vp)
371 : {
372 219736 : CallArgs args = CallArgsFromVp(argc, vp);
373 :
374 : /* Fast paths and exceptional cases. */
375 219736 : if (args.length() == 0) {
376 0 : args.rval().setDouble(js_NaN);
377 0 : return true;
378 : }
379 :
380 276911 : if (args.length() == 1 ||
381 57175 : (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) {
382 212434 : if (args[0].isInt32()) {
383 3199 : args.rval() = args[0];
384 3199 : return true;
385 : }
386 : /*
387 : * Step 1 is |inputString = ToString(string)|. When string >=
388 : * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
389 : * the word, which would mean the result of parseInt(string) should be |N|.
390 : *
391 : * To preserve this behaviour, we can't use the fast-path when string >
392 : * 1e21, or else the result would be |NeM|.
393 : *
394 : * The same goes for values smaller than 1.0e-6, because the string would be in
395 : * the form of "Ne-M".
396 : */
397 209235 : if (args[0].isDouble()) {
398 124992 : double d = args[0].toDouble();
399 124992 : if (1.0e-6 < d && d < 1.0e21) {
400 50463 : args.rval().setNumber(floor(d));
401 50463 : return true;
402 : }
403 74529 : if (-1.0e21 < d && d < -1.0e-6) {
404 1656 : args.rval().setNumber(-floor(-d));
405 1656 : return true;
406 : }
407 72873 : if (d == 0.0) {
408 72 : args.rval().setInt32(0);
409 72 : return true;
410 : }
411 : }
412 : }
413 :
414 : /* Step 1. */
415 164346 : JSString *inputString = ToString(cx, args[0]);
416 164346 : if (!inputString)
417 0 : return false;
418 164346 : args[0].setString(inputString);
419 :
420 : /* 15.1.2.2 steps 6-8. */
421 164346 : bool stripPrefix = true;
422 164346 : int32_t radix = 0;
423 164346 : if (args.length() > 1) {
424 16779 : if (!ToInt32(cx, args[1], &radix))
425 0 : return false;
426 16779 : if (radix != 0) {
427 16194 : if (radix < 2 || radix > 36) {
428 0 : args.rval().setDouble(js_NaN);
429 0 : return true;
430 : }
431 16194 : if (radix != 16)
432 9119 : stripPrefix = false;
433 : }
434 : }
435 :
436 : /* Steps 2-5, 9-14. */
437 164346 : const jschar *ws = inputString->getChars(cx);
438 164346 : if (!ws)
439 0 : return false;
440 164346 : const jschar *end = ws + inputString->length();
441 :
442 : double number;
443 164346 : if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number))
444 0 : return false;
445 :
446 : /* Step 15. */
447 164346 : args.rval().setNumber(number);
448 164346 : return true;
449 : }
450 :
451 : const char js_Infinity_str[] = "Infinity";
452 : const char js_NaN_str[] = "NaN";
453 : const char js_isNaN_str[] = "isNaN";
454 : const char js_isFinite_str[] = "isFinite";
455 : const char js_parseFloat_str[] = "parseFloat";
456 : const char js_parseInt_str[] = "parseInt";
457 :
458 : static JSFunctionSpec number_functions[] = {
459 : JS_FN(js_isNaN_str, num_isNaN, 1,0),
460 : JS_FN(js_isFinite_str, num_isFinite, 1,0),
461 : JS_FN(js_parseFloat_str, num_parseFloat, 1,0),
462 : JS_FN(js_parseInt_str, num_parseInt, 2,0),
463 : JS_FS_END
464 : };
465 :
466 : Class js::NumberClass = {
467 : js_Number_str,
468 : JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
469 : JS_PropertyStub, /* addProperty */
470 : JS_PropertyStub, /* delProperty */
471 : JS_PropertyStub, /* getProperty */
472 : JS_StrictPropertyStub, /* setProperty */
473 : JS_EnumerateStub,
474 : JS_ResolveStub,
475 : JS_ConvertStub
476 : };
477 :
478 : static JSBool
479 22581 : Number(JSContext *cx, unsigned argc, Value *vp)
480 : {
481 : /* Sample JS_CALLEE before clobbering. */
482 22581 : bool isConstructing = IsConstructing(vp);
483 :
484 22581 : if (argc > 0) {
485 22554 : if (!ToNumber(cx, &vp[2]))
486 0 : return false;
487 22554 : vp[0] = vp[2];
488 : } else {
489 27 : vp[0].setInt32(0);
490 : }
491 :
492 22581 : if (!isConstructing)
493 21832 : return true;
494 :
495 749 : JSObject *obj = NumberObject::create(cx, vp[0].toNumber());
496 749 : if (!obj)
497 0 : return false;
498 749 : vp->setObject(*obj);
499 749 : return true;
500 : }
501 :
502 : #if JS_HAS_TOSOURCE
503 : static JSBool
504 180 : num_toSource(JSContext *cx, unsigned argc, Value *vp)
505 : {
506 180 : CallArgs args = CallArgsFromVp(argc, vp);
507 :
508 : double d;
509 : bool ok;
510 180 : if (!BoxedPrimitiveMethodGuard(cx, args, num_toSource, &d, &ok))
511 63 : return ok;
512 :
513 234 : StringBuffer sb(cx);
514 234 : if (!sb.append("(new Number(") || !NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
515 117 : !sb.append("))"))
516 : {
517 0 : return false;
518 : }
519 :
520 117 : JSString *str = sb.finishString();
521 117 : if (!str)
522 0 : return false;
523 117 : args.rval().setString(str);
524 117 : return true;
525 : }
526 : #endif
527 :
528 3803388 : ToCStringBuf::ToCStringBuf() :dbuf(NULL)
529 : {
530 : JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
531 3803388 : }
532 :
533 3803388 : ToCStringBuf::~ToCStringBuf()
534 : {
535 3803388 : if (dbuf)
536 326 : UnwantedForeground::free_(dbuf);
537 3803388 : }
538 :
539 : JSString * JS_FASTCALL
540 46150849 : js_IntToString(JSContext *cx, int32_t si)
541 : {
542 : uint32_t ui;
543 46150849 : if (si >= 0) {
544 46126567 : if (StaticStrings::hasInt(si))
545 3607643 : return cx->runtime->staticStrings.getInt(si);
546 42518924 : ui = si;
547 : } else {
548 24282 : ui = uint32_t(-si);
549 24282 : JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
550 : }
551 :
552 42543206 : JSCompartment *c = cx->compartment;
553 42543206 : if (JSString *str = c->dtoaCache.lookup(10, si))
554 115053 : return str;
555 :
556 42428153 : JSShortString *str = js_NewGCShortString(cx);
557 42428153 : if (!str)
558 9 : return NULL;
559 :
560 42428144 : jschar *storage = str->inlineStorageBeforeInit();
561 : RangedPtr<jschar> end(storage + JSShortString::MAX_SHORT_LENGTH,
562 42428144 : storage, JSShortString::MAX_SHORT_LENGTH + 1);
563 42428144 : *end = '\0';
564 :
565 42428144 : RangedPtr<jschar> start = BackfillIndexInCharBuffer(ui, end);
566 :
567 42428144 : if (si < 0)
568 5836 : *--start = '-';
569 :
570 42428144 : str->initAtOffsetInBuffer(start.get(), end - start);
571 :
572 42428144 : c->dtoaCache.cache(10, si, str);
573 42428144 : return str;
574 : }
575 :
576 : /* Returns a non-NULL pointer to inside cbuf. */
577 : static char *
578 688668 : IntToCString(ToCStringBuf *cbuf, int i, int base = 10)
579 : {
580 688668 : unsigned u = (i < 0) ? -i : i;
581 :
582 688668 : RangedPtr<char> cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize);
583 688668 : *cp = '\0';
584 :
585 : /* Build the string from behind. */
586 688668 : switch (base) {
587 : case 10:
588 636646 : cp = BackfillIndexInCharBuffer(u, cp);
589 636646 : break;
590 : case 16:
591 104016 : do {
592 104016 : unsigned newu = u / 16;
593 104016 : *--cp = "0123456789abcdef"[u - newu * 16];
594 104016 : u = newu;
595 : } while (u != 0);
596 51558 : break;
597 : default:
598 464 : JS_ASSERT(base >= 2 && base <= 36);
599 2166 : do {
600 2166 : unsigned newu = u / base;
601 2166 : *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
602 2166 : u = newu;
603 : } while (u != 0);
604 464 : break;
605 : }
606 688668 : if (i < 0)
607 29433 : *--cp = '-';
608 :
609 688668 : return cp.get();
610 : }
611 :
612 : static JSString * JS_FASTCALL
613 : js_NumberToStringWithBase(JSContext *cx, double d, int base);
614 :
615 : static JS_ALWAYS_INLINE bool
616 245877 : num_toStringHelper(JSContext *cx, Native native, unsigned argc, Value *vp)
617 : {
618 245877 : CallArgs args = CallArgsFromVp(argc, vp);
619 :
620 : double d;
621 : bool ok;
622 245877 : if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok))
623 135 : return ok;
624 :
625 245742 : int32_t base = 10;
626 245742 : if (args.hasDefined(0)) {
627 : double d2;
628 233108 : if (!ToInteger(cx, args[0], &d2))
629 0 : return false;
630 :
631 233108 : if (d2 < 2 || d2 > 36) {
632 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX);
633 0 : return false;
634 : }
635 :
636 233108 : base = int32_t(d2);
637 : }
638 245742 : JSString *str = js_NumberToStringWithBase(cx, d, base);
639 245742 : if (!str) {
640 0 : JS_ReportOutOfMemory(cx);
641 0 : return false;
642 : }
643 245742 : args.rval().setString(str);
644 245742 : return true;
645 : }
646 :
647 : static JSBool
648 245766 : num_toString(JSContext *cx, unsigned argc, Value *vp)
649 : {
650 245766 : return num_toStringHelper(cx, num_toString, argc, vp);
651 : }
652 :
653 : static JSBool
654 21 : num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
655 : {
656 : size_t thousandsLength, decimalLength;
657 : const char *numGrouping, *tmpGroup;
658 : JSRuntime *rt;
659 : JSString *str;
660 : const char *num, *end, *tmpSrc;
661 : char *buf, *tmpDest;
662 : const char *nint;
663 : int digits, buflen, remainder, nrepeat;
664 :
665 : /*
666 : * Create the string, move back to bytes to make string twiddling
667 : * a bit easier and so we can insert platform charset seperators.
668 : */
669 21 : if (!num_toStringHelper(cx, num_toLocaleString, 0, vp))
670 9 : return JS_FALSE;
671 12 : JS_ASSERT(vp->isString());
672 24 : JSAutoByteString numBytes(cx, vp->toString());
673 12 : if (!numBytes)
674 0 : return JS_FALSE;
675 12 : num = numBytes.ptr();
676 12 : if (!num)
677 0 : return JS_FALSE;
678 :
679 : /*
680 : * Find the first non-integer value, whether it be a letter as in
681 : * 'Infinity', a decimal point, or an 'e' from exponential notation.
682 : */
683 12 : nint = num;
684 12 : if (*nint == '-')
685 0 : nint++;
686 36 : while (*nint >= '0' && *nint <= '9')
687 12 : nint++;
688 12 : digits = nint - num;
689 12 : end = num + digits;
690 12 : if (!digits)
691 0 : return JS_TRUE;
692 :
693 12 : rt = cx->runtime;
694 12 : thousandsLength = strlen(rt->thousandsSeparator);
695 12 : decimalLength = strlen(rt->decimalSeparator);
696 :
697 : /* Figure out how long resulting string will be. */
698 12 : buflen = strlen(num);
699 12 : if (*nint == '.')
700 12 : buflen += decimalLength - 1; /* -1 to account for existing '.' */
701 :
702 12 : numGrouping = tmpGroup = rt->numGrouping;
703 12 : remainder = digits;
704 12 : if (*num == '-')
705 0 : remainder--;
706 :
707 24 : while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
708 12 : if (*tmpGroup >= remainder)
709 12 : break;
710 0 : buflen += thousandsLength;
711 0 : remainder -= *tmpGroup;
712 0 : tmpGroup++;
713 : }
714 12 : if (*tmpGroup == '\0' && *numGrouping != '\0') {
715 0 : nrepeat = (remainder - 1) / tmpGroup[-1];
716 0 : buflen += thousandsLength * nrepeat;
717 0 : remainder -= nrepeat * tmpGroup[-1];
718 : } else {
719 12 : nrepeat = 0;
720 : }
721 12 : tmpGroup--;
722 :
723 12 : buf = (char *)cx->malloc_(buflen + 1);
724 12 : if (!buf)
725 0 : return JS_FALSE;
726 :
727 12 : tmpDest = buf;
728 12 : tmpSrc = num;
729 :
730 36 : while (*tmpSrc == '-' || remainder--) {
731 12 : JS_ASSERT(tmpDest - buf < buflen);
732 12 : *tmpDest++ = *tmpSrc++;
733 : }
734 24 : while (tmpSrc < end) {
735 0 : JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
736 0 : strcpy(tmpDest, rt->thousandsSeparator);
737 0 : tmpDest += thousandsLength;
738 0 : JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
739 0 : js_memcpy(tmpDest, tmpSrc, *tmpGroup);
740 0 : tmpDest += *tmpGroup;
741 0 : tmpSrc += *tmpGroup;
742 0 : if (--nrepeat < 0)
743 0 : tmpGroup--;
744 : }
745 :
746 12 : if (*nint == '.') {
747 12 : JS_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
748 12 : strcpy(tmpDest, rt->decimalSeparator);
749 12 : tmpDest += decimalLength;
750 12 : JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
751 12 : strcpy(tmpDest, nint + 1);
752 : } else {
753 0 : JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
754 0 : strcpy(tmpDest, nint);
755 : }
756 :
757 12 : if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) {
758 3 : JSBool ok = cx->localeCallbacks->localeToUnicode(cx, buf, vp);
759 3 : cx->free_(buf);
760 3 : return ok;
761 : }
762 :
763 9 : str = js_NewStringCopyN(cx, buf, buflen);
764 9 : cx->free_(buf);
765 9 : if (!str)
766 0 : return JS_FALSE;
767 :
768 9 : vp->setString(str);
769 9 : return JS_TRUE;
770 : }
771 :
772 : JSBool
773 99 : js_num_valueOf(JSContext *cx, unsigned argc, Value *vp)
774 : {
775 99 : CallArgs args = CallArgsFromVp(argc, vp);
776 :
777 : double d;
778 : bool ok;
779 99 : if (!BoxedPrimitiveMethodGuard(cx, args, js_num_valueOf, &d, &ok))
780 63 : return ok;
781 :
782 36 : args.rval().setNumber(d);
783 36 : return true;
784 : }
785 :
786 :
787 : #define MAX_PRECISION 100
788 :
789 : static JSBool
790 411 : num_to(JSContext *cx, Native native, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode,
791 : int precisionMin, int precisionMax, int precisionOffset,
792 : CallArgs args)
793 : {
794 : /* Use MAX_PRECISION+1 because precisionOffset can be 1. */
795 : char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)];
796 : char *numStr;
797 :
798 : double d;
799 : bool ok;
800 411 : if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok))
801 126 : return ok;
802 :
803 : double precision;
804 285 : if (args.length() == 0) {
805 54 : precision = 0.0;
806 54 : oneArgMode = zeroArgMode;
807 : } else {
808 231 : if (!ToInteger(cx, args[0], &precision))
809 0 : return false;
810 231 : if (precision < precisionMin || precision > precisionMax) {
811 0 : ToCStringBuf cbuf;
812 0 : numStr = IntToCString(&cbuf, int(precision));
813 0 : JS_ASSERT(numStr);
814 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
815 0 : return JS_FALSE;
816 : }
817 : }
818 :
819 : numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf,
820 285 : oneArgMode, (int)precision + precisionOffset, d);
821 285 : if (!numStr) {
822 0 : JS_ReportOutOfMemory(cx);
823 0 : return JS_FALSE;
824 : }
825 285 : JSString *str = js_NewStringCopyZ(cx, numStr);
826 285 : if (!str)
827 0 : return JS_FALSE;
828 285 : args.rval().setString(str);
829 285 : return JS_TRUE;
830 : }
831 :
832 : /*
833 : * In the following three implementations, we allow a larger range of precision
834 : * than ECMA requires; this is permitted by ECMA-262.
835 : */
836 : static JSBool
837 321 : num_toFixed(JSContext *cx, unsigned argc, Value *vp)
838 : {
839 : return num_to(cx, num_toFixed, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0,
840 321 : CallArgsFromVp(argc, vp));
841 : }
842 :
843 : static JSBool
844 90 : num_toExponential(JSContext *cx, unsigned argc, Value *vp)
845 : {
846 : return num_to(cx, num_toExponential, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0,
847 90 : MAX_PRECISION, 1, CallArgsFromVp(argc, vp));
848 : }
849 :
850 : static JSBool
851 90 : num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
852 : {
853 90 : CallArgs args = CallArgsFromVp(argc, vp);
854 90 : if (!args.hasDefined(0))
855 90 : return num_toStringHelper(cx, num_toPrecision, 0, vp);
856 : return num_to(cx, num_toPrecision, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0,
857 0 : args);
858 : }
859 :
860 : static JSFunctionSpec number_methods[] = {
861 : #if JS_HAS_TOSOURCE
862 : JS_FN(js_toSource_str, num_toSource, 0, 0),
863 : #endif
864 : JS_FN(js_toString_str, num_toString, 1, 0),
865 : JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0),
866 : JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
867 : JS_FN("toFixed", num_toFixed, 1, 0),
868 : JS_FN("toExponential", num_toExponential, 1, 0),
869 : JS_FN("toPrecision", num_toPrecision, 1, 0),
870 : JS_FS_END
871 : };
872 :
873 : /* NB: Keep this in synch with number_constants[]. */
874 : enum nc_slot {
875 : NC_NaN,
876 : NC_POSITIVE_INFINITY,
877 : NC_NEGATIVE_INFINITY,
878 : NC_MAX_VALUE,
879 : NC_MIN_VALUE,
880 : NC_LIMIT
881 : };
882 :
883 : /*
884 : * Some to most C compilers forbid spelling these at compile time, or barf
885 : * if you try, so all but MAX_VALUE are set up by InitRuntimeNumberState
886 : * using union jsdpun.
887 : */
888 : static JSConstDoubleSpec number_constants[] = {
889 : {0, js_NaN_str, 0,{0,0,0}},
890 : {0, "POSITIVE_INFINITY", 0,{0,0,0}},
891 : {0, "NEGATIVE_INFINITY", 0,{0,0,0}},
892 : {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}},
893 : {0, "MIN_VALUE", 0,{0,0,0}},
894 : {0,0,0,{0,0,0}}
895 : };
896 :
897 : double js_NaN;
898 : double js_PositiveInfinity;
899 : double js_NegativeInfinity;
900 :
901 : #if (defined __GNUC__ && defined __i386__) || \
902 : (defined __SUNPRO_CC && defined __i386)
903 :
904 : /*
905 : * Set the exception mask to mask all exceptions and set the FPU precision
906 : * to 53 bit mantissa (64 bit doubles).
907 : */
908 23449 : inline void FIX_FPU() {
909 : short control;
910 23449 : asm("fstcw %0" : "=m" (control) : );
911 23449 : control &= ~0x300; // Lower bits 8 and 9 (precision control).
912 23449 : control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
913 23449 : asm("fldcw %0" : : "m" (control) );
914 23449 : }
915 :
916 : #else
917 :
918 : #define FIX_FPU() ((void)0)
919 :
920 : #endif
921 :
922 : namespace js {
923 :
924 : bool
925 19910 : InitRuntimeNumberState(JSRuntime *rt)
926 : {
927 19910 : FIX_FPU();
928 :
929 : jsdpun u;
930 19910 : u.s.hi = JSDOUBLE_HI32_NAN;
931 19910 : u.s.lo = JSDOUBLE_LO32_NAN;
932 19910 : number_constants[NC_NaN].dval = js_NaN = u.d;
933 19910 : rt->NaNValue.setDouble(u.d);
934 :
935 19910 : u.s.hi = JSDOUBLE_HI32_EXPMASK;
936 19910 : u.s.lo = 0x00000000;
937 19910 : number_constants[NC_POSITIVE_INFINITY].dval = js_PositiveInfinity = u.d;
938 19910 : rt->positiveInfinityValue.setDouble(u.d);
939 :
940 19910 : u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
941 19910 : u.s.lo = 0x00000000;
942 19910 : number_constants[NC_NEGATIVE_INFINITY].dval = js_NegativeInfinity = u.d;
943 19910 : rt->negativeInfinityValue.setDouble(u.d);
944 :
945 19910 : u.s.hi = 0;
946 19910 : u.s.lo = 1;
947 19910 : number_constants[NC_MIN_VALUE].dval = u.d;
948 :
949 : /* Copy locale-specific separators into the runtime strings. */
950 : const char *thousandsSeparator, *decimalPoint, *grouping;
951 : #ifdef HAVE_LOCALECONV
952 19910 : struct lconv *locale = localeconv();
953 19910 : thousandsSeparator = locale->thousands_sep;
954 19910 : decimalPoint = locale->decimal_point;
955 19910 : grouping = locale->grouping;
956 : #else
957 : thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
958 : decimalPoint = getenv("LOCALE_DECIMAL_POINT");
959 : grouping = getenv("LOCALE_GROUPING");
960 : #endif
961 19910 : if (!thousandsSeparator)
962 0 : thousandsSeparator = "'";
963 19910 : if (!decimalPoint)
964 0 : decimalPoint = ".";
965 19910 : if (!grouping)
966 0 : grouping = "\3\0";
967 :
968 : /*
969 : * We use single malloc to get the memory for all separator and grouping
970 : * strings.
971 : */
972 19910 : size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
973 19910 : size_t decimalPointSize = strlen(decimalPoint) + 1;
974 19910 : size_t groupingSize = strlen(grouping) + 1;
975 :
976 : char *storage = static_cast<char *>(OffTheBooks::malloc_(thousandsSeparatorSize +
977 : decimalPointSize +
978 19910 : groupingSize));
979 19910 : if (!storage)
980 0 : return false;
981 :
982 19910 : js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
983 19910 : rt->thousandsSeparator = storage;
984 19910 : storage += thousandsSeparatorSize;
985 :
986 19910 : js_memcpy(storage, decimalPoint, decimalPointSize);
987 19910 : rt->decimalSeparator = storage;
988 19910 : storage += decimalPointSize;
989 :
990 19910 : js_memcpy(storage, grouping, groupingSize);
991 19910 : rt->numGrouping = grouping;
992 19910 : return true;
993 : }
994 :
995 : void
996 19908 : FinishRuntimeNumberState(JSRuntime *rt)
997 : {
998 : /*
999 : * The free also releases the memory for decimalSeparator and numGrouping
1000 : * strings.
1001 : */
1002 19908 : char *storage = const_cast<char *>(rt->thousandsSeparator);
1003 19908 : Foreground::free_(storage);
1004 19908 : }
1005 :
1006 : } /* namespace js */
1007 :
1008 : JSObject *
1009 3539 : js_InitNumberClass(JSContext *cx, JSObject *obj)
1010 : {
1011 3539 : JS_ASSERT(obj->isNative());
1012 :
1013 : /* XXX must do at least once per new thread, so do it per JSContext... */
1014 3539 : FIX_FPU();
1015 :
1016 3539 : GlobalObject *global = &obj->asGlobal();
1017 :
1018 3539 : JSObject *numberProto = global->createBlankPrototype(cx, &NumberClass);
1019 3539 : if (!numberProto)
1020 0 : return NULL;
1021 3539 : numberProto->asNumber().setPrimitiveValue(0);
1022 :
1023 : JSFunction *ctor = global->createConstructor(cx, Number, &NumberClass,
1024 3539 : CLASS_ATOM(cx, Number), 1);
1025 3539 : if (!ctor)
1026 0 : return NULL;
1027 :
1028 3539 : if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
1029 0 : return NULL;
1030 :
1031 : /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */
1032 3539 : if (!JS_DefineConstDoubles(cx, ctor, number_constants))
1033 0 : return NULL;
1034 :
1035 3539 : if (!DefinePropertiesAndBrand(cx, numberProto, NULL, number_methods))
1036 0 : return NULL;
1037 :
1038 3539 : if (!JS_DefineFunctions(cx, global, number_functions))
1039 0 : return NULL;
1040 :
1041 : /* ES5 15.1.1.1, 15.1.1.2 */
1042 10617 : if (!DefineNativeProperty(cx, global, ATOM_TO_JSID(cx->runtime->atomState.NaNAtom),
1043 : cx->runtime->NaNValue, JS_PropertyStub, JS_StrictPropertyStub,
1044 7078 : JSPROP_PERMANENT | JSPROP_READONLY, 0, 0) ||
1045 3539 : !DefineNativeProperty(cx, global, ATOM_TO_JSID(cx->runtime->atomState.InfinityAtom),
1046 : cx->runtime->positiveInfinityValue,
1047 : JS_PropertyStub, JS_StrictPropertyStub,
1048 7078 : JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
1049 : {
1050 0 : return NULL;
1051 : }
1052 :
1053 3539 : if (!DefineConstructorAndPrototype(cx, global, JSProto_Number, ctor, numberProto))
1054 0 : return NULL;
1055 :
1056 3539 : return numberProto;
1057 : }
1058 :
1059 : namespace v8 {
1060 : namespace internal {
1061 : extern char* DoubleToCString(double v, char* buffer, int buflen);
1062 : }
1063 : }
1064 :
1065 : namespace js {
1066 :
1067 : static char *
1068 279837 : FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base = 10)
1069 : {
1070 : #ifdef DEBUG
1071 : {
1072 : int32_t _;
1073 279837 : JS_ASSERT(!JSDOUBLE_IS_INT32(d, &_));
1074 : }
1075 : #endif
1076 :
1077 : char* numStr;
1078 279837 : if (base == 10) {
1079 : /*
1080 : * This is V8's implementation of the algorithm described in the
1081 : * following paper:
1082 : *
1083 : * Printing floating-point numbers quickly and accurately with integers.
1084 : * Florian Loitsch, PLDI 2010.
1085 : *
1086 : * It fails on a small number of cases, whereupon we fall back to
1087 : * js_dtostr() (which uses David Gay's dtoa).
1088 : */
1089 279511 : numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
1090 279511 : if (!numStr)
1091 : numStr = js_dtostr(cx->runtime->dtoaState, cbuf->sbuf, cbuf->sbufSize,
1092 13454 : DTOSTR_STANDARD, 0, d);
1093 : } else {
1094 326 : numStr = cbuf->dbuf = js_dtobasestr(cx->runtime->dtoaState, base, d);
1095 : }
1096 279837 : return numStr;
1097 : }
1098 :
1099 : char *
1100 89414 : NumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base/* = 10*/)
1101 : {
1102 : int32_t i;
1103 89414 : return (JSDOUBLE_IS_INT32(d, &i))
1104 41050 : ? IntToCString(cbuf, i, base)
1105 130464 : : FracNumberToCString(cx, cbuf, d, base);
1106 : }
1107 :
1108 : }
1109 :
1110 : static JSString * JS_FASTCALL
1111 3132879 : js_NumberToStringWithBase(JSContext *cx, double d, int base)
1112 : {
1113 6265758 : ToCStringBuf cbuf;
1114 : char *numStr;
1115 :
1116 : /*
1117 : * Caller is responsible for error reporting. When called from trace,
1118 : * returning NULL here will cause us to fall of trace and then retry
1119 : * from the interpreter (which will report the error).
1120 : */
1121 3132879 : if (base < 2 || base > 36)
1122 0 : return NULL;
1123 :
1124 3132879 : JSCompartment *c = cx->compartment;
1125 :
1126 : int32_t i;
1127 3132879 : if (JSDOUBLE_IS_INT32(d, &i)) {
1128 263062 : if (base == 10 && StaticStrings::hasInt(i))
1129 13246 : return cx->runtime->staticStrings.getInt(i);
1130 249816 : if (unsigned(i) < unsigned(base)) {
1131 180260 : if (i < 10)
1132 92104 : return cx->runtime->staticStrings.getInt(i);
1133 88156 : jschar c = 'a' + i - 10;
1134 88156 : JS_ASSERT(StaticStrings::hasUnit(c));
1135 88156 : return cx->runtime->staticStrings.getUnit(c);
1136 : }
1137 :
1138 69556 : if (JSFlatString *str = c->dtoaCache.lookup(base, d))
1139 3033 : return str;
1140 :
1141 66523 : numStr = IntToCString(&cbuf, i, base);
1142 66523 : JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1143 : } else {
1144 2869817 : if (JSFlatString *str = c->dtoaCache.lookup(base, d))
1145 2638344 : return str;
1146 :
1147 231473 : numStr = FracNumberToCString(cx, &cbuf, d, base);
1148 231473 : if (!numStr) {
1149 0 : JS_ReportOutOfMemory(cx);
1150 0 : return NULL;
1151 : }
1152 462294 : JS_ASSERT_IF(base == 10,
1153 693767 : !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
1154 0 : JS_ASSERT_IF(base != 10,
1155 231473 : cbuf.dbuf && cbuf.dbuf == numStr);
1156 : }
1157 :
1158 297996 : JSFixedString *s = js_NewStringCopyZ(cx, numStr);
1159 297996 : c->dtoaCache.cache(base, d, s);
1160 297996 : return s;
1161 : }
1162 :
1163 : JSString * JS_FASTCALL
1164 2887137 : js_NumberToString(JSContext *cx, double d)
1165 : {
1166 2887137 : return js_NumberToStringWithBase(cx, d, 10);
1167 : }
1168 :
1169 : namespace js {
1170 :
1171 : JSFixedString *
1172 0 : NumberToString(JSContext *cx, double d)
1173 : {
1174 0 : if (JSString *str = js_NumberToStringWithBase(cx, d, 10))
1175 0 : return &str->asFixed();
1176 0 : return NULL;
1177 : }
1178 :
1179 : JSFixedString *
1180 668 : IndexToString(JSContext *cx, uint32_t index)
1181 : {
1182 668 : if (StaticStrings::hasUint(index))
1183 638 : return cx->runtime->staticStrings.getUint(index);
1184 :
1185 30 : JSCompartment *c = cx->compartment;
1186 30 : if (JSFixedString *str = c->dtoaCache.lookup(10, index))
1187 0 : return str;
1188 :
1189 30 : JSShortString *str = js_NewGCShortString(cx);
1190 30 : if (!str)
1191 0 : return NULL;
1192 :
1193 30 : jschar *storage = str->inlineStorageBeforeInit();
1194 30 : size_t length = JSShortString::MAX_SHORT_LENGTH;
1195 30 : const RangedPtr<jschar> end(storage + length, storage, length + 1);
1196 30 : *end = '\0';
1197 :
1198 30 : RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
1199 :
1200 30 : str->initAtOffsetInBuffer(start.get(), end - start);
1201 :
1202 30 : c->dtoaCache.cache(10, index, str);
1203 30 : return str;
1204 : }
1205 :
1206 : bool JS_FASTCALL
1207 670433 : NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
1208 : {
1209 : /* Convert to C-string. */
1210 1340866 : ToCStringBuf cbuf;
1211 : const char *cstr;
1212 670433 : if (v.isInt32()) {
1213 581095 : cstr = IntToCString(&cbuf, v.toInt32());
1214 : } else {
1215 89338 : cstr = NumberToCString(cx, &cbuf, v.toDouble());
1216 89338 : if (!cstr) {
1217 0 : JS_ReportOutOfMemory(cx);
1218 0 : return JS_FALSE;
1219 : }
1220 : }
1221 :
1222 : /*
1223 : * Inflate to jschar string. The input C-string characters are < 127, so
1224 : * even if jschars are UTF-8, all chars should map to one jschar.
1225 : */
1226 670433 : size_t cstrlen = strlen(cstr);
1227 670433 : JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
1228 670433 : return sb.appendInflated(cstr, cstrlen);
1229 : }
1230 :
1231 : bool
1232 1303591 : ToNumberSlow(JSContext *cx, Value v, double *out)
1233 : {
1234 1303591 : JS_ASSERT(!v.isNumber());
1235 1303591 : goto skip_int_double;
1236 : for (;;) {
1237 259757 : if (v.isNumber()) {
1238 4055 : *out = v.toNumber();
1239 4055 : return true;
1240 : }
1241 : skip_int_double:
1242 1559293 : if (v.isString())
1243 712590 : return StringToNumberType<double>(cx, v.toString(), out);
1244 846703 : if (v.isBoolean()) {
1245 149609 : if (v.toBoolean()) {
1246 75179 : *out = 1.0;
1247 75179 : return true;
1248 : }
1249 74430 : *out = 0.0;
1250 74430 : return true;
1251 : }
1252 697094 : if (v.isNull()) {
1253 16120 : *out = 0.0;
1254 16120 : return true;
1255 : }
1256 680974 : if (v.isUndefined())
1257 421172 : break;
1258 :
1259 259802 : JS_ASSERT(v.isObject());
1260 259802 : if (!ToPrimitive(cx, JSTYPE_NUMBER, &v))
1261 45 : return false;
1262 259757 : if (v.isObject())
1263 0 : break;
1264 : }
1265 :
1266 421172 : *out = js_NaN;
1267 421172 : return true;
1268 : }
1269 :
1270 : bool
1271 1747951 : ToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
1272 : {
1273 1747951 : JS_ASSERT(!v.isInt32());
1274 : double d;
1275 1747951 : if (v.isDouble()) {
1276 1069475 : d = v.toDouble();
1277 : } else {
1278 678476 : if (!ToNumberSlow(cx, v, &d))
1279 27 : return false;
1280 : }
1281 1747924 : *out = js_DoubleToECMAInt32(d);
1282 1747924 : return true;
1283 : }
1284 :
1285 : bool
1286 18581 : ToUint32Slow(JSContext *cx, const Value &v, uint32_t *out)
1287 : {
1288 18581 : JS_ASSERT(!v.isInt32());
1289 : double d;
1290 18581 : if (v.isDouble()) {
1291 14428 : d = v.toDouble();
1292 : } else {
1293 4153 : if (!ToNumberSlow(cx, v, &d))
1294 0 : return false;
1295 : }
1296 18581 : *out = js_DoubleToECMAUint32(d);
1297 18581 : return true;
1298 : }
1299 :
1300 : } /* namespace js */
1301 :
1302 : namespace js {
1303 :
1304 : bool
1305 0 : NonstandardToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
1306 : {
1307 0 : JS_ASSERT(!v.isInt32());
1308 : double d;
1309 0 : if (v.isDouble()) {
1310 0 : d = v.toDouble();
1311 0 : } else if (!ToNumberSlow(cx, v, &d)) {
1312 0 : return false;
1313 : }
1314 :
1315 0 : if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
1316 : js_ReportValueError(cx, JSMSG_CANT_CONVERT,
1317 0 : JSDVG_SEARCH_STACK, v, NULL);
1318 0 : return false;
1319 : }
1320 0 : *out = (int32_t) floor(d + 0.5); /* Round to nearest */
1321 0 : return true;
1322 : }
1323 :
1324 : bool
1325 576 : ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out)
1326 : {
1327 576 : JS_ASSERT(!v.isInt32());
1328 : double d;
1329 576 : if (v.isDouble()) {
1330 0 : d = v.toDouble();
1331 576 : } else if (!ToNumberSlow(cx, v, &d)) {
1332 9 : return false;
1333 : }
1334 :
1335 567 : if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
1336 90 : *out = 0;
1337 90 : return true;
1338 : }
1339 :
1340 477 : uint16_t u = (uint16_t) d;
1341 477 : if ((double)u == d) {
1342 477 : *out = u;
1343 477 : return true;
1344 : }
1345 :
1346 0 : bool neg = (d < 0);
1347 0 : d = floor(neg ? -d : d);
1348 0 : d = neg ? -d : d;
1349 0 : unsigned m = JS_BIT(16);
1350 0 : d = fmod(d, (double) m);
1351 0 : if (d < 0)
1352 0 : d += m;
1353 0 : *out = (uint16_t) d;
1354 0 : return true;
1355 : }
1356 :
1357 : } /* namespace js */
1358 :
1359 : JSBool
1360 624726 : js_strtod(JSContext *cx, const jschar *s, const jschar *send,
1361 : const jschar **ep, double *dp)
1362 : {
1363 : size_t i;
1364 : char cbuf[32];
1365 : char *cstr, *istr, *estr;
1366 : JSBool negative;
1367 : double d;
1368 :
1369 624726 : const jschar *s1 = SkipSpace(s, send);
1370 624726 : size_t length = send - s1;
1371 :
1372 : /* Use cbuf to avoid malloc */
1373 624726 : if (length >= sizeof cbuf) {
1374 254261 : cstr = (char *) cx->malloc_(length + 1);
1375 254261 : if (!cstr)
1376 0 : return JS_FALSE;
1377 : } else {
1378 370465 : cstr = cbuf;
1379 : }
1380 :
1381 65049110 : for (i = 0; i != length; i++) {
1382 64424384 : if (s1[i] >> 8)
1383 0 : break;
1384 64424384 : cstr[i] = (char)s1[i];
1385 : }
1386 624726 : cstr[i] = 0;
1387 :
1388 624726 : istr = cstr;
1389 624726 : if ((negative = (*istr == '-')) != 0 || *istr == '+')
1390 437 : istr++;
1391 624726 : if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
1392 172 : d = negative ? js_NegativeInfinity : js_PositiveInfinity;
1393 172 : estr = istr + 8;
1394 : } else {
1395 : int err;
1396 624554 : d = js_strtod_harder(cx->runtime->dtoaState, cstr, &estr, &err);
1397 624554 : if (d == HUGE_VAL)
1398 10 : d = js_PositiveInfinity;
1399 624544 : else if (d == -HUGE_VAL)
1400 0 : d = js_NegativeInfinity;
1401 : }
1402 :
1403 624726 : i = estr - cstr;
1404 624726 : if (cstr != cbuf)
1405 254261 : cx->free_(cstr);
1406 624726 : *ep = i ? s1 + i : s;
1407 624726 : *dp = d;
1408 624726 : return JS_TRUE;
1409 : }
|