Wrap external pointers more carefully.
On 32-bit platforms any pointer with 0 as LSB can be wrapped into Smi. However, on 64-bit platforms it's currently not the case as x64 Smis must have 0s in lower 32 bit word. Even worse, macroassembler Move instruction will try to fetch integer value from Smi and will shift by 32 bits to the right rendering stored pointer incorrect. BUG=v8:1037 Review URL: http://codereview.chromium.org/6119009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6301 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ac9b035a45
commit
8805509f28
40
include/v8.h
40
include/v8.h
@ -3353,10 +3353,10 @@ const int kSmiTag = 0;
|
||||
const int kSmiTagSize = 1;
|
||||
const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1;
|
||||
|
||||
template <size_t ptr_size> struct SmiConstants;
|
||||
template <size_t ptr_size> struct SmiTagging;
|
||||
|
||||
// Smi constants for 32-bit systems.
|
||||
template <> struct SmiConstants<4> {
|
||||
template <> struct SmiTagging<4> {
|
||||
static const int kSmiShiftSize = 0;
|
||||
static const int kSmiValueSize = 31;
|
||||
static inline int SmiToInt(internal::Object* value) {
|
||||
@ -3364,10 +3364,15 @@ template <> struct SmiConstants<4> {
|
||||
// Throw away top 32 bits and shift down (requires >> to be sign extending).
|
||||
return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> shift_bits;
|
||||
}
|
||||
|
||||
// For 32-bit systems any 2 bytes aligned pointer can be encoded as smi
|
||||
// with a plain reinterpret_cast.
|
||||
static const intptr_t kEncodablePointerMask = 0x1;
|
||||
static const int kPointerToSmiShift = 0;
|
||||
};
|
||||
|
||||
// Smi constants for 64-bit systems.
|
||||
template <> struct SmiConstants<8> {
|
||||
template <> struct SmiTagging<8> {
|
||||
static const int kSmiShiftSize = 31;
|
||||
static const int kSmiValueSize = 32;
|
||||
static inline int SmiToInt(internal::Object* value) {
|
||||
@ -3375,10 +3380,26 @@ template <> struct SmiConstants<8> {
|
||||
// Shift down and throw away top 32 bits.
|
||||
return static_cast<int>(reinterpret_cast<intptr_t>(value) >> shift_bits);
|
||||
}
|
||||
|
||||
// To maximize the range of pointers that can be encoded
|
||||
// in the available 32 bits, we require them to be 8 bytes aligned.
|
||||
// This gives 2 ^ (32 + 3) = 32G address space covered.
|
||||
// It might be not enough to cover stack allocated objects on some platforms.
|
||||
static const int kPointerAlignment = 3;
|
||||
|
||||
static const intptr_t kEncodablePointerMask =
|
||||
~(intptr_t(0xffffffff) << kPointerAlignment);
|
||||
|
||||
static const int kPointerToSmiShift =
|
||||
kSmiTagSize + kSmiShiftSize - kPointerAlignment;
|
||||
};
|
||||
|
||||
const int kSmiShiftSize = SmiConstants<kApiPointerSize>::kSmiShiftSize;
|
||||
const int kSmiValueSize = SmiConstants<kApiPointerSize>::kSmiValueSize;
|
||||
typedef SmiTagging<kApiPointerSize> PlatformSmiTagging;
|
||||
const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize;
|
||||
const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize;
|
||||
const intptr_t kEncodablePointerMask =
|
||||
PlatformSmiTagging::kEncodablePointerMask;
|
||||
const int kPointerToSmiShift = PlatformSmiTagging::kPointerToSmiShift;
|
||||
|
||||
template <size_t ptr_size> struct InternalConstants;
|
||||
|
||||
@ -3426,7 +3447,7 @@ class Internals {
|
||||
}
|
||||
|
||||
static inline int SmiValue(internal::Object* value) {
|
||||
return SmiConstants<kApiPointerSize>::SmiToInt(value);
|
||||
return PlatformSmiTagging::SmiToInt(value);
|
||||
}
|
||||
|
||||
static inline int GetInstanceType(internal::Object* obj) {
|
||||
@ -3435,9 +3456,14 @@ class Internals {
|
||||
return ReadField<uint8_t>(map, kMapInstanceTypeOffset);
|
||||
}
|
||||
|
||||
static inline void* GetExternalPointerFromSmi(internal::Object* value) {
|
||||
const intptr_t address = reinterpret_cast<intptr_t>(value);
|
||||
return reinterpret_cast<void*>(address >> kPointerToSmiShift);
|
||||
}
|
||||
|
||||
static inline void* GetExternalPointer(internal::Object* obj) {
|
||||
if (HasSmiTag(obj)) {
|
||||
return obj;
|
||||
return GetExternalPointerFromSmi(obj);
|
||||
} else if (GetInstanceType(obj) == kProxyType) {
|
||||
return ReadField<void*>(obj, kProxyProxyOffset);
|
||||
} else {
|
||||
|
52
src/api.cc
52
src/api.cc
@ -3266,18 +3266,35 @@ void v8::Object::SetInternalField(int index, v8::Handle<Value> value) {
|
||||
}
|
||||
|
||||
|
||||
static bool CanBeEncodedAsSmi(void* ptr) {
|
||||
const intptr_t address = reinterpret_cast<intptr_t>(ptr);
|
||||
return ((address & i::kEncodablePointerMask) == 0);
|
||||
}
|
||||
|
||||
|
||||
static i::Smi* EncodeAsSmi(void* ptr) {
|
||||
ASSERT(CanBeEncodedAsSmi(ptr));
|
||||
const intptr_t address = reinterpret_cast<intptr_t>(ptr);
|
||||
i::Smi* result = reinterpret_cast<i::Smi*>(address << i::kPointerToSmiShift);
|
||||
ASSERT(i::Internals::HasSmiTag(result));
|
||||
ASSERT_EQ(result, i::Smi::FromInt(result->value()));
|
||||
ASSERT_EQ(ptr, i::Internals::GetExternalPointerFromSmi(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void v8::Object::SetPointerInInternalField(int index, void* value) {
|
||||
ENTER_V8;
|
||||
i::Object* as_object = reinterpret_cast<i::Object*>(value);
|
||||
if (as_object->IsSmi()) {
|
||||
Utils::OpenHandle(this)->SetInternalField(index, as_object);
|
||||
return;
|
||||
if (CanBeEncodedAsSmi(value)) {
|
||||
Utils::OpenHandle(this)->SetInternalField(index, EncodeAsSmi(value));
|
||||
} else {
|
||||
HandleScope scope;
|
||||
i::Handle<i::Proxy> proxy =
|
||||
i::Factory::NewProxy(reinterpret_cast<i::Address>(value), i::TENURED);
|
||||
if (!proxy.is_null())
|
||||
Utils::OpenHandle(this)->SetInternalField(index, *proxy);
|
||||
}
|
||||
HandleScope scope;
|
||||
i::Handle<i::Proxy> proxy =
|
||||
i::Factory::NewProxy(reinterpret_cast<i::Address>(value), i::TENURED);
|
||||
if (!proxy.is_null())
|
||||
Utils::OpenHandle(this)->SetInternalField(index, *proxy);
|
||||
ASSERT_EQ(value, GetPointerFromInternalField(index));
|
||||
}
|
||||
|
||||
|
||||
@ -3562,11 +3579,13 @@ Local<Value> v8::External::Wrap(void* data) {
|
||||
LOG_API("External::Wrap");
|
||||
EnsureInitialized("v8::External::Wrap()");
|
||||
ENTER_V8;
|
||||
i::Object* as_object = reinterpret_cast<i::Object*>(data);
|
||||
if (as_object->IsSmi()) {
|
||||
return Utils::ToLocal(i::Handle<i::Object>(as_object));
|
||||
}
|
||||
return ExternalNewImpl(data);
|
||||
|
||||
v8::Local<v8::Value> result = CanBeEncodedAsSmi(data)
|
||||
? Utils::ToLocal(i::Handle<i::Object>(EncodeAsSmi(data)))
|
||||
: v8::Local<v8::Value>(ExternalNewImpl(data));
|
||||
|
||||
ASSERT_EQ(data, Unwrap(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -3574,7 +3593,7 @@ void* v8::Object::SlowGetPointerFromInternalField(int index) {
|
||||
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
|
||||
i::Object* value = obj->GetInternalField(index);
|
||||
if (value->IsSmi()) {
|
||||
return value;
|
||||
return i::Internals::GetExternalPointerFromSmi(value);
|
||||
} else if (value->IsProxy()) {
|
||||
return reinterpret_cast<void*>(i::Proxy::cast(value)->proxy());
|
||||
} else {
|
||||
@ -3588,8 +3607,7 @@ void* v8::External::FullUnwrap(v8::Handle<v8::Value> wrapper) {
|
||||
i::Handle<i::Object> obj = Utils::OpenHandle(*wrapper);
|
||||
void* result;
|
||||
if (obj->IsSmi()) {
|
||||
// The external value was an aligned pointer.
|
||||
result = *obj;
|
||||
result = i::Internals::GetExternalPointerFromSmi(*obj);
|
||||
} else if (obj->IsProxy()) {
|
||||
result = ExternalValueImpl(obj);
|
||||
} else {
|
||||
|
@ -814,6 +814,75 @@ THREADED_TEST(FunctionTemplate) {
|
||||
}
|
||||
|
||||
|
||||
static void* expected_ptr;
|
||||
static v8::Handle<v8::Value> callback(const v8::Arguments& args) {
|
||||
void* ptr = v8::External::Unwrap(args.Data());
|
||||
CHECK_EQ(expected_ptr, ptr);
|
||||
return v8::Boolean::New(true);
|
||||
}
|
||||
|
||||
|
||||
static void TestExternalPointerWrapping() {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr);
|
||||
|
||||
v8::Handle<v8::Object> obj = v8::Object::New();
|
||||
obj->Set(v8_str("func"),
|
||||
v8::FunctionTemplate::New(callback, data)->GetFunction());
|
||||
env->Global()->Set(v8_str("obj"), obj);
|
||||
|
||||
CHECK(CompileRun(
|
||||
"function foo() {\n"
|
||||
" for (var i = 0; i < 13; i++) obj.func();\n"
|
||||
"}\n"
|
||||
"foo(), true")->BooleanValue());
|
||||
}
|
||||
|
||||
|
||||
THREADED_TEST(ExternalWrap) {
|
||||
// Check heap allocated object.
|
||||
int* ptr = new int;
|
||||
expected_ptr = ptr;
|
||||
TestExternalPointerWrapping();
|
||||
delete ptr;
|
||||
|
||||
// Check stack allocated object.
|
||||
int foo;
|
||||
expected_ptr = &foo;
|
||||
TestExternalPointerWrapping();
|
||||
|
||||
// Check not aligned addresses.
|
||||
const int n = 100;
|
||||
char* s = new char[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
expected_ptr = s + i;
|
||||
TestExternalPointerWrapping();
|
||||
}
|
||||
|
||||
delete[] s;
|
||||
|
||||
// Check several invalid addresses.
|
||||
expected_ptr = reinterpret_cast<void*>(1);
|
||||
TestExternalPointerWrapping();
|
||||
|
||||
expected_ptr = reinterpret_cast<void*>(0xdeadbeef);
|
||||
TestExternalPointerWrapping();
|
||||
|
||||
expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1);
|
||||
TestExternalPointerWrapping();
|
||||
|
||||
#if defined(V8_HOST_ARCH_X64)
|
||||
expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef);
|
||||
TestExternalPointerWrapping();
|
||||
|
||||
expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1);
|
||||
TestExternalPointerWrapping();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
THREADED_TEST(FindInstanceInPrototypeChain) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
Loading…
Reference in New Issue
Block a user