• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 namespace v8 {
17 
18 class HandleScope;
19 
20 namespace internal {
21 
22 // Forward declarations.
23 class HandleScopeImplementer;
24 class Isolate;
25 class LocalHeap;
26 class LocalIsolate;
27 template <typename T>
28 class MaybeHandle;
29 class Object;
30 class OrderedHashMap;
31 class OrderedHashSet;
32 class OrderedNameDictionary;
33 class RootVisitor;
34 class SmallOrderedHashMap;
35 class SmallOrderedHashSet;
36 class SmallOrderedNameDictionary;
37 class SwissNameDictionary;
38 class WasmExportedFunctionData;
39 
40 // ----------------------------------------------------------------------------
41 // Base class for Handle instantiations.  Don't use directly.
42 class HandleBase {
43  public:
HandleBase(Address * location)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;
is_null()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.
address()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.
location()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.
90 template <typename T>
91 class 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>;
ObjectRef(T object)104     explicit ObjectRef(T object) : object_(object) {}
105 
106     T object_;
107   };
108 
Handle()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 
Handle(Address * location)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>
Handle(Handle<S> handle)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.
null()149   static const Handle<T> null() { return Handle<T>(); }
150 
151   // Location equality.
equals(Handle<T> other)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.
PatchValue(T new_value)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 {
operatorequal_to163     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 {
operatorhash170     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 
184 template <typename T>
185 std::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.
200 class 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.
new(size_t size,void * storage)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 
isolate()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.
276 template <typename V, class AllocationPolicy>
277 class IdentityMap;
278 class RootIndexMap;
279 class OptimizedCompilationInfo;
280 
281 namespace maglev {
282 class ExportedMaglevCompilationInfo;
283 }  // namespace maglev
284 
285 using 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.
292 class 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 
319 template <class CompilationInfoT>
320 class 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 
335 using CanonicalHandleScopeForTurbofan =
336     CanonicalHandleScopeForOptimization<OptimizedCompilationInfo>;
337 using 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.
342 class V8_NODISCARD SealHandleScope final {
343  public:
344 #ifndef DEBUG
SealHandleScope(Isolate * isolate)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 
358 struct HandleScopeData final {
359   Address* next;
360   Address* limit;
361   int level;
362   int sealed_level;
363   CanonicalHandleScope* canonical_scope;
364 
Initializefinal365   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