diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/vm/Scope.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-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.h | 1451 |
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 |