1 // Copyright 2013 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_COMMON_ASSERT_SCOPE_H_
6 #define V8_COMMON_ASSERT_SCOPE_H_
7 
8 #include <stdint.h>
9 
10 #include <memory>
11 
12 #include "src/base/macros.h"
13 #include "src/base/optional.h"
14 #include "src/base/platform/mutex.h"
15 #include "src/common/globals.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 // Forward declarations.
21 class Isolate;
22 
23 enum PerThreadAssertType {
24   SAFEPOINTS_ASSERT,
25   HEAP_ALLOCATION_ASSERT,
26   HANDLE_ALLOCATION_ASSERT,
27   HANDLE_DEREFERENCE_ASSERT,
28   CODE_DEPENDENCY_CHANGE_ASSERT,
29   CODE_ALLOCATION_ASSERT,
30   // Dummy type for disabling GC mole.
31   GC_MOLE,
32 };
33 
34 template <PerThreadAssertType kType, bool kAllow>
35 class V8_NODISCARD PerThreadAssertScope {
36  public:
37   V8_EXPORT_PRIVATE PerThreadAssertScope();
38   V8_EXPORT_PRIVATE ~PerThreadAssertScope();
39 
40   PerThreadAssertScope(const PerThreadAssertScope&) = delete;
41   PerThreadAssertScope& operator=(const PerThreadAssertScope&) = delete;
42 
43   V8_EXPORT_PRIVATE static bool IsAllowed();
44 
45   void Release();
46 
47  private:
48   base::Optional<uint32_t> old_data_;
49 };
50 
51 // Per-isolate assert scopes.
52 
53 #define PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(V, enable)                   \
54   /* Scope to document where we do not expect javascript execution. */  \
55   /* Scope to introduce an exception to DisallowJavascriptExecution. */ \
56   V(AllowJavascriptExecution, DisallowJavascriptExecution,              \
57     javascript_execution_assert, enable)                                \
58   /* Scope to document where we do not expect deoptimization. */        \
59   /* Scope to introduce an exception to DisallowDeoptimization. */      \
60   V(AllowDeoptimization, DisallowDeoptimization, deoptimization_assert, \
61     enable)                                                             \
62   /* Scope to document where we do not expect deoptimization. */        \
63   /* Scope to introduce an exception to DisallowDeoptimization. */      \
64   V(AllowCompilation, DisallowCompilation, compilation_assert, enable)  \
65   /* Scope to document where we do not expect exceptions. */            \
66   /* Scope to introduce an exception to DisallowExceptions. */          \
67   V(AllowExceptions, DisallowExceptions, no_exception_assert, enable)
68 
69 #define PER_ISOLATE_ASSERT_TYPE(V, enable)                                   \
70   /* Scope in which javascript execution leads to exception being thrown. */ \
71   /* Scope to introduce an exception to ThrowOnJavascriptExecution. */       \
72   V(NoThrowOnJavascriptExecution, ThrowOnJavascriptExecution,                \
73     javascript_execution_throws, enable)                                     \
74   /* Scope in which javascript execution causes dumps. */                    \
75   /* Scope in which javascript execution doesn't cause dumps. */             \
76   V(NoDumpOnJavascriptExecution, DumpOnJavascriptExecution,                  \
77     javascript_execution_dump, enable)                                       \
78   PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(V, enable)
79 
80 #define PER_ISOLATE_ASSERT_SCOPE_DECLARATION(ScopeType)              \
81   class V8_NODISCARD ScopeType {                                     \
82    public:                                                           \
83     V8_EXPORT_PRIVATE explicit ScopeType(Isolate* isolate);          \
84     ScopeType(const ScopeType&) = delete;                            \
85     ScopeType& operator=(const ScopeType&) = delete;                 \
86     V8_EXPORT_PRIVATE ~ScopeType();                                  \
87                                                                      \
88     static bool IsAllowed(Isolate* isolate);                         \
89                                                                      \
90     V8_EXPORT_PRIVATE static void Open(Isolate* isolate,             \
91                                        bool* was_execution_allowed); \
92     V8_EXPORT_PRIVATE static void Close(Isolate* isolate,            \
93                                         bool was_execution_allowed); \
94                                                                      \
95    private:                                                          \
96     Isolate* isolate_;                                               \
97     bool old_data_;                                                  \
98   };
99 
100 #define PER_ISOLATE_ASSERT_ENABLE_SCOPE(EnableType, _1, _2, _3) \
101   PER_ISOLATE_ASSERT_SCOPE_DECLARATION(EnableType)
102 
103 #define PER_ISOLATE_ASSERT_DISABLE_SCOPE(_1, DisableType, _2, _3) \
104   PER_ISOLATE_ASSERT_SCOPE_DECLARATION(DisableType)
105 
106 PER_ISOLATE_ASSERT_TYPE(PER_ISOLATE_ASSERT_ENABLE_SCOPE, true)
107 PER_ISOLATE_ASSERT_TYPE(PER_ISOLATE_ASSERT_DISABLE_SCOPE, false)
108 
109 #ifdef DEBUG
110 #define PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType,   \
111                                                    field, _)                  \
112   class EnableType##DebugOnly : public EnableType {                           \
113    public:                                                                    \
114     explicit EnableType##DebugOnly(Isolate* isolate) : EnableType(isolate) {} \
115   };
116 #else
117 #define PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \
118                                                    field, _)                \
119   class V8_NODISCARD EnableType##DebugOnly {                                \
120    public:                                                                  \
121     explicit EnableType##DebugOnly(Isolate* isolate) {}                     \
122   };
123 #endif
124 
125 #ifdef DEBUG
126 #define PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \
127                                                     field, _)                \
128   class DisableType##DebugOnly : public DisableType {                        \
129    public:                                                                   \
130     explicit DisableType##DebugOnly(Isolate* isolate)                        \
131         : DisableType(isolate) {}                                            \
132   };
133 #else
134 #define PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \
135                                                     field, _)                \
136   class V8_NODISCARD DisableType##DebugOnly {                                \
137    public:                                                                   \
138     explicit DisableType##DebugOnly(Isolate* isolate) {}                     \
139   };
140 #endif
141 
142 PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY,
143                                    true)
144 PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY,
145                                    false)
146 
147 template <typename... Scopes>
148 class CombinationAssertScope;
149 
150 // Base case for CombinationAssertScope (equivalent to Scope).
151 template <typename Scope>
152 class V8_NODISCARD CombinationAssertScope<Scope> : public Scope {
153  public:
IsAllowed()154   V8_EXPORT_PRIVATE static bool IsAllowed() {
155     // Define IsAllowed() explicitly rather than with using Scope::IsAllowed, to
156     // allow SFINAE removal of IsAllowed() when it's not defined (under debug).
157     return Scope::IsAllowed();
158   }
159   using Scope::Release;
160   using Scope::Scope;
161 };
162 
163 // Inductive case for CombinationAssertScope.
164 template <typename Scope, typename... Scopes>
165 class CombinationAssertScope<Scope, Scopes...>
166     : public Scope, public CombinationAssertScope<Scopes...> {
167   using NextScopes = CombinationAssertScope<Scopes...>;
168 
169  public:
170   // Constructor for per-thread scopes.
CombinationAssertScope()171   V8_EXPORT_PRIVATE CombinationAssertScope() : Scope(), NextScopes() {}
172   // Constructor for per-isolate scopes.
CombinationAssertScope(Isolate * isolate)173   V8_EXPORT_PRIVATE explicit CombinationAssertScope(Isolate* isolate)
174       : Scope(isolate), NextScopes(isolate) {}
175 
IsAllowed()176   V8_EXPORT_PRIVATE static bool IsAllowed() {
177     return Scope::IsAllowed() && NextScopes::IsAllowed();
178   }
179 
Release()180   void Release() {
181     // Release in reverse order.
182     NextScopes::Release();
183     Scope::Release();
184   }
185 };
186 
187 template <PerThreadAssertType kType, bool kAllow>
188 #ifdef DEBUG
189 class PerThreadAssertScopeDebugOnly
190     : public PerThreadAssertScope<kType, kAllow> {
191 #else
192 class V8_NODISCARD PerThreadAssertScopeDebugOnly {
193  public:
194   PerThreadAssertScopeDebugOnly() {
195     // Define a constructor to avoid unused variable warnings.
196   }
197   void Release() {}
198 #endif
199 };
200 
201 // Per-thread assert scopes.
202 
203 // Scope to document where we do not expect handles to be created.
204 using DisallowHandleAllocation =
205     PerThreadAssertScopeDebugOnly<HANDLE_ALLOCATION_ASSERT, false>;
206 
207 // Scope to introduce an exception to DisallowHandleAllocation.
208 using AllowHandleAllocation =
209     PerThreadAssertScopeDebugOnly<HANDLE_ALLOCATION_ASSERT, true>;
210 
211 // Scope to document where we do not expect safepoints to be entered.
212 using DisallowSafepoints =
213     PerThreadAssertScopeDebugOnly<SAFEPOINTS_ASSERT, false>;
214 
215 // Scope to introduce an exception to DisallowSafepoints.
216 using AllowSafepoints = PerThreadAssertScopeDebugOnly<SAFEPOINTS_ASSERT, true>;
217 
218 // Scope to document where we do not expect any allocation.
219 using DisallowHeapAllocation =
220     PerThreadAssertScopeDebugOnly<HEAP_ALLOCATION_ASSERT, false>;
221 
222 // Scope to introduce an exception to DisallowHeapAllocation.
223 using AllowHeapAllocation =
224     PerThreadAssertScopeDebugOnly<HEAP_ALLOCATION_ASSERT, true>;
225 
226 // Scope to document where we do not expect any handle dereferences.
227 using DisallowHandleDereference =
228     PerThreadAssertScopeDebugOnly<HANDLE_DEREFERENCE_ASSERT, false>;
229 
230 // Scope to introduce an exception to DisallowHandleDereference.
231 using AllowHandleDereference =
232     PerThreadAssertScopeDebugOnly<HANDLE_DEREFERENCE_ASSERT, true>;
233 
234 // Scope to document where we do not expect code dependencies to change.
235 using DisallowCodeDependencyChange =
236     PerThreadAssertScopeDebugOnly<CODE_DEPENDENCY_CHANGE_ASSERT, false>;
237 
238 // Scope to introduce an exception to DisallowCodeDependencyChange.
239 using AllowCodeDependencyChange =
240     PerThreadAssertScopeDebugOnly<CODE_DEPENDENCY_CHANGE_ASSERT, true>;
241 
242 // Scope to document where we do not expect code to be allocated.
243 using DisallowCodeAllocation =
244     PerThreadAssertScopeDebugOnly<CODE_ALLOCATION_ASSERT, false>;
245 
246 // Scope to introduce an exception to DisallowCodeAllocation.
247 using AllowCodeAllocation =
248     PerThreadAssertScopeDebugOnly<CODE_ALLOCATION_ASSERT, true>;
249 
250 // Scope to document where we do not expect garbage collections. It differs from
251 // DisallowHeapAllocation by also forbidding safepoints.
252 using DisallowGarbageCollection =
253     CombinationAssertScope<DisallowSafepoints, DisallowHeapAllocation>;
254 
255 // Scope to skip gc mole verification in places where we do tricky raw
256 // work.
257 using DisableGCMole = PerThreadAssertScopeDebugOnly<GC_MOLE, false>;
258 
259 // The DISALLOW_GARBAGE_COLLECTION macro can be used to define a
260 // DisallowGarbageCollection field in classes that isn't present in release
261 // builds.
262 #ifdef DEBUG
263 #define DISALLOW_GARBAGE_COLLECTION(name) DisallowGarbageCollection name;
264 #else
265 #define DISALLOW_GARBAGE_COLLECTION(name)
266 #endif
267 
268 // Scope to introduce an exception to DisallowGarbageCollection.
269 using AllowGarbageCollection =
270     CombinationAssertScope<AllowSafepoints, AllowHeapAllocation>;
271 
272 // Scope to document where we do not expect any access to the heap.
273 using DisallowHeapAccess =
274     CombinationAssertScope<DisallowCodeDependencyChange,
275                            DisallowHandleDereference, DisallowHandleAllocation,
276                            DisallowHeapAllocation>;
277 
278 // Scope to introduce an exception to DisallowHeapAccess.
279 using AllowHeapAccess =
280     CombinationAssertScope<AllowCodeDependencyChange, AllowHandleDereference,
281                            AllowHandleAllocation, AllowHeapAllocation>;
282 
283 class DisallowHeapAccessIf {
284  public:
DisallowHeapAccessIf(bool condition)285   explicit DisallowHeapAccessIf(bool condition) {
286     if (condition) maybe_disallow_.emplace();
287   }
288 
289  private:
290   base::Optional<DisallowHeapAccess> maybe_disallow_;
291 };
292 
293 // Like MutexGuard but also asserts that no garbage collection happens while
294 // we're holding the mutex.
295 class V8_NODISCARD NoGarbageCollectionMutexGuard {
296  public:
NoGarbageCollectionMutexGuard(base::Mutex * mutex)297   explicit NoGarbageCollectionMutexGuard(base::Mutex* mutex)
298       : guard_(mutex), mutex_(mutex), no_gc_(new DisallowGarbageCollection()) {}
299 
Unlock()300   void Unlock() {
301     mutex_->Unlock();
302     no_gc_.reset();
303   }
Lock()304   void Lock() {
305     mutex_->Lock();
306     no_gc_.reset(new DisallowGarbageCollection());
307   }
308 
309  private:
310   base::MutexGuard guard_;
311   base::Mutex* mutex_;
312   std::unique_ptr<DisallowGarbageCollection> no_gc_;
313 };
314 
315 // Explicit instantiation declarations.
316 extern template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, false>;
317 extern template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, true>;
318 extern template class PerThreadAssertScope<SAFEPOINTS_ASSERT, false>;
319 extern template class PerThreadAssertScope<SAFEPOINTS_ASSERT, true>;
320 extern template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, false>;
321 extern template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, true>;
322 extern template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, false>;
323 extern template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, true>;
324 extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT,
325                                            false>;
326 extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>;
327 extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>;
328 extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>;
329 extern template class PerThreadAssertScope<GC_MOLE, false>;
330 
331 }  // namespace internal
332 }  // namespace v8
333 
334 #endif  // V8_COMMON_ASSERT_SCOPE_H_
335