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 jsint radix;
118 JSString *str;
119 jsdouble d;
120 const jschar *bp, *ep;
122 if (argc > 1) {
123 if (!js_ValueToECMAInt32(cx, argv[1], &radix))
124 return JS_FALSE;
125 } else {
126 radix = 0;
127 }
128 if (radix != 0 && (radix < 2 || radix > 36)) {
129 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
130 return JS_TRUE;
131 }
133 str = js_ValueToString(cx, argv[0]);
134 if (!str)
135 return JS_FALSE;
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 JSClass js_NumberClass = {
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, &js_NumberClass, 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))", js_NumberClass.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, &js_NumberClass, 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, &js_NumberClass, 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, &js_NumberClass, 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 WINCE && \
509 !defined __MWERKS__ && \
510 (defined _M_IX86 || \
511 (defined __GNUC__ && !defined __MINGW32__))
513 /*
514 * Set the exception mask to mask all exceptions and set the FPU precision
515 * to 53 bit mantissa.
516 * On Alpha platform this is handled via Compiler option.
517 */
518 #define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC)
520 #else
522 #define FIX_FPU() ((void)0)
524 #endif
526 JSBool
527 js_InitRuntimeNumberState(JSContext *cx)
528 {
529 JSRuntime *rt;
530 jsdpun u;
531 struct lconv *locale;
533 rt = cx->runtime;
534 JS_ASSERT(!rt->jsNaN);
536 FIX_FPU();
538 u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;
539 u.s.lo = 0xffffffff;
540 number_constants[NC_NaN].dval = NaN = u.d;
541 rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK);
542 if (!rt->jsNaN)
543 return JS_FALSE;
545 u.s.hi = JSDOUBLE_HI32_EXPMASK;
546 u.s.lo = 0x00000000;
547 number_constants[NC_POSITIVE_INFINITY].dval = u.d;
548 rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK);
549 if (!rt->jsPositiveInfinity)
550 return JS_FALSE;
552 u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
553 u.s.lo = 0x00000000;
554 number_constants[NC_NEGATIVE_INFINITY].dval = u.d;
555 rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK);
556 if (!rt->jsNegativeInfinity)
557 return JS_FALSE;
559 u.s.hi = 0;
560 u.s.lo = 1;
561 number_constants[NC_MIN_VALUE].dval = u.d;
563 locale = localeconv();
564 rt->thousandsSeparator =
565 JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'");
566 rt->decimalSeparator =
567 JS_strdup(cx, locale->decimal_point ? locale->decimal_point : ".");
568 rt->numGrouping =
569 JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0");
571 return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;
572 }
574 void
575 js_FinishRuntimeNumberState(JSContext *cx)
576 {
577 JSRuntime *rt = cx->runtime;
579 js_UnlockGCThingRT(rt, rt->jsNaN);
580 js_UnlockGCThingRT(rt, rt->jsNegativeInfinity);
581 js_UnlockGCThingRT(rt, rt->jsPositiveInfinity);
583 rt->jsNaN = NULL;
584 rt->jsNegativeInfinity = NULL;
585 rt->jsPositiveInfinity = NULL;
587 JS_free(cx, (void *)rt->thousandsSeparator);
588 JS_free(cx, (void *)rt->decimalSeparator);
589 JS_free(cx, (void *)rt->numGrouping);
590 rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL;
591 }
593 JSObject *
594 js_InitNumberClass(JSContext *cx, JSObject *obj)
595 {
596 JSObject *proto, *ctor;
597 JSRuntime *rt;
599 /* XXX must do at least once per new thread, so do it per JSContext... */
600 FIX_FPU();
602 if (!JS_DefineFunctions(cx, obj, number_functions))
603 return NULL;
605 proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1,
606 NULL, number_methods, NULL, NULL);
607 if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
608 return NULL;
609 OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);
610 if (!JS_DefineConstDoubles(cx, ctor, number_constants))
611 return NULL;
613 /* ECMA 15.1.1.1 */
614 rt = cx->runtime;
615 if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),
616 NULL, NULL, JSPROP_PERMANENT)) {
617 return NULL;
618 }
620 /* ECMA 15.1.1.2 */
621 if (!JS_DefineProperty(cx, obj, js_Infinity_str,
622 DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),
623 NULL, NULL, JSPROP_PERMANENT)) {
624 return NULL;
625 }
626 return proto;
627 }
629 jsdouble *
630 js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag)
631 {
632 jsdouble *dp;
634 dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble));
635 if (!dp)
636 return NULL;
637 *dp = d;
638 return dp;
639 }
641 void
642 js_FinalizeDouble(JSContext *cx, jsdouble *dp)
643 {
644 *dp = NaN;
645 }
647 JSBool
648 js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
649 {
650 jsdouble *dp;
652 dp = js_NewDouble(cx, d, 0);
653 if (!dp)
654 return JS_FALSE;
655 *rval = DOUBLE_TO_JSVAL(dp);
656 return JS_TRUE;
657 }
659 JSBool
660 js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
661 {
662 jsint i;
664 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
665 *rval = INT_TO_JSVAL(i);
666 } else {
667 if (!js_NewDoubleValue(cx, d, rval))
668 return JS_FALSE;
669 }
670 return JS_TRUE;
671 }
673 JSObject *
674 js_NumberToObject(JSContext *cx, jsdouble d)
675 {
676 JSObject *obj;
677 jsval v;
679 obj = js_NewObject(cx, &js_NumberClass, NULL, NULL);
680 if (!obj)
681 return NULL;
682 if (!js_NewNumberValue(cx, d, &v)) {
683 cx->newborn[GCX_OBJECT] = NULL;
684 return NULL;
685 }
686 OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);
687 return obj;
688 }
690 JSString *
691 js_NumberToString(JSContext *cx, jsdouble d)
692 {
693 jsint i;
694 char buf[DTOSTR_STANDARD_BUFFER_SIZE];
695 char *numStr;
697 if (JSDOUBLE_IS_INT(d, i))
698 numStr = IntToString(i, buf, sizeof buf);
699 else {
700 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
701 if (!numStr) {
702 JS_ReportOutOfMemory(cx);
703 return NULL;
704 }
705 }
706 return JS_NewStringCopyZ(cx, numStr);
707 }
709 JSBool
710 js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
711 {
712 JSObject *obj;
713 JSString *str;
714 const jschar *bp, *ep;
716 if (JSVAL_IS_OBJECT(v)) {
717 obj = JSVAL_TO_OBJECT(v);
718 if (!obj) {
719 *dp = 0;
720 return JS_TRUE;
721 }
722 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v))
723 return JS_FALSE;
724 }
725 if (JSVAL_IS_INT(v)) {
726 *dp = (jsdouble)JSVAL_TO_INT(v);
727 } else if (JSVAL_IS_DOUBLE(v)) {
728 *dp = *JSVAL_TO_DOUBLE(v);
729 } else if (JSVAL_IS_STRING(v)) {
730 str = JSVAL_TO_STRING(v);
731 /*
732 * Note that ECMA doesn't treat a string beginning with a '0' as an
733 * octal number here. This works because all such numbers will be
734 * interpreted as decimal by js_strtod and will never get passed to
735 * js_strtointeger (which would interpret them as octal).
736 */
737 /* XXXbe js_strtod shouldn't require NUL termination */
738 bp = js_UndependString(cx, str);
739 if (!bp)
740 return JS_FALSE;
741 if ((!js_strtod(cx, bp, &ep, dp) ||
742 js_SkipWhiteSpace(ep) != bp + str->length) &&
743 (!js_strtointeger(cx, bp, &ep, 0, dp) ||
744 js_SkipWhiteSpace(ep) != bp + str->length)) {
745 goto badstr;
746 }
747 } else if (JSVAL_IS_BOOLEAN(v)) {
748 *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;
749 } else {
750 #if JS_BUG_FALLIBLE_TONUM
751 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
752 badstr:
753 if (str) {
754 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN,
755 JS_GetStringBytes(str));
757 }
758 return JS_FALSE;
759 #else
760 badstr:
761 *dp = *cx->runtime->jsNaN;
762 #endif
763 }
764 return JS_TRUE;
765 }
767 JSBool
768 js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
769 {
770 jsdouble d;
772 if (!js_ValueToNumber(cx, v, &d))
773 return JS_FALSE;
774 return js_DoubleToECMAInt32(cx, d, ip);
775 }
777 JSBool
778 js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)
779 {
780 jsdouble two32 = 4294967296.0;
781 jsdouble two31 = 2147483648.0;
783 if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
784 *ip = 0;
785 return JS_TRUE;
786 }
787 d = fmod(d, two32);
788 d = (d >= 0) ? floor(d) : ceil(d) + two32;
789 if (d >= two31)
790 *ip = (int32)(d - two32);
791 else
792 *ip = (int32)d;
793 return JS_TRUE;
794 }
796 JSBool
797 js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
798 {
799 jsdouble d;
801 if (!js_ValueToNumber(cx, v, &d))
802 return JS_FALSE;
803 return js_DoubleToECMAUint32(cx, d, ip);
804 }
806 JSBool
807 js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)
808 {
809 JSBool neg;
810 jsdouble two32 = 4294967296.0;
812 if (!JSDOUBLE_IS_FINITE(d) || d == 0) {
813 *ip = 0;
814 return JS_TRUE;
815 }
817 neg = (d < 0);
818 d = floor(neg ? -d : d);
819 d = neg ? -d : d;
821 d = fmod(d, two32);
823 d = (d >= 0) ? d : d + two32;
824 *ip = (uint32)d;
825 return JS_TRUE;
826 }
828 JSBool
829 js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
830 {
831 jsdouble d;
832 JSString *str;
834 if (JSVAL_IS_INT(v)) {
835 *ip = JSVAL_TO_INT(v);
836 return JS_TRUE;
837 }
838 if (!js_ValueToNumber(cx, v, &d))
839 return JS_FALSE;
840 if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
841 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
842 if (str) {
843 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
844 JSMSG_CANT_CONVERT, JS_GetStringBytes(str));
846 }
847 return JS_FALSE;
848 }
849 *ip = (int32)floor(d + 0.5); /* Round to nearest */
850 return JS_TRUE;
851 }
853 JSBool
854 js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
855 {
856 jsdouble d;
857 jsuint i, m;
858 JSBool neg;
860 if (!js_ValueToNumber(cx, v, &d))
861 return JS_FALSE;
862 if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
863 *ip = 0;
864 return JS_TRUE;
865 }
866 i = (jsuint)d;
867 if ((jsdouble)i == d) {
868 *ip = (uint16)i;
869 return JS_TRUE;
870 }
871 neg = (d < 0);
872 d = floor(neg ? -d : d);
873 d = neg ? -d : d;
874 m = JS_BIT(16);
875 d = fmod(d, (double)m);
876 if (d < 0)
877 d += m;
878 *ip = (uint16) d;
879 return JS_TRUE;
880 }
882 jsdouble
883 js_DoubleToInteger(jsdouble d)
884 {
885 JSBool neg;
887 if (d == 0)
888 return d;
889 if (!JSDOUBLE_IS_FINITE(d)) {
890 if (JSDOUBLE_IS_NaN(d))
891 return 0;
892 return d;
893 }
894 neg = (d < 0);
895 d = floor(neg ? -d : d);
896 return neg ? -d : d;
897 }
900 JSBool
901 js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)
902 {
903 char cbuf[32];
904 size_t i;
905 char *cstr, *istr, *estr;
906 JSBool negative;
907 jsdouble d;
908 const jschar *s1 = js_SkipWhiteSpace(s);
909 size_t length = js_strlen(s1);
911 /* Use cbuf to avoid malloc */
912 if (length >= sizeof cbuf) {
913 cstr = (char *) JS_malloc(cx, length + 1);
914 if (!cstr)
915 return JS_FALSE;
916 } else {
917 cstr = cbuf;
918 }
920 for (i = 0; i <= length; i++) {
921 if (s1[i] >> 8) {
922 cstr[i] = 0;
923 break;
924 }
925 cstr[i] = (char)s1[i];
926 }
928 istr = cstr;
929 if ((negative = (*istr == '-')) != 0 || *istr == '+')
930 istr++;
931 if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
932 d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);
933 estr = istr + 8;
934 } else {
935 int err;
936 d = JS_strtod(cstr, &estr, &err);
937 if (err == JS_DTOA_ENOMEM) {
938 JS_ReportOutOfMemory(cx);
939 if (cstr != cbuf)
940 JS_free(cx, cstr);
941 return JS_FALSE;
942 }
943 if (err == JS_DTOA_ERANGE) {
944 if (d == HUGE_VAL)
945 d = *cx->runtime->jsPositiveInfinity;
946 else if (d == -HUGE_VAL)
947 d = *cx->runtime->jsNegativeInfinity;
948 }
949 #ifdef HPUX
950 if (d == 0.0 && negative) {
951 /*
952 * "-0", "-1e-2000" come out as positive zero
953 * here on HPUX. Force a negative zero instead.
954 */
955 JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT;
956 JSDOUBLE_LO32(d) = 0;
957 }
958 #endif
959 }
961 i = estr - cstr;
962 if (cstr != cbuf)
963 JS_free(cx, cstr);
964 *ep = i ? s1 + i : s;
965 *dp = d;
966 return JS_TRUE;
967 }
969 struct BinaryDigitReader
970 {
971 uintN base; /* Base of number; must be a power of 2 */
972 uintN digit; /* Current digit value in radix given by base */
973 uintN digitMask; /* Mask to extract the next bit from digit */
974 const jschar *digits; /* Pointer to the remaining digits */
975 const jschar *end; /* Pointer to first non-digit */
976 };
978 /* Return the next binary digit from the number or -1 if done */
979 static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)
980 {
981 intN bit;
983 if (bdr->digitMask == 0) {
984 uintN c;
986 if (bdr->digits == bdr->end)
987 return -1;
989 c = *bdr->digits++;
990 if ('0' <= c && c <= '9')
991 bdr->digit = c - '0';
992 else if ('a' <= c && c <= 'z')
993 bdr->digit = c - 'a' + 10;
994 else bdr->digit = c - 'A' + 10;
995 bdr->digitMask = bdr->base >> 1;
996 }
997 bit = (bdr->digit & bdr->digitMask) != 0;
998 bdr->digitMask >>= 1;
999 return bit;
1000 }
1002 JSBool
1003 js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)
1004 {
1005 JSBool negative;
1006 jsdouble value;
1007 const jschar *start;
1008 const jschar *s1 = js_SkipWhiteSpace(s);
1010 if ((negative = (*s1 == '-')) != 0 || *s1 == '+')
1011 s1++;
1013 if (base == 0) {
1014 /* No base supplied, or some base that evaluated to 0. */
1015 if (*s1 == '0') {
1016 /* It's either hex or octal; only increment char if str isn't '0' */
1017 if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */
1018 s1 += 2;
1019 base = 16;
1020 } else { /* Octal */
1021 base = 8;
1022 }
1023 } else {
1024 base = 10; /* Default to decimal. */
1025 }
1026 } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) {
1027 /* If base is 16, ignore hex prefix. */
1028 s1 += 2;
1029 }
1031 /*
1032 * Done with the preliminaries; find some prefix of the string that's
1033 * a number in the given base.
1034 */
1035 start = s1; /* Mark - if string is empty, we return NaN. */
1036 value = 0.0;
1037 for (;;) {
1038 uintN digit;
1039 jschar c = *s1;
1040 if ('0' <= c && c <= '9')
1041 digit = c - '0';
1042 else if ('a' <= c && c <= 'z')
1043 digit = c - 'a' + 10;
1044 else if ('A' <= c && c <= 'Z')
1045 digit = c - 'A' + 10;
1046 else
1047 break;
1048 if (digit >= (uintN)base)
1049 break;
1050 value = value * base + digit;
1051 s1++;
1052 }
1054 if (value >= 9007199254740992.0) {
1055 if (base == 10) {
1056 /*
1057 * If we're accumulating a decimal number and the number is >=
1058 * 2^53, then the result from the repeated multiply-add above may
1059 * be inaccurate. Call JS_strtod to get the correct answer.
1060 */
1061 size_t i;
1062 size_t length = s1 - start;
1063 char *cstr = (char *) JS_malloc(cx, length + 1);
1064 char *estr;
1065 int err=0;
1067 if (!cstr)
1068 return JS_FALSE;
1069 for (i = 0; i != length; i++)
1070 cstr[i] = (char)start[i];
1071 cstr[length] = 0;
1073 value = JS_strtod(cstr, &estr, &err);
1074 if (err == JS_DTOA_ENOMEM) {
1075 JS_ReportOutOfMemory(cx);
1076 JS_free(cx, cstr);
1077 return JS_FALSE;
1078 }
1079 if (err == JS_DTOA_ERANGE && value == HUGE_VAL)
1080 value = *cx->runtime->jsPositiveInfinity;
1081 JS_free(cx, cstr);
1082 } else if ((base & (base - 1)) == 0) {
1083 /*
1084 * The number may also be inaccurate for power-of-two bases. This
1085 * happens if the addition in value * base + digit causes a round-
1086 * down to an even least significant mantissa bit when the first
1087 * dropped bit is a one. If any of the following digits in the
1088 * number (which haven't been added in yet) are nonzero, then the
1089 * correct action would have been to round up instead of down. An
1090 * example occurs when reading the number 0x1000000000000081, which
1091 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
1092 */
1093 struct BinaryDigitReader bdr;
1094 intN bit, bit2;
1095 intN j;
1097 bdr.base = base;
1098 bdr.digitMask = 0;
1099 bdr.digits = start;
1100 bdr.end = s1;
1101 value = 0.0;
1103 /* Skip leading zeros. */
1104 do {
1105 bit = GetNextBinaryDigit(&bdr);
1106 } while (bit == 0);
1108 if (bit == 1) {
1109 /* Gather the 53 significant bits (including the leading 1) */
1110 value = 1.0;
1111 for (j = 52; j; j--) {
1112 bit = GetNextBinaryDigit(&bdr);
1113 if (bit < 0)
1114 goto done;
1115 value = value*2 + bit;
1116 }
1117 /* bit2 is the 54th bit (the first dropped from the mantissa) */
1118 bit2 = GetNextBinaryDigit(&bdr);
1119 if (bit2 >= 0) {
1120 jsdouble factor = 2.0;
1121 intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
1122 intN bit3;
1124 while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) {
1125 sticky |= bit3;
1126 factor *= 2;
1127 }
1128 value += bit2 & (bit | sticky);
1129 value *= factor;
1130 }
1131 done:;
1132 }
1133 }
1134 }
1135 /* We don't worry about inaccurate numbers for any other base. */
1137 if (s1 == start) {
1138 *dp = 0.0;
1139 *ep = s;
1140 } else {
1141 *dp = negative ? -value : value;
1142 *ep = s1;
1143 }
1144 return JS_TRUE;
1145 }