diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/bindings/PrimitiveConversions.h | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/bindings/PrimitiveConversions.h')
-rw-r--r-- | dom/bindings/PrimitiveConversions.h | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/dom/bindings/PrimitiveConversions.h b/dom/bindings/PrimitiveConversions.h new file mode 100644 index 0000000000..1c5b62ec8f --- /dev/null +++ b/dom/bindings/PrimitiveConversions.h @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Conversions from jsval to primitive values + */ + +#ifndef mozilla_dom_PrimitiveConversions_h +#define mozilla_dom_PrimitiveConversions_h + +#include <limits> +#include <math.h> +#include <stdint.h> + +#include "js/Conversions.h" +#include "js/RootingAPI.h" +#include "mozilla/Assertions.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/dom/BindingCallContext.h" + +namespace mozilla::dom { + +template <typename T> +struct TypeName {}; + +template <> +struct TypeName<int8_t> { + static const char* value() { return "byte"; } +}; +template <> +struct TypeName<uint8_t> { + static const char* value() { return "octet"; } +}; +template <> +struct TypeName<int16_t> { + static const char* value() { return "short"; } +}; +template <> +struct TypeName<uint16_t> { + static const char* value() { return "unsigned short"; } +}; +template <> +struct TypeName<int32_t> { + static const char* value() { return "long"; } +}; +template <> +struct TypeName<uint32_t> { + static const char* value() { return "unsigned long"; } +}; +template <> +struct TypeName<int64_t> { + static const char* value() { return "long long"; } +}; +template <> +struct TypeName<uint64_t> { + static const char* value() { return "unsigned long long"; } +}; + +enum ConversionBehavior { eDefault, eEnforceRange, eClamp }; + +template <typename T, ConversionBehavior B> +struct PrimitiveConversionTraits {}; + +template <typename T> +struct DisallowedConversion { + typedef int jstype; + typedef int intermediateType; + + private: + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + MOZ_CRASH("This should never be instantiated!"); + } +}; + +struct PrimitiveConversionTraits_smallInt { + // The output of JS::ToInt32 is determined as follows: + // 1) The value is converted to a double + // 2) Anything that's not a finite double returns 0 + // 3) The double is rounded towards zero to the nearest integer + // 4) The resulting integer is reduced mod 2^32. The output of this + // operation is an integer in the range [0, 2^32). + // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it. + // + // The result of all this is a number in the range [-2^31, 2^31) + // + // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types + // are defined in the same way, except that step 4 uses reduction mod + // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5 + // is only done for the signed types. + // + // C/C++ define integer conversion semantics to unsigned types as taking + // your input integer mod (1 + largest value representable in the + // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32, + // converting to the unsigned int of the relevant width will correctly + // perform step 4; in particular, the 2^32 possibly subtracted in step 5 + // will become 0. + // + // Once we have step 4 done, we're just going to assume 2s-complement + // representation and cast directly to the type we really want. + // + // So we can cast directly for all unsigned types and for int32_t; for + // the smaller-width signed types we need to cast through the + // corresponding unsigned type. + typedef int32_t jstype; + typedef int32_t intermediateType; + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + return JS::ToInt32(cx, v, retval); + } +}; +template <> +struct PrimitiveConversionTraits<int8_t, eDefault> + : PrimitiveConversionTraits_smallInt { + typedef uint8_t intermediateType; +}; +template <> +struct PrimitiveConversionTraits<uint8_t, eDefault> + : PrimitiveConversionTraits_smallInt {}; +template <> +struct PrimitiveConversionTraits<int16_t, eDefault> + : PrimitiveConversionTraits_smallInt { + typedef uint16_t intermediateType; +}; +template <> +struct PrimitiveConversionTraits<uint16_t, eDefault> + : PrimitiveConversionTraits_smallInt {}; +template <> +struct PrimitiveConversionTraits<int32_t, eDefault> + : PrimitiveConversionTraits_smallInt {}; +template <> +struct PrimitiveConversionTraits<uint32_t, eDefault> + : PrimitiveConversionTraits_smallInt {}; + +template <> +struct PrimitiveConversionTraits<int64_t, eDefault> { + typedef int64_t jstype; + typedef int64_t intermediateType; + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + return JS::ToInt64(cx, v, retval); + } +}; + +template <> +struct PrimitiveConversionTraits<uint64_t, eDefault> { + typedef uint64_t jstype; + typedef uint64_t intermediateType; + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + return JS::ToUint64(cx, v, retval); + } +}; + +template <typename T> +struct PrimitiveConversionTraits_Limits { + static inline T min() { return std::numeric_limits<T>::min(); } + static inline T max() { return std::numeric_limits<T>::max(); } +}; + +template <> +struct PrimitiveConversionTraits_Limits<int64_t> { + static inline int64_t min() { return -(1LL << 53) + 1; } + static inline int64_t max() { return (1LL << 53) - 1; } +}; + +template <> +struct PrimitiveConversionTraits_Limits<uint64_t> { + static inline uint64_t min() { return 0; } + static inline uint64_t max() { return (1LL << 53) - 1; } +}; + +template <typename T, typename U, + bool (*Enforce)(U cx, const char* sourceDescription, const double& d, + T* retval)> +struct PrimitiveConversionTraits_ToCheckedIntHelper { + typedef T jstype; + typedef T intermediateType; + + static inline bool converter(U cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + double intermediate; + if (!JS::ToNumber(cx, v, &intermediate)) { + return false; + } + + return Enforce(cx, sourceDescription, intermediate, retval); + } +}; + +template <typename T> +inline bool PrimitiveConversionTraits_EnforceRange( + BindingCallContext& cx, const char* sourceDescription, const double& d, + T* retval) { + static_assert(std::numeric_limits<T>::is_integer, + "This can only be applied to integers!"); + + if (!std::isfinite(d)) { + return cx.ThrowErrorMessage<MSG_ENFORCE_RANGE_NON_FINITE>( + sourceDescription, TypeName<T>::value()); + } + + bool neg = (d < 0); + double rounded = floor(neg ? -d : d); + rounded = neg ? -rounded : rounded; + if (rounded < PrimitiveConversionTraits_Limits<T>::min() || + rounded > PrimitiveConversionTraits_Limits<T>::max()) { + return cx.ThrowErrorMessage<MSG_ENFORCE_RANGE_OUT_OF_RANGE>( + sourceDescription, TypeName<T>::value()); + } + + *retval = static_cast<T>(rounded); + return true; +} + +template <typename T> +struct PrimitiveConversionTraits<T, eEnforceRange> + : public PrimitiveConversionTraits_ToCheckedIntHelper< + T, BindingCallContext&, PrimitiveConversionTraits_EnforceRange<T> > { +}; + +template <typename T> +inline bool PrimitiveConversionTraits_Clamp(JSContext* cx, + const char* sourceDescription, + const double& d, T* retval) { + static_assert(std::numeric_limits<T>::is_integer, + "This can only be applied to integers!"); + + if (std::isnan(d)) { + *retval = 0; + return true; + } + if (d >= PrimitiveConversionTraits_Limits<T>::max()) { + *retval = PrimitiveConversionTraits_Limits<T>::max(); + return true; + } + if (d <= PrimitiveConversionTraits_Limits<T>::min()) { + *retval = PrimitiveConversionTraits_Limits<T>::min(); + return true; + } + + MOZ_ASSERT(std::isfinite(d)); + + // Banker's rounding (round ties towards even). + // We move away from 0 by 0.5f and then truncate. That gets us the right + // answer for any starting value except plus or minus N.5. With a starting + // value of that form, we now have plus or minus N+1. If N is odd, this is + // the correct result. If N is even, plus or minus N is the correct result. + double toTruncate = (d < 0) ? d - 0.5 : d + 0.5; + + T truncated = static_cast<T>(toTruncate); + + if (truncated == toTruncate) { + /* + * It was a tie (since moving away from 0 by 0.5 gave us the exact integer + * we want). Since we rounded away from 0, we either already have an even + * number or we have an odd number but the number we want is one closer to + * 0. So just unconditionally masking out the ones bit should do the trick + * to get us the value we want. + */ + truncated &= ~1; + } + + *retval = truncated; + return true; +} + +template <typename T> +struct PrimitiveConversionTraits<T, eClamp> + : public PrimitiveConversionTraits_ToCheckedIntHelper< + T, JSContext*, PrimitiveConversionTraits_Clamp<T> > {}; + +template <ConversionBehavior B> +struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> { +}; + +template <> +struct PrimitiveConversionTraits<bool, eDefault> { + typedef bool jstype; + typedef bool intermediateType; + static inline bool converter(JSContext* /* unused */, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + *retval = JS::ToBoolean(v); + return true; + } +}; + +template <ConversionBehavior B> +struct PrimitiveConversionTraits<float, B> + : public DisallowedConversion<float> {}; + +template <ConversionBehavior B> +struct PrimitiveConversionTraits<double, B> + : public DisallowedConversion<double> {}; + +struct PrimitiveConversionTraits_float { + typedef double jstype; + typedef double intermediateType; + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, + const char* sourceDescription, jstype* retval) { + return JS::ToNumber(cx, v, retval); + } +}; + +template <> +struct PrimitiveConversionTraits<float, eDefault> + : PrimitiveConversionTraits_float {}; +template <> +struct PrimitiveConversionTraits<double, eDefault> + : PrimitiveConversionTraits_float {}; + +template <typename T, ConversionBehavior B, typename U> +bool ValueToPrimitive(U& cx, JS::Handle<JS::Value> v, + const char* sourceDescription, T* retval) { + typename PrimitiveConversionTraits<T, B>::jstype t; + if (!PrimitiveConversionTraits<T, B>::converter(cx, v, sourceDescription, &t)) + return false; + + *retval = static_cast<T>( + static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>( + t)); + return true; +} + +} // namespace mozilla::dom + +#endif /* mozilla_dom_PrimitiveConversions_h */ |