michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ michael@0: /* vim: set ts=2 sw=2 et tw=79: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Conversions from jsval to primitive values michael@0: */ michael@0: michael@0: #ifndef mozilla_dom_PrimitiveConversions_h michael@0: #define mozilla_dom_PrimitiveConversions_h michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "jsapi.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/ErrorResult.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: template michael@0: struct TypeName { michael@0: }; michael@0: michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "byte"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "octet"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "short"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "unsigned short"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "long"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "unsigned long"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "long long"; michael@0: } michael@0: }; michael@0: template<> michael@0: struct TypeName { michael@0: static const char* value() { michael@0: return "unsigned long long"; michael@0: } michael@0: }; michael@0: michael@0: michael@0: enum ConversionBehavior { michael@0: eDefault, michael@0: eEnforceRange, michael@0: eClamp michael@0: }; michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits { michael@0: }; michael@0: michael@0: template michael@0: struct DisallowedConversion { michael@0: typedef int jstype; michael@0: typedef int intermediateType; michael@0: michael@0: private: michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: MOZ_CRASH("This should never be instantiated!"); michael@0: } michael@0: }; michael@0: michael@0: struct PrimitiveConversionTraits_smallInt { michael@0: // The output of JS::ToInt32 is determined as follows: michael@0: // 1) The value is converted to a double michael@0: // 2) Anything that's not a finite double returns 0 michael@0: // 3) The double is rounded towards zero to the nearest integer michael@0: // 4) The resulting integer is reduced mod 2^32. The output of this michael@0: // operation is an integer in the range [0, 2^32). michael@0: // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it. michael@0: // michael@0: // The result of all this is a number in the range [-2^31, 2^31) michael@0: // michael@0: // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types michael@0: // are defined in the same way, except that step 4 uses reduction mod michael@0: // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5 michael@0: // is only done for the signed types. michael@0: // michael@0: // C/C++ define integer conversion semantics to unsigned types as taking michael@0: // your input integer mod (1 + largest value representable in the michael@0: // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32, michael@0: // converting to the unsigned int of the relevant width will correctly michael@0: // perform step 4; in particular, the 2^32 possibly subtracted in step 5 michael@0: // will become 0. michael@0: // michael@0: // Once we have step 4 done, we're just going to assume 2s-complement michael@0: // representation and cast directly to the type we really want. michael@0: // michael@0: // So we can cast directly for all unsigned types and for int32_t; for michael@0: // the smaller-width signed types we need to cast through the michael@0: // corresponding unsigned type. michael@0: typedef int32_t jstype; michael@0: typedef int32_t intermediateType; michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: return JS::ToInt32(cx, v, retval); michael@0: } michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: typedef uint8_t intermediateType; michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: typedef uint16_t intermediateType; michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_smallInt { michael@0: }; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits { michael@0: typedef int64_t jstype; michael@0: typedef int64_t intermediateType; michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: return JS::ToInt64(cx, v, retval); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits { michael@0: typedef uint64_t jstype; michael@0: typedef uint64_t intermediateType; michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: return JS::ToUint64(cx, v, retval); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits_Limits { michael@0: static inline T min() { michael@0: return std::numeric_limits::min(); michael@0: } michael@0: static inline T max() { michael@0: return std::numeric_limits::max(); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits_Limits { michael@0: static inline int64_t min() { michael@0: return -(1LL << 53); michael@0: } michael@0: static inline int64_t max() { michael@0: return (1LL << 53); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits_Limits { michael@0: static inline uint64_t min() { michael@0: return 0; michael@0: } michael@0: static inline uint64_t max() { michael@0: return (1LL << 53); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits_ToCheckedIntHelper { michael@0: typedef T jstype; michael@0: typedef T intermediateType; michael@0: michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: double intermediate; michael@0: if (!JS::ToNumber(cx, v, &intermediate)) { michael@0: return false; michael@0: } michael@0: michael@0: return Enforce(cx, intermediate, retval); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: inline bool michael@0: PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval) michael@0: { michael@0: static_assert(std::numeric_limits::is_integer, michael@0: "This can only be applied to integers!"); michael@0: michael@0: if (!mozilla::IsFinite(d)) { michael@0: return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName::value()); michael@0: } michael@0: michael@0: bool neg = (d < 0); michael@0: double rounded = floor(neg ? -d : d); michael@0: rounded = neg ? -rounded : rounded; michael@0: if (rounded < PrimitiveConversionTraits_Limits::min() || michael@0: rounded > PrimitiveConversionTraits_Limits::max()) { michael@0: return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName::value()); michael@0: } michael@0: michael@0: *retval = static_cast(rounded); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits : michael@0: public PrimitiveConversionTraits_ToCheckedIntHelper > { michael@0: }; michael@0: michael@0: template michael@0: inline bool michael@0: PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval) michael@0: { michael@0: static_assert(std::numeric_limits::is_integer, michael@0: "This can only be applied to integers!"); michael@0: michael@0: if (mozilla::IsNaN(d)) { michael@0: *retval = 0; michael@0: return true; michael@0: } michael@0: if (d >= PrimitiveConversionTraits_Limits::max()) { michael@0: *retval = PrimitiveConversionTraits_Limits::max(); michael@0: return true; michael@0: } michael@0: if (d <= PrimitiveConversionTraits_Limits::min()) { michael@0: *retval = PrimitiveConversionTraits_Limits::min(); michael@0: return true; michael@0: } michael@0: michael@0: MOZ_ASSERT(mozilla::IsFinite(d)); michael@0: michael@0: // Banker's rounding (round ties towards even). michael@0: // We move away from 0 by 0.5f and then truncate. That gets us the right michael@0: // answer for any starting value except plus or minus N.5. With a starting michael@0: // value of that form, we now have plus or minus N+1. If N is odd, this is michael@0: // the correct result. If N is even, plus or minus N is the correct result. michael@0: double toTruncate = (d < 0) ? d - 0.5 : d + 0.5; michael@0: michael@0: T truncated = static_cast(toTruncate); michael@0: michael@0: if (truncated == toTruncate) { michael@0: /* michael@0: * It was a tie (since moving away from 0 by 0.5 gave us the exact integer michael@0: * we want). Since we rounded away from 0, we either already have an even michael@0: * number or we have an odd number but the number we want is one closer to michael@0: * 0. So just unconditionally masking out the ones bit should do the trick michael@0: * to get us the value we want. michael@0: */ michael@0: truncated &= ~1; michael@0: } michael@0: michael@0: *retval = truncated; michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits : michael@0: public PrimitiveConversionTraits_ToCheckedIntHelper > { michael@0: }; michael@0: michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits : public DisallowedConversion {}; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits { michael@0: typedef bool jstype; michael@0: typedef bool intermediateType; michael@0: static inline bool converter(JSContext* /* unused */, JS::Handle v, michael@0: jstype* retval) { michael@0: *retval = JS::ToBoolean(v); michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits : public DisallowedConversion {}; michael@0: michael@0: template michael@0: struct PrimitiveConversionTraits : public DisallowedConversion {}; michael@0: michael@0: struct PrimitiveConversionTraits_float { michael@0: typedef double jstype; michael@0: typedef double intermediateType; michael@0: static inline bool converter(JSContext* cx, JS::Handle v, michael@0: jstype* retval) { michael@0: return JS::ToNumber(cx, v, retval); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_float { michael@0: }; michael@0: template<> michael@0: struct PrimitiveConversionTraits : PrimitiveConversionTraits_float { michael@0: }; michael@0: michael@0: michael@0: template michael@0: bool ValueToPrimitive(JSContext* cx, JS::Handle v, T* retval) michael@0: { michael@0: typename PrimitiveConversionTraits::jstype t; michael@0: if (!PrimitiveConversionTraits::converter(cx, v, &t)) michael@0: return false; michael@0: michael@0: *retval = static_cast( michael@0: static_cast::intermediateType>(t)); michael@0: return true; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_dom_PrimitiveConversions_h */