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