/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsnum.cpp
C++ | 1338 lines | 1058 code | 152 blank | 128 comment | 307 complexity | b37ca3d44caff98cee1c0c5008a15765 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * ***** BEGIN LICENSE BLOCK ***** 4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 * 6 * The contents of this file are subject to the Mozilla Public License Version 7 * 1.1 (the "License"); you may not use this file except in compliance with 8 * the License. You may obtain a copy of the License at 9 * http://www.mozilla.org/MPL/ 10 * 11 * Software distributed under the License is distributed on an "AS IS" basis, 12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 13 * for the specific language governing rights and limitations under the 14 * License. 15 * 16 * The Original Code is Mozilla Communicator client code, released 17 * March 31, 1998. 18 * 19 * The Initial Developer of the Original Code is 20 * Netscape Communications Corporation. 21 * Portions created by the Initial Developer are Copyright (C) 1998 22 * the Initial Developer. All Rights Reserved. 23 * 24 * Contributor(s): 25 * IBM Corp. 26 * 27 * Alternatively, the contents of this file may be used under the terms of 28 * either of the GNU General Public License Version 2 or later (the "GPL"), 29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 30 * in which case the provisions of the GPL or the LGPL are applicable instead 31 * of those above. If you wish to allow use of your version of this file only 32 * under the terms of either the GPL or the LGPL, and not to allow others to 33 * use your version of this file under the terms of the MPL, indicate your 34 * decision by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL or the LGPL. If you do not delete 36 * the provisions above, a recipient may use your version of this file under 37 * the terms of any one of the MPL, the GPL or the LGPL. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41/* 42 * JS number type and wrapper class. 43 */ 44#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 "jsbuiltins.h" 58#include "jscntxt.h" 59#include "jsversion.h" 60#include "jsdtoa.h" 61#include "jsgc.h" 62#include "jsinterp.h" 63#include "jsnum.h" 64#include "jsobj.h" 65#include "jsopcode.h" 66#include "jsprf.h" 67#include "jsscope.h" 68#include "jsstr.h" 69 70static JSBool 71num_isNaN(JSContext *cx, uintN argc, jsval *vp) 72{ 73 jsdouble x; 74 75 if (argc == 0) { 76 *vp = JSVAL_TRUE; 77 return JS_TRUE; 78 } 79 x = js_ValueToNumber(cx, &vp[2]); 80 if (JSVAL_IS_NULL(vp[2])) 81 return JS_FALSE; 82 *vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); 83 return JS_TRUE; 84} 85 86static JSBool 87num_isFinite(JSContext *cx, uintN argc, jsval *vp) 88{ 89 jsdouble x; 90 91 if (argc == 0) { 92 *vp = JSVAL_FALSE; 93 return JS_TRUE; 94 } 95 x = js_ValueToNumber(cx, &vp[2]); 96 if (JSVAL_IS_NULL(vp[2])) 97 return JS_FALSE; 98 *vp = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); 99 return JS_TRUE; 100} 101 102static JSBool 103num_parseFloat(JSContext *cx, uintN argc, jsval *vp) 104{ 105 JSString *str; 106 jsdouble d; 107 const jschar *bp, *end, *ep; 108 109 if (argc == 0) { 110 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 111 return JS_TRUE; 112 } 113 str = js_ValueToString(cx, vp[2]); 114 if (!str) 115 return JS_FALSE; 116 JSSTRING_CHARS_AND_END(str, bp, end); 117 if (!js_strtod(cx, bp, end, &ep, &d)) 118 return JS_FALSE; 119 if (ep == bp) { 120 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 121 return JS_TRUE; 122 } 123 return js_NewNumberInRootedValue(cx, d, vp); 124} 125 126#ifdef JS_TRACER 127static jsdouble FASTCALL 128ParseFloat(JSContext* cx, JSString* str) 129{ 130 const jschar* bp; 131 const jschar* end; 132 const jschar* ep; 133 jsdouble d; 134 135 JSSTRING_CHARS_AND_END(str, bp, end); 136 if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp) 137 return js_NaN; 138 return d; 139} 140#endif 141 142/* See ECMA 15.1.2.2. */ 143static JSBool 144num_parseInt(JSContext *cx, uintN argc, jsval *vp) 145{ 146 jsint radix; 147 JSString *str; 148 jsdouble d; 149 const jschar *bp, *end, *ep; 150 151 if (argc == 0) { 152 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 153 return JS_TRUE; 154 } 155 if (argc > 1) { 156 radix = js_ValueToECMAInt32(cx, &vp[3]); 157 if (JSVAL_IS_NULL(vp[3])) 158 return JS_FALSE; 159 } else { 160 radix = 0; 161 } 162 if (radix != 0 && (radix < 2 || radix > 36)) { 163 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 164 return JS_TRUE; 165 } 166 167 if (JSVAL_IS_INT(vp[2]) && (radix == 0 || radix == 10)) { 168 *vp = vp[2]; 169 return JS_TRUE; 170 } 171 172 str = js_ValueToString(cx, vp[2]); 173 if (!str) 174 return JS_FALSE; 175 JSSTRING_CHARS_AND_END(str, bp, end); 176 if (!js_strtointeger(cx, bp, end, &ep, radix, &d)) 177 return JS_FALSE; 178 if (ep == bp) { 179 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 180 return JS_TRUE; 181 } 182 return js_NewNumberInRootedValue(cx, d, vp); 183} 184 185#ifdef JS_TRACER 186static jsdouble FASTCALL 187ParseInt(JSContext* cx, JSString* str) 188{ 189 const jschar* bp; 190 const jschar* end; 191 const jschar* ep; 192 jsdouble d; 193 194 JSSTRING_CHARS_AND_END(str, bp, end); 195 if (!js_strtointeger(cx, bp, end, &ep, 0, &d) || ep == bp) 196 return js_NaN; 197 return d; 198} 199 200static jsdouble FASTCALL 201ParseIntDouble(jsdouble d) 202{ 203 if (!JSDOUBLE_IS_FINITE(d)) 204 return js_NaN; 205 return floor(d); 206} 207#endif 208 209const char js_Infinity_str[] = "Infinity"; 210const char js_NaN_str[] = "NaN"; 211const char js_isNaN_str[] = "isNaN"; 212const char js_isFinite_str[] = "isFinite"; 213const char js_parseFloat_str[] = "parseFloat"; 214const char js_parseInt_str[] = "parseInt"; 215 216#ifdef JS_TRACER 217 218JS_DEFINE_TRCINFO_2(num_parseInt, 219 (2, (static, DOUBLE, ParseInt, CONTEXT, STRING, 1, 1)), 220 (1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, 1))) 221 222JS_DEFINE_TRCINFO_1(num_parseFloat, 223 (2, (static, DOUBLE, ParseFloat, CONTEXT, STRING, 1, 1))) 224 225#endif /* JS_TRACER */ 226 227static JSFunctionSpec number_functions[] = { 228 JS_FN(js_isNaN_str, num_isNaN, 1,0), 229 JS_FN(js_isFinite_str, num_isFinite, 1,0), 230 JS_TN(js_parseFloat_str, num_parseFloat, 1,0, num_parseFloat_trcinfo), 231 JS_TN(js_parseInt_str, num_parseInt, 2,0, num_parseInt_trcinfo), 232 JS_FS_END 233}; 234 235JSClass js_NumberClass = { 236 js_Number_str, 237 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), 238 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, 239 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, 240 JSCLASS_NO_OPTIONAL_MEMBERS 241}; 242 243static JSBool 244Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) 245{ 246 jsval v; 247 jsdouble d; 248 249 if (argc != 0) { 250 d = js_ValueToNumber(cx, &argv[0]); 251 v = argv[0]; 252 if (JSVAL_IS_NULL(v)) 253 return JS_FALSE; 254 if (v != JSVAL_TRUE) { 255 JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)); 256 } else { 257 if (!js_NewNumberInRootedValue(cx, d, &argv[0])) 258 return JS_FALSE; 259 v = argv[0]; 260 } 261 } else { 262 v = JSVAL_ZERO; 263 } 264 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { 265 *rval = v; 266 return JS_TRUE; 267 } 268 STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, v); 269 return JS_TRUE; 270} 271 272#if JS_HAS_TOSOURCE 273static JSBool 274num_toSource(JSContext *cx, uintN argc, jsval *vp) 275{ 276 jsval v; 277 jsdouble d; 278 char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; 279 char buf[64]; 280 JSString *str; 281 282 if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) 283 return JS_FALSE; 284 JS_ASSERT(JSVAL_IS_NUMBER(v)); 285 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); 286 numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); 287 if (!numStr) { 288 JS_ReportOutOfMemory(cx); 289 return JS_FALSE; 290 } 291 JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); 292 str = JS_NewStringCopyZ(cx, buf); 293 if (!str) 294 return JS_FALSE; 295 *vp = STRING_TO_JSVAL(str); 296 return JS_TRUE; 297} 298#endif 299 300/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ 301char * 302js_IntToCString(jsint i, jsint base, char *buf, size_t bufSize) 303{ 304 char *cp; 305 jsuint u; 306 307 u = (i < 0) ? -i : i; 308 309 cp = buf + bufSize; /* one past last buffer cell */ 310 *--cp = '\0'; /* null terminate the string to be */ 311 312 /* 313 * Build the string from behind. We use multiply and subtraction 314 * instead of modulus because that's much faster. 315 */ 316 switch (base) { 317 case 10: 318 do { 319 jsuint newu = u / 10; 320 *--cp = (char)(u - newu * 10) + '0'; 321 u = newu; 322 } while (u != 0); 323 break; 324 case 16: 325 do { 326 jsuint newu = u / 16; 327 *--cp = "0123456789abcdef"[u - newu * 16]; 328 u = newu; 329 } while (u != 0); 330 break; 331 default: 332 JS_ASSERT(base >= 2 && base <= 36); 333 do { 334 jsuint newu = u / base; 335 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base]; 336 u = newu; 337 } while (u != 0); 338 break; 339 } 340 if (i < 0) 341 *--cp = '-'; 342 343 JS_ASSERT(cp >= buf); 344 return cp; 345} 346 347static JSBool 348num_toString(JSContext *cx, uintN argc, jsval *vp) 349{ 350 jsval v; 351 jsdouble d; 352 jsint base; 353 JSString *str; 354 355 if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) 356 return JS_FALSE; 357 JS_ASSERT(JSVAL_IS_NUMBER(v)); 358 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); 359 base = 10; 360 if (argc != 0 && !JSVAL_IS_VOID(vp[2])) { 361 base = js_ValueToECMAInt32(cx, &vp[2]); 362 if (JSVAL_IS_NULL(vp[2])) 363 return JS_FALSE; 364 if (base < 2 || base > 36) { 365 char numBuf[12]; 366 char *numStr = js_IntToCString(base, 10, numBuf, sizeof numBuf); 367 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, 368 numStr); 369 return JS_FALSE; 370 } 371 } 372 if (base == 10) { 373 str = js_NumberToString(cx, d); 374 } else { 375 char *dStr = JS_dtobasestr(base, d); 376 if (!dStr) { 377 JS_ReportOutOfMemory(cx); 378 return JS_FALSE; 379 } 380 str = JS_NewStringCopyZ(cx, dStr); 381 free(dStr); 382 } 383 if (!str) 384 return JS_FALSE; 385 *vp = STRING_TO_JSVAL(str); 386 return JS_TRUE; 387} 388 389static JSBool 390num_toLocaleString(JSContext *cx, uintN argc, jsval *vp) 391{ 392 char thousandsLength, decimalLength; 393 const char *numGrouping, *tmpGroup; 394 JSRuntime *rt; 395 JSString *numStr, *str; 396 const char *num, *end, *tmpSrc; 397 char *buf, *tmpDest; 398 const char *nint; 399 int digits, size, remainder, nrepeat; 400 401 /* 402 * Create the string, move back to bytes to make string twiddling 403 * a bit easier and so we can insert platform charset seperators. 404 */ 405 if (!num_toString(cx, 0, vp)) 406 return JS_FALSE; 407 JS_ASSERT(JSVAL_IS_STRING(*vp)); 408 numStr = JSVAL_TO_STRING(*vp); 409 num = js_GetStringBytes(cx, numStr); 410 if (!num) 411 return JS_FALSE; 412 413 /* 414 * Find the first non-integer value, whether it be a letter as in 415 * 'Infinity', a decimal point, or an 'e' from exponential notation. 416 */ 417 nint = num; 418 if (*nint == '-') 419 nint++; 420 while (*nint >= '0' && *nint <= '9') 421 nint++; 422 digits = nint - num; 423 end = num + digits; 424 if (!digits) 425 return JS_TRUE; 426 427 rt = cx->runtime; 428 thousandsLength = strlen(rt->thousandsSeparator); 429 decimalLength = strlen(rt->decimalSeparator); 430 431 /* Figure out how long resulting string will be. */ 432 size = digits + (*nint ? strlen(nint + 1) + 1 : 0); 433 if (*nint == '.') 434 size += decimalLength; 435 436 numGrouping = tmpGroup = rt->numGrouping; 437 remainder = digits; 438 if (*num == '-') 439 remainder--; 440 441 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { 442 if (*tmpGroup >= remainder) 443 break; 444 size += thousandsLength; 445 remainder -= *tmpGroup; 446 tmpGroup++; 447 } 448 if (*tmpGroup == '\0' && *numGrouping != '\0') { 449 nrepeat = (remainder - 1) / tmpGroup[-1]; 450 size += thousandsLength * nrepeat; 451 remainder -= nrepeat * tmpGroup[-1]; 452 } else { 453 nrepeat = 0; 454 } 455 tmpGroup--; 456 457 buf = (char *)JS_malloc(cx, size + 1); 458 if (!buf) 459 return JS_FALSE; 460 461 tmpDest = buf; 462 tmpSrc = num; 463 464 while (*tmpSrc == '-' || remainder--) 465 *tmpDest++ = *tmpSrc++; 466 while (tmpSrc < end) { 467 strcpy(tmpDest, rt->thousandsSeparator); 468 tmpDest += thousandsLength; 469 memcpy(tmpDest, tmpSrc, *tmpGroup); 470 tmpDest += *tmpGroup; 471 tmpSrc += *tmpGroup; 472 if (--nrepeat < 0) 473 tmpGroup--; 474 } 475 476 if (*nint == '.') { 477 strcpy(tmpDest, rt->decimalSeparator); 478 tmpDest += decimalLength; 479 strcpy(tmpDest, nint + 1); 480 } else { 481 strcpy(tmpDest, nint); 482 } 483 484 if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) 485 return cx->localeCallbacks->localeToUnicode(cx, buf, vp); 486 487 str = JS_NewString(cx, buf, size); 488 if (!str) { 489 JS_free(cx, buf); 490 return JS_FALSE; 491 } 492 493 *vp = STRING_TO_JSVAL(str); 494 return JS_TRUE; 495} 496 497static JSBool 498num_valueOf(JSContext *cx, uintN argc, jsval *vp) 499{ 500 jsval v; 501 JSObject *obj; 502 503 v = vp[1]; 504 if (JSVAL_IS_NUMBER(v)) { 505 *vp = v; 506 return JS_TRUE; 507 } 508 obj = JS_THIS_OBJECT(cx, vp); 509 if (!JS_InstanceOf(cx, obj, &js_NumberClass, vp + 2)) 510 return JS_FALSE; 511 *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); 512 return JS_TRUE; 513} 514 515 516#define MAX_PRECISION 100 517 518static JSBool 519num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode, 520 jsint precisionMin, jsint precisionMax, jsint precisionOffset, 521 uintN argc, jsval *vp) 522{ 523 jsval v; 524 jsdouble d, precision; 525 JSString *str; 526 527 /* Use MAX_PRECISION+1 because precisionOffset can be 1. */ 528 char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)]; 529 char *numStr; 530 531 if (!js_GetPrimitiveThis(cx, vp, &js_NumberClass, &v)) 532 return JS_FALSE; 533 JS_ASSERT(JSVAL_IS_NUMBER(v)); 534 d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); 535 536 if (argc == 0) { 537 precision = 0.0; 538 oneArgMode = zeroArgMode; 539 } else { 540 precision = js_ValueToNumber(cx, &vp[2]); 541 if (JSVAL_IS_NULL(vp[2])) 542 return JS_FALSE; 543 precision = js_DoubleToInteger(precision); 544 if (precision < precisionMin || precision > precisionMax) { 545 numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); 546 if (!numStr) 547 JS_ReportOutOfMemory(cx); 548 else 549 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); 550 return JS_FALSE; 551 } 552 } 553 554 numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); 555 if (!numStr) { 556 JS_ReportOutOfMemory(cx); 557 return JS_FALSE; 558 } 559 str = JS_NewStringCopyZ(cx, numStr); 560 if (!str) 561 return JS_FALSE; 562 *vp = STRING_TO_JSVAL(str); 563 return JS_TRUE; 564} 565 566/* 567 * In the following three implementations, we allow a larger range of precision 568 * than ECMA requires; this is permitted by ECMA-262. 569 */ 570static JSBool 571num_toFixed(JSContext *cx, uintN argc, jsval *vp) 572{ 573 return num_to(cx, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0, 574 argc, vp); 575} 576 577static JSBool 578num_toExponential(JSContext *cx, uintN argc, jsval *vp) 579{ 580 return num_to(cx, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, 581 MAX_PRECISION, 1, argc, vp); 582} 583 584static JSBool 585num_toPrecision(JSContext *cx, uintN argc, jsval *vp) 586{ 587 if (argc == 0 || JSVAL_IS_VOID(vp[2])) 588 return num_toString(cx, 0, vp); 589 return num_to(cx, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0, 590 argc, vp); 591} 592 593#ifdef JS_TRACER 594 595JS_DEFINE_TRCINFO_2(num_toString, 596 (3, (static, STRING, NumberToStringWithBase, CONTEXT, THIS_DOUBLE, INT32, 1, 1)), 597 (2, (extern, STRING, js_NumberToString, CONTEXT, THIS_DOUBLE, 1, 1))) 598 599#endif /* JS_TRACER */ 600 601static JSFunctionSpec number_methods[] = { 602#if JS_HAS_TOSOURCE 603 JS_FN(js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER), 604#endif 605 JS_TN(js_toString_str, num_toString, 1,JSFUN_THISP_NUMBER, 606 num_toString_trcinfo), 607 JS_FN(js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER), 608 JS_FN(js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER), 609 JS_FN(js_toJSON_str, num_valueOf, 0,JSFUN_THISP_NUMBER), 610 JS_FN("toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER), 611 JS_FN("toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER), 612 JS_FN("toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER), 613 JS_FS_END 614}; 615 616/* NB: Keep this in synch with number_constants[]. */ 617enum nc_slot { 618 NC_NaN, 619 NC_POSITIVE_INFINITY, 620 NC_NEGATIVE_INFINITY, 621 NC_MAX_VALUE, 622 NC_MIN_VALUE, 623 NC_LIMIT 624}; 625 626/* 627 * Some to most C compilers forbid spelling these at compile time, or barf 628 * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState 629 * using union jsdpun. 630 */ 631static JSConstDoubleSpec number_constants[] = { 632 {0, js_NaN_str, 0,{0,0,0}}, 633 {0, "POSITIVE_INFINITY", 0,{0,0,0}}, 634 {0, "NEGATIVE_INFINITY", 0,{0,0,0}}, 635 {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, 636 {0, "MIN_VALUE", 0,{0,0,0}}, 637 {0,0,0,{0,0,0}} 638}; 639 640jsdouble js_NaN; 641 642#if (defined XP_WIN || defined XP_OS2) && \ 643 !defined WINCE && \ 644 !defined __MWERKS__ && \ 645 (defined _M_IX86 || \ 646 (defined __GNUC__ && !defined __MINGW32__)) 647 648/* 649 * Set the exception mask to mask all exceptions and set the FPU precision 650 * to 53 bit mantissa. 651 * On Alpha platform this is handled via Compiler option. 652 */ 653#define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC) 654 655#else 656 657#define FIX_FPU() ((void)0) 658 659#endif 660 661JSBool 662js_InitRuntimeNumberState(JSContext *cx) 663{ 664 JSRuntime *rt; 665 jsdpun u; 666 struct lconv *locale; 667 668 rt = cx->runtime; 669 JS_ASSERT(!rt->jsNaN); 670 671 FIX_FPU(); 672 673 u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; 674 u.s.lo = 0xffffffff; 675 number_constants[NC_NaN].dval = js_NaN = u.d; 676 rt->jsNaN = js_NewWeaklyRootedDouble(cx, js_NaN); 677 if (!rt->jsNaN) 678 return JS_FALSE; 679 680 u.s.hi = JSDOUBLE_HI32_EXPMASK; 681 u.s.lo = 0x00000000; 682 number_constants[NC_POSITIVE_INFINITY].dval = u.d; 683 rt->jsPositiveInfinity = js_NewWeaklyRootedDouble(cx, u.d); 684 if (!rt->jsPositiveInfinity) 685 return JS_FALSE; 686 687 u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; 688 u.s.lo = 0x00000000; 689 number_constants[NC_NEGATIVE_INFINITY].dval = u.d; 690 rt->jsNegativeInfinity = js_NewWeaklyRootedDouble(cx, u.d); 691 if (!rt->jsNegativeInfinity) 692 return JS_FALSE; 693 694 u.s.hi = 0; 695 u.s.lo = 1; 696 number_constants[NC_MIN_VALUE].dval = u.d; 697 698 locale = localeconv(); 699 rt->thousandsSeparator = 700 JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); 701 rt->decimalSeparator = 702 JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); 703 rt->numGrouping = 704 JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); 705 706 return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; 707} 708 709void 710js_TraceRuntimeNumberState(JSTracer *trc) 711{ 712 JSRuntime *rt; 713 714 rt = trc->context->runtime; 715 if (rt->jsNaN) 716 JS_CALL_DOUBLE_TRACER(trc, rt->jsNaN, "NaN"); 717 if (rt->jsPositiveInfinity) 718 JS_CALL_DOUBLE_TRACER(trc, rt->jsPositiveInfinity, "+Infinity"); 719 if (rt->jsNegativeInfinity) 720 JS_CALL_DOUBLE_TRACER(trc, rt->jsNegativeInfinity, "-Infinity"); 721} 722 723void 724js_FinishRuntimeNumberState(JSContext *cx) 725{ 726 JSRuntime *rt = cx->runtime; 727 728 js_UnlockGCThingRT(rt, rt->jsNaN); 729 js_UnlockGCThingRT(rt, rt->jsNegativeInfinity); 730 js_UnlockGCThingRT(rt, rt->jsPositiveInfinity); 731 732 rt->jsNaN = NULL; 733 rt->jsNegativeInfinity = NULL; 734 rt->jsPositiveInfinity = NULL; 735 736 JS_free(cx, (void *)rt->thousandsSeparator); 737 JS_free(cx, (void *)rt->decimalSeparator); 738 JS_free(cx, (void *)rt->numGrouping); 739 rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; 740} 741 742JSObject * 743js_InitNumberClass(JSContext *cx, JSObject *obj) 744{ 745 JSObject *proto, *ctor; 746 JSRuntime *rt; 747 748 /* XXX must do at least once per new thread, so do it per JSContext... */ 749 FIX_FPU(); 750 751 if (!JS_DefineFunctions(cx, obj, number_functions)) 752 return NULL; 753 754 proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, 755 NULL, number_methods, NULL, NULL); 756 if (!proto || !(ctor = JS_GetConstructor(cx, proto))) 757 return NULL; 758 STOBJ_SET_SLOT(proto, JSSLOT_PRIVATE, JSVAL_ZERO); 759 if (!JS_DefineConstDoubles(cx, ctor, number_constants)) 760 return NULL; 761 762 /* ECMA 15.1.1.1 */ 763 rt = cx->runtime; 764 if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN), 765 NULL, NULL, JSPROP_PERMANENT)) { 766 return NULL; 767 } 768 769 /* ECMA 15.1.1.2 */ 770 if (!JS_DefineProperty(cx, obj, js_Infinity_str, 771 DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), 772 NULL, NULL, JSPROP_PERMANENT)) { 773 return NULL; 774 } 775 return proto; 776} 777 778JSBool 779js_NewNumberInRootedValue(JSContext *cx, jsdouble d, jsval *vp) 780{ 781 jsint i; 782 783 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { 784 *vp = INT_TO_JSVAL(i); 785 return JS_TRUE; 786 } 787 return js_NewDoubleInRootedValue(cx, d, vp); 788} 789 790char * 791js_NumberToCString(JSContext *cx, jsdouble d, jsint base, char *buf, size_t bufSize) 792{ 793 jsint i; 794 char *numStr; 795 796 JS_ASSERT(bufSize >= DTOSTR_STANDARD_BUFFER_SIZE); 797 if (JSDOUBLE_IS_INT(d, i)) { 798 numStr = js_IntToCString(i, base, buf, bufSize); 799 } else { 800 if (base == 10) 801 numStr = JS_dtostr(buf, bufSize, DTOSTR_STANDARD, 0, d); 802 else 803 numStr = JS_dtobasestr(base, d); 804 if (!numStr) { 805 JS_ReportOutOfMemory(cx); 806 return NULL; 807 } 808 } 809 return numStr; 810} 811 812static JSString * JS_FASTCALL 813NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base) 814{ 815 char buf[DTOSTR_STANDARD_BUFFER_SIZE]; 816 char *numStr; 817 818 if (base < 2 || base > 36) 819 return NULL; 820 numStr = js_NumberToCString(cx, d, base, buf, sizeof buf); 821 if (!numStr) 822 return NULL; 823 return JS_NewStringCopyZ(cx, numStr); 824} 825 826JSString * JS_FASTCALL 827js_NumberToString(JSContext *cx, jsdouble d) 828{ 829 return NumberToStringWithBase(cx, d, 10); 830} 831 832jsdouble 833js_ValueToNumber(JSContext *cx, jsval *vp) 834{ 835 jsval v; 836 JSString *str; 837 const jschar *bp, *end, *ep; 838 jsdouble d, *dp; 839 JSObject *obj; 840 JSTempValueRooter tvr; 841 842 v = *vp; 843 for (;;) { 844 if (JSVAL_IS_INT(v)) 845 return (jsdouble) JSVAL_TO_INT(v); 846 if (JSVAL_IS_DOUBLE(v)) 847 return *JSVAL_TO_DOUBLE(v); 848 if (JSVAL_IS_STRING(v)) { 849 str = JSVAL_TO_STRING(v); 850 851 /* 852 * Note that ECMA doesn't treat a string beginning with a '0' as 853 * an octal number here. This works because all such numbers will 854 * be interpreted as decimal by js_strtod and will never get 855 * passed to js_strtointeger (which would interpret them as 856 * octal). 857 */ 858 JSSTRING_CHARS_AND_END(str, bp, end); 859 if ((!js_strtod(cx, bp, end, &ep, &d) || 860 js_SkipWhiteSpace(ep, end) != end) && 861 (!js_strtointeger(cx, bp, end, &ep, 0, &d) || 862 js_SkipWhiteSpace(ep, end) != end)) { 863 break; 864 } 865 866 /* 867 * JSVAL_TRUE indicates that double jsval was never constructed 868 * for the result. 869 */ 870 *vp = JSVAL_TRUE; 871 return d; 872 } 873 if (JSVAL_IS_BOOLEAN(v)) { 874 if (JSVAL_TO_BOOLEAN(v)) { 875 *vp = JSVAL_ONE; 876 return 1.0; 877 } else { 878 *vp = JSVAL_ZERO; 879 return 0.0; 880 } 881 } 882 if (JSVAL_IS_NULL(v)) { 883 *vp = JSVAL_ZERO; 884 return 0.0; 885 } 886 if (JSVAL_IS_VOID(v)) 887 break; 888 889 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); 890 obj = JSVAL_TO_OBJECT(v); 891 892 /* 893 * vp roots obj so we cannot use it as an extra root for 894 * OBJ_DEFAULT_VALUE result when calling the hook. 895 */ 896 JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr); 897 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &tvr.u.value)) 898 obj = NULL; 899 else 900 v = *vp = tvr.u.value; 901 JS_POP_TEMP_ROOT(cx, &tvr); 902 if (!obj) { 903 *vp = JSVAL_NULL; 904 return 0.0; 905 } 906 if (!JSVAL_IS_PRIMITIVE(v)) 907 break; 908 } 909 910 dp = cx->runtime->jsNaN; 911 *vp = DOUBLE_TO_JSVAL(dp); 912 return *dp; 913} 914 915int32 916js_ValueToECMAInt32(JSContext *cx, jsval *vp) 917{ 918 jsval v; 919 jsdouble d; 920 921 v = *vp; 922 if (JSVAL_IS_INT(v)) 923 return JSVAL_TO_INT(v); 924 if (JSVAL_IS_DOUBLE(v)) { 925 d = *JSVAL_TO_DOUBLE(v); 926 *vp = JSVAL_TRUE; 927 } else { 928 d = js_ValueToNumber(cx, vp); 929 if (JSVAL_IS_NULL(*vp)) 930 return 0; 931 *vp = JSVAL_TRUE; 932 } 933 return js_DoubleToECMAInt32(d); 934} 935 936int32 937js_DoubleToECMAInt32(jsdouble d) 938{ 939 int32 i; 940 jsdouble two32, two31; 941 942 if (!JSDOUBLE_IS_FINITE(d)) 943 return 0; 944 945 i = (int32) d; 946 if ((jsdouble) i == d) 947 return i; 948 949 two32 = 4294967296.0; 950 two31 = 2147483648.0; 951 d = fmod(d, two32); 952 d = (d >= 0) ? floor(d) : ceil(d) + two32; 953 return (int32) (d >= two31 ? d - two32 : d); 954} 955 956uint32 957js_ValueToECMAUint32(JSContext *cx, jsval *vp) 958{ 959 jsval v; 960 jsint i; 961 jsdouble d; 962 963 v = *vp; 964 if (JSVAL_IS_INT(v)) { 965 i = JSVAL_TO_INT(v); 966 if (i < 0) 967 *vp = JSVAL_TRUE; 968 return (uint32) i; 969 } 970 if (JSVAL_IS_DOUBLE(v)) { 971 d = *JSVAL_TO_DOUBLE(v); 972 *vp = JSVAL_TRUE; 973 } else { 974 d = js_ValueToNumber(cx, vp); 975 if (JSVAL_IS_NULL(*vp)) 976 return 0; 977 *vp = JSVAL_TRUE; 978 } 979 return js_DoubleToECMAUint32(d); 980} 981 982uint32 983js_DoubleToECMAUint32(jsdouble d) 984{ 985 int32 i; 986 JSBool neg; 987 jsdouble two32; 988 989 if (!JSDOUBLE_IS_FINITE(d)) 990 return 0; 991 992 /* 993 * We check whether d fits int32, not uint32, as all but the ">>>" bit 994 * manipulation bytecode stores the result as int, not uint. When the 995 * result does not fit int jsval, it will be stored as a negative double. 996 */ 997 i = (int32) d; 998 if ((jsdouble) i == d) 999 return (int32)i; 1000 1001 neg = (d < 0); 1002 d = floor(neg ? -d : d); 1003 d = neg ? -d : d; 1004 1005 two32 = 4294967296.0; 1006 d = fmod(d, two32); 1007 1008 return (uint32) (d >= 0 ? d : d + two32); 1009} 1010 1011int32 1012js_ValueToInt32(JSContext *cx, jsval *vp) 1013{ 1014 jsval v; 1015 jsdouble d; 1016 1017 v = *vp; 1018 if (JSVAL_IS_INT(v)) 1019 return JSVAL_TO_INT(v); 1020 d = js_ValueToNumber(cx, vp); 1021 if (JSVAL_IS_NULL(*vp)) 1022 return 0; 1023 if (JSVAL_IS_INT(*vp)) 1024 return JSVAL_TO_INT(*vp); 1025 1026 *vp = JSVAL_TRUE; 1027 if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { 1028 js_ReportValueError(cx, JSMSG_CANT_CONVERT, 1029 JSDVG_SEARCH_STACK, v, NULL); 1030 *vp = JSVAL_NULL; 1031 return 0; 1032 } 1033 return (int32) floor(d + 0.5); /* Round to nearest */ 1034} 1035 1036uint16 1037js_ValueToUint16(JSContext *cx, jsval *vp) 1038{ 1039 jsdouble d; 1040 uint16 u; 1041 jsuint m; 1042 JSBool neg; 1043 1044 d = js_ValueToNumber(cx, vp); 1045 if (JSVAL_IS_NULL(*vp)) 1046 return 0; 1047 1048 if (JSVAL_IS_INT(*vp)) { 1049 u = (uint16) JSVAL_TO_INT(*vp); 1050 } else if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { 1051 u = (uint16) 0; 1052 } else { 1053 u = (uint16) d; 1054 if ((jsdouble) u != d) { 1055 neg = (d < 0); 1056 d = floor(neg ? -d : d); 1057 d = neg ? -d : d; 1058 m = JS_BIT(16); 1059 d = fmod(d, (double) m); 1060 if (d < 0) 1061 d += m; 1062 u = (uint16) d; 1063 } 1064 } 1065 *vp = INT_TO_JSVAL(u); 1066 return u; 1067} 1068 1069jsdouble 1070js_DoubleToInteger(jsdouble d) 1071{ 1072 JSBool neg; 1073 1074 if (d == 0) 1075 return d; 1076 if (!JSDOUBLE_IS_FINITE(d)) { 1077 if (JSDOUBLE_IS_NaN(d)) 1078 return 0; 1079 return d; 1080 } 1081 neg = (d < 0); 1082 d = floor(neg ? -d : d); 1083 return neg ? -d : d; 1084} 1085 1086JSBool 1087js_strtod(JSContext *cx, const jschar *s, const jschar *send, 1088 const jschar **ep, jsdouble *dp) 1089{ 1090 const jschar *s1; 1091 size_t length, i; 1092 char cbuf[32]; 1093 char *cstr, *istr, *estr; 1094 JSBool negative; 1095 jsdouble d; 1096 1097 s1 = js_SkipWhiteSpace(s, send); 1098 length = send - s1; 1099 1100 /* Use cbuf to avoid malloc */ 1101 if (length >= sizeof cbuf) { 1102 cstr = (char *) JS_malloc(cx, length + 1); 1103 if (!cstr) 1104 return JS_FALSE; 1105 } else { 1106 cstr = cbuf; 1107 } 1108 1109 for (i = 0; i != length; i++) { 1110 if (s1[i] >> 8) 1111 break; 1112 cstr[i] = (char)s1[i]; 1113 } 1114 cstr[i] = 0; 1115 1116 istr = cstr; 1117 if ((negative = (*istr == '-')) != 0 || *istr == '+') 1118 istr++; 1119 if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { 1120 d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); 1121 estr = istr + 8; 1122 } else { 1123 int err; 1124 d = JS_strtod(cstr, &estr, &err); 1125 if (d == HUGE_VAL) 1126 d = *cx->runtime->jsPositiveInfinity; 1127 else if (d == -HUGE_VAL) 1128 d = *cx->runtime->jsNegativeInfinity; 1129#ifdef HPUX 1130 if (d == 0.0 && negative) { 1131 /* 1132 * "-0", "-1e-2000" come out as positive zero 1133 * here on HPUX. Force a negative zero instead. 1134 */ 1135 JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT; 1136 JSDOUBLE_LO32(d) = 0; 1137 } 1138#endif 1139 } 1140 1141 i = estr - cstr; 1142 if (cstr != cbuf) 1143 JS_free(cx, cstr); 1144 *ep = i ? s1 + i : s; 1145 *dp = d; 1146 return JS_TRUE; 1147} 1148 1149struct BinaryDigitReader 1150{ 1151 uintN base; /* Base of number; must be a power of 2 */ 1152 uintN digit; /* Current digit value in radix given by base */ 1153 uintN digitMask; /* Mask to extract the next bit from digit */ 1154 const jschar *digits; /* Pointer to the remaining digits */ 1155 const jschar *end; /* Pointer to first non-digit */ 1156}; 1157 1158/* Return the next binary digit from the number or -1 if done */ 1159static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) 1160{ 1161 intN bit; 1162 1163 if (bdr->digitMask == 0) { 1164 uintN c; 1165 1166 if (bdr->digits == bdr->end) 1167 return -1; 1168 1169 c = *bdr->digits++; 1170 if ('0' <= c && c <= '9') 1171 bdr->digit = c - '0'; 1172 else if ('a' <= c && c <= 'z') 1173 bdr->digit = c - 'a' + 10; 1174 else bdr->digit = c - 'A' + 10; 1175 bdr->digitMask = bdr->base >> 1; 1176 } 1177 bit = (bdr->digit & bdr->digitMask) != 0; 1178 bdr->digitMask >>= 1; 1179 return bit; 1180} 1181 1182JSBool 1183js_strtointeger(JSContext *cx, const jschar *s, const jschar *send, 1184 const jschar **ep, jsint base, jsdouble *dp) 1185{ 1186 const jschar *s1, *start; 1187 JSBool negative; 1188 jsdouble value; 1189 1190 s1 = js_SkipWhiteSpace(s, send); 1191 if (s1 == send) 1192 goto no_digits; 1193 if ((negative = (*s1 == '-')) != 0 || *s1 == '+') { 1194 s1++; 1195 if (s1 == send) 1196 goto no_digits; 1197 } 1198 1199 if (base == 0) { 1200 /* No base supplied, or some base that evaluated to 0. */ 1201 if (*s1 == '0') { 1202 /* It's either hex or octal; only increment char if str isn't '0' */ 1203 if (s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { 1204 base = 16; 1205 s1 += 2; 1206 if (s1 == send) 1207 goto no_digits; 1208 } else { 1209 base = 8; 1210 } 1211 } else { 1212 base = 10; /* Default to decimal. */ 1213 } 1214 } else if (base == 16) { 1215 /* If base is 16, ignore hex prefix. */ 1216 if (*s1 == '0' && s1 + 1 != send && (s1[1] == 'X' || s1[1] == 'x')) { 1217 s1 += 2; 1218 if (s1 == send) 1219 goto no_digits; 1220 } 1221 } 1222 1223 /* 1224 * Done with the preliminaries; find some prefix of the string that's 1225 * a number in the given base. 1226 */ 1227 JS_ASSERT(s1 < send); 1228 start = s1; 1229 value = 0.0; 1230 do { 1231 uintN digit; 1232 jschar c = *s1; 1233 if ('0' <= c && c <= '9') 1234 digit = c - '0'; 1235 else if ('a' <= c && c <= 'z') 1236 digit = c - 'a' + 10; 1237 else if ('A' <= c && c <= 'Z') 1238 digit = c - 'A' + 10; 1239 else 1240 break; 1241 if (digit >= (uintN)base) 1242 break; 1243 value = value * base + digit; 1244 } while (++s1 != send); 1245 1246 if (value >= 9007199254740992.0) { 1247 if (base == 10) { 1248 /* 1249 * If we're accumulating a decimal number and the number is >= 1250 * 2^53, then the result from the repeated multiply-add above may 1251 * be inaccurate. Call JS_strtod to get the correct answer. 1252 */ 1253 size_t i; 1254 size_t length = s1 - start; 1255 char *cstr = (char *) JS_malloc(cx, length + 1); 1256 char *estr; 1257 int err=0; 1258 1259 if (!cstr) 1260 return JS_FALSE; 1261 for (i = 0; i != length; i++) 1262 cstr[i] = (char)start[i]; 1263 cstr[length] = 0; 1264 1265 value = JS_strtod(cstr, &estr, &err); 1266 if (err == JS_DTOA_ENOMEM) { 1267 JS_ReportOutOfMemory(cx); 1268 JS_free(cx, cstr); 1269 return JS_FALSE; 1270 } 1271 if (err == JS_DTOA_ERANGE && value == HUGE_VAL) 1272 value = *cx->runtime->jsPositiveInfinity; 1273 JS_free(cx, cstr); 1274 } else if ((base & (base - 1)) == 0) { 1275 /* 1276 * The number may also be inaccurate for power-of-two bases. This 1277 * happens if the addition in value * base + digit causes a round- 1278 * down to an even least significant mantissa bit when the first 1279 * dropped bit is a one. If any of the following digits in the 1280 * number (which haven't been added in yet) are nonzero, then the 1281 * correct action would have been to round up instead of down. An 1282 * example occurs when reading the number 0x1000000000000081, which 1283 * rounds to 0x1000000000000000 instead of 0x1000000000000100. 1284 */ 1285 struct BinaryDigitReader bdr; 1286 intN bit, bit2; 1287 intN j; 1288 1289 bdr.base = base; 1290 bdr.digitMask = 0; 1291 bdr.digits = start; 1292 bdr.end = s1; 1293 value = 0.0; 1294 1295 /* Skip leading zeros. */ 1296 do { 1297 bit = GetNextBinaryDigit(&bdr); 1298 } while (bit == 0); 1299 1300 if (bit == 1) { 1301 /* Gather the 53 significant bits (including the leading 1) */ 1302 value = 1.0; 1303 for (j = 52; j; j--) { 1304 bit = GetNextBinaryDigit(&bdr); 1305 if (bit < 0) 1306 goto done; 1307 value = value*2 + bit; 1308 } 1309 /* bit2 is the 54th bit (the first dropped from the mantissa) */ 1310 bit2 = GetNextBinaryDigit(&bdr); 1311 if (bit2 >= 0) { 1312 jsdouble factor = 2.0; 1313 intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ 1314 intN bit3; 1315 1316 while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { 1317 sticky |= bit3; 1318 factor *= 2; 1319 } 1320 value += bit2 & (bit | sticky); 1321 value *= factor; 1322 } 1323 done:; 1324 } 1325 } 1326 } 1327 /* We don't worry about inaccurate numbers for any other base. */ 1328 1329 if (s1 == start) { 1330 no_digits: 1331 *dp = 0.0; 1332 *ep = s; 1333 } else { 1334 *dp = negative ? -value : value; 1335 *ep = s1; 1336 } 1337 return JS_TRUE; 1338}