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