js/src/jsnum.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * JS number type and wrapper class.
michael@0 9 */
michael@0 10
michael@0 11 #include "jsnum.h"
michael@0 12
michael@0 13 #include "mozilla/FloatingPoint.h"
michael@0 14 #include "mozilla/PodOperations.h"
michael@0 15 #include "mozilla/RangedPtr.h"
michael@0 16
michael@0 17 #ifdef HAVE_LOCALECONV
michael@0 18 #include <locale.h>
michael@0 19 #endif
michael@0 20 #include <math.h>
michael@0 21 #include <string.h>
michael@0 22
michael@0 23 #include "double-conversion.h"
michael@0 24 #include "jsatom.h"
michael@0 25 #include "jscntxt.h"
michael@0 26 #include "jsdtoa.h"
michael@0 27 #include "jsobj.h"
michael@0 28 #include "jsstr.h"
michael@0 29 #include "jstypes.h"
michael@0 30
michael@0 31 #include "vm/GlobalObject.h"
michael@0 32 #include "vm/NumericConversions.h"
michael@0 33 #include "vm/StringBuffer.h"
michael@0 34
michael@0 35 #include "jsatominlines.h"
michael@0 36
michael@0 37 #include "vm/NumberObject-inl.h"
michael@0 38 #include "vm/String-inl.h"
michael@0 39
michael@0 40 using namespace js;
michael@0 41 using namespace js::types;
michael@0 42
michael@0 43 using mozilla::Abs;
michael@0 44 using mozilla::MinNumberValue;
michael@0 45 using mozilla::NegativeInfinity;
michael@0 46 using mozilla::PodCopy;
michael@0 47 using mozilla::PositiveInfinity;
michael@0 48 using mozilla::RangedPtr;
michael@0 49 using JS::GenericNaN;
michael@0 50
michael@0 51 /*
michael@0 52 * If we're accumulating a decimal number and the number is >= 2^53, then the
michael@0 53 * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
michael@0 54 * Call js_strtod_harder to get the correct answer.
michael@0 55 */
michael@0 56 static bool
michael@0 57 ComputeAccurateDecimalInteger(ThreadSafeContext *cx,
michael@0 58 const jschar *start, const jschar *end, double *dp)
michael@0 59 {
michael@0 60 size_t length = end - start;
michael@0 61 char *cstr = cx->pod_malloc<char>(length + 1);
michael@0 62 if (!cstr)
michael@0 63 return false;
michael@0 64
michael@0 65 for (size_t i = 0; i < length; i++) {
michael@0 66 char c = char(start[i]);
michael@0 67 JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
michael@0 68 cstr[i] = c;
michael@0 69 }
michael@0 70 cstr[length] = 0;
michael@0 71
michael@0 72 char *estr;
michael@0 73 int err = 0;
michael@0 74 *dp = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
michael@0 75 if (err == JS_DTOA_ENOMEM) {
michael@0 76 js_ReportOutOfMemory(cx);
michael@0 77 js_free(cstr);
michael@0 78 return false;
michael@0 79 }
michael@0 80 js_free(cstr);
michael@0 81 return true;
michael@0 82 }
michael@0 83
michael@0 84 namespace {
michael@0 85
michael@0 86 class BinaryDigitReader
michael@0 87 {
michael@0 88 const int base; /* Base of number; must be a power of 2 */
michael@0 89 int digit; /* Current digit value in radix given by base */
michael@0 90 int digitMask; /* Mask to extract the next bit from digit */
michael@0 91 const jschar *start; /* Pointer to the remaining digits */
michael@0 92 const jschar *end; /* Pointer to first non-digit */
michael@0 93
michael@0 94 public:
michael@0 95 BinaryDigitReader(int base, const jschar *start, const jschar *end)
michael@0 96 : base(base), digit(0), digitMask(0), start(start), end(end)
michael@0 97 {
michael@0 98 }
michael@0 99
michael@0 100 /* Return the next binary digit from the number, or -1 if done. */
michael@0 101 int nextDigit() {
michael@0 102 if (digitMask == 0) {
michael@0 103 if (start == end)
michael@0 104 return -1;
michael@0 105
michael@0 106 int c = *start++;
michael@0 107 JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
michael@0 108 if ('0' <= c && c <= '9')
michael@0 109 digit = c - '0';
michael@0 110 else if ('a' <= c && c <= 'z')
michael@0 111 digit = c - 'a' + 10;
michael@0 112 else
michael@0 113 digit = c - 'A' + 10;
michael@0 114 digitMask = base >> 1;
michael@0 115 }
michael@0 116
michael@0 117 int bit = (digit & digitMask) != 0;
michael@0 118 digitMask >>= 1;
michael@0 119 return bit;
michael@0 120 }
michael@0 121 };
michael@0 122
michael@0 123 } /* anonymous namespace */
michael@0 124
michael@0 125 /*
michael@0 126 * The fast result might also have been inaccurate for power-of-two bases. This
michael@0 127 * happens if the addition in value * 2 + digit causes a round-down to an even
michael@0 128 * least significant mantissa bit when the first dropped bit is a one. If any
michael@0 129 * of the following digits in the number (which haven't been added in yet) are
michael@0 130 * nonzero, then the correct action would have been to round up instead of
michael@0 131 * down. An example occurs when reading the number 0x1000000000000081, which
michael@0 132 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
michael@0 133 */
michael@0 134 static double
michael@0 135 ComputeAccurateBinaryBaseInteger(const jschar *start, const jschar *end, int base)
michael@0 136 {
michael@0 137 BinaryDigitReader bdr(base, start, end);
michael@0 138
michael@0 139 /* Skip leading zeroes. */
michael@0 140 int bit;
michael@0 141 do {
michael@0 142 bit = bdr.nextDigit();
michael@0 143 } while (bit == 0);
michael@0 144
michael@0 145 JS_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
michael@0 146
michael@0 147 /* Gather the 53 significant bits (including the leading 1). */
michael@0 148 double value = 1.0;
michael@0 149 for (int j = 52; j > 0; j--) {
michael@0 150 bit = bdr.nextDigit();
michael@0 151 if (bit < 0)
michael@0 152 return value;
michael@0 153 value = value * 2 + bit;
michael@0 154 }
michael@0 155
michael@0 156 /* bit2 is the 54th bit (the first dropped from the mantissa). */
michael@0 157 int bit2 = bdr.nextDigit();
michael@0 158 if (bit2 >= 0) {
michael@0 159 double factor = 2.0;
michael@0 160 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
michael@0 161 int bit3;
michael@0 162
michael@0 163 while ((bit3 = bdr.nextDigit()) >= 0) {
michael@0 164 sticky |= bit3;
michael@0 165 factor *= 2;
michael@0 166 }
michael@0 167 value += bit2 & (bit | sticky);
michael@0 168 value *= factor;
michael@0 169 }
michael@0 170
michael@0 171 return value;
michael@0 172 }
michael@0 173
michael@0 174 double
michael@0 175 js::ParseDecimalNumber(const JS::TwoByteChars chars)
michael@0 176 {
michael@0 177 MOZ_ASSERT(chars.length() > 0);
michael@0 178 uint64_t dec = 0;
michael@0 179 RangedPtr<jschar> s = chars.start(), end = chars.end();
michael@0 180 do {
michael@0 181 jschar c = *s;
michael@0 182 MOZ_ASSERT('0' <= c && c <= '9');
michael@0 183 uint8_t digit = c - '0';
michael@0 184 uint64_t next = dec * 10 + digit;
michael@0 185 MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
michael@0 186 "next value won't be an integrally-precise double");
michael@0 187 dec = next;
michael@0 188 } while (++s < end);
michael@0 189 return static_cast<double>(dec);
michael@0 190 }
michael@0 191
michael@0 192 bool
michael@0 193 js::GetPrefixInteger(ThreadSafeContext *cx, const jschar *start, const jschar *end, int base,
michael@0 194 const jschar **endp, double *dp)
michael@0 195 {
michael@0 196 JS_ASSERT(start <= end);
michael@0 197 JS_ASSERT(2 <= base && base <= 36);
michael@0 198
michael@0 199 const jschar *s = start;
michael@0 200 double d = 0.0;
michael@0 201 for (; s < end; s++) {
michael@0 202 int digit;
michael@0 203 jschar c = *s;
michael@0 204 if ('0' <= c && c <= '9')
michael@0 205 digit = c - '0';
michael@0 206 else if ('a' <= c && c <= 'z')
michael@0 207 digit = c - 'a' + 10;
michael@0 208 else if ('A' <= c && c <= 'Z')
michael@0 209 digit = c - 'A' + 10;
michael@0 210 else
michael@0 211 break;
michael@0 212 if (digit >= base)
michael@0 213 break;
michael@0 214 d = d * base + digit;
michael@0 215 }
michael@0 216
michael@0 217 *endp = s;
michael@0 218 *dp = d;
michael@0 219
michael@0 220 /* If we haven't reached the limit of integer precision, we're done. */
michael@0 221 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
michael@0 222 return true;
michael@0 223
michael@0 224 /*
michael@0 225 * Otherwise compute the correct integer from the prefix of valid digits
michael@0 226 * if we're computing for base ten or a power of two. Don't worry about
michael@0 227 * other bases; see 15.1.2.2 step 13.
michael@0 228 */
michael@0 229 if (base == 10)
michael@0 230 return ComputeAccurateDecimalInteger(cx, start, s, dp);
michael@0 231 if ((base & (base - 1)) == 0)
michael@0 232 *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
michael@0 233
michael@0 234 return true;
michael@0 235 }
michael@0 236
michael@0 237 bool
michael@0 238 js::GetDecimalInteger(ExclusiveContext *cx, const jschar *start, const jschar *end, double *dp)
michael@0 239 {
michael@0 240 JS_ASSERT(start <= end);
michael@0 241
michael@0 242 const jschar *s = start;
michael@0 243 double d = 0.0;
michael@0 244 for (; s < end; s++) {
michael@0 245 jschar c = *s;
michael@0 246 JS_ASSERT('0' <= c && c <= '9');
michael@0 247 int digit = c - '0';
michael@0 248 d = d * 10 + digit;
michael@0 249 }
michael@0 250
michael@0 251 *dp = d;
michael@0 252
michael@0 253 // If we haven't reached the limit of integer precision, we're done.
michael@0 254 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
michael@0 255 return true;
michael@0 256
michael@0 257 // Otherwise compute the correct integer from the prefix of valid digits.
michael@0 258 return ComputeAccurateDecimalInteger(cx, start, s, dp);
michael@0 259 }
michael@0 260
michael@0 261 static bool
michael@0 262 num_isNaN(JSContext *cx, unsigned argc, Value *vp)
michael@0 263 {
michael@0 264 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 265
michael@0 266 if (args.length() == 0) {
michael@0 267 args.rval().setBoolean(true);
michael@0 268 return true;
michael@0 269 }
michael@0 270
michael@0 271 double x;
michael@0 272 if (!ToNumber(cx, args[0], &x))
michael@0 273 return false;
michael@0 274
michael@0 275 args.rval().setBoolean(mozilla::IsNaN(x));
michael@0 276 return true;
michael@0 277 }
michael@0 278
michael@0 279 static bool
michael@0 280 num_isFinite(JSContext *cx, unsigned argc, Value *vp)
michael@0 281 {
michael@0 282 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 283
michael@0 284 if (args.length() == 0) {
michael@0 285 args.rval().setBoolean(false);
michael@0 286 return true;
michael@0 287 }
michael@0 288
michael@0 289 double x;
michael@0 290 if (!ToNumber(cx, args[0], &x))
michael@0 291 return false;
michael@0 292
michael@0 293 args.rval().setBoolean(mozilla::IsFinite(x));
michael@0 294 return true;
michael@0 295 }
michael@0 296
michael@0 297 static bool
michael@0 298 num_parseFloat(JSContext *cx, unsigned argc, Value *vp)
michael@0 299 {
michael@0 300 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 301
michael@0 302 if (args.length() == 0) {
michael@0 303 args.rval().setNaN();
michael@0 304 return true;
michael@0 305 }
michael@0 306 JSString *str = ToString<CanGC>(cx, args[0]);
michael@0 307 if (!str)
michael@0 308 return false;
michael@0 309 const jschar *bp = str->getChars(cx);
michael@0 310 if (!bp)
michael@0 311 return false;
michael@0 312 const jschar *end = bp + str->length();
michael@0 313 const jschar *ep;
michael@0 314 double d;
michael@0 315 if (!js_strtod(cx, bp, end, &ep, &d))
michael@0 316 return false;
michael@0 317 if (ep == bp) {
michael@0 318 args.rval().setNaN();
michael@0 319 return true;
michael@0 320 }
michael@0 321 args.rval().setDouble(d);
michael@0 322 return true;
michael@0 323 }
michael@0 324
michael@0 325 /* ES5 15.1.2.2. */
michael@0 326 bool
michael@0 327 js::num_parseInt(JSContext *cx, unsigned argc, Value *vp)
michael@0 328 {
michael@0 329 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 330
michael@0 331 /* Fast paths and exceptional cases. */
michael@0 332 if (args.length() == 0) {
michael@0 333 args.rval().setNaN();
michael@0 334 return true;
michael@0 335 }
michael@0 336
michael@0 337 if (args.length() == 1 ||
michael@0 338 (args[1].isInt32() && (args[1].toInt32() == 0 || args[1].toInt32() == 10))) {
michael@0 339 if (args[0].isInt32()) {
michael@0 340 args.rval().set(args[0]);
michael@0 341 return true;
michael@0 342 }
michael@0 343
michael@0 344 /*
michael@0 345 * Step 1 is |inputString = ToString(string)|. When string >=
michael@0 346 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
michael@0 347 * the word, which would mean the result of parseInt(string) should be |N|.
michael@0 348 *
michael@0 349 * To preserve this behaviour, we can't use the fast-path when string >
michael@0 350 * 1e21, or else the result would be |NeM|.
michael@0 351 *
michael@0 352 * The same goes for values smaller than 1.0e-6, because the string would be in
michael@0 353 * the form of "Ne-M".
michael@0 354 */
michael@0 355 if (args[0].isDouble()) {
michael@0 356 double d = args[0].toDouble();
michael@0 357 if (1.0e-6 < d && d < 1.0e21) {
michael@0 358 args.rval().setNumber(floor(d));
michael@0 359 return true;
michael@0 360 }
michael@0 361 if (-1.0e21 < d && d < -1.0e-6) {
michael@0 362 args.rval().setNumber(-floor(-d));
michael@0 363 return true;
michael@0 364 }
michael@0 365 if (d == 0.0) {
michael@0 366 args.rval().setInt32(0);
michael@0 367 return true;
michael@0 368 }
michael@0 369 }
michael@0 370 }
michael@0 371
michael@0 372 /* Step 1. */
michael@0 373 RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
michael@0 374 if (!inputString)
michael@0 375 return false;
michael@0 376 args[0].setString(inputString);
michael@0 377
michael@0 378 /* Steps 6-9. */
michael@0 379 bool stripPrefix = true;
michael@0 380 int32_t radix;
michael@0 381 if (!args.hasDefined(1)) {
michael@0 382 radix = 10;
michael@0 383 } else {
michael@0 384 if (!ToInt32(cx, args[1], &radix))
michael@0 385 return false;
michael@0 386 if (radix == 0) {
michael@0 387 radix = 10;
michael@0 388 } else {
michael@0 389 if (radix < 2 || radix > 36) {
michael@0 390 args.rval().setNaN();
michael@0 391 return true;
michael@0 392 }
michael@0 393 if (radix != 16)
michael@0 394 stripPrefix = false;
michael@0 395 }
michael@0 396 }
michael@0 397
michael@0 398 /* Step 2. */
michael@0 399 const jschar *s;
michael@0 400 const jschar *end;
michael@0 401 {
michael@0 402 const jschar *ws = inputString->getChars(cx);
michael@0 403 if (!ws)
michael@0 404 return false;
michael@0 405 end = ws + inputString->length();
michael@0 406 s = SkipSpace(ws, end);
michael@0 407
michael@0 408 MOZ_ASSERT(ws <= s);
michael@0 409 MOZ_ASSERT(s <= end);
michael@0 410 }
michael@0 411
michael@0 412 /* Steps 3-4. */
michael@0 413 bool negative = (s != end && s[0] == '-');
michael@0 414
michael@0 415 /* Step 5. */
michael@0 416 if (s != end && (s[0] == '-' || s[0] == '+'))
michael@0 417 s++;
michael@0 418
michael@0 419 /* Step 10. */
michael@0 420 if (stripPrefix) {
michael@0 421 if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
michael@0 422 s += 2;
michael@0 423 radix = 16;
michael@0 424 }
michael@0 425 }
michael@0 426
michael@0 427 /* Steps 11-15. */
michael@0 428 const jschar *actualEnd;
michael@0 429 double number;
michael@0 430 if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, &number))
michael@0 431 return false;
michael@0 432 if (s == actualEnd)
michael@0 433 args.rval().setNaN();
michael@0 434 else
michael@0 435 args.rval().setNumber(negative ? -number : number);
michael@0 436 return true;
michael@0 437 }
michael@0 438
michael@0 439 static const JSFunctionSpec number_functions[] = {
michael@0 440 JS_FN(js_isNaN_str, num_isNaN, 1,0),
michael@0 441 JS_FN(js_isFinite_str, num_isFinite, 1,0),
michael@0 442 JS_FN(js_parseFloat_str, num_parseFloat, 1,0),
michael@0 443 JS_FN(js_parseInt_str, num_parseInt, 2,0),
michael@0 444 JS_FS_END
michael@0 445 };
michael@0 446
michael@0 447 const Class NumberObject::class_ = {
michael@0 448 js_Number_str,
michael@0 449 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
michael@0 450 JS_PropertyStub, /* addProperty */
michael@0 451 JS_DeletePropertyStub, /* delProperty */
michael@0 452 JS_PropertyStub, /* getProperty */
michael@0 453 JS_StrictPropertyStub, /* setProperty */
michael@0 454 JS_EnumerateStub,
michael@0 455 JS_ResolveStub,
michael@0 456 JS_ConvertStub
michael@0 457 };
michael@0 458
michael@0 459 static bool
michael@0 460 Number(JSContext *cx, unsigned argc, Value *vp)
michael@0 461 {
michael@0 462 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 463
michael@0 464 /* Sample JS_CALLEE before clobbering. */
michael@0 465 bool isConstructing = args.isConstructing();
michael@0 466
michael@0 467 if (args.length() > 0) {
michael@0 468 if (!ToNumber(cx, args[0]))
michael@0 469 return false;
michael@0 470 args.rval().set(args[0]);
michael@0 471 } else {
michael@0 472 args.rval().setInt32(0);
michael@0 473 }
michael@0 474
michael@0 475 if (!isConstructing)
michael@0 476 return true;
michael@0 477
michael@0 478 JSObject *obj = NumberObject::create(cx, args.rval().toNumber());
michael@0 479 if (!obj)
michael@0 480 return false;
michael@0 481 args.rval().setObject(*obj);
michael@0 482 return true;
michael@0 483 }
michael@0 484
michael@0 485 MOZ_ALWAYS_INLINE bool
michael@0 486 IsNumber(HandleValue v)
michael@0 487 {
michael@0 488 return v.isNumber() || (v.isObject() && v.toObject().is<NumberObject>());
michael@0 489 }
michael@0 490
michael@0 491 static inline double
michael@0 492 Extract(const Value &v)
michael@0 493 {
michael@0 494 if (v.isNumber())
michael@0 495 return v.toNumber();
michael@0 496 return v.toObject().as<NumberObject>().unbox();
michael@0 497 }
michael@0 498
michael@0 499 #if JS_HAS_TOSOURCE
michael@0 500 MOZ_ALWAYS_INLINE bool
michael@0 501 num_toSource_impl(JSContext *cx, CallArgs args)
michael@0 502 {
michael@0 503 double d = Extract(args.thisv());
michael@0 504
michael@0 505 StringBuffer sb(cx);
michael@0 506 if (!sb.append("(new Number(") ||
michael@0 507 !NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
michael@0 508 !sb.append("))"))
michael@0 509 {
michael@0 510 return false;
michael@0 511 }
michael@0 512
michael@0 513 JSString *str = sb.finishString();
michael@0 514 if (!str)
michael@0 515 return false;
michael@0 516 args.rval().setString(str);
michael@0 517 return true;
michael@0 518 }
michael@0 519
michael@0 520 static bool
michael@0 521 num_toSource(JSContext *cx, unsigned argc, Value *vp)
michael@0 522 {
michael@0 523 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 524 return CallNonGenericMethod<IsNumber, num_toSource_impl>(cx, args);
michael@0 525 }
michael@0 526 #endif
michael@0 527
michael@0 528 ToCStringBuf::ToCStringBuf() :dbuf(nullptr)
michael@0 529 {
michael@0 530 JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
michael@0 531 }
michael@0 532
michael@0 533 ToCStringBuf::~ToCStringBuf()
michael@0 534 {
michael@0 535 js_free(dbuf);
michael@0 536 }
michael@0 537
michael@0 538 MOZ_ALWAYS_INLINE
michael@0 539 static JSFlatString *
michael@0 540 LookupDtoaCache(ThreadSafeContext *cx, double d)
michael@0 541 {
michael@0 542 if (!cx->isExclusiveContext())
michael@0 543 return nullptr;
michael@0 544
michael@0 545 if (JSCompartment *comp = cx->asExclusiveContext()->compartment()) {
michael@0 546 if (JSFlatString *str = comp->dtoaCache.lookup(10, d))
michael@0 547 return str;
michael@0 548 }
michael@0 549
michael@0 550 return nullptr;
michael@0 551 }
michael@0 552
michael@0 553 MOZ_ALWAYS_INLINE
michael@0 554 static void
michael@0 555 CacheNumber(ThreadSafeContext *cx, double d, JSFlatString *str)
michael@0 556 {
michael@0 557 if (!cx->isExclusiveContext())
michael@0 558 return;
michael@0 559
michael@0 560 if (JSCompartment *comp = cx->asExclusiveContext()->compartment())
michael@0 561 comp->dtoaCache.cache(10, d, str);
michael@0 562 }
michael@0 563
michael@0 564 MOZ_ALWAYS_INLINE
michael@0 565 static JSFlatString *
michael@0 566 LookupInt32ToString(ThreadSafeContext *cx, int32_t si)
michael@0 567 {
michael@0 568 if (si >= 0 && StaticStrings::hasInt(si))
michael@0 569 return cx->staticStrings().getInt(si);
michael@0 570
michael@0 571 return LookupDtoaCache(cx, si);
michael@0 572 }
michael@0 573
michael@0 574 template <typename T>
michael@0 575 MOZ_ALWAYS_INLINE
michael@0 576 static T *
michael@0 577 BackfillInt32InBuffer(int32_t si, T *buffer, size_t size, size_t *length)
michael@0 578 {
michael@0 579 uint32_t ui = Abs(si);
michael@0 580 JS_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
michael@0 581
michael@0 582 RangedPtr<T> end(buffer + size - 1, buffer, size);
michael@0 583 *end = '\0';
michael@0 584 RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
michael@0 585 if (si < 0)
michael@0 586 *--start = '-';
michael@0 587
michael@0 588 *length = end - start;
michael@0 589 return start.get();
michael@0 590 }
michael@0 591
michael@0 592 template <AllowGC allowGC>
michael@0 593 JSFlatString *
michael@0 594 js::Int32ToString(ThreadSafeContext *cx, int32_t si)
michael@0 595 {
michael@0 596 if (JSFlatString *str = LookupInt32ToString(cx, si))
michael@0 597 return str;
michael@0 598
michael@0 599 JSFatInlineString *str = js_NewGCFatInlineString<allowGC>(cx);
michael@0 600 if (!str)
michael@0 601 return nullptr;
michael@0 602
michael@0 603 jschar buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
michael@0 604 size_t length;
michael@0 605 jschar *start = BackfillInt32InBuffer(si, buffer,
michael@0 606 JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1, &length);
michael@0 607
michael@0 608 PodCopy(str->init(length), start, length + 1);
michael@0 609
michael@0 610 CacheNumber(cx, si, str);
michael@0 611 return str;
michael@0 612 }
michael@0 613
michael@0 614 template JSFlatString *
michael@0 615 js::Int32ToString<CanGC>(ThreadSafeContext *cx, int32_t si);
michael@0 616
michael@0 617 template JSFlatString *
michael@0 618 js::Int32ToString<NoGC>(ThreadSafeContext *cx, int32_t si);
michael@0 619
michael@0 620 JSAtom *
michael@0 621 js::Int32ToAtom(ExclusiveContext *cx, int32_t si)
michael@0 622 {
michael@0 623 if (JSFlatString *str = LookupInt32ToString(cx, si))
michael@0 624 return js::AtomizeString(cx, str);
michael@0 625
michael@0 626 char buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
michael@0 627 size_t length;
michael@0 628 char *start = BackfillInt32InBuffer(si, buffer, JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1, &length);
michael@0 629
michael@0 630 JSAtom *atom = Atomize(cx, start, length);
michael@0 631 if (!atom)
michael@0 632 return nullptr;
michael@0 633
michael@0 634 CacheNumber(cx, si, atom);
michael@0 635 return atom;
michael@0 636 }
michael@0 637
michael@0 638 /* Returns a non-nullptr pointer to inside cbuf. */
michael@0 639 static char *
michael@0 640 Int32ToCString(ToCStringBuf *cbuf, int32_t i, size_t *len, int base = 10)
michael@0 641 {
michael@0 642 uint32_t u = Abs(i);
michael@0 643
michael@0 644 RangedPtr<char> cp(cbuf->sbuf + ToCStringBuf::sbufSize - 1, cbuf->sbuf, ToCStringBuf::sbufSize);
michael@0 645 char *end = cp.get();
michael@0 646 *cp = '\0';
michael@0 647
michael@0 648 /* Build the string from behind. */
michael@0 649 switch (base) {
michael@0 650 case 10:
michael@0 651 cp = BackfillIndexInCharBuffer(u, cp);
michael@0 652 break;
michael@0 653 case 16:
michael@0 654 do {
michael@0 655 unsigned newu = u / 16;
michael@0 656 *--cp = "0123456789abcdef"[u - newu * 16];
michael@0 657 u = newu;
michael@0 658 } while (u != 0);
michael@0 659 break;
michael@0 660 default:
michael@0 661 JS_ASSERT(base >= 2 && base <= 36);
michael@0 662 do {
michael@0 663 unsigned newu = u / base;
michael@0 664 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
michael@0 665 u = newu;
michael@0 666 } while (u != 0);
michael@0 667 break;
michael@0 668 }
michael@0 669 if (i < 0)
michael@0 670 *--cp = '-';
michael@0 671
michael@0 672 *len = end - cp.get();
michael@0 673 return cp.get();
michael@0 674 }
michael@0 675
michael@0 676 template <AllowGC allowGC>
michael@0 677 static JSString * JS_FASTCALL
michael@0 678 js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base);
michael@0 679
michael@0 680 MOZ_ALWAYS_INLINE bool
michael@0 681 num_toString_impl(JSContext *cx, CallArgs args)
michael@0 682 {
michael@0 683 JS_ASSERT(IsNumber(args.thisv()));
michael@0 684
michael@0 685 double d = Extract(args.thisv());
michael@0 686
michael@0 687 int32_t base = 10;
michael@0 688 if (args.hasDefined(0)) {
michael@0 689 double d2;
michael@0 690 if (!ToInteger(cx, args[0], &d2))
michael@0 691 return false;
michael@0 692
michael@0 693 if (d2 < 2 || d2 > 36) {
michael@0 694 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
michael@0 695 return false;
michael@0 696 }
michael@0 697
michael@0 698 base = int32_t(d2);
michael@0 699 }
michael@0 700 JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, base);
michael@0 701 if (!str) {
michael@0 702 JS_ReportOutOfMemory(cx);
michael@0 703 return false;
michael@0 704 }
michael@0 705 args.rval().setString(str);
michael@0 706 return true;
michael@0 707 }
michael@0 708
michael@0 709 bool
michael@0 710 js_num_toString(JSContext *cx, unsigned argc, Value *vp)
michael@0 711 {
michael@0 712 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 713 return CallNonGenericMethod<IsNumber, num_toString_impl>(cx, args);
michael@0 714 }
michael@0 715
michael@0 716 #if !EXPOSE_INTL_API
michael@0 717 MOZ_ALWAYS_INLINE bool
michael@0 718 num_toLocaleString_impl(JSContext *cx, CallArgs args)
michael@0 719 {
michael@0 720 JS_ASSERT(IsNumber(args.thisv()));
michael@0 721
michael@0 722 double d = Extract(args.thisv());
michael@0 723
michael@0 724 Rooted<JSString*> str(cx, js_NumberToStringWithBase<CanGC>(cx, d, 10));
michael@0 725 if (!str) {
michael@0 726 JS_ReportOutOfMemory(cx);
michael@0 727 return false;
michael@0 728 }
michael@0 729
michael@0 730 /*
michael@0 731 * Create the string, move back to bytes to make string twiddling
michael@0 732 * a bit easier and so we can insert platform charset seperators.
michael@0 733 */
michael@0 734 JSAutoByteString numBytes(cx, str);
michael@0 735 if (!numBytes)
michael@0 736 return false;
michael@0 737 const char *num = numBytes.ptr();
michael@0 738 if (!num)
michael@0 739 return false;
michael@0 740
michael@0 741 /*
michael@0 742 * Find the first non-integer value, whether it be a letter as in
michael@0 743 * 'Infinity', a decimal point, or an 'e' from exponential notation.
michael@0 744 */
michael@0 745 const char *nint = num;
michael@0 746 if (*nint == '-')
michael@0 747 nint++;
michael@0 748 while (*nint >= '0' && *nint <= '9')
michael@0 749 nint++;
michael@0 750 int digits = nint - num;
michael@0 751 const char *end = num + digits;
michael@0 752 if (!digits) {
michael@0 753 args.rval().setString(str);
michael@0 754 return true;
michael@0 755 }
michael@0 756
michael@0 757 JSRuntime *rt = cx->runtime();
michael@0 758 size_t thousandsLength = strlen(rt->thousandsSeparator);
michael@0 759 size_t decimalLength = strlen(rt->decimalSeparator);
michael@0 760
michael@0 761 /* Figure out how long resulting string will be. */
michael@0 762 int buflen = strlen(num);
michael@0 763 if (*nint == '.')
michael@0 764 buflen += decimalLength - 1; /* -1 to account for existing '.' */
michael@0 765
michael@0 766 const char *numGrouping;
michael@0 767 const char *tmpGroup;
michael@0 768 numGrouping = tmpGroup = rt->numGrouping;
michael@0 769 int remainder = digits;
michael@0 770 if (*num == '-')
michael@0 771 remainder--;
michael@0 772
michael@0 773 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
michael@0 774 if (*tmpGroup >= remainder)
michael@0 775 break;
michael@0 776 buflen += thousandsLength;
michael@0 777 remainder -= *tmpGroup;
michael@0 778 tmpGroup++;
michael@0 779 }
michael@0 780
michael@0 781 int nrepeat;
michael@0 782 if (*tmpGroup == '\0' && *numGrouping != '\0') {
michael@0 783 nrepeat = (remainder - 1) / tmpGroup[-1];
michael@0 784 buflen += thousandsLength * nrepeat;
michael@0 785 remainder -= nrepeat * tmpGroup[-1];
michael@0 786 } else {
michael@0 787 nrepeat = 0;
michael@0 788 }
michael@0 789 tmpGroup--;
michael@0 790
michael@0 791 char *buf = cx->pod_malloc<char>(buflen + 1);
michael@0 792 if (!buf)
michael@0 793 return false;
michael@0 794
michael@0 795 char *tmpDest = buf;
michael@0 796 const char *tmpSrc = num;
michael@0 797
michael@0 798 while (*tmpSrc == '-' || remainder--) {
michael@0 799 JS_ASSERT(tmpDest - buf < buflen);
michael@0 800 *tmpDest++ = *tmpSrc++;
michael@0 801 }
michael@0 802 while (tmpSrc < end) {
michael@0 803 JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
michael@0 804 strcpy(tmpDest, rt->thousandsSeparator);
michael@0 805 tmpDest += thousandsLength;
michael@0 806 JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
michael@0 807 js_memcpy(tmpDest, tmpSrc, *tmpGroup);
michael@0 808 tmpDest += *tmpGroup;
michael@0 809 tmpSrc += *tmpGroup;
michael@0 810 if (--nrepeat < 0)
michael@0 811 tmpGroup--;
michael@0 812 }
michael@0 813
michael@0 814 if (*nint == '.') {
michael@0 815 JS_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
michael@0 816 strcpy(tmpDest, rt->decimalSeparator);
michael@0 817 tmpDest += decimalLength;
michael@0 818 JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
michael@0 819 strcpy(tmpDest, nint + 1);
michael@0 820 } else {
michael@0 821 JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
michael@0 822 strcpy(tmpDest, nint);
michael@0 823 }
michael@0 824
michael@0 825 if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode) {
michael@0 826 Rooted<Value> v(cx, StringValue(str));
michael@0 827 bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
michael@0 828 if (ok)
michael@0 829 args.rval().set(v);
michael@0 830 js_free(buf);
michael@0 831 return ok;
michael@0 832 }
michael@0 833
michael@0 834 str = js_NewStringCopyN<CanGC>(cx, buf, buflen);
michael@0 835 js_free(buf);
michael@0 836 if (!str)
michael@0 837 return false;
michael@0 838
michael@0 839 args.rval().setString(str);
michael@0 840 return true;
michael@0 841 }
michael@0 842
michael@0 843 static bool
michael@0 844 num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
michael@0 845 {
michael@0 846 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 847 return CallNonGenericMethod<IsNumber, num_toLocaleString_impl>(cx, args);
michael@0 848 }
michael@0 849 #endif /* !EXPOSE_INTL_API */
michael@0 850
michael@0 851 MOZ_ALWAYS_INLINE bool
michael@0 852 num_valueOf_impl(JSContext *cx, CallArgs args)
michael@0 853 {
michael@0 854 JS_ASSERT(IsNumber(args.thisv()));
michael@0 855 args.rval().setNumber(Extract(args.thisv()));
michael@0 856 return true;
michael@0 857 }
michael@0 858
michael@0 859 bool
michael@0 860 js_num_valueOf(JSContext *cx, unsigned argc, Value *vp)
michael@0 861 {
michael@0 862 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 863 return CallNonGenericMethod<IsNumber, num_valueOf_impl>(cx, args);
michael@0 864 }
michael@0 865
michael@0 866 static const unsigned MAX_PRECISION = 100;
michael@0 867
michael@0 868 static bool
michael@0 869 ComputePrecisionInRange(JSContext *cx, int minPrecision, int maxPrecision, HandleValue v,
michael@0 870 int *precision)
michael@0 871 {
michael@0 872 double prec;
michael@0 873 if (!ToInteger(cx, v, &prec))
michael@0 874 return false;
michael@0 875 if (minPrecision <= prec && prec <= maxPrecision) {
michael@0 876 *precision = int(prec);
michael@0 877 return true;
michael@0 878 }
michael@0 879
michael@0 880 ToCStringBuf cbuf;
michael@0 881 if (char *numStr = NumberToCString(cx, &cbuf, prec, 10))
michael@0 882 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE, numStr);
michael@0 883 return false;
michael@0 884 }
michael@0 885
michael@0 886 static bool
michael@0 887 DToStrResult(JSContext *cx, double d, JSDToStrMode mode, int precision, CallArgs args)
michael@0 888 {
michael@0 889 char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION + 1)];
michael@0 890 char *numStr = js_dtostr(cx->mainThread().dtoaState, buf, sizeof buf, mode, precision, d);
michael@0 891 if (!numStr) {
michael@0 892 JS_ReportOutOfMemory(cx);
michael@0 893 return false;
michael@0 894 }
michael@0 895 JSString *str = js_NewStringCopyZ<CanGC>(cx, numStr);
michael@0 896 if (!str)
michael@0 897 return false;
michael@0 898 args.rval().setString(str);
michael@0 899 return true;
michael@0 900 }
michael@0 901
michael@0 902 /*
michael@0 903 * In the following three implementations, we allow a larger range of precision
michael@0 904 * than ECMA requires; this is permitted by ECMA-262.
michael@0 905 */
michael@0 906 MOZ_ALWAYS_INLINE bool
michael@0 907 num_toFixed_impl(JSContext *cx, CallArgs args)
michael@0 908 {
michael@0 909 JS_ASSERT(IsNumber(args.thisv()));
michael@0 910
michael@0 911 int precision;
michael@0 912 if (args.length() == 0) {
michael@0 913 precision = 0;
michael@0 914 } else {
michael@0 915 if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, args[0], &precision))
michael@0 916 return false;
michael@0 917 }
michael@0 918
michael@0 919 return DToStrResult(cx, Extract(args.thisv()), DTOSTR_FIXED, precision, args);
michael@0 920 }
michael@0 921
michael@0 922 static bool
michael@0 923 num_toFixed(JSContext *cx, unsigned argc, Value *vp)
michael@0 924 {
michael@0 925 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 926 return CallNonGenericMethod<IsNumber, num_toFixed_impl>(cx, args);
michael@0 927 }
michael@0 928
michael@0 929 MOZ_ALWAYS_INLINE bool
michael@0 930 num_toExponential_impl(JSContext *cx, CallArgs args)
michael@0 931 {
michael@0 932 JS_ASSERT(IsNumber(args.thisv()));
michael@0 933
michael@0 934 JSDToStrMode mode;
michael@0 935 int precision;
michael@0 936 if (!args.hasDefined(0)) {
michael@0 937 mode = DTOSTR_STANDARD_EXPONENTIAL;
michael@0 938 precision = 0;
michael@0 939 } else {
michael@0 940 mode = DTOSTR_EXPONENTIAL;
michael@0 941 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, args[0], &precision))
michael@0 942 return false;
michael@0 943 }
michael@0 944
michael@0 945 return DToStrResult(cx, Extract(args.thisv()), mode, precision + 1, args);
michael@0 946 }
michael@0 947
michael@0 948 static bool
michael@0 949 num_toExponential(JSContext *cx, unsigned argc, Value *vp)
michael@0 950 {
michael@0 951 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 952 return CallNonGenericMethod<IsNumber, num_toExponential_impl>(cx, args);
michael@0 953 }
michael@0 954
michael@0 955 MOZ_ALWAYS_INLINE bool
michael@0 956 num_toPrecision_impl(JSContext *cx, CallArgs args)
michael@0 957 {
michael@0 958 JS_ASSERT(IsNumber(args.thisv()));
michael@0 959
michael@0 960 double d = Extract(args.thisv());
michael@0 961
michael@0 962 if (!args.hasDefined(0)) {
michael@0 963 JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10);
michael@0 964 if (!str) {
michael@0 965 JS_ReportOutOfMemory(cx);
michael@0 966 return false;
michael@0 967 }
michael@0 968 args.rval().setString(str);
michael@0 969 return true;
michael@0 970 }
michael@0 971
michael@0 972 int precision;
michael@0 973 if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, args[0], &precision))
michael@0 974 return false;
michael@0 975
michael@0 976 return DToStrResult(cx, d, DTOSTR_PRECISION, precision, args);
michael@0 977 }
michael@0 978
michael@0 979 static bool
michael@0 980 num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
michael@0 981 {
michael@0 982 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 983 return CallNonGenericMethod<IsNumber, num_toPrecision_impl>(cx, args);
michael@0 984 }
michael@0 985
michael@0 986 static const JSFunctionSpec number_methods[] = {
michael@0 987 #if JS_HAS_TOSOURCE
michael@0 988 JS_FN(js_toSource_str, num_toSource, 0, 0),
michael@0 989 #endif
michael@0 990 JS_FN(js_toString_str, js_num_toString, 1, 0),
michael@0 991 #if EXPOSE_INTL_API
michael@0 992 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0,0),
michael@0 993 #else
michael@0 994 JS_FN(js_toLocaleString_str, num_toLocaleString, 0,0),
michael@0 995 #endif
michael@0 996 JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
michael@0 997 JS_FN("toFixed", num_toFixed, 1, 0),
michael@0 998 JS_FN("toExponential", num_toExponential, 1, 0),
michael@0 999 JS_FN("toPrecision", num_toPrecision, 1, 0),
michael@0 1000 JS_FS_END
michael@0 1001 };
michael@0 1002
michael@0 1003
michael@0 1004 // ES6 draft ES6 15.7.3.10
michael@0 1005 static bool
michael@0 1006 Number_isNaN(JSContext *cx, unsigned argc, Value *vp)
michael@0 1007 {
michael@0 1008 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1009 if (args.length() < 1 || !args[0].isDouble()) {
michael@0 1010 args.rval().setBoolean(false);
michael@0 1011 return true;
michael@0 1012 }
michael@0 1013 args.rval().setBoolean(mozilla::IsNaN(args[0].toDouble()));
michael@0 1014 return true;
michael@0 1015 }
michael@0 1016
michael@0 1017 // ES6 draft ES6 15.7.3.11
michael@0 1018 static bool
michael@0 1019 Number_isFinite(JSContext *cx, unsigned argc, Value *vp)
michael@0 1020 {
michael@0 1021 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1022 if (args.length() < 1 || !args[0].isNumber()) {
michael@0 1023 args.rval().setBoolean(false);
michael@0 1024 return true;
michael@0 1025 }
michael@0 1026 args.rval().setBoolean(args[0].isInt32() ||
michael@0 1027 mozilla::IsFinite(args[0].toDouble()));
michael@0 1028 return true;
michael@0 1029 }
michael@0 1030
michael@0 1031 // ES6 draft ES6 15.7.3.12
michael@0 1032 static bool
michael@0 1033 Number_isInteger(JSContext *cx, unsigned argc, Value *vp)
michael@0 1034 {
michael@0 1035 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1036 if (args.length() < 1 || !args[0].isNumber()) {
michael@0 1037 args.rval().setBoolean(false);
michael@0 1038 return true;
michael@0 1039 }
michael@0 1040 Value val = args[0];
michael@0 1041 args.rval().setBoolean(val.isInt32() ||
michael@0 1042 (mozilla::IsFinite(val.toDouble()) &&
michael@0 1043 ToInteger(val.toDouble()) == val.toDouble()));
michael@0 1044 return true;
michael@0 1045 }
michael@0 1046
michael@0 1047 // ES6 drafult ES6 15.7.3.13
michael@0 1048 static bool
michael@0 1049 Number_toInteger(JSContext *cx, unsigned argc, Value *vp)
michael@0 1050 {
michael@0 1051 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1052 if (args.length() < 1) {
michael@0 1053 args.rval().setInt32(0);
michael@0 1054 return true;
michael@0 1055 }
michael@0 1056 double asint;
michael@0 1057 if (!ToInteger(cx, args[0], &asint))
michael@0 1058 return false;
michael@0 1059 args.rval().setNumber(asint);
michael@0 1060 return true;
michael@0 1061 }
michael@0 1062
michael@0 1063
michael@0 1064 static const JSFunctionSpec number_static_methods[] = {
michael@0 1065 JS_FN("isFinite", Number_isFinite, 1, 0),
michael@0 1066 JS_FN("isInteger", Number_isInteger, 1, 0),
michael@0 1067 JS_FN("isNaN", Number_isNaN, 1, 0),
michael@0 1068 JS_FN("toInteger", Number_toInteger, 1, 0),
michael@0 1069 /* ES6 additions. */
michael@0 1070 JS_FN("parseFloat", num_parseFloat, 1, 0),
michael@0 1071 JS_FN("parseInt", num_parseInt, 2, 0),
michael@0 1072 JS_FS_END
michael@0 1073 };
michael@0 1074
michael@0 1075
michael@0 1076 /* NB: Keep this in synch with number_constants[]. */
michael@0 1077 enum nc_slot {
michael@0 1078 NC_NaN,
michael@0 1079 NC_POSITIVE_INFINITY,
michael@0 1080 NC_NEGATIVE_INFINITY,
michael@0 1081 NC_MAX_VALUE,
michael@0 1082 NC_MIN_VALUE,
michael@0 1083 NC_MAX_SAFE_INTEGER,
michael@0 1084 NC_MIN_SAFE_INTEGER,
michael@0 1085 NC_EPSILON,
michael@0 1086 NC_LIMIT
michael@0 1087 };
michael@0 1088
michael@0 1089 /*
michael@0 1090 * Some to most C compilers forbid spelling these at compile time, or barf
michael@0 1091 * if you try, so all but MAX_VALUE are set up by InitRuntimeNumberState
michael@0 1092 * using union jsdpun.
michael@0 1093 */
michael@0 1094 static JSConstDoubleSpec number_constants[] = {
michael@0 1095 {0, "NaN", 0,{0,0,0}},
michael@0 1096 {0, "POSITIVE_INFINITY", 0,{0,0,0}},
michael@0 1097 {0, "NEGATIVE_INFINITY", 0,{0,0,0}},
michael@0 1098 {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}},
michael@0 1099 {0, "MIN_VALUE", 0,{0,0,0}},
michael@0 1100 /* ES6 (April 2014 draft) 20.1.2.6 */
michael@0 1101 {9007199254740991, "MAX_SAFE_INTEGER", 0,{0,0,0}},
michael@0 1102 /* ES6 (April 2014 draft) 20.1.2.10 */
michael@0 1103 {-9007199254740991, "MIN_SAFE_INTEGER", 0,{0,0,0}},
michael@0 1104 /* ES6 (May 2013 draft) 15.7.3.7 */
michael@0 1105 {2.2204460492503130808472633361816e-16, "EPSILON", 0,{0,0,0}},
michael@0 1106 {0,0,0,{0,0,0}}
michael@0 1107 };
michael@0 1108
michael@0 1109 #if (defined __GNUC__ && defined __i386__) || \
michael@0 1110 (defined __SUNPRO_CC && defined __i386)
michael@0 1111
michael@0 1112 /*
michael@0 1113 * Set the exception mask to mask all exceptions and set the FPU precision
michael@0 1114 * to 53 bit mantissa (64 bit doubles).
michael@0 1115 */
michael@0 1116 static inline void FIX_FPU() {
michael@0 1117 short control;
michael@0 1118 asm("fstcw %0" : "=m" (control) : );
michael@0 1119 control &= ~0x300; // Lower bits 8 and 9 (precision control).
michael@0 1120 control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
michael@0 1121 asm("fldcw %0" : : "m" (control) );
michael@0 1122 }
michael@0 1123
michael@0 1124 #else
michael@0 1125
michael@0 1126 #define FIX_FPU() ((void)0)
michael@0 1127
michael@0 1128 #endif
michael@0 1129
michael@0 1130 bool
michael@0 1131 js::InitRuntimeNumberState(JSRuntime *rt)
michael@0 1132 {
michael@0 1133 FIX_FPU();
michael@0 1134
michael@0 1135 /*
michael@0 1136 * Our NaN must be one particular canonical value, because we rely on NaN
michael@0 1137 * encoding for our value representation. See Value.h.
michael@0 1138 */
michael@0 1139 number_constants[NC_NaN].dval = GenericNaN();
michael@0 1140
michael@0 1141 number_constants[NC_POSITIVE_INFINITY].dval = mozilla::PositiveInfinity<double>();
michael@0 1142 number_constants[NC_NEGATIVE_INFINITY].dval = mozilla::NegativeInfinity<double>();
michael@0 1143
michael@0 1144 number_constants[NC_MIN_VALUE].dval = MinNumberValue<double>();
michael@0 1145
michael@0 1146 // XXX If EXPOSE_INTL_API becomes true all the time at some point,
michael@0 1147 // js::InitRuntimeNumberState is no longer fallible, and we should
michael@0 1148 // change its return type.
michael@0 1149 #if !EXPOSE_INTL_API
michael@0 1150 /* Copy locale-specific separators into the runtime strings. */
michael@0 1151 const char *thousandsSeparator, *decimalPoint, *grouping;
michael@0 1152 #ifdef HAVE_LOCALECONV
michael@0 1153 struct lconv *locale = localeconv();
michael@0 1154 thousandsSeparator = locale->thousands_sep;
michael@0 1155 decimalPoint = locale->decimal_point;
michael@0 1156 grouping = locale->grouping;
michael@0 1157 #else
michael@0 1158 thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
michael@0 1159 decimalPoint = getenv("LOCALE_DECIMAL_POINT");
michael@0 1160 grouping = getenv("LOCALE_GROUPING");
michael@0 1161 #endif
michael@0 1162 if (!thousandsSeparator)
michael@0 1163 thousandsSeparator = "'";
michael@0 1164 if (!decimalPoint)
michael@0 1165 decimalPoint = ".";
michael@0 1166 if (!grouping)
michael@0 1167 grouping = "\3\0";
michael@0 1168
michael@0 1169 /*
michael@0 1170 * We use single malloc to get the memory for all separator and grouping
michael@0 1171 * strings.
michael@0 1172 */
michael@0 1173 size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
michael@0 1174 size_t decimalPointSize = strlen(decimalPoint) + 1;
michael@0 1175 size_t groupingSize = strlen(grouping) + 1;
michael@0 1176
michael@0 1177 char *storage = js_pod_malloc<char>(thousandsSeparatorSize +
michael@0 1178 decimalPointSize +
michael@0 1179 groupingSize);
michael@0 1180 if (!storage)
michael@0 1181 return false;
michael@0 1182
michael@0 1183 js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
michael@0 1184 rt->thousandsSeparator = storage;
michael@0 1185 storage += thousandsSeparatorSize;
michael@0 1186
michael@0 1187 js_memcpy(storage, decimalPoint, decimalPointSize);
michael@0 1188 rt->decimalSeparator = storage;
michael@0 1189 storage += decimalPointSize;
michael@0 1190
michael@0 1191 js_memcpy(storage, grouping, groupingSize);
michael@0 1192 rt->numGrouping = grouping;
michael@0 1193 #endif /* !EXPOSE_INTL_API */
michael@0 1194 return true;
michael@0 1195 }
michael@0 1196
michael@0 1197 #if !EXPOSE_INTL_API
michael@0 1198 void
michael@0 1199 js::FinishRuntimeNumberState(JSRuntime *rt)
michael@0 1200 {
michael@0 1201 /*
michael@0 1202 * The free also releases the memory for decimalSeparator and numGrouping
michael@0 1203 * strings.
michael@0 1204 */
michael@0 1205 char *storage = const_cast<char *>(rt->thousandsSeparator);
michael@0 1206 js_free(storage);
michael@0 1207 }
michael@0 1208 #endif
michael@0 1209
michael@0 1210 JSObject *
michael@0 1211 js_InitNumberClass(JSContext *cx, HandleObject obj)
michael@0 1212 {
michael@0 1213 JS_ASSERT(obj->isNative());
michael@0 1214
michael@0 1215 /* XXX must do at least once per new thread, so do it per JSContext... */
michael@0 1216 FIX_FPU();
michael@0 1217
michael@0 1218 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
michael@0 1219
michael@0 1220 RootedObject numberProto(cx, global->createBlankPrototype(cx, &NumberObject::class_));
michael@0 1221 if (!numberProto)
michael@0 1222 return nullptr;
michael@0 1223 numberProto->as<NumberObject>().setPrimitiveValue(0);
michael@0 1224
michael@0 1225 RootedFunction ctor(cx);
michael@0 1226 ctor = global->createConstructor(cx, Number, cx->names().Number, 1);
michael@0 1227 if (!ctor)
michael@0 1228 return nullptr;
michael@0 1229
michael@0 1230 if (!LinkConstructorAndPrototype(cx, ctor, numberProto))
michael@0 1231 return nullptr;
michael@0 1232
michael@0 1233 /* Add numeric constants (MAX_VALUE, NaN, &c.) to the Number constructor. */
michael@0 1234 if (!JS_DefineConstDoubles(cx, ctor, number_constants))
michael@0 1235 return nullptr;
michael@0 1236
michael@0 1237 if (!DefinePropertiesAndBrand(cx, ctor, nullptr, number_static_methods))
michael@0 1238 return nullptr;
michael@0 1239
michael@0 1240 if (!DefinePropertiesAndBrand(cx, numberProto, nullptr, number_methods))
michael@0 1241 return nullptr;
michael@0 1242
michael@0 1243 if (!JS_DefineFunctions(cx, global, number_functions))
michael@0 1244 return nullptr;
michael@0 1245
michael@0 1246 RootedValue valueNaN(cx, cx->runtime()->NaNValue);
michael@0 1247 RootedValue valueInfinity(cx, cx->runtime()->positiveInfinityValue);
michael@0 1248
michael@0 1249 /* ES5 15.1.1.1, 15.1.1.2 */
michael@0 1250 if (!DefineNativeProperty(cx, global, cx->names().NaN, valueNaN,
michael@0 1251 JS_PropertyStub, JS_StrictPropertyStub,
michael@0 1252 JSPROP_PERMANENT | JSPROP_READONLY) ||
michael@0 1253 !DefineNativeProperty(cx, global, cx->names().Infinity, valueInfinity,
michael@0 1254 JS_PropertyStub, JS_StrictPropertyStub,
michael@0 1255 JSPROP_PERMANENT | JSPROP_READONLY))
michael@0 1256 {
michael@0 1257 return nullptr;
michael@0 1258 }
michael@0 1259
michael@0 1260 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Number, ctor, numberProto))
michael@0 1261 return nullptr;
michael@0 1262
michael@0 1263 return numberProto;
michael@0 1264 }
michael@0 1265
michael@0 1266 static char *
michael@0 1267 FracNumberToCString(ThreadSafeContext *cx, ToCStringBuf *cbuf, double d, int base = 10)
michael@0 1268 {
michael@0 1269 #ifdef DEBUG
michael@0 1270 {
michael@0 1271 int32_t _;
michael@0 1272 JS_ASSERT(!mozilla::NumberIsInt32(d, &_));
michael@0 1273 }
michael@0 1274 #endif
michael@0 1275
michael@0 1276 char* numStr;
michael@0 1277 if (base == 10) {
michael@0 1278 /*
michael@0 1279 * This is V8's implementation of the algorithm described in the
michael@0 1280 * following paper:
michael@0 1281 *
michael@0 1282 * Printing floating-point numbers quickly and accurately with integers.
michael@0 1283 * Florian Loitsch, PLDI 2010.
michael@0 1284 */
michael@0 1285 const double_conversion::DoubleToStringConverter &converter
michael@0 1286 = double_conversion::DoubleToStringConverter::EcmaScriptConverter();
michael@0 1287 double_conversion::StringBuilder builder(cbuf->sbuf, cbuf->sbufSize);
michael@0 1288 converter.ToShortest(d, &builder);
michael@0 1289 numStr = builder.Finalize();
michael@0 1290 } else {
michael@0 1291 numStr = cbuf->dbuf = js_dtobasestr(cx->dtoaState(), base, d);
michael@0 1292 }
michael@0 1293 return numStr;
michael@0 1294 }
michael@0 1295
michael@0 1296 char *
michael@0 1297 js::NumberToCString(JSContext *cx, ToCStringBuf *cbuf, double d, int base/* = 10*/)
michael@0 1298 {
michael@0 1299 int32_t i;
michael@0 1300 size_t len;
michael@0 1301 return mozilla::NumberIsInt32(d, &i)
michael@0 1302 ? Int32ToCString(cbuf, i, &len, base)
michael@0 1303 : FracNumberToCString(cx, cbuf, d, base);
michael@0 1304 }
michael@0 1305
michael@0 1306 template <AllowGC allowGC>
michael@0 1307 static JSString * JS_FASTCALL
michael@0 1308 js_NumberToStringWithBase(ThreadSafeContext *cx, double d, int base)
michael@0 1309 {
michael@0 1310 ToCStringBuf cbuf;
michael@0 1311 char *numStr;
michael@0 1312
michael@0 1313 /*
michael@0 1314 * Caller is responsible for error reporting. When called from trace,
michael@0 1315 * returning nullptr here will cause us to fall of trace and then retry
michael@0 1316 * from the interpreter (which will report the error).
michael@0 1317 */
michael@0 1318 if (base < 2 || base > 36)
michael@0 1319 return nullptr;
michael@0 1320
michael@0 1321 JSCompartment *comp = cx->isExclusiveContext()
michael@0 1322 ? cx->asExclusiveContext()->compartment()
michael@0 1323 : nullptr;
michael@0 1324
michael@0 1325 int32_t i;
michael@0 1326 if (mozilla::NumberIsInt32(d, &i)) {
michael@0 1327 if (base == 10 && StaticStrings::hasInt(i))
michael@0 1328 return cx->staticStrings().getInt(i);
michael@0 1329 if (unsigned(i) < unsigned(base)) {
michael@0 1330 if (i < 10)
michael@0 1331 return cx->staticStrings().getInt(i);
michael@0 1332 jschar c = 'a' + i - 10;
michael@0 1333 JS_ASSERT(StaticStrings::hasUnit(c));
michael@0 1334 return cx->staticStrings().getUnit(c);
michael@0 1335 }
michael@0 1336
michael@0 1337 if (comp) {
michael@0 1338 if (JSFlatString *str = comp->dtoaCache.lookup(base, d))
michael@0 1339 return str;
michael@0 1340 }
michael@0 1341
michael@0 1342 size_t len;
michael@0 1343 numStr = Int32ToCString(&cbuf, i, &len, base);
michael@0 1344 JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
michael@0 1345 } else {
michael@0 1346 if (comp) {
michael@0 1347 if (JSFlatString *str = comp->dtoaCache.lookup(base, d))
michael@0 1348 return str;
michael@0 1349 }
michael@0 1350
michael@0 1351 numStr = FracNumberToCString(cx, &cbuf, d, base);
michael@0 1352 if (!numStr) {
michael@0 1353 js_ReportOutOfMemory(cx);
michael@0 1354 return nullptr;
michael@0 1355 }
michael@0 1356 JS_ASSERT_IF(base == 10,
michael@0 1357 !cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
michael@0 1358 JS_ASSERT_IF(base != 10,
michael@0 1359 cbuf.dbuf && cbuf.dbuf == numStr);
michael@0 1360 }
michael@0 1361
michael@0 1362 JSFlatString *s = js_NewStringCopyZ<allowGC>(cx, numStr);
michael@0 1363
michael@0 1364 if (comp)
michael@0 1365 comp->dtoaCache.cache(base, d, s);
michael@0 1366
michael@0 1367 return s;
michael@0 1368 }
michael@0 1369
michael@0 1370 template <AllowGC allowGC>
michael@0 1371 JSString *
michael@0 1372 js::NumberToString(ThreadSafeContext *cx, double d)
michael@0 1373 {
michael@0 1374 return js_NumberToStringWithBase<allowGC>(cx, d, 10);
michael@0 1375 }
michael@0 1376
michael@0 1377 template JSString *
michael@0 1378 js::NumberToString<CanGC>(ThreadSafeContext *cx, double d);
michael@0 1379
michael@0 1380 template JSString *
michael@0 1381 js::NumberToString<NoGC>(ThreadSafeContext *cx, double d);
michael@0 1382
michael@0 1383 JSAtom *
michael@0 1384 js::NumberToAtom(ExclusiveContext *cx, double d)
michael@0 1385 {
michael@0 1386 int32_t si;
michael@0 1387 if (mozilla::NumberIsInt32(d, &si))
michael@0 1388 return Int32ToAtom(cx, si);
michael@0 1389
michael@0 1390 if (JSFlatString *str = LookupDtoaCache(cx, d))
michael@0 1391 return AtomizeString(cx, str);
michael@0 1392
michael@0 1393 ToCStringBuf cbuf;
michael@0 1394 char *numStr = FracNumberToCString(cx, &cbuf, d);
michael@0 1395 if (!numStr) {
michael@0 1396 js_ReportOutOfMemory(cx);
michael@0 1397 return nullptr;
michael@0 1398 }
michael@0 1399 JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
michael@0 1400
michael@0 1401 size_t length = strlen(numStr);
michael@0 1402 JSAtom *atom = Atomize(cx, numStr, length);
michael@0 1403 if (!atom)
michael@0 1404 return nullptr;
michael@0 1405
michael@0 1406 CacheNumber(cx, d, atom);
michael@0 1407
michael@0 1408 return atom;
michael@0 1409 }
michael@0 1410
michael@0 1411 JSFlatString *
michael@0 1412 js::NumberToString(JSContext *cx, double d)
michael@0 1413 {
michael@0 1414 if (JSString *str = js_NumberToStringWithBase<CanGC>(cx, d, 10))
michael@0 1415 return &str->asFlat();
michael@0 1416 return nullptr;
michael@0 1417 }
michael@0 1418
michael@0 1419 JSFlatString *
michael@0 1420 js::IndexToString(JSContext *cx, uint32_t index)
michael@0 1421 {
michael@0 1422 if (StaticStrings::hasUint(index))
michael@0 1423 return cx->staticStrings().getUint(index);
michael@0 1424
michael@0 1425 JSCompartment *c = cx->compartment();
michael@0 1426 if (JSFlatString *str = c->dtoaCache.lookup(10, index))
michael@0 1427 return str;
michael@0 1428
michael@0 1429 JSFatInlineString *str = js_NewGCFatInlineString<CanGC>(cx);
michael@0 1430 if (!str)
michael@0 1431 return nullptr;
michael@0 1432
michael@0 1433 jschar buffer[JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1];
michael@0 1434 RangedPtr<jschar> end(buffer + JSFatInlineString::MAX_FAT_INLINE_LENGTH,
michael@0 1435 buffer, JSFatInlineString::MAX_FAT_INLINE_LENGTH + 1);
michael@0 1436 *end = '\0';
michael@0 1437 RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
michael@0 1438
michael@0 1439 jschar *dst = str->init(end - start);
michael@0 1440 PodCopy(dst, start.get(), end - start + 1);
michael@0 1441
michael@0 1442 c->dtoaCache.cache(10, index, str);
michael@0 1443 return str;
michael@0 1444 }
michael@0 1445
michael@0 1446 bool JS_FASTCALL
michael@0 1447 js::NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
michael@0 1448 {
michael@0 1449 /* Convert to C-string. */
michael@0 1450 ToCStringBuf cbuf;
michael@0 1451 const char *cstr;
michael@0 1452 size_t cstrlen;
michael@0 1453 if (v.isInt32()) {
michael@0 1454 cstr = Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
michael@0 1455 JS_ASSERT(cstrlen == strlen(cstr));
michael@0 1456 } else {
michael@0 1457 cstr = NumberToCString(cx, &cbuf, v.toDouble());
michael@0 1458 if (!cstr) {
michael@0 1459 JS_ReportOutOfMemory(cx);
michael@0 1460 return false;
michael@0 1461 }
michael@0 1462 cstrlen = strlen(cstr);
michael@0 1463 }
michael@0 1464
michael@0 1465 /*
michael@0 1466 * Inflate to jschar string. The input C-string characters are < 127, so
michael@0 1467 * even if jschars are UTF-8, all chars should map to one jschar.
michael@0 1468 */
michael@0 1469 JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
michael@0 1470 return sb.appendInflated(cstr, cstrlen);
michael@0 1471 }
michael@0 1472
michael@0 1473 static bool
michael@0 1474 CharsToNumber(ThreadSafeContext *cx, const jschar *chars, size_t length, double *result)
michael@0 1475 {
michael@0 1476 if (length == 1) {
michael@0 1477 jschar c = chars[0];
michael@0 1478 if ('0' <= c && c <= '9')
michael@0 1479 *result = c - '0';
michael@0 1480 else if (unicode::IsSpace(c))
michael@0 1481 *result = 0.0;
michael@0 1482 else
michael@0 1483 *result = GenericNaN();
michael@0 1484 return true;
michael@0 1485 }
michael@0 1486
michael@0 1487 const jschar *end = chars + length;
michael@0 1488 const jschar *bp = SkipSpace(chars, end);
michael@0 1489
michael@0 1490 /* ECMA doesn't allow signed hex numbers (bug 273467). */
michael@0 1491 if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) {
michael@0 1492 /*
michael@0 1493 * It's probably a hex number. Accept if there's at least one hex
michael@0 1494 * digit after the 0x, and if no non-whitespace characters follow all
michael@0 1495 * the hex digits.
michael@0 1496 */
michael@0 1497 const jschar *endptr;
michael@0 1498 double d;
michael@0 1499 if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) ||
michael@0 1500 endptr == bp + 2 ||
michael@0 1501 SkipSpace(endptr, end) != end)
michael@0 1502 {
michael@0 1503 *result = GenericNaN();
michael@0 1504 } else {
michael@0 1505 *result = d;
michael@0 1506 }
michael@0 1507 return true;
michael@0 1508 }
michael@0 1509
michael@0 1510 /*
michael@0 1511 * Note that ECMA doesn't treat a string beginning with a '0' as
michael@0 1512 * an octal number here. This works because all such numbers will
michael@0 1513 * be interpreted as decimal by js_strtod. Also, any hex numbers
michael@0 1514 * that have made it here (which can only be negative ones) will
michael@0 1515 * be treated as 0 without consuming the 'x' by js_strtod.
michael@0 1516 */
michael@0 1517 const jschar *ep;
michael@0 1518 double d;
michael@0 1519 if (!js_strtod(cx, bp, end, &ep, &d)) {
michael@0 1520 *result = GenericNaN();
michael@0 1521 return false;
michael@0 1522 }
michael@0 1523
michael@0 1524 if (SkipSpace(ep, end) != end)
michael@0 1525 *result = GenericNaN();
michael@0 1526 else
michael@0 1527 *result = d;
michael@0 1528
michael@0 1529 return true;
michael@0 1530 }
michael@0 1531
michael@0 1532 bool
michael@0 1533 js::StringToNumber(ThreadSafeContext *cx, JSString *str, double *result)
michael@0 1534 {
michael@0 1535 ScopedThreadSafeStringInspector inspector(str);
michael@0 1536 if (!inspector.ensureChars(cx))
michael@0 1537 return false;
michael@0 1538
michael@0 1539 return CharsToNumber(cx, inspector.chars(), str->length(), result);
michael@0 1540 }
michael@0 1541
michael@0 1542 bool
michael@0 1543 js::NonObjectToNumberSlow(ThreadSafeContext *cx, Value v, double *out)
michael@0 1544 {
michael@0 1545 JS_ASSERT(!v.isNumber());
michael@0 1546 JS_ASSERT(!v.isObject());
michael@0 1547
michael@0 1548 if (v.isString())
michael@0 1549 return StringToNumber(cx, v.toString(), out);
michael@0 1550 if (v.isBoolean()) {
michael@0 1551 *out = v.toBoolean() ? 1.0 : 0.0;
michael@0 1552 return true;
michael@0 1553 }
michael@0 1554 if (v.isNull()) {
michael@0 1555 *out = 0.0;
michael@0 1556 return true;
michael@0 1557 }
michael@0 1558
michael@0 1559 JS_ASSERT(v.isUndefined());
michael@0 1560 *out = GenericNaN();
michael@0 1561 return true;
michael@0 1562 }
michael@0 1563
michael@0 1564 #if defined(_MSC_VER)
michael@0 1565 # pragma optimize("g", off)
michael@0 1566 #endif
michael@0 1567
michael@0 1568 bool
michael@0 1569 js::ToNumberSlow(ExclusiveContext *cx, Value v, double *out)
michael@0 1570 {
michael@0 1571 JS_ASSERT(!v.isNumber());
michael@0 1572 goto skip_int_double;
michael@0 1573 for (;;) {
michael@0 1574 if (v.isNumber()) {
michael@0 1575 *out = v.toNumber();
michael@0 1576 return true;
michael@0 1577 }
michael@0 1578
michael@0 1579 skip_int_double:
michael@0 1580 if (!v.isObject())
michael@0 1581 return NonObjectToNumberSlow(cx, v, out);
michael@0 1582
michael@0 1583 if (!cx->isJSContext())
michael@0 1584 return false;
michael@0 1585
michael@0 1586 RootedValue v2(cx, v);
michael@0 1587 if (!ToPrimitive(cx->asJSContext(), JSTYPE_NUMBER, &v2))
michael@0 1588 return false;
michael@0 1589 v = v2;
michael@0 1590 if (v.isObject())
michael@0 1591 break;
michael@0 1592 }
michael@0 1593
michael@0 1594 *out = GenericNaN();
michael@0 1595 return true;
michael@0 1596 }
michael@0 1597
michael@0 1598 JS_PUBLIC_API(bool)
michael@0 1599 js::ToNumberSlow(JSContext *cx, Value v, double *out)
michael@0 1600 {
michael@0 1601 return ToNumberSlow(static_cast<ExclusiveContext *>(cx), v, out);
michael@0 1602 }
michael@0 1603
michael@0 1604 #if defined(_MSC_VER)
michael@0 1605 # pragma optimize("", on)
michael@0 1606 #endif
michael@0 1607
michael@0 1608 /*
michael@0 1609 * Convert a value to an int64_t, according to the WebIDL rules for long long
michael@0 1610 * conversion. Return converted value in *out on success, false on failure.
michael@0 1611 */
michael@0 1612 JS_PUBLIC_API(bool)
michael@0 1613 js::ToInt64Slow(JSContext *cx, const HandleValue v, int64_t *out)
michael@0 1614 {
michael@0 1615 JS_ASSERT(!v.isInt32());
michael@0 1616 double d;
michael@0 1617 if (v.isDouble()) {
michael@0 1618 d = v.toDouble();
michael@0 1619 } else {
michael@0 1620 if (!ToNumberSlow(cx, v, &d))
michael@0 1621 return false;
michael@0 1622 }
michael@0 1623 *out = ToInt64(d);
michael@0 1624 return true;
michael@0 1625 }
michael@0 1626
michael@0 1627 /*
michael@0 1628 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long
michael@0 1629 * conversion. Return converted value in *out on success, false on failure.
michael@0 1630 */
michael@0 1631 JS_PUBLIC_API(bool)
michael@0 1632 js::ToUint64Slow(JSContext *cx, const HandleValue v, uint64_t *out)
michael@0 1633 {
michael@0 1634 JS_ASSERT(!v.isInt32());
michael@0 1635 double d;
michael@0 1636 if (v.isDouble()) {
michael@0 1637 d = v.toDouble();
michael@0 1638 } else {
michael@0 1639 if (!ToNumberSlow(cx, v, &d))
michael@0 1640 return false;
michael@0 1641 }
michael@0 1642 *out = ToUint64(d);
michael@0 1643 return true;
michael@0 1644 }
michael@0 1645
michael@0 1646 template <typename ContextType,
michael@0 1647 bool (*ToNumberSlowFn)(ContextType *, Value, double *),
michael@0 1648 typename ValueType>
michael@0 1649 static bool
michael@0 1650 ToInt32SlowImpl(ContextType *cx, const ValueType v, int32_t *out)
michael@0 1651 {
michael@0 1652 JS_ASSERT(!v.isInt32());
michael@0 1653 double d;
michael@0 1654 if (v.isDouble()) {
michael@0 1655 d = v.toDouble();
michael@0 1656 } else {
michael@0 1657 if (!ToNumberSlowFn(cx, v, &d))
michael@0 1658 return false;
michael@0 1659 }
michael@0 1660 *out = ToInt32(d);
michael@0 1661 return true;
michael@0 1662 }
michael@0 1663
michael@0 1664 JS_PUBLIC_API(bool)
michael@0 1665 js::ToInt32Slow(JSContext *cx, const HandleValue v, int32_t *out)
michael@0 1666 {
michael@0 1667 return ToInt32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
michael@0 1668 }
michael@0 1669
michael@0 1670 bool
michael@0 1671 js::NonObjectToInt32Slow(ThreadSafeContext *cx, const Value &v, int32_t *out)
michael@0 1672 {
michael@0 1673 return ToInt32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
michael@0 1674 }
michael@0 1675
michael@0 1676 template <typename ContextType,
michael@0 1677 bool (*ToNumberSlowFn)(ContextType *, Value, double *),
michael@0 1678 typename ValueType>
michael@0 1679 static bool
michael@0 1680 ToUint32SlowImpl(ContextType *cx, const ValueType v, uint32_t *out)
michael@0 1681 {
michael@0 1682 JS_ASSERT(!v.isInt32());
michael@0 1683 double d;
michael@0 1684 if (v.isDouble()) {
michael@0 1685 d = v.toDouble();
michael@0 1686 } else {
michael@0 1687 if (!ToNumberSlowFn(cx, v, &d))
michael@0 1688 return false;
michael@0 1689 }
michael@0 1690 *out = ToUint32(d);
michael@0 1691 return true;
michael@0 1692 }
michael@0 1693
michael@0 1694 JS_PUBLIC_API(bool)
michael@0 1695 js::ToUint32Slow(JSContext *cx, const HandleValue v, uint32_t *out)
michael@0 1696 {
michael@0 1697 return ToUint32SlowImpl<JSContext, ToNumberSlow>(cx, v, out);
michael@0 1698 }
michael@0 1699
michael@0 1700 bool
michael@0 1701 js::NonObjectToUint32Slow(ThreadSafeContext *cx, const Value &v, uint32_t *out)
michael@0 1702 {
michael@0 1703 return ToUint32SlowImpl<ThreadSafeContext, NonObjectToNumberSlow>(cx, v, out);
michael@0 1704 }
michael@0 1705
michael@0 1706 JS_PUBLIC_API(bool)
michael@0 1707 js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
michael@0 1708 {
michael@0 1709 JS_ASSERT(!v.isInt32());
michael@0 1710 double d;
michael@0 1711 if (v.isDouble()) {
michael@0 1712 d = v.toDouble();
michael@0 1713 } else if (!ToNumberSlow(cx, v, &d)) {
michael@0 1714 return false;
michael@0 1715 }
michael@0 1716
michael@0 1717 if (d == 0 || !mozilla::IsFinite(d)) {
michael@0 1718 *out = 0;
michael@0 1719 return true;
michael@0 1720 }
michael@0 1721
michael@0 1722 uint16_t u = (uint16_t) d;
michael@0 1723 if ((double)u == d) {
michael@0 1724 *out = u;
michael@0 1725 return true;
michael@0 1726 }
michael@0 1727
michael@0 1728 bool neg = (d < 0);
michael@0 1729 d = floor(neg ? -d : d);
michael@0 1730 d = neg ? -d : d;
michael@0 1731 unsigned m = JS_BIT(16);
michael@0 1732 d = fmod(d, (double) m);
michael@0 1733 if (d < 0)
michael@0 1734 d += m;
michael@0 1735 *out = (uint16_t) d;
michael@0 1736 return true;
michael@0 1737 }
michael@0 1738
michael@0 1739 bool
michael@0 1740 js_strtod(ThreadSafeContext *cx, const jschar *s, const jschar *send,
michael@0 1741 const jschar **ep, double *dp)
michael@0 1742 {
michael@0 1743 size_t i;
michael@0 1744 char cbuf[32];
michael@0 1745 char *cstr, *istr, *estr;
michael@0 1746 bool negative;
michael@0 1747 double d;
michael@0 1748
michael@0 1749 const jschar *s1 = SkipSpace(s, send);
michael@0 1750 size_t length = send - s1;
michael@0 1751
michael@0 1752 /* Use cbuf to avoid malloc */
michael@0 1753 if (length >= sizeof cbuf) {
michael@0 1754 cstr = (char *) cx->malloc_(length + 1);
michael@0 1755 if (!cstr)
michael@0 1756 return false;
michael@0 1757 } else {
michael@0 1758 cstr = cbuf;
michael@0 1759 }
michael@0 1760
michael@0 1761 for (i = 0; i != length; i++) {
michael@0 1762 if (s1[i] >> 8)
michael@0 1763 break;
michael@0 1764 cstr[i] = (char)s1[i];
michael@0 1765 }
michael@0 1766 cstr[i] = 0;
michael@0 1767
michael@0 1768 istr = cstr;
michael@0 1769 if ((negative = (*istr == '-')) != 0 || *istr == '+')
michael@0 1770 istr++;
michael@0 1771 if (*istr == 'I' && !strncmp(istr, "Infinity", 8)) {
michael@0 1772 d = negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
michael@0 1773 estr = istr + 8;
michael@0 1774 } else {
michael@0 1775 int err;
michael@0 1776 d = js_strtod_harder(cx->dtoaState(), cstr, &estr, &err);
michael@0 1777 }
michael@0 1778
michael@0 1779 i = estr - cstr;
michael@0 1780 if (cstr != cbuf)
michael@0 1781 js_free(cstr);
michael@0 1782 *ep = i ? s1 + i : s;
michael@0 1783 *dp = d;
michael@0 1784 return true;
michael@0 1785 }

mercurial