~ubuntu-branches/ubuntu/vivid/mozjs24/vivid

« back to all changes in this revision

Viewing changes to js/src/jsnum.cpp

  • Committer: Package Import Robot
  • Author(s): Tim Lunn
  • Date: 2014-02-11 21:55:34 UTC
  • Revision ID: package-import@ubuntu.com-20140211215534-m1zyq5aj59md3y07
Tags: upstream-24.2.0
ImportĀ upstreamĀ versionĀ 24.2.0

Show diffs side-by-side

added added

removed removed

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