From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- js/src/vm/Scope.h | 1822 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1822 insertions(+) create mode 100644 js/src/vm/Scope.h (limited to 'js/src/vm/Scope.h') diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h new file mode 100644 index 0000000000..2ad8340fa7 --- /dev/null +++ b/js/src/vm/Scope.h @@ -0,0 +1,1822 @@ +/* -*- 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 // std::fill_n +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uintptr_t +#include // std::is_same_v + +#include "builtin/ModuleObject.h" // ModuleObject, HandleModuleObject +#include "frontend/ParserAtom.h" // frontend::TaggedParserAtomIndex +#include "gc/Allocator.h" // AllowGC +#include "gc/Barrier.h" // HeapPtr +#include "gc/Cell.h" // TenuredCellWithNonGCPointer +#include "gc/MaybeRooted.h" // MaybeRooted +#include "gc/Rooting.h" // HandleScope, HandleShape, MutableHandleShape +#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/BytecodeUtil.h" // LOCALNO_LIMIT, ENVCOORD_SLOT_LIMIT +#include "vm/JSFunction.h" // JSFunction +#include "vm/ScopeKind.h" // ScopeKind +#include "vm/Shape.h" // Shape +#include "vm/Xdr.h" // XDRResult, XDRState +#include "wasm/WasmJS.h" // WasmInstanceObject + +class JSAtom; +class JSFreeOp; +class JSFunction; +class JSScript; +class JSTracer; +struct JSContext; + +namespace JS { +class Zone; +} // namespace JS + +namespace js { + +class GenericPrinter; + +namespace frontend { +struct CompilationAtomCache; +class ScopeStencil; +class ParserAtom; +} // namespace frontend + +template +class AbstractBaseScopeData; + +template +class BaseAbstractBindingIter; + +template +class AbstractBindingIter; + +using BindingIter = AbstractBindingIter; + +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 +class AbstractBindingName; + +template <> +class AbstractBindingName { + 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)) {} + + private: + // For fromXDR. + AbstractBindingName(NameT* name, uint8_t flags) + : bits_(uintptr_t(name) | flags) { + static_assert(FlagMask < alignof(NameT), + "Flags should fit into unused low bits of atom repr"); + MOZ_ASSERT((flags & FlagMask) == flags); + } + + public: + static AbstractBindingName fromXDR(NameT* name, uint8_t flags) { + return AbstractBindingName(name, flags); + } + + uint8_t flagsForXDR() const { return static_cast(bits_ & FlagMask); } + + NamePointerT name() const { + return reinterpret_cast(bits_ & ~FlagMask); + } + + bool closedOver() const { return bits_ & ClosedOverFlag; } + + private: + friend class BaseAbstractBindingIter; + + // 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); +}; + +template <> +class AbstractBindingName { + 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: + uint32_t* rawData() { return &bits_; } + + NamePointerT name() const { + return TaggedParserAtomIndex::fromRaw(bits_ & ~FlagMask); + } + + bool closedOver() const { return bits_ & ClosedOverFlag; } + + AbstractBindingName copyWithNewAtom(JSAtom* newName) const { + return AbstractBindingName(newName, closedOver(), + isTopLevelFunction()); + } + + private: + friend class BaseAbstractBindingIter; + 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; + +const size_t ScopeDataAlignBytes = size_t(1) << gc::CellFlagBitsReservedForGC; + +/** + * Empty base class for scope {Runtime,Parser}Data classes to inherit from. + * + * Scope GC things store a pointer to these in their first word so they must be + * suitably aligned to allow storing GC flags in the low bits. + */ +template +class AbstractBaseScopeData { + public: + using NameType = NameT; +}; + +using BaseScopeData = AbstractBaseScopeData; + +inline void PoisonNames(AbstractBindingName* data, size_t nameCount) { + AlwaysPoison(data, JS_SCOPE_DATA_TRAILING_NAMES_PATTERN, + sizeof(AbstractBindingName) * nameCount, + MemCheckKind::MakeUndefined); +} + +// frontend::TaggedParserAtomIndex doesn't require poison value. +// Fill with null value instead. +inline void PoisonNames( + AbstractBindingName* data, + size_t nameCount) { + std::fill_n(data, nameCount, + AbstractBindingName()); +} + +/** + * The various {Global,Module,...}Scope::{Runtime,Parser}Data classes consist + * of always-present bits, then a trailing array of BindingNames. The various + * {Runtime,Parser}Data classes all end in a TrailingNamesArray that contains + * sized/aligned space for *one* BindingName. {Runtime,Parser}Data instances + * that contain N BindingNames, are then allocated in + * sizeof({Runtime,Parser}Data) + (space for (N - 1) BindingNames). + * Because this class's |data_| field is properly sized/aligned, the + * N-BindingName array can start at |data_|. + * + * This is concededly a very low-level representation, but we want to only + * allocate once for data+bindings both, and this does so approximately as + * elegantly as C++ allows. + * + * The names array is implemented in terms of an generic type that + * allows specialization between a (JSAtom*) BindingName and a + * ParserAtom + */ +template +class AbstractTrailingNamesArray { + using BindingNameT = AbstractBindingName; + + private: + alignas(BindingNameT) unsigned char data_[sizeof(BindingNameT)]; + + private: + // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a + // -Werror compile error) to reinterpret_cast<> |data_| to |T*|, even + // through |void*|. Placing the latter cast in these separate functions + // breaks the chain such that affected GCC versions no longer warn/error. + void* ptr() { return data_; } + + public: + // Explicitly ensure no one accidentally allocates scope data without + // poisoning its trailing names. + AbstractTrailingNamesArray() = delete; + + explicit AbstractTrailingNamesArray(size_t nameCount) { + if (nameCount) { + PoisonNames(reinterpret_cast(&data_), nameCount); + } + } + + BindingNameT* start() { return reinterpret_cast(ptr()); } + + BindingNameT& get(size_t i) { return start()[i]; } + BindingNameT& operator[](size_t i) { return get(i); } +}; + +// +// Allow using is and as on Rooted and Handle. +// +template +class WrappedPtrOperations { + public: + template + JS::Handle as() const { + const Wrapper& self = *static_cast(this); + MOZ_ASSERT_IF(self, self->template is()); + return Handle::fromMarkedLocation( + reinterpret_cast(self.address())); + } +}; + +// +// The base class of all Scopes. +// +class Scope : public gc::TenuredCellWithNonGCPointer { + friend class GCMarker; + friend class frontend::ScopeStencil; + friend class js::AbstractBindingIter; + + 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 environmentShape_; + + // The enclosing scope or nullptr. + HeapPtr enclosingScope_; + + Scope(ScopeKind kind, Scope* enclosing, Shape* environmentShape) + : TenuredCellWithNonGCPointer(nullptr), + kind_(kind), + environmentShape_(environmentShape), + enclosingScope_(enclosing) {} + + static Scope* create(JSContext* cx, ScopeKind kind, HandleScope enclosing, + HandleShape envShape); + + template + static XDRResult XDRSizedBindingNames( + XDRState* xdr, Handle scope, + MutableHandle data); + + Shape* maybeCloneEnvironmentShape(JSContext* cx); + + template + void initData( + MutableHandle> data); + + template + void applyScopeDataTyped(F&& f); + + template + static bool updateEnvShapeIfRequired(JSContext* cx, MutableHandleShape shape, + bool needsEnvironment); + + template + static bool updateEnvShapeIfRequired(JSContext* cx, + mozilla::Maybe* envShape, + bool needsEnvironment); + + public: + template + static ConcreteScope* create( + JSContext* cx, ScopeKind kind, HandleScope enclosing, + HandleShape envShape, + MutableHandle> data); + + static const JS::TraceKind TraceKind = JS::TraceKind::Scope; + + template + bool is() const { + return kind_ == T::classScopeKind_; + } + + template + T& as() { + MOZ_ASSERT(this->is()); + return *static_cast(this); + } + + template + const T& as() const { + MOZ_ASSERT(this->is()); + return *static_cast(this); + } + + ScopeKind kind() const { return kind_; } + + Shape* environmentShape() const { return environmentShape_; } + + Scope* enclosing() const { return enclosingScope_; } + + static bool hasEnvironment(ScopeKind kind, bool environmentShape) { + 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 environmentShape; + } + } + + bool hasEnvironment() const { + return hasEnvironment(kind_, environmentShape()); + } + + uint32_t firstFrameSlot() const; + + uint32_t chainLength() const; + uint32_t environmentChainLength() const; + + template + bool hasOnChain() const { + for (const Scope* it = this; it; it = it->enclosing()) { + if (it->is()) { + 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; + } + + static Scope* clone(JSContext* cx, HandleScope scope, HandleScope enclosing); + + void traceChildren(JSTracer* trc); + void finalize(JSFreeOp* fop); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + void dump(); +#if defined(DEBUG) || defined(JS_JITSPEW) + static bool dumpForDisassemble(JSContext* cx, JS::Handle scope, + GenericPrinter& out, const char* indent); +#endif /* defined(DEBUG) || defined(JS_JITSPEW) */ +}; + +template +inline size_t SizeOfScopeData(uint32_t numBindings) { + using BindingT = AbstractBindingName; + +#if JS_BITS_PER_WORD == 64 + static_assert( + sizeof(DataT) == offsetof(DataT, trailingNames) + sizeof(BindingT), + "Unexpected default number of inlined elements"); + // -1 because AbstractTrailingNamesArray (trailingNames field in DataT) + // contains one inlined element in data_ field. + return sizeof(DataT) + + ((numBindings ? numBindings - 1 : 0) * sizeof(BindingT)); +#else + // RuntimeData has alignas(ScopeDataAlignBytes), that is 8-bytes. + // RuntimeData on 32-bit arch may have 4-bytes trailing padding after + // trailingNames, and in that case there are effectively 2 inlined elements + // inside sizeof(DataT). + static_assert( + sizeof(DataT) == offsetof(DataT, trailingNames) + sizeof(BindingT) || + sizeof(DataT) == + offsetof(DataT, trailingNames) + 2 * sizeof(BindingT), + "Unexpected default number of inlined elements"); + + if constexpr (sizeof(DataT) == + offsetof(DataT, trailingNames) + sizeof(BindingT)) { + // There's no trailing padding, and there's only one inlined element. + // This is RuntimeData without padding, or ParserData. + return sizeof(DataT) + + ((numBindings ? numBindings - 1 : 0) * sizeof(BindingT)); + } + + // There's trailing padding, and there are two inlined elements. + return sizeof(DataT) + + ((numBindings > 2 ? numBindings - 2 : 0) * sizeof(BindingT)); +#endif +} + +// +// 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 +using AbstractScopeData = typename ScopeT::template AbstractData; + +template +using MaybeRootedScopeData = std::conditional_t< + std::is_same_v, + MaybeRooted, AllowGC::CanGC>, + MaybeRooted*, AllowGC::NoGC>>; + +template +struct ParserScopeData + : public AbstractBaseScopeData { + SlotInfo slotInfo; + AbstractTrailingNamesArray trailingNames; + + explicit ParserScopeData(size_t nameCount) : trailingNames(nameCount) {} + ParserScopeData() = delete; +}; + +// +// 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; + 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; + uint32_t length = 0; + }; + + struct alignas(ScopeDataAlignBytes) RuntimeData + : public AbstractBaseScopeData { + SlotInfo slotInfo; + AbstractTrailingNamesArray trailingNames; + + explicit RuntimeData(size_t nameCount) : trailingNames(nameCount) {} + RuntimeData() = delete; + + void trace(JSTracer* trc); + }; + + using ParserData = ParserScopeData; + + template + using AbstractData = + typename std::conditional_t::value, + RuntimeData, ParserData>; + + template + static XDRResult XDR(XDRState* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + + private: + static LexicalScope* createWithData( + JSContext* cx, ScopeKind kind, MutableHandle> data, + uint32_t firstFrameSlot, HandleScope enclosing); + + template + static bool prepareForScopeCreation( + JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, + typename MaybeRootedScopeData::MutableHandleType + data, + ShapeT envShape); + + RuntimeData& data() { return *static_cast(rawData()); } + const RuntimeData& data() const { + return *static_cast(rawData()); + } + + static uint32_t nextFrameSlot(Scope* scope); + + public: + uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } + + // Returns an empty shape for extensible global and non-syntactic lexical + // scopes. + static Shape* getEmptyExtensibleEnvironmentShape(JSContext* cx); +}; + +template <> +inline bool Scope::is() const { + return kind_ == ScopeKind::Lexical || kind_ == ScopeKind::SimpleCatch || + kind_ == ScopeKind::Catch || kind_ == ScopeKind::NamedLambda || + kind_ == ScopeKind::StrictNamedLambda || + kind_ == ScopeKind::FunctionLexical || kind_ == ScopeKind::ClassBody; +} + +// +// 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; + 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; + uint32_t length = 0; + + bool hasParameterExprs() const { return flags & HasParameterExprsFlag; } + void setHasParameterExprs() { flags |= HasParameterExprsFlag; } + }; + + struct alignas(ScopeDataAlignBytes) RuntimeData + : public AbstractBaseScopeData { + // 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 canonicalFunction = {}; + SlotInfo slotInfo; + AbstractTrailingNamesArray trailingNames; + + explicit RuntimeData(size_t nameCount) : trailingNames(nameCount) {} + RuntimeData() = delete; + + void trace(JSTracer* trc); + }; + + using ParserData = ParserScopeData; + + template + using AbstractData = + typename std::conditional_t::value, + RuntimeData, ParserData>; + + template + static bool prepareForScopeCreation( + JSContext* cx, + typename MaybeRootedScopeData::MutableHandleType + data, + bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, + ShapeT envShape); + + static FunctionScope* clone(JSContext* cx, Handle scope, + HandleFunction fun, HandleScope enclosing); + + template + static XDRResult XDR(XDRState* xdr, HandleFunction fun, + HandleScope enclosing, MutableHandleScope scope); + + private: + static FunctionScope* createWithData( + JSContext* cx, MutableHandle> data, + bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, + HandleScope enclosing); + + RuntimeData& data() { return *static_cast(rawData()); } + + const RuntimeData& data() const { + return *static_cast(rawData()); + } + + public: + uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } + + JSFunction* canonicalFunction() const { return data().canonicalFunction; } + + JSScript* script() const; + + bool hasParameterExprs() const { return data().slotInfo.hasParameterExprs(); } + + uint32_t numPositionalFormalParameters() const { + return data().slotInfo.nonPositionalFormalStart; + } + + static bool isSpecialName(JSContext* cx, JSAtom* name); + static bool isSpecialName(JSContext* cx, + 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; + 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) + uint32_t length = 0; + }; + + struct alignas(ScopeDataAlignBytes) RuntimeData + : public AbstractBaseScopeData { + SlotInfo slotInfo; + AbstractTrailingNamesArray trailingNames; + + explicit RuntimeData(size_t nameCount) : trailingNames(nameCount) {} + RuntimeData() = delete; + + void trace(JSTracer* trc); + }; + + using ParserData = ParserScopeData; + + template + using AbstractData = + typename std::conditional_t::value, + RuntimeData, ParserData>; + + template + static XDRResult XDR(XDRState* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + + private: + static VarScope* createWithData(JSContext* cx, ScopeKind kind, + MutableHandle> data, + uint32_t firstFrameSlot, + bool needsEnvironment, HandleScope enclosing); + + template + static bool prepareForScopeCreation( + JSContext* cx, ScopeKind kind, + typename MaybeRootedScopeData::MutableHandleType data, + uint32_t firstFrameSlot, bool needsEnvironment, ShapeT envShape); + + RuntimeData& data() { return *static_cast(rawData()); } + + const RuntimeData& data() const { + return *static_cast(rawData()); + } + + public: + uint32_t nextFrameSlot() const { return data().slotInfo.nextFrameSlot; } +}; + +template <> +inline bool Scope::is() 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