summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Scope.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Scope.cpp')
-rw-r--r--js/src/vm/Scope.cpp1460
1 files changed, 1460 insertions, 0 deletions
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp
new file mode 100644
index 000000000..112b34586
--- /dev/null
+++ b/js/src/vm/Scope.cpp
@@ -0,0 +1,1460 @@
+/* -*- 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/. */
+
+#include "vm/Scope.h"
+
+#include "mozilla/ScopeExit.h"
+
+#include "jsscript.h"
+#include "builtin/ModuleObject.h"
+#include "gc/Allocator.h"
+#include "vm/EnvironmentObject.h"
+#include "vm/Runtime.h"
+
+#include "vm/Shape-inl.h"
+
+using namespace js;
+
+using mozilla::Maybe;
+using mozilla::MakeScopeExit;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::Some;
+
+const char*
+js::BindingKindString(BindingKind kind)
+{
+ switch (kind) {
+ case BindingKind::Import:
+ return "import";
+ case BindingKind::FormalParameter:
+ return "formal parameter";
+ case BindingKind::Var:
+ return "var";
+ case BindingKind::Let:
+ return "let";
+ case BindingKind::Const:
+ return "const";
+ case BindingKind::NamedLambdaCallee:
+ return "named lambda callee";
+ }
+ MOZ_CRASH("Bad BindingKind");
+}
+
+const char*
+js::ScopeKindString(ScopeKind kind)
+{
+ switch (kind) {
+ case ScopeKind::Function:
+ return "function";
+ case ScopeKind::FunctionBodyVar:
+ return "function body var";
+ case ScopeKind::ParameterExpressionVar:
+ return "parameter expression var";
+ case ScopeKind::Lexical:
+ return "lexical";
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ return "catch";
+ case ScopeKind::NamedLambda:
+ return "named lambda";
+ case ScopeKind::StrictNamedLambda:
+ return "strict named lambda";
+ case ScopeKind::With:
+ return "with";
+ case ScopeKind::Eval:
+ return "eval";
+ case ScopeKind::StrictEval:
+ return "strict eval";
+ case ScopeKind::Global:
+ return "global";
+ case ScopeKind::NonSyntactic:
+ return "non-syntactic";
+ case ScopeKind::Module:
+ return "module";
+ }
+ MOZ_CRASH("Bad ScopeKind");
+}
+
+static Shape*
+EmptyEnvironmentShape(ExclusiveContext* cx, const Class* cls, uint32_t numSlots,
+ uint32_t baseShapeFlags)
+{
+ // Put as many slots into the object header as possible.
+ uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
+ return EmptyShape::getInitialShape(cx, cls, TaggedProto(nullptr), numFixed,
+ baseShapeFlags);
+}
+
+static Shape*
+NextEnvironmentShape(ExclusiveContext* cx, HandleAtom name, BindingKind bindKind, uint32_t slot,
+ StackBaseShape& stackBase, HandleShape shape)
+{
+ UnownedBaseShape* base = BaseShape::getUnowned(cx, stackBase);
+ if (!base)
+ return nullptr;
+
+ unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE;
+ switch (bindKind) {
+ case BindingKind::Const:
+ case BindingKind::NamedLambdaCallee:
+ attrs |= JSPROP_READONLY;
+ break;
+ default:
+ break;
+ }
+
+ jsid id = NameToId(name->asPropertyName());
+ Rooted<StackShape> child(cx, StackShape(base, id, slot, attrs, 0));
+ return cx->zone()->propertyTree.getChild(cx, shape, child);
+}
+
+static Shape*
+CreateEnvironmentShape(ExclusiveContext* cx, BindingIter& bi, const Class* cls,
+ uint32_t numSlots, uint32_t baseShapeFlags)
+{
+ RootedShape shape(cx, EmptyEnvironmentShape(cx, cls, numSlots, baseShapeFlags));
+ if (!shape)
+ return nullptr;
+
+ RootedAtom name(cx);
+ StackBaseShape stackBase(cx, cls, baseShapeFlags);
+ for (; bi; bi++) {
+ BindingLocation loc = bi.location();
+ if (loc.kind() == BindingLocation::Kind::Environment) {
+ name = bi.name();
+ shape = NextEnvironmentShape(cx, name, bi.kind(), loc.slot(), stackBase, shape);
+ if (!shape)
+ return nullptr;
+ }
+ }
+
+ return shape;
+}
+
+template <typename ConcreteScope>
+static UniquePtr<typename ConcreteScope::Data>
+CopyScopeData(ExclusiveContext* cx, Handle<typename ConcreteScope::Data*> data)
+{
+ size_t dataSize = ConcreteScope::sizeOfData(data->length);
+ size_t headerSize = sizeof(typename ConcreteScope::Data);
+ MOZ_ASSERT(dataSize >= headerSize);
+ size_t extraSize = dataSize - headerSize;
+
+ uint8_t* copyBytes = cx->zone()->pod_malloc<uint8_t>(dataSize);
+ if (!copyBytes) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ auto dataCopy = reinterpret_cast<typename ConcreteScope::Data*>(copyBytes);
+ new (dataCopy) typename ConcreteScope::Data(*data);
+
+ uint8_t* extra = reinterpret_cast<uint8_t*>(data.get()) + headerSize;
+ uint8_t* extraCopy = copyBytes + headerSize;
+
+ mozilla::PodCopy<uint8_t>(extraCopy, extra, extraSize);
+ return UniquePtr<typename ConcreteScope::Data>(dataCopy);
+}
+
+template <typename ConcreteScope>
+static bool
+PrepareScopeData(ExclusiveContext* cx, BindingIter& bi, Handle<UniquePtr<typename ConcreteScope::Data>> data,
+ const Class* cls, uint32_t baseShapeFlags, MutableHandleShape envShape)
+{
+ // Copy a fresh BindingIter for use below.
+ BindingIter freshBi(bi);
+
+ // Iterate through all bindings. This counts the number of environment
+ // slots needed and computes the maximum frame slot.
+ while (bi)
+ bi++;
+ data->nextFrameSlot = bi.canHaveFrameSlots() ? bi.nextFrameSlot() : LOCALNO_LIMIT;
+
+ // Make a new environment shape if any environment slots were used.
+ if (bi.nextEnvironmentSlot() == JSSLOT_FREE(cls)) {
+ envShape.set(nullptr);
+ } else {
+ envShape.set(CreateEnvironmentShape(cx, freshBi, cls, bi.nextEnvironmentSlot(),
+ baseShapeFlags));
+ if (!envShape)
+ return false;
+ }
+
+ return true;
+}
+
+template <typename ConcreteScope>
+static UniquePtr<typename ConcreteScope::Data>
+NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
+{
+ uint8_t* bytes = cx->zone()->pod_calloc<uint8_t>(ConcreteScope::sizeOfData(length));
+ if (!bytes)
+ ReportOutOfMemory(cx);
+ auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes);
+ if (data)
+ new (data) typename ConcreteScope::Data();
+ return UniquePtr<typename ConcreteScope::Data>(data);
+}
+
+static bool
+XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
+{
+ JSContext* cx = xdr->cx();
+
+ RootedAtom atom(cx, bindingName->name());
+ bool hasAtom = !!atom;
+
+ uint8_t u8 = uint8_t(hasAtom << 1) | uint8_t(bindingName->closedOver());
+ if (!xdr->codeUint8(&u8))
+ return false;
+
+ if (atom && !XDRAtom(xdr, &atom))
+ return false;
+
+ return true;
+}
+
+static bool
+XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
+{
+ JSContext* cx = xdr->cx();
+
+ uint8_t u8;
+ if (!xdr->codeUint8(&u8))
+ return false;
+
+ bool closedOver = u8 & 1;
+ bool hasAtom = u8 >> 1;
+
+ RootedAtom atom(cx);
+ if (hasAtom && !XDRAtom(xdr, &atom))
+ return false;
+
+ *bindingName = BindingName(atom, closedOver);
+
+ return true;
+}
+
+template <typename ConcreteScopeData>
+static void
+DeleteScopeData(ConcreteScopeData* data)
+{
+ // Some scope Data classes have GCManagedDeletePolicy because then contain
+ // GCPtrs. Dispose of them in the appropriate way.
+ JS::DeletePolicy<ConcreteScopeData>()(data);
+}
+
+template <typename ConcreteScope, XDRMode mode>
+/* static */ bool
+Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
+ MutableHandle<typename ConcreteScope::Data*> data)
+{
+ MOZ_ASSERT(!data);
+
+ JSContext* cx = xdr->cx();
+
+ uint32_t length;
+ if (mode == XDR_ENCODE)
+ length = scope->data().length;
+ if (!xdr->codeUint32(&length))
+ return false;
+
+ if (mode == XDR_ENCODE) {
+ data.set(&scope->data());
+ } else {
+ data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
+ if (!data)
+ return false;
+ data->length = length;
+ }
+
+ for (uint32_t i = 0; i < length; i++) {
+ if (!XDRBindingName(xdr, &data->names[i])) {
+ if (mode == XDR_DECODE) {
+ DeleteScopeData(data.get());
+ data.set(nullptr);
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */ Scope*
+Scope::create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape)
+{
+ Scope* scope = Allocate<Scope>(cx);
+ if (scope)
+ new (scope) Scope(kind, enclosing, envShape);
+ return scope;
+}
+
+template <typename T, typename D>
+/* static */ Scope*
+Scope::create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing,
+ HandleShape envShape, mozilla::UniquePtr<T, D> data)
+{
+ Scope* scope = create(cx, kind, enclosing, envShape);
+ if (!scope)
+ return nullptr;
+
+ // It is an invariant that all Scopes that have data (currently, all
+ // ScopeKinds except With) must have non-null data.
+ MOZ_ASSERT(data);
+ scope->initData(Move(data));
+
+ return scope;
+}
+
+uint32_t
+Scope::chainLength() const
+{
+ uint32_t length = 0;
+ for (ScopeIter si(const_cast<Scope*>(this)); si; si++)
+ length++;
+ return length;
+}
+
+uint32_t
+Scope::environmentChainLength() const
+{
+ uint32_t length = 0;
+ for (ScopeIter si(const_cast<Scope*>(this)); si; si++) {
+ if (si.hasSyntacticEnvironment())
+ length++;
+ }
+ return length;
+}
+
+Shape*
+Scope::maybeCloneEnvironmentShape(JSContext* cx)
+{
+ // Clone the environment shape if cloning into a different zone.
+ if (environmentShape_ && environmentShape_->zoneFromAnyThread() != cx->zone()) {
+ BindingIter bi(this);
+ return CreateEnvironmentShape(cx, bi,
+ environmentShape_->getObjectClass(),
+ environmentShape_->slotSpan(),
+ environmentShape_->getObjectFlags());
+ }
+ return environmentShape_;
+}
+
+/* static */ Scope*
+Scope::clone(JSContext* cx, HandleScope scope, HandleScope enclosing)
+{
+ RootedShape envShape(cx);
+ if (scope->environmentShape()) {
+ envShape = scope->maybeCloneEnvironmentShape(cx);
+ if (!envShape)
+ return nullptr;
+ }
+
+ switch (scope->kind_) {
+ case ScopeKind::Function:
+ MOZ_CRASH("Use FunctionScope::clone.");
+ break;
+
+ case ScopeKind::FunctionBodyVar:
+ case ScopeKind::ParameterExpressionVar: {
+ Rooted<VarScope::Data*> original(cx, &scope->as<VarScope>().data());
+ UniquePtr<VarScope::Data> dataClone = CopyScopeData<VarScope>(cx, original);
+ if (!dataClone)
+ return nullptr;
+ return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
+ }
+
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda: {
+ Rooted<LexicalScope::Data*> original(cx, &scope->as<LexicalScope>().data());
+ UniquePtr<LexicalScope::Data> dataClone = CopyScopeData<LexicalScope>(cx, original);
+ if (!dataClone)
+ return nullptr;
+ return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
+ }
+
+ case ScopeKind::With:
+ return create(cx, scope->kind_, enclosing, envShape);
+
+ case ScopeKind::Eval:
+ case ScopeKind::StrictEval: {
+ Rooted<EvalScope::Data*> original(cx, &scope->as<EvalScope>().data());
+ UniquePtr<EvalScope::Data> dataClone = CopyScopeData<EvalScope>(cx, original);
+ if (!dataClone)
+ return nullptr;
+ return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
+ }
+
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic:
+ MOZ_CRASH("Use GlobalScope::clone.");
+ break;
+
+ case ScopeKind::Module:
+ MOZ_CRASH("NYI");
+ break;
+ }
+
+ return nullptr;
+}
+
+void
+Scope::finalize(FreeOp* fop)
+{
+ MOZ_ASSERT(CurrentThreadIsGCSweeping());
+ if (data_) {
+ // We don't need to call the destructors for any GCPtrs in Data because
+ // this only happens during a GC.
+ fop->free_(reinterpret_cast<void*>(data_));
+ data_ = 0;
+ }
+}
+
+size_t
+Scope::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
+{
+ if (data_)
+ return mallocSizeOf(reinterpret_cast<void*>(data_));
+ return 0;
+}
+
+void
+Scope::dump()
+{
+ for (ScopeIter si(this); si; si++) {
+ fprintf(stderr, "%s [%p]", ScopeKindString(si.kind()), si.scope());
+ if (si.scope()->enclosing())
+ fprintf(stderr, " -> ");
+ }
+ fprintf(stderr, "\n");
+}
+
+uint32_t
+LexicalScope::firstFrameSlot() const
+{
+ switch (kind()) {
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ // For intra-frame scopes, find the enclosing scope's next frame slot.
+ return nextFrameSlot(enclosing());
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda:
+ // Named lambda scopes cannot have frame slots.
+ return LOCALNO_LIMIT;
+ default:
+ // Otherwise start at 0.
+ break;
+ }
+ return 0;
+}
+
+/* static */ uint32_t
+LexicalScope::nextFrameSlot(Scope* scope)
+{
+ for (ScopeIter si(scope); si; si++) {
+ switch (si.kind()) {
+ case ScopeKind::Function:
+ return si.scope()->as<FunctionScope>().nextFrameSlot();
+ case ScopeKind::FunctionBodyVar:
+ case ScopeKind::ParameterExpressionVar:
+ return si.scope()->as<VarScope>().nextFrameSlot();
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ return si.scope()->as<LexicalScope>().nextFrameSlot();
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda:
+ // Named lambda scopes cannot have frame slots.
+ return 0;
+ case ScopeKind::With:
+ continue;
+ case ScopeKind::Eval:
+ case ScopeKind::StrictEval:
+ return si.scope()->as<EvalScope>().nextFrameSlot();
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic:
+ return 0;
+ case ScopeKind::Module:
+ return si.scope()->as<ModuleScope>().nextFrameSlot();
+ }
+ }
+ MOZ_CRASH("Not an enclosing intra-frame Scope");
+}
+
+/* static */ LexicalScope*
+LexicalScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
+ uint32_t firstFrameSlot, HandleScope enclosing)
+{
+ MOZ_ASSERT(data, "LexicalScopes should not be created if there are no bindings.");
+
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ Rooted<UniquePtr<Data>> copy(cx, CopyScopeData<LexicalScope>(cx, data));
+ if (!copy)
+ return nullptr;
+
+ return createWithData(cx, kind, &copy, firstFrameSlot, enclosing);
+}
+
+/* static */ LexicalScope*
+LexicalScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
+ uint32_t firstFrameSlot, HandleScope enclosing)
+{
+ bool isNamedLambda = kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda;
+
+ MOZ_ASSERT_IF(!isNamedLambda && firstFrameSlot != 0,
+ firstFrameSlot == nextFrameSlot(enclosing));
+ MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT);
+
+ RootedShape envShape(cx);
+ BindingIter bi(*data, firstFrameSlot, isNamedLambda);
+ if (!PrepareScopeData<LexicalScope>(cx, bi, data, &LexicalEnvironmentObject::class_,
+ BaseShape::NOT_EXTENSIBLE | BaseShape::DELEGATE, &envShape))
+ {
+ return nullptr;
+ }
+
+ Scope* scope = Scope::create(cx, kind, enclosing, envShape, Move(data.get()));
+ if (!scope)
+ return nullptr;
+ MOZ_ASSERT(scope->as<LexicalScope>().firstFrameSlot() == firstFrameSlot);
+ return &scope->as<LexicalScope>();
+}
+
+/* static */ Shape*
+LexicalScope::getEmptyExtensibleEnvironmentShape(ExclusiveContext* cx)
+{
+ const Class* cls = &LexicalEnvironmentObject::class_;
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), BaseShape::DELEGATE);
+}
+
+template <XDRMode mode>
+/* static */ bool
+LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope)
+{
+ JSContext* cx = xdr->cx();
+
+ Rooted<Data*> data(cx);
+ if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
+ return false;
+
+ {
+ Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+ if (mode == XDR_DECODE)
+ uniqueData.emplace(cx, data);
+
+ uint32_t firstFrameSlot;
+ uint32_t nextFrameSlot;
+ if (mode == XDR_ENCODE) {
+ firstFrameSlot = scope->as<LexicalScope>().firstFrameSlot();
+ nextFrameSlot = data->nextFrameSlot;
+ }
+
+ if (!xdr->codeUint32(&data->constStart))
+ return false;
+ if (!xdr->codeUint32(&firstFrameSlot))
+ return false;
+ if (!xdr->codeUint32(&nextFrameSlot))
+ return false;
+
+ if (mode == XDR_DECODE) {
+ scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, enclosing));
+ if (!scope)
+ return false;
+
+ // nextFrameSlot is used only for this correctness check.
+ MOZ_ASSERT(nextFrameSlot == scope->as<LexicalScope>().data().nextFrameSlot);
+ }
+ }
+
+ return true;
+}
+
+template
+/* static */ bool
+LexicalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+template
+/* static */ bool
+LexicalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+static inline uint32_t
+FunctionScopeEnvShapeFlags(bool hasParameterExprs)
+{
+ if (hasParameterExprs)
+ return BaseShape::DELEGATE;
+ return BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
+}
+
+/* static */ FunctionScope*
+FunctionScope::create(ExclusiveContext* cx, Handle<Data*> dataArg,
+ bool hasParameterExprs, bool needsEnvironment,
+ HandleFunction fun, HandleScope enclosing)
+{
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<FunctionScope>(cx, dataArg)
+ : NewEmptyScopeData<FunctionScope>(cx));
+ if (!data)
+ return nullptr;
+
+ return createWithData(cx, &data, hasParameterExprs, needsEnvironment, fun, enclosing);
+}
+
+/* static */ FunctionScope*
+FunctionScope::createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
+ bool hasParameterExprs, bool needsEnvironment,
+ HandleFunction fun, HandleScope enclosing)
+{
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(fun->isTenured());
+
+ // FunctionScope::Data has GCManagedDeletePolicy because it contains a
+ // GCPtr. Destruction of |data| below may trigger calls into the GC.
+ Rooted<FunctionScope*> funScope(cx);
+
+ {
+ RootedShape envShape(cx);
+
+ BindingIter bi(*data, hasParameterExprs);
+ uint32_t shapeFlags = FunctionScopeEnvShapeFlags(hasParameterExprs);
+ if (!PrepareScopeData<FunctionScope>(cx, bi, data, &CallObject::class_, shapeFlags,
+ &envShape))
+ {
+ return nullptr;
+ }
+
+ data->hasParameterExprs = hasParameterExprs;
+ data->canonicalFunction.init(fun);
+
+ // An environment may be needed regardless of existence of any closed over
+ // bindings:
+ // - Extensible scopes (i.e., due to direct eval)
+ // - Needing a home object
+ // - Being a derived class constructor
+ // - Being a generator
+ if (!envShape && needsEnvironment) {
+ envShape = getEmptyEnvironmentShape(cx, hasParameterExprs);
+ if (!envShape)
+ return nullptr;
+ }
+
+ Scope* scope = Scope::create(cx, ScopeKind::Function, enclosing, envShape);
+ if (!scope)
+ return nullptr;
+
+ funScope = &scope->as<FunctionScope>();
+ funScope->initData(Move(data.get()));
+ }
+
+ return funScope;
+}
+
+JSScript*
+FunctionScope::script() const
+{
+ return canonicalFunction()->nonLazyScript();
+}
+
+/* static */ Shape*
+FunctionScope::getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs)
+{
+ const Class* cls = &CallObject::class_;
+ uint32_t shapeFlags = FunctionScopeEnvShapeFlags(hasParameterExprs);
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), shapeFlags);
+}
+
+/* static */ FunctionScope*
+FunctionScope::clone(JSContext* cx, Handle<FunctionScope*> scope, HandleFunction fun,
+ HandleScope enclosing)
+{
+ MOZ_ASSERT(fun != scope->canonicalFunction());
+
+ // FunctionScope::Data has GCManagedDeletePolicy because it contains a
+ // GCPtr. Destruction of |dataClone| below may trigger calls into the GC.
+ Rooted<FunctionScope*> funScopeClone(cx);
+
+ {
+ RootedShape envShape(cx);
+ if (scope->environmentShape()) {
+ envShape = scope->maybeCloneEnvironmentShape(cx);
+ if (!envShape)
+ return nullptr;
+ }
+
+ Rooted<Data*> dataOriginal(cx, &scope->as<FunctionScope>().data());
+ Rooted<UniquePtr<Data>> dataClone(cx, CopyScopeData<FunctionScope>(cx, dataOriginal));
+ if (!dataClone)
+ return nullptr;
+
+ dataClone->canonicalFunction.init(fun);
+
+ Scope* scopeClone = Scope::create(cx, scope->kind(), enclosing, envShape);
+ if (!scopeClone)
+ return nullptr;
+
+ funScopeClone = &scopeClone->as<FunctionScope>();
+ funScopeClone->initData(Move(dataClone.get()));
+ }
+
+ return funScopeClone;
+}
+
+template <XDRMode mode>
+/* static */ bool
+FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
+ MutableHandleScope scope)
+{
+ JSContext* cx = xdr->cx();
+ Rooted<Data*> data(cx);
+ if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
+ return false;
+
+ {
+ Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+ if (mode == XDR_DECODE)
+ uniqueData.emplace(cx, data);
+
+ uint8_t needsEnvironment;
+ uint8_t hasParameterExprs;
+ uint32_t nextFrameSlot;
+ if (mode == XDR_ENCODE) {
+ needsEnvironment = scope->hasEnvironment();
+ hasParameterExprs = data->hasParameterExprs;
+ nextFrameSlot = data->nextFrameSlot;
+ }
+ if (!xdr->codeUint8(&needsEnvironment))
+ return false;
+ if (!xdr->codeUint8(&hasParameterExprs))
+ return false;
+ if (!xdr->codeUint16(&data->nonPositionalFormalStart))
+ return false;
+ if (!xdr->codeUint16(&data->varStart))
+ return false;
+ if (!xdr->codeUint32(&nextFrameSlot))
+ return false;
+
+ if (mode == XDR_DECODE) {
+ if (!data->length) {
+ MOZ_ASSERT(!data->nonPositionalFormalStart);
+ MOZ_ASSERT(!data->varStart);
+ MOZ_ASSERT(!data->nextFrameSlot);
+ }
+
+ scope.set(createWithData(cx, &uniqueData.ref(), hasParameterExprs, needsEnvironment, fun,
+ enclosing));
+ if (!scope)
+ return false;
+
+ // nextFrameSlot is used only for this correctness check.
+ MOZ_ASSERT(nextFrameSlot == scope->as<FunctionScope>().data().nextFrameSlot);
+ }
+ }
+
+ return true;
+}
+
+template
+/* static */ bool
+FunctionScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleFunction fun, HandleScope enclosing,
+ MutableHandleScope scope);
+
+template
+/* static */ bool
+FunctionScope::XDR(XDRState<XDR_DECODE>* xdr, HandleFunction fun, HandleScope enclosing,
+ MutableHandleScope scope);
+
+static const uint32_t VarScopeEnvShapeFlags =
+ BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
+
+static UniquePtr<VarScope::Data>
+NewEmptyVarScopeData(ExclusiveContext* cx, uint32_t firstFrameSlot)
+{
+ UniquePtr<VarScope::Data> data(NewEmptyScopeData<VarScope>(cx));
+ if (data)
+ data->nextFrameSlot = firstFrameSlot;
+
+ return data;
+}
+
+/* static */ VarScope*
+VarScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> dataArg,
+ uint32_t firstFrameSlot, bool needsEnvironment, HandleScope enclosing)
+{
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<VarScope>(cx, dataArg)
+ : NewEmptyVarScopeData(cx, firstFrameSlot));
+ if (!data)
+ return nullptr;
+
+ return createWithData(cx, kind, &data, firstFrameSlot, needsEnvironment, enclosing);
+}
+
+/* static */ VarScope*
+VarScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
+ uint32_t firstFrameSlot, bool needsEnvironment, HandleScope enclosing)
+{
+ MOZ_ASSERT(data);
+
+ RootedShape envShape(cx);
+ BindingIter bi(*data, firstFrameSlot);
+ if (!PrepareScopeData<VarScope>(cx, bi, data, &VarEnvironmentObject::class_, VarScopeEnvShapeFlags,
+ &envShape))
+ {
+ return nullptr;
+ }
+
+ // An environment may be needed regardless of existence of any closed over
+ // bindings:
+ // - Extensible scopes (i.e., due to direct eval)
+ // - Being a generator
+ if (!envShape && needsEnvironment) {
+ envShape = getEmptyEnvironmentShape(cx);
+ if (!envShape)
+ return nullptr;
+ }
+
+ Scope* scope = Scope::create(cx, kind, enclosing, envShape, Move(data.get()));
+ if (!scope)
+ return nullptr;
+ return &scope->as<VarScope>();
+}
+
+/* static */ Shape*
+VarScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
+{
+ const Class* cls = &VarEnvironmentObject::class_;
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), VarScopeEnvShapeFlags);
+}
+
+uint32_t
+VarScope::firstFrameSlot() const
+{
+ if (enclosing()->is<FunctionScope>())
+ return enclosing()->as<FunctionScope>().nextFrameSlot();
+ return 0;
+}
+
+template <XDRMode mode>
+/* static */ bool
+VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope)
+{
+ JSContext* cx = xdr->cx();
+ Rooted<Data*> data(cx);
+ if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
+ return false;
+
+ {
+ Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+ if (mode == XDR_DECODE)
+ uniqueData.emplace(cx, data);
+
+ uint8_t needsEnvironment;
+ uint32_t firstFrameSlot;
+ uint32_t nextFrameSlot;
+ if (mode == XDR_ENCODE) {
+ needsEnvironment = scope->hasEnvironment();
+ firstFrameSlot = scope->as<VarScope>().firstFrameSlot();
+ nextFrameSlot = data->nextFrameSlot;
+ }
+ if (!xdr->codeUint8(&needsEnvironment))
+ return false;
+ if (!xdr->codeUint32(&firstFrameSlot))
+ return false;
+ if (!xdr->codeUint32(&nextFrameSlot))
+ return false;
+
+ if (mode == XDR_DECODE) {
+ if (!data->length) {
+ MOZ_ASSERT(!data->nextFrameSlot);
+ }
+
+ scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, needsEnvironment,
+ enclosing));
+ if (!scope)
+ return false;
+
+ // nextFrameSlot is used only for this correctness check.
+ MOZ_ASSERT(nextFrameSlot == scope->as<VarScope>().data().nextFrameSlot);
+ }
+ }
+
+ return true;
+}
+
+template
+/* static */ bool
+VarScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+template
+/* static */ bool
+VarScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+/* static */ GlobalScope*
+GlobalScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> dataArg)
+{
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<GlobalScope>(cx, dataArg)
+ : NewEmptyScopeData<GlobalScope>(cx));
+ if (!data)
+ return nullptr;
+
+ return createWithData(cx, kind, &data);
+}
+
+/* static */ GlobalScope*
+GlobalScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data)
+{
+ MOZ_ASSERT(data);
+
+ // The global scope has no environment shape. Its environment is the
+ // global lexical scope and the global object or non-syntactic objects
+ // created by embedding, all of which are not only extensible but may
+ // have names on them deleted.
+ Scope* scope = Scope::create(cx, kind, nullptr, nullptr, Move(data.get()));
+ if (!scope)
+ return nullptr;
+ return &scope->as<GlobalScope>();
+}
+
+/* static */ GlobalScope*
+GlobalScope::clone(JSContext* cx, Handle<GlobalScope*> scope, ScopeKind kind)
+{
+ Rooted<Data*> dataOriginal(cx, &scope->as<GlobalScope>().data());
+ Rooted<UniquePtr<Data>> dataClone(cx, CopyScopeData<GlobalScope>(cx, dataOriginal));
+ if (!dataClone)
+ return nullptr;
+
+ Scope* scopeClone = Scope::create(cx, kind, nullptr, nullptr, Move(dataClone.get()));
+ if (!scopeClone)
+ return nullptr;
+ return &scopeClone->as<GlobalScope>();
+}
+
+template <XDRMode mode>
+/* static */ bool
+GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
+{
+ MOZ_ASSERT((mode == XDR_DECODE) == !scope);
+
+ JSContext* cx = xdr->cx();
+ Rooted<Data*> data(cx);
+ if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
+ return false;
+
+ {
+ Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+ if (mode == XDR_DECODE)
+ uniqueData.emplace(cx, data);
+
+ if (!xdr->codeUint32(&data->letStart))
+ return false;
+ if (!xdr->codeUint32(&data->constStart))
+ return false;
+
+ if (mode == XDR_DECODE) {
+ if (!data->length) {
+ MOZ_ASSERT(!data->letStart);
+ MOZ_ASSERT(!data->constStart);
+ }
+
+ scope.set(createWithData(cx, kind, &uniqueData.ref()));
+ if (!scope)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template
+/* static */ bool
+GlobalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, MutableHandleScope scope);
+
+template
+/* static */ bool
+GlobalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, MutableHandleScope scope);
+
+/* static */ WithScope*
+WithScope::create(ExclusiveContext* cx, HandleScope enclosing)
+{
+ Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr);
+ return static_cast<WithScope*>(scope);
+}
+
+static const uint32_t EvalScopeEnvShapeFlags =
+ BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
+
+/* static */ EvalScope*
+EvalScope::create(ExclusiveContext* cx, ScopeKind scopeKind, Handle<Data*> dataArg,
+ HandleScope enclosing)
+{
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<EvalScope>(cx, dataArg)
+ : NewEmptyScopeData<EvalScope>(cx));
+ if (!data)
+ return nullptr;
+
+ return createWithData(cx, scopeKind, &data, enclosing);
+}
+
+/* static */ EvalScope*
+EvalScope::createWithData(ExclusiveContext* cx, ScopeKind scopeKind, MutableHandle<UniquePtr<Data>> data,
+ HandleScope enclosing)
+{
+ MOZ_ASSERT(data);
+
+ RootedShape envShape(cx);
+ if (scopeKind == ScopeKind::StrictEval) {
+ BindingIter bi(*data, true);
+ if (!PrepareScopeData<EvalScope>(cx, bi, data, &VarEnvironmentObject::class_,
+ EvalScopeEnvShapeFlags, &envShape))
+ {
+ return nullptr;
+ }
+ }
+
+ // Strict eval and direct eval in parameter expressions always get their own
+ // var environment even if there are no bindings.
+ if (!envShape && scopeKind == ScopeKind::StrictEval) {
+ envShape = getEmptyEnvironmentShape(cx);
+ if (!envShape)
+ return nullptr;
+ }
+
+ Scope* scope = Scope::create(cx, scopeKind, enclosing, envShape, Move(data.get()));
+ if (!scope)
+ return nullptr;
+ return &scope->as<EvalScope>();
+}
+
+/* static */ Scope*
+EvalScope::nearestVarScopeForDirectEval(Scope* scope)
+{
+ for (ScopeIter si(scope); si; si++) {
+ switch (si.kind()) {
+ case ScopeKind::Function:
+ case ScopeKind::FunctionBodyVar:
+ case ScopeKind::ParameterExpressionVar:
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic:
+ return scope;
+ default:
+ break;
+ }
+ }
+ return nullptr;
+}
+
+/* static */ Shape*
+EvalScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
+{
+ const Class* cls = &VarEnvironmentObject::class_;
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), EvalScopeEnvShapeFlags);
+}
+
+template <XDRMode mode>
+/* static */ bool
+EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope)
+{
+ JSContext* cx = xdr->cx();
+ Rooted<Data*> data(cx);
+
+ {
+ Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+ if (mode == XDR_DECODE)
+ uniqueData.emplace(cx, data);
+
+ if (!XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data))
+ return false;
+
+ if (mode == XDR_DECODE) {
+ if (!data->length)
+ MOZ_ASSERT(!data->nextFrameSlot);
+
+ scope.set(createWithData(cx, kind, &uniqueData.ref(), enclosing));
+ if (!scope)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template
+/* static */ bool
+EvalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+template
+/* static */ bool
+EvalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
+ MutableHandleScope scope);
+
+static const uint32_t ModuleScopeEnvShapeFlags =
+ BaseShape::NOT_EXTENSIBLE | BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
+
+/* static */ ModuleScope*
+ModuleScope::create(ExclusiveContext* cx, Handle<Data*> dataArg,
+ HandleModuleObject module, HandleScope enclosing)
+{
+ Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<ModuleScope>(cx, dataArg)
+ : NewEmptyScopeData<ModuleScope>(cx));
+ if (!data)
+ return nullptr;
+
+ return createWithData(cx, &data, module, enclosing);
+}
+
+/* static */ ModuleScope*
+ModuleScope::createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
+ HandleModuleObject module, HandleScope enclosing)
+{
+ MOZ_ASSERT(data);
+ MOZ_ASSERT(enclosing->is<GlobalScope>());
+
+ // ModuleScope::Data has GCManagedDeletePolicy because it contains a
+ // GCPtr. Destruction of |copy| below may trigger calls into the GC.
+ Rooted<ModuleScope*> moduleScope(cx);
+
+ {
+ // The data that's passed in is be from the frontend and is LifoAlloc'd.
+ // Copy it now that we're creating a permanent VM scope.
+ RootedShape envShape(cx);
+ BindingIter bi(*data);
+ if (!PrepareScopeData<ModuleScope>(cx, bi, data, &ModuleEnvironmentObject::class_,
+ ModuleScopeEnvShapeFlags, &envShape))
+ {
+ return nullptr;
+ }
+
+ // Modules always need an environment object for now.
+ if (!envShape) {
+ envShape = getEmptyEnvironmentShape(cx);
+ if (!envShape)
+ return nullptr;
+ }
+
+ Scope* scope = Scope::create(cx, ScopeKind::Module, enclosing, envShape);
+ if (!scope)
+ return nullptr;
+
+ data->module.init(module);
+
+ moduleScope = &scope->as<ModuleScope>();
+ moduleScope->initData(Move(data.get()));
+ }
+
+ return moduleScope;
+}
+
+/* static */ Shape*
+ModuleScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
+{
+ const Class* cls = &ModuleEnvironmentObject::class_;
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), ModuleScopeEnvShapeFlags);
+}
+
+JSScript*
+ModuleScope::script() const
+{
+ return module()->script();
+}
+
+ScopeIter::ScopeIter(JSScript* script)
+ : scope_(script->bodyScope())
+{ }
+
+bool
+ScopeIter::hasSyntacticEnvironment() const
+{
+ return scope()->hasEnvironment() && scope()->kind() != ScopeKind::NonSyntactic;
+}
+
+BindingIter::BindingIter(Scope* scope)
+{
+ switch (scope->kind()) {
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ init(scope->as<LexicalScope>().data(),
+ scope->as<LexicalScope>().firstFrameSlot(), 0);
+ break;
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda:
+ init(scope->as<LexicalScope>().data(), LOCALNO_LIMIT, IsNamedLambda);
+ break;
+ case ScopeKind::With:
+ // With scopes do not have bindings.
+ index_ = length_ = 0;
+ MOZ_ASSERT(done());
+ break;
+ case ScopeKind::Function: {
+ uint8_t flags = IgnoreDestructuredFormalParameters;
+ if (scope->as<FunctionScope>().hasParameterExprs())
+ flags |= HasFormalParameterExprs;
+ init(scope->as<FunctionScope>().data(), flags);
+ break;
+ }
+ case ScopeKind::FunctionBodyVar:
+ case ScopeKind::ParameterExpressionVar:
+ init(scope->as<VarScope>().data(),
+ scope->as<VarScope>().firstFrameSlot());
+ break;
+ case ScopeKind::Eval:
+ case ScopeKind::StrictEval:
+ init(scope->as<EvalScope>().data(), scope->kind() == ScopeKind::StrictEval);
+ break;
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic:
+ init(scope->as<GlobalScope>().data());
+ break;
+ case ScopeKind::Module:
+ init(scope->as<ModuleScope>().data());
+ break;
+ }
+}
+
+BindingIter::BindingIter(JSScript* script)
+ : BindingIter(script->bodyScope())
+{ }
+
+void
+BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t flags)
+{
+ // Named lambda scopes can only have environment slots. If the callee
+ // isn't closed over, it is accessed via JSOP_CALLEE.
+ if (flags & IsNamedLambda) {
+ // Named lambda binding is weird. Normal BindingKind ordering rules
+ // don't apply.
+ init(0, 0, 0, 0, 0, 0,
+ CanHaveEnvironmentSlots | flags,
+ firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
+ data.names, data.length);
+ } else {
+ // imports - [0, 0)
+ // positional formals - [0, 0)
+ // other formals - [0, 0)
+ // top-level funcs - [0, 0)
+ // vars - [0, 0)
+ // lets - [0, data.constStart)
+ // consts - [data.constStart, data.length)
+ init(0, 0, 0, 0, 0, data.constStart,
+ CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
+ firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
+ data.names, data.length);
+ }
+}
+
+void
+BindingIter::init(FunctionScope::Data& data, uint8_t flags)
+{
+ flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags;
+ if (!(flags & HasFormalParameterExprs))
+ flags |= CanHaveArgumentSlots;
+
+ // imports - [0, 0)
+ // positional formals - [0, data.nonPositionalFormalStart)
+ // other formals - [data.nonPositionalParamStart, data.varStart)
+ // top-level funcs - [data.varStart, data.varStart)
+ // vars - [data.varStart, data.length)
+ // lets - [data.length, data.length)
+ // consts - [data.length, data.length)
+ init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length,
+ flags,
+ 0, JSSLOT_FREE(&CallObject::class_),
+ data.names, data.length);
+}
+
+void
+BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot)
+{
+ // imports - [0, 0)
+ // positional formals - [0, 0)
+ // other formals - [0, 0)
+ // top-level funcs - [0, 0)
+ // vars - [0, data.length)
+ // lets - [data.length, data.length)
+ // consts - [data.length, data.length)
+ init(0, 0, 0, 0, data.length, data.length,
+ CanHaveFrameSlots | CanHaveEnvironmentSlots,
+ firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_),
+ data.names, data.length);
+}
+
+void
+BindingIter::init(GlobalScope::Data& data)
+{
+ // imports - [0, 0)
+ // positional formals - [0, 0)
+ // other formals - [0, 0)
+ // top-level funcs - [0, data.varStart)
+ // vars - [data.varStart, data.letStart)
+ // lets - [data.letStart, data.constStart)
+ // consts - [data.constStart, data.length)
+ init(0, 0, 0, data.varStart, data.letStart, data.constStart,
+ CannotHaveSlots,
+ UINT32_MAX, UINT32_MAX,
+ data.names, data.length);
+}
+
+void
+BindingIter::init(EvalScope::Data& data, bool strict)
+{
+ uint32_t flags;
+ uint32_t firstFrameSlot;
+ uint32_t firstEnvironmentSlot;
+ if (strict) {
+ flags = CanHaveFrameSlots | CanHaveEnvironmentSlots;
+ firstFrameSlot = 0;
+ firstEnvironmentSlot = JSSLOT_FREE(&VarEnvironmentObject::class_);
+ } else {
+ flags = CannotHaveSlots;
+ firstFrameSlot = UINT32_MAX;
+ firstEnvironmentSlot = UINT32_MAX;
+ }
+
+ // imports - [0, 0)
+ // positional formals - [0, 0)
+ // other formals - [0, 0)
+ // top-level funcs - [0, data.varStart)
+ // vars - [data.varStart, data.length)
+ // lets - [data.length, data.length)
+ // consts - [data.length, data.length)
+ init(0, 0, 0, data.varStart, data.length, data.length,
+ flags, firstFrameSlot, firstEnvironmentSlot,
+ data.names, data.length);
+}
+
+void
+BindingIter::init(ModuleScope::Data& data)
+{
+ // imports - [0, data.varStart)
+ // positional formals - [data.varStart, data.varStart)
+ // other formals - [data.varStart, data.varStart)
+ // top-level funcs - [data.varStart, data.varStart)
+ // vars - [data.varStart, data.letStart)
+ // lets - [data.letStart, data.constStart)
+ // consts - [data.constStart, data.length)
+ init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
+ CanHaveFrameSlots | CanHaveEnvironmentSlots,
+ 0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
+ data.names, data.length);
+}
+
+PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
+ : BindingIter(script)
+{
+ // Reinit with flags = 0, i.e., iterate over all positional parameters.
+ if (script->bodyScope()->is<FunctionScope>())
+ init(script->bodyScope()->as<FunctionScope>().data(), /* flags = */ 0);
+ settle();
+}
+
+void
+js::DumpBindings(JSContext* cx, Scope* scopeArg)
+{
+ RootedScope scope(cx, scopeArg);
+ for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
+ JSAutoByteString bytes;
+ if (!AtomToPrintableString(cx, bi.name(), &bytes))
+ return;
+ fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.ptr());
+ switch (bi.location().kind()) {
+ case BindingLocation::Kind::Global:
+ fprintf(stderr, "global\n");
+ break;
+ case BindingLocation::Kind::Argument:
+ fprintf(stderr, "arg slot %u\n", bi.location().argumentSlot());
+ break;
+ case BindingLocation::Kind::Frame:
+ fprintf(stderr, "frame slot %u\n", bi.location().slot());
+ break;
+ case BindingLocation::Kind::Environment:
+ fprintf(stderr, "env slot %u\n", bi.location().slot());
+ break;
+ case BindingLocation::Kind::NamedLambdaCallee:
+ fprintf(stderr, "named lambda callee\n");
+ break;
+ case BindingLocation::Kind::Import:
+ fprintf(stderr, "import\n");
+ break;
+ }
+ }
+}
+
+static JSAtom*
+GetFrameSlotNameInScope(Scope* scope, uint32_t slot)
+{
+ for (BindingIter bi(scope); bi; bi++) {
+ BindingLocation loc = bi.location();
+ if (loc.kind() == BindingLocation::Kind::Frame && loc.slot() == slot)
+ return bi.name();
+ }
+ return nullptr;
+}
+
+JSAtom*
+js::FrameSlotName(JSScript* script, jsbytecode* pc)
+{
+ MOZ_ASSERT(IsLocalOp(JSOp(*pc)));
+ uint32_t slot = GET_LOCALNO(pc);
+ MOZ_ASSERT(slot < script->nfixed());
+
+ // Look for it in the body scope first.
+ if (JSAtom* name = GetFrameSlotNameInScope(script->bodyScope(), slot))
+ return name;
+
+ // If this is a function script and there is an extra var scope, look for
+ // it there.
+ if (script->functionHasExtraBodyVarScope()) {
+ if (JSAtom* name = GetFrameSlotNameInScope(script->functionExtraBodyVarScope(), slot))
+ return name;
+ }
+
+ // If not found, look for it in a lexical scope.
+ for (ScopeIter si(script->innermostScope(pc)); si; si++) {
+ if (!si.scope()->is<LexicalScope>())
+ continue;
+ LexicalScope& lexicalScope = si.scope()->as<LexicalScope>();
+
+ // Is the slot within bounds of the current lexical scope?
+ if (slot < lexicalScope.firstFrameSlot())
+ continue;
+ if (slot >= lexicalScope.nextFrameSlot())
+ break;
+
+ // If so, get the name.
+ if (JSAtom* name = GetFrameSlotNameInScope(&lexicalScope, slot))
+ return name;
+ }
+
+ MOZ_CRASH("Frame slot not found");
+}
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<Scope>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+ return js::gc::Arena::thingSize(get().asTenured().getAllocKind()) +
+ get().sizeOfExcludingThis(mallocSizeOf);
+}