diff options
Diffstat (limited to 'js/src/jsnum.h')
-rw-r--r-- | js/src/jsnum.h | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/js/src/jsnum.h b/js/src/jsnum.h new file mode 100644 index 0000000..9548cf5 --- /dev/null +++ b/js/src/jsnum.h @@ -0,0 +1,708 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** 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): + * + * 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 ***** */ + +#ifndef jsnum_h___ +#define jsnum_h___ + +#include <math.h> +#if defined(XP_WIN) || defined(XP_OS2) +#include <float.h> +#endif +#ifdef SOLARIS +#include <ieeefp.h> +#endif +#include "jsvalue.h" + +#include "jsstdint.h" +#include "jsstr.h" +#include "jsobj.h" + +/* + * JS number (IEEE double) interface. + * + * JS numbers are optimistically stored in the top 31 bits of 32-bit integers, + * but floating point literals, results that overflow 31 bits, and division and + * modulus operands and results require a 64-bit IEEE double. These are GC'ed + * and pointed to by 32-bit jsvals on the stack and in object properties. + */ + +/* + * The ARM architecture supports two floating point models: VFP and FPA. When + * targetting FPA, doubles are mixed-endian on little endian ARMs (meaning that + * the high and low words are in big endian order). + */ +#if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) +#if !defined(__VFP_FP__) +#define FPU_IS_ARM_FPA +#endif +#endif + +typedef union jsdpun { + struct { +#if defined(IS_LITTLE_ENDIAN) && !defined(FPU_IS_ARM_FPA) + uint32 lo, hi; +#else + uint32 hi, lo; +#endif + } s; + uint64 u64; + jsdouble d; +} jsdpun; + +static inline int +JSDOUBLE_IS_NaN(jsdouble d) +{ +#ifdef WIN32 + return _isnan(d); +#else + return isnan(d); +#endif +} + +static inline int +JSDOUBLE_IS_FINITE(jsdouble d) +{ +#ifdef WIN32 + return _finite(d); +#else + return finite(d); +#endif +} + +static inline int +JSDOUBLE_IS_INFINITE(jsdouble d) +{ +#ifdef WIN32 + int c = _fpclass(d); + return c == _FPCLASS_NINF || c == _FPCLASS_PINF; +#elif defined(SOLARIS) + return !finite(d) && !isnan(d); +#else + return isinf(d); +#endif +} + +#define JSDOUBLE_HI32_SIGNBIT 0x80000000 +#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 +#define JSDOUBLE_HI32_MANTMASK 0x000fffff +#define JSDOUBLE_HI32_NAN 0x7ff80000 +#define JSDOUBLE_LO32_NAN 0x00000000 + +static inline bool +JSDOUBLE_IS_NEG(jsdouble d) +{ +#ifdef WIN32 + return JSDOUBLE_IS_NEGZERO(d) || d < 0; +#elif defined(SOLARIS) + return copysign(1, d) < 0; +#else + return signbit(d); +#endif +} + +static inline uint32 +JS_HASH_DOUBLE(jsdouble d) +{ + jsdpun u; + u.d = d; + return u.s.lo ^ u.s.hi; +} + +#if defined(XP_WIN) +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ + ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ + ? (IFNAN) \ + : (LVAL) OP (RVAL)) +#else +#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) +#endif + +extern jsdouble js_NaN; +extern jsdouble js_PositiveInfinity; +extern jsdouble js_NegativeInfinity; + +/* Initialize number constants and runtime state for the first context. */ +extern JSBool +js_InitRuntimeNumberState(JSContext *cx); + +extern void +js_FinishRuntimeNumberState(JSContext *cx); + +/* Initialize the Number class, returning its prototype object. */ +extern js::Class js_NumberClass; + +inline bool +JSObject::isNumber() const +{ + return getClass() == &js_NumberClass; +} + +extern JSObject * +js_InitNumberClass(JSContext *cx, JSObject *obj); + +/* + * String constants for global function names, used in jsapi.c and jsnum.c. + */ +extern const char js_Infinity_str[]; +extern const char js_NaN_str[]; +extern const char js_isNaN_str[]; +extern const char js_isFinite_str[]; +extern const char js_parseFloat_str[]; +extern const char js_parseInt_str[]; + +extern JSString * JS_FASTCALL +js_IntToString(JSContext *cx, jsint i); + +/* + * When base == 10, this function implements ToString() as specified by + * ECMA-262-5 section 9.8.1; but note that it handles integers specially for + * performance. See also js::NumberToCString(). + */ +extern JSString * JS_FASTCALL +js_NumberToString(JSContext *cx, jsdouble d); + +namespace js { + +/* + * Convert an integer or double (contained in the given value) to a string and + * append to the given buffer. + */ +extern bool JS_FASTCALL +NumberValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb); + +/* Same as js_NumberToString, different signature. */ +extern JSFlatString * +NumberToString(JSContext *cx, jsdouble d); + +/* + * Usually a small amount of static storage is enough, but sometimes we need + * to dynamically allocate much more. This struct encapsulates that. + * Dynamically allocated memory will be freed when the object is destroyed. + */ +struct ToCStringBuf +{ + /* + * The longest possible result that would need to fit in sbuf is + * (-0x80000000).toString(2), which has length 33. Longer cases are + * possible, but they'll go in dbuf. + */ + static const size_t sbufSize = 34; + char sbuf[sbufSize]; + char *dbuf; /* must be allocated with js_malloc() */ + + ToCStringBuf(); + ~ToCStringBuf(); +}; + +/* + * Convert a number to a C string. When base==10, this function implements + * ToString() as specified by ECMA-262-5 section 9.8.1. It handles integral + * values cheaply. Return NULL if we ran out of memory. See also + * js_NumberToCString(). + */ +extern char * +NumberToCString(JSContext *cx, ToCStringBuf *cbuf, jsdouble d, jsint base = 10); + +/* + * The largest positive integer such that all positive integers less than it + * may be precisely represented using the IEEE-754 double-precision format. + */ +const double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64(1) << 53; + +/* + * Compute the positive integer of the given base described immediately at the + * start of the range [start, end) -- no whitespace-skipping, no magical + * leading-"0" octal or leading-"0x" hex behavior, no "+"/"-" parsing, just + * reading the digits of the integer. Return the index one past the end of the + * digits of the integer in *endp, and return the integer itself in *dp. If + * base is 10 or a power of two the returned integer is the closest possible + * double; otherwise extremely large integers may be slightly inaccurate. + * + * If [start, end) does not begin with a number with the specified base, + * *dp == 0 and *endp == start upon return. + */ +extern bool +GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base, + const jschar **endp, jsdouble *dp); + +/* + * Convert a value to a number, returning the converted value in 'out' if the + * conversion succeeds. + */ +JS_ALWAYS_INLINE bool +ValueToNumber(JSContext *cx, const js::Value &v, double *out) +{ + if (v.isNumber()) { + *out = v.toNumber(); + return true; + } + extern bool ValueToNumberSlow(JSContext *, js::Value, double *); + return ValueToNumberSlow(cx, v, out); +} + +/* Convert a value to a number, replacing 'vp' with the converted value. */ +JS_ALWAYS_INLINE bool +ValueToNumber(JSContext *cx, js::Value *vp) +{ + if (vp->isNumber()) + return true; + double d; + extern bool ValueToNumberSlow(JSContext *, js::Value, double *); + if (!ValueToNumberSlow(cx, *vp, &d)) + return false; + vp->setNumber(d); + return true; +} + +/* + * Convert a value to an int32 or uint32, according to the ECMA rules for + * ToInt32 and ToUint32. Return converted value in *out on success, !ok on + * failure. + */ +JS_ALWAYS_INLINE bool +ValueToECMAInt32(JSContext *cx, const js::Value &v, int32_t *out) +{ + if (v.isInt32()) { + *out = v.toInt32(); + return true; + } + extern bool ValueToECMAInt32Slow(JSContext *, const js::Value &, int32_t *); + return ValueToECMAInt32Slow(cx, v, out); +} + +JS_ALWAYS_INLINE bool +ValueToECMAUint32(JSContext *cx, const js::Value &v, uint32_t *out) +{ + if (v.isInt32()) { + *out = (uint32_t)v.toInt32(); + return true; + } + extern bool ValueToECMAUint32Slow(JSContext *, const js::Value &, uint32_t *); + return ValueToECMAUint32Slow(cx, v, out); +} + +/* + * Convert a value to a number, then to an int32 if it fits by rounding to + * nearest. Return converted value in *out on success, !ok on failure. As a + * side effect, *vp will be mutated to match *out. + */ +JS_ALWAYS_INLINE bool +ValueToInt32(JSContext *cx, const js::Value &v, int32_t *out) +{ + if (v.isInt32()) { + *out = v.toInt32(); + return true; + } + extern bool ValueToInt32Slow(JSContext *, const js::Value &, int32_t *); + return ValueToInt32Slow(cx, v, out); +} + +/* + * Convert a value to a number, then to a uint16 according to the ECMA rules + * for ToUint16. Return converted value on success, !ok on failure. v must be a + * copy of a rooted value. + */ +JS_ALWAYS_INLINE bool +ValueToUint16(JSContext *cx, const js::Value &v, uint16_t *out) +{ + if (v.isInt32()) { + *out = (uint16_t)v.toInt32(); + return true; + } + extern bool ValueToUint16Slow(JSContext *, const js::Value &, uint16_t *); + return ValueToUint16Slow(cx, v, out); +} + +} /* namespace js */ + +/* + * Specialized ToInt32 and ToUint32 converters for doubles. + */ +/* + * From the ES3 spec, 9.5 + * 2. If Result(1) is NaN, +0, -0, +Inf, or -Inf, return +0. + * 3. Compute sign(Result(1)) * floor(abs(Result(1))). + * 4. Compute Result(3) modulo 2^32; that is, a finite integer value k of Number + * type with positive sign and less than 2^32 in magnitude such the mathematical + * difference of Result(3) and k is mathematically an integer multiple of 2^32. + * 5. If Result(4) is greater than or equal to 2^31, return Result(4)- 2^32, + * otherwise return Result(4). + */ +static inline int32 +js_DoubleToECMAInt32(jsdouble d) +{ +#if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64) + jsdpun du, duh, two32; + uint32 di_h, u_tmp, expon, shift_amount; + int32 mask32; + + /* + * Algorithm Outline + * Step 1. If d is NaN, +/-Inf or |d|>=2^84 or |d|<1, then return 0 + * All of this is implemented based on an exponent comparison. + * Step 2. If |d|<2^31, then return (int)d + * The cast to integer (conversion in RZ mode) returns the correct result. + * Step 3. If |d|>=2^32, d:=fmod(d, 2^32) is taken -- but without a call + * Step 4. If |d|>=2^31, then the fractional bits are cleared before + * applying the correction by 2^32: d - sign(d)*2^32 + * Step 5. Return (int)d + */ + + du.d = d; + di_h = du.s.hi; + + u_tmp = (di_h & 0x7ff00000) - 0x3ff00000; + if (u_tmp >= (0x45300000-0x3ff00000)) { + // d is Nan, +/-Inf or +/-0, or |d|>=2^(32+52) or |d|<1, in which case result=0 + return 0; + } + + if (u_tmp < 0x01f00000) { + // |d|<2^31 + return int32_t(d); + } + + if (u_tmp > 0x01f00000) { + // |d|>=2^32 + expon = u_tmp >> 20; + shift_amount = expon - 21; + duh.u64 = du.u64; + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + duh.s.hi = du.s.hi & mask32; + duh.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + duh.s.hi = du.s.hi; + duh.s.lo = du.s.lo & mask32; + } + du.d -= duh.d; + } + + di_h = du.s.hi; + + // eliminate fractional bits + u_tmp = (di_h & 0x7ff00000); + if (u_tmp >= 0x41e00000) { + // |d|>=2^31 + expon = u_tmp >> 20; + shift_amount = expon - (0x3ff - 11); + mask32 = 0x80000000; + if (shift_amount < 32) { + mask32 >>= shift_amount; + du.s.hi &= mask32; + du.s.lo = 0; + } else { + mask32 >>= (shift_amount-32); + du.s.lo &= mask32; + } + two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000); + two32.s.lo = 0; + du.d -= two32.d; + } + + return int32(du.d); +#elif defined (__arm__) && defined (__GNUC__) + int32_t i; + uint32_t tmp0; + uint32_t tmp1; + uint32_t tmp2; + asm ( + // We use a pure integer solution here. In the 'softfp' ABI, the argument + // will start in r0 and r1, and VFP can't do all of the necessary ECMA + // conversions by itself so some integer code will be required anyway. A + // hybrid solution is faster on A9, but this pure integer solution is + // notably faster for A8. + + // %0 is the result register, and may alias either of the %[QR]1 registers. + // %Q4 holds the lower part of the mantissa. + // %R4 holds the sign, exponent, and the upper part of the mantissa. + // %1, %2 and %3 are used as temporary values. + + // Extract the exponent. +" mov %1, %R4, LSR #20\n" +" bic %1, %1, #(1 << 11)\n" // Clear the sign. + + // Set the implicit top bit of the mantissa. This clobbers a bit of the + // exponent, but we have already extracted that. +" orr %R4, %R4, #(1 << 20)\n" + + // Special Cases + // We should return zero in the following special cases: + // - Exponent is 0x000 - 1023: +/-0 or subnormal. + // - Exponent is 0x7ff - 1023: +/-INFINITY or NaN + // - This case is implicitly handled by the standard code path anyway, + // as shifting the mantissa up by the exponent will result in '0'. + // + // The result is composed of the mantissa, prepended with '1' and + // bit-shifted left by the (decoded) exponent. Note that because the r1[20] + // is the bit with value '1', r1 is effectively already shifted (left) by + // 20 bits, and r0 is already shifted by 52 bits. + + // Adjust the exponent to remove the encoding offset. If the decoded + // exponent is negative, quickly bail out with '0' as such values round to + // zero anyway. This also catches +/-0 and subnormals. +" sub %1, %1, #0xff\n" +" subs %1, %1, #0x300\n" +" bmi 8f\n" + + // %1 = (decoded) exponent >= 0 + // %R4 = upper mantissa and sign + + // ---- Lower Mantissa ---- +" subs %3, %1, #52\n" // Calculate exp-52 +" bmi 1f\n" + + // Shift r0 left by exp-52. + // Ensure that we don't overflow ARM's 8-bit shift operand range. + // We need to handle anything up to an 11-bit value here as we know that + // 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero + // anyway, so as long as we don't touch the bottom 5 bits, we can use + // a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255 range. +" bic %2, %3, #0xff\n" +" orr %3, %3, %2, LSR #3\n" + // We can now perform a straight shift, avoiding the need for any + // conditional instructions or extra branches. +" mov %Q4, %Q4, LSL %3\n" +" b 2f\n" +"1:\n" // Shift r0 right by 52-exp. + // We know that 0 <= exp < 52, and we can shift up to 255 bits so 52-exp + // will always be a valid shift and we can sk%3 the range check for this case. +" rsb %3, %1, #52\n" +" mov %Q4, %Q4, LSR %3\n" + + // %1 = (decoded) exponent + // %R4 = upper mantissa and sign + // %Q4 = partially-converted integer + +"2:\n" + // ---- Upper Mantissa ---- + // This is much the same as the lower mantissa, with a few different + // boundary checks and some masking to hide the exponent & sign bit in the + // upper word. + // Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift + // it left more to remove the sign and exponent so it is effectively + // pre-shifted by 31 bits. +" subs %3, %1, #31\n" // Calculate exp-31 +" mov %1, %R4, LSL #11\n" // Re-use %1 as a temporary register. +" bmi 3f\n" + + // Shift %R4 left by exp-31. + // Avoid overflowing the 8-bit shift range, as before. +" bic %2, %3, #0xff\n" +" orr %3, %3, %2, LSR #3\n" + // Perform the shift. +" mov %2, %1, LSL %3\n" +" b 4f\n" +"3:\n" // Shift r1 right by 31-exp. + // We know that 0 <= exp < 31, and we can shift up to 255 bits so 31-exp + // will always be a valid shift and we can skip the range check for this case. +" rsb %3, %3, #0\n" // Calculate 31-exp from -(exp-31) +" mov %2, %1, LSR %3\n" // Thumb-2 can't do "LSR %3" in "orr". + + // %Q4 = partially-converted integer (lower) + // %R4 = upper mantissa and sign + // %2 = partially-converted integer (upper) + +"4:\n" + // Combine the converted parts. +" orr %Q4, %Q4, %2\n" + // Negate the result if we have to, and move it to %0 in the process. To + // avoid conditionals, we can do this by inverting on %R4[31], then adding + // %R4[31]>>31. +" eor %Q4, %Q4, %R4, ASR #31\n" +" add %0, %Q4, %R4, LSR #31\n" +" b 9f\n" +"8:\n" + // +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range that + // will result in a conversion of '0'. +" mov %0, #0\n" +"9:\n" + : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2) + : "r" (d) + : "cc" + ); + return i; +#else + int32 i; + jsdouble two32, two31; + + if (!JSDOUBLE_IS_FINITE(d)) + return 0; + + i = (int32) d; + if ((jsdouble) i == d) + return i; + + two32 = 4294967296.0; + two31 = 2147483648.0; + d = fmod(d, two32); + d = (d >= 0) ? floor(d) : ceil(d) + two32; + return (int32) (d >= two31 ? d - two32 : d); +#endif +} + +uint32 +js_DoubleToECMAUint32(jsdouble d); + +/* + * Convert a jsdouble to an integral number, stored in a jsdouble. + * If d is NaN, return 0. If d is an infinity, return it without conversion. + */ +static inline jsdouble +js_DoubleToInteger(jsdouble d) +{ + if (d == 0) + return d; + + if (!JSDOUBLE_IS_FINITE(d)) { + if (JSDOUBLE_IS_NaN(d)) + return 0; + return d; + } + + JSBool neg = (d < 0); + d = floor(neg ? -d : d); + + return neg ? -d : d; +} + +/* + * Similar to strtod except that it replaces overflows with infinities of the + * correct sign, and underflows with zeros of the correct sign. Guaranteed to + * return the closest double number to the given input in dp. + * + * Also allows inputs of the form [+|-]Infinity, which produce an infinity of + * the appropriate sign. The case of the "Infinity" string must match exactly. + * If the string does not contain a number, set *ep to s and return 0.0 in dp. + * Return false if out of memory. + */ +extern JSBool +js_strtod(JSContext *cx, const jschar *s, const jschar *send, + const jschar **ep, jsdouble *dp); + +extern JSBool +js_num_valueOf(JSContext *cx, uintN argc, js::Value *vp); + +namespace js { + +static JS_ALWAYS_INLINE bool +ValueFitsInInt32(const Value &v, int32_t *pi) +{ + if (v.isInt32()) { + *pi = v.toInt32(); + return true; + } + return v.isDouble() && JSDOUBLE_IS_INT32(v.toDouble(), pi); +} + +template<typename T> struct NumberTraits { }; +template<> struct NumberTraits<int32> { + static JS_ALWAYS_INLINE int32 NaN() { return 0; } + static JS_ALWAYS_INLINE int32 toSelfType(int32 i) { return i; } + static JS_ALWAYS_INLINE int32 toSelfType(jsdouble d) { return js_DoubleToECMAUint32(d); } +}; +template<> struct NumberTraits<jsdouble> { + static JS_ALWAYS_INLINE jsdouble NaN() { return js_NaN; } + static JS_ALWAYS_INLINE jsdouble toSelfType(int32 i) { return i; } + static JS_ALWAYS_INLINE jsdouble toSelfType(jsdouble d) { return d; } +}; + +template<typename T> +static JS_ALWAYS_INLINE bool +StringToNumberType(JSContext *cx, JSString *str, T *result) +{ + size_t length = str->length(); + const jschar *chars = str->getChars(NULL); + if (!chars) + return false; + + if (length == 1) { + jschar c = chars[0]; + if ('0' <= c && c <= '9') { + *result = NumberTraits<T>::toSelfType(T(c - '0')); + return true; + } + if (JS_ISSPACE(c)) { + *result = NumberTraits<T>::toSelfType(T(0)); + return true; + } + *result = NumberTraits<T>::NaN(); + return true; + } + + const jschar *bp = chars; + const jschar *end = chars + length; + bp = js_SkipWhiteSpace(bp, end); + + /* ECMA doesn't allow signed hex numbers (bug 273467). */ + if (end - bp >= 2 && bp[0] == '0' && (bp[1] == 'x' || bp[1] == 'X')) { + /* Looks like a hex number. */ + const jschar *endptr; + double d; + if (!GetPrefixInteger(cx, bp + 2, end, 16, &endptr, &d) || + js_SkipWhiteSpace(endptr, end) != end) { + *result = NumberTraits<T>::NaN(); + return true; + } + *result = NumberTraits<T>::toSelfType(d); + return true; + } + + /* + * Note that ECMA doesn't treat a string beginning with a '0' as + * an octal number here. This works because all such numbers will + * be interpreted as decimal by js_strtod. Also, any hex numbers + * that have made it here (which can only be negative ones) will + * be treated as 0 without consuming the 'x' by js_strtod. + */ + const jschar *ep; + double d; + if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end) { + *result = NumberTraits<T>::NaN(); + return true; + } + *result = NumberTraits<T>::toSelfType(d); + return true; +} +} + +#endif /* jsnum_h___ */ |