1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_HANDLES_HANDLES_H_ 6 #define V8_HANDLES_HANDLES_H_ 7 8 #include <type_traits> 9 10 #include "include/v8.h" 11 #include "src/base/functional.h" 12 #include "src/base/macros.h" 13 #include "src/common/checks.h" 14 #include "src/common/globals.h" 15 #include "src/zone/zone.h" 16 17 namespace v8 { 18 namespace internal { 19 20 // Forward declarations. 21 class HandleScopeImplementer; 22 class Isolate; 23 class LocalHeap; 24 class LocalIsolate; 25 template <typename T> 26 class MaybeHandle; 27 class Object; 28 class OrderedHashMap; 29 class OrderedHashSet; 30 class OrderedNameDictionary; 31 class RootVisitor; 32 class SmallOrderedHashMap; 33 class SmallOrderedHashSet; 34 class SmallOrderedNameDictionary; 35 class WasmExportedFunctionData; 36 37 // ---------------------------------------------------------------------------- 38 // Base class for Handle instantiations. Don't use directly. 39 class HandleBase { 40 public: HandleBase(Address * location)41 V8_INLINE explicit HandleBase(Address* location) : location_(location) {} 42 V8_INLINE explicit HandleBase(Address object, Isolate* isolate); 43 V8_INLINE explicit HandleBase(Address object, LocalIsolate* isolate); 44 V8_INLINE explicit HandleBase(Address object, LocalHeap* local_heap); 45 46 // Check if this handle refers to the exact same object as the other handle. 47 V8_INLINE bool is_identical_to(const HandleBase that) const; is_null()48 V8_INLINE bool is_null() const { return location_ == nullptr; } 49 50 // Returns the raw address where this handle is stored. This should only be 51 // used for hashing handles; do not ever try to dereference it. address()52 V8_INLINE Address address() const { return bit_cast<Address>(location_); } 53 54 // Returns the address to where the raw pointer is stored. 55 // TODO(leszeks): This should probably be a const Address*, to encourage using 56 // PatchValue for modifying the handle's value. location()57 V8_INLINE Address* location() const { 58 SLOW_DCHECK(location_ == nullptr || IsDereferenceAllowed()); 59 return location_; 60 } 61 62 protected: 63 #ifdef DEBUG 64 bool V8_EXPORT_PRIVATE IsDereferenceAllowed() const; 65 #else 66 V8_INLINE 67 bool V8_EXPORT_PRIVATE IsDereferenceAllowed() const { return true; } 68 #endif // DEBUG 69 70 // This uses type Address* as opposed to a pointer type to a typed 71 // wrapper class, because it doesn't point to instances of such a 72 // wrapper class. Design overview: https://goo.gl/Ph4CGz 73 Address* location_; 74 }; 75 76 // ---------------------------------------------------------------------------- 77 // A Handle provides a reference to an object that survives relocation by 78 // the garbage collector. 79 // 80 // Handles are only valid within a HandleScope. When a handle is created 81 // for an object a cell is allocated in the current HandleScope. 82 // 83 // Also note that Handles do not provide default equality comparison or hashing 84 // operators on purpose. Such operators would be misleading, because intended 85 // semantics is ambiguous between Handle location and object identity. Instead 86 // use either {is_identical_to} or {location} explicitly. 87 template <typename T> 88 class Handle final : public HandleBase { 89 public: 90 // {ObjectRef} is returned by {Handle::operator->}. It should never be stored 91 // anywhere or used in any other code; no one should ever have to spell out 92 // {ObjectRef} in code. Its only purpose is to be dereferenced immediately by 93 // "operator-> chaining". Returning the address of the field is valid because 94 // this objects lifetime only ends at the end of the full statement. 95 class ObjectRef { 96 public: 97 T* operator->() { return &object_; } 98 99 private: 100 friend class Handle<T>; ObjectRef(T object)101 explicit ObjectRef(T object) : object_(object) {} 102 103 T object_; 104 }; 105 Handle()106 V8_INLINE explicit Handle() : HandleBase(nullptr) { 107 // Skip static type check in order to allow Handle<XXX>::null() as default 108 // parameter values in non-inl header files without requiring full 109 // definition of type XXX. 110 } 111 Handle(Address * location)112 V8_INLINE explicit Handle(Address* location) : HandleBase(location) { 113 // This static type check also fails for forward class declarations. 114 static_assert(std::is_convertible<T*, Object*>::value, 115 "static type violation"); 116 // TODO(jkummerow): Runtime type check here as a SLOW_DCHECK? 117 } 118 119 V8_INLINE Handle(T object, Isolate* isolate); 120 V8_INLINE Handle(T object, LocalIsolate* isolate); 121 V8_INLINE Handle(T object, LocalHeap* local_heap); 122 123 // Allocate a new handle for the object, do not canonicalize. 124 V8_INLINE static Handle<T> New(T object, Isolate* isolate); 125 126 // Constructor for handling automatic up casting. 127 // Ex. Handle<JSFunction> can be passed when Handle<Object> is expected. 128 template <typename S, typename = typename std::enable_if< 129 std::is_convertible<S*, T*>::value>::type> 130 // NOLINTNEXTLINE Handle(Handle<S> handle)131 V8_INLINE Handle(Handle<S> handle) : HandleBase(handle) {} 132 133 V8_INLINE ObjectRef operator->() const { return ObjectRef{**this}; } 134 135 V8_INLINE T operator*() const { 136 // unchecked_cast because we rather trust Handle<T> to contain a T than 137 // include all the respective -inl.h headers for SLOW_DCHECKs. 138 SLOW_DCHECK(IsDereferenceAllowed()); 139 return T::unchecked_cast(Object(*location())); 140 } 141 142 template <typename S> 143 inline static const Handle<T> cast(Handle<S> that); 144 145 // Consider declaring values that contain empty handles as 146 // MaybeHandle to force validation before being used as handles. null()147 static const Handle<T> null() { return Handle<T>(); } 148 149 // Location equality. equals(Handle<T> other)150 bool equals(Handle<T> other) const { return address() == other.address(); } 151 152 // Patches this Handle's value, in-place, with a new value. All handles with 153 // the same location will see this update. PatchValue(T new_value)154 void PatchValue(T new_value) { 155 SLOW_DCHECK(location_ != nullptr && IsDereferenceAllowed()); 156 *location_ = new_value.ptr(); 157 } 158 159 // Provide function object for location equality comparison. 160 struct equal_to { operatorequal_to161 V8_INLINE bool operator()(Handle<T> lhs, Handle<T> rhs) const { 162 return lhs.equals(rhs); 163 } 164 }; 165 166 // Provide function object for location hashing. 167 struct hash { operatorhash168 V8_INLINE size_t operator()(Handle<T> const& handle) const { 169 return base::hash<Address>()(handle.address()); 170 } 171 }; 172 173 private: 174 // Handles of different classes are allowed to access each other's location_. 175 template <typename> 176 friend class Handle; 177 // MaybeHandle is allowed to access location_. 178 template <typename> 179 friend class MaybeHandle; 180 }; 181 182 template <typename T> 183 inline std::ostream& operator<<(std::ostream& os, Handle<T> handle); 184 185 // ---------------------------------------------------------------------------- 186 // A stack-allocated class that governs a number of local handles. 187 // After a handle scope has been created, all local handles will be 188 // allocated within that handle scope until either the handle scope is 189 // deleted or another handle scope is created. If there is already a 190 // handle scope and a new one is created, all allocations will take 191 // place in the new handle scope until it is deleted. After that, 192 // new handles will again be allocated in the original handle scope. 193 // 194 // After the handle scope of a local handle has been deleted the 195 // garbage collector will no longer track the object stored in the 196 // handle and may deallocate it. The behavior of accessing a handle 197 // for which the handle scope has been deleted is undefined. 198 class HandleScope { 199 public: 200 explicit inline HandleScope(Isolate* isolate); 201 inline HandleScope(HandleScope&& other) V8_NOEXCEPT; 202 203 // Allow placement new. new(size_t size,void * storage)204 void* operator new(size_t size, void* storage) { 205 return ::operator new(size, storage); 206 } 207 208 // Prevent heap allocation or illegal handle scopes. 209 void* operator new(size_t size) = delete; 210 void operator delete(void* size_t) = delete; 211 212 inline ~HandleScope(); 213 214 inline HandleScope& operator=(HandleScope&& other) V8_NOEXCEPT; 215 216 // Counts the number of allocated handles. 217 V8_EXPORT_PRIVATE static int NumberOfHandles(Isolate* isolate); 218 219 // Create a new handle or lookup a canonical handle. 220 V8_INLINE static Address* GetHandle(Isolate* isolate, Address value); 221 222 // Creates a new handle with the given value. 223 V8_INLINE static Address* CreateHandle(Isolate* isolate, Address value); 224 225 // Deallocates any extensions used by the current scope. 226 V8_EXPORT_PRIVATE static void DeleteExtensions(Isolate* isolate); 227 228 static Address current_next_address(Isolate* isolate); 229 static Address current_limit_address(Isolate* isolate); 230 static Address current_level_address(Isolate* isolate); 231 232 // Closes the HandleScope (invalidating all handles 233 // created in the scope of the HandleScope) and returns 234 // a Handle backed by the parent scope holding the 235 // value of the argument handle. 236 template <typename T> 237 Handle<T> CloseAndEscape(Handle<T> handle_value); 238 isolate()239 Isolate* isolate() { return isolate_; } 240 241 // Limit for number of handles with --check-handle-count. This is 242 // large enough to compile natives and pass unit tests with some 243 // slack for future changes to natives. 244 static const int kCheckHandleThreshold = 30 * 1024; 245 246 private: 247 Isolate* isolate_; 248 Address* prev_next_; 249 Address* prev_limit_; 250 251 // Close the handle scope resetting limits to a previous state. 252 static inline void CloseScope(Isolate* isolate, Address* prev_next, 253 Address* prev_limit); 254 255 // Extend the handle scope making room for more handles. 256 V8_EXPORT_PRIVATE static Address* Extend(Isolate* isolate); 257 258 #ifdef ENABLE_HANDLE_ZAPPING 259 // Zaps the handles in the half-open interval [start, end). 260 V8_EXPORT_PRIVATE static void ZapRange(Address* start, Address* end); 261 #endif 262 263 friend class v8::HandleScope; 264 friend class HandleScopeImplementer; 265 friend class Isolate; 266 friend class LocalHandles; 267 friend class PersistentHandles; 268 269 DISALLOW_COPY_AND_ASSIGN(HandleScope); 270 }; 271 272 // Forward declarations for CanonicalHandleScope. 273 template <typename V, class AllocationPolicy> 274 class IdentityMap; 275 class RootIndexMap; 276 class OptimizedCompilationInfo; 277 278 using CanonicalHandlesMap = IdentityMap<Address*, ZoneAllocationPolicy>; 279 280 // A CanonicalHandleScope does not open a new HandleScope. It changes the 281 // existing HandleScope so that Handles created within are canonicalized. 282 // This does not apply to nested inner HandleScopes unless a nested 283 // CanonicalHandleScope is introduced. Handles are only canonicalized within 284 // the same CanonicalHandleScope, but not across nested ones. 285 class V8_EXPORT_PRIVATE CanonicalHandleScope final { 286 public: 287 // If we passed a compilation info as parameter, we created the 288 // CanonicalHandlesMap on said compilation info's zone(). If so, in the 289 // CanonicalHandleScope destructor we hand off the canonical handle map to the 290 // compilation info. The compilation info is responsible for the disposal. If 291 // we don't have a compilation info, we create a zone in this constructor. To 292 // properly dispose of said zone, we need to first free the identity_map_ 293 // which is done manually even though identity_map_ is a unique_ptr. 294 explicit CanonicalHandleScope(Isolate* isolate, 295 OptimizedCompilationInfo* info = nullptr); 296 ~CanonicalHandleScope(); 297 298 private: 299 Address* Lookup(Address object); 300 301 std::unique_ptr<CanonicalHandlesMap> DetachCanonicalHandles(); 302 303 Isolate* isolate_; 304 OptimizedCompilationInfo* info_; 305 Zone* zone_; 306 RootIndexMap* root_index_map_; 307 std::unique_ptr<CanonicalHandlesMap> identity_map_; 308 // Ordinary nested handle scopes within the current one are not canonical. 309 int canonical_level_; 310 // We may have nested canonical scopes. Handles are canonical within each one. 311 CanonicalHandleScope* prev_canonical_scope_; 312 313 friend class HandleScope; 314 }; 315 316 // Seal off the current HandleScope so that new handles can only be created 317 // if a new HandleScope is entered. 318 class SealHandleScope final { 319 public: 320 #ifndef DEBUG SealHandleScope(Isolate * isolate)321 explicit SealHandleScope(Isolate* isolate) {} 322 ~SealHandleScope() = default; 323 #else 324 explicit inline SealHandleScope(Isolate* isolate); 325 inline ~SealHandleScope(); 326 327 private: 328 Isolate* isolate_; 329 Address* prev_limit_; 330 int prev_sealed_level_; 331 #endif 332 }; 333 334 struct HandleScopeData final { 335 Address* next; 336 Address* limit; 337 int level; 338 int sealed_level; 339 CanonicalHandleScope* canonical_scope; 340 Initializefinal341 void Initialize() { 342 next = limit = nullptr; 343 sealed_level = level = 0; 344 canonical_scope = nullptr; 345 } 346 }; 347 348 } // namespace internal 349 } // namespace v8 350 351 #endif // V8_HANDLES_HANDLES_H_ 352