diff --git a/include/v8.h b/include/v8.h index fb142d7a36..dfba9dbae4 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1151,6 +1151,10 @@ class V8EXPORT External : public Value { static External* Cast(Value* obj); void* Value() const; private: + enum { + kAlignedPointerMask = 3, + kAlignedPointerShift = 2 + }; External(); }; diff --git a/src/api.cc b/src/api.cc index 5c48e5e98a..5ae13a12d8 100644 --- a/src/api.cc +++ b/src/api.cc @@ -1447,7 +1447,7 @@ Local Value::ToInteger() const { External* External::Cast(v8::Value* that) { if (IsDeadCheck("v8::External::Cast()")) return 0; i::Handle obj = Utils::OpenHandle(that); - ApiCheck(obj->IsProxy(), + ApiCheck(obj->IsProxy() || obj->IsSmi(), "v8::External::Cast()", "Could not convert to external"); return static_cast(that); @@ -2229,6 +2229,11 @@ int32_t Int32::Value() const { void* External::Value() const { if (IsDeadCheck("v8::External::Value()")) return 0; i::Handle obj = Utils::OpenHandle(this); + if (obj->IsSmi()) { + // The external value was an aligned pointer. + return reinterpret_cast( + i::Smi::cast(*obj)->value() << kAlignedPointerShift); + } return reinterpret_cast(i::Proxy::cast(*obj)->proxy()); } @@ -2467,8 +2472,14 @@ Local v8::External::New(void* data) { STATIC_ASSERT(sizeof(data) == sizeof(i::Address)); LOG_API("External::New"); EnsureInitialized("v8::External::New()"); - i::Handle obj = i::Factory::NewProxy(static_cast(data)); - return Utils::ToLocal(obj); + if ((reinterpret_cast(data) & kAlignedPointerMask) == 0) { + uintptr_t data_ptr = reinterpret_cast(data); + int data_value = static_cast(data_ptr >> kAlignedPointerShift); + STATIC_ASSERT(sizeof(data_ptr) == sizeof(data_value)); + i::Handle obj(i::Smi::FromInt(data_value)); + return Utils::ToLocal(obj); + } + return Utils::ToLocal(i::Factory::NewProxy(static_cast(data))); } diff --git a/src/api.h b/src/api.h index 85b13ec9a6..27ec3415d5 100644 --- a/src/api.h +++ b/src/api.h @@ -181,6 +181,8 @@ class Utils { v8::internal::Handle obj); static inline Local ToLocal( v8::internal::Handle obj); + static inline Local ToLocal( + v8::internal::Handle obj); static inline Local MessageToLocal( v8::internal::Handle obj); static inline Local NumberToLocal( @@ -256,6 +258,7 @@ MAKE_TO_LOCAL(ToLocal, String, String) MAKE_TO_LOCAL(ToLocal, JSObject, Object) MAKE_TO_LOCAL(ToLocal, JSArray, Array) MAKE_TO_LOCAL(ToLocal, Proxy, External) +MAKE_TO_LOCAL(ToLocal, Smi, External) MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate) MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate) MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature) diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 2f7f16d3e5..a1fcfa8edb 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -1339,6 +1339,22 @@ THREADED_TEST(External) { CHECK_EQ(x, 3); *ptr = 10; CHECK_EQ(x, 10); + + // Make sure unaligned pointers are wrapped properly. + char* data = "0123456789"; + Local zero = v8::External::New(&data[0]); + Local one = v8::External::New(&data[1]); + Local two = v8::External::New(&data[2]); + Local three = v8::External::New(&data[3]); + + char* char_ptr = reinterpret_cast(zero->Value()); + CHECK_EQ('0', *char_ptr); + char_ptr = reinterpret_cast(one->Value()); + CHECK_EQ('1', *char_ptr); + char_ptr = reinterpret_cast(two->Value()); + CHECK_EQ('2', *char_ptr); + char_ptr = reinterpret_cast(three->Value()); + CHECK_EQ('3', *char_ptr); }