diff --git a/include/v8.h b/include/v8.h index f77c56ae9f..92e1bb63f2 100644 --- a/include/v8.h +++ b/include/v8.h @@ -550,13 +550,13 @@ class V8EXPORT Script { * Compiles the specified script (context-independent). * * \param source Script source code. - * \param origin Script origin, owned by caller, no references are kept + * \param origin Script origin, owned by caller, no references are kept * when New() returns * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() * using pre_data speeds compilation if it's done multiple times. * Owned by caller, no references are kept when New() returns. * \param script_data Arbitrary data associated with script. Using - * this has same effect as calling SetData(), but allows data to be + * this has same effect as calling SetData(), but allows data to be * available to compile event handlers. * \return Compiled script object (context independent; when run it * will use the currently entered context). @@ -571,7 +571,7 @@ class V8EXPORT Script { * object (typically a string) as the script's origin. * * \param source Script source code. - * \patam file_name file name object (typically a string) to be used + * \param file_name file name object (typically a string) to be used * as the script's origin. * \return Compiled script object (context independent; when run it * will use the currently entered context). @@ -583,7 +583,7 @@ class V8EXPORT Script { * Compiles the specified script (bound to current context). * * \param source Script source code. - * \param origin Script origin, owned by caller, no references are kept + * \param origin Script origin, owned by caller, no references are kept * when Compile() returns * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() * using pre_data speeds compilation if it's done multiple times. @@ -766,6 +766,11 @@ class V8EXPORT Value : public Data { */ bool IsInt32() const; + /** + * Returns true if this value is a 32-bit signed integer. + */ + bool IsUint32() const; + /** * Returns true if this value is a Date. */ diff --git a/src/api.cc b/src/api.cc index bb40079758..cc810919fb 100644 --- a/src/api.cc +++ b/src/api.cc @@ -1569,6 +1569,18 @@ bool Value::IsInt32() const { } +bool Value::IsUint32() const { + if (IsDeadCheck("v8::Value::IsUint32()")) return false; + i::Handle obj = Utils::OpenHandle(this); + if (obj->IsSmi()) return i::Smi::cast(*obj)->value() >= 0; + if (obj->IsNumber()) { + double value = obj->Number(); + return i::FastUI2D(i::FastD2UI(value)) == value; + } + return false; +} + + bool Value::IsDate() const { if (IsDeadCheck("v8::Value::IsDate()")) return false; i::Handle obj = Utils::OpenHandle(this); @@ -2756,6 +2768,17 @@ int32_t Int32::Value() const { } +uint32_t Uint32::Value() const { + if (IsDeadCheck("v8::Uint32::Value()")) return 0; + i::Handle obj = Utils::OpenHandle(this); + if (obj->IsSmi()) { + return i::Smi::cast(*obj)->value(); + } else { + return static_cast(obj->Number()); + } +} + + int v8::Object::InternalFieldCount() { if (IsDeadCheck("v8::Object::InternalFieldCount()")) return 0; i::Handle obj = Utils::OpenHandle(this); diff --git a/src/conversions-inl.h b/src/conversions-inl.h index ba7220a4a6..f7210d5af9 100644 --- a/src/conversions-inl.h +++ b/src/conversions-inl.h @@ -59,6 +59,32 @@ static inline int FastD2I(double x) { } +// The fast double-to-unsigned-int conversion routine does not guarantee +// rounding towards zero. +static 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; + memcpy(&result, &x, sizeof(result)); // Copy low 32 bits. + return negative ? ~result + 1 : result; + } + // Large number (outside uint32 range), Infinity or NaN. + return 0x80000000u; // Return integer indefinite. +} + + static inline double DoubleToInteger(double x) { if (isnan(x)) return 0; if (!isfinite(x) || x == 0) return x; diff --git a/src/conversions.h b/src/conversions.h index 67f7d53f51..bdc7e44a16 100644 --- a/src/conversions.h +++ b/src/conversions.h @@ -32,11 +32,12 @@ namespace v8 { namespace internal { -// The fast double-to-int conversion routine does not guarantee +// The fast double-to-(unsigned-)int conversion routine does not guarantee // rounding towards zero. // The result is unspecified if x is infinite or NaN, or if the rounded // integer value is outside the range of type int. static inline int FastD2I(double x); +static inline unsigned int FastD2UI(double x); static inline double FastI2D(int x) { diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 820586d4cf..b3c88a56ea 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -1960,6 +1960,95 @@ static void CheckUncle(v8::TryCatch* try_catch) { } +THREADED_TEST(ConversionNumber) { + v8::HandleScope scope; + LocalContext env; + // Very large number. + CompileRun("var obj = Math.pow(2,32) * 1237;"); + Local obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); + CHECK_EQ(0, obj->ToInt32()->Value()); + CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. + // Large number. + CompileRun("var obj = -1234567890123;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); + CHECK_EQ(-1912276171, obj->ToInt32()->Value()); + CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT + // Small positive integer. + CompileRun("var obj = 42;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(42.0, obj->ToNumber()->Value()); + CHECK_EQ(42, obj->ToInt32()->Value()); + CHECK(42u == obj->ToUint32()->Value()); // NOLINT + // Negative integer. + CompileRun("var obj = -37;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(-37.0, obj->ToNumber()->Value()); + CHECK_EQ(-37, obj->ToInt32()->Value()); + CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT + // Positive non-int32 integer. + CompileRun("var obj = 0x81234567;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); + CHECK_EQ(-2128394905, obj->ToInt32()->Value()); + CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT + // Fraction. + CompileRun("var obj = 42.3;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(42.3, obj->ToNumber()->Value()); + CHECK_EQ(42, obj->ToInt32()->Value()); + CHECK(42u == obj->ToUint32()->Value()); // NOLINT + // Large negative fraction. + CompileRun("var obj = -5726623061.75;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); + CHECK_EQ(-1431655765, obj->ToInt32()->Value()); + CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT +} + + +THREADED_TEST(isNumberType) { + v8::HandleScope scope; + LocalContext env; + // Very large number. + CompileRun("var obj = Math.pow(2,32) * 1237;"); + Local obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(!obj->IsUint32()); + // Large negative number. + CompileRun("var obj = -1234567890123;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(!obj->IsUint32()); + // Small positive integer. + CompileRun("var obj = 42;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(obj->IsInt32()); + CHECK(obj->IsUint32()); + // Negative integer. + CompileRun("var obj = -37;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(obj->IsInt32()); + CHECK(!obj->IsUint32()); + // Positive non-int32 integer. + CompileRun("var obj = 0x81234567;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(obj->IsUint32()); + // Fraction. + CompileRun("var obj = 42.3;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(!obj->IsUint32()); + // Large negative fraction. + CompileRun("var obj = -5726623061.75;"); + obj = env->Global()->Get(v8_str("obj")); + CHECK(!obj->IsInt32()); + CHECK(!obj->IsUint32()); +} + + THREADED_TEST(ConversionException) { v8::HandleScope scope; LocalContext env;