1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Slightly adapted for inclusion in V8.
6 // Copyright 2014 the V8 project authors. All rights reserved.
7 // List of adaptations:
8 // - include guard names
9 // - wrap in v8 namespace
10 // - formatting (git cl format)
11 
12 #ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_
13 #define V8_BASE_SAFE_CONVERSIONS_IMPL_H_
14 
15 #include <stdint.h>
16 
17 #include <limits>
18 #include <type_traits>
19 
20 #if defined(__GNUC__) || defined(__clang__)
21 #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
22 #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
23 #else
24 #define BASE_NUMERICS_LIKELY(x) (x)
25 #define BASE_NUMERICS_UNLIKELY(x) (x)
26 #endif
27 
28 namespace v8 {
29 namespace base {
30 namespace internal {
31 
32 // The std library doesn't provide a binary max_exponent for integers, however
33 // we can compute an analog using std::numeric_limits<>::digits.
34 template <typename NumericType>
35 struct MaxExponent {
36   static const int value = std::is_floating_point<NumericType>::value
37                                ? std::numeric_limits<NumericType>::max_exponent
38                                : std::numeric_limits<NumericType>::digits + 1;
39 };
40 
41 // The number of bits (including the sign) in an integer. Eliminates sizeof
42 // hacks.
43 template <typename NumericType>
44 struct IntegerBitsPlusSign {
45   static const int value = std::numeric_limits<NumericType>::digits +
46                            std::is_signed<NumericType>::value;
47 };
48 
49 // Helper templates for integer manipulations.
50 
51 template <typename Integer>
52 struct PositionOfSignBit {
53   static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
54 };
55 
56 // Determines if a numeric value is negative without throwing compiler
57 // warnings on: unsigned(value) < 0.
58 template <typename T,
59           typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T value)60 constexpr bool IsValueNegative(T value) {
61   static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
62   return value < 0;
63 }
64 
65 template <typename T,
66           typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
IsValueNegative(T)67 constexpr bool IsValueNegative(T) {
68   static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
69   return false;
70 }
71 
72 // This performs a fast negation, returning a signed value. It works on unsigned
73 // arguments, but probably doesn't do what you want for any unsigned value
74 // larger than max / 2 + 1 (i.e. signed min cast to unsigned).
75 template <typename T>
ConditionalNegate(T x,bool is_negative)76 constexpr typename std::make_signed<T>::type ConditionalNegate(
77     T x, bool is_negative) {
78   static_assert(std::is_integral<T>::value, "Type must be integral");
79   using SignedT = typename std::make_signed<T>::type;
80   using UnsignedT = typename std::make_unsigned<T>::type;
81   return static_cast<SignedT>(
82       (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
83 }
84 
85 // This performs a safe, absolute value via unsigned overflow.
86 template <typename T>
SafeUnsignedAbs(T value)87 constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
88   static_assert(std::is_integral<T>::value, "Type must be integral");
89   using UnsignedT = typename std::make_unsigned<T>::type;
90   return IsValueNegative(value)
91              ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
92              : static_cast<UnsignedT>(value);
93 }
94 
95 // This allows us to switch paths on known compile-time constants.
96 #if defined(__clang__) || defined(__GNUC__)
CanDetectCompileTimeConstant()97 constexpr bool CanDetectCompileTimeConstant() { return true; }
98 template <typename T>
IsCompileTimeConstant(const T v)99 constexpr bool IsCompileTimeConstant(const T v) {
100   return __builtin_constant_p(v);
101 }
102 #else
CanDetectCompileTimeConstant()103 constexpr bool CanDetectCompileTimeConstant() { return false; }
104 template <typename T>
IsCompileTimeConstant(const T)105 constexpr bool IsCompileTimeConstant(const T) {
106   return false;
107 }
108 #endif
109 template <typename T>
MustTreatAsConstexpr(const T v)110 constexpr bool MustTreatAsConstexpr(const T v) {
111   // Either we can't detect a compile-time constant, and must always use the
112   // constexpr path, or we know we have a compile-time constant.
113   return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
114 }
115 
116 // Forces a crash, like a CHECK(false). Used for numeric boundary errors.
117 // Also used in a constexpr template to trigger a compilation failure on
118 // an error condition.
119 struct CheckOnFailure {
120   template <typename T>
HandleFailureCheckOnFailure121   static T HandleFailure() {
122 #if defined(_MSC_VER)
123     __debugbreak();
124 #elif defined(__GNUC__) || defined(__clang__)
125     __builtin_trap();
126 #else
127     ((void)(*(volatile char*)0 = 0));
128 #endif
129     return T();
130   }
131 };
132 
133 enum IntegerRepresentation {
134   INTEGER_REPRESENTATION_UNSIGNED,
135   INTEGER_REPRESENTATION_SIGNED
136 };
137 
138 // A range for a given nunmeric Src type is contained for a given numeric Dst
139 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
140 // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
141 // We implement this as template specializations rather than simple static
142 // comparisons to ensure type correctness in our comparisons.
143 enum NumericRangeRepresentation {
144   NUMERIC_RANGE_NOT_CONTAINED,
145   NUMERIC_RANGE_CONTAINED
146 };
147 
148 // Helper templates to statically determine if our destination type can contain
149 // maximum and minimum values represented by the source type.
150 
151 template <typename Dst, typename Src,
152           IntegerRepresentation DstSign = std::is_signed<Dst>::value
153                                               ? INTEGER_REPRESENTATION_SIGNED
154                                               : INTEGER_REPRESENTATION_UNSIGNED,
155           IntegerRepresentation SrcSign = std::is_signed<Src>::value
156                                               ? INTEGER_REPRESENTATION_SIGNED
157                                               : INTEGER_REPRESENTATION_UNSIGNED>
158 struct StaticDstRangeRelationToSrcRange;
159 
160 // Same sign: Dst is guaranteed to contain Src only if its range is equal or
161 // larger.
162 template <typename Dst, typename Src, IntegerRepresentation Sign>
163 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
164   static const NumericRangeRepresentation value =
165       MaxExponent<Dst>::value >= MaxExponent<Src>::value
166           ? NUMERIC_RANGE_CONTAINED
167           : NUMERIC_RANGE_NOT_CONTAINED;
168 };
169 
170 // Unsigned to signed: Dst is guaranteed to contain source only if its range is
171 // larger.
172 template <typename Dst, typename Src>
173 struct StaticDstRangeRelationToSrcRange<Dst,
174                                         Src,
175                                         INTEGER_REPRESENTATION_SIGNED,
176                                         INTEGER_REPRESENTATION_UNSIGNED> {
177   static const NumericRangeRepresentation value =
178       MaxExponent<Dst>::value > MaxExponent<Src>::value
179           ? NUMERIC_RANGE_CONTAINED
180           : NUMERIC_RANGE_NOT_CONTAINED;
181 };
182 
183 // Signed to unsigned: Dst cannot be statically determined to contain Src.
184 template <typename Dst, typename Src>
185 struct StaticDstRangeRelationToSrcRange<Dst,
186                                         Src,
187                                         INTEGER_REPRESENTATION_UNSIGNED,
188                                         INTEGER_REPRESENTATION_SIGNED> {
189   static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
190 };
191 
192 // This class wraps the range constraints as separate booleans so the compiler
193 // can identify constants and eliminate unused code paths.
194 class RangeCheck {
195  public:
196   constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
197       : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
198   constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
199   constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
200   constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
201   constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
202   constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
203   constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
204   constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
205   constexpr bool operator==(const RangeCheck rhs) const {
206     return is_underflow_ == rhs.is_underflow_ &&
207            is_overflow_ == rhs.is_overflow_;
208   }
209   constexpr bool operator!=(const RangeCheck rhs) const {
210     return !(*this == rhs);
211   }
212 
213  private:
214   // Do not change the order of these member variables. The integral conversion
215   // optimization depends on this exact order.
216   const bool is_underflow_;
217   const bool is_overflow_;
218 };
219 
220 // The following helper template addresses a corner case in range checks for
221 // conversion from a floating-point type to an integral type of smaller range
222 // but larger precision (e.g. float -> unsigned). The problem is as follows:
223 //   1. Integral maximum is always one less than a power of two, so it must be
224 //      truncated to fit the mantissa of the floating point. The direction of
225 //      rounding is implementation defined, but by default it's always IEEE
226 //      floats, which round to nearest and thus result in a value of larger
227 //      magnitude than the integral value.
228 //      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX
229 //                                   // is 4294967295u.
230 //   2. If the floating point value is equal to the promoted integral maximum
231 //      value, a range check will erroneously pass.
232 //      Example: (4294967296f <= 4294967295u) // This is true due to a precision
233 //                                            // loss in rounding up to float.
234 //   3. When the floating point value is then converted to an integral, the
235 //      resulting value is out of range for the target integral type and
236 //      thus is implementation defined.
237 //      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0.
238 // To fix this bug we manually truncate the maximum value when the destination
239 // type is an integral of larger precision than the source floating-point type,
240 // such that the resulting maximum is represented exactly as a floating point.
241 template <typename Dst, typename Src, template <typename> class Bounds>
242 struct NarrowingRange {
243   using SrcLimits = std::numeric_limits<Src>;
244   using DstLimits = typename std::numeric_limits<Dst>;
245 
246   // Computes the mask required to make an accurate comparison between types.
247   static const int kShift =
248       (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
249        SrcLimits::digits < DstLimits::digits)
250           ? (DstLimits::digits - SrcLimits::digits)
251           : 0;
252   template <typename T, typename std::enable_if<
253                             std::is_integral<T>::value>::type* = nullptr>
254 
255   // Masks out the integer bits that are beyond the precision of the
256   // intermediate type used for comparison.
257   static constexpr T Adjust(T value) {
258     static_assert(std::is_same<T, Dst>::value, "");
259     static_assert(kShift < DstLimits::digits, "");
260     return static_cast<T>(
261         ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
262                           IsValueNegative(value)));
263   }
264 
265   template <typename T, typename std::enable_if<
266                             std::is_floating_point<T>::value>::type* = nullptr>
267   static constexpr T Adjust(T value) {
268     static_assert(std::is_same<T, Dst>::value, "");
269     static_assert(kShift == 0, "");
270     return value;
271   }
272 
273   static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
274   static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
275 };
276 
277 template <typename Dst, typename Src, template <typename> class Bounds,
278           IntegerRepresentation DstSign = std::is_signed<Dst>::value
279                                               ? INTEGER_REPRESENTATION_SIGNED
280                                               : INTEGER_REPRESENTATION_UNSIGNED,
281           IntegerRepresentation SrcSign = std::is_signed<Src>::value
282                                               ? INTEGER_REPRESENTATION_SIGNED
283                                               : INTEGER_REPRESENTATION_UNSIGNED,
284           NumericRangeRepresentation DstRange =
285               StaticDstRangeRelationToSrcRange<Dst, Src>::value>
286 struct DstRangeRelationToSrcRangeImpl;
287 
288 // The following templates are for ranges that must be verified at runtime. We
289 // split it into checks based on signedness to avoid confusing casts and
290 // compiler warnings on signed an unsigned comparisons.
291 
292 // Same sign narrowing: The range is contained for normal limits.
293 template <typename Dst, typename Src, template <typename> class Bounds,
294           IntegerRepresentation DstSign, IntegerRepresentation SrcSign>
295 struct DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds, DstSign, SrcSign,
296                                       NUMERIC_RANGE_CONTAINED> {
297   static constexpr RangeCheck Check(Src value) {
298     using SrcLimits = std::numeric_limits<Src>;
299     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
300     return RangeCheck(
301         static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
302             static_cast<Dst>(value) >= DstLimits::lowest(),
303         static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
304             static_cast<Dst>(value) <= DstLimits::max());
305   }
306 };
307 
308 // Signed to signed narrowing: Both the upper and lower boundaries may be
309 // exceeded for standard limits.
310 template <typename Dst, typename Src, template <typename> class Bounds>
311 struct DstRangeRelationToSrcRangeImpl<
312     Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
313     INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
314   static constexpr RangeCheck Check(Src value) {
315     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
316     return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
317   }
318 };
319 
320 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
321 // standard limits.
322 template <typename Dst, typename Src, template <typename> class Bounds>
323 struct DstRangeRelationToSrcRangeImpl<
324     Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
325     INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
326   static constexpr RangeCheck Check(Src value) {
327     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
328     return RangeCheck(
329         DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
330         value <= DstLimits::max());
331   }
332 };
333 
334 // Unsigned to signed: Only the upper bound can be exceeded for standard limits.
335 template <typename Dst, typename Src, template <typename> class Bounds>
336 struct DstRangeRelationToSrcRangeImpl<
337     Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED,
338     INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
339   static constexpr RangeCheck Check(Src value) {
340     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
341     using Promotion = decltype(Src() + Dst());
342     return RangeCheck(DstLimits::lowest() <= Dst(0) ||
343                           static_cast<Promotion>(value) >=
344                               static_cast<Promotion>(DstLimits::lowest()),
345                       static_cast<Promotion>(value) <=
346                           static_cast<Promotion>(DstLimits::max()));
347   }
348 };
349 
350 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
351 // and any negative value exceeds the lower boundary for standard limits.
352 template <typename Dst, typename Src, template <typename> class Bounds>
353 struct DstRangeRelationToSrcRangeImpl<
354     Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED,
355     INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> {
356   static constexpr RangeCheck Check(Src value) {
357     using SrcLimits = std::numeric_limits<Src>;
358     using DstLimits = NarrowingRange<Dst, Src, Bounds>;
359     using Promotion = decltype(Src() + Dst());
360     bool ge_zero = false;
361     // Converting floating-point to integer will discard fractional part, so
362     // values in (-1.0, -0.0) will truncate to 0 and fit in Dst.
363     if (std::is_floating_point<Src>::value) {
364       ge_zero = value > Src(-1);
365     } else {
366       ge_zero = value >= Src(0);
367     }
368     return RangeCheck(
369         ge_zero && (DstLimits::lowest() == 0 ||
370                     static_cast<Dst>(value) >= DstLimits::lowest()),
371         static_cast<Promotion>(SrcLimits::max()) <=
372                 static_cast<Promotion>(DstLimits::max()) ||
373             static_cast<Promotion>(value) <=
374                 static_cast<Promotion>(DstLimits::max()));
375   }
376 };
377 
378 // Simple wrapper for statically checking if a type's range is contained.
379 template <typename Dst, typename Src>
380 struct IsTypeInRangeForNumericType {
381   static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
382                             NUMERIC_RANGE_CONTAINED;
383 };
384 
385 template <typename Dst, template <typename> class Bounds = std::numeric_limits,
386           typename Src>
387 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
388   static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
389   static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
390   static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
391   return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
392 }
393 
394 // Integer promotion templates used by the portable checked integer arithmetic.
395 template <size_t Size, bool IsSigned>
396 struct IntegerForDigitsAndSign;
397 
398 #define INTEGER_FOR_DIGITS_AND_SIGN(I)                          \
399   template <>                                                   \
400   struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
401                                  std::is_signed<I>::value> {    \
402     using type = I;                                             \
403   }
404 
405 INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
406 INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
407 INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
408 INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
409 INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
410 INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
411 INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
412 INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
413 #undef INTEGER_FOR_DIGITS_AND_SIGN
414 
415 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
416 // support 128-bit math, then the ArithmeticPromotion template below will need
417 // to be updated (or more likely replaced with a decltype expression).
418 static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
419               "Max integer size not supported for this toolchain.");
420 
421 template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
422 struct TwiceWiderInteger {
423   using type =
424       typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
425                                        IsSigned>::type;
426 };
427 
428 enum ArithmeticPromotionCategory {
429   LEFT_PROMOTION,  // Use the type of the left-hand argument.
430   RIGHT_PROMOTION  // Use the type of the right-hand argument.
431 };
432 
433 // Determines the type that can represent the largest positive value.
434 template <typename Lhs, typename Rhs,
435           ArithmeticPromotionCategory Promotion =
436               (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
437                   ? LEFT_PROMOTION
438                   : RIGHT_PROMOTION>
439 struct MaxExponentPromotion;
440 
441 template <typename Lhs, typename Rhs>
442 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
443   using type = Lhs;
444 };
445 
446 template <typename Lhs, typename Rhs>
447 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
448   using type = Rhs;
449 };
450 
451 // Determines the type that can represent the lowest arithmetic value.
452 template <typename Lhs, typename Rhs,
453           ArithmeticPromotionCategory Promotion =
454               std::is_signed<Lhs>::value
455                   ? (std::is_signed<Rhs>::value
456                          ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
457                                 ? LEFT_PROMOTION
458                                 : RIGHT_PROMOTION)
459                          : LEFT_PROMOTION)
460                   : (std::is_signed<Rhs>::value
461                          ? RIGHT_PROMOTION
462                          : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
463                                 ? LEFT_PROMOTION
464                                 : RIGHT_PROMOTION))>
465 struct LowestValuePromotion;
466 
467 template <typename Lhs, typename Rhs>
468 struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
469   using type = Lhs;
470 };
471 
472 template <typename Lhs, typename Rhs>
473 struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
474   using type = Rhs;
475 };
476 
477 // Determines the type that is best able to represent an arithmetic result.
478 template <
479     typename Lhs, typename Rhs = Lhs,
480     bool is_intmax_type =
481         std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
482             IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
483                 value == IntegerBitsPlusSign<intmax_t>::value,
484     bool is_max_exponent =
485         StaticDstRangeRelationToSrcRange<
486             typename MaxExponentPromotion<Lhs, Rhs>::type, Lhs>::value ==
487         NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
488             typename MaxExponentPromotion<Lhs, Rhs>::type, Rhs>::value ==
489         NUMERIC_RANGE_CONTAINED>
490 struct BigEnoughPromotion;
491 
492 // The side with the max exponent is big enough.
493 template <typename Lhs, typename Rhs, bool is_intmax_type>
494 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
495   using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
496   static const bool is_contained = true;
497 };
498 
499 // We can use a twice wider type to fit.
500 template <typename Lhs, typename Rhs>
501 struct BigEnoughPromotion<Lhs, Rhs, false, false> {
502   using type =
503       typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
504                                  std::is_signed<Lhs>::value ||
505                                      std::is_signed<Rhs>::value>::type;
506   static const bool is_contained = true;
507 };
508 
509 // No type is large enough.
510 template <typename Lhs, typename Rhs>
511 struct BigEnoughPromotion<Lhs, Rhs, true, false> {
512   using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
513   static const bool is_contained = false;
514 };
515 
516 // We can statically check if operations on the provided types can wrap, so we
517 // can skip the checked operations if they're not needed. So, for an integer we
518 // care if the destination type preserves the sign and is twice the width of
519 // the source.
520 template <typename T, typename Lhs, typename Rhs = Lhs>
521 struct IsIntegerArithmeticSafe {
522   static const bool value =
523       !std::is_floating_point<T>::value &&
524       !std::is_floating_point<Lhs>::value &&
525       !std::is_floating_point<Rhs>::value &&
526       std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
527       IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
528       std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
529       IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
530 };
531 
532 // Promotes to a type that can represent any possible result of a binary
533 // arithmetic operation with the source types.
534 template <typename Lhs, typename Rhs,
535           bool is_promotion_possible = IsIntegerArithmeticSafe<
536               typename std::conditional<std::is_signed<Lhs>::value ||
537                                             std::is_signed<Rhs>::value,
538                                         intmax_t, uintmax_t>::type,
539               typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
540 struct FastIntegerArithmeticPromotion;
541 
542 template <typename Lhs, typename Rhs>
543 struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
544   using type =
545       typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
546                                  std::is_signed<Lhs>::value ||
547                                      std::is_signed<Rhs>::value>::type;
548   static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
549   static const bool is_contained = true;
550 };
551 
552 template <typename Lhs, typename Rhs>
553 struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
554   using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
555   static const bool is_contained = false;
556 };
557 
558 // Extracts the underlying type from an enum.
559 template <typename T, bool is_enum = std::is_enum<T>::value>
560 struct ArithmeticOrUnderlyingEnum;
561 
562 template <typename T>
563 struct ArithmeticOrUnderlyingEnum<T, true> {
564   using type = typename std::underlying_type<T>::type;
565   static const bool value = std::is_arithmetic<type>::value;
566 };
567 
568 template <typename T>
569 struct ArithmeticOrUnderlyingEnum<T, false> {
570   using type = T;
571   static const bool value = std::is_arithmetic<type>::value;
572 };
573 
574 // The following are helper templates used in the CheckedNumeric class.
575 template <typename T>
576 class CheckedNumeric;
577 
578 template <typename T>
579 class ClampedNumeric;
580 
581 template <typename T>
582 class StrictNumeric;
583 
584 // Used to treat CheckedNumeric and arithmetic underlying types the same.
585 template <typename T>
586 struct UnderlyingType {
587   using type = typename ArithmeticOrUnderlyingEnum<T>::type;
588   static const bool is_numeric = std::is_arithmetic<type>::value;
589   static const bool is_checked = false;
590   static const bool is_clamped = false;
591   static const bool is_strict = false;
592 };
593 
594 template <typename T>
595 struct UnderlyingType<CheckedNumeric<T>> {
596   using type = T;
597   static const bool is_numeric = true;
598   static const bool is_checked = true;
599   static const bool is_clamped = false;
600   static const bool is_strict = false;
601 };
602 
603 template <typename T>
604 struct UnderlyingType<ClampedNumeric<T>> {
605   using type = T;
606   static const bool is_numeric = true;
607   static const bool is_checked = false;
608   static const bool is_clamped = true;
609   static const bool is_strict = false;
610 };
611 
612 template <typename T>
613 struct UnderlyingType<StrictNumeric<T>> {
614   using type = T;
615   static const bool is_numeric = true;
616   static const bool is_checked = false;
617   static const bool is_clamped = false;
618   static const bool is_strict = true;
619 };
620 
621 template <typename L, typename R>
622 struct IsCheckedOp {
623   static const bool value =
624       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
625       (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
626 };
627 
628 template <typename L, typename R>
629 struct IsClampedOp {
630   static const bool value =
631       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
632       (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
633       !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
634 };
635 
636 template <typename L, typename R>
637 struct IsStrictOp {
638   static const bool value =
639       UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
640       (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
641       !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
642       !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
643 };
644 
645 // as_signed<> returns the supplied integral value (or integral castable
646 // Numeric template) cast as a signed integral of equivalent precision.
647 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
648 template <typename Src>
649 constexpr typename std::make_signed<
650     typename base::internal::UnderlyingType<Src>::type>::type
651 as_signed(const Src value) {
652   static_assert(std::is_integral<decltype(as_signed(value))>::value,
653                 "Argument must be a signed or unsigned integer type.");
654   return static_cast<decltype(as_signed(value))>(value);
655 }
656 
657 // as_unsigned<> returns the supplied integral value (or integral castable
658 // Numeric template) cast as an unsigned integral of equivalent precision.
659 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
660 template <typename Src>
661 constexpr typename std::make_unsigned<
662     typename base::internal::UnderlyingType<Src>::type>::type
663 as_unsigned(const Src value) {
664   static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
665                 "Argument must be a signed or unsigned integer type.");
666   return static_cast<decltype(as_unsigned(value))>(value);
667 }
668 
669 template <typename L, typename R>
670 constexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range,
671                           const RangeCheck r_range) {
672   return l_range.IsUnderflow() || r_range.IsOverflow() ||
673          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <
674                                     static_cast<decltype(lhs + rhs)>(rhs));
675 }
676 
677 template <typename L, typename R>
678 struct IsLess {
679   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
680                 "Types must be numeric.");
681   static constexpr bool Test(const L lhs, const R rhs) {
682     return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
683                       DstRangeRelationToSrcRange<L>(rhs));
684   }
685 };
686 
687 template <typename L, typename R>
688 constexpr bool IsLessOrEqualImpl(const L lhs, const R rhs,
689                                  const RangeCheck l_range,
690                                  const RangeCheck r_range) {
691   return l_range.IsUnderflow() || r_range.IsOverflow() ||
692          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <=
693                                     static_cast<decltype(lhs + rhs)>(rhs));
694 }
695 
696 template <typename L, typename R>
697 struct IsLessOrEqual {
698   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
699                 "Types must be numeric.");
700   static constexpr bool Test(const L lhs, const R rhs) {
701     return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
702                              DstRangeRelationToSrcRange<L>(rhs));
703   }
704 };
705 
706 template <typename L, typename R>
707 constexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range,
708                              const RangeCheck r_range) {
709   return l_range.IsOverflow() || r_range.IsUnderflow() ||
710          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >
711                                     static_cast<decltype(lhs + rhs)>(rhs));
712 }
713 
714 template <typename L, typename R>
715 struct IsGreater {
716   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
717                 "Types must be numeric.");
718   static constexpr bool Test(const L lhs, const R rhs) {
719     return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
720                          DstRangeRelationToSrcRange<L>(rhs));
721   }
722 };
723 
724 template <typename L, typename R>
725 constexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs,
726                                     const RangeCheck l_range,
727                                     const RangeCheck r_range) {
728   return l_range.IsOverflow() || r_range.IsUnderflow() ||
729          (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >=
730                                     static_cast<decltype(lhs + rhs)>(rhs));
731 }
732 
733 template <typename L, typename R>
734 struct IsGreaterOrEqual {
735   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
736                 "Types must be numeric.");
737   static constexpr bool Test(const L lhs, const R rhs) {
738     return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
739                                 DstRangeRelationToSrcRange<L>(rhs));
740   }
741 };
742 
743 template <typename L, typename R>
744 struct IsEqual {
745   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
746                 "Types must be numeric.");
747   static constexpr bool Test(const L lhs, const R rhs) {
748     return DstRangeRelationToSrcRange<R>(lhs) ==
749                DstRangeRelationToSrcRange<L>(rhs) &&
750            static_cast<decltype(lhs + rhs)>(lhs) ==
751                static_cast<decltype(lhs + rhs)>(rhs);
752   }
753 };
754 
755 template <typename L, typename R>
756 struct IsNotEqual {
757   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
758                 "Types must be numeric.");
759   static constexpr bool Test(const L lhs, const R rhs) {
760     return DstRangeRelationToSrcRange<R>(lhs) !=
761                DstRangeRelationToSrcRange<L>(rhs) ||
762            static_cast<decltype(lhs + rhs)>(lhs) !=
763                static_cast<decltype(lhs + rhs)>(rhs);
764   }
765 };
766 
767 // These perform the actual math operations on the CheckedNumerics.
768 // Binary arithmetic operations.
769 template <template <typename, typename> class C, typename L, typename R>
770 constexpr bool SafeCompare(const L lhs, const R rhs) {
771   static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
772                 "Types must be numeric.");
773   using Promotion = BigEnoughPromotion<L, R>;
774   using BigType = typename Promotion::type;
775   return Promotion::is_contained
776              // Force to a larger type for speed if both are contained.
777              ? C<BigType, BigType>::Test(
778                    static_cast<BigType>(static_cast<L>(lhs)),
779                    static_cast<BigType>(static_cast<R>(rhs)))
780              // Let the template functions figure it out for mixed types.
781              : C<L, R>::Test(lhs, rhs);
782 }
783 
784 template <typename Dst, typename Src>
785 constexpr bool IsMaxInRangeForNumericType() {
786   return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
787                                           std::numeric_limits<Src>::max());
788 }
789 
790 template <typename Dst, typename Src>
791 constexpr bool IsMinInRangeForNumericType() {
792   return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
793                                        std::numeric_limits<Src>::lowest());
794 }
795 
796 template <typename Dst, typename Src>
797 constexpr Dst CommonMax() {
798   return !IsMaxInRangeForNumericType<Dst, Src>()
799              ? Dst(std::numeric_limits<Dst>::max())
800              : Dst(std::numeric_limits<Src>::max());
801 }
802 
803 template <typename Dst, typename Src>
804 constexpr Dst CommonMin() {
805   return !IsMinInRangeForNumericType<Dst, Src>()
806              ? Dst(std::numeric_limits<Dst>::lowest())
807              : Dst(std::numeric_limits<Src>::lowest());
808 }
809 
810 // This is a wrapper to generate return the max or min for a supplied type.
811 // If the argument is false, the returned value is the maximum. If true the
812 // returned value is the minimum.
813 template <typename Dst, typename Src = Dst>
814 constexpr Dst CommonMaxOrMin(bool is_min) {
815   return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
816 }
817 
818 }  // namespace internal
819 }  // namespace base
820 }  // namespace v8
821 
822 #endif  // V8_BASE_SAFE_CONVERSIONS_IMPL_H_
823