From 3283195d0408333cce552cf4087577e6f41054e5 Mon Sep 17 00:00:00 2001 From: bmeurer Date: Tue, 5 May 2015 05:48:14 -0700 Subject: [PATCH] [handles] Sanitize Handle and friends. This almost allows us to get rid of handles-inl.h, once we fix the cyclic dependencies (follow up CL). R=yangguo@chromium.org Review URL: https://codereview.chromium.org/1128533002 Cr-Commit-Position: refs/heads/master@{#28222} --- src/handles-inl.h | 175 +-------------------------------------- src/handles.cc | 156 +++++++++++++++++++++++++++++++--- src/handles.h | 207 +++++++++++++++++++++++++++++----------------- 3 files changed, 276 insertions(+), 262 deletions(-) diff --git a/src/handles-inl.h b/src/handles-inl.h index 34b3f32d96..725726d737 100644 --- a/src/handles-inl.h +++ b/src/handles-inl.h @@ -1,183 +1,10 @@ // Copyright 2006-2008 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_HANDLES_INL_H_ -#define V8_HANDLES_INL_H_ #include "src/api.h" #include "src/handles.h" #include "src/heap/heap.h" #include "src/isolate.h" -namespace v8 { -namespace internal { - -template -Handle::Handle(T* obj) { - location_ = HandleScope::CreateHandle(obj->GetIsolate(), obj); -} - - -template -Handle::Handle(T* obj, Isolate* isolate) { - location_ = HandleScope::CreateHandle(isolate, obj); -} - - -template -inline bool Handle::is_identical_to(const Handle o) const { - // Dereferencing deferred handles to check object equality is safe. - SLOW_DCHECK( - (location_ == NULL || IsDereferenceAllowed(NO_DEFERRED_CHECK)) && - (o.location_ == NULL || o.IsDereferenceAllowed(NO_DEFERRED_CHECK))); - if (location_ == o.location_) return true; - if (location_ == NULL || o.location_ == NULL) return false; - return *location_ == *o.location_; -} - - -template -inline T* Handle::operator*() const { - SLOW_DCHECK(IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK)); - return *bit_cast(location_); -} - -template -inline T** Handle::location() const { - SLOW_DCHECK(location_ == NULL || - IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK)); - return location_; -} - -#ifdef DEBUG -template -bool Handle::IsDereferenceAllowed(DereferenceCheckMode mode) const { - DCHECK(location_ != NULL); - Object* object = *bit_cast(location_); - if (object->IsSmi()) return true; - HeapObject* heap_object = HeapObject::cast(object); - Heap* heap = heap_object->GetHeap(); - Object** handle = reinterpret_cast(location_); - Object** roots_array_start = heap->roots_array_start(); - if (roots_array_start <= handle && - handle < roots_array_start + Heap::kStrongRootListLength && - heap->RootCanBeTreatedAsConstant( - static_cast(handle - roots_array_start))) { - return true; - } - if (!AllowHandleDereference::IsAllowed()) return false; - if (mode == INCLUDE_DEFERRED_CHECK && - !AllowDeferredHandleDereference::IsAllowed()) { - // Accessing cells, maps and internalized strings is safe. - if (heap_object->IsCell()) return true; - if (heap_object->IsMap()) return true; - if (heap_object->IsInternalizedString()) return true; - return !heap->isolate()->IsDeferredHandle(handle); - } - return true; -} -#endif - - - -HandleScope::HandleScope(Isolate* isolate) { - HandleScopeData* current = isolate->handle_scope_data(); - isolate_ = isolate; - prev_next_ = current->next; - prev_limit_ = current->limit; - current->level++; -} - - -HandleScope::~HandleScope() { - CloseScope(isolate_, prev_next_, prev_limit_); -} - - -void HandleScope::CloseScope(Isolate* isolate, - Object** prev_next, - Object** prev_limit) { - HandleScopeData* current = isolate->handle_scope_data(); - - std::swap(current->next, prev_next); - current->level--; - if (current->limit != prev_limit) { - current->limit = prev_limit; - DeleteExtensions(isolate); -#ifdef ENABLE_HANDLE_ZAPPING - ZapRange(current->next, prev_limit); - } else { - ZapRange(current->next, prev_next); -#endif - } -} - - -template -Handle HandleScope::CloseAndEscape(Handle handle_value) { - HandleScopeData* current = isolate_->handle_scope_data(); - - T* value = *handle_value; - // Throw away all handles in the current scope. - CloseScope(isolate_, prev_next_, prev_limit_); - // Allocate one handle in the parent scope. - DCHECK(current->level > 0); - Handle result(CreateHandle(isolate_, value)); - // Reinitialize the current scope (so that it's ready - // to be used or closed again). - prev_next_ = current->next; - prev_limit_ = current->limit; - current->level++; - return result; -} - - -template -T** HandleScope::CreateHandle(Isolate* isolate, T* value) { - DCHECK(AllowHandleAllocation::IsAllowed()); - HandleScopeData* current = isolate->handle_scope_data(); - - internal::Object** cur = current->next; - if (cur == current->limit) cur = Extend(isolate); - // Update the current next field, set the value in the created - // handle, and return the result. - DCHECK(cur < current->limit); - current->next = cur + 1; - - T** result = reinterpret_cast(cur); - *result = value; - return result; -} - - -#ifdef DEBUG -inline SealHandleScope::SealHandleScope(Isolate* isolate) : isolate_(isolate) { - // Make sure the current thread is allowed to create handles to begin with. - CHECK(AllowHandleAllocation::IsAllowed()); - HandleScopeData* current = isolate_->handle_scope_data(); - // Shrink the current handle scope to make it impossible to do - // handle allocations without an explicit handle scope. - limit_ = current->limit; - current->limit = current->next; - level_ = current->level; - current->level = 0; -} - - -inline SealHandleScope::~SealHandleScope() { - // Restore state in current handle scope to re-enable handle - // allocations. - HandleScopeData* current = isolate_->handle_scope_data(); - DCHECK_EQ(0, current->level); - current->level = level_; - DCHECK_EQ(current->next, current->limit); - current->limit = limit_; -} - -#endif - -} } // namespace v8::internal - -#endif // V8_HANDLES_INL_H_ +// TODO(bmeurer): Break all include cycles and remove this file! diff --git a/src/handles.cc b/src/handles.cc index d9b130f3ca..c5410ab41e 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -2,14 +2,64 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "src/v8.h" - #include "src/handles.h" +#include "src/api.h" +#include "src/isolate.h" + namespace v8 { namespace internal { +HandleBase::HandleBase(HeapObject* object) + : HandleBase(object, object->GetIsolate()) {} + +HandleBase::HandleBase(Object* object, Isolate* isolate) + : HandleBase(HandleScope::CreateHandle(isolate, object)) {} + + +#ifdef DEBUG + +bool HandleBase::IsDereferenceAllowed(DereferenceCheckMode mode) const { + DCHECK_NOT_NULL(location_); + Object* const object = *location_; + if (object->IsSmi()) return true; + HeapObject* const heap_object = HeapObject::cast(object); + Heap* const heap = heap_object->GetHeap(); + Object** roots_array_start = heap->roots_array_start(); + if (roots_array_start <= location_ && + location_ < roots_array_start + Heap::kStrongRootListLength && + heap->RootCanBeTreatedAsConstant( + static_cast(location_ - roots_array_start))) { + return true; + } + if (!AllowHandleDereference::IsAllowed()) return false; + if (mode == INCLUDE_DEFERRED_CHECK && + !AllowDeferredHandleDereference::IsAllowed()) { + // Accessing cells, maps and internalized strings is safe. + if (heap_object->IsCell()) return true; + if (heap_object->IsMap()) return true; + if (heap_object->IsInternalizedString()) return true; + return !heap->isolate()->IsDeferredHandle(location_); + } + return true; +} + +#endif // DEBUG + + +HandleScope::HandleScope(Isolate* isolate) : isolate_(isolate) { + HandleScopeData* const current = isolate->handle_scope_data(); + prev_next_ = current->next; + prev_limit_ = current->limit; + current->level++; +} + + +HandleScope::~HandleScope() { CloseScope(isolate_, prev_next_, prev_limit_); } + + +// static int HandleScope::NumberOfHandles(Isolate* isolate) { HandleScopeImplementer* impl = isolate->handle_scope_implementer(); int n = impl->blocks()->length(); @@ -19,12 +69,73 @@ int HandleScope::NumberOfHandles(Isolate* isolate) { } +// static +Object** HandleScope::CreateHandle(Isolate* isolate, Object* value) { + DCHECK(AllowHandleAllocation::IsAllowed()); + HandleScopeData* const current = isolate->handle_scope_data(); + + Object** result = current->next; + if (result == current->limit) result = Extend(isolate); + // Update the current next field, set the value in the created + // handle, and return the result. + DCHECK_LT(result, current->limit); + current->next = result + 1; + + *result = value; + return result; +} + + +// static +void HandleScope::DeleteExtensions(Isolate* isolate) { + HandleScopeData* const current = isolate->handle_scope_data(); + isolate->handle_scope_implementer()->DeleteExtensions(current->limit); +} + + +Handle HandleScope::CloseAndEscape(Handle handle) { + HandleScopeData* const current = isolate_->handle_scope_data(); + + Object* value = *handle; + // Throw away all handles in the current scope. + CloseScope(isolate_, prev_next_, prev_limit_); + // Allocate one handle in the parent scope. + DCHECK_LT(0, current->level); + Handle result(CreateHandle(isolate_, value)); + // Reinitialize the current scope (so that it's ready + // to be used or closed again). + prev_next_ = current->next; + prev_limit_ = current->limit; + current->level++; + return result; +} + + +// static +void HandleScope::CloseScope(Isolate* isolate, Object** prev_next, + Object** prev_limit) { + HandleScopeData* const current = isolate->handle_scope_data(); + + std::swap(current->next, prev_next); + current->level--; + if (current->limit != prev_limit) { + current->limit = prev_limit; + DeleteExtensions(isolate); +#ifdef ENABLE_HANDLE_ZAPPING + ZapRange(current->next, prev_limit); + } else { + ZapRange(current->next, prev_next); +#endif + } +} + + +// static Object** HandleScope::Extend(Isolate* isolate) { HandleScopeData* current = isolate->handle_scope_data(); Object** result = current->next; - - DCHECK(result == current->limit); + DCHECK_EQ(result, current->limit); // Make sure there's at least one scope on the stack and that the // top of the scope stack isn't a barrier. if (!Utils::ApiCheck(current->level != 0, @@ -58,12 +169,6 @@ Object** HandleScope::Extend(Isolate* isolate) { } -void HandleScope::DeleteExtensions(Isolate* isolate) { - HandleScopeData* current = isolate->handle_scope_data(); - isolate->handle_scope_implementer()->DeleteExtensions(current->limit); -} - - #ifdef ENABLE_HANDLE_ZAPPING void HandleScope::ZapRange(Object** start, Object** end) { DCHECK(end - start <= kHandleBlockSize); @@ -127,4 +232,33 @@ DeferredHandles* DeferredHandleScope::Detach() { return deferred; } -} } // namespace v8::internal + +#ifdef DEBUG + +SealHandleScope::SealHandleScope(Isolate* isolate) : isolate_(isolate) { + // Make sure the current thread is allowed to create handles to begin with. + CHECK(AllowHandleAllocation::IsAllowed()); + HandleScopeData* const current = isolate_->handle_scope_data(); + // Shrink the current handle scope to make it impossible to do + // handle allocations without an explicit handle scope. + limit_ = current->limit; + current->limit = current->next; + level_ = current->level; + current->level = 0; +} + + +SealHandleScope::~SealHandleScope() { + // Restore state in current handle scope to re-enable handle + // allocations. + HandleScopeData* const current = isolate_->handle_scope_data(); + DCHECK_EQ(0, current->level); + current->level = level_; + DCHECK_EQ(current->next, current->limit); + current->limit = limit_; +} + +#endif // DEBUG + +} // namespace internal +} // namespace v8 diff --git a/src/handles.h b/src/handles.h index eb57f0e260..36d10acaa7 100644 --- a/src/handles.h +++ b/src/handles.h @@ -10,22 +10,26 @@ namespace v8 { namespace internal { +// Forward declarations. +class DeferredHandles; +class HandleScopeImplementer; + + // A Handle can be converted into a MaybeHandle. Converting a MaybeHandle // into a Handle requires checking that it does not point to NULL. This // ensures NULL checks before use. // Do not use MaybeHandle as argument type. - template class MaybeHandle { public: - INLINE(MaybeHandle()) : location_(NULL) { } + V8_INLINE MaybeHandle() : location_(nullptr) {} // Constructor for handling automatic up casting from Handle. // Ex. Handle can be passed when MaybeHandle is expected. template MaybeHandle(Handle handle) { #ifdef DEBUG - T* a = NULL; - S* b = NULL; + T* a = nullptr; + S* b = nullptr; a = b; // Fake assignment to enforce type checks. USE(a); #endif @@ -44,10 +48,10 @@ class MaybeHandle { location_ = reinterpret_cast(maybe_handle.location_); } - INLINE(void Assert() const) { DCHECK(location_ != NULL); } - INLINE(void Check() const) { CHECK(location_ != NULL); } + V8_INLINE void Assert() const { DCHECK_NOT_NULL(location_); } + V8_INLINE void Check() const { CHECK_NOT_NULL(location_); } - INLINE(Handle ToHandleChecked()) const { + V8_INLINE Handle ToHandleChecked() const { Check(); return Handle(location_); } @@ -74,69 +78,114 @@ class MaybeHandle { template friend class MaybeHandle; }; + +// Base class for Handles. Don't use this directly. +class HandleBase { + public: + V8_INLINE explicit HandleBase(Object** location = nullptr) + : location_(location) {} + V8_INLINE explicit HandleBase(HandleBase const& other) + : location_(other.location_) {} + explicit HandleBase(HeapObject* object); + explicit HandleBase(Object* object, Isolate* isolate); + V8_INLINE ~HandleBase() {} + + // Check if this handle refers to the exact same object as the other handle. + V8_INLINE bool is_identical_to(HandleBase const& other) const { + // Dereferencing deferred handles to check object equality is safe. + SLOW_DCHECK(is_null() || IsDereferenceAllowed(NO_DEFERRED_CHECK)); + SLOW_DCHECK(other.is_null() || + other.IsDereferenceAllowed(NO_DEFERRED_CHECK)); + if (location_ == other.location_) return true; + if (location_ == nullptr || other.location_ == nullptr) return false; + return *location_ == *other.location_; + } + + // Check if this handle is a NULL handle. + V8_INLINE bool is_null() const { return location_ == nullptr; } + + protected: + // Provides the C++ deference operator. + V8_INLINE Object* operator*() const { + SLOW_DCHECK(IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK)); + return *location_; + } + + // Returns the address to where the raw pointer is stored. + V8_INLINE Object** location() const { + SLOW_DCHECK(location_ == nullptr || + IsDereferenceAllowed(INCLUDE_DEFERRED_CHECK)); + return location_; + } + + enum DereferenceCheckMode { INCLUDE_DEFERRED_CHECK, NO_DEFERRED_CHECK }; +#ifdef DEBUG + bool IsDereferenceAllowed(DereferenceCheckMode mode) const; +#else + V8_INLINE bool IsDereferenceAllowed(DereferenceCheckMode) const { + return true; + } +#endif // DEBUG + + Object** location_; +}; + + // ---------------------------------------------------------------------------- // A Handle provides a reference to an object that survives relocation by // the garbage collector. // Handles are only valid within a HandleScope. // When a handle is created for an object a cell is allocated in the heap. - -template -class Handle { +template +class Handle final : public HandleBase { public: - INLINE(explicit Handle(T** location)) { location_ = location; } - INLINE(explicit Handle(T* obj)); - INLINE(Handle(T* obj, Isolate* isolate)); + V8_INLINE explicit Handle(T** location) + : HandleBase(reinterpret_cast(location)) {} + V8_INLINE explicit Handle(T* object) : HandleBase(object) {} + V8_INLINE Handle(T* object, Isolate* isolate) : HandleBase(object, isolate) {} // TODO(yangguo): Values that contain empty handles should be declared as // MaybeHandle to force validation before being used as handles. - INLINE(Handle()) : location_(NULL) { } + V8_INLINE Handle() {} // Constructor for handling automatic up casting. // Ex. Handle can be passed when Handle is expected. - template Handle(Handle handle) { -#ifdef DEBUG - T* a = NULL; - S* b = NULL; + template + V8_INLINE Handle(Handle const& other) + : HandleBase(other) { + T* a = nullptr; + S* b = nullptr; a = b; // Fake assignment to enforce type checks. USE(a); -#endif - location_ = reinterpret_cast(handle.location_); } - INLINE(T* operator->() const) { return operator*(); } - - // Check if this handle refers to the exact same object as the other handle. - INLINE(bool is_identical_to(const Handle other) const); + V8_INLINE T* operator->() const { return operator*(); } // Provides the C++ dereference operator. - INLINE(T* operator*() const); + V8_INLINE T* operator*() const { + return reinterpret_cast(HandleBase::operator*()); + } // Returns the address to where the raw pointer is stored. - INLINE(T** location() const); + V8_INLINE T** location() const { + return reinterpret_cast(HandleBase::location()); + } - template static Handle cast(Handle that) { - T::cast(*reinterpret_cast(that.location_)); - return Handle(reinterpret_cast(that.location_)); + template + V8_INLINE static Handle cast(Handle const& other) { + T::cast(*reinterpret_cast(other.location_)); + return Handle(reinterpret_cast(other.location_)); } // TODO(yangguo): Values that contain empty handles should be declared as // MaybeHandle to force validation before being used as handles. static Handle null() { return Handle(); } - bool is_null() const { return location_ == NULL; } // Closes the given scope, but lets this handle escape. See // implementation in api.h. inline Handle EscapeFrom(v8::EscapableHandleScope* scope); -#ifdef DEBUG - enum DereferenceCheckMode { INCLUDE_DEFERRED_CHECK, NO_DEFERRED_CHECK }; - - bool IsDereferenceAllowed(DereferenceCheckMode mode) const; -#endif // DEBUG - private: - T** location_; - // Handles of different classes are allowed to access each other's location_. template friend class Handle; }; @@ -150,9 +199,9 @@ inline Handle handle(T* t, Isolate* isolate) { // Convenience wrapper. -template +template inline Handle handle(T* t) { - return Handle(t, t->GetIsolate()); + return Handle(t); } @@ -163,10 +212,6 @@ inline bool operator<(const Handle& lhs, const Handle& rhs) { } -class DeferredHandles; -class HandleScopeImplementer; - - // A stack-allocated class that governs a number of local handles. // After a handle scope has been created, all local handles will be // allocated within that handle scope until either the handle scope is @@ -179,18 +224,21 @@ class HandleScopeImplementer; // garbage collector will no longer track the object stored in the // handle and may deallocate it. The behavior of accessing a handle // for which the handle scope has been deleted is undefined. -class HandleScope { +class HandleScope final { public: - explicit inline HandleScope(Isolate* isolate); - - inline ~HandleScope(); + explicit HandleScope(Isolate* isolate); + ~HandleScope(); // Counts the number of allocated handles. static int NumberOfHandles(Isolate* isolate); // Creates a new handle with the given value. + static Object** CreateHandle(Isolate* isolate, Object* value); template - static inline T** CreateHandle(Isolate* isolate, T* value); + static T** CreateHandle(Isolate* isolate, T* value) { + return reinterpret_cast( + CreateHandle(isolate, static_cast(value))); + } // Deallocates any extensions used by the current scope. static void DeleteExtensions(Isolate* isolate); @@ -203,45 +251,44 @@ class HandleScope { // created in the scope of the HandleScope) and returns // a Handle backed by the parent scope holding the // value of the argument handle. + Handle CloseAndEscape(Handle handle); template - Handle CloseAndEscape(Handle handle_value); + Handle CloseAndEscape(Handle handle) { + return Handle::cast(CloseAndEscape(Handle::cast(handle))); + } - Isolate* isolate() { return isolate_; } + Isolate* isolate() const { return isolate_; } private: - // Prevent heap allocation or illegal handle scopes. - HandleScope(const HandleScope&); - void operator=(const HandleScope&); - void* operator new(size_t size); - void operator delete(void* size_t); + friend class v8::HandleScope; + friend class v8::internal::DeferredHandles; + friend class v8::internal::HandleScopeImplementer; + friend class v8::internal::Isolate; - Isolate* isolate_; - Object** prev_next_; - Object** prev_limit_; + // Prevent heap allocation or illegal handle scopes. + void* operator new(size_t size) = delete; + void operator delete(void* size_t) = delete; // Close the handle scope resetting limits to a previous state. - static inline void CloseScope(Isolate* isolate, - Object** prev_next, - Object** prev_limit); + static void CloseScope(Isolate* isolate, Object** prev_next, + Object** prev_limit); // Extend the handle scope making room for more handles. - static internal::Object** Extend(Isolate* isolate); + static Object** Extend(Isolate* isolate); #ifdef ENABLE_HANDLE_ZAPPING // Zaps the handles in the half-open interval [start, end). static void ZapRange(Object** start, Object** end); #endif - friend class v8::HandleScope; - friend class v8::internal::DeferredHandles; - friend class v8::internal::HandleScopeImplementer; - friend class v8::internal::Isolate; + Isolate* const isolate_; + Object** prev_next_; + Object** prev_limit_; + + DISALLOW_COPY_AND_ASSIGN(HandleScope); }; -class DeferredHandles; - - class DeferredHandleScope { public: explicit DeferredHandleScope(Isolate* isolate); @@ -267,32 +314,38 @@ class DeferredHandleScope { // Seal off the current HandleScope so that new handles can only be created // if a new HandleScope is entered. -class SealHandleScope BASE_EMBEDDED { +class SealHandleScope final { public: #ifndef DEBUG explicit SealHandleScope(Isolate* isolate) {} ~SealHandleScope() {} #else - explicit inline SealHandleScope(Isolate* isolate); - inline ~SealHandleScope(); + explicit SealHandleScope(Isolate* isolate); + ~SealHandleScope(); + private: - Isolate* isolate_; + Isolate* const isolate_; Object** limit_; int level_; -#endif +#endif // DEBUG + + private: + DISALLOW_COPY_AND_ASSIGN(SealHandleScope); }; + struct HandleScopeData { internal::Object** next; internal::Object** limit; int level; void Initialize() { - next = limit = NULL; + next = limit = nullptr; level = 0; } }; -} } // namespace v8::internal +} // namespace internal +} // namespace v8 #endif // V8_HANDLES_H_