fc329ce22a
Mostly signed integer overflows, and a few cases of double division by zero (which is defined by IEEE-754 to return Infinity (or NaN for 0/0) but is UB in C++). In base/ieee754.cc, use constants for NaN and Infinity instead of computing these values. In spaces-unittest.cc, ensure that a large enough allocation is used. Bug: v8:3770 Change-Id: I50d9a77dc860ef9993b7b269a5f8c117b0f62f9d Reviewed-on: https://chromium-review.googlesource.com/c/1403454 Commit-Queue: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Adam Klein <adamk@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Cr-Commit-Position: refs/heads/master@{#58701}
248 lines
7.9 KiB
C++
248 lines
7.9 KiB
C++
// Copyright 2011 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef V8_CONVERSIONS_INL_H_
|
|
#define V8_CONVERSIONS_INL_H_
|
|
|
|
#include <float.h> // Required for DBL_MAX and on Win32 for finite()
|
|
#include <limits.h> // Required for INT_MAX etc.
|
|
#include <stdarg.h>
|
|
#include <cmath>
|
|
#include "src/globals.h" // Required for V8_INFINITY
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Extra POSIX/ANSI functions for Win32/MSVC.
|
|
|
|
#include "src/base/bits.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/conversions.h"
|
|
#include "src/double.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/objects/heap-number-inl.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
// The fast double-to-unsigned-int conversion routine does not guarantee
|
|
// rounding towards zero, or any reasonable value if the argument is larger
|
|
// than what fits in an unsigned 32-bit integer.
|
|
inline unsigned int FastD2UI(double x) {
|
|
// There is no unsigned version of lrint, so there is no fast path
|
|
// in this function as there is in FastD2I. Using lrint doesn't work
|
|
// for values of 2^31 and above.
|
|
|
|
// Convert "small enough" doubles to uint32_t by fixing the 32
|
|
// least significant non-fractional bits in the low 32 bits of the
|
|
// double, and reading them from there.
|
|
const double k2Pow52 = 4503599627370496.0;
|
|
bool negative = x < 0;
|
|
if (negative) {
|
|
x = -x;
|
|
}
|
|
if (x < k2Pow52) {
|
|
x += k2Pow52;
|
|
uint32_t result;
|
|
#ifndef V8_TARGET_BIG_ENDIAN
|
|
void* mantissa_ptr = reinterpret_cast<void*>(&x);
|
|
#else
|
|
void* mantissa_ptr =
|
|
reinterpret_cast<void*>(reinterpret_cast<Address>(&x) + kInt32Size);
|
|
#endif
|
|
// Copy least significant 32 bits of mantissa.
|
|
memcpy(&result, mantissa_ptr, sizeof(result));
|
|
return negative ? ~result + 1 : result;
|
|
}
|
|
// Large number (outside uint32 range), Infinity or NaN.
|
|
return 0x80000000u; // Return integer indefinite.
|
|
}
|
|
|
|
|
|
inline float DoubleToFloat32(double x) {
|
|
// TODO(yangguo): This static_cast is implementation-defined behaviour in C++,
|
|
// so we may need to do the conversion manually instead to match the spec.
|
|
volatile float f = static_cast<float>(x);
|
|
return f;
|
|
}
|
|
|
|
|
|
inline double DoubleToInteger(double x) {
|
|
if (std::isnan(x)) return 0;
|
|
if (!std::isfinite(x) || x == 0) return x;
|
|
return (x >= 0) ? std::floor(x) : std::ceil(x);
|
|
}
|
|
|
|
// Implements most of https://tc39.github.io/ecma262/#sec-toint32.
|
|
int32_t DoubleToInt32(double x) {
|
|
if ((std::isfinite(x)) && (x <= INT_MAX) && (x >= INT_MIN)) {
|
|
int32_t i = static_cast<int32_t>(x);
|
|
if (FastI2D(i) == x) return i;
|
|
}
|
|
Double d(x);
|
|
int exponent = d.Exponent();
|
|
uint64_t bits;
|
|
if (exponent < 0) {
|
|
if (exponent <= -Double::kSignificandSize) return 0;
|
|
bits = d.Significand() >> -exponent;
|
|
} else {
|
|
if (exponent > 31) return 0;
|
|
// Masking to a 32-bit value ensures that the result of the
|
|
// static_cast<int64_t> below is not the minimal int64_t value,
|
|
// which would overflow on multiplication with d.Sign().
|
|
bits = (d.Significand() << exponent) & 0xFFFFFFFFul;
|
|
}
|
|
return static_cast<int32_t>(d.Sign() * static_cast<int64_t>(bits));
|
|
}
|
|
|
|
bool DoubleToSmiInteger(double value, int* smi_int_value) {
|
|
if (!IsSmiDouble(value)) return false;
|
|
*smi_int_value = FastD2I(value);
|
|
DCHECK(Smi::IsValid(*smi_int_value));
|
|
return true;
|
|
}
|
|
|
|
bool IsSmiDouble(double value) {
|
|
return value >= Smi::kMinValue && value <= Smi::kMaxValue &&
|
|
!IsMinusZero(value) && value == FastI2D(FastD2I(value));
|
|
}
|
|
|
|
|
|
bool IsInt32Double(double value) {
|
|
return value >= kMinInt && value <= kMaxInt && !IsMinusZero(value) &&
|
|
value == FastI2D(FastD2I(value));
|
|
}
|
|
|
|
|
|
bool IsUint32Double(double value) {
|
|
return !IsMinusZero(value) && value >= 0 && value <= kMaxUInt32 &&
|
|
value == FastUI2D(FastD2UI(value));
|
|
}
|
|
|
|
bool DoubleToUint32IfEqualToSelf(double value, uint32_t* uint32_value) {
|
|
const double k2Pow52 = 4503599627370496.0;
|
|
const uint32_t kValidTopBits = 0x43300000;
|
|
const uint64_t kBottomBitMask = V8_2PART_UINT64_C(0x00000000, FFFFFFFF);
|
|
|
|
// Add 2^52 to the double, to place valid uint32 values in the low-significant
|
|
// bits of the exponent, by effectively setting the (implicit) top bit of the
|
|
// significand. Note that this addition also normalises 0.0 and -0.0.
|
|
double shifted_value = value + k2Pow52;
|
|
|
|
// At this point, a valid uint32 valued double will be represented as:
|
|
//
|
|
// sign = 0
|
|
// exponent = 52
|
|
// significand = 1. 00...00 <value>
|
|
// implicit^ ^^^^^^^ 32 bits
|
|
// ^^^^^^^^^^^^^^^ 52 bits
|
|
//
|
|
// Therefore, we can first check the top 32 bits to make sure that the sign,
|
|
// exponent and remaining significand bits are valid, and only then check the
|
|
// value in the bottom 32 bits.
|
|
|
|
uint64_t result = bit_cast<uint64_t>(shifted_value);
|
|
if ((result >> 32) == kValidTopBits) {
|
|
*uint32_value = result & kBottomBitMask;
|
|
return FastUI2D(result & kBottomBitMask) == value;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int32_t NumberToInt32(Object number) {
|
|
if (number->IsSmi()) return Smi::ToInt(number);
|
|
return DoubleToInt32(number->Number());
|
|
}
|
|
|
|
uint32_t NumberToUint32(Object number) {
|
|
if (number->IsSmi()) return Smi::ToInt(number);
|
|
return DoubleToUint32(number->Number());
|
|
}
|
|
|
|
uint32_t PositiveNumberToUint32(Object number) {
|
|
if (number->IsSmi()) {
|
|
int value = Smi::ToInt(number);
|
|
if (value <= 0) return 0;
|
|
return value;
|
|
}
|
|
DCHECK(number->IsHeapNumber());
|
|
double value = number->Number();
|
|
// Catch all values smaller than 1 and use the double-negation trick for NANs.
|
|
if (!(value >= 1)) return 0;
|
|
uint32_t max = std::numeric_limits<uint32_t>::max();
|
|
if (value < max) return static_cast<uint32_t>(value);
|
|
return max;
|
|
}
|
|
|
|
int64_t NumberToInt64(Object number) {
|
|
if (number->IsSmi()) return Smi::ToInt(number);
|
|
double d = number->Number();
|
|
if (std::isnan(d)) return 0;
|
|
if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
|
|
return std::numeric_limits<int64_t>::max();
|
|
}
|
|
if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
|
|
return std::numeric_limits<int64_t>::min();
|
|
}
|
|
return static_cast<int64_t>(d);
|
|
}
|
|
|
|
uint64_t PositiveNumberToUint64(Object number) {
|
|
if (number->IsSmi()) {
|
|
int value = Smi::ToInt(number);
|
|
if (value <= 0) return 0;
|
|
return value;
|
|
}
|
|
DCHECK(number->IsHeapNumber());
|
|
double value = number->Number();
|
|
// Catch all values smaller than 1 and use the double-negation trick for NANs.
|
|
if (!(value >= 1)) return 0;
|
|
uint64_t max = std::numeric_limits<uint64_t>::max();
|
|
if (value < max) return static_cast<uint64_t>(value);
|
|
return max;
|
|
}
|
|
|
|
bool TryNumberToSize(Object number, size_t* result) {
|
|
// Do not create handles in this function! Don't use SealHandleScope because
|
|
// the function can be used concurrently.
|
|
if (number->IsSmi()) {
|
|
int value = Smi::ToInt(number);
|
|
DCHECK(static_cast<unsigned>(Smi::kMaxValue) <=
|
|
std::numeric_limits<size_t>::max());
|
|
if (value >= 0) {
|
|
*result = static_cast<size_t>(value);
|
|
return true;
|
|
}
|
|
return false;
|
|
} else {
|
|
DCHECK(number->IsHeapNumber());
|
|
double value = HeapNumber::cast(number)->value();
|
|
// If value is compared directly to the limit, the limit will be
|
|
// casted to a double and could end up as limit + 1,
|
|
// because a double might not have enough mantissa bits for it.
|
|
// So we might as well cast the limit first, and use < instead of <=.
|
|
double maxSize = static_cast<double>(std::numeric_limits<size_t>::max());
|
|
if (value >= 0 && value < maxSize) {
|
|
*result = static_cast<size_t>(value);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t NumberToSize(Object number) {
|
|
size_t result = 0;
|
|
bool is_valid = TryNumberToSize(number, &result);
|
|
CHECK(is_valid);
|
|
return result;
|
|
}
|
|
|
|
uint32_t DoubleToUint32(double x) {
|
|
return static_cast<uint32_t>(DoubleToInt32(x));
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_CONVERSIONS_INL_H_
|