2025-02-05 12:40:54 +10:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2018-09-08 18:08:48 +08:00
|
|
|
*
|
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
|
*
|
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
|
*
|
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
|
* License.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
|
* March 31, 1998.
|
|
|
|
|
*
|
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
|
|
|
|
* IBM Corp.
|
|
|
|
|
*
|
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
|
*
|
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* JS number type and wrapper class.
|
|
|
|
|
*/
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifdef XP_OS2
|
|
|
|
|
#define _PC_53 PC_53
|
|
|
|
|
#define _MCW_EM MCW_EM
|
|
|
|
|
#define _MCW_PC MCW_PC
|
2018-09-08 18:08:48 +08:00
|
|
|
#endif
|
|
|
|
|
#include <locale.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include "jstypes.h"
|
2025-02-05 12:40:54 +10:00
|
|
|
#include "jsstdint.h"
|
2025-03-19 21:25:58 +10:00
|
|
|
#include "jsutil.h"
|
2018-09-08 18:08:48 +08:00
|
|
|
#include "jsapi.h"
|
|
|
|
|
#include "jsatom.h"
|
2025-02-05 12:40:54 +10:00
|
|
|
#include "jsbuiltins.h"
|
2018-09-08 18:08:48 +08:00
|
|
|
#include "jscntxt.h"
|
2025-02-03 12:35:24 +10:00
|
|
|
#include "jsversion.h"
|
2018-09-08 18:08:48 +08:00
|
|
|
#include "jsdtoa.h"
|
|
|
|
|
#include "jsgc.h"
|
|
|
|
|
#include "jsinterp.h"
|
|
|
|
|
#include "jsnum.h"
|
|
|
|
|
#include "jsobj.h"
|
|
|
|
|
#include "jsopcode.h"
|
|
|
|
|
#include "jsprf.h"
|
2023-07-12 13:27:26 +10:00
|
|
|
#include "jsscope.h"
|
2018-09-08 18:08:48 +08:00
|
|
|
#include "jsstr.h"
|
2025-03-19 21:25:58 +10:00
|
|
|
#include "jstracer.h"
|
2025-02-05 12:40:54 +10:00
|
|
|
#include "jsvector.h"
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
#include "jsinterpinlines.h"
|
2025-02-05 12:40:54 +10:00
|
|
|
#include "jsobjinlines.h"
|
|
|
|
|
#include "jsstrinlines.h"
|
|
|
|
|
|
|
|
|
|
using namespace js;
|
|
|
|
|
|
|
|
|
|
#ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */
|
|
|
|
|
|
|
|
|
|
JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(uint64_t(-1) == UINT64_MAX);
|
|
|
|
|
|
|
|
|
|
JS_STATIC_ASSERT(INT8_MAX > INT8_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uint8_t(INT8_MAX) + uint8_t(1) == uint8_t(INT8_MIN));
|
|
|
|
|
JS_STATIC_ASSERT(INT16_MAX > INT16_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uint16_t(INT16_MAX) + uint16_t(1) == uint16_t(INT16_MIN));
|
|
|
|
|
JS_STATIC_ASSERT(INT32_MAX > INT32_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uint32_t(INT32_MAX) + uint32_t(1) == uint32_t(INT32_MIN));
|
|
|
|
|
JS_STATIC_ASSERT(INT64_MAX > INT64_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uint64_t(INT64_MAX) + uint64_t(1) == uint64_t(INT64_MIN));
|
|
|
|
|
|
|
|
|
|
JS_STATIC_ASSERT(INTPTR_MAX > INTPTR_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uintptr_t(INTPTR_MAX) + uintptr_t(1) == uintptr_t(INTPTR_MIN));
|
|
|
|
|
JS_STATIC_ASSERT(uintptr_t(-1) == UINTPTR_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(size_t(-1) == SIZE_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(PTRDIFF_MAX > PTRDIFF_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MAX) == PTRDIFF_MAX);
|
|
|
|
|
JS_STATIC_ASSERT(ptrdiff_t(PTRDIFF_MIN) == PTRDIFF_MIN);
|
|
|
|
|
JS_STATIC_ASSERT(uintptr_t(PTRDIFF_MAX) + uintptr_t(1) == uintptr_t(PTRDIFF_MIN));
|
|
|
|
|
|
|
|
|
|
#endif /* JS_HAVE_STDINT_H */
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
/*
|
|
|
|
|
* If we're accumulating a decimal number and the number is >= 2^53, then the
|
|
|
|
|
* fast result from the loop in GetPrefixInteger may be inaccurate. Call
|
|
|
|
|
* js_strtod_harder to get the correct answer.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
ComputeAccurateDecimalInteger(JSContext *cx, const jschar *start, const jschar *end, jsdouble *dp)
|
|
|
|
|
{
|
|
|
|
|
size_t length = end - start;
|
|
|
|
|
char *cstr = static_cast<char *>(cx->malloc(length + 1));
|
|
|
|
|
if (!cstr)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
|
|
|
char c = char(start[i]);
|
|
|
|
|
JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
|
|
|
|
|
cstr[i] = c;
|
|
|
|
|
}
|
|
|
|
|
cstr[length] = 0;
|
|
|
|
|
|
|
|
|
|
char *estr;
|
|
|
|
|
int err = 0;
|
|
|
|
|
*dp = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
|
|
|
|
|
if (err == JS_DTOA_ENOMEM) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
|
cx->free(cstr);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (err == JS_DTOA_ERANGE && *dp == HUGE_VAL)
|
|
|
|
|
*dp = js_PositiveInfinity;
|
|
|
|
|
cx->free(cstr);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class BinaryDigitReader
|
|
|
|
|
{
|
|
|
|
|
const int base; /* Base of number; must be a power of 2 */
|
|
|
|
|
int digit; /* Current digit value in radix given by base */
|
|
|
|
|
int digitMask; /* Mask to extract the next bit from digit */
|
|
|
|
|
const jschar *start; /* Pointer to the remaining digits */
|
|
|
|
|
const jschar *end; /* Pointer to first non-digit */
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
BinaryDigitReader(int base, const jschar *start, const jschar *end)
|
|
|
|
|
: base(base), digit(0), digitMask(0), start(start), end(end)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the next binary digit from the number, or -1 if done. */
|
|
|
|
|
int nextDigit() {
|
|
|
|
|
if (digitMask == 0) {
|
|
|
|
|
if (start == end)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
int c = *start++;
|
|
|
|
|
JS_ASSERT(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
|
|
|
|
|
if ('0' <= c && c <= '9')
|
|
|
|
|
digit = c - '0';
|
|
|
|
|
else if ('a' <= c && c <= 'z')
|
|
|
|
|
digit = c - 'a' + 10;
|
|
|
|
|
else
|
|
|
|
|
digit = c - 'A' + 10;
|
|
|
|
|
digitMask = base >> 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int bit = (digit & digitMask) != 0;
|
|
|
|
|
digitMask >>= 1;
|
|
|
|
|
return bit;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The fast result might also have been inaccurate for power-of-two bases. This
|
|
|
|
|
* happens if the addition in value * 2 + digit causes a round-down to an even
|
|
|
|
|
* least significant mantissa bit when the first dropped bit is a one. If any
|
|
|
|
|
* of the following digits in the number (which haven't been added in yet) are
|
|
|
|
|
* nonzero, then the correct action would have been to round up instead of
|
|
|
|
|
* down. An example occurs when reading the number 0x1000000000000081, which
|
|
|
|
|
* rounds to 0x1000000000000000 instead of 0x1000000000000100.
|
|
|
|
|
*/
|
|
|
|
|
static jsdouble
|
|
|
|
|
ComputeAccurateBinaryBaseInteger(JSContext *cx, const jschar *start, const jschar *end, int base)
|
|
|
|
|
{
|
|
|
|
|
BinaryDigitReader bdr(base, start, end);
|
|
|
|
|
|
|
|
|
|
/* Skip leading zeroes. */
|
|
|
|
|
int bit;
|
|
|
|
|
do {
|
|
|
|
|
bit = bdr.nextDigit();
|
|
|
|
|
} while (bit == 0);
|
|
|
|
|
|
|
|
|
|
JS_ASSERT(bit == 1); // guaranteed by GetPrefixInteger
|
|
|
|
|
|
|
|
|
|
/* Gather the 53 significant bits (including the leading 1). */
|
|
|
|
|
jsdouble value = 1.0;
|
|
|
|
|
for (int j = 52; j > 0; j--) {
|
|
|
|
|
bit = bdr.nextDigit();
|
|
|
|
|
if (bit < 0)
|
|
|
|
|
return value;
|
|
|
|
|
value = value * 2 + bit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* bit2 is the 54th bit (the first dropped from the mantissa). */
|
|
|
|
|
int bit2 = bdr.nextDigit();
|
|
|
|
|
if (bit2 >= 0) {
|
|
|
|
|
jsdouble factor = 2.0;
|
|
|
|
|
int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
|
|
|
|
|
int bit3;
|
|
|
|
|
|
|
|
|
|
while ((bit3 = bdr.nextDigit()) >= 0) {
|
|
|
|
|
sticky |= bit3;
|
|
|
|
|
factor *= 2;
|
|
|
|
|
}
|
|
|
|
|
value += bit2 & (bit | sticky);
|
|
|
|
|
value *= factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace js {
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base,
|
|
|
|
|
const jschar **endp, jsdouble *dp)
|
|
|
|
|
{
|
|
|
|
|
JS_ASSERT(start <= end);
|
|
|
|
|
JS_ASSERT(2 <= base && base <= 36);
|
|
|
|
|
|
|
|
|
|
const jschar *s = start;
|
|
|
|
|
jsdouble d = 0.0;
|
|
|
|
|
for (; s < end; s++) {
|
|
|
|
|
int digit;
|
|
|
|
|
jschar c = *s;
|
|
|
|
|
if ('0' <= c && c <= '9')
|
|
|
|
|
digit = c - '0';
|
|
|
|
|
else if ('a' <= c && c <= 'z')
|
|
|
|
|
digit = c - 'a' + 10;
|
|
|
|
|
else if ('A' <= c && c <= 'Z')
|
|
|
|
|
digit = c - 'A' + 10;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
if (digit >= base)
|
|
|
|
|
break;
|
|
|
|
|
d = d * base + digit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*endp = s;
|
|
|
|
|
*dp = d;
|
|
|
|
|
|
|
|
|
|
/* If we haven't reached the limit of integer precision, we're done. */
|
|
|
|
|
if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Otherwise compute the correct integer from the prefix of valid digits
|
|
|
|
|
* if we're computing for base ten or a power of two. Don't worry about
|
|
|
|
|
* other bases; see 15.1.2.2 step 13.
|
|
|
|
|
*/
|
|
|
|
|
if (base == 10)
|
|
|
|
|
return ComputeAccurateDecimalInteger(cx, start, s, dp);
|
|
|
|
|
if ((base & (base - 1)) == 0)
|
|
|
|
|
*dp = ComputeAccurateBinaryBaseInteger(cx, start, s, base);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace js
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_isNaN(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-02-03 12:35:24 +10:00
|
|
|
if (argc == 0) {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setBoolean(true);
|
2025-02-03 12:35:24 +10:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
jsdouble x;
|
|
|
|
|
if (!ValueToNumber(cx, vp[2], &x))
|
|
|
|
|
return false;
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setBoolean(JSDOUBLE_IS_NaN(x));
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_isFinite(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-02-03 12:35:24 +10:00
|
|
|
if (argc == 0) {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setBoolean(false);
|
2025-02-03 12:35:24 +10:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
jsdouble x;
|
|
|
|
|
if (!ValueToNumber(cx, vp[2], &x))
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setBoolean(JSDOUBLE_IS_FINITE(x));
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_parseFloat(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
|
|
|
|
JSString *str;
|
|
|
|
|
jsdouble d;
|
2023-07-12 13:27:26 +10:00
|
|
|
const jschar *bp, *end, *ep;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-02-03 12:35:24 +10:00
|
|
|
if (argc == 0) {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setDouble(js_NaN);
|
2025-02-03 12:35:24 +10:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
2023-07-12 13:27:26 +10:00
|
|
|
str = js_ValueToString(cx, vp[2]);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!str)
|
|
|
|
|
return JS_FALSE;
|
2025-03-19 21:25:58 +10:00
|
|
|
bp = str->getChars(cx);
|
|
|
|
|
if (!bp)
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
end = bp + str->length();
|
2023-07-12 13:27:26 +10:00
|
|
|
if (!js_strtod(cx, bp, end, &ep, &d))
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
if (ep == bp) {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setDouble(js_NaN);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setNumber(d);
|
|
|
|
|
return JS_TRUE;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
|
static jsdouble FASTCALL
|
|
|
|
|
ParseFloat(JSContext* cx, JSString* str)
|
|
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
|
|
|
|
|
|
|
|
|
|
const jschar *bp = str->getChars(cx);
|
|
|
|
|
if (!bp) {
|
|
|
|
|
SetBuiltinError(tm);
|
|
|
|
|
return js_NaN;
|
|
|
|
|
}
|
|
|
|
|
const jschar *end = bp + str->length();
|
2025-02-05 12:40:54 +10:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
const jschar *ep;
|
|
|
|
|
double d;
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!js_strtod(cx, bp, end, &ep, &d) || ep == bp)
|
|
|
|
|
return js_NaN;
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
static bool
|
|
|
|
|
ParseIntStringHelper(JSContext *cx, const jschar *ws, const jschar *end, int maybeRadix,
|
|
|
|
|
bool stripPrefix, jsdouble *dp)
|
|
|
|
|
{
|
|
|
|
|
JS_ASSERT(maybeRadix == 0 || (2 <= maybeRadix && maybeRadix <= 36));
|
|
|
|
|
JS_ASSERT(ws <= end);
|
|
|
|
|
|
|
|
|
|
const jschar *s = js_SkipWhiteSpace(ws, end);
|
|
|
|
|
JS_ASSERT(ws <= s);
|
|
|
|
|
JS_ASSERT(s <= end);
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 steps 3-4. */
|
|
|
|
|
bool negative = (s != end && s[0] == '-');
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 step 5. */
|
|
|
|
|
if (s != end && (s[0] == '-' || s[0] == '+'))
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 step 9. */
|
|
|
|
|
int radix = maybeRadix;
|
|
|
|
|
if (radix == 0) {
|
|
|
|
|
if (end - s >= 2 && s[0] == '0' && (s[1] != 'x' && s[1] != 'X')) {
|
|
|
|
|
/*
|
|
|
|
|
* Non-standard: ES5 requires that parseInt interpret leading-zero
|
|
|
|
|
* strings not starting with "0x" or "0X" as decimal (absent an
|
|
|
|
|
* explicitly specified non-zero radix), but we continue to
|
|
|
|
|
* interpret such strings as octal, as per ES3 and web practice.
|
|
|
|
|
*/
|
|
|
|
|
radix = 8;
|
|
|
|
|
} else {
|
|
|
|
|
radix = 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 step 10. */
|
|
|
|
|
if (stripPrefix) {
|
|
|
|
|
if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
|
|
|
|
|
s += 2;
|
|
|
|
|
radix = 16;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 steps 11-14. */
|
|
|
|
|
const jschar *actualEnd;
|
|
|
|
|
if (!GetPrefixInteger(cx, s, end, radix, &actualEnd, dp))
|
|
|
|
|
return false;
|
|
|
|
|
if (s == actualEnd)
|
|
|
|
|
*dp = js_NaN;
|
|
|
|
|
else if (negative)
|
|
|
|
|
*dp = -*dp;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jsdouble
|
|
|
|
|
ParseIntDoubleHelper(jsdouble d)
|
|
|
|
|
{
|
|
|
|
|
if (!JSDOUBLE_IS_FINITE(d))
|
|
|
|
|
return js_NaN;
|
|
|
|
|
if (d > 0)
|
|
|
|
|
return floor(d);
|
|
|
|
|
if (d < 0)
|
|
|
|
|
return -floor(-d);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
/* See ECMA 15.1.2.2. */
|
2025-02-05 12:40:54 +10:00
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_parseInt(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
/* Fast paths and exceptional cases. */
|
2025-02-03 12:35:24 +10:00
|
|
|
if (argc == 0) {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setDouble(js_NaN);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (argc == 1 || (vp[3].isInt32() && (vp[3].toInt32() == 0 || vp[3].toInt32() == 10))) {
|
|
|
|
|
if (vp[2].isInt32()) {
|
|
|
|
|
*vp = vp[2];
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (vp[2].isDouble()) {
|
|
|
|
|
vp->setDouble(ParseIntDoubleHelper(vp[2].toDouble()));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-02-03 12:35:24 +10:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
|
|
|
|
|
/* Step 1. */
|
|
|
|
|
JSString *inputString = js_ValueToString(cx, vp[2]);
|
|
|
|
|
if (!inputString)
|
|
|
|
|
return false;
|
|
|
|
|
vp[2].setString(inputString);
|
|
|
|
|
|
|
|
|
|
/* 15.1.2.2 steps 6-8. */
|
|
|
|
|
bool stripPrefix = true;
|
|
|
|
|
int32_t radix = 0;
|
2018-09-08 18:08:48 +08:00
|
|
|
if (argc > 1) {
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!ValueToECMAInt32(cx, vp[3], &radix))
|
2025-03-19 21:25:58 +10:00
|
|
|
return false;
|
|
|
|
|
if (radix != 0) {
|
|
|
|
|
if (radix < 2 || radix > 36) {
|
|
|
|
|
vp->setDouble(js_NaN);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (radix != 16)
|
|
|
|
|
stripPrefix = false;
|
|
|
|
|
}
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
/* Steps 2-5, 9-14. */
|
|
|
|
|
const jschar *ws = inputString->getChars(cx);
|
|
|
|
|
if (!ws)
|
|
|
|
|
return false;
|
|
|
|
|
const jschar *end = ws + inputString->length();
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
jsdouble number;
|
|
|
|
|
if (!ParseIntStringHelper(cx, ws, end, radix, stripPrefix, &number))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* Step 15. */
|
|
|
|
|
vp->setNumber(number);
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
|
static jsdouble FASTCALL
|
|
|
|
|
ParseInt(JSContext* cx, JSString* str)
|
|
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
|
|
|
|
|
|
|
|
|
|
const jschar *start = str->getChars(cx);
|
|
|
|
|
if (!start) {
|
|
|
|
|
SetBuiltinError(tm);
|
|
|
|
|
return js_NaN;
|
|
|
|
|
}
|
|
|
|
|
const jschar *end = start + str->length();
|
2025-02-05 12:40:54 +10:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
jsdouble d;
|
|
|
|
|
if (!ParseIntStringHelper(cx, start, end, 0, true, &d)) {
|
|
|
|
|
SetBuiltinError(tm);
|
2025-02-05 12:40:54 +10:00
|
|
|
return js_NaN;
|
2025-03-19 21:25:58 +10:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static jsdouble FASTCALL
|
|
|
|
|
ParseIntDouble(jsdouble d)
|
|
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
return ParseIntDoubleHelper(d);
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
const char js_Infinity_str[] = "Infinity";
|
|
|
|
|
const char js_NaN_str[] = "NaN";
|
|
|
|
|
const char js_isNaN_str[] = "isNaN";
|
|
|
|
|
const char js_isFinite_str[] = "isFinite";
|
|
|
|
|
const char js_parseFloat_str[] = "parseFloat";
|
|
|
|
|
const char js_parseInt_str[] = "parseInt";
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
|
|
|
|
|
|
JS_DEFINE_TRCINFO_2(num_parseInt,
|
2025-03-19 21:25:58 +10:00
|
|
|
(2, (static, DOUBLE_FAIL, ParseInt, CONTEXT, STRING,1, nanojit::ACCSET_NONE)),
|
|
|
|
|
(1, (static, DOUBLE, ParseIntDouble, DOUBLE, 1, nanojit::ACCSET_NONE)))
|
2025-02-05 12:40:54 +10:00
|
|
|
|
|
|
|
|
JS_DEFINE_TRCINFO_1(num_parseFloat,
|
2025-03-19 21:25:58 +10:00
|
|
|
(2, (static, DOUBLE_FAIL, ParseFloat, CONTEXT, STRING, 1, nanojit::ACCSET_NONE)))
|
2025-02-05 12:40:54 +10:00
|
|
|
|
|
|
|
|
#endif /* JS_TRACER */
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
static JSFunctionSpec number_functions[] = {
|
2025-02-05 12:40:54 +10:00
|
|
|
JS_FN(js_isNaN_str, num_isNaN, 1,0),
|
|
|
|
|
JS_FN(js_isFinite_str, num_isFinite, 1,0),
|
|
|
|
|
JS_TN(js_parseFloat_str, num_parseFloat, 1,0, &num_parseFloat_trcinfo),
|
|
|
|
|
JS_TN(js_parseInt_str, num_parseInt, 2,0, &num_parseInt_trcinfo),
|
2023-07-12 13:27:26 +10:00
|
|
|
JS_FS_END
|
2018-09-08 18:08:48 +08:00
|
|
|
};
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
Class js_NumberClass = {
|
Numerous changes with focus on feature and stability parity across Windows, Linux and MacOS platforms
Added new Makefile that handles compiling UOX3 and Spidermonkey on both Linux and MacOS (punt)
Added VS solution (SpiderMonkey.sln) and VC++ project files for compiling SpiderMonkey on Windows (Xuri)
Updated SpiderMonkey from v1.6.0 to v1.7.0
Added optimization flag -O2 for release build in CMakeLists.txt in project root (Xuri)
Added StringUtility file with general common string manipulation functions (punt)
Updated RandomNum function to use a "seedless" random number generator from C++11 instead of the old C rand() function. (punt)
Overall code cleanup to remove/replace platform-dependent code (punt)
Replaced potentially unsafe usage of C string stuff like sprintf, vsprintf, vsnprintf, strncat, strcpy and strlen throughout the code with calls to a format() function containing only a single, safe use of vsnprintf, ensuring there's a single place of failure if there's anything to fix and and making it easy to potentially replace this with std::format from C++20 when the time comes. (punt)
Replaced usage of char in many places with std::string (punt)
Started process of replacing UString usage with functions provided through StringUtility instead (punt)
Replaced platform specific fileIO handling in Windows/Linux with cross-platform C++17 standard std::filesystem (punt)
Replaced platform specific time handling by using cross-platform chrono library (punt)
Removed UOX namespace to reduce complexity (punt)
Elimitated template code approach to singletons for more modern c++ constructs, removing dependices in the process (punt)
Removed ODBC support; it hasn't been touched since the initial implementation in 2008, and there is a lack of someone to maintain the code. (punt)
Removed some platform specific files like uoxlinux.h, which is no longer required (punt)
Removed legacy crash protection code, and removed support for cluox (punt)
Removed legacy "support" for Borland compiler (punt)
Removed legacy VC++ 6 Workspace/Project files, VS2005 Solution/Project files, as these are no longer supported (Xuri)
Removed legacy BUILD folder with outdated compilation instructions (Xuri)
Removed old Changelog file (merged into Changelog.txt) (Xuri)
Fixed a pointer bug for items in multis on world load (punt)
Fixed a dictionary related issue that caused segmentation faults on Linux/MacOS (punt)
Fixed an issue with callbacks to JS scripts that caused segmentation faults on Linux/MacOS (punt)
Paths in uox.ini should now load properly on all platforms regardless of whether those paths use slashes or backslashes, and whether or not they end in a slash/backslash. (punt)
Moved to c++17 style of threading, and got rid of threadsafeobject.cpp/h (punt)
Replaced parsing of UOX.INI tags with a system that's more easily maintainable (punt)
Added some new commands to js/commands/custom/misc-cmd.js (Xuri)
cont // Targeted item will be made a container, set to nondecay and movable 2
endfight // Targeted character (and character being fought) will stop fighting
getmulti // Get multiObject for targeted item
finditem // Find item at layer X
movespeed // Set movement speed of target player (0x0 Normal, 0x1 Mounted, 0x2 Slow (walk only), 0x3 Hybrid ("jog"?), 0x4 Frozen)
Added new HTML based documentation in docs folder, and removed many old legacy docs that are either incorporated into this document already or no longer relevant for the current UOX3 version (Xuri)
Co-Authored-By: Charles Kerr <punt1959@users.noreply.github.com>
2020-09-07 18:09:55 +08:00
|
|
|
js_Number_str,
|
2025-02-05 12:40:54 +10:00
|
|
|
JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
|
2025-03-19 21:25:58 +10:00
|
|
|
PropertyStub, /* addProperty */
|
|
|
|
|
PropertyStub, /* delProperty */
|
|
|
|
|
PropertyStub, /* getProperty */
|
|
|
|
|
StrictPropertyStub, /* setProperty */
|
|
|
|
|
EnumerateStub,
|
|
|
|
|
ResolveStub,
|
|
|
|
|
ConvertStub
|
2018-09-08 18:08:48 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
Number(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
/* Sample JS_CALLEE before clobbering. */
|
|
|
|
|
bool isConstructing = IsConstructing(vp);
|
|
|
|
|
|
|
|
|
|
if (argc > 0) {
|
|
|
|
|
if (!ValueToNumber(cx, &vp[2]))
|
|
|
|
|
return false;
|
|
|
|
|
vp[0] = vp[2];
|
2018-09-08 18:08:48 +08:00
|
|
|
} else {
|
2025-03-19 21:25:58 +10:00
|
|
|
vp[0].setInt32(0);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
|
|
|
|
|
if (!isConstructing)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
JSObject *obj = NewBuiltinClassInstance(cx, &js_NumberClass);
|
|
|
|
|
if (!obj)
|
|
|
|
|
return false;
|
|
|
|
|
obj->setPrimitiveThis(vp[0]);
|
|
|
|
|
vp->setObject(*obj);
|
2025-02-05 12:40:54 +10:00
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if JS_HAS_TOSOURCE
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toSource(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
double d;
|
|
|
|
|
if (!GetPrimitiveThis(cx, vp, &d))
|
|
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf cbuf;
|
|
|
|
|
char *numStr = NumberToCString(cx, &cbuf, d);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!numStr) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
2025-03-19 21:25:58 +10:00
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
|
|
|
|
|
char buf[64];
|
2018-09-08 18:08:48 +08:00
|
|
|
JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr);
|
2025-03-19 21:25:58 +10:00
|
|
|
JSString *str = js_NewStringCopyZ(cx, buf);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!str)
|
2025-03-19 21:25:58 +10:00
|
|
|
return false;
|
|
|
|
|
vp->setString(str);
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf::ToCStringBuf() :dbuf(NULL)
|
|
|
|
|
{
|
|
|
|
|
JS_STATIC_ASSERT(sbufSize >= DTOSTR_STANDARD_BUFFER_SIZE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ToCStringBuf::~ToCStringBuf()
|
|
|
|
|
{
|
|
|
|
|
if (dbuf)
|
|
|
|
|
js_free(dbuf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSString * JS_FASTCALL
|
|
|
|
|
js_IntToString(JSContext *cx, int32 si)
|
|
|
|
|
{
|
|
|
|
|
uint32 ui;
|
|
|
|
|
if (si >= 0) {
|
|
|
|
|
if (si < INT_STRING_LIMIT)
|
|
|
|
|
return JSString::intString(si);
|
|
|
|
|
ui = si;
|
|
|
|
|
} else {
|
|
|
|
|
ui = uint32(-si);
|
|
|
|
|
JS_ASSERT_IF(si == INT32_MIN, ui == uint32(INT32_MAX) + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSCompartment *c = cx->compartment;
|
|
|
|
|
if (JSString *str = c->dtoaCache.lookup(10, si))
|
|
|
|
|
return str;
|
|
|
|
|
|
|
|
|
|
JSShortString *str = js_NewGCShortString(cx);
|
|
|
|
|
if (!str)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* +1, since MAX_SHORT_STRING_LENGTH does not count the null char. */
|
|
|
|
|
JS_STATIC_ASSERT(JSShortString::MAX_SHORT_STRING_LENGTH + 1 >= sizeof("-2147483648"));
|
|
|
|
|
|
|
|
|
|
jschar *end = str->getInlineStorageBeforeInit() + JSShortString::MAX_SHORT_STRING_LENGTH;
|
|
|
|
|
jschar *cp = end;
|
|
|
|
|
*cp = 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
jsuint newui = ui / 10, digit = ui % 10; /* optimizers are our friends */
|
|
|
|
|
*--cp = '0' + digit;
|
|
|
|
|
ui = newui;
|
|
|
|
|
} while (ui != 0);
|
|
|
|
|
|
|
|
|
|
if (si < 0)
|
|
|
|
|
*--cp = '-';
|
|
|
|
|
|
|
|
|
|
str->initAtOffsetInBuffer(cp, end - cp);
|
|
|
|
|
|
|
|
|
|
JSString *ret = str->header();
|
|
|
|
|
c->dtoaCache.cache(10, si, ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a non-NULL pointer to inside cbuf. */
|
2025-02-05 12:40:54 +10:00
|
|
|
static char *
|
2025-03-19 21:25:58 +10:00
|
|
|
IntToCString(ToCStringBuf *cbuf, jsint i, jsint base = 10)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
|
|
|
|
char *cp;
|
|
|
|
|
jsuint u;
|
|
|
|
|
|
|
|
|
|
u = (i < 0) ? -i : i;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
cp = cbuf->sbuf + cbuf->sbufSize; /* one past last buffer cell */
|
|
|
|
|
*--cp = '\0'; /* null terminate the string to be */
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Build the string from behind. We use multiply and subtraction
|
|
|
|
|
* instead of modulus because that's much faster.
|
|
|
|
|
*/
|
2025-02-05 12:40:54 +10:00
|
|
|
switch (base) {
|
|
|
|
|
case 10:
|
|
|
|
|
do {
|
|
|
|
|
jsuint newu = u / 10;
|
|
|
|
|
*--cp = (char)(u - newu * 10) + '0';
|
|
|
|
|
u = newu;
|
|
|
|
|
} while (u != 0);
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
do {
|
|
|
|
|
jsuint newu = u / 16;
|
|
|
|
|
*--cp = "0123456789abcdef"[u - newu * 16];
|
|
|
|
|
u = newu;
|
|
|
|
|
} while (u != 0);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
JS_ASSERT(base >= 2 && base <= 36);
|
|
|
|
|
do {
|
|
|
|
|
jsuint newu = u / base;
|
|
|
|
|
*--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
|
|
|
|
|
u = newu;
|
|
|
|
|
} while (u != 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
if (i < 0)
|
|
|
|
|
*--cp = '-';
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(cp >= cbuf->sbuf);
|
2018-09-08 18:08:48 +08:00
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
static JSString * JS_FASTCALL
|
|
|
|
|
js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base);
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toString(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
double d;
|
|
|
|
|
if (!GetPrimitiveThis(cx, vp, &d))
|
|
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
int32_t base = 10;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (argc != 0 && !vp[2].isUndefined()) {
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!ValueToECMAInt32(cx, vp[2], &base))
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
2025-02-05 12:40:54 +10:00
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
if (base < 2 || base > 36) {
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf cbuf;
|
|
|
|
|
char *numStr = IntToCString(&cbuf, base); /* convert the base itself to a string */
|
|
|
|
|
JS_ASSERT(numStr);
|
2018-09-08 18:08:48 +08:00
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,
|
|
|
|
|
numStr);
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
JSString *str = js_NumberToStringWithBase(cx, d, base);
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!str) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setString(str);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toLocaleString(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-02-05 12:40:54 +10:00
|
|
|
size_t thousandsLength, decimalLength;
|
2018-09-08 18:08:48 +08:00
|
|
|
const char *numGrouping, *tmpGroup;
|
|
|
|
|
JSRuntime *rt;
|
2025-03-19 21:25:58 +10:00
|
|
|
JSString *str;
|
2023-07-12 13:27:26 +10:00
|
|
|
const char *num, *end, *tmpSrc;
|
|
|
|
|
char *buf, *tmpDest;
|
|
|
|
|
const char *nint;
|
2025-03-19 21:25:58 +10:00
|
|
|
int digits, buflen, remainder, nrepeat;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create the string, move back to bytes to make string twiddling
|
|
|
|
|
* a bit easier and so we can insert platform charset seperators.
|
|
|
|
|
*/
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!num_toString(cx, 0, vp))
|
2023-07-12 13:27:26 +10:00
|
|
|
return JS_FALSE;
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(vp->isString());
|
|
|
|
|
JSAutoByteString numBytes(cx, vp->toString());
|
|
|
|
|
if (!numBytes)
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
num = numBytes.ptr();
|
2023-07-12 13:27:26 +10:00
|
|
|
if (!num)
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
/*
|
|
|
|
|
* Find the first non-integer value, whether it be a letter as in
|
2025-02-03 12:35:24 +10:00
|
|
|
* 'Infinity', a decimal point, or an 'e' from exponential notation.
|
2023-07-12 13:27:26 +10:00
|
|
|
*/
|
|
|
|
|
nint = num;
|
|
|
|
|
if (*nint == '-')
|
|
|
|
|
nint++;
|
|
|
|
|
while (*nint >= '0' && *nint <= '9')
|
|
|
|
|
nint++;
|
|
|
|
|
digits = nint - num;
|
2018-09-08 18:08:48 +08:00
|
|
|
end = num + digits;
|
2023-07-12 13:27:26 +10:00
|
|
|
if (!digits)
|
|
|
|
|
return JS_TRUE;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
rt = cx->runtime;
|
|
|
|
|
thousandsLength = strlen(rt->thousandsSeparator);
|
|
|
|
|
decimalLength = strlen(rt->decimalSeparator);
|
|
|
|
|
|
|
|
|
|
/* Figure out how long resulting string will be. */
|
2025-03-19 21:25:58 +10:00
|
|
|
buflen = strlen(num);
|
2023-07-12 13:27:26 +10:00
|
|
|
if (*nint == '.')
|
2025-03-19 21:25:58 +10:00
|
|
|
buflen += decimalLength - 1; /* -1 to account for existing '.' */
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
numGrouping = tmpGroup = rt->numGrouping;
|
|
|
|
|
remainder = digits;
|
|
|
|
|
if (*num == '-')
|
|
|
|
|
remainder--;
|
|
|
|
|
|
|
|
|
|
while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
|
|
|
|
|
if (*tmpGroup >= remainder)
|
|
|
|
|
break;
|
2025-03-19 21:25:58 +10:00
|
|
|
buflen += thousandsLength;
|
2018-09-08 18:08:48 +08:00
|
|
|
remainder -= *tmpGroup;
|
|
|
|
|
tmpGroup++;
|
|
|
|
|
}
|
|
|
|
|
if (*tmpGroup == '\0' && *numGrouping != '\0') {
|
|
|
|
|
nrepeat = (remainder - 1) / tmpGroup[-1];
|
2025-03-19 21:25:58 +10:00
|
|
|
buflen += thousandsLength * nrepeat;
|
2018-09-08 18:08:48 +08:00
|
|
|
remainder -= nrepeat * tmpGroup[-1];
|
|
|
|
|
} else {
|
|
|
|
|
nrepeat = 0;
|
|
|
|
|
}
|
|
|
|
|
tmpGroup--;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
buf = (char *)cx->malloc(buflen + 1);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!buf)
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
|
|
tmpDest = buf;
|
|
|
|
|
tmpSrc = num;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
while (*tmpSrc == '-' || remainder--) {
|
|
|
|
|
JS_ASSERT(tmpDest - buf < buflen);
|
2018-09-08 18:08:48 +08:00
|
|
|
*tmpDest++ = *tmpSrc++;
|
2025-03-19 21:25:58 +10:00
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
while (tmpSrc < end) {
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
|
2018-09-08 18:08:48 +08:00
|
|
|
strcpy(tmpDest, rt->thousandsSeparator);
|
|
|
|
|
tmpDest += thousandsLength;
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
|
2018-09-08 18:08:48 +08:00
|
|
|
memcpy(tmpDest, tmpSrc, *tmpGroup);
|
|
|
|
|
tmpDest += *tmpGroup;
|
|
|
|
|
tmpSrc += *tmpGroup;
|
|
|
|
|
if (--nrepeat < 0)
|
|
|
|
|
tmpGroup--;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
if (*nint == '.') {
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
|
2018-09-08 18:08:48 +08:00
|
|
|
strcpy(tmpDest, rt->decimalSeparator);
|
|
|
|
|
tmpDest += decimalLength;
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
|
2023-07-12 13:27:26 +10:00
|
|
|
strcpy(tmpDest, nint + 1);
|
2018-09-08 18:08:48 +08:00
|
|
|
} else {
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
|
2023-07-12 13:27:26 +10:00
|
|
|
strcpy(tmpDest, nint);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) {
|
|
|
|
|
JSBool ok = cx->localeCallbacks->localeToUnicode(cx, buf, Jsvalify(vp));
|
2025-02-05 12:40:54 +10:00
|
|
|
cx->free(buf);
|
2025-03-19 21:25:58 +10:00
|
|
|
return ok;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
str = js_NewStringCopyN(cx, buf, buflen);
|
|
|
|
|
cx->free(buf);
|
|
|
|
|
if (!str)
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
|
|
vp->setString(str);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
JSBool
|
|
|
|
|
js_num_valueOf(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
double d;
|
|
|
|
|
if (!GetPrimitiveThis(cx, vp, &d))
|
|
|
|
|
return false;
|
2023-07-12 13:27:26 +10:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setNumber(d);
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_PRECISION 100
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2023-07-12 13:27:26 +10:00
|
|
|
num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode,
|
|
|
|
|
jsint precisionMin, jsint precisionMax, jsint precisionOffset,
|
2025-03-19 21:25:58 +10:00
|
|
|
uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2023-07-12 13:27:26 +10:00
|
|
|
/* Use MAX_PRECISION+1 because precisionOffset can be 1. */
|
|
|
|
|
char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)];
|
|
|
|
|
char *numStr;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
double d;
|
|
|
|
|
if (!GetPrimitiveThis(cx, vp, &d))
|
|
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
double precision;
|
2023-07-12 13:27:26 +10:00
|
|
|
if (argc == 0) {
|
2018-09-08 18:08:48 +08:00
|
|
|
precision = 0.0;
|
|
|
|
|
oneArgMode = zeroArgMode;
|
|
|
|
|
} else {
|
2025-02-05 12:40:54 +10:00
|
|
|
if (!ValueToNumber(cx, vp[2], &precision))
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
precision = js_DoubleToInteger(precision);
|
|
|
|
|
if (precision < precisionMin || precision > precisionMax) {
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf cbuf;
|
|
|
|
|
numStr = IntToCString(&cbuf, jsint(precision));
|
|
|
|
|
JS_ASSERT(numStr);
|
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, buf, sizeof buf,
|
|
|
|
|
oneArgMode, (jsint)precision + precisionOffset, d);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!numStr) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
JSString *str = js_NewStringCopyZ(cx, numStr);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!str)
|
|
|
|
|
return JS_FALSE;
|
2025-03-19 21:25:58 +10:00
|
|
|
vp->setString(str);
|
2018-09-08 18:08:48 +08:00
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
/*
|
|
|
|
|
* In the following three implementations, we allow a larger range of precision
|
|
|
|
|
* than ECMA requires; this is permitted by ECMA-262.
|
|
|
|
|
*/
|
2018-09-08 18:08:48 +08:00
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toFixed(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2023-07-12 13:27:26 +10:00
|
|
|
return num_to(cx, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0,
|
|
|
|
|
argc, vp);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toExponential(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
return num_to(cx, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1,
|
|
|
|
|
argc, vp);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSBool
|
2025-03-19 21:25:58 +10:00
|
|
|
num_toPrecision(JSContext *cx, uintN argc, Value *vp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
if (argc == 0 || vp[2].isUndefined())
|
2025-02-05 12:40:54 +10:00
|
|
|
return num_toString(cx, 0, vp);
|
2023-07-12 13:27:26 +10:00
|
|
|
return num_to(cx, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0,
|
|
|
|
|
argc, vp);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifdef JS_TRACER
|
|
|
|
|
|
|
|
|
|
JS_DEFINE_TRCINFO_2(num_toString,
|
2025-03-19 21:25:58 +10:00
|
|
|
(2, (extern, STRING_RETRY, js_NumberToString, CONTEXT, THIS_DOUBLE,
|
|
|
|
|
1, nanojit::ACCSET_NONE)),
|
|
|
|
|
(3, (static, STRING_RETRY, js_NumberToStringWithBase, CONTEXT, THIS_DOUBLE, INT32,
|
|
|
|
|
1, nanojit::ACCSET_NONE)))
|
2025-02-05 12:40:54 +10:00
|
|
|
|
|
|
|
|
#endif /* JS_TRACER */
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
static JSFunctionSpec number_methods[] = {
|
|
|
|
|
#if JS_HAS_TOSOURCE
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_FN(js_toSource_str, num_toSource, 0, 0),
|
2018-09-08 18:08:48 +08:00
|
|
|
#endif
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_TN(js_toString_str, num_toString, 1, 0, &num_toString_trcinfo),
|
|
|
|
|
JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0),
|
|
|
|
|
JS_FN(js_valueOf_str, js_num_valueOf, 0, 0),
|
|
|
|
|
JS_FN(js_toJSON_str, js_num_valueOf, 0, 0),
|
|
|
|
|
JS_FN("toFixed", num_toFixed, 1, 0),
|
|
|
|
|
JS_FN("toExponential", num_toExponential, 1, 0),
|
|
|
|
|
JS_FN("toPrecision", num_toPrecision, 1, 0),
|
2023-07-12 13:27:26 +10:00
|
|
|
JS_FS_END
|
2018-09-08 18:08:48 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* NB: Keep this in synch with number_constants[]. */
|
|
|
|
|
enum nc_slot {
|
|
|
|
|
NC_NaN,
|
|
|
|
|
NC_POSITIVE_INFINITY,
|
|
|
|
|
NC_NEGATIVE_INFINITY,
|
|
|
|
|
NC_MAX_VALUE,
|
|
|
|
|
NC_MIN_VALUE,
|
|
|
|
|
NC_LIMIT
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some to most C compilers forbid spelling these at compile time, or barf
|
|
|
|
|
* if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState
|
|
|
|
|
* using union jsdpun.
|
|
|
|
|
*/
|
|
|
|
|
static JSConstDoubleSpec number_constants[] = {
|
|
|
|
|
{0, js_NaN_str, 0,{0,0,0}},
|
|
|
|
|
{0, "POSITIVE_INFINITY", 0,{0,0,0}},
|
|
|
|
|
{0, "NEGATIVE_INFINITY", 0,{0,0,0}},
|
|
|
|
|
{1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}},
|
|
|
|
|
{0, "MIN_VALUE", 0,{0,0,0}},
|
|
|
|
|
{0,0,0,{0,0,0}}
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-03 12:35:24 +10:00
|
|
|
jsdouble js_NaN;
|
2025-02-05 12:40:54 +10:00
|
|
|
jsdouble js_PositiveInfinity;
|
|
|
|
|
jsdouble js_NegativeInfinity;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#if (defined __GNUC__ && defined __i386__) || \
|
|
|
|
|
(defined __SUNPRO_CC && defined __i386)
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set the exception mask to mask all exceptions and set the FPU precision
|
2025-02-05 12:40:54 +10:00
|
|
|
* to 53 bit mantissa (64 bit doubles).
|
2018-09-08 18:08:48 +08:00
|
|
|
*/
|
2025-02-05 12:40:54 +10:00
|
|
|
inline void FIX_FPU() {
|
|
|
|
|
short control;
|
|
|
|
|
asm("fstcw %0" : "=m" (control) : );
|
|
|
|
|
control &= ~0x300; // Lower bits 8 and 9 (precision control).
|
|
|
|
|
control |= 0x2f3; // Raise bits 0-5 (exception masks) and 9 (64-bit precision).
|
|
|
|
|
asm("fldcw %0" : : "m" (control) );
|
|
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
#define FIX_FPU() ((void)0)
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
JSBool
|
|
|
|
|
js_InitRuntimeNumberState(JSContext *cx)
|
|
|
|
|
{
|
2025-02-05 12:40:54 +10:00
|
|
|
JSRuntime *rt = cx->runtime;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
FIX_FPU();
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
jsdpun u;
|
2025-03-19 21:25:58 +10:00
|
|
|
u.s.hi = JSDOUBLE_HI32_NAN;
|
|
|
|
|
u.s.lo = JSDOUBLE_LO32_NAN;
|
2025-02-03 12:35:24 +10:00
|
|
|
number_constants[NC_NaN].dval = js_NaN = u.d;
|
2025-03-19 21:25:58 +10:00
|
|
|
rt->NaNValue.setDouble(u.d);
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
u.s.hi = JSDOUBLE_HI32_EXPMASK;
|
|
|
|
|
u.s.lo = 0x00000000;
|
2025-02-05 12:40:54 +10:00
|
|
|
number_constants[NC_POSITIVE_INFINITY].dval = js_PositiveInfinity = u.d;
|
2025-03-19 21:25:58 +10:00
|
|
|
rt->positiveInfinityValue.setDouble(u.d);
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;
|
|
|
|
|
u.s.lo = 0x00000000;
|
2025-02-05 12:40:54 +10:00
|
|
|
number_constants[NC_NEGATIVE_INFINITY].dval = js_NegativeInfinity = u.d;
|
2025-03-19 21:25:58 +10:00
|
|
|
rt->negativeInfinityValue.setDouble(u.d);
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
u.s.hi = 0;
|
|
|
|
|
u.s.lo = 1;
|
|
|
|
|
number_constants[NC_MIN_VALUE].dval = u.d;
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
#ifndef HAVE_LOCALECONV
|
2025-03-19 21:25:58 +10:00
|
|
|
const char* thousands_sep = getenv("LOCALE_THOUSANDS_SEP");
|
|
|
|
|
const char* decimal_point = getenv("LOCALE_DECIMAL_POINT");
|
|
|
|
|
const char* grouping = getenv("LOCALE_GROUPING");
|
|
|
|
|
|
|
|
|
|
rt->thousandsSeparator =
|
|
|
|
|
JS_strdup(cx, thousands_sep ? thousands_sep : "'");
|
|
|
|
|
rt->decimalSeparator =
|
|
|
|
|
JS_strdup(cx, decimal_point ? decimal_point : ".");
|
|
|
|
|
rt->numGrouping =
|
|
|
|
|
JS_strdup(cx, grouping ? grouping : "\3\0");
|
2025-02-05 12:40:54 +10:00
|
|
|
#else
|
|
|
|
|
struct lconv *locale = localeconv();
|
2018-09-08 18:08:48 +08:00
|
|
|
rt->thousandsSeparator =
|
|
|
|
|
JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'");
|
|
|
|
|
rt->decimalSeparator =
|
|
|
|
|
JS_strdup(cx, locale->decimal_point ? locale->decimal_point : ".");
|
|
|
|
|
rt->numGrouping =
|
|
|
|
|
JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0");
|
2025-02-05 12:40:54 +10:00
|
|
|
#endif
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
js_FinishRuntimeNumberState(JSContext *cx)
|
|
|
|
|
{
|
|
|
|
|
JSRuntime *rt = cx->runtime;
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
cx->free((void *) rt->thousandsSeparator);
|
|
|
|
|
cx->free((void *) rt->decimalSeparator);
|
|
|
|
|
cx->free((void *) rt->numGrouping);
|
2018-09-08 18:08:48 +08:00
|
|
|
rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSObject *
|
|
|
|
|
js_InitNumberClass(JSContext *cx, JSObject *obj)
|
|
|
|
|
{
|
|
|
|
|
JSObject *proto, *ctor;
|
|
|
|
|
JSRuntime *rt;
|
|
|
|
|
|
|
|
|
|
/* XXX must do at least once per new thread, so do it per JSContext... */
|
|
|
|
|
FIX_FPU();
|
|
|
|
|
|
|
|
|
|
if (!JS_DefineFunctions(cx, obj, number_functions))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
proto = js_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1,
|
2018-09-08 18:08:48 +08:00
|
|
|
NULL, number_methods, NULL, NULL);
|
|
|
|
|
if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
|
|
|
|
|
return NULL;
|
2025-03-19 21:25:58 +10:00
|
|
|
proto->setPrimitiveThis(Int32Value(0));
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!JS_DefineConstDoubles(cx, ctor, number_constants))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* ECMA 15.1.1.1 */
|
|
|
|
|
rt = cx->runtime;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (!JS_DefineProperty(cx, obj, js_NaN_str, Jsvalify(rt->NaNValue),
|
|
|
|
|
JS_PropertyStub, JS_StrictPropertyStub,
|
2025-02-05 12:40:54 +10:00
|
|
|
JSPROP_PERMANENT | JSPROP_READONLY)) {
|
2018-09-08 18:08:48 +08:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ECMA 15.1.1.2 */
|
2025-03-19 21:25:58 +10:00
|
|
|
if (!JS_DefineProperty(cx, obj, js_Infinity_str, Jsvalify(rt->positiveInfinityValue),
|
|
|
|
|
JS_PropertyStub, JS_StrictPropertyStub,
|
2025-02-05 12:40:54 +10:00
|
|
|
JSPROP_PERMANENT | JSPROP_READONLY)) {
|
2018-09-08 18:08:48 +08:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return proto;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
namespace v8 {
|
|
|
|
|
namespace internal {
|
|
|
|
|
extern char* DoubleToCString(double v, char* buffer, int buflen);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
namespace js {
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
static char *
|
2025-03-19 21:25:58 +10:00
|
|
|
FracNumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = 10)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
{
|
|
|
|
|
int32_t _;
|
|
|
|
|
JS_ASSERT(!JSDOUBLE_IS_INT32(d, &_));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
char* numStr;
|
|
|
|
|
if (base == 10) {
|
|
|
|
|
/*
|
|
|
|
|
* This is V8's implementation of the algorithm described in the
|
|
|
|
|
* following paper:
|
|
|
|
|
*
|
|
|
|
|
* Printing floating-point numbers quickly and accurately with integers.
|
|
|
|
|
* Florian Loitsch, PLDI 2010.
|
|
|
|
|
*
|
|
|
|
|
* It fails on a small number of cases, whereupon we fall back to
|
|
|
|
|
* js_dtostr() (which uses David Gay's dtoa).
|
|
|
|
|
*/
|
|
|
|
|
numStr = v8::internal::DoubleToCString(d, cbuf->sbuf, cbuf->sbufSize);
|
|
|
|
|
if (!numStr)
|
|
|
|
|
numStr = js_dtostr(JS_THREAD_DATA(cx)->dtoaState, cbuf->sbuf, cbuf->sbufSize,
|
2025-02-05 12:40:54 +10:00
|
|
|
DTOSTR_STANDARD, 0, d);
|
2025-03-19 21:25:58 +10:00
|
|
|
} else {
|
|
|
|
|
numStr = cbuf->dbuf = js_dtobasestr(JS_THREAD_DATA(cx)->dtoaState, base, d);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2023-07-12 13:27:26 +10:00
|
|
|
return numStr;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
char *
|
|
|
|
|
NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base/* = 10*/)
|
2025-02-05 12:40:54 +10:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
int32_t i;
|
|
|
|
|
return (JSDOUBLE_IS_INT32(d, &i))
|
|
|
|
|
? IntToCString(cbuf, i, base)
|
|
|
|
|
: FracNumberToCString(cx, cbuf, d, base);
|
|
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSString * JS_FASTCALL
|
|
|
|
|
js_NumberToStringWithBase(JSContext *cx, jsdouble d, jsint base)
|
2023-07-12 13:27:26 +10:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf cbuf;
|
2023-07-12 13:27:26 +10:00
|
|
|
char *numStr;
|
2025-02-05 12:40:54 +10:00
|
|
|
JSString *s;
|
2023-07-12 13:27:26 +10:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
/*
|
|
|
|
|
* Caller is responsible for error reporting. When called from trace,
|
|
|
|
|
* returning NULL here will cause us to fall of trace and then retry
|
|
|
|
|
* from the interpreter (which will report the error).
|
|
|
|
|
*/
|
|
|
|
|
if (base < 2 || base > 36)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
JSCompartment *c = cx->compartment;
|
|
|
|
|
|
|
|
|
|
int32_t i;
|
|
|
|
|
if (JSDOUBLE_IS_INT32(d, &i)) {
|
2025-02-05 12:40:54 +10:00
|
|
|
if (base == 10 && jsuint(i) < INT_STRING_LIMIT)
|
|
|
|
|
return JSString::intString(i);
|
|
|
|
|
if (jsuint(i) < jsuint(base)) {
|
|
|
|
|
if (i < 10)
|
|
|
|
|
return JSString::intString(i);
|
|
|
|
|
return JSString::unitString(jschar('a' + i - 10));
|
|
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
|
|
|
|
|
if (JSString *str = c->dtoaCache.lookup(base, d))
|
|
|
|
|
return str;
|
|
|
|
|
|
|
|
|
|
numStr = IntToCString(&cbuf, i, base);
|
|
|
|
|
JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
|
|
|
|
|
} else {
|
|
|
|
|
if (JSString *str = c->dtoaCache.lookup(base, d))
|
|
|
|
|
return str;
|
|
|
|
|
|
|
|
|
|
numStr = FracNumberToCString(cx, &cbuf, d, base);
|
|
|
|
|
if (!numStr) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
JS_ASSERT_IF(base == 10,
|
|
|
|
|
!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
|
|
|
|
|
JS_ASSERT_IF(base != 10,
|
|
|
|
|
cbuf.dbuf && cbuf.dbuf == numStr);
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
|
|
|
|
|
s = js_NewStringCopyZ(cx, numStr);
|
|
|
|
|
|
|
|
|
|
c->dtoaCache.cache(base, d, s);
|
2025-02-05 12:40:54 +10:00
|
|
|
return s;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
JSString * JS_FASTCALL
|
|
|
|
|
js_NumberToString(JSContext *cx, jsdouble d)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-02-05 12:40:54 +10:00
|
|
|
return js_NumberToStringWithBase(cx, d, 10);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
namespace js {
|
|
|
|
|
|
|
|
|
|
JSFlatString *
|
|
|
|
|
NumberToString(JSContext *cx, jsdouble d)
|
|
|
|
|
{
|
|
|
|
|
if (JSString *str = js_NumberToStringWithBase(cx, d, 10))
|
|
|
|
|
return str->assertIsFlat();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool JS_FASTCALL
|
|
|
|
|
NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
|
2025-02-05 12:40:54 +10:00
|
|
|
{
|
|
|
|
|
/* Convert to C-string. */
|
2025-03-19 21:25:58 +10:00
|
|
|
ToCStringBuf cbuf;
|
2025-02-05 12:40:54 +10:00
|
|
|
const char *cstr;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isInt32()) {
|
|
|
|
|
cstr = IntToCString(&cbuf, v.toInt32());
|
2025-02-05 12:40:54 +10:00
|
|
|
} else {
|
2025-03-19 21:25:58 +10:00
|
|
|
cstr = NumberToCString(cx, &cbuf, v.toDouble());
|
|
|
|
|
if (!cstr) {
|
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Inflate to jschar string. The input C-string characters are < 127, so
|
|
|
|
|
* even if jschars are UTF-8, all chars should map to one jschar.
|
|
|
|
|
*/
|
|
|
|
|
size_t cstrlen = strlen(cstr);
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!cbuf.dbuf && cstrlen < cbuf.sbufSize);
|
|
|
|
|
return sb.appendInflated(cstr, cstrlen);
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
bool
|
|
|
|
|
ValueToNumberSlow(JSContext *cx, Value v, double *out)
|
2025-02-05 12:40:54 +10:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!v.isNumber());
|
2025-02-05 12:40:54 +10:00
|
|
|
goto skip_int_double;
|
2023-07-12 13:27:26 +10:00
|
|
|
for (;;) {
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isNumber()) {
|
|
|
|
|
*out = v.toNumber();
|
|
|
|
|
return true;
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
|
|
|
|
skip_int_double:
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isString())
|
|
|
|
|
return StringToNumberType<jsdouble>(cx, v.toString(), out);
|
|
|
|
|
if (v.isBoolean()) {
|
|
|
|
|
if (v.toBoolean()) {
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = 1.0;
|
2025-03-19 21:25:58 +10:00
|
|
|
return true;
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = 0.0;
|
2025-03-19 21:25:58 +10:00
|
|
|
return true;
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isNull()) {
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = 0.0;
|
2025-03-19 21:25:58 +10:00
|
|
|
return true;
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isUndefined())
|
2023-07-12 13:27:26 +10:00
|
|
|
break;
|
|
|
|
|
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(v.isObject());
|
|
|
|
|
if (!DefaultValue(cx, &v.toObject(), JSTYPE_NUMBER, &v))
|
|
|
|
|
return false;
|
|
|
|
|
if (v.isObject())
|
2023-07-12 13:27:26 +10:00
|
|
|
break;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2023-07-12 13:27:26 +10:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = js_NaN;
|
2025-03-19 21:25:58 +10:00
|
|
|
return true;
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
bool
|
2025-03-19 21:25:58 +10:00
|
|
|
ValueToECMAInt32Slow(JSContext *cx, const Value &v, int32_t *out)
|
2025-02-05 12:40:54 +10:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!v.isInt32());
|
2025-02-05 12:40:54 +10:00
|
|
|
jsdouble d;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isDouble()) {
|
|
|
|
|
d = v.toDouble();
|
2023-07-12 13:27:26 +10:00
|
|
|
} else {
|
2025-03-19 21:25:58 +10:00
|
|
|
if (!ValueToNumberSlow(cx, v, &d))
|
2025-02-05 12:40:54 +10:00
|
|
|
return false;
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = js_DoubleToECMAInt32(d);
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
bool
|
2025-03-19 21:25:58 +10:00
|
|
|
ValueToECMAUint32Slow(JSContext *cx, const Value &v, uint32_t *out)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!v.isInt32());
|
2018-09-08 18:08:48 +08:00
|
|
|
jsdouble d;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isDouble()) {
|
|
|
|
|
d = v.toDouble();
|
2023-07-12 13:27:26 +10:00
|
|
|
} else {
|
2025-03-19 21:25:58 +10:00
|
|
|
if (!ValueToNumberSlow(cx, v, &d))
|
2025-02-05 12:40:54 +10:00
|
|
|
return false;
|
2023-07-12 13:27:26 +10:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = js_DoubleToECMAUint32(d);
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
} /* namespace js */
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
uint32
|
|
|
|
|
js_DoubleToECMAUint32(jsdouble d)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2023-07-12 13:27:26 +10:00
|
|
|
int32 i;
|
2018-09-08 18:08:48 +08:00
|
|
|
JSBool neg;
|
2023-07-12 13:27:26 +10:00
|
|
|
jsdouble two32;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
if (!JSDOUBLE_IS_FINITE(d))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We check whether d fits int32, not uint32, as all but the ">>>" bit
|
|
|
|
|
* manipulation bytecode stores the result as int, not uint. When the
|
2025-03-19 21:25:58 +10:00
|
|
|
* result does not fit int Value, it will be stored as a negative double.
|
2023-07-12 13:27:26 +10:00
|
|
|
*/
|
|
|
|
|
i = (int32) d;
|
|
|
|
|
if ((jsdouble) i == d)
|
|
|
|
|
return (int32)i;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
neg = (d < 0);
|
|
|
|
|
d = floor(neg ? -d : d);
|
|
|
|
|
d = neg ? -d : d;
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
two32 = 4294967296.0;
|
2018-09-08 18:08:48 +08:00
|
|
|
d = fmod(d, two32);
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
return (uint32) (d >= 0 ? d : d + two32);
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
namespace js {
|
|
|
|
|
|
|
|
|
|
bool
|
2025-03-19 21:25:58 +10:00
|
|
|
ValueToInt32Slow(JSContext *cx, const Value &v, int32_t *out)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!v.isInt32());
|
2018-09-08 18:08:48 +08:00
|
|
|
jsdouble d;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isDouble()) {
|
|
|
|
|
d = v.toDouble();
|
|
|
|
|
} else if (!ValueToNumberSlow(cx, v, &d)) {
|
|
|
|
|
return false;
|
2025-02-05 12:40:54 +10:00
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {
|
|
|
|
|
js_ReportValueError(cx, JSMSG_CANT_CONVERT,
|
|
|
|
|
JSDVG_SEARCH_STACK, v, NULL);
|
2025-02-05 12:40:54 +10:00
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
*out = (int32) floor(d + 0.5); /* Round to nearest */
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
bool
|
2025-03-19 21:25:58 +10:00
|
|
|
ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2025-03-19 21:25:58 +10:00
|
|
|
JS_ASSERT(!v.isInt32());
|
2018-09-08 18:08:48 +08:00
|
|
|
jsdouble d;
|
2025-03-19 21:25:58 +10:00
|
|
|
if (v.isDouble()) {
|
|
|
|
|
d = v.toDouble();
|
|
|
|
|
} else if (!ValueToNumberSlow(cx, v, &d)) {
|
|
|
|
|
return false;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {
|
|
|
|
|
*out = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-09-08 18:08:48 +08:00
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
uint16 u = (uint16) d;
|
|
|
|
|
if ((jsdouble)u == d) {
|
|
|
|
|
*out = u;
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
2025-02-05 12:40:54 +10:00
|
|
|
|
|
|
|
|
bool neg = (d < 0);
|
2018-09-08 18:08:48 +08:00
|
|
|
d = floor(neg ? -d : d);
|
2025-02-05 12:40:54 +10:00
|
|
|
d = neg ? -d : d;
|
|
|
|
|
jsuint m = JS_BIT(16);
|
|
|
|
|
d = fmod(d, (double) m);
|
|
|
|
|
if (d < 0)
|
|
|
|
|
d += m;
|
|
|
|
|
*out = (uint16_t) d;
|
|
|
|
|
return true;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
2025-02-05 12:40:54 +10:00
|
|
|
} /* namespace js */
|
|
|
|
|
|
2018-09-08 18:08:48 +08:00
|
|
|
JSBool
|
2023-07-12 13:27:26 +10:00
|
|
|
js_strtod(JSContext *cx, const jschar *s, const jschar *send,
|
|
|
|
|
const jschar **ep, jsdouble *dp)
|
2018-09-08 18:08:48 +08:00
|
|
|
{
|
2023-07-12 13:27:26 +10:00
|
|
|
const jschar *s1;
|
|
|
|
|
size_t length, i;
|
2018-09-08 18:08:48 +08:00
|
|
|
char cbuf[32];
|
|
|
|
|
char *cstr, *istr, *estr;
|
|
|
|
|
JSBool negative;
|
|
|
|
|
jsdouble d;
|
2023-07-12 13:27:26 +10:00
|
|
|
|
|
|
|
|
s1 = js_SkipWhiteSpace(s, send);
|
|
|
|
|
length = send - s1;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
/* Use cbuf to avoid malloc */
|
|
|
|
|
if (length >= sizeof cbuf) {
|
2025-02-05 12:40:54 +10:00
|
|
|
cstr = (char *) cx->malloc(length + 1);
|
2018-09-08 18:08:48 +08:00
|
|
|
if (!cstr)
|
|
|
|
|
return JS_FALSE;
|
|
|
|
|
} else {
|
|
|
|
|
cstr = cbuf;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-12 13:27:26 +10:00
|
|
|
for (i = 0; i != length; i++) {
|
|
|
|
|
if (s1[i] >> 8)
|
2018-09-08 18:08:48 +08:00
|
|
|
break;
|
|
|
|
|
cstr[i] = (char)s1[i];
|
|
|
|
|
}
|
2023-07-12 13:27:26 +10:00
|
|
|
cstr[i] = 0;
|
2018-09-08 18:08:48 +08:00
|
|
|
|
|
|
|
|
istr = cstr;
|
|
|
|
|
if ((negative = (*istr == '-')) != 0 || *istr == '+')
|
|
|
|
|
istr++;
|
2025-02-05 12:40:54 +10:00
|
|
|
if (*istr == 'I' && !strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {
|
|
|
|
|
d = negative ? js_NegativeInfinity : js_PositiveInfinity;
|
2018-09-08 18:08:48 +08:00
|
|
|
estr = istr + 8;
|
|
|
|
|
} else {
|
|
|
|
|
int err;
|
2025-02-05 12:40:54 +10:00
|
|
|
d = js_strtod_harder(JS_THREAD_DATA(cx)->dtoaState, cstr, &estr, &err);
|
2025-02-03 12:35:24 +10:00
|
|
|
if (d == HUGE_VAL)
|
2025-02-05 12:40:54 +10:00
|
|
|
d = js_PositiveInfinity;
|
2025-02-03 12:35:24 +10:00
|
|
|
else if (d == -HUGE_VAL)
|
2025-02-05 12:40:54 +10:00
|
|
|
d = js_NegativeInfinity;
|
2018-09-08 18:08:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i = estr - cstr;
|
|
|
|
|
if (cstr != cbuf)
|
2025-02-05 12:40:54 +10:00
|
|
|
cx->free(cstr);
|
2018-09-08 18:08:48 +08:00
|
|
|
*ep = i ? s1 + i : s;
|
|
|
|
|
*dp = d;
|
|
|
|
|
return JS_TRUE;
|
|
|
|
|
}
|