mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
fa5d4cca1f
Differential Revision: https://phabricator.services.mozilla.com/D173036
331 lines
11 KiB
C++
331 lines
11 KiB
C++
/* -*- 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 */
|