#ifndef vm_Scope_h
#define vm_Scope_h
#include "mozilla/Maybe.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Variant.h"
#include <stddef.h>
#include "jsutil.h"
#include "gc/DeletePolicy.h"
#include "gc/Heap.h"
#include "gc/Policy.h"
#include "js/UbiNode.h"
#include "js/UniquePtr.h"
#include "vm/BytecodeUtil.h"
#include "vm/JSObject.h"
#include "vm/Xdr.h"
namespace js {
class BaseScopeData;
class ModuleObject;
class Scope;
enum class BindingKind : uint8_t {
Import,
FormalParameter,
Var,
Let,
Const,
NamedLambdaCallee
};
static inline bool BindingKindIsLexical(BindingKind kind) {
return kind == BindingKind::Let || kind == BindingKind::Const;
}
enum class ScopeKind : uint8_t {
Function,
FunctionBodyVar,
ParameterExpressionVar,
Lexical,
SimpleCatch,
Catch,
NamedLambda,
StrictNamedLambda,
With,
Eval,
StrictEval,
Global,
NonSyntactic,
Module,
WasmInstance,
WasmFunction
};
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::FunctionBodyVar ||
kind == ScopeKind::ParameterExpressionVar;
}
const char* BindingKindString(BindingKind kind);
const char* ScopeKindString(ScopeKind kind);
class BindingName {
uintptr_t bits_;
static const uintptr_t ClosedOverFlag = 0x1;
static const uintptr_t TopLevelFunctionFlag = 0x2;
static const uintptr_t FlagMask = 0x3;
public:
BindingName() : bits_(0) {}
BindingName(JSAtom* name, bool closedOver, bool isTopLevelFunction = false)
: bits_(uintptr_t(name) | (closedOver ? ClosedOverFlag : 0x0) |
(isTopLevelFunction ? TopLevelFunctionFlag : 0x0)) {}
private:
BindingName(JSAtom* name, uint8_t flags) : bits_(uintptr_t(name) | flags) {
static_assert(FlagMask < alignof(JSAtom),
"Flags should fit into unused bits of JSAtom pointer");
MOZ_ASSERT((flags & FlagMask) == flags);
}
public:
static BindingName fromXDR(JSAtom* name, uint8_t flags) {
return BindingName(name, flags);
}
uint8_t flagsForXDR() const { return static_cast<uint8_t>(bits_ & FlagMask); }
JSAtom* name() const { return reinterpret_cast<JSAtom*>(bits_ & ~FlagMask); }
bool closedOver() const { return bits_ & ClosedOverFlag; }
private:
friend class BindingIter;
bool isTopLevelFunction() const { return bits_ & TopLevelFunctionFlag; }
public:
void trace(JSTracer* trc);
};
class TrailingNamesArray {
private:
alignas(BindingName) unsigned char data_[sizeof(BindingName)];
private:
void* ptr() { return data_; }
public:
TrailingNamesArray() = delete;
explicit TrailingNamesArray(size_t nameCount) {
if (nameCount) {
AlwaysPoison(&data_, 0xCC, sizeof(BindingName) * nameCount,
MemCheckKind::MakeUndefined);
}
}
BindingName* start() { return reinterpret_cast<BindingName*>(ptr()); }
BindingName& get(size_t i) { return start()[i]; }
BindingName& operator[](size_t i) { return get(i); }
};
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_);
}
};
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()));
}
};
class Scope : public js::gc::TenuredCell {
friend class GCMarker;
GCPtrScope enclosing_;
ScopeKind kind_;
GCPtrShape environmentShape_;
protected:
BaseScopeData* data_;
Scope(ScopeKind kind, Scope* enclosing, Shape* environmentShape)
: enclosing_(enclosing),
kind_(kind),
environmentShape_(environmentShape),
data_(nullptr) {}
static Scope* create(JSContext* cx, ScopeKind kind, HandleScope enclosing,
HandleShape envShape);
template <typename ConcreteScope>
static ConcreteScope* create(
JSContext* cx, ScopeKind kind, HandleScope enclosing,
HandleShape envShape,
MutableHandle<UniquePtr<typename ConcreteScope::Data>> data);
template <typename ConcreteScope, XDRMode mode>
static XDRResult XDRSizedBindingNames(
XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
MutableHandle<typename ConcreteScope::Data*> data);
Shape* maybeCloneEnvironmentShape(JSContext* cx);
template <typename ConcreteScope>
void initData(MutableHandle<UniquePtr<typename ConcreteScope::Data>> data);
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:
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();
};
class BaseScopeData {};
template <class Data>
inline size_t SizeOfData(uint32_t numBindings) {
static_assert(mozilla::IsBaseOf<BaseScopeData, Data>::value,
"Data must be the correct sort of data, i.e. it must "
"inherit from BaseScopeData");
return sizeof(Data) +
(numBindings ? numBindings - 1 : 0) * sizeof(BindingName);
}
class LexicalScope : public Scope {
friend class Scope;
friend class BindingIter;
friend class GCMarker;
public:
struct Data : public BaseScopeData {
uint32_t constStart = 0;
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static LexicalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
uint32_t firstFrameSlot, HandleScope enclosing);
template <XDRMode mode>
static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind,
HandleScope enclosing, MutableHandleScope scope);
private:
static LexicalScope* createWithData(JSContext* cx, ScopeKind kind,
MutableHandle<UniquePtr<Data>> data,
uint32_t firstFrameSlot,
HandleScope enclosing);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
static uint32_t nextFrameSlot(Scope* start);
public:
uint32_t firstFrameSlot() const;
uint32_t nextFrameSlot() const { return data().nextFrameSlot; }
static Shape* 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;
}
class FunctionScope : public Scope {
friend class GCMarker;
friend class BindingIter;
friend class PositionalFormalParameterIter;
friend class Scope;
static const ScopeKind classScopeKind_ = ScopeKind::Function;
public:
struct Data : public BaseScopeData {
GCPtrFunction canonicalFunction = {};
bool hasParameterExprs = false;
uint16_t nonPositionalFormalStart = 0;
uint16_t varStart = 0;
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
Zone* zone() const;
};
static FunctionScope* create(JSContext* 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 XDRResult XDR(XDRState<mode>* xdr, HandleFunction fun,
HandleScope enclosing, MutableHandleScope scope);
private:
static FunctionScope* createWithData(JSContext* cx,
MutableHandle<UniquePtr<Data>> data,
bool hasParameterExprs,
bool needsEnvironment,
HandleFunction fun,
HandleScope enclosing);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_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 bool isSpecialName(JSContext* cx, JSAtom* name);
static Shape* getEmptyEnvironmentShape(JSContext* cx, bool hasParameterExprs);
};
class VarScope : public Scope {
friend class GCMarker;
friend class BindingIter;
friend class Scope;
public:
struct Data : public BaseScopeData {
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static VarScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
uint32_t firstFrameSlot, bool needsEnvironment,
HandleScope enclosing);
template <XDRMode mode>
static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind,
HandleScope enclosing, MutableHandleScope scope);
private:
static VarScope* createWithData(JSContext* cx, ScopeKind kind,
MutableHandle<UniquePtr<Data>> data,
uint32_t firstFrameSlot,
bool needsEnvironment, HandleScope enclosing);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
public:
uint32_t firstFrameSlot() const;
uint32_t nextFrameSlot() const { return data().nextFrameSlot; }
static Shape* getEmptyEnvironmentShape(JSContext* cx);
};
template <>
inline bool Scope::is<VarScope>() const {
return kind_ == ScopeKind::FunctionBodyVar ||
kind_ == ScopeKind::ParameterExpressionVar;
}
class GlobalScope : public Scope {
friend class Scope;
friend class BindingIter;
friend class GCMarker;
public:
struct Data : BaseScopeData {
uint32_t letStart = 0;
uint32_t constStart = 0;
uint32_t length = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static GlobalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data);
static GlobalScope* createEmpty(JSContext* cx, ScopeKind kind) {
return create(cx, kind, nullptr);
}
static GlobalScope* clone(JSContext* cx, Handle<GlobalScope*> scope,
ScopeKind kind);
template <XDRMode mode>
static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind,
MutableHandleScope scope);
private:
static GlobalScope* createWithData(JSContext* cx, ScopeKind kind,
MutableHandle<UniquePtr<Data>> data);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_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;
}
class WithScope : public Scope {
friend class Scope;
static const ScopeKind classScopeKind_ = ScopeKind::With;
public:
static WithScope* create(JSContext* cx, HandleScope enclosing);
template <XDRMode mode>
static XDRResult XDR(XDRState<mode>* xdr, HandleScope enclosing,
MutableHandleScope scope);
};
class EvalScope : public Scope {
friend class Scope;
friend class BindingIter;
friend class GCMarker;
public:
struct Data : public BaseScopeData {
uint32_t varStart = 0;
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static EvalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
HandleScope enclosing);
template <XDRMode mode>
static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind,
HandleScope enclosing, MutableHandleScope scope);
private:
static EvalScope* createWithData(JSContext* cx, ScopeKind kind,
MutableHandle<UniquePtr<Data>> data,
HandleScope enclosing);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
public:
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(JSContext* cx);
};
template <>
inline bool Scope::is<EvalScope>() const {
return kind_ == ScopeKind::Eval || kind_ == ScopeKind::StrictEval;
}
class ModuleScope : public Scope {
friend class GCMarker;
friend class BindingIter;
friend class Scope;
static const ScopeKind classScopeKind_ = ScopeKind::Module;
public:
struct Data : BaseScopeData {
GCPtr<ModuleObject*> module = {};
uint32_t varStart = 0;
uint32_t letStart = 0;
uint32_t constStart = 0;
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
Zone* zone() const;
};
static ModuleScope* create(JSContext* cx, Handle<Data*> data,
Handle<ModuleObject*> module,
HandleScope enclosing);
private:
static ModuleScope* createWithData(JSContext* cx,
MutableHandle<UniquePtr<Data>> data,
Handle<ModuleObject*> module,
HandleScope enclosing);
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
public:
uint32_t nextFrameSlot() const { return data().nextFrameSlot; }
ModuleObject* module() const { return data().module; }
static Shape* getEmptyEnvironmentShape(JSContext* cx);
};
class WasmInstanceScope : public Scope {
friend class BindingIter;
friend class Scope;
friend class GCMarker;
static const ScopeKind classScopeKind_ = ScopeKind::WasmInstance;
public:
struct Data : public BaseScopeData {
uint32_t memoriesStart = 0;
uint32_t globalsStart = 0;
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
GCPtr<WasmInstanceObject*> instance = {};
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static WasmInstanceScope* create(JSContext* cx, WasmInstanceObject* instance);
private:
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
public:
WasmInstanceObject* instance() const { return data().instance; }
uint32_t memoriesStart() const { return data().memoriesStart; }
uint32_t globalsStart() const { return data().globalsStart; }
uint32_t namesCount() const { return data().length; }
static Shape* getEmptyEnvironmentShape(JSContext* cx);
};
class WasmFunctionScope : public Scope {
friend class BindingIter;
friend class Scope;
friend class GCMarker;
static const ScopeKind classScopeKind_ = ScopeKind::WasmFunction;
public:
struct Data : public BaseScopeData {
uint32_t length = 0;
uint32_t nextFrameSlot = 0;
uint32_t funcIndex = 0;
TrailingNamesArray trailingNames;
explicit Data(size_t nameCount) : trailingNames(nameCount) {}
Data() = delete;
void trace(JSTracer* trc);
};
static WasmFunctionScope* create(JSContext* cx, HandleScope enclosing,
uint32_t funcIndex);
private:
Data& data() { return *static_cast<Data*>(data_); }
const Data& data() const { return *static_cast<Data*>(data_); }
public:
uint32_t funcIndex() const { return data().funcIndex; }
static Shape* getEmptyEnvironmentShape(JSContext* cx);
};
class BindingIter {
protected:
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 length_;
MOZ_INIT_OUTSIDE_CTOR uint32_t index_;
enum Flags : uint8_t {
CannotHaveSlots = 0,
CanHaveArgumentSlots = 1 << 0,
CanHaveFrameSlots = 1 << 1,
CanHaveEnvironmentSlots = 1 << 2,
HasFormalParameterExprs = 1 << 3,
IgnoreDestructuredFormalParameters = 1 << 4,
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 BindingName* names_;
void init(uint32_t positionalFormalStart, uint32_t nonPositionalFormalStart,
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;
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);
void init(WasmInstanceScope::Data& data);
void init(WasmFunctionScope::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()) {
MOZ_ASSERT(kind() != BindingKind::Import);
MOZ_ASSERT(canHaveEnvironmentSlots());
environmentSlot_++;
} else if (canHaveFrameSlots()) {
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); }
explicit BindingIter(WasmFunctionScope::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_ < varStart_) {
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());
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_;
}
void trace(JSTracer* trc);
};
void DumpBindings(JSContext* cx, Scope* scope);
JSAtom* FrameSlotName(JSScript* script, jsbytecode* pc);
class PositionalFormalParameterIter : public BindingIter {
void settle() {
if (index_ >= nonPositionalFormalStart_) {
index_ = length_;
}
}
public:
explicit PositionalFormalParameterIter(Scope* scope);
explicit PositionalFormalParameterIter(JSScript* script);
void operator++(int) {
BindingIter::operator++(1);
settle();
}
bool isDestructured() const { return !name(); }
};
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();
}
Shape* environmentShape() const { return scope()->environmentShape(); }
bool hasSyntacticEnvironment() const;
void trace(JSTracer* trc) {
if (scope_) {
TraceRoot(trc, &scope_, "scope iter scope");
}
}
};
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(); }
Shape* 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); }
};
}
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::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);
DEFINE_SCOPE_DATA_GCPOLICY(js::WasmFunctionScope::Data);
#undef DEFINE_SCOPE_DATA_GCPOLICY
template <>
struct DeletePolicy<js::FunctionScope::Data>
: public js::GCManagedDeletePolicy<js::FunctionScope::Data> {};
template <>
struct DeletePolicy<js::ModuleScope::Data>
: public js::GCManagedDeletePolicy<js::ModuleScope::Data> {};
template <>
struct DeletePolicy<js::WasmInstanceScope::Data>
: public js::GCManagedDeletePolicy<js::WasmInstanceScope::Data> {};
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[];
};
} }
#endif