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