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 ***** */
41 /*
42 * JS number type and wrapper class.
43 */
44 #include "jsstddef.h"
45 #if defined(XP_WIN) || defined(XP_OS2)
46 #include <float.h>
47 #endif
48 #include <locale.h>
49 #include <limits.h>
50 #include <math.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include "jstypes.h"
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsapi.h"
56 #include "jsatom.h"
57 #include "jscntxt.h"
58 #include "jsconfig.h"
59 #include "jsdtoa.h"
60 #include "jsgc.h"
61 #include "jsinterp.h"
62 #include "jsnum.h"
63 #include "jsobj.h"
64 #include "jsopcode.h"
65 #include "jsprf.h"
66 #include "jsstr.h"
68 static JSBool
69 num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
70 {
71 jsdouble x;
73 if (!js_ValueToNumber(cx, argv[0], &x))
74 return JS_FALSE;
75 *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x));
76 return JS_TRUE;
77 }
79 static JSBool
80 num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
81 {
82 jsdouble x;
84 if (!js_ValueToNumber(cx, argv[0], &x))
85 return JS_FALSE;
86 *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x));
87 return JS_TRUE;
88 }
90 static JSBool
91 num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
92 {
93 JSString *str;
94 jsdouble d;
95 const jschar *bp, *ep;
97 str = js_ValueToString(cx, argv[0]);
98 if (!str)
99 return JS_FALSE;
100 /* XXXbe js_strtod shouldn't require NUL termination */
101 bp = js_UndependString(cx, str);
102 if (!bp)
103 return JS_FALSE;
104 if (!js_strtod(cx, bp, &ep, &d))
105 return JS_FALSE;
106 if (ep == bp) {
107 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
108 return JS_TRUE;
109 }
110 return js_NewNumberValue(cx, d, rval);
111 }
113 /* See ECMA 15.1.2.2. */
114 static JSBool
115 num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
116 {
117 JSString *str;
118 jsint radix;
119 jsdouble d;
120 const jschar *bp, *ep;
122 str = js_ValueToString(cx, argv[0]);
123 if (!str)
124 return JS_FALSE;
126 if (argc > 1) {
127 if (!js_ValueToECMAInt32(cx, argv[1], &radix))
128 return JS_FALSE;
129 } else
130 radix = 0;
132 if (radix != 0 && (radix < 2 || radix > 36)) {
133 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
134 return JS_TRUE;
135 }
136 /* XXXbe js_strtointeger shouldn't require NUL termination */
137 bp = js_UndependString(cx, str);
138 if (!bp)
139 return JS_FALSE;
140 if (!js_strtointeger(cx, bp, &ep, radix, &d))
141 return JS_FALSE;
142 if (ep == bp) {
143 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
144 return JS_TRUE;
145 }
146 return js_NewNumberValue(cx, d, rval);
147 }
149 const char js_Infinity_str[] = "Infinity";
150 const char js_NaN_str[] = "NaN";
151 const char js_isNaN_str[] = "isNaN";
152 const char js_isFinite_str[] = "isFinite";
153 const char js_parseFloat_str[] = "parseFloat";
154 const char js_parseInt_str[] = "parseInt";
156 static JSFunctionSpec number_functions[] = {
157 {"isNaN", num_isNaN, 1,0,0},
158 {"isFinite", num_isFinite, 1,0,0},
159 {"parseFloat", num_parseFloat, 1,0,0},
160 {"parseInt", num_parseInt, 2,0,0},
161 {0,0,0,0,0}
162 };
164 static JSClass number_class = {
165 "Number",
166 JSCLASS_HAS_PRIVATE,
167 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
168 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
169 JSCLASS_NO_OPTIONAL_MEMBERS
170 };
172 static JSBool
173 Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
174 {
175 jsdouble d;
176 jsval v;
178 if (argc != 0) {
179 if (!js_ValueToNumber(cx, argv[0], &d))
180 return JS_FALSE;
181 } else {
182 d = 0.0;
183 }
184 if (!js_NewNumberValue(cx, d, &v))
185 return JS_FALSE;
186 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
187 *rval = v;
188 return JS_TRUE;
189 }
190 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
191 return JS_TRUE;
192 }
194 #if JS_HAS_TOSOURCE
195 static JSBool
196 num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
197 {
198 jsval v;
199 jsdouble d;
200 char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
201 char buf[64];
202 JSString *str;
204 if (!JS_InstanceOf(cx, obj, &number_class, argv))
205 return JS_FALSE;
206 v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
207 JS_ASSERT(JSVAL_IS_NUMBER(v));
208 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
209 numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
210 if (!numStr) {
211 JS_ReportOutOfMemory(cx);
212 return JS_FALSE;
213 }
214 JS_snprintf(buf, sizeof buf, "(new %s(%s))", number_class.name, numStr);
215 str = JS_NewStringCopyZ(cx, buf);
216 if (!str)
217 return JS_FALSE;
218 *rval = STRING_TO_JSVAL(str);
219 return JS_TRUE;
220 }
221 #endif
223 /* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */
224 static char *
225 IntToString(jsint i, char *buf, size_t bufSize)
226 {
227 char *cp;
228 jsuint u;
230 u = (i < 0) ? -i : i;
232 cp = buf + bufSize; /* one past last buffer cell */
233 *--cp = '\0'; /* null terminate the string to be */
235 /*
236 * Build the string from behind. We use multiply and subtraction
237 * instead of modulus because that's much faster.
238 */
239 do {
240 jsuint newu = u / 10;
241 *--cp = (char)(u - newu * 10) + '0';
242 u = newu;
243 } while (u != 0);
245 if (i < 0)
246 *--cp = '-';
248 return cp;
249 }
251 static JSBool
252 num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
253 {
254 jsval v;
255 jsdouble d;
256 jsint base;
257 JSString *str;
259 if (!JS_InstanceOf(cx, obj, &number_class, argv))
260 return JS_FALSE;
261 v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
262 JS_ASSERT(JSVAL_IS_NUMBER(v));
263 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
264 base = 10;
265 if (argc != 0) {
266 if (!js_ValueToECMAInt32(cx, argv[0], &base))
267 return JS_FALSE;
268 if (base < 2 || base > 36) {
269 char numBuf[12];
270 char *numStr = IntToString(base, numBuf, sizeof numBuf);
271 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,
272 numStr);
273 return JS_FALSE;
274 }
275 }
276 if (base == 10)
277 str = js_NumberToString(cx, d);
278 else {
279 char *dStr = JS_dtobasestr(base, d);
280 if (!dStr) {
281 JS_ReportOutOfMemory(cx);
282 return JS_FALSE;
283 }
284 str = JS_NewStringCopyZ(cx, dStr);
285 free(dStr);
286 }
287 if (!str)
288 return JS_FALSE;
289 *rval = STRING_TO_JSVAL(str);
290 return JS_TRUE;
291 }
293 static JSBool
294 num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
295 jsval *argv, jsval *rval)
296 {
297 char thousandsLength, decimalLength;
298 const char *numGrouping, *tmpGroup;
299 JSRuntime *rt;
300 JSString *numStr, *str;
301 char *num, *buf, *dec, *end, *tmpSrc, *tmpDest;
302 int digits, size, remainder, nrepeat;
304 /*
305 * Create the string, move back to bytes to make string twiddling
306 * a bit easier and so we can insert platform charset seperators.
307 */
308 if (!num_toString(cx, obj, 0, argv, rval))
309 return JS_FALSE;
310 JS_ASSERT(JSVAL_IS_STRING(*rval));
311 numStr = JSVAL_TO_STRING(*rval);
312 num = js_GetStringBytes(numStr);
314 /* Find bit before the decimal. */
315 dec = strchr(num, '.');
316 digits = dec ? dec - num : (int)strlen(num);
317 end = num + digits;
319 rt = cx->runtime;
320 thousandsLength = strlen(rt->thousandsSeparator);
321 decimalLength = strlen(rt->decimalSeparator);
323 /* Figure out how long resulting string will be. */
324 size = digits + (dec ? decimalLength + strlen(dec + 1) : 0);
326 numGrouping = tmpGroup = rt->numGrouping;
327 remainder = digits;
328 if (*num == '-')
329 remainder--;
331 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
332 if (*tmpGroup >= remainder)
333 break;
334 size += thousandsLength;
335 remainder -= *tmpGroup;
336 tmpGroup++;
337 }
338 if (*tmpGroup == '\0' && *numGrouping != '\0') {
339 nrepeat = (remainder - 1) / tmpGroup[-1];
340 size += thousandsLength * nrepeat;
341 remainder -= nrepeat * tmpGroup[-1];
342 } else {
343 nrepeat = 0;
344 }
345 tmpGroup--;
347 buf = (char *)JS_malloc(cx, size + 1);
348 if (!buf)
349 return JS_FALSE;
351 tmpDest = buf;
352 tmpSrc = num;
354 while (*tmpSrc == '-' || remainder--)
355 *tmpDest++ = *tmpSrc++;
356 while (tmpSrc < end) {
357 strcpy(tmpDest, rt->thousandsSeparator);
358 tmpDest += thousandsLength;
359 memcpy(tmpDest, tmpSrc, *tmpGroup);
360 tmpDest += *tmpGroup;
361 tmpSrc += *tmpGroup;
362 if (--nrepeat < 0)
363 tmpGroup--;
364 }
366 if (dec) {
367 strcpy(tmpDest, rt->decimalSeparator);
368 tmpDest += decimalLength;
369 strcpy(tmpDest, dec + 1);
370 } else {
371 *tmpDest++ = '\0';
372 }
374 str = JS_NewString(cx, buf, size);
375 if (!str) {
376 JS_free(cx, buf);
377 return JS_FALSE;
378 }
380 *rval = STRING_TO_JSVAL(str);
382 return JS_TRUE;
383 }
385 static JSBool
386 num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
387 {
388 if (!JS_InstanceOf(cx, obj, &number_class, argv))
389 return JS_FALSE;
390 *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
391 return JS_TRUE;
392 }
395 #if JS_HAS_NUMBER_FORMATS
396 #define MAX_PRECISION 100
398 static JSBool
399 num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode,
400 JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset)
401 {
402 jsval v;
403 jsdouble d, precision;
404 JSString *str;
405 char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */
407 if (!JS_InstanceOf(cx, obj, &number_class, argv))
408 return JS_FALSE;
409 v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
410 JS_ASSERT(JSVAL_IS_NUMBER(v));
411 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
413 if (JSVAL_IS_VOID(argv[0])) {
414 precision = 0.0;
415 oneArgMode = zeroArgMode;
416 } else {
417 if (!js_ValueToNumber(cx, argv[0], &precision))
418 return JS_FALSE;
419 precision = js_DoubleToInteger(precision);
420 if (precision < precisionMin || precision > precisionMax) {
421 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision);
422 if (!numStr)
423 JS_ReportOutOfMemory(cx);
424 else
425 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
426 return JS_FALSE;
427 }
428 }
430 numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d);
431 if (!numStr) {
432 JS_ReportOutOfMemory(cx);
433 return JS_FALSE;
434 }
435 str = JS_NewStringCopyZ(cx, numStr);
436 if (!str)
437 return JS_FALSE;
438 *rval = STRING_TO_JSVAL(str);
439 return JS_TRUE;
440 }
442 static JSBool
443 num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
444 {
445 /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
446 return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0);
447 }
449 static JSBool
450 num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
451 {
452 /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
453 return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1);
454 }
456 static JSBool
457 num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
458 {
459 /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
460 return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0);
461 }
462 #endif /* JS_HAS_NUMBER_FORMATS */
465 static JSFunctionSpec number_methods[] = {
466 #if JS_HAS_TOSOURCE
467 {js_toSource_str, num_toSource, 0,0,0},
468 #endif
469 {js_toString_str, num_toString, 0,0,0},
470 {js_toLocaleString_str, num_toLocaleString, 0,0,0},
471 {js_valueOf_str, num_valueOf, 0,0,0},
472 #if JS_HAS_NUMBER_FORMATS
473 {"toFixed", num_toFixed, 1,0,0},
474 {"toExponential", num_toExponential, 1,0,0},
475 {"toPrecision", num_toPrecision, 1,0,0},
476 #endif
477 {0,0,0,0,0}
478 };
480 /* NB: Keep this in synch with number_constants[]. */
481 enum nc_slot {
482 NC_NaN,
483 NC_POSITIVE_INFINITY,
484 NC_NEGATIVE_INFINITY,
485 NC_MAX_VALUE,
486 NC_MIN_VALUE,
487 NC_LIMIT
488 };
490 /*
491 * Some to most C compilers forbid spelling these at compile time, or barf
492 * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState
493 * using union jsdpun.
494 */
495 static JSConstDoubleSpec number_constants[] = {
496 {0, js_NaN_str, 0,{0,0,0}},
497 {0, "POSITIVE_INFINITY", 0,{0,0,0}},
498 {0, "NEGATIVE_INFINITY", 0,{0,0,0}},
499 {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}},
500 {0, "MIN_VALUE", 0,{0,0,0}},
501 {0,0,0,{0,0,0}}
502 };
504 static jsdouble NaN;
507 #if (defined XP_WIN || defined XP_OS2) && \
508 !defined __MWERKS__ && \
509 (defined _M_IX86 || \
510 (defined __GNUC__ && !defined __MINGW32__))
512 /*
513 * Set the exception mask to mask all exceptions and set the FPU precision
514 * to 53 bit mantissa.
515 * On Alpha platform this is handled via Compiler option.
516 */
517 #define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC)
519 #else
521 #define FIX_FPU() ((void)0)
523 #endif
525 JSBool
526 js_InitRuntimeNumberState(JSContext *cx)
527 {
528 JSRuntime *rt;
529 jsdpun u;
530 struct lconv *locale;
532 rt = cx->runtime;
533 JS_ASSERT(!rt->jsNaN);
535 FIX_FPU();
537 u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
538 u.s.lo = 0xffffffff;
539 number_constants[NC_NaN].dval = NaN = u.d;
540 rt->jsNaN = js_NewDouble(cx, NaN);
541 if (!rt->jsNaN || !js_LockGCThing(cx, rt->jsNaN))
542 return JS_FALSE;
544 u.s.hi = JSDOUBLE_HI32_EXPMASK;
545 u.s.lo = 0x00000000;
546 number_constants[NC_POSITIVE_INFINITY].dval = u.d;
547 rt->jsPositiveInfinity = js_NewDouble(cx, u.d);
548 if (!rt->jsPositiveInfinity ||
549 !js_LockGCThing(cx, rt->jsPositiveInfinity)) {
550 return JS_FALSE;
551 }
553 u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
554 u.s.lo = 0x00000000;
555 number_constants[NC_NEGATIVE_INFINITY].dval = u.d;
556 rt->jsNegativeInfinity = js_NewDouble(cx, u.d);
557 if (!rt->jsNegativeInfinity ||
558 !js_LockGCThing(cx, rt->jsNegativeInfinity)) {
559 return JS_FALSE;
560 }
562 u.s.hi = 0;
563 u.s.lo = 1;
564 number_constants[NC_MIN_VALUE].dval = u.d;
566 locale = localeconv();
567 rt->thousandsSeparator =
568 JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'");
569 rt->decimalSeparator =
570 JS_strdup(cx, locale->decimal_point ? locale->decimal_point : ".");
571 rt->numGrouping =
572 JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0");
574 return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;
575 }
577 void
578 js_FinishRuntimeNumberState(JSContext *cx)
579 {
580 JSRuntime *rt = cx->runtime;
582 js_UnlockGCThingRT(rt, rt->jsNaN);
583 js_UnlockGCThingRT(rt, rt->jsNegativeInfinity);
584 js_UnlockGCThingRT(rt, rt->jsPositiveInfinity);
586 rt->jsNaN = NULL;
587 rt->jsNegativeInfinity = NULL;
588 rt->jsPositiveInfinity = NULL;
590 JS_free(cx, (void *)rt->thousandsSeparator);
591 JS_free(cx, (void *)rt->decimalSeparator);
592 JS_free(cx, (void *)rt->numGrouping);
593 rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL;
594 }
596 JSObject *
597 js_InitNumberClass(JSContext *cx, JSObject *obj)
598 {
599 JSObject *proto, *ctor;
600 JSRuntime *rt;
602 /* XXX must do at least once per new thread, so do it per JSContext... */
603 FIX_FPU();
605 if (!JS_DefineFunctions(cx, obj, number_functions))
606 return NULL;
608 proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1,
609 NULL, number_methods, NULL, NULL);
610 if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
611 return NULL;
612 OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);
613 if (!JS_DefineConstDoubles(cx, ctor, number_constants))
614 return NULL;
616 /* ECMA 15.1.1.1 */
617 rt = cx->runtime;
618 if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),
619 NULL, NULL, JSPROP_PERMANENT)) {
620 return NULL;
621 }
623 /* ECMA 15.1.1.2 */
624 if (!JS_DefineProperty(cx, obj, js_Infinity_str,
625 DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
626 NULL, NULL, JSPROP_PERMANENT)) {
627 return NULL;
628 }
629 return proto;
630 }
632 jsdouble *
633 js_NewDouble(JSContext *cx, jsdouble d)
634 {
635 jsdouble *dp;
637 dp = (jsdouble *) js_AllocGCThing(cx, GCX_DOUBLE);
638 if (!dp)
639 return NULL;
640 *dp = d;
641 return dp;
642 }
644 void
645 js_FinalizeDouble(JSContext *cx, jsdouble *dp)
646 {
647 *dp = NaN;
648 }
650 JSBool
651 js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
652 {
653 jsdouble *dp;
655 dp = js_NewDouble(cx, d);
656 if (!dp)
657 return JS_FALSE;
658 *rval = DOUBLE_TO_JSVAL(dp);
659 return JS_TRUE;
660 }
662 JSBool
663 js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
664 {
665 jsint i;
667 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
668 *rval = INT_TO_JSVAL(i);
669 } else {
670 if (!js_NewDoubleValue(cx, d, rval))
671 return JS_FALSE;
672 }
673 return JS_TRUE;
674 }
676 JSObject *
677 js_NumberToObject(JSContext *cx, jsdouble d)
678 {
679 JSObject *obj;
680 jsval v;
682 obj = js_NewObject(cx, &number_class, NULL, NULL);
683 if (!obj)
684 return NULL;
685 if (!js_NewNumberValue(cx, d, &v)) {
686 cx->newborn[GCX_OBJECT] = NULL;
687 return NULL;
688 }
689 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
690 return obj;
691 }
693 JSString *
694 js_NumberToString(JSContext *cx, jsdouble d)
695 {
696 jsint i;
697 char buf[DTOSTR_STANDARD_BUFFER_SIZE];
698 char *numStr;
700 if (JSDOUBLE_IS_INT(d, i))
701 numStr = IntToString(i, buf, sizeof buf);
702 else {
703 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
704 if (!numStr) {
705 JS_ReportOutOfMemory(cx);
706 return NULL;
707 }
708 }
709 return JS_NewStringCopyZ(cx, numStr);
710 }
712 JSBool
713 js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
714 {
715 JSObject *obj;
716 JSString *str;
717 const jschar *bp, *ep;
719 if (JSVAL_IS_OBJECT(v)) {
720 obj = JSVAL_TO_OBJECT(v);
721 if (!obj) {
722 *dp = 0;
723 return JS_TRUE;
724 }
725 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v))
726 return JS_FALSE;
727 }
728 if (JSVAL_IS_INT(v)) {
729 *dp = (jsdouble)JSVAL_TO_INT(v);
730 } else if (JSVAL_IS_DOUBLE(v)) {
731 *dp = *JSVAL_TO_DOUBLE(v);
732 } else if (JSVAL_IS_STRING(v)) {
733 str = JSVAL_TO_STRING(v);
734 /*
735 * Note that ECMA doesn't treat a string beginning with a '0' as an
736 * octal number here. This works because all such numbers will be
737 * interpreted as decimal by js_strtod and will never get passed to
738 * js_strtointeger (which would interpret them as octal).
739 */
740 /* XXXbe js_strtod shouldn't require NUL termination */
741 bp = js_UndependString(cx, str);
742 if (!bp)
743 return JS_FALSE;
744 if ((!js_strtod(cx, bp, &ep, dp) ||
745 js_SkipWhiteSpace(ep) != bp + str->length) &&
746 (!js_strtointeger(cx, bp, &ep, 0, dp) ||
747 js_SkipWhiteSpace(ep) != bp + str->length)) {
748 goto badstr;
749 }
750 } else if (JSVAL_IS_BOOLEAN(v)) {
751 *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;
752 } else {
753 #if JS_BUG_FALLIBLE_TONUM
754 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
755 badstr:
756 if (str) {
757 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN,
758 JS_GetStringBytes(str));
760 }
761 return JS_FALSE;
762 #else
763 badstr:
764 *dp = *cx->runtime->jsNaN;
765 #endif
766 }
767 return JS_TRUE;
768 }
770 JSBool
771 js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
772 {
773 jsdouble d;
775 if (!js_ValueToNumber(cx, v, &d))
776 return JS_FALSE;
777 return js_DoubleToECMAInt32(cx, d, ip);
778 }
780 JSBool
781 js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)
782 {
783 jsdouble two32 = 4294967296.0;
784 jsdouble two31 = 2147483648.0;
786 if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
787 *ip = 0;
788 return JS_TRUE;
789 }
790 d = fmod(d, two32);
791 d = (d >= 0) ? floor(d) : ceil(d) + two32;
792 if (d >= two31)
793 *ip = (int32)(d - two32);
794 else
795 *ip = (int32)d;
796 return JS_TRUE;
797 }
799 JSBool
800 js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
801 {
802 jsdouble d;
804 if (!js_ValueToNumber(cx, v, &d))
805 return JS_FALSE;
806 return js_DoubleToECMAUint32(cx, d, ip);
807 }
809 JSBool
810 js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)
811 {
812 JSBool neg;
813 jsdouble two32 = 4294967296.0;
815 if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
816 *ip = 0;
817 return JS_TRUE;
818 }
820 neg = (d < 0);
821 d = floor(neg ? -d : d);
822 d = neg ? -d : d;
824 d = fmod(d, two32);
826 d = (d >= 0) ? d : d + two32;
827 *ip = (uint32)d;
828 return JS_TRUE;
829 }
831 JSBool
832 js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
833 {
834 jsdouble d;
835 JSString *str;
837 if (JSVAL_IS_INT(v)) {
838 *ip = JSVAL_TO_INT(v);
839 return JS_TRUE;
840 }
841 if (!js_ValueToNumber(cx, v, &d))
842 return JS_FALSE;
843 if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
844 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
845 if (str) {
846 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
847 JSMSG_CANT_CONVERT, JS_GetStringBytes(str));
849 }
850 return JS_FALSE;
851 }
852 *ip = (int32)floor(d + 0.5); /* Round to nearest */
853 return JS_TRUE;
854 }
856 JSBool
857 js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
858 {
859 jsdouble d;
860 jsuint i, m;
861 JSBool neg;
863 if (!js_ValueToNumber(cx, v, &d))
864 return JS_FALSE;
865 if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
866 *ip = 0;
867 return JS_TRUE;
868 }
869 i = (jsuint)d;
870 if ((jsdouble)i == d) {
871 *ip = (uint16)i;
872 return JS_TRUE;
873 }
874 neg = (d < 0);
875 d = floor(neg ? -d : d);
876 d = neg ? -d : d;
877 m = JS_BIT(16);
878 d = fmod(d, (double)m);
879 if (d < 0)
880 d += m;
881 *ip = (uint16) d;
882 return JS_TRUE;
883 }
885 jsdouble
886 js_DoubleToInteger(jsdouble d)
887 {
888 JSBool neg;
890 if (d == 0)
891 return d;
892 if (!JSDOUBLE_IS_FINITE(d)) {
893 if (JSDOUBLE_IS_NaN(d))
894 return 0;
895 return d;
896 }
897 neg = (d < 0);
898 d = floor(neg ? -d : d);
899 return neg ? -d : d;
900 }
903 JSBool
904 js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
905 {
906 char cbuf[32];
907 size_t i;
908 char *cstr, *istr, *estr;
909 JSBool negative;
910 jsdouble d;
911 const jschar *s1 = js_SkipWhiteSpace(s);
912 size_t length = js_strlen(s1);
914 /* Use cbuf to avoid malloc */
915 if (length >= sizeof cbuf) {
916 cstr = (char *) JS_malloc(cx, length + 1);
917 if (!cstr)
918 return JS_FALSE;
919 } else {
920 cstr = cbuf;
921 }
923 for (i = 0; i <= length; i++) {
924 if (s1[i] >> 8) {
925 cstr[i] = 0;
926 break;
927 }
928 cstr[i] = (char)s1[i];
929 }
931 istr = cstr;
932 if ((negative = (*istr == '-')) != 0 || *istr == '+')
933 istr++;
934 if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
935 d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);
936 estr = istr + 8;
937 } else {
938 int err;
939 d = JS_strtod(cstr, &estr, &err);
940 if (err == JS_DTOA_ENOMEM) {
941 JS_ReportOutOfMemory(cx);
942 if (cstr != cbuf)
943 JS_free(cx, cstr);
944 return JS_FALSE;
945 }
946 if (err == JS_DTOA_ERANGE) {
947 if (d == HUGE_VAL)
948 d = *cx->runtime->jsPositiveInfinity;
949 else if (d == -HUGE_VAL)
950 d = *cx->runtime->jsNegativeInfinity;
951 }
952 #ifdef HPUX
953 if (d == 0.0 && negative) {
954 /*
955 * "-0", "-1e-2000" come out as positive zero
956 * here on HPUX. Force a negative zero instead.
957 */
958 JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT;
959 JSDOUBLE_LO32(d) = 0;
960 }
961 #endif
962 }
964 i = estr - cstr;
965 if (cstr != cbuf)
966 JS_free(cx, cstr);
967 *ep = i ? s1 + i : s;
968 *dp = d;
969 return JS_TRUE;
970 }
972 struct BinaryDigitReader
973 {
974 uintN base; /* Base of number; must be a power of 2 */
975 uintN digit; /* Current digit value in radix given by base */
976 uintN digitMask; /* Mask to extract the next bit from digit */
977 const jschar *digits; /* Pointer to the remaining digits */
978 const jschar *end; /* Pointer to first non-digit */
979 };
981 /* Return the next binary digit from the number or -1 if done */
982 static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
983 {
984 intN bit;
986 if (bdr->digitMask == 0) {
987 uintN c;
989 if (bdr->digits == bdr->end)
990 return -1;
992 c = *bdr->digits++;
993 if ('0' <= c && c <= '9')
994 bdr->digit = c - '0';
995 else if ('a' <= c && c <= 'z')
996 bdr->digit = c - 'a' + 10;
997 else bdr->digit = c - 'A' + 10;
998 bdr->digitMask = bdr->base >> 1;
999 }
1000 bit = (bdr->digit & bdr->digitMask) != 0;
1001 bdr->digitMask >>= 1;
1002 return bit;
1003 }
1005 JSBool
1006 js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
1007 {
1008 JSBool negative;
1009 jsdouble value;
1010 const jschar *start;
1011 const jschar *s1 = js_SkipWhiteSpace(s);
1013 if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
1014 s1++;
1016 if (base == 0) {
1017 /* No base supplied, or some base that evaluated to 0. */
1018 if (*s1 == '0') {
1019 /* It's either hex or octal; only increment char if str isn't '0' */
1020 if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
1021 s1 += 2;
1022 base = 16;
1023 } else { /* Octal */
1024 base = 8;
1025 }
1026 } else {
1027 base = 10; /* Default to decimal. */
1028 }
1029 } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) {
1030 /* If base is 16, ignore hex prefix. */
1031 s1 += 2;
1032 }
1034 /*
1035 * Done with the preliminaries; find some prefix of the string that's
1036 * a number in the given base.
1037 */
1038 start = s1; /* Mark - if string is empty, we return NaN. */
1039 value = 0.0;
1040 for (;;) {
1041 uintN digit;
1042 jschar c = *s1;
1043 if ('0' <= c && c <= '9')
1044 digit = c - '0';
1045 else if ('a' <= c && c <= 'z')
1046 digit = c - 'a' + 10;
1047 else if ('A' <= c && c <= 'Z')
1048 digit = c - 'A' + 10;
1049 else
1050 break;
1051 if (digit >= (uintN)base)
1052 break;
1053 value = value * base + digit;
1054 s1++;
1055 }
1057 if (value >= 9007199254740992.0) {
1058 if (base == 10) {
1059 /*
1060 * If we're accumulating a decimal number and the number is >=
1061 * 2^53, then the result from the repeated multiply-add above may
1062 * be inaccurate. Call JS_strtod to get the correct answer.
1063 */
1064 size_t i;
1065 size_t length = s1 - start;
1066 char *cstr = (char *) JS_malloc(cx, length + 1);
1067 char *estr;
1068 int err=0;
1070 if (!cstr)
1071 return JS_FALSE;
1072 for (i = 0; i != length; i++)
1073 cstr[i] = (char)start[i];
1074 cstr[length] = 0;
1076 value = JS_strtod(cstr, &estr, &err);
1077 if (err == JS_DTOA_ENOMEM) {
1078 JS_ReportOutOfMemory(cx);
1079 JS_free(cx, cstr);
1080 return JS_FALSE;
1081 }
1082 if (err == JS_DTOA_ERANGE && value == HUGE_VAL)
1083 value = *cx->runtime->jsPositiveInfinity;
1084 JS_free(cx, cstr);
1085 } else if ((base & (base - 1)) == 0) {
1086 /*
1087 * The number may also be inaccurate for power-of-two bases. This
1088 * happens if the addition in value * base + digit causes a round-
1089 * down to an even least significant mantissa bit when the first
1090 * dropped bit is a one. If any of the following digits in the
1091 * number (which haven't been added in yet) are nonzero, then the
1092 * correct action would have been to round up instead of down. An
1093 * example occurs when reading the number 0x1000000000000081, which
1094 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
1095 */
1096 struct BinaryDigitReader bdr;
1097 intN bit, bit2;
1098 intN j;
1100 bdr.base = base;
1101 bdr.digitMask = 0;
1102 bdr.digits = start;
1103 bdr.end = s1;
1104 value = 0.0;
1106 /* Skip leading zeros. */
1107 do {
1108 bit = GetNextBinaryDigit(&bdr);
1109 } while (bit == 0);
1111 if (bit == 1) {
1112 /* Gather the 53 significant bits (including the leading 1) */
1113 value = 1.0;
1114 for (j = 52; j; j--) {
1115 bit = GetNextBinaryDigit(&bdr);
1116 if (bit < 0)
1117 goto done;
1118 value = value*2 + bit;
1119 }
1120 /* bit2 is the 54th bit (the first dropped from the mantissa) */
1121 bit2 = GetNextBinaryDigit(&bdr);
1122 if (bit2 >= 0) {
1123 jsdouble factor = 2.0;
1124 intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
1125 intN bit3;
1127 while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) {
1128 sticky |= bit3;
1129 factor *= 2;
1130 }
1131 value += bit2 & (bit | sticky);
1132 value *= factor;
1133 }
1134 done:;
1135 }
1136 }
1137 }
1138 /* We don't worry about inaccurate numbers for any other base. */
1140 if (s1 == start) {
1141 *dp = 0.0;
1142 *ep = s;
1143 } else {
1144 *dp = negative ? -value : value;
1145 *ep = s1;
1146 }
1147 return JS_TRUE;
1148 }