author | Eric Faust <efaust@mozilla.com> |
Fri, 03 Aug 2012 15:15:04 -0700 | |
changeset 101398 | 83c9437ff26db74dab81cf5352b7f247b2c1471e |
parent 101397 | cda60803eb9185886caa16c437cc2194f665aeaf |
child 101399 | 6609595c84eb7819a4166102296cb73c9a4e139a |
push id | 23232 |
push user | emorley@mozilla.com |
push date | Sat, 04 Aug 2012 18:12:50 +0000 |
treeherder | mozilla-central@0a17cde2a4b6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jorendorff |
bugs | 742188 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/bindings/PrimitiveConversions.h +++ b/dom/bindings/PrimitiveConversions.h @@ -86,26 +86,26 @@ struct PrimitiveConversionTraits<bool> { } }; template<> struct PrimitiveConversionTraits<int64_t> { typedef int64_t jstype; typedef int64_t intermediateType; static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) { - return xpc::ValueToInt64(cx, v, retval); + return JS::ToInt64(cx, v, retval); } }; template<> struct PrimitiveConversionTraits<uint64_t> { typedef uint64_t jstype; typedef uint64_t intermediateType; static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) { - return xpc::ValueToUint64(cx, v, retval); + return JS::ToUint64(cx, v, retval); } }; struct PrimitiveConversionTraits_float { typedef double jstype; typedef double intermediateType; static inline bool converter(JSContext* cx, JS::Value v, jstype* retval) { return JS::ToNumber(cx, v, retval);
--- a/dom/file/LockedFile.cpp +++ b/dom/file/LockedFile.cpp @@ -530,17 +530,17 @@ LockedFile::SetLocation(JSContext* aCx, // Null means the end-of-file. if (JSVAL_IS_NULL(aLocation)) { mLocation = LL_MAXUINT; return NS_OK; } uint64_t location; - if (!xpc::ValueToUint64(aCx, aLocation, &location)) { + if (!JS::ToUint64(aCx, aLocation, &location)) { return NS_ERROR_TYPE_ERR; } mLocation = location; return NS_OK; } NS_IMETHODIMP
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -577,16 +577,30 @@ JS_ValueToECMAInt32(JSContext *cx, jsval JS_PUBLIC_API(JSBool) JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip) { RootedValue value(cx, v); return JS::ToUint32(cx, value, ip); } JS_PUBLIC_API(JSBool) +JS_ValueToInt64(JSContext *cx, jsval v, int64_t *ip) +{ + RootedValue value(cx, v); + return JS::ToInt64(cx, value, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToUint64(JSContext *cx, jsval v, uint64_t *ip) +{ + RootedValue value(cx, v); + return JS::ToUint64(cx, value, ip); +} + +JS_PUBLIC_API(JSBool) JS_ValueToInt32(JSContext *cx, jsval v, int32_t *ip) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); assertSameCompartment(cx, v); if (v.isInt32()) { *ip = v.toInt32();
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2749,29 +2749,51 @@ JS_DoubleToUint32(double d); /* * Convert a value to a number, then to an int32_t, according to the ECMA rules * for ToInt32. */ extern JS_PUBLIC_API(JSBool) JS_ValueToECMAInt32(JSContext *cx, jsval v, int32_t *ip); +/* + * Convert a value to a number, then to an int64_t, according to the WebIDL + * rules for ToInt64: http://dev.w3.org/2006/webapi/WebIDL/#es-long-long + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToInt64(JSContext *cx, jsval v, int64_t *ip); + +/* + * Convert a value to a number, then to an uint64_t, according to the WebIDL + * rules for ToUint64: http://dev.w3.org/2006/webapi/WebIDL/#es-unsigned-long-long + */ +extern JS_PUBLIC_API(JSBool) +JS_ValueToUint64(JSContext *cx, jsval v, uint64_t *ip); + #ifdef __cplusplus namespace js { /* DO NOT CALL THIS. Use JS::ToInt16. */ extern JS_PUBLIC_API(bool) ToUint16Slow(JSContext *cx, const JS::Value &v, uint16_t *out); /* DO NOT CALL THIS. Use JS::ToInt32. */ extern JS_PUBLIC_API(bool) ToInt32Slow(JSContext *cx, const JS::Value &v, int32_t *out); /* DO NOT CALL THIS. Use JS::ToUint32. */ extern JS_PUBLIC_API(bool) ToUint32Slow(JSContext *cx, const JS::Value &v, uint32_t *out); + +/* DO NOT CALL THIS. Use JS::ToInt64. */ +extern JS_PUBLIC_API(bool) +ToInt64Slow(JSContext *cx, const JS::Value &v, int64_t *out); + +/* DO NOT CALL THIS. Use JS::ToUint64. */ +extern JS_PUBLIC_API(bool) +ToUint64Slow(JSContext *cx, const JS::Value &v, uint64_t *out); } /* namespace js */ namespace JS { JS_ALWAYS_INLINE bool ToUint16(JSContext *cx, const js::Value &v, uint16_t *out) { AssertArgumentsAreSane(cx, v); @@ -2814,16 +2836,52 @@ ToUint32(JSContext *cx, const js::Value if (v.isInt32()) { *out = uint32_t(v.toInt32()); return true; } return js::ToUint32Slow(cx, v, out); } +JS_ALWAYS_INLINE bool +ToInt64(JSContext *cx, const js::Value &v, int64_t *out) +{ + AssertArgumentsAreSane(cx, v); + { + JS::SkipRoot skip(cx, &v); + MaybeCheckStackRoots(cx); + } + + if (v.isInt32()) { + *out = int64_t(v.toInt32()); + return true; + } + + return js::ToInt64Slow(cx, v, out); +} + +JS_ALWAYS_INLINE bool +ToUint64(JSContext *cx, const js::Value &v, uint64_t *out) +{ + AssertArgumentsAreSane(cx, v); + { + SkipRoot skip(cx, &v); + MaybeCheckStackRoots(cx); + } + + if (v.isInt32()) { + // Account for sign extension of negatives into the longer 64bit space. + *out = uint64_t(int64_t(v.toInt32())); + return true; + } + + return js::ToUint64Slow(cx, v, out); +} + + } /* namespace JS */ #endif /* __cplusplus */ /* * Convert a value to a number, then to a uint32_t, according to the ECMA rules * for ToUint32. */ extern JS_PUBLIC_API(JSBool)
--- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1395,16 +1395,54 @@ ToNumberSlow(JSContext *cx, Value v, dou if (v.isObject()) break; } *out = js_NaN; return true; } +/* + * Convert a value to an int64_t, according to the WebIDL rules for long long + * conversion. Return converted value in *out on success, false on failure. + */ +JS_PUBLIC_API(bool) +ToInt64Slow(JSContext *cx, const Value &v, int64_t *out) +{ + JS_ASSERT(!v.isInt32()); + double d; + if (v.isDouble()) { + d = v.toDouble(); + } else { + if (!ToNumberSlow(cx, v, &d)) + return false; + } + *out = ToInt64(d); + return true; +} + +/* + * Convert a value to an uint64_t, according to the WebIDL rules for unsigned long long + * conversion. Return converted value in *out on success, false on failure. + */ +JS_PUBLIC_API(bool) +ToUint64Slow(JSContext *cx, const Value &v, uint64_t *out) +{ + JS_ASSERT(!v.isInt32()); + double d; + if (v.isDouble()) { + d = v.toDouble(); + } else { + if (!ToNumberSlow(cx, v, &d)) + return false; + } + *out = ToUint64(d); + return true; +} + JS_PUBLIC_API(bool) ToInt32Slow(JSContext *cx, const Value &v, int32_t *out) { JS_ASSERT(!v.isInt32()); double d; if (v.isDouble()) { d = v.toDouble(); } else {
--- a/js/src/vm/NumericConversions.h +++ b/js/src/vm/NumericConversions.h @@ -28,94 +28,138 @@ union DoublePun { #endif } s; uint64_t u64; double d; }; } /* namespace detail */ -/* ES5 9.5 ToInt32 (specialized for doubles). */ -inline int32_t -ToInt32(double d) +/* Numeric Conversion base. Round doubles to Ints according to ECMA or WEBIDL standards. */ +template<size_t width, typename ResultType> +inline ResultType +ToIntWidth(double d) { #if defined(__i386__) || defined(__i386) || defined(__x86_64__) || \ defined(_M_IX86) || defined(_M_X64) - detail::DoublePun du, duh, two32; + detail::DoublePun du, duh, twoWidth; uint32_t di_h, u_tmp, expon, shift_amount; int32_t 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 + * Step 1. If d is NaN, +/-Inf or |d|>=2^(width + 52) or |d|<1, then return 0 + * All of this is implemented based on an exponent comparison, + * since anything with a higher exponent is either not finite, or + * going to round to 0.. + * Step 2. If |d|<2^(width - 1), 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 3. If |d|>=2^width, d:=fmod(d, 2^width) is taken -- but without a call + * Step 4. If |d|>=2^(width - 1), then the fractional bits are cleared before + * applying the correction by 2^width: d - sign(d)*2^width * 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 + if (u_tmp >= ((width + 52) << 20)) { + // d is Nan, +/-Inf or +/-0, or |d|>=2^(width+52) or |d|<1, in which case result=0 + // If we need to shift by more than (width + 52), there are no data bits + // to preserve, and the mod will turn out 0. return 0; } - if (u_tmp < 0x01f00000) { - // |d|<2^31 - return int32_t(d); + if (u_tmp < ((width - 1) << 20)) { + // |d|<2^(width - 1) + return ResultType(d); } - if (u_tmp > 0x01f00000) { - // |d|>=2^32 + if (u_tmp > ((width - 1) << 20)) { + // |d|>=2^width + // Throw away multiples of 2^width. + // + // That is, compute du.d = the value in (-2^width, 2^width) + // that has the same sign as d and is equal to d modulo 2^width. + // + // This can't be done simply by masking away bits of du because + // the implicit one-bit of the mantissa is one of the ones we want to + // eliminate. So instead we compute duh.d = the appropriate multiple + // of 2^width, which *can* be computed by masking, and then we + // subtract that from du.d. expon = u_tmp >> 20; - shift_amount = expon - 21; - duh.u64 = du.u64; + shift_amount = expon - (width - 11); mask32 = 0x80000000; if (shift_amount < 32) { + // Shift only affects top word. mask32 >>= shift_amount; duh.s.hi = du.s.hi & mask32; duh.s.lo = 0; } else { + // Top word all 1s, shift affects bottom word. 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 + // Eliminate fractional bits u_tmp = (di_h & 0x7ff00000); - if (u_tmp >= 0x41e00000) { - // |d|>=2^31 + if (u_tmp >= (0x3ff00000 + ((width - 1) << 20))) { + // |d|>=2^(width - 1) expon = u_tmp >> 20; + + // Same idea as before, except save everything non-fractional. shift_amount = expon - (0x3ff - 11); mask32 = 0x80000000; if (shift_amount < 32) { + // Top word only mask32 >>= shift_amount; du.s.hi &= mask32; du.s.lo = 0; } else { + // Bottom word. Top word all 1s. mask32 >>= (shift_amount-32); du.s.lo &= mask32; } - two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000); - two32.s.lo = 0; - du.d -= two32.d; + // Apply step 4's 2^width correction. + twoWidth.s.hi = (0x3ff00000 + (width << 20)) ^ (du.s.hi & 0x80000000); + twoWidth.s.lo = 0; + du.d -= twoWidth.d; } - return int32_t(du.d); -#elif defined (__arm__) && defined (__GNUC__) + return ResultType(du.d); +#else + double twoWidth, twoWidthMin1; + + if (!MOZ_DOUBLE_IS_FINITE(d)) + return 0; + + /* FIXME: This relies on undefined behavior; see bug 667739. */ + ResultType i = (ResultType) d; + if ((double) i == d) + return ResultType(i); + + twoWidth = width == 32 ? 4294967296.0 : 18446744073709551616.0; + twoWidthMin1 = width == 32 ? 2147483648.0 : 9223372036854775808.0; + d = fmod(d, twoWidth); + d = (d >= 0) ? floor(d) : ceil(d) + twoWidth; + return (ResultType) (d >= twoWidthMin1 ? d - twoWidth : d); +#endif +} + +/* ES5 9.5 ToInt32 (specialized for doubles). */ +inline int32_t +ToInt32(double d) +{ +#if 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 @@ -227,42 +271,41 @@ ToInt32(double d) " mov %0, #0\n" "9:\n" : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2), "=&r" (d) : "4" (d) : "cc" ); return i; #else - int32_t i; - double two32, two31; - - if (!MOZ_DOUBLE_IS_FINITE(d)) - return 0; - - /* FIXME: This relies on undefined behavior; see bug 667739. */ - i = (int32_t) d; - if ((double) i == d) - return i; - - two32 = 4294967296.0; - two31 = 2147483648.0; - d = fmod(d, two32); - d = (d >= 0) ? floor(d) : ceil(d) + two32; - return (int32_t) (d >= two31 ? d - two32 : d); + return ToIntWidth<32, int32_t>(d); #endif } /* ES5 9.6 (specialized for doubles). */ inline uint32_t ToUint32(double d) { return uint32_t(ToInt32(d)); } +/* WEBIDL 4.2.10 */ +inline int64_t +ToInt64(double d) +{ + return ToIntWidth<64, int64_t>(d); +} + +/* WEBIDL 4.2.11 */ +inline uint64_t +ToUint64(double d) +{ + return uint64_t(ToInt64(d)); +} + /* ES5 9.4 ToInteger (specialized for doubles). */ inline double ToInteger(double d) { if (d == 0) return d; if (!MOZ_DOUBLE_IS_FINITE(d)) {
--- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -392,34 +392,34 @@ XPCConvert::JSData2Native(XPCCallContext return false; *((int16_t*)d) = int16_t(ti); break; case nsXPTType::T_I32 : if (!JS_ValueToECMAInt32(cx, s, (int32_t*)d)) return false; break; case nsXPTType::T_I64 : - return ValueToInt64(cx, s, (int64_t*)d); + return JS::ToInt64(cx, s, (int64_t*)d); case nsXPTType::T_U8 : if (!JS_ValueToECMAUint32(cx, s, &tu)) return false; *((uint8_t*)d) = uint8_t(tu); break; case nsXPTType::T_U16 : if (!JS_ValueToECMAUint32(cx, s, &tu)) return false; *((uint16_t*)d) = uint16_t(tu); break; case nsXPTType::T_U32 : if (!JS_ValueToECMAUint32(cx, s, (uint32_t*)d)) return false; break; case nsXPTType::T_U64 : - return ValueToUint64(cx, s, (uint64_t*)d); + return JS::ToUint64(cx, s, (uint64_t*)d); case nsXPTType::T_FLOAT : if (!JS_ValueToNumber(cx, s, &td)) return false; *((float*)d) = (float) td; break; case nsXPTType::T_DOUBLE : if (!JS_ValueToNumber(cx, s, (double*)d))
--- a/js/xpconnect/src/codegen.py +++ b/js/xpconnect/src/codegen.py @@ -90,22 +90,22 @@ argumentUnboxingTemplates = { 'unsigned long': " uint32_t ${name};\n" " if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'long long': " int64_t ${name};\n" - " if (!xpc::ValueToInt64(cx, ${argVal}, &${name}))\n" + " if (!JS::ToInt64(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'unsigned long long': " uint64_t ${name};\n" - " if (!xpc::ValueToUint64(cx, ${argVal}, &${name}))\n" + " if (!JS::ToUint64(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'float': " double ${name}_dbl;\n" " if (!JS_ValueToNumber(cx, ${argVal}, &${name}_dbl))\n" " return JS_FALSE;\n" " float ${name} = (float) ${name}_dbl;\n",
--- a/js/xpconnect/src/qsgen.py +++ b/js/xpconnect/src/qsgen.py @@ -419,22 +419,22 @@ argumentUnboxingTemplates = { 'unsigned long': " uint32_t ${name};\n" " if (!JS_ValueToECMAUint32(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'long long': " int64_t ${name};\n" - " if (!xpc::ValueToInt64(cx, ${argVal}, &${name}))\n" + " if (!JS::ToInt64(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'unsigned long long': " uint64_t ${name};\n" - " if (!xpc::ValueToUint64(cx, ${argVal}, &${name}))\n" + " if (!JS::ToUint64(cx, ${argVal}, &${name}))\n" " return JS_FALSE;\n", 'float': " double ${name}_dbl;\n" " if (!JS_ValueToNumber(cx, ${argVal}, &${name}_dbl))\n" " return JS_FALSE;\n" " float ${name} = (float) ${name}_dbl;\n",
--- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -276,66 +276,16 @@ DOM_DefineQuickStubs(JSContext *cx, JSOb // (which isn't all of them). nsresult ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, const nsACString &rtPath, nsIMemoryMultiReporterCallback *cb, nsISupports *closure, size_t *rtTotal = NULL); /** - * Convert a jsval to PRInt64. Return true on success. - */ -inline bool -ValueToInt64(JSContext *cx, JS::Value v, int64_t *result) -{ - if (JSVAL_IS_INT(v)) { - int32_t intval; - if (!JS_ValueToECMAInt32(cx, v, &intval)) - return false; - *result = static_cast<int64_t>(intval); - } else { - double doubleval; - if (!JS_ValueToNumber(cx, v, &doubleval)) - return false; - // Be careful with non-finite doubles - if (NS_finite(doubleval)) - // XXXbz this isn't quite right either; need to do the mod thing - *result = static_cast<int64_t>(doubleval); - else - *result = 0; - } - return true; -} - -/** - * Convert a jsval to uint64_t. Return true on success. - */ -inline bool -ValueToUint64(JSContext *cx, JS::Value v, uint64_t *result) -{ - if (JSVAL_IS_INT(v)) { - uint32_t intval; - if (!JS_ValueToECMAUint32(cx, v, &intval)) - return false; - *result = static_cast<uint64_t>(intval); - } else { - double doubleval; - if (!JS_ValueToNumber(cx, v, &doubleval)) - return false; - // Be careful with non-finite doubles - if (NS_finite(doubleval)) - // XXXbz this isn't quite right either; need to do the mod thing - *result = static_cast<uint64_t>(doubleval); - else - *result = 0; - } - return true; -} - -/** * Given an arbitrary object, Unwrap will return the wrapped object if the * passed-in object is a wrapper that Unwrap knows about *and* the * currently running code has permission to access both the wrapper and * wrapped object. * * Since this is meant to be called from functions like * XPCWrappedNative::GetWrappedNativeOfJSObject, it does not set an * exception on |cx|.