Bug 742188 - Implement ToInt64 and ToUint64 per the WebIDL standard. (r=jorendorff)

This commit is contained in:
Eric Faust 2012-08-03 15:15:04 -07:00
Родитель 483ef1d25c
Коммит 072ab750db
10 изменённых файлов: 205 добавлений и 102 удалений

Просмотреть файл

@ -91,7 +91,7 @@ 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);
}
};
@ -100,7 +100,7 @@ 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);
}
};

Просмотреть файл

@ -535,7 +535,7 @@ LockedFile::SetLocation(JSContext* aCx,
}
uint64_t location;
if (!xpc::ValueToUint64(aCx, aLocation, &location)) {
if (!JS::ToUint64(aCx, aLocation, &location)) {
return NS_ERROR_TYPE_ERR;
}

Просмотреть файл

@ -581,6 +581,20 @@ JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32_t *ip)
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)
{

Просмотреть файл

@ -2754,6 +2754,20 @@ JS_DoubleToUint32(double d);
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. */
@ -2767,6 +2781,14 @@ 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 {
@ -2819,6 +2841,42 @@ ToUint32(JSContext *cx, const js::Value &v, uint32_t *out)
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 */

Просмотреть файл

@ -1400,6 +1400,44 @@ ToNumberSlow(JSContext *cx, Value v, double *out)
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)
{

Просмотреть файл

@ -33,25 +33,28 @@ union DoublePun {
} /* 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
*/
@ -59,27 +62,40 @@ ToInt32(double 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;
@ -89,28 +105,56 @@ ToInt32(double 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;
@ -232,22 +276,7 @@ ToInt32(double d)
);
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
}
@ -258,6 +287,20 @@ 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)

Просмотреть файл

@ -397,7 +397,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s,
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))
@ -414,7 +414,7 @@ XPCConvert::JSData2Native(XPCCallContext& ccx, void* d, jsval s,
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))

Просмотреть файл

@ -95,12 +95,12 @@ argumentUnboxingTemplates = {
'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':

Просмотреть файл

@ -424,12 +424,12 @@ argumentUnboxingTemplates = {
'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':

Просмотреть файл

@ -280,56 +280,6 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
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