| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_COMMON_ASSERT_SCOPE_H_ |
| #define V8_COMMON_ASSERT_SCOPE_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "src/base/macros.h" |
| #include "src/base/optional.h" |
| #include "src/base/platform/mutex.h" |
| #include "src/common/globals.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| // Forward declarations. |
| class Isolate; |
| |
| enum PerThreadAssertType { |
| SAFEPOINTS_ASSERT, |
| HEAP_ALLOCATION_ASSERT, |
| HANDLE_ALLOCATION_ASSERT, |
| HANDLE_DEREFERENCE_ASSERT, |
| CODE_DEPENDENCY_CHANGE_ASSERT, |
| CODE_ALLOCATION_ASSERT, |
| // Dummy type for disabling GC mole. |
| GC_MOLE, |
| }; |
| |
| template <PerThreadAssertType kType, bool kAllow> |
| class V8_NODISCARD PerThreadAssertScope { |
| public: |
| V8_EXPORT_PRIVATE PerThreadAssertScope(); |
| V8_EXPORT_PRIVATE ~PerThreadAssertScope(); |
| |
| PerThreadAssertScope(const PerThreadAssertScope&) = delete; |
| PerThreadAssertScope& operator=(const PerThreadAssertScope&) = delete; |
| |
| V8_EXPORT_PRIVATE static bool IsAllowed(); |
| |
| void Release(); |
| |
| private: |
| base::Optional<uint32_t> old_data_; |
| }; |
| |
| // Per-isolate assert scopes. |
| |
| #define PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(V, enable) \ |
| /* Scope to document where we do not expect javascript execution. */ \ |
| /* Scope to introduce an exception to DisallowJavascriptExecution. */ \ |
| V(AllowJavascriptExecution, DisallowJavascriptExecution, \ |
| javascript_execution_assert, enable) \ |
| /* Scope to document where we do not expect deoptimization. */ \ |
| /* Scope to introduce an exception to DisallowDeoptimization. */ \ |
| V(AllowDeoptimization, DisallowDeoptimization, deoptimization_assert, \ |
| enable) \ |
| /* Scope to document where we do not expect deoptimization. */ \ |
| /* Scope to introduce an exception to DisallowDeoptimization. */ \ |
| V(AllowCompilation, DisallowCompilation, compilation_assert, enable) \ |
| /* Scope to document where we do not expect exceptions. */ \ |
| /* Scope to introduce an exception to DisallowExceptions. */ \ |
| V(AllowExceptions, DisallowExceptions, no_exception_assert, enable) |
| |
| #define PER_ISOLATE_ASSERT_TYPE(V, enable) \ |
| /* Scope in which javascript execution leads to exception being thrown. */ \ |
| /* Scope to introduce an exception to ThrowOnJavascriptExecution. */ \ |
| V(NoThrowOnJavascriptExecution, ThrowOnJavascriptExecution, \ |
| javascript_execution_throws, enable) \ |
| /* Scope in which javascript execution causes dumps. */ \ |
| /* Scope in which javascript execution doesn't cause dumps. */ \ |
| V(NoDumpOnJavascriptExecution, DumpOnJavascriptExecution, \ |
| javascript_execution_dump, enable) \ |
| PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(V, enable) |
| |
| #define PER_ISOLATE_ASSERT_SCOPE_DECLARATION(ScopeType) \ |
| class V8_NODISCARD ScopeType { \ |
| public: \ |
| V8_EXPORT_PRIVATE explicit ScopeType(Isolate* isolate); \ |
| ScopeType(const ScopeType&) = delete; \ |
| ScopeType& operator=(const ScopeType&) = delete; \ |
| V8_EXPORT_PRIVATE ~ScopeType(); \ |
| \ |
| static bool IsAllowed(Isolate* isolate); \ |
| \ |
| V8_EXPORT_PRIVATE static void Open(Isolate* isolate, \ |
| bool* was_execution_allowed); \ |
| V8_EXPORT_PRIVATE static void Close(Isolate* isolate, \ |
| bool was_execution_allowed); \ |
| \ |
| private: \ |
| Isolate* isolate_; \ |
| bool old_data_; \ |
| }; |
| |
| #define PER_ISOLATE_ASSERT_ENABLE_SCOPE(EnableType, _1, _2, _3) \ |
| PER_ISOLATE_ASSERT_SCOPE_DECLARATION(EnableType) |
| |
| #define PER_ISOLATE_ASSERT_DISABLE_SCOPE(_1, DisableType, _2, _3) \ |
| PER_ISOLATE_ASSERT_SCOPE_DECLARATION(DisableType) |
| |
| PER_ISOLATE_ASSERT_TYPE(PER_ISOLATE_ASSERT_ENABLE_SCOPE, true) |
| PER_ISOLATE_ASSERT_TYPE(PER_ISOLATE_ASSERT_DISABLE_SCOPE, false) |
| |
| #ifdef DEBUG |
| #define PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \ |
| field, _) \ |
| class EnableType##DebugOnly : public EnableType { \ |
| public: \ |
| explicit EnableType##DebugOnly(Isolate* isolate) : EnableType(isolate) {} \ |
| }; |
| #else |
| #define PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \ |
| field, _) \ |
| class V8_NODISCARD EnableType##DebugOnly { \ |
| public: \ |
| explicit EnableType##DebugOnly(Isolate* isolate) {} \ |
| }; |
| #endif |
| |
| #ifdef DEBUG |
| #define PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \ |
| field, _) \ |
| class DisableType##DebugOnly : public DisableType { \ |
| public: \ |
| explicit DisableType##DebugOnly(Isolate* isolate) \ |
| : DisableType(isolate) {} \ |
| }; |
| #else |
| #define PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY(EnableType, DisableType, \ |
| field, _) \ |
| class V8_NODISCARD DisableType##DebugOnly { \ |
| public: \ |
| explicit DisableType##DebugOnly(Isolate* isolate) {} \ |
| }; |
| #endif |
| |
| PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(PER_ISOLATE_ASSERT_ENABLE_SCOPE_DEBUG_ONLY, |
| true) |
| PER_ISOLATE_ASSERT_TYPE_DEBUG_ONLY(PER_ISOLATE_ASSERT_DISABLE_SCOPE_DEBUG_ONLY, |
| false) |
| |
| template <typename... Scopes> |
| class CombinationAssertScope; |
| |
| // Base case for CombinationAssertScope (equivalent to Scope). |
| template <typename Scope> |
| class V8_NODISCARD CombinationAssertScope<Scope> : public Scope { |
| public: |
| V8_EXPORT_PRIVATE static bool IsAllowed() { |
| // Define IsAllowed() explicitly rather than with using Scope::IsAllowed, to |
| // allow SFINAE removal of IsAllowed() when it's not defined (under debug). |
| return Scope::IsAllowed(); |
| } |
| using Scope::Release; |
| using Scope::Scope; |
| }; |
| |
| // Inductive case for CombinationAssertScope. |
| template <typename Scope, typename... Scopes> |
| class CombinationAssertScope<Scope, Scopes...> |
| : public Scope, public CombinationAssertScope<Scopes...> { |
| using NextScopes = CombinationAssertScope<Scopes...>; |
| |
| public: |
| // Constructor for per-thread scopes. |
| V8_EXPORT_PRIVATE CombinationAssertScope() : Scope(), NextScopes() {} |
| // Constructor for per-isolate scopes. |
| V8_EXPORT_PRIVATE explicit CombinationAssertScope(Isolate* isolate) |
| : Scope(isolate), NextScopes(isolate) {} |
| |
| V8_EXPORT_PRIVATE static bool IsAllowed() { |
| return Scope::IsAllowed() && NextScopes::IsAllowed(); |
| } |
| |
| void Release() { |
| // Release in reverse order. |
| NextScopes::Release(); |
| Scope::Release(); |
| } |
| }; |
| |
| template <PerThreadAssertType kType, bool kAllow> |
| #ifdef DEBUG |
| class PerThreadAssertScopeDebugOnly |
| : public PerThreadAssertScope<kType, kAllow> { |
| #else |
| class V8_NODISCARD PerThreadAssertScopeDebugOnly { |
| public: |
| PerThreadAssertScopeDebugOnly() { |
| // Define a constructor to avoid unused variable warnings. |
| } |
| void Release() {} |
| #endif |
| }; |
| |
| // Per-thread assert scopes. |
| |
| // Scope to document where we do not expect handles to be created. |
| using DisallowHandleAllocation = |
| PerThreadAssertScopeDebugOnly<HANDLE_ALLOCATION_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowHandleAllocation. |
| using AllowHandleAllocation = |
| PerThreadAssertScopeDebugOnly<HANDLE_ALLOCATION_ASSERT, true>; |
| |
| // Scope to document where we do not expect safepoints to be entered. |
| using DisallowSafepoints = |
| PerThreadAssertScopeDebugOnly<SAFEPOINTS_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowSafepoints. |
| using AllowSafepoints = PerThreadAssertScopeDebugOnly<SAFEPOINTS_ASSERT, true>; |
| |
| // Scope to document where we do not expect any allocation. |
| using DisallowHeapAllocation = |
| PerThreadAssertScopeDebugOnly<HEAP_ALLOCATION_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowHeapAllocation. |
| using AllowHeapAllocation = |
| PerThreadAssertScopeDebugOnly<HEAP_ALLOCATION_ASSERT, true>; |
| |
| // Scope to document where we do not expect any handle dereferences. |
| using DisallowHandleDereference = |
| PerThreadAssertScopeDebugOnly<HANDLE_DEREFERENCE_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowHandleDereference. |
| using AllowHandleDereference = |
| PerThreadAssertScopeDebugOnly<HANDLE_DEREFERENCE_ASSERT, true>; |
| |
| // Scope to document where we do not expect code dependencies to change. |
| using DisallowCodeDependencyChange = |
| PerThreadAssertScopeDebugOnly<CODE_DEPENDENCY_CHANGE_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowCodeDependencyChange. |
| using AllowCodeDependencyChange = |
| PerThreadAssertScopeDebugOnly<CODE_DEPENDENCY_CHANGE_ASSERT, true>; |
| |
| // Scope to document where we do not expect code to be allocated. |
| using DisallowCodeAllocation = |
| PerThreadAssertScopeDebugOnly<CODE_ALLOCATION_ASSERT, false>; |
| |
| // Scope to introduce an exception to DisallowCodeAllocation. |
| using AllowCodeAllocation = |
| PerThreadAssertScopeDebugOnly<CODE_ALLOCATION_ASSERT, true>; |
| |
| // Scope to document where we do not expect garbage collections. It differs from |
| // DisallowHeapAllocation by also forbidding safepoints. |
| using DisallowGarbageCollection = |
| CombinationAssertScope<DisallowSafepoints, DisallowHeapAllocation>; |
| |
| // Scope to skip gc mole verification in places where we do tricky raw |
| // work. |
| using DisableGCMole = PerThreadAssertScopeDebugOnly<GC_MOLE, false>; |
| |
| // The DISALLOW_GARBAGE_COLLECTION macro can be used to define a |
| // DisallowGarbageCollection field in classes that isn't present in release |
| // builds. |
| #ifdef DEBUG |
| #define DISALLOW_GARBAGE_COLLECTION(name) DisallowGarbageCollection name; |
| #else |
| #define DISALLOW_GARBAGE_COLLECTION(name) |
| #endif |
| |
| // Scope to introduce an exception to DisallowGarbageCollection. |
| using AllowGarbageCollection = |
| CombinationAssertScope<AllowSafepoints, AllowHeapAllocation>; |
| |
| // Scope to document where we do not expect any access to the heap. |
| using DisallowHeapAccess = |
| CombinationAssertScope<DisallowCodeDependencyChange, |
| DisallowHandleDereference, DisallowHandleAllocation, |
| DisallowHeapAllocation>; |
| |
| // Scope to introduce an exception to DisallowHeapAccess. |
| using AllowHeapAccess = |
| CombinationAssertScope<AllowCodeDependencyChange, AllowHandleDereference, |
| AllowHandleAllocation, AllowHeapAllocation>; |
| |
| class DisallowHeapAccessIf { |
| public: |
| explicit DisallowHeapAccessIf(bool condition) { |
| if (condition) maybe_disallow_.emplace(); |
| } |
| |
| private: |
| base::Optional<DisallowHeapAccess> maybe_disallow_; |
| }; |
| |
| // Like MutexGuard but also asserts that no garbage collection happens while |
| // we're holding the mutex. |
| class V8_NODISCARD NoGarbageCollectionMutexGuard { |
| public: |
| explicit NoGarbageCollectionMutexGuard(base::Mutex* mutex) |
| : guard_(mutex), mutex_(mutex), no_gc_(new DisallowGarbageCollection()) {} |
| |
| void Unlock() { |
| mutex_->Unlock(); |
| no_gc_.reset(); |
| } |
| void Lock() { |
| mutex_->Lock(); |
| no_gc_.reset(new DisallowGarbageCollection()); |
| } |
| |
| private: |
| base::MutexGuard guard_; |
| base::Mutex* mutex_; |
| std::unique_ptr<DisallowGarbageCollection> no_gc_; |
| }; |
| |
| // Explicit instantiation declarations. |
| extern template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, false>; |
| extern template class PerThreadAssertScope<HEAP_ALLOCATION_ASSERT, true>; |
| extern template class PerThreadAssertScope<SAFEPOINTS_ASSERT, false>; |
| extern template class PerThreadAssertScope<SAFEPOINTS_ASSERT, true>; |
| extern template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, false>; |
| extern template class PerThreadAssertScope<HANDLE_ALLOCATION_ASSERT, true>; |
| extern template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, false>; |
| extern template class PerThreadAssertScope<HANDLE_DEREFERENCE_ASSERT, true>; |
| extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, |
| false>; |
| extern template class PerThreadAssertScope<CODE_DEPENDENCY_CHANGE_ASSERT, true>; |
| extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, false>; |
| extern template class PerThreadAssertScope<CODE_ALLOCATION_ASSERT, true>; |
| extern template class PerThreadAssertScope<GC_MOLE, false>; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_COMMON_ASSERT_SCOPE_H_ |