Code

r11451@tres: ted | 2006-04-17 22:21:33 -0700
[inkscape.git] / src / dom / js / jsnum.c
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);
113 /* See ECMA 15.1.2.2. */
114 static JSBool
115 num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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);
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)
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;
194 #if JS_HAS_TOSOURCE
195 static JSBool
196 num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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;
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)
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;
251 static JSBool
252 num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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;
293 static JSBool
294 num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
295                    jsval *argv, jsval *rval)
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;
385 static JSBool
386 num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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;
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)
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;
442 static JSBool
443 num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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);
449 static JSBool
450 num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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);
456 static JSBool
457 num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
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);
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)
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;
577 void
578 js_FinishRuntimeNumberState(JSContext *cx)
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;
596 JSObject *
597 js_InitNumberClass(JSContext *cx, JSObject *obj)
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;
632 jsdouble *
633 js_NewDouble(JSContext *cx, jsdouble d)
635     jsdouble *dp;
637     dp = (jsdouble *) js_AllocGCThing(cx, GCX_DOUBLE);
638     if (!dp)
639         return NULL;
640     *dp = d;
641     return dp;
644 void
645 js_FinalizeDouble(JSContext *cx, jsdouble *dp)
647     *dp = NaN;
650 JSBool
651 js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
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;
662 JSBool
663 js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
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;
676 JSObject *
677 js_NumberToObject(JSContext *cx, jsdouble d)
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;
693 JSString *
694 js_NumberToString(JSContext *cx, jsdouble d)
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);
712 JSBool
713 js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
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;
770 JSBool
771 js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
773     jsdouble d;
775     if (!js_ValueToNumber(cx, v, &d))
776         return JS_FALSE;
777     return js_DoubleToECMAInt32(cx, d, ip);
780 JSBool
781 js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)
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;
799 JSBool
800 js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
802     jsdouble d;
804     if (!js_ValueToNumber(cx, v, &d))
805         return JS_FALSE;
806     return js_DoubleToECMAUint32(cx, d, ip);
809 JSBool
810 js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)
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;
831 JSBool
832 js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
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;
856 JSBool
857 js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
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;
885 jsdouble
886 js_DoubleToInteger(jsdouble d)
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;
903 JSBool
904 js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
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;
972 struct BinaryDigitReader
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)
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;
1005 JSBool
1006 js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
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;