/* -*- 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 vm_Scope_h #define vm_Scope_h #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF #include "mozilla/Attributes.h" // MOZ_IMPLICIT, MOZ_INIT_OUTSIDE_CTOR, MOZ_STACK_CLASS #include "mozilla/Casting.h" // mozilla::AssertedCast #include "mozilla/Maybe.h" // mozilla::Maybe #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf #include "mozilla/Span.h" // mozilla::Span #include <algorithm> // std::fill_n #include <stddef.h> // size_t #include <stdint.h> // uint8_t, uint16_t, uint32_t, uintptr_t #include <type_traits> // std::is_same_v, std::is_base_of_v #include "builtin/ModuleObject.h" // ModuleObject, Handle<ModuleObject*> #include "frontend/ParserAtom.h" // frontend::TaggedParserAtomIndex #include "gc/Barrier.h" // HeapPtr #include "gc/Cell.h" // TenuredCellWithNonGCPointer #include "js/GCPolicyAPI.h" // GCPolicy, IgnoreGCPolicy #include "js/HeapAPI.h" // CellFlagBitsReservedForGC #include "js/RootingAPI.h" // Handle, MutableHandle #include "js/TraceKind.h" // JS::TraceKind #include "js/TypeDecls.h" // HandleFunction #include "js/UbiNode.h" // ubi::* #include "js/UniquePtr.h" // UniquePtr #include "util/Poison.h" // AlwaysPoison, JS_SCOPE_DATA_TRAILING_NAMES_PATTERN, MemCheckKind #include "vm/JSFunction.h" // JSFunction #include "vm/ScopeKind.h" // ScopeKind #include "vm/Shape.h" // Shape #include "wasm/WasmJS.h" // WasmInstanceObject class JSAtom; class JSScript; class JSTracer; struct JSContext; namespace js { class GenericPrinter; namespace frontend { class ScopeStencil; struct ScopeStencilRef; class RuntimeScopeBindingCache; } // namespace frontend template <typename NameT> class AbstractBaseScopeData; template <typename NameT> class BaseAbstractBindingIter; template <typename NameT> class AbstractBindingIter; using BindingIter = AbstractBindingIter<JSAtom>; class AbstractScopePtr; static inline bool ScopeKindIsCatch(ScopeKind kind) { return kind == ScopeKind::SimpleCatch || kind == ScopeKind::Catch; } static inline bool ScopeKindIsInBody(ScopeKind kind) { return kind == ScopeKind::Lexical || kind == ScopeKind::SimpleCatch || kind == ScopeKind::Catch || kind == ScopeKind::With || kind == ScopeKind::FunctionLexical || kind == ScopeKind::FunctionBodyVar || kind == ScopeKind::ClassBody; } const char* BindingKindString(BindingKind kind); const char* ScopeKindString(ScopeKind kind); template <typename NameT> class AbstractBindingName; template <> class AbstractBindingName<JSAtom> { public: using NameT = JSAtom; using NamePointerT = NameT*; private: // A JSAtom* with its low bit used as a tag for the: // * whether it is closed over (i.e., exists in the environment shape) // * whether it is a top-level function binding in global or eval scope, // instead of var binding (both are in the same range in Scope data) uintptr_t bits_; static constexpr uintptr_t ClosedOverFlag = 0x1; // TODO: We should reuse this bit for let vs class distinction to // show the better redeclaration error message (bug 1428672). static constexpr uintptr_t TopLevelFunctionFlag = 0x2; static constexpr uintptr_t FlagMask = 0x3; public: AbstractBindingName() : bits_(0) {} AbstractBindingName(NameT* name, bool closedOver, bool isTopLevelFunction = false) : bits_(uintptr_t(name) | (closedOver ? ClosedOverFlag : 0x0) | (isTopLevelFunction ? TopLevelFunctionFlag : 0x0)) {} NamePointerT name() const { return reinterpret_cast<NameT*>(bits_ & ~FlagMask); } bool closedOver() const { return bits_ & ClosedOverFlag; } private: friend class BaseAbstractBindingIter<NameT>; // This method should be called only for binding names in `vars` range in // BindingIter. bool isTopLevelFunction() const { return bits_ & TopLevelFunctionFlag; } public: void trace(JSTracer* trc) { if (JSAtom* atom = name()) { TraceManuallyBarrieredEdge(trc, &atom, "binding name"); } } }; template <> class AbstractBindingName<frontend::TaggedParserAtomIndex> { uint32_t bits_; using TaggedParserAtomIndex = frontend::TaggedParserAtomIndex; public: using NameT = TaggedParserAtomIndex; using NamePointerT = NameT; private: static constexpr size_t TaggedIndexBit = TaggedParserAtomIndex::IndexBit + 2; static constexpr size_t FlagShift = TaggedIndexBit; static constexpr size_t FlagBit = 2; static constexpr uint32_t FlagMask = BitMask(FlagBit) << FlagShift; static constexpr uint32_t ClosedOverFlag = 1 << FlagShift; static constexpr uint32_t TopLevelFunctionFlag = 2 << FlagShift; public: AbstractBindingName() : bits_(TaggedParserAtomIndex::NullTag) { // TaggedParserAtomIndex's tags shouldn't overlap with flags. static_assert((TaggedParserAtomIndex::NullTag & FlagMask) == 0); static_assert((TaggedParserAtomIndex::ParserAtomIndexTag & FlagMask) == 0); static_assert((TaggedParserAtomIndex::WellKnownTag & FlagMask) == 0); } AbstractBindingName(TaggedParserAtomIndex name, bool closedOver, bool isTopLevelFunction = false) : bits_(name.rawData() | (closedOver ? ClosedOverFlag : 0x0) | (isTopLevelFunction ? TopLevelFunctionFlag : 0x0)) {} public: NamePointerT name() const { return TaggedParserAtomIndex::fromRaw(bits_ & ~FlagMask); } bool closedOver() const { return bits_ & ClosedOverFlag; } AbstractBindingName<JSAtom> copyWithNewAtom(JSAtom* newName) const { return AbstractBindingName<JSAtom>(newName, closedOver(), isTopLevelFunction()); } void updateNameAfterStencilMerge(TaggedParserAtomIndex name) { bits_ = (bits_ & FlagMask) | name.rawData(); } private: friend class BaseAbstractBindingIter<TaggedParserAtomIndex>; friend class frontend::ScopeStencil; // This method should be called only for binding names in `vars` range in // BindingIter. bool isTopLevelFunction() const { return bits_ & TopLevelFunctionFlag; } }; using BindingName = AbstractBindingName<JSAtom>; static inline void TraceBindingNames(JSTracer* trc, BindingName* names, uint32_t length) { for (uint32_t i = 0; i < length; i++) { JSAtom* name = names[i].name(); MOZ_ASSERT(name); TraceManuallyBarrieredEdge(trc, &name, "scope name"); } }; static inline void TraceNullableBindingNames(JSTracer* trc, BindingName* names, uint32_t length) { for (uint32_t i = 0; i < length; i++) { if (JSAtom* name = names[i].name()) { TraceManuallyBarrieredEdge(trc, &name, "scope name"); } } }; const size_t ScopeDataAlignBytes = size_t(1) << gc::CellFlagBitsReservedForGC; /** * Base class for scope {Runtime,Parser}Data classes to inherit from. * * `js::Scope` stores a pointer to RuntimeData classes in their first word, so * they must be suitably aligned to allow storing GC flags in the low bits. */ template <typename NameT> class AbstractBaseScopeData { public: using NameType = NameT; // The length of names after specialized ScopeData subclasses. uint32_t length = 0; }; template <typename ScopeDataT> static inline void AssertDerivedScopeData() { static_assert( !std::is_same_v<ScopeDataT, AbstractBaseScopeData<typename ScopeDataT::NameType>>, "ScopeDataT shouldn't be AbstractBaseScopeData"); static_assert( std::is_base_of_v<AbstractBaseScopeData<typename ScopeDataT::NameType>, ScopeDataT>, "ScopeDataT should be subclass of AbstractBaseScopeData"); } template <typename ScopeDataT> static inline size_t GetOffsetOfScopeDataTrailingNames() { AssertDerivedScopeData<ScopeDataT>(); return sizeof(ScopeDataT); } template <typename ScopeDataT> static inline AbstractBindingName<typename ScopeDataT::NameType>* GetScopeDataTrailingNamesPointer(ScopeDataT* data) { AssertDerivedScopeData<ScopeDataT>(); return reinterpret_cast<AbstractBindingName<typename ScopeDataT::NameType>*>( data + 1); } template <typename ScopeDataT> static inline const AbstractBindingName<typename ScopeDataT::NameType>* GetScopeDataTrailingNamesPointer(const ScopeDataT* data) { AssertDerivedScopeData<ScopeDataT>(); return reinterpret_cast< const AbstractBindingName<typename ScopeDataT::NameType>*>(data + 1); } template <typename ScopeDataT> static inline mozilla::Span<AbstractBindingName<typename ScopeDataT::NameType>> GetScopeDataTrailingNames(ScopeDataT* data) { return mozilla::Span(GetScopeDataTrailingNamesPointer(data), data->length); } template <typename ScopeDataT> static inline mozilla::Span< const AbstractBindingName<typename ScopeDataT::NameType>> GetScopeDataTrailingNames(const ScopeDataT* data) { return mozilla::Span(GetScopeDataTrailingNamesPointer(data), data->length); } using BaseScopeData = AbstractBaseScopeData<JSAtom>; inline void PoisonNames(AbstractBindingName<JSAtom>* data, uint32_t length) { AlwaysPoison(data, JS_SCOPE_DATA_TRAILING_NAMES_PATTERN, sizeof(AbstractBindingName<JSAtom>) * length, MemCheckKind::MakeUndefined); } // frontend::TaggedParserAtomIndex doesn't require poison value. // Fill with null value instead. inline void PoisonNames( AbstractBindingName<frontend::TaggedParserAtomIndex>* data, uint32_t length) { std::fill_n(data, length, AbstractBindingName<frontend::TaggedParserAtomIndex>()); } template <typename ScopeDataT> static inline void PoisonNames(ScopeDataT* data, uint32_t length) { if (length) { PoisonNames(GetScopeDataTrailingNamesPointer(data), length); } } // // Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>. // template <typename Wrapper> class WrappedPtrOperations<Scope*, Wrapper> { public: template <class U> JS::Handle<U*> as() const { const Wrapper& self = *static_cast<const Wrapper*>(this); MOZ_ASSERT_IF(self, self->template is<U>()); return Handle<U*>::fromMarkedLocation( reinterpret_cast<U* const*>(self.address())); } }; // // The base class of all Scopes. // class Scope : public gc::TenuredCellWithNonGCPointer<BaseScopeData> { friend class GCMarker; friend class frontend::ScopeStencil; friend class js::AbstractBindingIter<JSAtom>; friend class js::frontend::RuntimeScopeBindingCache; friend class gc::CellAllocator; protected: // The raw data pointer, stored in the cell header. BaseScopeData* rawData() { return headerPtr(); } const BaseScopeData* rawData() const { return headerPtr(); } // The kind determines data_. const ScopeKind kind_; // If there are any aliased bindings, the shape for the // EnvironmentObject. Otherwise nullptr. const HeapPtr<SharedShape*> environmentShape_; // The enclosing scope or nullptr. HeapPtr<Scope*> enclosingScope_; Scope(ScopeKind kind, Scope* enclosing, SharedShape* environmentShape) : TenuredCellWithNonGCPointer(nullptr), kind_(kind), environmentShape_(environmentShape), enclosingScope_(enclosing) {} static Scope* create(JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing, Handle<SharedShape*> envShape); template <typename ConcreteScope> void initData( MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> data); template <typename F> void applyScopeDataTyped(F&& f); static void updateEnvShapeIfRequired(mozilla::Maybe<uint32_t>* envShape, bool needsEnvironment); public: template <typename ConcreteScope> static ConcreteScope* create( JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing, Handle<SharedShape*> envShape, MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> data); static const JS::TraceKind TraceKind = JS::TraceKind::Scope; template <typename T> bool is() const { return kind_ == T::classScopeKind_; } template <typename T> T& as() { MOZ_ASSERT(this->is<T>()); return *static_cast<T*>(this); } template <typename T> const T& as() const { MOZ_ASSERT(this->is<T>()); return *static_cast<const T*>(this); } ScopeKind kind() const { return kind_; } bool isNamedLambda() const { return kind() == ScopeKind::NamedLambda || kind() == ScopeKind::StrictNamedLambda; } SharedShape* environmentShape() const { return environmentShape_; } Scope* enclosing() const { return enclosingScope_; } static bool hasEnvironment(ScopeKind kind, bool hasEnvironmentShape = false) { switch (kind) { case ScopeKind::With: case ScopeKind::Global: case ScopeKind::NonSyntactic: return true; default: // If there's a shape, an environment must be created for this scope. return hasEnvironmentShape; } } bool hasEnvironment() const { return hasEnvironment(kind_, !!environmentShape()); } uint32_t firstFrameSlot() const; uint32_t chainLength() const; uint32_t environmentChainLength() const; template <typename T> bool hasOnChain() const { for (const Scope* it = this; it; it = it->enclosing()) { if (it->is<T>()) { return true; } } return false; } bool hasOnChain(ScopeKind kind) const { for (const Scope* it = this; it; it = it->enclosing()) { if (it->kind() == kind) { return true; } } return false; } void traceChildren(JSTracer* trc); void finalize(JS::GCContext* gcx); size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; void dump(); #if defined(DEBUG) || defined(JS_JITSPEW) static bool dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope, GenericPrinter& out, const char* indent); #endif /* defined(DEBUG) || defined(JS_JITSPEW) */ }; template <class DataT> inline size_t SizeOfScopeData(uint32_t length) { using BindingT = AbstractBindingName<typename DataT::NameType>; return GetOffsetOfScopeDataTrailingNames<DataT>() + length * sizeof(BindingT); } // // A useful typedef for selecting between a gc-aware wrappers // around pointers to BaseScopeData-derived types, and around raw // pointer wrappers around BaseParserScopeData-derived types. // template <typename ScopeT, typename AtomT> using AbstractScopeData = typename ScopeT::template AbstractData<AtomT>; // Binding names are stored from `this+1`. // Make sure the class aligns the binding name size. template <typename SlotInfo> struct alignas(alignof(AbstractBindingName<frontend::TaggedParserAtomIndex>)) ParserScopeData : public AbstractBaseScopeData<frontend::TaggedParserAtomIndex> { SlotInfo slotInfo; explicit ParserScopeData(size_t length) { PoisonNames(this, length); } ParserScopeData() = delete; }; // RuntimeScopeData has 2 requirements: // * It aligns with `BindingName`, that is stored after `this+1` // * It aligns with ScopeDataAlignBytes, in order to put it in the first // word of `js::Scope` static_assert(alignof(BindingName) <= ScopeDataAlignBytes); template <typename SlotInfo> struct alignas(ScopeDataAlignBytes) RuntimeScopeData : public AbstractBaseScopeData<JSAtom> { SlotInfo slotInfo; explicit RuntimeScopeData(size_t length) { PoisonNames(this, length); } RuntimeScopeData() = delete; void trace(JSTracer* trc); }; // // A lexical scope that holds let and const bindings. There are 4 kinds of // LexicalScopes. // // Lexical // A plain lexical scope. // // SimpleCatch // Holds the single catch parameter of a catch block. // // Catch // Holds the catch parameters (and only the catch parameters) of a catch // block. // // NamedLambda // StrictNamedLambda // Holds the single name of the callee for a named lambda expression. // // All kinds of LexicalScopes correspond to LexicalEnvironmentObjects on the // environment chain. // class LexicalScope : public Scope { friend class Scope; friend class AbstractBindingIter<JSAtom>; friend class GCMarker; friend class frontend::ScopeStencil; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Bindings are sorted by kind in both frames and environments. // // lets - [0, constStart) // consts - [constStart, length) uint32_t constStart = 0; }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; private: static void prepareForScopeCreation(ScopeKind kind, uint32_t firstFrameSlot, LexicalScope::ParserData* data, mozilla::Maybe<uint32_t>* envShape); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: static uint32_t nextFrameSlot(Scope* scope); uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } // Returns an empty shape for extensible global and non-syntactic lexical // scopes. static SharedShape* getEmptyExtensibleEnvironmentShape(JSContext* cx); }; template <> inline bool Scope::is<LexicalScope>() const { return kind_ == ScopeKind::Lexical || kind_ == ScopeKind::SimpleCatch || kind_ == ScopeKind::Catch || kind_ == ScopeKind::NamedLambda || kind_ == ScopeKind::StrictNamedLambda || kind_ == ScopeKind::FunctionLexical; } // The body scope of a JS class, containing only synthetic bindings for private // class members. (The binding for the class name, `C` in the example below, is // in another scope, a `LexicalScope`, that encloses the `ClassBodyScope`.) // Example: // // class C { // #f = 0; // #m() { // return this.#f++; // } // } // // This class has a ClassBodyScope with four synthetic bindings: // - `#f` (private name) // - `#m` (private name) // - `#m.method` (function object) // - `.privateBrand` (the class's private brand) class ClassBodyScope : public Scope { friend class Scope; friend class AbstractBindingIter<JSAtom>; friend class GCMarker; friend class frontend::ScopeStencil; friend class AbstractScopePtr; static const ScopeKind classScopeKind_ = ScopeKind::ClassBody; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Bindings are sorted by kind in both frames and environments. // // synthetic - [0, privateMethodStart) // privateMethod - [privateMethodStart, length) uint32_t privateMethodStart = 0; }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; private: static void prepareForScopeCreation(ScopeKind kind, uint32_t firstFrameSlot, ClassBodyScope::ParserData* data, mozilla::Maybe<uint32_t>* envShape); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: static uint32_t nextFrameSlot(Scope* scope); uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } // Returns an empty shape for extensible global and non-syntactic lexical // scopes. static SharedShape* getEmptyExtensibleEnvironmentShape(JSContext* cx); }; // // Scope corresponding to a function. Holds formal parameter names, special // internal names (see FunctionScope::isSpecialName), and, if the function // parameters contain no expressions that might possibly be evaluated, the // function's var bindings. For example, in these functions, the FunctionScope // will store a/b/c bindings but not d/e/f bindings: // // function f1(a, b) { // var c; // let e; // const f = 3; // } // function f2([a], b = 4, ...c) { // var d, e, f; // stored in VarScope // } // // Corresponds to CallObject on environment chain. // class FunctionScope : public Scope { friend class GCMarker; friend class AbstractBindingIter<JSAtom>; friend class PositionalFormalParameterIter; friend class Scope; friend class AbstractScopePtr; static const ScopeKind classScopeKind_ = ScopeKind::Function; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Flag bits. // This uses uint32_t in order to make this struct packed. uint32_t flags = 0; // If parameter expressions are present, parameters act like lexical // bindings. static constexpr uint32_t HasParameterExprsFlag = 1; // Bindings are sorted by kind in both frames and environments. // // Positional formal parameter names are those that are not // destructured. They may be referred to by argument slots if // !script()->hasParameterExprs(). // // An argument slot that needs to be skipped due to being destructured // or having defaults will have a nullptr name in the name array to // advance the argument slot. // // Rest parameter binding is also included in positional formals. // This also becomes nullptr if destructuring. // // The number of positional formals is equal to function.length if // there's no rest, function.length+1 otherwise. // // Destructuring parameters and destructuring rest are included in // "other formals" below. // // "vars" contains the following: // * function's top level vars if !script()->hasParameterExprs() // * special internal names (arguments, .this, .generator) if // they're used. // // positional formals - [0, nonPositionalFormalStart) // other formals - [nonPositionalParamStart, varStart) // vars - [varStart, length) uint16_t nonPositionalFormalStart = 0; uint16_t varStart = 0; bool hasParameterExprs() const { return flags & HasParameterExprsFlag; } void setHasParameterExprs() { flags |= HasParameterExprsFlag; } }; struct alignas(ScopeDataAlignBytes) RuntimeData : public AbstractBaseScopeData<JSAtom> { SlotInfo slotInfo; // The canonical function of the scope, as during a scope walk we // often query properties of the JSFunction (e.g., is the function an // arrow). HeapPtr<JSFunction*> canonicalFunction = {}; explicit RuntimeData(size_t length) { PoisonNames(this, length); } RuntimeData() = delete; void trace(JSTracer* trc); }; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; static void prepareForScopeCreation(FunctionScope::ParserData* data, bool hasParameterExprs, bool needsEnvironment, mozilla::Maybe<uint32_t>* envShape); private: RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } JSFunction* canonicalFunction() const { return data().canonicalFunction; } void initCanonicalFunction(JSFunction* fun) { data().canonicalFunction.init(fun); } JSScript* script() const; bool hasParameterExprs() const { return data().slotInfo.hasParameterExprs(); } uint32_t numPositionalFormalParameters() const { return data().slotInfo.nonPositionalFormalStart; } static bool isSpecialName(frontend::TaggedParserAtomIndex name); }; // // Scope holding only vars. There is a single kind of VarScopes. // // FunctionBodyVar // Corresponds to the extra var scope present in functions with parameter // expressions. See examples in comment above FunctionScope. // // Corresponds to VarEnvironmentObject on environment chain. // class VarScope : public Scope { friend class GCMarker; friend class AbstractBindingIter<JSAtom>; friend class Scope; friend class frontend::ScopeStencil; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // All bindings are vars. // // vars - [0, length) }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; private: static void prepareForScopeCreation(ScopeKind kind, VarScope::ParserData* data, uint32_t firstFrameSlot, bool needsEnvironment, mozilla::Maybe<uint32_t>* envShape); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } }; template <> inline bool Scope::is<VarScope>() const { return kind_ == ScopeKind::FunctionBodyVar; } // // Scope corresponding to both the global object scope and the global lexical // scope. // // Both are extensible and are singletons across <script> tags, so these // scopes are a fragment of the names in global scope. In other words, two // global scripts may have two different GlobalScopes despite having the same // GlobalObject. // // There are 2 kinds of GlobalScopes. // // Global // Corresponds to a GlobalObject and its GlobalLexicalEnvironmentObject on // the environment chain. // // NonSyntactic // Corresponds to a non-GlobalObject created by the embedding on the // environment chain. This distinction is important for optimizations. // class GlobalScope : public Scope { friend class Scope; friend class AbstractBindingIter<JSAtom>; friend class GCMarker; public: struct SlotInfo { // Bindings are sorted by kind. // `vars` includes top-level functions which is distinguished by a bit // on the BindingName. // // vars - [0, letStart) // lets - [letStart, constStart) // consts - [constStart, length) uint32_t letStart = 0; uint32_t constStart = 0; }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; static GlobalScope* createEmpty(JSContext* cx, ScopeKind kind); private: static GlobalScope* createWithData( JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<RuntimeData>> data); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: bool isSyntactic() const { return kind() != ScopeKind::NonSyntactic; } bool hasBindings() const { return data().length > 0; } }; template <> inline bool Scope::is<GlobalScope>() const { return kind_ == ScopeKind::Global || kind_ == ScopeKind::NonSyntactic; } // // Scope of a 'with' statement. Has no bindings. // // Corresponds to a WithEnvironmentObject on the environment chain. class WithScope : public Scope { friend class Scope; friend class AbstractScopePtr; static const ScopeKind classScopeKind_ = ScopeKind::With; public: static WithScope* create(JSContext* cx, Handle<Scope*> enclosing); }; // // Scope of an eval. Holds var bindings. There are 2 kinds of EvalScopes. // // StrictEval // A strict eval. Corresponds to a VarEnvironmentObject, where its var // bindings lives. // // Eval // A sloppy eval. This is an empty scope, used only in the frontend, to // detect redeclaration errors. It has no Environment. Any `var`s declared // in the eval code are bound on the nearest enclosing var environment. // class EvalScope : public Scope { friend class Scope; friend class AbstractBindingIter<JSAtom>; friend class GCMarker; friend class frontend::ScopeStencil; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // All bindings in an eval script are 'var' bindings. The implicit // lexical scope around the eval is present regardless of strictness // and is its own LexicalScope. // `vars` includes top-level functions which is distinguished by a bit // on the BindingName. // // vars - [0, length) }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; private: static void prepareForScopeCreation(ScopeKind scopeKind, EvalScope::ParserData* data, mozilla::Maybe<uint32_t>* envShape); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: // Starting a scope, the nearest var scope that a direct eval can // introduce vars on. static Scope* nearestVarScopeForDirectEval(Scope* scope); uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } bool strict() const { return kind() == ScopeKind::StrictEval; } bool hasBindings() const { return data().length > 0; } bool isNonGlobal() const { if (strict()) { return true; } return !nearestVarScopeForDirectEval(enclosing())->is<GlobalScope>(); } }; template <> inline bool Scope::is<EvalScope>() const { return kind_ == ScopeKind::Eval || kind_ == ScopeKind::StrictEval; } // // Scope corresponding to the toplevel script in an ES module. // // Like GlobalScopes, these scopes contain both vars and lexical bindings, as // the treating of imports and exports requires putting them in one scope. // // Corresponds to a ModuleEnvironmentObject on the environment chain. // class ModuleScope : public Scope { friend class GCMarker; friend class AbstractBindingIter<JSAtom>; friend class Scope; friend class AbstractScopePtr; friend class frontend::ScopeStencil; static const ScopeKind classScopeKind_ = ScopeKind::Module; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Bindings are sorted by kind. // // imports - [0, varStart) // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) uint32_t varStart = 0; uint32_t letStart = 0; uint32_t constStart = 0; }; struct alignas(ScopeDataAlignBytes) RuntimeData : public AbstractBaseScopeData<JSAtom> { SlotInfo slotInfo; // The module of the scope. HeapPtr<ModuleObject*> module = {}; explicit RuntimeData(size_t length); RuntimeData() = delete; void trace(JSTracer* trc); }; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; private: static void prepareForScopeCreation(ModuleScope::ParserData* data, mozilla::Maybe<uint32_t>* envShape); RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } ModuleObject* module() const { return data().module; } void initModule(ModuleObject* mod) { return data().module.init(mod); } // Off-thread compilation needs to calculate environmentChainLength for // an emptyGlobalScope where the global may not be available. static const size_t EnclosingEnvironmentChainLength = 1; }; class WasmInstanceScope : public Scope { friend class AbstractBindingIter<JSAtom>; friend class Scope; friend class GCMarker; friend class AbstractScopePtr; static const ScopeKind classScopeKind_ = ScopeKind::WasmInstance; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Bindings list the WASM memories and globals. // // memories - [0, globalsStart) // globals - [globalsStart, length) uint32_t globalsStart = 0; }; struct alignas(ScopeDataAlignBytes) RuntimeData : public AbstractBaseScopeData<JSAtom> { SlotInfo slotInfo; // The wasm instance of the scope. HeapPtr<WasmInstanceObject*> instance = {}; explicit RuntimeData(size_t length); RuntimeData() = delete; void trace(JSTracer* trc); }; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; static WasmInstanceScope* create(JSContext* cx, WasmInstanceObject* instance); private: RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } public: WasmInstanceObject* instance() const { return data().instance; } uint32_t memoriesStart() const { return 0; } uint32_t globalsStart() const { return data().slotInfo.globalsStart; } uint32_t namesCount() const { return data().length; } }; // Scope corresponding to the wasm function. A WasmFunctionScope is used by // Debugger only, and not for wasm execution. // class WasmFunctionScope : public Scope { friend class AbstractBindingIter<JSAtom>; friend class Scope; friend class GCMarker; friend class AbstractScopePtr; static const ScopeKind classScopeKind_ = ScopeKind::WasmFunction; public: struct SlotInfo { // Frame slots [0, nextFrameSlot) are live when this is the innermost // scope. uint32_t nextFrameSlot = 0; // Bindings are the local variable names. // // vars - [0, length) }; using RuntimeData = RuntimeScopeData<SlotInfo>; using ParserData = ParserScopeData<SlotInfo>; template <typename NameT> using AbstractData = typename std::conditional_t<std::is_same<NameT, JSAtom>::value, RuntimeData, ParserData>; static WasmFunctionScope* create(JSContext* cx, Handle<Scope*> enclosing, uint32_t funcIndex); private: RuntimeData& data() { return *static_cast<RuntimeData*>(rawData()); } const RuntimeData& data() const { return *static_cast<const RuntimeData*>(rawData()); } }; template <typename F> void Scope::applyScopeDataTyped(F&& f) { switch (kind()) { case ScopeKind::Function: { f(&as<FunctionScope>().data()); break; case ScopeKind::FunctionBodyVar: f(&as<VarScope>().data()); break; case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: case ScopeKind::FunctionLexical: f(&as<LexicalScope>().data()); break; case ScopeKind::ClassBody: f(&as<ClassBodyScope>().data()); break; case ScopeKind::With: // With scopes do not have data. break; case ScopeKind::Eval: case ScopeKind::StrictEval: f(&as<EvalScope>().data()); break; case ScopeKind::Global: case ScopeKind::NonSyntactic: f(&as<GlobalScope>().data()); break; case ScopeKind::Module: f(&as<ModuleScope>().data()); break; case ScopeKind::WasmInstance: f(&as<WasmInstanceScope>().data()); break; case ScopeKind::WasmFunction: f(&as<WasmFunctionScope>().data()); break; } } } // // An iterator for a Scope's bindings. This is the source of truth for frame // and environment object layout. // // It may be placed in GC containers; for example: // // for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { // use(bi); // SomeMayGCOperation(); // use(bi); // } // template <typename NameT> class BaseAbstractBindingIter { protected: // Bindings are sorted by kind. Because different Scopes have differently // laid out {Runtime,Parser}Data for packing, BindingIter must handle all // binding kinds. // // Kind ranges: // // imports - [0, positionalFormalStart) // positional formals - [positionalFormalStart, nonPositionalFormalStart) // other formals - [nonPositionalParamStart, varStart) // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, syntheticStart) // synthetic - [syntheticStart, privateMethodStart) // private methods = [privateMethodStart, length) // // Access method when not closed over: // // imports - name // positional formals - argument slot // other formals - frame slot // vars - frame slot // lets - frame slot // consts - frame slot // synthetic - frame slot // private methods - frame slot // // Access method when closed over: // // imports - name // positional formals - environment slot or name // other formals - environment slot or name // vars - environment slot or name // lets - environment slot or name // consts - environment slot or name // synthetic - environment slot or name // private methods - environment slot or name MOZ_INIT_OUTSIDE_CTOR uint32_t positionalFormalStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t nonPositionalFormalStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t varStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t letStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t constStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t syntheticStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t privateMethodStart_; MOZ_INIT_OUTSIDE_CTOR uint32_t length_; MOZ_INIT_OUTSIDE_CTOR uint32_t index_; enum Flags : uint8_t { CannotHaveSlots = 0, CanHaveArgumentSlots = 1 << 0, CanHaveFrameSlots = 1 << 1, CanHaveEnvironmentSlots = 1 << 2, // See comment in settle below. HasFormalParameterExprs = 1 << 3, IgnoreDestructuredFormalParameters = 1 << 4, // Truly I hate named lambdas. IsNamedLambda = 1 << 5 }; static const uint8_t CanHaveSlotsMask = 0x7; MOZ_INIT_OUTSIDE_CTOR uint8_t flags_; MOZ_INIT_OUTSIDE_CTOR uint16_t argumentSlot_; MOZ_INIT_OUTSIDE_CTOR uint32_t frameSlot_; MOZ_INIT_OUTSIDE_CTOR uint32_t environmentSlot_; MOZ_INIT_OUTSIDE_CTOR AbstractBindingName<NameT>* names_; void init(uint32_t positionalFormalStart, uint32_t nonPositionalFormalStart, uint32_t varStart, uint32_t letStart, uint32_t constStart, uint32_t syntheticStart, uint32_t privateMethodStart, uint8_t flags, uint32_t firstFrameSlot, uint32_t firstEnvironmentSlot, mozilla::Span<AbstractBindingName<NameT>> names) { positionalFormalStart_ = positionalFormalStart; nonPositionalFormalStart_ = nonPositionalFormalStart; varStart_ = varStart; letStart_ = letStart; constStart_ = constStart; syntheticStart_ = syntheticStart; privateMethodStart_ = privateMethodStart; length_ = names.size(); index_ = 0; flags_ = flags; argumentSlot_ = 0; frameSlot_ = firstFrameSlot; environmentSlot_ = firstEnvironmentSlot; names_ = names.data(); settle(); } void init(LexicalScope::AbstractData<NameT>& data, uint32_t firstFrameSlot, uint8_t flags); void init(ClassBodyScope::AbstractData<NameT>& data, uint32_t firstFrameSlot); void init(FunctionScope::AbstractData<NameT>& data, uint8_t flags); void init(VarScope::AbstractData<NameT>& data, uint32_t firstFrameSlot); void init(GlobalScope::AbstractData<NameT>& data); void init(EvalScope::AbstractData<NameT>& data, bool strict); void init(ModuleScope::AbstractData<NameT>& data); void init(WasmInstanceScope::AbstractData<NameT>& data); void init(WasmFunctionScope::AbstractData<NameT>& data); bool hasFormalParameterExprs() const { return flags_ & HasFormalParameterExprs; } bool ignoreDestructuredFormalParameters() const { return flags_ & IgnoreDestructuredFormalParameters; } bool isNamedLambda() const { return flags_ & IsNamedLambda; } void increment() { MOZ_ASSERT(!done()); if (flags_ & CanHaveSlotsMask) { if (canHaveArgumentSlots()) { if (index_ < nonPositionalFormalStart_) { MOZ_ASSERT(index_ >= positionalFormalStart_); argumentSlot_++; } } if (closedOver()) { // Imports must not be given known slots. They are // indirect bindings. MOZ_ASSERT(kind() != BindingKind::Import); MOZ_ASSERT(canHaveEnvironmentSlots()); environmentSlot_++; } else if (canHaveFrameSlots()) { // Usually positional formal parameters don't have frame // slots, except when there are parameter expressions, in // which case they act like lets. if (index_ >= nonPositionalFormalStart_ || (hasFormalParameterExprs() && name())) { frameSlot_++; } } } index_++; } void settle() { if (ignoreDestructuredFormalParameters()) { while (!done() && !name()) { increment(); } } } BaseAbstractBindingIter() = default; public: BaseAbstractBindingIter(LexicalScope::AbstractData<NameT>& data, uint32_t firstFrameSlot, bool isNamedLambda) { init(data, firstFrameSlot, isNamedLambda ? IsNamedLambda : 0); } BaseAbstractBindingIter(ClassBodyScope::AbstractData<NameT>& data, uint32_t firstFrameSlot) { init(data, firstFrameSlot); } BaseAbstractBindingIter(FunctionScope::AbstractData<NameT>& data, bool hasParameterExprs) { init(data, IgnoreDestructuredFormalParameters | (hasParameterExprs ? HasFormalParameterExprs : 0)); } BaseAbstractBindingIter(VarScope::AbstractData<NameT>& data, uint32_t firstFrameSlot) { init(data, firstFrameSlot); } explicit BaseAbstractBindingIter(GlobalScope::AbstractData<NameT>& data) { init(data); } explicit BaseAbstractBindingIter(ModuleScope::AbstractData<NameT>& data) { init(data); } explicit BaseAbstractBindingIter( WasmFunctionScope::AbstractData<NameT>& data) { init(data); } BaseAbstractBindingIter(EvalScope::AbstractData<NameT>& data, bool strict) { init(data, strict); } MOZ_IMPLICIT BaseAbstractBindingIter( const BaseAbstractBindingIter<NameT>& bi) = default; bool done() const { return index_ == length_; } explicit operator bool() const { return !done(); } void operator++(int) { increment(); settle(); } bool isLast() const { MOZ_ASSERT(!done()); return index_ + 1 == length_; } bool canHaveArgumentSlots() const { return flags_ & CanHaveArgumentSlots; } bool canHaveFrameSlots() const { return flags_ & CanHaveFrameSlots; } bool canHaveEnvironmentSlots() const { return flags_ & CanHaveEnvironmentSlots; } typename AbstractBindingName<NameT>::NamePointerT name() const { MOZ_ASSERT(!done()); return names_[index_].name(); } bool closedOver() const { MOZ_ASSERT(!done()); return names_[index_].closedOver(); } BindingLocation location() const { MOZ_ASSERT(!done()); if (!(flags_ & CanHaveSlotsMask)) { return BindingLocation::Global(); } if (index_ < positionalFormalStart_) { return BindingLocation::Import(); } if (closedOver()) { MOZ_ASSERT(canHaveEnvironmentSlots()); return BindingLocation::Environment(environmentSlot_); } if (index_ < nonPositionalFormalStart_ && canHaveArgumentSlots()) { return BindingLocation::Argument(argumentSlot_); } if (canHaveFrameSlots()) { return BindingLocation::Frame(frameSlot_); } MOZ_ASSERT(isNamedLambda()); return BindingLocation::NamedLambdaCallee(); } BindingKind kind() const { MOZ_ASSERT(!done()); if (index_ < positionalFormalStart_) { return BindingKind::Import; } if (index_ < varStart_) { // When the parameter list has expressions, the parameters act // like lexical bindings and have TDZ. if (hasFormalParameterExprs()) { return BindingKind::Let; } return BindingKind::FormalParameter; } if (index_ < letStart_) { return BindingKind::Var; } if (index_ < constStart_) { return BindingKind::Let; } if (index_ < syntheticStart_) { return isNamedLambda() ? BindingKind::NamedLambdaCallee : BindingKind::Const; } if (index_ < privateMethodStart_) { return BindingKind::Synthetic; } return BindingKind::PrivateMethod; } js::frontend::NameLocation nameLocation() const { using js::frontend::NameLocation; BindingKind bindKind = kind(); BindingLocation bl = location(); switch (bl.kind()) { case BindingLocation::Kind::Global: return NameLocation::Global(bindKind); case BindingLocation::Kind::Argument: return NameLocation::ArgumentSlot(bl.argumentSlot()); case BindingLocation::Kind::Frame: return NameLocation::FrameSlot(bindKind, bl.slot()); case BindingLocation::Kind::Environment: return NameLocation::EnvironmentCoordinate(bindKind, 0, bl.slot()); case BindingLocation::Kind::Import: return NameLocation::Import(); case BindingLocation::Kind::NamedLambdaCallee: return NameLocation::NamedLambdaCallee(); } MOZ_CRASH("Bad BindingKind"); } bool isTopLevelFunction() const { MOZ_ASSERT(!done()); bool result = names_[index_].isTopLevelFunction(); MOZ_ASSERT_IF(result, kind() == BindingKind::Var); return result; } bool hasArgumentSlot() const { MOZ_ASSERT(!done()); if (hasFormalParameterExprs()) { return false; } return index_ >= positionalFormalStart_ && index_ < nonPositionalFormalStart_; } uint16_t argumentSlot() const { MOZ_ASSERT(canHaveArgumentSlots()); return mozilla::AssertedCast<uint16_t>(index_); } uint32_t nextFrameSlot() const { MOZ_ASSERT(canHaveFrameSlots()); return frameSlot_; } uint32_t nextEnvironmentSlot() const { MOZ_ASSERT(canHaveEnvironmentSlots()); return environmentSlot_; } }; template <typename NameT> class AbstractBindingIter; template <> class AbstractBindingIter<JSAtom> : public BaseAbstractBindingIter<JSAtom> { using Base = BaseAbstractBindingIter<JSAtom>; public: AbstractBindingIter(ScopeKind kind, BaseScopeData* data, uint32_t firstFrameSlot); explicit AbstractBindingIter(Scope* scope); explicit AbstractBindingIter(JSScript* script); using Base::Base; inline void trace(JSTracer* trc) { TraceNullableBindingNames(trc, names_, length_); } }; template <> class AbstractBindingIter<frontend::TaggedParserAtomIndex> : public BaseAbstractBindingIter<frontend::TaggedParserAtomIndex> { using Base = BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>; public: explicit AbstractBindingIter(const frontend::ScopeStencilRef& ref); using Base::Base; }; void DumpBindings(JSContext* cx, Scope* scope); JSAtom* FrameSlotName(JSScript* script, jsbytecode* pc); SharedShape* EmptyEnvironmentShape(JSContext* cx, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); template <class T> SharedShape* EmptyEnvironmentShape(JSContext* cx) { return EmptyEnvironmentShape(cx, &T::class_, T::RESERVED_SLOTS, T::OBJECT_FLAGS); } // // A refinement BindingIter that only iterates over positional formal // parameters of a function. // class PositionalFormalParameterIter : public BindingIter { void settle() { if (index_ >= nonPositionalFormalStart_) { index_ = length_; } } public: explicit PositionalFormalParameterIter(Scope* scope); explicit PositionalFormalParameterIter(JSScript* script); void operator++(int) { BindingIter::operator++(1); settle(); } bool isDestructured() const { return !name(); } }; // // Iterator for walking the scope chain. // // It may be placed in GC containers; for example: // // for (Rooted<ScopeIter> si(cx, ScopeIter(scope)); si; si++) { // use(si); // SomeMayGCOperation(); // use(si); // } // class MOZ_STACK_CLASS ScopeIter { Scope* scope_; public: explicit ScopeIter(Scope* scope) : scope_(scope) {} explicit ScopeIter(JSScript* script); explicit ScopeIter(const ScopeIter& si) = default; bool done() const { return !scope_; } explicit operator bool() const { return !done(); } void operator++(int) { MOZ_ASSERT(!done()); scope_ = scope_->enclosing(); } Scope* scope() const { MOZ_ASSERT(!done()); return scope_; } ScopeKind kind() const { MOZ_ASSERT(!done()); return scope_->kind(); } // Returns the shape of the environment if it is known. It is possible to // hasSyntacticEnvironment and to have no known shape, e.g., eval. SharedShape* environmentShape() const { return scope()->environmentShape(); } // Returns whether this scope has a syntactic environment (i.e., an // Environment that isn't a non-syntactic With or NonSyntacticVariables) // on the environment chain. bool hasSyntacticEnvironment() const; void trace(JSTracer* trc) { if (scope_) { TraceRoot(trc, &scope_, "scope iter scope"); } } }; // // Specializations of Rooted containers for the iterators. // template <typename Wrapper> class WrappedPtrOperations<BindingIter, Wrapper> { const BindingIter& iter() const { return static_cast<const Wrapper*>(this)->get(); } public: bool done() const { return iter().done(); } explicit operator bool() const { return !done(); } bool isLast() const { return iter().isLast(); } bool canHaveArgumentSlots() const { return iter().canHaveArgumentSlots(); } bool canHaveFrameSlots() const { return iter().canHaveFrameSlots(); } bool canHaveEnvironmentSlots() const { return iter().canHaveEnvironmentSlots(); } JSAtom* name() const { return iter().name(); } bool closedOver() const { return iter().closedOver(); } BindingLocation location() const { return iter().location(); } BindingKind kind() const { return iter().kind(); } bool isTopLevelFunction() const { return iter().isTopLevelFunction(); } bool hasArgumentSlot() const { return iter().hasArgumentSlot(); } uint16_t argumentSlot() const { return iter().argumentSlot(); } uint32_t nextFrameSlot() const { return iter().nextFrameSlot(); } uint32_t nextEnvironmentSlot() const { return iter().nextEnvironmentSlot(); } }; template <typename Wrapper> class MutableWrappedPtrOperations<BindingIter, Wrapper> : public WrappedPtrOperations<BindingIter, Wrapper> { BindingIter& iter() { return static_cast<Wrapper*>(this)->get(); } public: void operator++(int) { iter().operator++(1); } }; template <typename Wrapper> class WrappedPtrOperations<ScopeIter, Wrapper> { const ScopeIter& iter() const { return static_cast<const Wrapper*>(this)->get(); } public: bool done() const { return iter().done(); } explicit operator bool() const { return !done(); } Scope* scope() const { return iter().scope(); } ScopeKind kind() const { return iter().kind(); } SharedShape* environmentShape() const { return iter().environmentShape(); } bool hasSyntacticEnvironment() const { return iter().hasSyntacticEnvironment(); } }; template <typename Wrapper> class MutableWrappedPtrOperations<ScopeIter, Wrapper> : public WrappedPtrOperations<ScopeIter, Wrapper> { ScopeIter& iter() { return static_cast<Wrapper*>(this)->get(); } public: void operator++(int) { iter().operator++(1); } }; SharedShape* CreateEnvironmentShape(JSContext* cx, BindingIter& bi, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); SharedShape* EmptyEnvironmentShape(JSContext* cx, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags); static inline size_t GetOffsetOfParserScopeDataTrailingNames(ScopeKind kind) { switch (kind) { // FunctionScope case ScopeKind::Function: return GetOffsetOfScopeDataTrailingNames<FunctionScope::ParserData>(); // VarScope case ScopeKind::FunctionBodyVar: return GetOffsetOfScopeDataTrailingNames<VarScope::ParserData>(); // LexicalScope case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: case ScopeKind::FunctionLexical: return GetOffsetOfScopeDataTrailingNames<LexicalScope::ParserData>(); // ClassBodyScope case ScopeKind::ClassBody: return GetOffsetOfScopeDataTrailingNames<ClassBodyScope::ParserData>(); // EvalScope case ScopeKind::Eval: case ScopeKind::StrictEval: return GetOffsetOfScopeDataTrailingNames<EvalScope::ParserData>(); // GlobalScope case ScopeKind::Global: case ScopeKind::NonSyntactic: return GetOffsetOfScopeDataTrailingNames<GlobalScope::ParserData>(); // ModuleScope case ScopeKind::Module: return GetOffsetOfScopeDataTrailingNames<ModuleScope::ParserData>(); // WasmInstanceScope case ScopeKind::WasmInstance: return GetOffsetOfScopeDataTrailingNames<WasmInstanceScope::ParserData>(); // WasmFunctionScope case ScopeKind::WasmFunction: return GetOffsetOfScopeDataTrailingNames<WasmFunctionScope::ParserData>(); // WithScope doesn't have ScopeData. case ScopeKind::With: default: MOZ_CRASH("Unexpected ScopeKind"); } return 0; } inline size_t SizeOfParserScopeData(ScopeKind kind, uint32_t length) { return GetOffsetOfParserScopeDataTrailingNames(kind) + sizeof(AbstractBindingName<frontend::TaggedParserAtomIndex>) * length; } inline mozilla::Span<AbstractBindingName<frontend::TaggedParserAtomIndex>> GetParserScopeDataTrailingNames( ScopeKind kind, AbstractBaseScopeData<frontend::TaggedParserAtomIndex>* data) { return mozilla::Span( reinterpret_cast<AbstractBindingName<frontend::TaggedParserAtomIndex>*>( uintptr_t(data) + GetOffsetOfParserScopeDataTrailingNames(kind)), data->length); } } // namespace js namespace JS { template <> struct GCPolicy<js::ScopeKind> : public IgnoreGCPolicy<js::ScopeKind> {}; template <typename T> struct ScopeDataGCPolicy : public NonGCPointerPolicy<T> {}; #define DEFINE_SCOPE_DATA_GCPOLICY(Data) \ template <> \ struct MapTypeToRootKind<Data*> { \ static const RootKind kind = RootKind::Traceable; \ }; \ template <> \ struct GCPolicy<Data*> : public ScopeDataGCPolicy<Data*> {} DEFINE_SCOPE_DATA_GCPOLICY(js::LexicalScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::ClassBodyScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::FunctionScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::VarScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::GlobalScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::EvalScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::ModuleScope::RuntimeData); DEFINE_SCOPE_DATA_GCPOLICY(js::WasmFunctionScope::RuntimeData); #undef DEFINE_SCOPE_DATA_GCPOLICY namespace ubi { template <> class Concrete<js::Scope> : TracerConcrete<js::Scope> { protected: explicit Concrete(js::Scope* ptr) : TracerConcrete<js::Scope>(ptr) {} public: static void construct(void* storage, js::Scope* ptr) { new (storage) Concrete(ptr); } CoarseType coarseType() const final { return CoarseType::Script; } Size size(mozilla::MallocSizeOf mallocSizeOf) const override; const char16_t* typeName() const override { return concreteTypeName; } static const char16_t concreteTypeName[]; }; } // namespace ubi } // namespace JS #endif // vm_Scope_h