summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Scope.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Scope.h')
-rw-r--r--js/src/vm/Scope.h1892
1 files changed, 1892 insertions, 0 deletions
diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h
new file mode 100644
index 0000000000..a914a14f28
--- /dev/null
+++ b/js/src/vm/Scope.h
@@ -0,0 +1,1892 @@
+/* -*- 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 JS_PUBLIC_API 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;
+
+template <typename NameT>
+class AbstractPositionalFormalParameterIter;
+
+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 AbstractPositionalFormalParameterIter<JSAtom>;
+ 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 memoriesStart = 0;
+ 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 data().slotInfo.memoriesStart; }
+
+ 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);
+}
+
+//
+// PositionalFormalParameterIter is a refinement BindingIter that only iterates
+// over positional formal parameters of a function.
+//
+template <typename NameT>
+class BasePositionalFormalParamterIter : public AbstractBindingIter<NameT> {
+ using Base = AbstractBindingIter<NameT>;
+
+ protected:
+ void settle() {
+ if (this->index_ >= this->nonPositionalFormalStart_) {
+ this->index_ = this->length_;
+ }
+ }
+
+ public:
+ using Base::Base;
+
+ void operator++(int) {
+ Base::operator++(1);
+ settle();
+ }
+
+ bool isDestructured() const { return !this->name(); }
+};
+
+template <typename NameT>
+class AbstractPositionalFormalParameterIter;
+
+template <>
+class AbstractPositionalFormalParameterIter<JSAtom>
+ : public BasePositionalFormalParamterIter<JSAtom> {
+ using Base = BasePositionalFormalParamterIter<JSAtom>;
+
+ public:
+ explicit AbstractPositionalFormalParameterIter(Scope* scope);
+ explicit AbstractPositionalFormalParameterIter(JSScript* script);
+
+ using Base::Base;
+};
+
+template <>
+class AbstractPositionalFormalParameterIter<frontend::TaggedParserAtomIndex>
+ : public BasePositionalFormalParamterIter<frontend::TaggedParserAtomIndex> {
+ using Base =
+ BasePositionalFormalParamterIter<frontend::TaggedParserAtomIndex>;
+
+ public:
+ AbstractPositionalFormalParameterIter(
+ FunctionScope::AbstractData<frontend::TaggedParserAtomIndex>& data,
+ bool hasParameterExprs)
+ : Base(data, hasParameterExprs) {
+ settle();
+ }
+
+ using Base::Base;
+};
+
+using PositionalFormalParameterIter =
+ AbstractPositionalFormalParameterIter<JSAtom>;
+
+//
+// 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