/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef frontend_AbstractScopePtr_h #define frontend_AbstractScopePtr_h #include "mozilla/Maybe.h" #include "mozilla/Variant.h" #include "frontend/TypedIndex.h" #include "gc/Barrier.h" #include "gc/Rooting.h" #include "gc/Tracer.h" #include "vm/Scope.h" #include "vm/ScopeKind.h" // For ScopeKind namespace js { class Scope; class GlobalScope; class EvalScope; struct MemberInitializers; class GCMarker; namespace frontend { struct CompilationState; struct CompilationGCOutput; class ScopeStencil; } // namespace frontend using ScopeIndex = frontend::TypedIndex; using HeapPtrScope = HeapPtr; // An interface class to support Scope queries in the frontend without requiring // a GC Allocated scope to necessarily exist. // // This abstracts Scope* and a ScopeStencil type used within the frontend before // the Scope is allocated. // // Because a AbstractScopePtr may hold onto a Scope, it must be rooted if a GC // may occur to ensure that the scope is traced. class AbstractScopePtr { public: // Used to hold index and the compilationState together to avoid having a // potentially nullable compilationState. struct Deferred { ScopeIndex index; frontend::CompilationState& compilationState; }; // To make writing code and managing invariants easier, we require that // any nullptr scopes be stored on the HeapPtrScope arm of the variant. using ScopeType = mozilla::Variant; private: ScopeType scope_ = ScopeType(HeapPtrScope()); Scope* scope() const { return scope_.as(); } public: friend class js::Scope; AbstractScopePtr() = default; explicit AbstractScopePtr(Scope* scope) : scope_(HeapPtrScope(scope)) {} AbstractScopePtr(frontend::CompilationState& compilationState, ScopeIndex scope) : scope_(Deferred{scope, compilationState}) {} bool isNullptr() const { if (isScopeStencil()) { return false; } return scope_.as() == nullptr; } // Return true if this AbstractScopePtr represents a Scope, either existant // or to be reified. This indicates that queries can be executed on this // scope data. Returning false is the equivalent to a nullptr, and usually // indicates the end of the scope chain. explicit operator bool() const { return !isNullptr(); } bool isScopeStencil() const { return scope_.is(); } // Note: this handle is rooted in the CompilationState. frontend::ScopeStencil& scopeData() const; frontend::CompilationState& compilationState() const; // This allows us to check whether or not this provider wraps // or otherwise would reify to a particular scope type. template bool is() const { static_assert(std::is_base_of_v, "Trying to ask about non-Scope type"); if (isNullptr()) { return false; } return kind() == T::classScopeKind_; } ScopeKind kind() const; AbstractScopePtr enclosing() const; bool hasEnvironment() const; // Valid iff is bool isArrow() const; bool hasOnChain(ScopeKind kind) const { for (AbstractScopePtr it = *this; it; it = it.enclosing()) { if (it.kind() == kind) { return true; } } return false; } void trace(JSTracer* trc); }; // Specializations of AbstractScopePtr::is template <> inline bool AbstractScopePtr::is() const { return !isNullptr() && (kind() == ScopeKind::Global || kind() == ScopeKind::NonSyntactic); } template <> inline bool AbstractScopePtr::is() const { return !isNullptr() && (kind() == ScopeKind::Eval || kind() == ScopeKind::StrictEval); } } // namespace js namespace JS { template <> struct GCPolicy : JS::IgnoreGCPolicy {}; } // namespace JS #endif // frontend_AbstractScopePtr_h