Differential D3143 Diff 14286 ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/Conversions.h
Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/libraries/source/spidermonkey/include-win32-debug/js/Conversions.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- | ||||
* vim: set ts=8 sts=4 et sw=4 tw=99: | * vim: set ts=8 sts=2 et sw=2 tw=80: | ||||
* This Source Code Form is subject to the terms of the Mozilla Public | * This Source Code Form is subject to the terms of the Mozilla Public | ||||
* License, v. 2.0. If a copy of the MPL was not distributed with this | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||||
/* ECMAScript conversion operations. */ | /* ECMAScript conversion operations. */ | ||||
#ifndef js_Conversions_h | #ifndef js_Conversions_h | ||||
#define js_Conversions_h | #define js_Conversions_h | ||||
#include "mozilla/Casting.h" | #include "mozilla/Casting.h" | ||||
#include "mozilla/Compiler.h" | |||||
#include "mozilla/FloatingPoint.h" | #include "mozilla/FloatingPoint.h" | ||||
#include "mozilla/MathAlgorithms.h" | #include "mozilla/MathAlgorithms.h" | ||||
#include "mozilla/TypeTraits.h" | #include "mozilla/TypeTraits.h" | ||||
#include "mozilla/WrappingOperations.h" | #include "mozilla/WrappingOperations.h" | ||||
#include <math.h> | #include <math.h> | ||||
#include "jspubtd.h" | #include "jspubtd.h" | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
* objects in JS, codified as OrdinaryToPrimitive. | * objects in JS, codified as OrdinaryToPrimitive. | ||||
*/ | */ | ||||
extern JS_PUBLIC_API bool OrdinaryToPrimitive(JSContext* cx, HandleObject obj, | extern JS_PUBLIC_API bool OrdinaryToPrimitive(JSContext* cx, HandleObject obj, | ||||
JSType type, | JSType type, | ||||
MutableHandleValue vp); | MutableHandleValue vp); | ||||
/* ES6 draft 20141224, 7.1.2. */ | /* ES6 draft 20141224, 7.1.2. */ | ||||
MOZ_ALWAYS_INLINE bool ToBoolean(HandleValue v) { | MOZ_ALWAYS_INLINE bool ToBoolean(HandleValue v) { | ||||
if (v.isBoolean()) return v.toBoolean(); | if (v.isBoolean()) { | ||||
if (v.isInt32()) return v.toInt32() != 0; | return v.toBoolean(); | ||||
if (v.isNullOrUndefined()) return false; | } | ||||
if (v.isInt32()) { | |||||
return v.toInt32() != 0; | |||||
} | |||||
if (v.isNullOrUndefined()) { | |||||
return false; | |||||
} | |||||
if (v.isDouble()) { | if (v.isDouble()) { | ||||
double d = v.toDouble(); | double d = v.toDouble(); | ||||
return !mozilla::IsNaN(d) && d != 0; | return !mozilla::IsNaN(d) && d != 0; | ||||
} | } | ||||
if (v.isSymbol()) return true; | if (v.isSymbol()) { | ||||
return true; | |||||
} | |||||
/* The slow path handles strings and objects. */ | /* The slow path handles strings, BigInts and objects. */ | ||||
return js::ToBooleanSlow(v); | return js::ToBooleanSlow(v); | ||||
} | } | ||||
/* ES6 draft 20141224, 7.1.3. */ | /* ES6 draft 20141224, 7.1.3. */ | ||||
MOZ_ALWAYS_INLINE bool ToNumber(JSContext* cx, HandleValue v, double* out) { | MOZ_ALWAYS_INLINE bool ToNumber(JSContext* cx, HandleValue v, double* out) { | ||||
detail::AssertArgumentsAreSane(cx, v); | detail::AssertArgumentsAreSane(cx, v); | ||||
if (v.isNumber()) { | if (v.isNumber()) { | ||||
*out = v.toNumber(); | *out = v.toNumber(); | ||||
return true; | return true; | ||||
} | } | ||||
return js::ToNumberSlow(cx, v, out); | return js::ToNumberSlow(cx, v, out); | ||||
} | } | ||||
/* ES6 draft 20141224, ToInteger (specialized for doubles). */ | /* ES6 draft 20141224, ToInteger (specialized for doubles). */ | ||||
inline double ToInteger(double d) { | inline double ToInteger(double d) { | ||||
if (d == 0) return d; | if (d == 0) { | ||||
return d; | |||||
} | |||||
if (!mozilla::IsFinite(d)) { | if (!mozilla::IsFinite(d)) { | ||||
if (mozilla::IsNaN(d)) return 0; | if (mozilla::IsNaN(d)) { | ||||
return 0; | |||||
} | |||||
return d; | return d; | ||||
} | } | ||||
return d < 0 ? ceil(d) : floor(d); | return d < 0 ? ceil(d) : floor(d); | ||||
} | } | ||||
/* ES6 draft 20141224, 7.1.5. */ | /* ES6 draft 20141224, 7.1.5. */ | ||||
MOZ_ALWAYS_INLINE bool ToInt32(JSContext* cx, JS::HandleValue v, int32_t* out) { | MOZ_ALWAYS_INLINE bool ToInt32(JSContext* cx, JS::HandleValue v, int32_t* out) { | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | MOZ_ALWAYS_INLINE bool ToUint64(JSContext* cx, HandleValue v, uint64_t* out) { | ||||
} | } | ||||
return js::ToUint64Slow(cx, v, out); | return js::ToUint64Slow(cx, v, out); | ||||
} | } | ||||
/* ES6 draft 20141224, 7.1.12. */ | /* ES6 draft 20141224, 7.1.12. */ | ||||
MOZ_ALWAYS_INLINE JSString* ToString(JSContext* cx, HandleValue v) { | MOZ_ALWAYS_INLINE JSString* ToString(JSContext* cx, HandleValue v) { | ||||
detail::AssertArgumentsAreSane(cx, v); | detail::AssertArgumentsAreSane(cx, v); | ||||
if (v.isString()) return v.toString(); | if (v.isString()) { | ||||
return v.toString(); | |||||
} | |||||
return js::ToStringSlow(cx, v); | return js::ToStringSlow(cx, v); | ||||
} | } | ||||
/* ES6 draft 20141224, 7.1.13. */ | /* ES6 draft 20141224, 7.1.13. */ | ||||
inline JSObject* ToObject(JSContext* cx, HandleValue v) { | inline JSObject* ToObject(JSContext* cx, HandleValue v) { | ||||
detail::AssertArgumentsAreSane(cx, v); | detail::AssertArgumentsAreSane(cx, v); | ||||
if (v.isObject()) return &v.toObject(); | if (v.isObject()) { | ||||
return &v.toObject(); | |||||
} | |||||
return js::ToObjectSlow(cx, v, false); | return js::ToObjectSlow(cx, v, false); | ||||
} | } | ||||
namespace detail { | /** | ||||
* Convert a double value to UnsignedInteger (an unsigned integral type) using | |||||
/* | |||||
* Convert a double value to ResultType (an unsigned integral type) using | |||||
* ECMAScript-style semantics (that is, in like manner to how ECMAScript's | * ECMAScript-style semantics (that is, in like manner to how ECMAScript's | ||||
* ToInt32 converts to int32_t). | * ToInt32 converts to int32_t). | ||||
* | * | ||||
* If d is infinite or NaN, return 0. | * If d is infinite or NaN, return 0. | ||||
* Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType | * Otherwise compute d2 = sign(d) * floor(abs(d)), and return the | ||||
* value congruent to d2 mod 2**(bit width of ResultType). | * UnsignedInteger value congruent to d2 % 2**(bit width of UnsignedInteger). | ||||
* | * | ||||
* The algorithm below is inspired by that found in | * The algorithm below is inspired by that found in | ||||
* <http://trac.webkit.org/changeset/67825/trunk/JavaScriptCore/runtime/JSValue.cpp> | * <https://trac.webkit.org/changeset/67825/webkit/trunk/JavaScriptCore/runtime/JSValue.cpp> | ||||
* but has been generalized to all integer widths. | * but has been generalized to all integer widths. | ||||
*/ | */ | ||||
template <typename ResultType> | template <typename UnsignedInteger> | ||||
inline ResultType ToUintWidth(double d) { | inline UnsignedInteger ToUnsignedInteger(double d) { | ||||
static_assert(mozilla::IsUnsigned<ResultType>::value, | static_assert(mozilla::IsUnsigned<UnsignedInteger>::value, | ||||
"ResultType must be an unsigned type"); | "UnsignedInteger must be an unsigned type"); | ||||
uint64_t bits = mozilla::BitwiseCast<uint64_t>(d); | uint64_t bits = mozilla::BitwiseCast<uint64_t>(d); | ||||
unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift; | unsigned DoubleExponentShift = mozilla::FloatingPoint<double>::kExponentShift; | ||||
// Extract the exponent component. (Be careful here! It's not technically | // Extract the exponent component. (Be careful here! It's not technically | ||||
// the exponent in NaN, infinities, and subnormals.) | // the exponent in NaN, infinities, and subnormals.) | ||||
int_fast16_t exp = | int_fast16_t exp = | ||||
int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> | int_fast16_t((bits & mozilla::FloatingPoint<double>::kExponentBits) >> | ||||
DoubleExponentShift) - | DoubleExponentShift) - | ||||
int_fast16_t(mozilla::FloatingPoint<double>::kExponentBias); | int_fast16_t(mozilla::FloatingPoint<double>::kExponentBias); | ||||
// If the exponent's less than zero, abs(d) < 1, so the result is 0. (This | // If the exponent's less than zero, abs(d) < 1, so the result is 0. (This | ||||
// also handles subnormals.) | // also handles subnormals.) | ||||
if (exp < 0) return 0; | if (exp < 0) { | ||||
return 0; | |||||
} | |||||
uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp); | uint_fast16_t exponent = mozilla::AssertedCast<uint_fast16_t>(exp); | ||||
// If the exponent is greater than or equal to the bits of precision of a | // If the exponent is greater than or equal to the bits of precision of a | ||||
// double plus ResultType's width, the number is either infinite, NaN, or | // double plus UnsignedInteger's width, the number is either infinite, NaN, | ||||
// too large to have lower-order bits in the congruent value. (Example: | // or too large to have lower-order bits in the congruent value. (Example: | ||||
// 2**84 is exactly representable as a double. The next exact double is | // 2**84 is exactly representable as a double. The next exact double is | ||||
// 2**84 + 2**32. Thus if ResultType is int32_t, an exponent >= 84 implies | // 2**84 + 2**32. Thus if UnsignedInteger is uint32_t, an exponent >= 84 | ||||
// floor(abs(d)) == 0 mod 2**32.) Return 0 in all these cases. | // implies floor(abs(d)) == 0 mod 2**32.) Return 0 in all these cases. | ||||
const size_t ResultWidth = CHAR_BIT * sizeof(ResultType); | constexpr size_t ResultWidth = CHAR_BIT * sizeof(UnsignedInteger); | ||||
if (exponent >= DoubleExponentShift + ResultWidth) return 0; | if (exponent >= DoubleExponentShift + ResultWidth) { | ||||
return 0; | |||||
} | |||||
// The significand contains the bits that will determine the final result. | // The significand contains the bits that will determine the final result. | ||||
// Shift those bits left or right, according to the exponent, to their | // Shift those bits left or right, according to the exponent, to their | ||||
// locations in the unsigned binary representation of floor(abs(d)). | // locations in the unsigned binary representation of floor(abs(d)). | ||||
static_assert(sizeof(ResultType) <= sizeof(uint64_t), | static_assert(sizeof(UnsignedInteger) <= sizeof(uint64_t), | ||||
"Left-shifting below would lose upper bits"); | "left-shifting below would lose upper bits"); | ||||
ResultType result = | UnsignedInteger result = | ||||
(exponent > DoubleExponentShift) | (exponent > DoubleExponentShift) | ||||
? ResultType(bits << (exponent - DoubleExponentShift)) | ? UnsignedInteger(bits << (exponent - DoubleExponentShift)) | ||||
: ResultType(bits >> (DoubleExponentShift - exponent)); | : UnsignedInteger(bits >> (DoubleExponentShift - exponent)); | ||||
// Two further complications remain. First, |result| may contain bogus | // Two further complications remain. First, |result| may contain bogus | ||||
// sign/exponent bits. Second, IEEE-754 numbers' significands (excluding | // sign/exponent bits. Second, IEEE-754 numbers' significands (excluding | ||||
// subnormals, but we already handled those) have an implicit leading 1 | // subnormals, but we already handled those) have an implicit leading 1 | ||||
// which may affect the final result. | // which may affect the final result. | ||||
// | // | ||||
// It may appear that there's complexity here depending on how ResultWidth | // It may appear that there's complexity here depending on how ResultWidth | ||||
// and DoubleExponentShift relate, but it turns out there's not. | // and DoubleExponentShift relate, but it turns out there's not. | ||||
Show All 10 Lines | inline UnsignedInteger ToUnsignedInteger(double d) { | ||||
// bogus bits in |result|. This implies |exponent < ResultWidth|. Any | // bogus bits in |result|. This implies |exponent < ResultWidth|. Any | ||||
// right-shift less than |ResultWidth| does too, which implies | // right-shift less than |ResultWidth| does too, which implies | ||||
// |DoubleExponentShift - ResultWidth < exponent|. By assumption, then, | // |DoubleExponentShift - ResultWidth < exponent|. By assumption, then, | ||||
// |exponent| is negative, but we excluded that above. So bogus bits | // |exponent| is negative, but we excluded that above. So bogus bits | ||||
// need only |exponent < ResultWidth|. | // need only |exponent < ResultWidth|. | ||||
// The implicit leading bit matters identically to the other case, so | // The implicit leading bit matters identically to the other case, so | ||||
// again, |exponent < ResultWidth|. | // again, |exponent < ResultWidth|. | ||||
if (exponent < ResultWidth) { | if (exponent < ResultWidth) { | ||||
ResultType implicitOne = ResultType(1) << exponent; | const auto implicitOne = | ||||
static_cast<UnsignedInteger>(UnsignedInteger{1} << exponent); | |||||
result &= implicitOne - 1; // remove bogus bits | result &= implicitOne - 1; // remove bogus bits | ||||
result += implicitOne; // add the implicit bit | result += implicitOne; // add the implicit bit | ||||
} | } | ||||
// Compute the congruent value in the signed range. | // Compute the congruent value in the signed range. | ||||
return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1 | return (bits & mozilla::FloatingPoint<double>::kSignBit) ? ~result + 1 | ||||
: result; | : result; | ||||
} | } | ||||
template <typename ResultType> | template <typename SignedInteger> | ||||
inline ResultType ToIntWidth(double d) { | inline SignedInteger ToSignedInteger(double d) { | ||||
static_assert(mozilla::IsSigned<ResultType>::value, | static_assert(mozilla::IsSigned<SignedInteger>::value, | ||||
"ResultType must be a signed type"); | "SignedInteger must be a signed type"); | ||||
using UnsignedResult = typename mozilla::MakeUnsigned<ResultType>::Type; | using UnsignedInteger = typename mozilla::MakeUnsigned<SignedInteger>::Type; | ||||
UnsignedResult u = ToUintWidth<UnsignedResult>(d); | UnsignedInteger u = ToUnsignedInteger<UnsignedInteger>(d); | ||||
return mozilla::WrapToSigned(u); | return mozilla::WrapToSigned(u); | ||||
} | } | ||||
} // namespace detail | |||||
/* ES5 9.5 ToInt32 (specialized for doubles). */ | |||||
inline int32_t ToInt32(double d) { | |||||
// clang crashes compiling this when targeting arm: | // clang crashes compiling this when targeting arm: | ||||
// https://llvm.org/bugs/show_bug.cgi?id=22974 | // https://llvm.org/bugs/show_bug.cgi?id=22974 | ||||
#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) | #if defined(__arm__) && MOZ_IS_GCC | ||||
template <> | |||||
inline int32_t ToSignedInteger<int32_t>(double d) { | |||||
int32_t i; | int32_t i; | ||||
uint32_t tmp0; | uint32_t tmp0; | ||||
uint32_t tmp1; | uint32_t tmp1; | ||||
uint32_t tmp2; | uint32_t tmp2; | ||||
asm( | asm( | ||||
// We use a pure integer solution here. In the 'softfp' ABI, the argument | // 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 | // 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 | // conversions by itself so some integer code will be required anyway. A | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | asm( | ||||
// +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range | // +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range | ||||
// that will result in a conversion of '0'. | // that will result in a conversion of '0'. | ||||
" mov %0, #0\n" | " mov %0, #0\n" | ||||
"9:\n" | "9:\n" | ||||
: "=r"(i), "=&r"(tmp0), "=&r"(tmp1), "=&r"(tmp2), "=&r"(d) | : "=r"(i), "=&r"(tmp0), "=&r"(tmp1), "=&r"(tmp2), "=&r"(d) | ||||
: "4"(d) | : "4"(d) | ||||
: "cc"); | : "cc"); | ||||
return i; | return i; | ||||
#else | |||||
return detail::ToIntWidth<int32_t>(d); | |||||
#endif | |||||
} | } | ||||
/* ES5 9.6 (specialized for doubles). */ | #endif // defined (__arm__) && MOZ_IS_GCC | ||||
inline uint32_t ToUint32(double d) { return detail::ToUintWidth<uint32_t>(d); } | |||||
namespace detail { | |||||
template <typename IntegerType, | |||||
bool IsUnsigned = mozilla::IsUnsigned<IntegerType>::value> | |||||
struct ToSignedOrUnsignedInteger; | |||||
template <typename IntegerType> | |||||
struct ToSignedOrUnsignedInteger<IntegerType, true> { | |||||
static IntegerType compute(double d) { | |||||
return ToUnsignedInteger<IntegerType>(d); | |||||
} | |||||
}; | |||||
template <typename IntegerType> | |||||
struct ToSignedOrUnsignedInteger<IntegerType, false> { | |||||
static IntegerType compute(double d) { | |||||
return ToSignedInteger<IntegerType>(d); | |||||
} | |||||
}; | |||||
} // namespace detail | |||||
template <typename IntegerType> | |||||
inline IntegerType ToSignedOrUnsignedInteger(double d) { | |||||
return detail::ToSignedOrUnsignedInteger<IntegerType>::compute(d); | |||||
} | |||||
/* WEBIDL 4.2.4 */ | /* WEBIDL 4.2.4 */ | ||||
inline int8_t ToInt8(double d) { return detail::ToIntWidth<int8_t>(d); } | inline int8_t ToInt8(double d) { return ToSignedInteger<int8_t>(d); } | ||||
/* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */ | /* ECMA-262 7.1.10 ToUInt8() specialized for doubles. */ | ||||
inline int8_t ToUint8(double d) { return detail::ToUintWidth<uint8_t>(d); } | inline int8_t ToUint8(double d) { return ToUnsignedInteger<uint8_t>(d); } | ||||
/* WEBIDL 4.2.6 */ | /* WEBIDL 4.2.6 */ | ||||
inline int16_t ToInt16(double d) { return detail::ToIntWidth<int16_t>(d); } | inline int16_t ToInt16(double d) { return ToSignedInteger<int16_t>(d); } | ||||
inline uint16_t ToUint16(double d) { return detail::ToUintWidth<uint16_t>(d); } | inline uint16_t ToUint16(double d) { return ToUnsignedInteger<uint16_t>(d); } | ||||
/* ES5 9.5 ToInt32 (specialized for doubles). */ | |||||
inline int32_t ToInt32(double d) { return ToSignedInteger<int32_t>(d); } | |||||
/* ES5 9.6 (specialized for doubles). */ | |||||
inline uint32_t ToUint32(double d) { return ToUnsignedInteger<uint32_t>(d); } | |||||
/* WEBIDL 4.2.10 */ | /* WEBIDL 4.2.10 */ | ||||
inline int64_t ToInt64(double d) { return detail::ToIntWidth<int64_t>(d); } | inline int64_t ToInt64(double d) { return ToSignedInteger<int64_t>(d); } | ||||
/* WEBIDL 4.2.11 */ | /* WEBIDL 4.2.11 */ | ||||
inline uint64_t ToUint64(double d) { return detail::ToUintWidth<uint64_t>(d); } | inline uint64_t ToUint64(double d) { return ToUnsignedInteger<uint64_t>(d); } | ||||
} // namespace JS | } // namespace JS | ||||
#endif /* js_Conversions_h */ | #endif /* js_Conversions_h */ |
Wildfire Games · Phabricator