| // Copyright 2018 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_TORQUE_CONTEXTUAL_H_ |
| #define V8_TORQUE_CONTEXTUAL_H_ |
| |
| #include <type_traits> |
| |
| #include "src/base/macros.h" |
| #include "src/base/platform/platform.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace torque { |
| |
| template <class Variable> |
| V8_EXPORT_PRIVATE typename Variable::Scope*& ContextualVariableTop(); |
| |
| // {ContextualVariable} provides a clean alternative to a global variable. |
| // The contextual variable is mutable, and supports managing the value of |
| // a variable in a well-nested fashion via the {Scope} class. |
| // {ContextualVariable} only stores a pointer to the current value, which |
| // is stored in a {Scope} object. The most recent value can be retrieved |
| // via Get(). Because only {Scope} has actual storage, there must be at |
| // least one active {Scope} (i.e. in a surrounding C++ scope), whenever Get() |
| // is called. |
| // Note that contextual variables must only be used from the same thread, |
| // i.e. {Scope} and Get() have to be in the same thread. |
| template <class Derived, class VarType> |
| class ContextualVariable { |
| public: |
| // A {Scope} contains a new object of type {VarType} and gives |
| // ContextualVariable::Get() access to it. Upon destruction, the contextual |
| // variable is restored to the state before the {Scope} was created. Scopes |
| // have to follow a stack discipline: A {Scope} has to be destructed before |
| // any older scope is destructed. |
| class Scope { |
| public: |
| template <class... Args> |
| explicit Scope(Args&&... args) |
| : value_(std::forward<Args>(args)...), previous_(Top()) { |
| Top() = this; |
| } |
| ~Scope() { |
| // Ensure stack discipline. |
| DCHECK_EQ(this, Top()); |
| Top() = previous_; |
| } |
| |
| VarType& Value() { return value_; } |
| |
| private: |
| VarType value_; |
| Scope* previous_; |
| |
| static_assert(std::is_base_of<ContextualVariable, Derived>::value, |
| "Curiously Recurring Template Pattern"); |
| |
| DISALLOW_NEW_AND_DELETE() |
| DISALLOW_COPY_AND_ASSIGN(Scope); |
| }; |
| |
| // Access the most recent active {Scope}. There has to be an active {Scope} |
| // for this contextual variable. |
| static VarType& Get() { |
| DCHECK_NOT_NULL(Top()); |
| return Top()->Value(); |
| } |
| |
| private: |
| template <class T> |
| friend V8_EXPORT_PRIVATE typename T::Scope*& ContextualVariableTop(); |
| static Scope*& Top() { return ContextualVariableTop<Derived>(); } |
| |
| static bool HasScope() { return Top() != nullptr; } |
| friend class MessageBuilder; |
| }; |
| |
| // Usage: DECLARE_CONTEXTUAL_VARIABLE(VarName, VarType) |
| #define DECLARE_CONTEXTUAL_VARIABLE(VarName, ...) \ |
| struct VarName \ |
| : v8::internal::torque::ContextualVariable<VarName, __VA_ARGS__> {} |
| |
| #define DEFINE_CONTEXTUAL_VARIABLE(VarName) \ |
| template <> \ |
| V8_EXPORT_PRIVATE VarName::Scope*& ContextualVariableTop<VarName>() { \ |
| static thread_local VarName::Scope* top = nullptr; \ |
| return top; \ |
| } |
| |
| // By inheriting from {ContextualClass} a class can become a contextual variable |
| // of itself, which is very similar to a singleton. |
| template <class T> |
| using ContextualClass = ContextualVariable<T, T>; |
| |
| } // namespace torque |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_TORQUE_CONTEXTUAL_H_ |