Back to index

lightning-sunbird  0.9+nobinonly
jsnum.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   IBM Corp.
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS number type and wrapper class.
00043  */
00044 #include "jsstddef.h"
00045 #if defined(XP_WIN) || defined(XP_OS2)
00046 #include <float.h>
00047 #endif
00048 #include <locale.h>
00049 #include <limits.h>
00050 #include <math.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053 #include "jstypes.h"
00054 #include "jsutil.h" /* Added by JSIFY */
00055 #include "jsapi.h"
00056 #include "jsatom.h"
00057 #include "jscntxt.h"
00058 #include "jsconfig.h"
00059 #include "jsdtoa.h"
00060 #include "jsgc.h"
00061 #include "jsinterp.h"
00062 #include "jsnum.h"
00063 #include "jsobj.h"
00064 #include "jsopcode.h"
00065 #include "jsprf.h"
00066 #include "jsstr.h"
00067 
00068 static JSBool
00069 num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00070 {
00071     jsdouble x;
00072 
00073     if (!js_ValueToNumber(cx, argv[0], &x))
00074         return JS_FALSE;
00075     *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x));
00076     return JS_TRUE;
00077 }
00078 
00079 static JSBool
00080 num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00081 {
00082     jsdouble x;
00083 
00084     if (!js_ValueToNumber(cx, argv[0], &x))
00085         return JS_FALSE;
00086     *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x));
00087     return JS_TRUE;
00088 }
00089 
00090 static JSBool
00091 num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00092 {
00093     JSString *str;
00094     jsdouble d;
00095     const jschar *bp, *ep;
00096 
00097     str = js_ValueToString(cx, argv[0]);
00098     if (!str)
00099         return JS_FALSE;
00100     /* XXXbe js_strtod shouldn't require NUL termination */
00101     bp = js_UndependString(cx, str);
00102     if (!bp)
00103         return JS_FALSE;
00104     if (!js_strtod(cx, bp, &ep, &d))
00105         return JS_FALSE;
00106     if (ep == bp) {
00107         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00108         return JS_TRUE;
00109     }
00110     return js_NewNumberValue(cx, d, rval);
00111 }
00112 
00113 /* See ECMA 15.1.2.2. */
00114 static JSBool
00115 num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00116 {
00117     jsint radix;
00118     JSString *str;
00119     jsdouble d;
00120     const jschar *bp, *ep;
00121 
00122     if (argc > 1) {
00123         if (!js_ValueToECMAInt32(cx, argv[1], &radix))
00124             return JS_FALSE;
00125     } else {
00126         radix = 0;
00127     }
00128     if (radix != 0 && (radix < 2 || radix > 36)) {
00129         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00130         return JS_TRUE;
00131     }
00132 
00133     str = js_ValueToString(cx, argv[0]);
00134     if (!str)
00135         return JS_FALSE;
00136     /* XXXbe js_strtointeger shouldn't require NUL termination */
00137     bp = js_UndependString(cx, str);
00138     if (!bp)
00139         return JS_FALSE;
00140     if (!js_strtointeger(cx, bp, &ep, radix, &d))
00141         return JS_FALSE;
00142     if (ep == bp) {
00143         *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
00144         return JS_TRUE;
00145     }
00146     return js_NewNumberValue(cx, d, rval);
00147 }
00148 
00149 const char js_Infinity_str[]   = "Infinity";
00150 const char js_NaN_str[]        = "NaN";
00151 const char js_isNaN_str[]      = "isNaN";
00152 const char js_isFinite_str[]   = "isFinite";
00153 const char js_parseFloat_str[] = "parseFloat";
00154 const char js_parseInt_str[]   = "parseInt";
00155 
00156 static JSFunctionSpec number_functions[] = {
00157     {js_isNaN_str,          num_isNaN,              1,0,0},
00158     {js_isFinite_str,       num_isFinite,           1,0,0},
00159     {js_parseFloat_str,     num_parseFloat,         1,0,0},
00160     {js_parseInt_str,       num_parseInt,           2,0,0},
00161     {0,0,0,0,0}
00162 };
00163 
00164 JSClass js_NumberClass = {
00165     js_Number_str,
00166     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
00167     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00168     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
00169     JSCLASS_NO_OPTIONAL_MEMBERS
00170 };
00171 
00172 static JSBool
00173 Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00174 {
00175     jsdouble d;
00176     jsval v;
00177 
00178     if (argc != 0) {
00179         if (!js_ValueToNumber(cx, argv[0], &d))
00180             return JS_FALSE;
00181     } else {
00182         d = 0.0;
00183     }
00184     if (!js_NewNumberValue(cx, d, &v))
00185         return JS_FALSE;
00186     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
00187         *rval = v;
00188         return JS_TRUE;
00189     }
00190     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
00191     return JS_TRUE;
00192 }
00193 
00194 #if JS_HAS_TOSOURCE
00195 static JSBool
00196 num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00197 {
00198     jsval v;
00199     jsdouble d;
00200     char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
00201     char buf[64];
00202     JSString *str;
00203 
00204     if (JSVAL_IS_NUMBER((jsval)obj)) {
00205         v = (jsval)obj;
00206     } else {
00207         if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv))
00208             return JS_FALSE;
00209         v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
00210         JS_ASSERT(JSVAL_IS_NUMBER(v));
00211     }
00212     d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
00213     numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
00214     if (!numStr) {
00215         JS_ReportOutOfMemory(cx);
00216         return JS_FALSE;
00217     }
00218     JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr);
00219     str = JS_NewStringCopyZ(cx, buf);
00220     if (!str)
00221         return JS_FALSE;
00222     *rval = STRING_TO_JSVAL(str);
00223     return JS_TRUE;
00224 }
00225 #endif
00226 
00227 /* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */
00228 static char *
00229 IntToString(jsint i, char *buf, size_t bufSize)
00230 {
00231     char *cp;
00232     jsuint u;
00233 
00234     u = (i < 0) ? -i : i;
00235 
00236     cp = buf + bufSize; /* one past last buffer cell */
00237     *--cp = '\0';       /* null terminate the string to be */
00238 
00239     /*
00240      * Build the string from behind. We use multiply and subtraction
00241      * instead of modulus because that's much faster.
00242      */
00243     do {
00244         jsuint newu = u / 10;
00245         *--cp = (char)(u - newu * 10) + '0';
00246         u = newu;
00247     } while (u != 0);
00248 
00249     if (i < 0)
00250         *--cp = '-';
00251 
00252     return cp;
00253 }
00254 
00255 static JSBool
00256 num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00257 {
00258     jsval v;
00259     jsdouble d;
00260     jsint base;
00261     JSString *str;
00262 
00263     if (JSVAL_IS_NUMBER((jsval)obj)) {
00264         v = (jsval)obj;
00265     } else {
00266         if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv))
00267             return JS_FALSE;
00268         v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
00269         JS_ASSERT(JSVAL_IS_NUMBER(v));
00270     }
00271     d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
00272     base = 10;
00273     if (argc != 0) {
00274         if (!js_ValueToECMAInt32(cx, argv[0], &base))
00275             return JS_FALSE;
00276         if (base < 2 || base > 36) {
00277             char numBuf[12];
00278             char *numStr = IntToString(base, numBuf, sizeof numBuf);
00279             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,
00280                                  numStr);
00281             return JS_FALSE;
00282         }
00283     }
00284     if (base == 10) {
00285         str = js_NumberToString(cx, d);
00286     } else {
00287         char *dStr = JS_dtobasestr(base, d);
00288         if (!dStr) {
00289             JS_ReportOutOfMemory(cx);
00290             return JS_FALSE;
00291         }
00292         str = JS_NewStringCopyZ(cx, dStr);
00293         free(dStr);
00294     }
00295     if (!str)
00296         return JS_FALSE;
00297     *rval = STRING_TO_JSVAL(str);
00298     return JS_TRUE;
00299 }
00300 
00301 static JSBool
00302 num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,
00303                    jsval *argv, jsval *rval)
00304 {
00305     char thousandsLength, decimalLength;
00306     const char *numGrouping, *tmpGroup;
00307     JSRuntime *rt;
00308     JSString *numStr, *str;
00309     char *num, *buf, *nint, *end, *tmpSrc, *tmpDest;
00310     int digits, size, remainder, nrepeat;
00311 
00312     /*
00313      * Create the string, move back to bytes to make string twiddling
00314      * a bit easier and so we can insert platform charset seperators.
00315      */
00316     if (!num_toString(cx, obj, 0, argv, rval))
00317         return JS_FALSE;
00318     JS_ASSERT(JSVAL_IS_STRING(*rval));
00319     numStr = JSVAL_TO_STRING(*rval);
00320     num = js_GetStringBytes(cx->runtime, numStr);
00321 
00322     /*
00323      * Find the first non-integer value, whether it be a letter as in
00324      * 'Infinity', a decimal point, or an 'e' from exponential notation.
00325      */
00326     nint = num;
00327     if (*nint == '-')
00328         nint++;
00329     while(*nint >= '0' && *nint <= '9')
00330         nint++;
00331     digits = nint - num;
00332     end = num + digits;
00333     if (!digits)
00334         return JS_TRUE;
00335 
00336     rt = cx->runtime;
00337     thousandsLength = strlen(rt->thousandsSeparator);
00338     decimalLength = strlen(rt->decimalSeparator);
00339 
00340     /* Figure out how long resulting string will be. */
00341     size = digits + (*nint ? strlen(nint + 1) + 1 : 0);
00342     if (*nint == '.')
00343         size += decimalLength;
00344 
00345     numGrouping = tmpGroup = rt->numGrouping;
00346     remainder = digits;
00347     if (*num == '-')
00348         remainder--;
00349 
00350     while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
00351         if (*tmpGroup >= remainder)
00352             break;
00353         size += thousandsLength;
00354         remainder -= *tmpGroup;
00355         tmpGroup++;
00356     }
00357     if (*tmpGroup == '\0' && *numGrouping != '\0') {
00358         nrepeat = (remainder - 1) / tmpGroup[-1];
00359         size += thousandsLength * nrepeat;
00360         remainder -= nrepeat * tmpGroup[-1];
00361     } else {
00362         nrepeat = 0;
00363     }
00364     tmpGroup--;
00365 
00366     buf = (char *)JS_malloc(cx, size + 1);
00367     if (!buf)
00368         return JS_FALSE;
00369 
00370     tmpDest = buf;
00371     tmpSrc = num;
00372 
00373     while (*tmpSrc == '-' || remainder--)
00374         *tmpDest++ = *tmpSrc++;
00375     while (tmpSrc < end) {
00376         strcpy(tmpDest, rt->thousandsSeparator);
00377         tmpDest += thousandsLength;
00378         memcpy(tmpDest, tmpSrc, *tmpGroup);
00379         tmpDest += *tmpGroup;
00380         tmpSrc += *tmpGroup;
00381         if (--nrepeat < 0)
00382             tmpGroup--;
00383     }
00384 
00385     if (*nint == '.') {
00386         strcpy(tmpDest, rt->decimalSeparator);
00387         tmpDest += decimalLength;
00388         strcpy(tmpDest, nint + 1);
00389     } else {
00390         strcpy(tmpDest, nint);
00391     }
00392 
00393     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
00394         return cx->localeCallbacks->localeToUnicode(cx, buf, rval);
00395 
00396     str = JS_NewString(cx, buf, size);
00397     if (!str) {
00398         JS_free(cx, buf);
00399         return JS_FALSE;
00400     }
00401 
00402     *rval = STRING_TO_JSVAL(str);
00403 
00404     return JS_TRUE;
00405 }
00406 
00407 static JSBool
00408 num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00409 {
00410     if (JSVAL_IS_NUMBER((jsval)obj)) {
00411         *rval = (jsval)obj;
00412         return JS_TRUE;
00413     }
00414     if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv))
00415         return JS_FALSE;
00416     *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
00417     return JS_TRUE;
00418 }
00419 
00420 
00421 #define MAX_PRECISION 100
00422 
00423 static JSBool
00424 num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode,
00425        JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset)
00426 {
00427     jsval v;
00428     jsdouble d, precision;
00429     JSString *str;
00430     char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */
00431 
00432     if (JSVAL_IS_NUMBER((jsval)obj)) {
00433         v = (jsval)obj;
00434     } else {
00435         if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv))
00436             return JS_FALSE;
00437         v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
00438         JS_ASSERT(JSVAL_IS_NUMBER(v));
00439     }
00440     d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);
00441 
00442     if (JSVAL_IS_VOID(argv[0])) {
00443         precision = 0.0;
00444         oneArgMode = zeroArgMode;
00445     } else {
00446         if (!js_ValueToNumber(cx, argv[0], &precision))
00447             return JS_FALSE;
00448         precision = js_DoubleToInteger(precision);
00449         if (precision < precisionMin || precision > precisionMax) {
00450             numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision);
00451             if (!numStr)
00452                 JS_ReportOutOfMemory(cx);
00453             else
00454                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
00455             return JS_FALSE;
00456         }
00457     }
00458 
00459     numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d);
00460     if (!numStr) {
00461         JS_ReportOutOfMemory(cx);
00462         return JS_FALSE;
00463     }
00464     str = JS_NewStringCopyZ(cx, numStr);
00465     if (!str)
00466         return JS_FALSE;
00467     *rval = STRING_TO_JSVAL(str);
00468     return JS_TRUE;
00469 }
00470 
00471 static JSBool
00472 num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00473 {
00474     /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
00475     return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0);
00476 }
00477 
00478 static JSBool
00479 num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00480 {
00481     /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
00482     return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1);
00483 }
00484 
00485 static JSBool
00486 num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00487 {
00488     /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */
00489     return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0);
00490 }
00491 
00492 static JSFunctionSpec number_methods[] = {
00493 #if JS_HAS_TOSOURCE
00494     {js_toSource_str,       num_toSource,       0,JSFUN_THISP_NUMBER,0},
00495 #endif
00496     {js_toString_str,       num_toString,       0,JSFUN_THISP_NUMBER,0},
00497     {js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER,0},
00498     {js_valueOf_str,        num_valueOf,        0,JSFUN_THISP_NUMBER,0},
00499     {"toFixed",             num_toFixed,        1,JSFUN_THISP_NUMBER,0},
00500     {"toExponential",       num_toExponential,  1,JSFUN_THISP_NUMBER,0},
00501     {"toPrecision",         num_toPrecision,    1,JSFUN_THISP_NUMBER,0},
00502     {0,0,0,0,0}
00503 };
00504 
00505 /* NB: Keep this in synch with number_constants[]. */
00506 enum nc_slot {
00507     NC_NaN,
00508     NC_POSITIVE_INFINITY,
00509     NC_NEGATIVE_INFINITY,
00510     NC_MAX_VALUE,
00511     NC_MIN_VALUE,
00512     NC_LIMIT
00513 };
00514 
00515 /*
00516  * Some to most C compilers forbid spelling these at compile time, or barf
00517  * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState
00518  * using union jsdpun.
00519  */
00520 static JSConstDoubleSpec number_constants[] = {
00521     {0,                         js_NaN_str,          0,{0,0,0}},
00522     {0,                         "POSITIVE_INFINITY", 0,{0,0,0}},
00523     {0,                         "NEGATIVE_INFINITY", 0,{0,0,0}},
00524     {1.7976931348623157E+308,   "MAX_VALUE",         0,{0,0,0}},
00525     {0,                         "MIN_VALUE",         0,{0,0,0}},
00526     {0,0,0,{0,0,0}}
00527 };
00528 
00529 static jsdouble NaN;
00530 
00531 #if (defined XP_WIN || defined XP_OS2) &&                                     \
00532     !defined WINCE &&                                                         \
00533     !defined __MWERKS__ &&                                                    \
00534     (defined _M_IX86 ||                                                       \
00535      (defined __GNUC__ && !defined __MINGW32__))
00536 
00537 /*
00538  * Set the exception mask to mask all exceptions and set the FPU precision
00539  * to 53 bit mantissa.
00540  * On Alpha platform this is handled via Compiler option.
00541  */
00542 #define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC)
00543 
00544 #else
00545 
00546 #define FIX_FPU() ((void)0)
00547 
00548 #endif
00549 
00550 JSBool
00551 js_InitRuntimeNumberState(JSContext *cx)
00552 {
00553     JSRuntime *rt;
00554     jsdpun u;
00555     struct lconv *locale;
00556 
00557     rt = cx->runtime;
00558     JS_ASSERT(!rt->jsNaN);
00559 
00560     FIX_FPU();
00561 
00562     u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
00563     u.s.lo = 0xffffffff;
00564     number_constants[NC_NaN].dval = NaN = u.d;
00565     rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK);
00566     if (!rt->jsNaN)
00567         return JS_FALSE;
00568 
00569     u.s.hi = JSDOUBLE_HI32_EXPMASK;
00570     u.s.lo = 0x00000000;
00571     number_constants[NC_POSITIVE_INFINITY].dval = u.d;
00572     rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK);
00573     if (!rt->jsPositiveInfinity)
00574         return JS_FALSE;
00575 
00576     u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
00577     u.s.lo = 0x00000000;
00578     number_constants[NC_NEGATIVE_INFINITY].dval = u.d;
00579     rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK);
00580     if (!rt->jsNegativeInfinity)
00581         return JS_FALSE;
00582 
00583     u.s.hi = 0;
00584     u.s.lo = 1;
00585     number_constants[NC_MIN_VALUE].dval = u.d;
00586 
00587     locale = localeconv();
00588     rt->thousandsSeparator =
00589         JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'");
00590     rt->decimalSeparator =
00591         JS_strdup(cx, locale->decimal_point ? locale->decimal_point : ".");
00592     rt->numGrouping =
00593         JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0");
00594 
00595     return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;
00596 }
00597 
00598 void
00599 js_FinishRuntimeNumberState(JSContext *cx)
00600 {
00601     JSRuntime *rt = cx->runtime;
00602 
00603     js_UnlockGCThingRT(rt, rt->jsNaN);
00604     js_UnlockGCThingRT(rt, rt->jsNegativeInfinity);
00605     js_UnlockGCThingRT(rt, rt->jsPositiveInfinity);
00606 
00607     rt->jsNaN = NULL;
00608     rt->jsNegativeInfinity = NULL;
00609     rt->jsPositiveInfinity = NULL;
00610 
00611     JS_free(cx, (void *)rt->thousandsSeparator);
00612     JS_free(cx, (void *)rt->decimalSeparator);
00613     JS_free(cx, (void *)rt->numGrouping);
00614     rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL;
00615 }
00616 
00617 JSObject *
00618 js_InitNumberClass(JSContext *cx, JSObject *obj)
00619 {
00620     JSObject *proto, *ctor;
00621     JSRuntime *rt;
00622 
00623     /* XXX must do at least once per new thread, so do it per JSContext... */
00624     FIX_FPU();
00625 
00626     if (!JS_DefineFunctions(cx, obj, number_functions))
00627         return NULL;
00628 
00629     proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1,
00630                          NULL, number_methods, NULL, NULL);
00631     if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
00632         return NULL;
00633     OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);
00634     if (!JS_DefineConstDoubles(cx, ctor, number_constants))
00635         return NULL;
00636 
00637     /* ECMA 15.1.1.1 */
00638     rt = cx->runtime;
00639     if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),
00640                            NULL, NULL, JSPROP_PERMANENT)) {
00641         return NULL;
00642     }
00643 
00644     /* ECMA 15.1.1.2 */
00645     if (!JS_DefineProperty(cx, obj, js_Infinity_str,
00646                            DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
00647                            NULL, NULL, JSPROP_PERMANENT)) {
00648         return NULL;
00649     }
00650     return proto;
00651 }
00652 
00653 jsdouble *
00654 js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag)
00655 {
00656     jsdouble *dp;
00657 
00658     dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble));
00659     if (!dp)
00660         return NULL;
00661     *dp = d;
00662     return dp;
00663 }
00664 
00665 void
00666 js_FinalizeDouble(JSContext *cx, jsdouble *dp)
00667 {
00668     *dp = NaN;
00669 }
00670 
00671 JSBool
00672 js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
00673 {
00674     jsdouble *dp;
00675 
00676     dp = js_NewDouble(cx, d, 0);
00677     if (!dp)
00678         return JS_FALSE;
00679     *rval = DOUBLE_TO_JSVAL(dp);
00680     return JS_TRUE;
00681 }
00682 
00683 JSBool
00684 js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
00685 {
00686     jsint i;
00687 
00688     if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
00689         *rval = INT_TO_JSVAL(i);
00690     } else {
00691         if (!js_NewDoubleValue(cx, d, rval))
00692             return JS_FALSE;
00693     }
00694     return JS_TRUE;
00695 }
00696 
00697 JSObject *
00698 js_NumberToObject(JSContext *cx, jsdouble d)
00699 {
00700     JSObject *obj;
00701     jsval v;
00702 
00703     obj = js_NewObject(cx, &js_NumberClass, NULL, NULL);
00704     if (!obj)
00705         return NULL;
00706     if (!js_NewNumberValue(cx, d, &v)) {
00707         cx->weakRoots.newborn[GCX_OBJECT] = NULL;
00708         return NULL;
00709     }
00710     OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
00711     return obj;
00712 }
00713 
00714 JSString *
00715 js_NumberToString(JSContext *cx, jsdouble d)
00716 {
00717     jsint i;
00718     char buf[DTOSTR_STANDARD_BUFFER_SIZE];
00719     char *numStr;
00720 
00721     if (JSDOUBLE_IS_INT(d, i)) {
00722         numStr = IntToString(i, buf, sizeof buf);
00723     } else {
00724         numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
00725         if (!numStr) {
00726             JS_ReportOutOfMemory(cx);
00727             return NULL;
00728         }
00729     }
00730     return JS_NewStringCopyZ(cx, numStr);
00731 }
00732 
00733 JSBool
00734 js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
00735 {
00736     JSObject *obj;
00737     JSString *str;
00738     const jschar *bp, *ep;
00739 
00740     if (JSVAL_IS_OBJECT(v)) {
00741         obj = JSVAL_TO_OBJECT(v);
00742         if (!obj) {
00743             *dp = 0;
00744             return JS_TRUE;
00745         }
00746         if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v))
00747             return JS_FALSE;
00748     }
00749     if (JSVAL_IS_INT(v)) {
00750         *dp = (jsdouble)JSVAL_TO_INT(v);
00751     } else if (JSVAL_IS_DOUBLE(v)) {
00752         *dp = *JSVAL_TO_DOUBLE(v);
00753     } else if (JSVAL_IS_STRING(v)) {
00754         str = JSVAL_TO_STRING(v);
00755         /*
00756          * Note that ECMA doesn't treat a string beginning with a '0' as an
00757          * octal number here.  This works because all such numbers will be
00758          * interpreted as decimal by js_strtod and will never get passed to
00759          * js_strtointeger (which would interpret them as octal).
00760          */
00761         /* XXXbe js_strtod shouldn't require NUL termination */
00762         bp = js_UndependString(cx, str);
00763         if (!bp)
00764             return JS_FALSE;
00765         if ((!js_strtod(cx, bp, &ep, dp) ||
00766              js_SkipWhiteSpace(ep) != bp + str->length) &&
00767             (!js_strtointeger(cx, bp, &ep, 0, dp) ||
00768              js_SkipWhiteSpace(ep) != bp + str->length)) {
00769             goto badstr;
00770         }
00771     } else if (JSVAL_IS_BOOLEAN(v)) {
00772         *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;
00773     } else {
00774 badstr:
00775         *dp = *cx->runtime->jsNaN;
00776     }
00777     return JS_TRUE;
00778 }
00779 
00780 JSBool
00781 js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
00782 {
00783     jsdouble d;
00784 
00785     if (!js_ValueToNumber(cx, v, &d))
00786         return JS_FALSE;
00787     return js_DoubleToECMAInt32(cx, d, ip);
00788 }
00789 
00790 JSBool
00791 js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)
00792 {
00793     jsdouble two32 = 4294967296.0;
00794     jsdouble two31 = 2147483648.0;
00795 
00796     if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
00797         *ip = 0;
00798         return JS_TRUE;
00799     }
00800     d = fmod(d, two32);
00801     d = (d >= 0) ? floor(d) : ceil(d) + two32;
00802     if (d >= two31)
00803         *ip = (int32)(d - two32);
00804     else
00805         *ip = (int32)d;
00806     return JS_TRUE;
00807 }
00808 
00809 JSBool
00810 js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
00811 {
00812     jsdouble d;
00813 
00814     if (!js_ValueToNumber(cx, v, &d))
00815         return JS_FALSE;
00816     return js_DoubleToECMAUint32(cx, d, ip);
00817 }
00818 
00819 JSBool
00820 js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)
00821 {
00822     JSBool neg;
00823     jsdouble two32 = 4294967296.0;
00824 
00825     if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
00826         *ip = 0;
00827         return JS_TRUE;
00828     }
00829 
00830     neg = (d < 0);
00831     d = floor(neg ? -d : d);
00832     d = neg ? -d : d;
00833 
00834     d = fmod(d, two32);
00835 
00836     d = (d >= 0) ? d : d + two32;
00837     *ip = (uint32)d;
00838     return JS_TRUE;
00839 }
00840 
00841 JSBool
00842 js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
00843 {
00844     jsdouble d;
00845     JSString *str;
00846 
00847     if (JSVAL_IS_INT(v)) {
00848         *ip = JSVAL_TO_INT(v);
00849         return JS_TRUE;
00850     }
00851     if (!js_ValueToNumber(cx, v, &d))
00852         return JS_FALSE;
00853     if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
00854         str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
00855         if (str) {
00856             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00857                                  JSMSG_CANT_CONVERT, JS_GetStringBytes(str));
00858 
00859         }
00860         return JS_FALSE;
00861     }
00862     *ip = (int32)floor(d + 0.5);     /* Round to nearest */
00863     return JS_TRUE;
00864 }
00865 
00866 JSBool
00867 js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
00868 {
00869     jsdouble d;
00870     jsuint i, m;
00871     JSBool neg;
00872 
00873     if (!js_ValueToNumber(cx, v, &d))
00874         return JS_FALSE;
00875     if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
00876         *ip = 0;
00877         return JS_TRUE;
00878     }
00879     i = (jsuint)d;
00880     if ((jsdouble)i == d) {
00881         *ip = (uint16)i;
00882         return JS_TRUE;
00883     }
00884     neg = (d < 0);
00885     d = floor(neg ? -d : d);
00886     d = neg ? -d : d;
00887     m = JS_BIT(16);
00888     d = fmod(d, (double)m);
00889     if (d < 0)
00890         d += m;
00891     *ip = (uint16) d;
00892     return JS_TRUE;
00893 }
00894 
00895 jsdouble
00896 js_DoubleToInteger(jsdouble d)
00897 {
00898     JSBool neg;
00899 
00900     if (d == 0)
00901         return d;
00902     if (!JSDOUBLE_IS_FINITE(d)) {
00903         if (JSDOUBLE_IS_NaN(d))
00904             return 0;
00905         return d;
00906     }
00907     neg = (d < 0);
00908     d = floor(neg ? -d : d);
00909     return neg ? -d : d;
00910 }
00911 
00912 
00913 JSBool
00914 js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
00915 {
00916     char cbuf[32];
00917     size_t i;
00918     char *cstr, *istr, *estr;
00919     JSBool negative;
00920     jsdouble d;
00921     const jschar *s1 = js_SkipWhiteSpace(s);
00922     size_t length = js_strlen(s1);
00923 
00924     /* Use cbuf to avoid malloc */
00925     if (length >= sizeof cbuf) {
00926         cstr = (char *) JS_malloc(cx, length + 1);
00927         if (!cstr)
00928            return JS_FALSE;
00929     } else {
00930         cstr = cbuf;
00931     }
00932 
00933     for (i = 0; i <= length; i++) {
00934         if (s1[i] >> 8) {
00935             cstr[i] = 0;
00936             break;
00937         }
00938         cstr[i] = (char)s1[i];
00939     }
00940 
00941     istr = cstr;
00942     if ((negative = (*istr == '-')) != 0 || *istr == '+')
00943         istr++;
00944     if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
00945         d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);
00946         estr = istr + 8;
00947     } else {
00948         int err;
00949         d = JS_strtod(cstr, &estr, &err);
00950         if (err == JS_DTOA_ENOMEM) {
00951             JS_ReportOutOfMemory(cx);
00952             if (cstr != cbuf)
00953                 JS_free(cx, cstr);
00954             return JS_FALSE;
00955         }
00956         if (err == JS_DTOA_ERANGE) {
00957             if (d == HUGE_VAL)
00958                 d = *cx->runtime->jsPositiveInfinity;
00959             else if (d == -HUGE_VAL)
00960                 d = *cx->runtime->jsNegativeInfinity;
00961         }
00962 #ifdef HPUX
00963         if (d == 0.0 && negative) {
00964             /*
00965              * "-0", "-1e-2000" come out as positive zero
00966              * here on HPUX. Force a negative zero instead.
00967              */
00968             JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT;
00969             JSDOUBLE_LO32(d) = 0;
00970         }
00971 #endif
00972     }
00973 
00974     i = estr - cstr;
00975     if (cstr != cbuf)
00976         JS_free(cx, cstr);
00977     *ep = i ? s1 + i : s;
00978     *dp = d;
00979     return JS_TRUE;
00980 }
00981 
00982 struct BinaryDigitReader
00983 {
00984     uintN base;                 /* Base of number; must be a power of 2 */
00985     uintN digit;                /* Current digit value in radix given by base */
00986     uintN digitMask;            /* Mask to extract the next bit from digit */
00987     const jschar *digits;       /* Pointer to the remaining digits */
00988     const jschar *end;          /* Pointer to first non-digit */
00989 };
00990 
00991 /* Return the next binary digit from the number or -1 if done */
00992 static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
00993 {
00994     intN bit;
00995 
00996     if (bdr->digitMask == 0) {
00997         uintN c;
00998 
00999         if (bdr->digits == bdr->end)
01000             return -1;
01001 
01002         c = *bdr->digits++;
01003         if ('0' <= c && c <= '9')
01004             bdr->digit = c - '0';
01005         else if ('a' <= c && c <= 'z')
01006             bdr->digit = c - 'a' + 10;
01007         else bdr->digit = c - 'A' + 10;
01008         bdr->digitMask = bdr->base >> 1;
01009     }
01010     bit = (bdr->digit & bdr->digitMask) != 0;
01011     bdr->digitMask >>= 1;
01012     return bit;
01013 }
01014 
01015 JSBool
01016 js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
01017 {
01018     JSBool negative;
01019     jsdouble value;
01020     const jschar *start;
01021     const jschar *s1 = js_SkipWhiteSpace(s);
01022 
01023     if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
01024         s1++;
01025 
01026     if (base == 0) {
01027         /* No base supplied, or some base that evaluated to 0. */
01028         if (*s1 == '0') {
01029             /* It's either hex or octal; only increment char if str isn't '0' */
01030             if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
01031                 s1 += 2;
01032                 base = 16;
01033             } else {    /* Octal */
01034                 base = 8;
01035             }
01036         } else {
01037             base = 10; /* Default to decimal. */
01038         }
01039     } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) {
01040         /* If base is 16, ignore hex prefix. */
01041         s1 += 2;
01042     }
01043 
01044     /*
01045      * Done with the preliminaries; find some prefix of the string that's
01046      * a number in the given base.
01047      */
01048     start = s1; /* Mark - if string is empty, we return NaN. */
01049     value = 0.0;
01050     for (;;) {
01051         uintN digit;
01052         jschar c = *s1;
01053         if ('0' <= c && c <= '9')
01054             digit = c - '0';
01055         else if ('a' <= c && c <= 'z')
01056             digit = c - 'a' + 10;
01057         else if ('A' <= c && c <= 'Z')
01058             digit = c - 'A' + 10;
01059         else
01060             break;
01061         if (digit >= (uintN)base)
01062             break;
01063         value = value * base + digit;
01064         s1++;
01065     }
01066 
01067     if (value >= 9007199254740992.0) {
01068         if (base == 10) {
01069             /*
01070              * If we're accumulating a decimal number and the number is >=
01071              * 2^53, then the result from the repeated multiply-add above may
01072              * be inaccurate.  Call JS_strtod to get the correct answer.
01073              */
01074             size_t i;
01075             size_t length = s1 - start;
01076             char *cstr = (char *) JS_malloc(cx, length + 1);
01077             char *estr;
01078             int err=0;
01079 
01080             if (!cstr)
01081                 return JS_FALSE;
01082             for (i = 0; i != length; i++)
01083                 cstr[i] = (char)start[i];
01084             cstr[length] = 0;
01085 
01086             value = JS_strtod(cstr, &estr, &err);
01087             if (err == JS_DTOA_ENOMEM) {
01088                 JS_ReportOutOfMemory(cx);
01089                 JS_free(cx, cstr);
01090                 return JS_FALSE;
01091             }
01092             if (err == JS_DTOA_ERANGE && value == HUGE_VAL)
01093                 value = *cx->runtime->jsPositiveInfinity;
01094             JS_free(cx, cstr);
01095         } else if ((base & (base - 1)) == 0) {
01096             /*
01097              * The number may also be inaccurate for power-of-two bases.  This
01098              * happens if the addition in value * base + digit causes a round-
01099              * down to an even least significant mantissa bit when the first
01100              * dropped bit is a one.  If any of the following digits in the
01101              * number (which haven't been added in yet) are nonzero, then the
01102              * correct action would have been to round up instead of down.  An
01103              * example occurs when reading the number 0x1000000000000081, which
01104              * rounds to 0x1000000000000000 instead of 0x1000000000000100.
01105              */
01106             struct BinaryDigitReader bdr;
01107             intN bit, bit2;
01108             intN j;
01109 
01110             bdr.base = base;
01111             bdr.digitMask = 0;
01112             bdr.digits = start;
01113             bdr.end = s1;
01114             value = 0.0;
01115 
01116             /* Skip leading zeros. */
01117             do {
01118                 bit = GetNextBinaryDigit(&bdr);
01119             } while (bit == 0);
01120 
01121             if (bit == 1) {
01122                 /* Gather the 53 significant bits (including the leading 1) */
01123                 value = 1.0;
01124                 for (j = 52; j; j--) {
01125                     bit = GetNextBinaryDigit(&bdr);
01126                     if (bit < 0)
01127                         goto done;
01128                     value = value*2 + bit;
01129                 }
01130                 /* bit2 is the 54th bit (the first dropped from the mantissa) */
01131                 bit2 = GetNextBinaryDigit(&bdr);
01132                 if (bit2 >= 0) {
01133                     jsdouble factor = 2.0;
01134                     intN sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */
01135                     intN bit3;
01136 
01137                     while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) {
01138                         sticky |= bit3;
01139                         factor *= 2;
01140                     }
01141                     value += bit2 & (bit | sticky);
01142                     value *= factor;
01143                 }
01144               done:;
01145             }
01146         }
01147     }
01148     /* We don't worry about inaccurate numbers for any other base. */
01149 
01150     if (s1 == start) {
01151         *dp = 0.0;
01152         *ep = s;
01153     } else {
01154         *dp = negative ? -value : value;
01155         *ep = s1;
01156     }
01157     return JS_TRUE;
01158 }