summaryrefslogtreecommitdiff
path: root/js/src/vm/Scope.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/vm/Scope.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/vm/Scope.h')
-rw-r--r--js/src/vm/Scope.h1451
1 files changed, 1451 insertions, 0 deletions
diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h
new file mode 100644
index 0000000000..5304d6713e
--- /dev/null
+++ b/js/src/vm/Scope.h
@@ -0,0 +1,1451 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/Maybe.h"
+#include "mozilla/Variant.h"
+
+#include "jsobj.h"
+#include "jsopcode.h"
+
+#include "gc/Heap.h"
+#include "gc/Policy.h"
+#include "js/UbiNode.h"
+#include "js/UniquePtr.h"
+#include "vm/Xdr.h"
+
+namespace js {
+
+class ModuleObject;
+
+enum class BindingKind : uint8_t
+{
+ Import,
+ FormalParameter,
+ Var,
+ Let,
+ Const,
+
+ // So you think named lambda callee names are consts? Nope! They don't
+ // throw when being assigned to in sloppy mode.
+ NamedLambdaCallee
+};
+
+static inline bool
+BindingKindIsLexical(BindingKind kind)
+{
+ return kind == BindingKind::Let || kind == BindingKind::Const;
+}
+
+enum class ScopeKind : uint8_t
+{
+ // FunctionScope
+ Function,
+
+ // VarScope
+ FunctionBodyVar,
+ ParameterExpressionVar,
+
+ // LexicalScope
+ Lexical,
+ SimpleCatch,
+ Catch,
+ NamedLambda,
+ StrictNamedLambda,
+
+ // WithScope
+ With,
+
+ // EvalScope
+ Eval,
+ StrictEval,
+
+ // GlobalScope
+ Global,
+ NonSyntactic,
+
+ // ModuleScope
+ Module
+};
+
+static inline bool
+ScopeKindIsCatch(ScopeKind kind)
+{
+ return kind == ScopeKind::SimpleCatch || kind == ScopeKind::Catch;
+}
+
+const char* BindingKindString(BindingKind kind);
+const char* ScopeKindString(ScopeKind kind);
+
+class BindingName
+{
+ // A JSAtom* with its low bit used as a tag for whether it is closed over
+ // (i.e., exists in the environment shape).
+ uintptr_t bits_;
+
+ static const uintptr_t ClosedOverFlag = 0x1;
+ static const uintptr_t FlagMask = 0x1;
+
+ public:
+ BindingName()
+ : bits_(0)
+ { }
+
+ BindingName(JSAtom* name, bool closedOver)
+ : bits_(uintptr_t(name) | (closedOver ? ClosedOverFlag : 0x0))
+ { }
+
+ JSAtom* name() const {
+ return reinterpret_cast<JSAtom*>(bits_ & ~FlagMask);
+ }
+
+ bool closedOver() const {
+ return bits_ & ClosedOverFlag;
+ }
+
+ void trace(JSTracer* trc);
+};
+
+class BindingLocation
+{
+ public:
+ enum class Kind {
+ Global,
+ Argument,
+ Frame,
+ Environment,
+ Import,
+ NamedLambdaCallee
+ };
+
+ private:
+ Kind kind_;
+ uint32_t slot_;
+
+ BindingLocation(Kind kind, uint32_t slot)
+ : kind_(kind),
+ slot_(slot)
+ { }
+
+ public:
+ static BindingLocation Global() {
+ return BindingLocation(Kind::Global, UINT32_MAX);
+ }
+
+ static BindingLocation Argument(uint16_t slot) {
+ return BindingLocation(Kind::Argument, slot);
+ }
+
+ static BindingLocation Frame(uint32_t slot) {
+ MOZ_ASSERT(slot < LOCALNO_LIMIT);
+ return BindingLocation(Kind::Frame, slot);
+ }
+
+ static BindingLocation Environment(uint32_t slot) {
+ MOZ_ASSERT(slot < ENVCOORD_SLOT_LIMIT);
+ return BindingLocation(Kind::Environment, slot);
+ }
+
+ static BindingLocation Import() {
+ return BindingLocation(Kind::Import, UINT32_MAX);
+ }
+
+ static BindingLocation NamedLambdaCallee() {
+ return BindingLocation(Kind::NamedLambdaCallee, UINT32_MAX);
+ }
+
+ bool operator==(const BindingLocation& other) const {
+ return kind_ == other.kind_ && slot_ == other.slot_;
+ }
+
+ bool operator!=(const BindingLocation& other) const {
+ return !operator==(other);
+ }
+
+ Kind kind() const {
+ return kind_;
+ }
+
+ uint32_t slot() const {
+ MOZ_ASSERT(kind_ == Kind::Frame || kind_ == Kind::Environment);
+ return slot_;
+ }
+
+ uint16_t argumentSlot() const {
+ MOZ_ASSERT(kind_ == Kind::Argument);
+ return mozilla::AssertedCast<uint16_t>(slot_);
+ }
+};
+
+//
+// The base class of all Scopes.
+//
+class Scope : public js::gc::TenuredCell
+{
+ friend class GCMarker;
+
+ // The kind determines data_.
+ ScopeKind kind_;
+
+ // The enclosing scope or nullptr.
+ GCPtrScope enclosing_;
+
+ // If there are any aliased bindings, the shape for the
+ // EnvironmentObject. Otherwise nullptr.
+ GCPtrShape environmentShape_;
+
+ protected:
+ uintptr_t data_;
+
+ Scope(ScopeKind kind, Scope* enclosing, Shape* environmentShape)
+ : kind_(kind),
+ enclosing_(enclosing),
+ environmentShape_(environmentShape),
+ data_(0)
+ { }
+
+ static Scope* create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing,
+ HandleShape envShape);
+
+ template <typename T, typename D>
+ static Scope* create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing,
+ HandleShape envShape, mozilla::UniquePtr<T, D> data);
+
+ template <typename ConcreteScope, XDRMode mode>
+ static bool XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
+ MutableHandle<typename ConcreteScope::Data*> data);
+
+ Shape* maybeCloneEnvironmentShape(JSContext* cx);
+
+ template <typename T, typename D>
+ void initData(mozilla::UniquePtr<T, D> data) {
+ MOZ_ASSERT(!data_);
+ data_ = reinterpret_cast<uintptr_t>(data.release());
+ }
+
+ public:
+ 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_;
+ }
+
+ Scope* enclosing() const {
+ return enclosing_;
+ }
+
+ Shape* environmentShape() const {
+ return environmentShape_;
+ }
+
+ bool hasEnvironment() const {
+ 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_ != nullptr;
+ }
+ }
+
+ 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;
+ }
+
+ static Scope* clone(JSContext* cx, HandleScope scope, HandleScope enclosing);
+
+ void traceChildren(JSTracer* trc);
+ void finalize(FreeOp* fop);
+
+ size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ void dump();
+};
+
+//
+// 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 BindingIter;
+
+ public:
+ // Data is public because it is created by the frontend. See
+ // Parser<FullParseHandler>::newLexicalScopeData.
+ struct Data
+ {
+ // Bindings are sorted by kind in both frames and environments.
+ //
+ // lets - [0, constStart)
+ // consts - [constStart, length)
+ uint32_t constStart;
+ uint32_t length;
+
+ // Frame slots [0, nextFrameSlot) are live when this is the innermost
+ // scope.
+ uint32_t nextFrameSlot;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static LexicalScope* create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
+ uint32_t firstFrameSlot, HandleScope enclosing);
+
+ template <XDRMode mode>
+ static bool XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+ private:
+ static LexicalScope* createWithData(ExclusiveContext* cx, ScopeKind kind,
+ MutableHandle<UniquePtr<Data>> data,
+ uint32_t firstFrameSlot, HandleScope enclosing);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ static uint32_t nextFrameSlot(Scope* start);
+
+ public:
+ uint32_t firstFrameSlot() const;
+
+ uint32_t nextFrameSlot() const {
+ return data().nextFrameSlot;
+ }
+
+ // Returns an empty shape for extensible global and non-syntactic lexical
+ // scopes.
+ static Shape* getEmptyExtensibleEnvironmentShape(ExclusiveContext* cx);
+};
+
+template <>
+inline bool
+Scope::is<LexicalScope>() const
+{
+ return kind_ == ScopeKind::Lexical ||
+ kind_ == ScopeKind::SimpleCatch ||
+ kind_ == ScopeKind::Catch ||
+ kind_ == ScopeKind::NamedLambda ||
+ kind_ == ScopeKind::StrictNamedLambda;
+}
+
+//
+// Scope corresponding to a function. Holds formal parameter names 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 BindingIter;
+ friend class PositionalFormalParameterIter;
+ friend class Scope;
+ static const ScopeKind classScopeKind_ = ScopeKind::Function;
+
+ public:
+ // Data is public because it is created by the
+ // frontend. See Parser<FullParseHandler>::newFunctionScopeData.
+ struct Data
+ {
+ // 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).
+ GCPtrFunction canonicalFunction;
+
+ // If parameter expressions are present, parameters act like lexical
+ // bindings.
+ bool hasParameterExprs;
+
+ // 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.
+ //
+ // positional formals - [0, nonPositionalFormalStart)
+ // other formals - [nonPositionalParamStart, varStart)
+ // vars - [varStart, length)
+ uint16_t nonPositionalFormalStart;
+ uint16_t varStart;
+ uint32_t length;
+
+ // Frame slots [0, nextFrameSlot) are live when this is the innermost
+ // scope.
+ uint32_t nextFrameSlot;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static FunctionScope* create(ExclusiveContext* cx, Handle<Data*> data,
+ bool hasParameterExprs, bool needsEnvironment,
+ HandleFunction fun, HandleScope enclosing);
+
+ static FunctionScope* clone(JSContext* cx, Handle<FunctionScope*> scope, HandleFunction fun,
+ HandleScope enclosing);
+
+ template <XDRMode mode>
+ static bool XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
+ MutableHandleScope scope);
+
+ private:
+ static FunctionScope* createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
+ bool hasParameterExprs, bool needsEnvironment,
+ HandleFunction fun, HandleScope enclosing);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ public:
+ uint32_t nextFrameSlot() const {
+ return data().nextFrameSlot;
+ }
+
+ JSFunction* canonicalFunction() const {
+ return data().canonicalFunction;
+ }
+
+ JSScript* script() const;
+
+ bool hasParameterExprs() const {
+ return data().hasParameterExprs;
+ }
+
+ uint32_t numPositionalFormalParameters() const {
+ return data().nonPositionalFormalStart;
+ }
+
+ static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs);
+};
+
+//
+// Scope holding only vars. There are 2 kinds of VarScopes.
+//
+// FunctionBodyVar
+// Corresponds to the extra var scope present in functions with parameter
+// expressions. See examples in comment above FunctionScope.
+//
+// ParameterExpressionVar
+// Each parameter expression is evaluated in its own var environment. For
+// example, f() below will print 'fml', then 'global'. That's right.
+//
+// var a = 'global';
+// function f(x = (eval(`var a = 'fml'`), a), y = a) {
+// print(x);
+// print(y);
+// };
+//
+// Corresponds to VarEnvironmentObject on environment chain.
+//
+class VarScope : public Scope
+{
+ friend class GCMarker;
+ friend class BindingIter;
+ friend class Scope;
+
+ public:
+ // Data is public because it is created by the
+ // frontend. See Parser<FullParseHandler>::newVarScopeData.
+ struct Data
+ {
+ // All bindings are vars.
+ uint32_t length;
+
+ // Frame slots [firstFrameSlot(), nextFrameSlot) are live when this is
+ // the innermost scope.
+ uint32_t nextFrameSlot;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static VarScope* create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
+ uint32_t firstFrameSlot, bool needsEnvironment,
+ HandleScope enclosing);
+
+ template <XDRMode mode>
+ static bool XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+ private:
+ static VarScope* createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
+ uint32_t firstFrameSlot, bool needsEnvironment,
+ HandleScope enclosing);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ public:
+ uint32_t firstFrameSlot() const;
+
+ uint32_t nextFrameSlot() const {
+ return data().nextFrameSlot;
+ }
+
+ static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
+};
+
+template <>
+inline bool
+Scope::is<VarScope>() const
+{
+ return kind_ == ScopeKind::FunctionBodyVar || kind_ == ScopeKind::ParameterExpressionVar;
+}
+
+//
+// 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 global LexicalEnvironmentObject 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 BindingIter;
+
+ public:
+ // Data is public because it is created by the frontend. See
+ // Parser<FullParseHandler>::newGlobalScopeData.
+ struct Data
+ {
+ // Bindings are sorted by kind.
+ //
+ // top-level funcs - [0, varStart)
+ // vars - [varStart, letStart)
+ // lets - [letStart, constStart)
+ // consts - [constStart, length)
+ uint32_t varStart;
+ uint32_t letStart;
+ uint32_t constStart;
+ uint32_t length;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static GlobalScope* create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data);
+
+ static GlobalScope* createEmpty(ExclusiveContext* cx, ScopeKind kind) {
+ return create(cx, kind, nullptr);
+ }
+
+ static GlobalScope* clone(JSContext* cx, Handle<GlobalScope*> scope, ScopeKind kind);
+
+ template <XDRMode mode>
+ static bool XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope);
+
+ private:
+ static GlobalScope* createWithData(ExclusiveContext* cx, ScopeKind kind,
+ MutableHandle<UniquePtr<Data>> data);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ 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;
+ static const ScopeKind classScopeKind_ = ScopeKind::With;
+
+ public:
+ static WithScope* create(ExclusiveContext* cx, HandleScope 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 BindingIter;
+
+ public:
+ // Data is public because it is created by the frontend. See
+ // Parser<FullParseHandler>::newEvalScopeData.
+ struct Data
+ {
+ // 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. However, we need to track top-level
+ // functions specially for redeclaration checks.
+ //
+ // top-level funcs - [0, varStart)
+ // vars - [varStart, length)
+ uint32_t varStart;
+ uint32_t length;
+
+ // Frame slots [0, nextFrameSlot) are live when this is the innermost
+ // scope.
+ uint32_t nextFrameSlot;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static EvalScope* create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
+ HandleScope enclosing);
+
+ template <XDRMode mode>
+ static bool XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+ private:
+ static EvalScope* createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
+ HandleScope enclosing);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ 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().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>();
+ }
+
+ static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
+};
+
+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 BindingIter;
+ friend class Scope;
+ static const ScopeKind classScopeKind_ = ScopeKind::Module;
+
+ public:
+ // Data is public because it is created by the frontend. See
+ // Parser<FullParseHandler>::newModuleScopeData.
+ struct Data
+ {
+ // The module of the scope.
+ GCPtr<ModuleObject*> module;
+
+ // Bindings are sorted by kind.
+ //
+ // imports - [0, varStart)
+ // vars - [varStart, letStart)
+ // lets - [letStart, constStart)
+ // consts - [constStart, length)
+ uint32_t varStart;
+ uint32_t letStart;
+ uint32_t constStart;
+ uint32_t length;
+
+ // Frame slots [0, nextFrameSlot) are live when this is the innermost
+ // scope.
+ uint32_t nextFrameSlot;
+
+ // The array of tagged JSAtom* names, allocated beyond the end of the
+ // struct.
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ static ModuleScope* create(ExclusiveContext* cx, Handle<Data*> data,
+ Handle<ModuleObject*> module, HandleScope enclosing);
+
+ private:
+ static ModuleScope* createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
+ Handle<ModuleObject*> module, HandleScope enclosing);
+
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ public:
+ uint32_t nextFrameSlot() const {
+ return data().nextFrameSlot;
+ }
+
+ ModuleObject* module() const {
+ return data().module;
+ }
+
+ JSScript* script() const;
+
+ static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
+};
+
+//
+// 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);
+// }
+//
+class BindingIter
+{
+ protected:
+ // Bindings are sorted by kind. Because different Scopes have differently
+ // laid out Data for packing, BindingIter must handle all binding kinds.
+ //
+ // Kind ranges:
+ //
+ // imports - [0, positionalFormalStart)
+ // positional formals - [positionalFormalStart, nonPositionalFormalStart)
+ // other formals - [nonPositionalParamStart, topLevelFunctionStart)
+ // top-level funcs - [topLevelFunctionStart, varStart)
+ // vars - [varStart, letStart)
+ // lets - [letStart, constStart)
+ // consts - [constStart, length)
+ //
+ // Access method when not closed over:
+ //
+ // imports - name
+ // positional formals - argument slot
+ // other formals - frame slot
+ // top-level funcs - frame slot
+ // vars - frame slot
+ // lets - frame slot
+ // consts - frame slot
+ //
+ // Access method when closed over:
+ //
+ // imports - name
+ // positional formals - environment slot or name
+ // other formals - environment slot or name
+ // top-level funcs - environment slot or name
+ // vars - environment slot or name
+ // lets - environment slot or name
+ // consts - environment slot or name
+ uint32_t positionalFormalStart_;
+ uint32_t nonPositionalFormalStart_;
+ uint32_t topLevelFunctionStart_;
+ uint32_t varStart_;
+ uint32_t letStart_;
+ uint32_t constStart_;
+ uint32_t length_;
+
+ 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;
+
+ uint8_t flags_;
+ uint16_t argumentSlot_;
+ uint32_t frameSlot_;
+ uint32_t environmentSlot_;
+
+ BindingName* names_;
+
+ void init(uint32_t positionalFormalStart, uint32_t nonPositionalFormalStart,
+ uint32_t topLevelFunctionStart, uint32_t varStart,
+ uint32_t letStart, uint32_t constStart,
+ uint8_t flags, uint32_t firstFrameSlot, uint32_t firstEnvironmentSlot,
+ BindingName* names, uint32_t length)
+ {
+ positionalFormalStart_ = positionalFormalStart;
+ nonPositionalFormalStart_ = nonPositionalFormalStart;
+ topLevelFunctionStart_ = topLevelFunctionStart;
+ varStart_ = varStart;
+ letStart_ = letStart;
+ constStart_ = constStart;
+ length_ = length;
+ index_ = 0;
+ flags_ = flags;
+ argumentSlot_ = 0;
+ frameSlot_ = firstFrameSlot;
+ environmentSlot_ = firstEnvironmentSlot;
+ names_ = names;
+
+ settle();
+ }
+
+ void init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t flags);
+ void init(FunctionScope::Data& data, uint8_t flags);
+ void init(VarScope::Data& data, uint32_t firstFrameSlot);
+ void init(GlobalScope::Data& data);
+ void init(EvalScope::Data& data, bool strict);
+ void init(ModuleScope::Data& 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();
+ }
+ }
+
+ public:
+ explicit BindingIter(Scope* scope);
+ explicit BindingIter(JSScript* script);
+
+ BindingIter(LexicalScope::Data& data, uint32_t firstFrameSlot, bool isNamedLambda) {
+ init(data, firstFrameSlot, isNamedLambda ? IsNamedLambda : 0);
+ }
+
+ BindingIter(FunctionScope::Data& data, bool hasParameterExprs) {
+ init(data,
+ IgnoreDestructuredFormalParameters |
+ (hasParameterExprs ? HasFormalParameterExprs : 0));
+ }
+
+ BindingIter(VarScope::Data& data, uint32_t firstFrameSlot) {
+ init(data, firstFrameSlot);
+ }
+
+ explicit BindingIter(GlobalScope::Data& data) {
+ init(data);
+ }
+
+ explicit BindingIter(ModuleScope::Data& data) {
+ init(data);
+ }
+
+ BindingIter(EvalScope::Data& data, bool strict) {
+ init(data, strict);
+ }
+
+ explicit BindingIter(const BindingIter& 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;
+ }
+
+ JSAtom* 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_ < topLevelFunctionStart_) {
+ // 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 (isNamedLambda())
+ return BindingKind::NamedLambdaCallee;
+ return BindingKind::Const;
+ }
+
+ bool isTopLevelFunction() const {
+ MOZ_ASSERT(!done());
+ return index_ >= topLevelFunctionStart_ && index_ < varStart_;
+ }
+
+ 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_;
+ }
+
+ void trace(JSTracer* trc);
+};
+
+void DumpBindings(JSContext* cx, Scope* scope);
+JSAtom* FrameSlotName(JSScript* script, jsbytecode* pc);
+
+//
+// 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(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)
+ : scope_(si.scope_)
+ { }
+
+ 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.
+ Shape* 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 Outer>
+class BindingIterOperations
+{
+ const BindingIter& iter() const { return static_cast<const Outer*>(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 Outer>
+class MutableBindingIterOperations : public BindingIterOperations<Outer>
+{
+ BindingIter& iter() { return static_cast<Outer*>(this)->get(); }
+
+ public:
+ void operator++(int) { iter().operator++(1); }
+};
+
+template <typename Outer>
+class ScopeIterOperations
+{
+ const ScopeIter& iter() const { return static_cast<const Outer*>(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(); }
+ Shape* environmentShape() const { return iter().environmentShape(); }
+ bool hasSyntacticEnvironment() const { return iter().hasSyntacticEnvironment(); }
+};
+
+template <typename Outer>
+class MutableScopeIterOperations : public ScopeIterOperations<Outer>
+{
+ ScopeIter& iter() { return static_cast<Outer*>(this)->get(); }
+
+ public:
+ void operator++(int) { iter().operator++(1); }
+};
+
+#define SPECIALIZE_ROOTING_CONTAINERS(Iter, BaseIter) \
+ template <> \
+ class RootedBase<Iter> \
+ : public Mutable##BaseIter##Operations<JS::Rooted<Iter>> \
+ { }; \
+ \
+ template <> \
+ class MutableHandleBase<Iter> \
+ : public Mutable##BaseIter##Operations<JS::MutableHandle<Iter>> \
+ { }; \
+ \
+ template <> \
+ class HandleBase<Iter> \
+ : public BaseIter##Operations<JS::Handle<Iter>> \
+ { }; \
+ \
+ template <> \
+ class PersistentRootedBase<Iter> \
+ : public Mutable##BaseIter##Operations<JS::PersistentRooted<Iter>> \
+ { }
+
+SPECIALIZE_ROOTING_CONTAINERS(BindingIter, BindingIter);
+SPECIALIZE_ROOTING_CONTAINERS(PositionalFormalParameterIter, BindingIter);
+SPECIALIZE_ROOTING_CONTAINERS(ScopeIter, ScopeIter);
+
+#undef SPECIALIZE_ROOTING_CONTAINERS
+
+//
+// Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>.
+//
+
+template <typename Outer>
+struct ScopeCastOperation
+{
+ template <class U>
+ JS::Handle<U*> as() const {
+ const Outer& self = *static_cast<const Outer*>(this);
+ MOZ_ASSERT_IF(self, self->template is<U>());
+ return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
+ }
+};
+
+template <>
+class RootedBase<Scope*> : public ScopeCastOperation<JS::Rooted<Scope*>>
+{ };
+
+template <>
+class HandleBase<Scope*> : public ScopeCastOperation<JS::Handle<Scope*>>
+{ };
+
+template <>
+class MutableHandleBase<Scope*> : public ScopeCastOperation<JS::MutableHandle<Scope*>>
+{ };
+
+} // namespace js
+
+namespace JS {
+
+template <>
+struct GCPolicy<js::ScopeKind> : public IgnoreGCPolicy<js::ScopeKind>
+{ };
+
+template <typename T>
+struct ScopeDataGCPolicy
+{
+ static T initial() {
+ return nullptr;
+ }
+
+ static void trace(JSTracer* trc, T* vp, const char* name) {
+ if (*vp)
+ (*vp)->trace(trc);
+ }
+};
+
+#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::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::FunctionScope::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::VarScope::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::GlobalScope::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::EvalScope::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::ModuleScope::Data);
+
+#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