diff options
Diffstat (limited to 'js/src/vm/Scope.cpp')
-rw-r--r-- | js/src/vm/Scope.cpp | 2337 |
1 files changed, 2337 insertions, 0 deletions
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp new file mode 100644 index 0000000000..69abd0e12b --- /dev/null +++ b/js/src/vm/Scope.cpp @@ -0,0 +1,2337 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "vm/Scope.h" + +#include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull +#include "mozilla/ScopeExit.h" + +#include <memory> +#include <new> + +#include "builtin/ModuleObject.h" +#include "frontend/CompilationInfo.h" // CompiltionAtomCache, CompilationInput, BaseCompilationStencil, CompilationGCOutput +#include "frontend/Parser.h" // Copy*ScopeData +#include "frontend/ScriptIndex.h" // ScriptIndex +#include "frontend/SharedContext.h" +#include "frontend/Stencil.h" +#include "gc/Allocator.h" +#include "gc/MaybeRooted.h" +#include "util/StringBuffer.h" +#include "vm/EnvironmentObject.h" +#include "vm/JSScript.h" +#include "wasm/WasmInstance.h" + +#include "gc/FreeOp-inl.h" +#include "gc/ObjectKind-inl.h" +#include "vm/Shape-inl.h" + +using namespace js; +using namespace js::frontend; + +using mozilla::Maybe; + +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::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::FunctionLexical: + return "function lexical"; + case ScopeKind::ClassBody: + return "class body"; + 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"; + case ScopeKind::WasmInstance: + return "wasm instance"; + case ScopeKind::WasmFunction: + return "wasm function"; + } + MOZ_CRASH("Bad ScopeKind"); +} + +Shape* js::EmptyEnvironmentShape(JSContext* cx, const JSClass* 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(JSContext* 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)); + return cx->zone()->propertyTree().getChild(cx, shape, child); +} + +Shape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi, + const JSClass* cls, uint32_t numSlots, + uint32_t baseShapeFlags) { + RootedShape shape(cx, + EmptyEnvironmentShape(cx, cls, numSlots, baseShapeFlags)); + if (!shape) { + return nullptr; + } + + RootedAtom name(cx); + StackBaseShape stackBase(cls, baseShapeFlags); + for (; bi; bi++) { + BindingLocation loc = bi.location(); + if (loc.kind() == BindingLocation::Kind::Environment) { + name = bi.name(); + cx->markAtom(name); + shape = NextEnvironmentShape(cx, name, bi.kind(), loc.slot(), stackBase, + shape); + if (!shape) { + return nullptr; + } + } + } + + return shape; +} + +Shape* js::CreateEnvironmentShape( + JSContext* cx, frontend::CompilationAtomCache& atomCache, + AbstractBindingIter<frontend::TaggedParserAtomIndex>& bi, + const JSClass* cls, uint32_t numSlots, uint32_t baseShapeFlags) { + RootedShape shape(cx, + EmptyEnvironmentShape(cx, cls, numSlots, baseShapeFlags)); + if (!shape) { + return nullptr; + } + + RootedAtom name(cx); + StackBaseShape stackBase(cls, baseShapeFlags); + for (; bi; bi++) { + BindingLocation loc = bi.location(); + if (loc.kind() == BindingLocation::Kind::Environment) { + name = atomCache.getExistingAtomAt(cx, bi.name()); + MOZ_ASSERT(name); + cx->markAtom(name); + shape = NextEnvironmentShape(cx, name, bi.kind(), loc.slot(), stackBase, + shape); + if (!shape) { + return nullptr; + } + } + } + + return shape; +} + +template <class DataT> +inline size_t SizeOfAllocatedData(DataT* data) { + return SizeOfScopeData<DataT>(data->slotInfo.length); +} + +template <typename ConcreteScope> +static UniquePtr<typename ConcreteScope::RuntimeData> CopyScopeData( + JSContext* cx, typename ConcreteScope::RuntimeData* data) { + using Data = typename ConcreteScope::RuntimeData; + + // Make sure the binding names are marked in the context's zone, if we are + // copying data from another zone. + BindingName* names = data->trailingNames.start(); + uint32_t length = data->slotInfo.length; + for (size_t i = 0; i < length; i++) { + if (JSAtom* name = names[i].name()) { + cx->markAtom(name); + } + } + + size_t size = SizeOfAllocatedData(data); + void* bytes = cx->pod_malloc<char>(size); + if (!bytes) { + return nullptr; + } + + auto* dataCopy = new (bytes) Data(*data); + + std::uninitialized_copy_n(data->trailingNames.start(), data->slotInfo.length, + dataCopy->trailingNames.start()); + + return UniquePtr<Data>(dataCopy); +} + +template <typename ConcreteScope> +static void MarkParserScopeData(JSContext* cx, + typename ConcreteScope::ParserData* data, + frontend::CompilationState& compilationState) { + auto* names = data->trailingNames.start(); + uint32_t length = data->slotInfo.length; + for (size_t i = 0; i < length; i++) { + auto index = names[i].name(); + if (!index) { + continue; + } + compilationState.getParserAtomAt(cx, index)->markUsedByStencil(); + } +} + +static bool SetEnvironmentShape(JSContext* cx, BindingIter& freshBi, + BindingIter& bi, const JSClass* cls, + uint32_t firstFrameSlot, + uint32_t baseShapeFlags, + MutableHandleShape envShape) { + envShape.set(CreateEnvironmentShape( + cx, freshBi, cls, bi.nextEnvironmentSlot(), baseShapeFlags)); + return envShape; +} + +static bool SetEnvironmentShape(JSContext* cx, ParserBindingIter& freshBi, + ParserBindingIter& bi, const JSClass* cls, + uint32_t firstFrameSlot, + uint32_t baseShapeFlags, + mozilla::Maybe<uint32_t>* envShape) { + envShape->emplace(bi.nextEnvironmentSlot()); + return true; +} + +template <typename ConcreteScope, typename AtomT, typename EnvironmentT, + typename ShapeT> +static bool PrepareScopeData( + JSContext* cx, AbstractBindingIter<AtomT>& bi, + typename MaybeRootedScopeData<ConcreteScope, AtomT>::HandleType data, + uint32_t firstFrameSlot, ShapeT envShape) { + const JSClass* cls = &EnvironmentT::class_; + uint32_t baseShapeFlags = EnvironmentT::BASESHAPE_FLAGS; + + // Copy a fresh BindingIter for use below. + AbstractBindingIter<AtomT> freshBi(bi); + + // Iterate through all bindings. This counts the number of environment + // slots needed and computes the maximum frame slot. + while (bi) { + bi++; + } + data->slotInfo.nextFrameSlot = + bi.canHaveFrameSlots() ? bi.nextFrameSlot() : LOCALNO_LIMIT; + + // Data is not used after this point. Before this point, gc cannot + // occur, so `data` is fine as a raw pointer. + + // Make a new environment shape if any environment slots were used. + if (bi.nextEnvironmentSlot() != JSSLOT_FREE(cls)) { + if (!SetEnvironmentShape(cx, freshBi, bi, cls, firstFrameSlot, + baseShapeFlags, envShape)) { + return false; + } + } + + return true; +} + +template <typename ConcreteScope> +static typename ConcreteScope::ParserData* NewEmptyParserScopeData( + JSContext* cx, LifoAlloc& alloc, uint32_t length = 0) { + using Data = typename ConcreteScope::ParserData; + + size_t dataSize = SizeOfScopeData<Data>(length); + void* raw = alloc.alloc(dataSize); + if (!raw) { + js::ReportOutOfMemory(cx); + return nullptr; + } + + return new (raw) Data(length); +} + +template <typename ConcreteScope, typename AtomT> +static UniquePtr<AbstractScopeData<ConcreteScope, AtomT>> NewEmptyScopeData( + JSContext* cx, uint32_t length = 0) { + using Data = AbstractScopeData<ConcreteScope, AtomT>; + + size_t dataSize = SizeOfScopeData<Data>(length); + uint8_t* bytes = cx->pod_malloc<uint8_t>(dataSize); + auto data = reinterpret_cast<Data*>(bytes); + if (data) { + new (data) Data(length); + } + return UniquePtr<Data>(data); +} + +template <typename ConcreteScope> +static UniquePtr<typename ConcreteScope::RuntimeData> LiftParserScopeData( + JSContext* cx, frontend::CompilationAtomCache& atomCache, + BaseParserScopeData* baseData) { + using ConcreteData = typename ConcreteScope::RuntimeData; + + auto* data = static_cast<typename ConcreteScope::ParserData*>(baseData); + + // Convert all scope ParserAtoms to rooted JSAtoms. + // Rooting is necessary as conversion can gc. + JS::RootedVector<JSAtom*> jsatoms(cx); + if (!jsatoms.reserve(data->slotInfo.length)) { + return nullptr; + } + auto* names = data->trailingNames.start(); + uint32_t length = data->slotInfo.length; + for (size_t i = 0; i < length; i++) { + JSAtom* jsatom = nullptr; + if (names[i].name()) { + jsatom = atomCache.getExistingAtomAt(cx, names[i].name()); + MOZ_ASSERT(jsatom); + } + jsatoms.infallibleAppend(jsatom); + } + + // Allocate a new scope-data of the right kind. + UniquePtr<ConcreteData> scopeData( + NewEmptyScopeData<ConcreteScope, JSAtom>(cx, data->slotInfo.length)); + if (!scopeData) { + return nullptr; + } + + memcpy(&scopeData.get()->slotInfo, &data->slotInfo, + sizeof(typename ConcreteScope::SlotInfo)); + + // Initialize new scoped names. + auto* namesOut = scopeData->trailingNames.start(); + for (size_t i = 0; i < length; i++) { + namesOut[i] = names[i].copyWithNewAtom(jsatoms[i].get()); + } + + return scopeData; +} + +static constexpr size_t HasAtomMask = 1; +static constexpr size_t HasAtomShift = 1; + +static XDRResult XDRTrailingName(XDRState<XDR_ENCODE>* xdr, + BindingName* bindingName, + const uint32_t* length) { + JSContext* cx = xdr->cx(); + + RootedAtom atom(cx, bindingName->name()); + bool hasAtom = !!atom; + + uint8_t flags = bindingName->flagsForXDR(); + MOZ_ASSERT(((flags << HasAtomShift) >> HasAtomShift) == flags); + uint8_t u8 = (flags << HasAtomShift) | uint8_t(hasAtom); + MOZ_TRY(xdr->codeUint8(&u8)); + + if (hasAtom) { + MOZ_TRY(XDRAtom(xdr, &atom)); + } + + return Ok(); +} + +static XDRResult XDRTrailingName(XDRState<XDR_DECODE>* xdr, void* bindingName, + uint32_t* length) { + JSContext* cx = xdr->cx(); + + uint8_t u8; + MOZ_TRY(xdr->codeUint8(&u8)); + + bool hasAtom = u8 & HasAtomMask; + RootedAtom atom(cx); + if (hasAtom) { + MOZ_TRY(XDRAtom(xdr, &atom)); + } + + uint8_t flags = u8 >> HasAtomShift; + new (bindingName) BindingName(BindingName::fromXDR(atom, flags)); + ++*length; + + return Ok(); +} + +template <typename ConcreteScope, XDRMode mode> +/* static */ +XDRResult Scope::XDRSizedBindingNames( + XDRState<mode>* xdr, Handle<ConcreteScope*> scope, + MutableHandle<typename ConcreteScope::RuntimeData*> data) { + MOZ_ASSERT(!data); + + JSContext* cx = xdr->cx(); + + uint32_t length; + if (mode == XDR_ENCODE) { + length = scope->data().slotInfo.length; + } + MOZ_TRY(xdr->codeUint32(&length)); + + if (mode == XDR_ENCODE) { + data.set(&scope->data()); + } else { + data.set(NewEmptyScopeData<ConcreteScope, JSAtom>(cx, length).release()); + if (!data) { + return xdr->fail(JS::TranscodeResult_Throw); + } + } + + auto dataGuard = mozilla::MakeScopeExit([&]() { + if (mode == XDR_DECODE) { + js_delete(data.get()); + data.set(nullptr); + } + }); + + for (uint32_t i = 0; i < length; i++) { + if (mode == XDR_DECODE) { + MOZ_ASSERT(i == data->slotInfo.length, "must be decoding at the end"); + } + MOZ_TRY( + XDRTrailingName(xdr, &data->trailingNames[i], &data->slotInfo.length)); + } + MOZ_ASSERT(data->slotInfo.length == length); + + dataGuard.release(); + return Ok(); +} + +/* static */ +Scope* Scope::create(JSContext* cx, ScopeKind kind, HandleScope enclosing, + HandleShape envShape) { + Scope* scope = Allocate<Scope>(cx); + if (scope) { + new (scope) Scope(kind, enclosing, envShape); + } + return scope; +} + +template <typename ConcreteScope> +/* static */ +ConcreteScope* Scope::create( + JSContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape, + MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> 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<ConcreteScope>(data); + + return &scope->as<ConcreteScope>(); +} + +template <typename ConcreteScope> +inline void Scope::initData( + MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> data) { + MOZ_ASSERT(!rawData()); + + AddCellMemory(this, SizeOfAllocatedData(data.get().get()), + MemoryUse::ScopeData); + + setHeaderPtr(data.get().release()); +} + +template <typename EnvironmentT> +bool Scope::updateEnvShapeIfRequired(JSContext* cx, MutableHandleShape envShape, + bool needsEnvironment) { + if (!envShape && needsEnvironment) { + envShape.set(EmptyEnvironmentShape<EnvironmentT>(cx)); + if (!envShape) { + return false; + } + } + return true; +} + +template <typename EnvironmentT> +bool Scope::updateEnvShapeIfRequired(JSContext* cx, + mozilla::Maybe<uint32_t>* envShape, + bool needsEnvironment) { + if (envShape->isNothing() && needsEnvironment) { + uint32_t numSlots = 0; + envShape->emplace(numSlots); + } + return true; +} + +uint32_t Scope::firstFrameSlot() const { + switch (kind()) { + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + case ScopeKind::ClassBody: + // For intra-frame scopes, find the enclosing scope's next frame slot. + MOZ_ASSERT(is<LexicalScope>()); + return LexicalScope::nextFrameSlot(enclosing()); + + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + // Named lambda scopes cannot have frame slots. + return LOCALNO_LIMIT; + + case ScopeKind::FunctionBodyVar: + if (enclosing()->is<FunctionScope>()) { + return enclosing()->as<FunctionScope>().nextFrameSlot(); + } + break; + + default: + break; + } + return 0; +} + +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. + Shape* shape = environmentShape(); + if (shape && shape->zoneFromAnyThread() != cx->zone()) { + BindingIter bi(this); + return CreateEnvironmentShape(cx, bi, shape->getObjectClass(), + shape->slotSpan(), shape->getObjectFlags()); + } + return shape; +} + +/* 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: { + RootedScript script(cx, scope->as<FunctionScope>().script()); + const char* filename = script->filename(); + // If the script has an internal URL, include it in the crash reason. If + // not, it may be a web URL, and therefore privacy-sensitive. + if (!strncmp(filename, "chrome:", 7) || + !strncmp(filename, "resource:", 9)) { + MOZ_CRASH_UNSAFE_PRINTF("Use FunctionScope::clone (script URL: %s)", + filename); + } + + MOZ_CRASH("Use FunctionScope::clone."); + break; + } + + case ScopeKind::FunctionBodyVar: { + Rooted<UniquePtr<VarScope::RuntimeData>> dataClone(cx); + dataClone = CopyScopeData<VarScope>(cx, &scope->as<VarScope>().data()); + if (!dataClone) { + return nullptr; + } + return create<VarScope>(cx, scope->kind_, enclosing, envShape, + &dataClone); + } + + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + case ScopeKind::FunctionLexical: + case ScopeKind::ClassBody: { + Rooted<UniquePtr<LexicalScope::RuntimeData>> dataClone(cx); + dataClone = + CopyScopeData<LexicalScope>(cx, &scope->as<LexicalScope>().data()); + if (!dataClone) { + return nullptr; + } + return create<LexicalScope>(cx, scope->kind_, enclosing, envShape, + &dataClone); + } + + case ScopeKind::With: + return create(cx, scope->kind_, enclosing, envShape); + + case ScopeKind::Eval: + case ScopeKind::StrictEval: { + Rooted<UniquePtr<EvalScope::RuntimeData>> dataClone(cx); + dataClone = CopyScopeData<EvalScope>(cx, &scope->as<EvalScope>().data()); + if (!dataClone) { + return nullptr; + } + return create<EvalScope>(cx, scope->kind_, enclosing, envShape, + &dataClone); + } + + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + MOZ_CRASH("Use GlobalScope::clone."); + break; + + case ScopeKind::WasmFunction: + MOZ_CRASH("wasm functions are not nested in JSScript"); + break; + + case ScopeKind::Module: + case ScopeKind::WasmInstance: + MOZ_CRASH("NYI"); + break; + } + + return nullptr; +} + +void Scope::finalize(JSFreeOp* fop) { + MOZ_ASSERT(CurrentThreadIsGCFinalizing()); + applyScopeDataTyped([this, fop](auto data) { + fop->delete_(this, data, SizeOfAllocatedData(data), MemoryUse::ScopeData); + }); + setHeaderPtr(nullptr); +} + +size_t Scope::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + if (rawData()) { + return mallocSizeOf(rawData()); + } + 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"); +} + +#if defined(DEBUG) || defined(JS_JITSPEW) + +/* static */ +bool Scope::dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope, + GenericPrinter& out, const char* indent) { + if (!out.put(ScopeKindString(scope->kind()))) { + return false; + } + if (!out.put(" {")) { + return false; + } + + size_t i = 0; + for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++, i++) { + if (i == 0) { + if (!out.put("\n")) { + return false; + } + } + UniqueChars bytes = AtomToPrintableString(cx, bi.name()); + if (!bytes) { + return false; + } + if (!out.put(indent)) { + return false; + } + if (!out.printf(" %2zu: %s %s ", i, BindingKindString(bi.kind()), + bytes.get())) { + return false; + } + switch (bi.location().kind()) { + case BindingLocation::Kind::Global: + if (bi.isTopLevelFunction()) { + if (!out.put("(global function)\n")) { + return false; + } + } else { + if (!out.put("(global)\n")) { + return false; + } + } + break; + case BindingLocation::Kind::Argument: + if (!out.printf("(arg slot %u)\n", bi.location().argumentSlot())) { + return false; + } + break; + case BindingLocation::Kind::Frame: + if (!out.printf("(frame slot %u)\n", bi.location().slot())) { + return false; + } + break; + case BindingLocation::Kind::Environment: + if (!out.printf("(env slot %u)\n", bi.location().slot())) { + return false; + } + break; + case BindingLocation::Kind::NamedLambdaCallee: + if (!out.put("(named lambda callee)\n")) { + return false; + } + break; + case BindingLocation::Kind::Import: + if (!out.put("(import)\n")) { + return false; + } + break; + } + } + if (i > 0) { + if (!out.put(indent)) { + return false; + } + } + if (!out.put("}")) { + return false; + } + + ScopeIter si(scope); + si++; + for (; si; si++) { + if (!out.put(" -> ")) { + return false; + } + if (!out.put(ScopeKindString(si.kind()))) { + return false; + } + } + return true; +} + +#endif /* defined(DEBUG) || defined(JS_JITSPEW) */ + +/* static */ +uint32_t LexicalScope::nextFrameSlot(Scope* scope) { + for (ScopeIter si(scope); si; si++) { + switch (si.kind()) { + case ScopeKind::With: + continue; + + case ScopeKind::Function: + return si.scope()->as<FunctionScope>().nextFrameSlot(); + + case ScopeKind::FunctionBodyVar: + return si.scope()->as<VarScope>().nextFrameSlot(); + + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + case ScopeKind::ClassBody: + return si.scope()->as<LexicalScope>().nextFrameSlot(); + + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + // Named lambda scopes cannot have frame slots. + return 0; + + 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(); + + case ScopeKind::WasmInstance: + case ScopeKind::WasmFunction: + // Invalid; MOZ_CRASH below. + break; + } + } + MOZ_CRASH("Not an enclosing intra-frame Scope"); +} + +template <typename AtomT, typename ShapeT> +bool LexicalScope::prepareForScopeCreation( + JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, + typename MaybeRootedScopeData<LexicalScope, AtomT>::MutableHandleType data, + ShapeT envShape) { + bool isNamedLambda = + kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda; + + MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT); + + AbstractBindingIter<AtomT> bi(*data, firstFrameSlot, isNamedLambda); + if (!PrepareScopeData<LexicalScope, AtomT, LexicalEnvironmentObject>( + cx, bi, data, firstFrameSlot, envShape)) { + return false; + } + return true; +} + +/* static */ +LexicalScope* LexicalScope::createWithData( + JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<RuntimeData>> data, + uint32_t firstFrameSlot, HandleScope enclosing) { + RootedShape envShape(cx); + + if (!prepareForScopeCreation<JSAtom>(cx, kind, firstFrameSlot, data, + &envShape)) { + return nullptr; + } + + auto scope = Scope::create<LexicalScope>(cx, kind, enclosing, envShape, data); + if (!scope) { + return nullptr; + } + + MOZ_ASSERT(scope->firstFrameSlot() == firstFrameSlot); + return scope; +} + +/* static */ +Shape* LexicalScope::getEmptyExtensibleEnvironmentShape(JSContext* cx) { + const JSClass* cls = &LexicalEnvironmentObject::class_; + return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), + /* baseShapeFlags = */ 0); +} + +template <XDRMode mode> +/* static */ +XDRResult LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + + Rooted<RuntimeData*> data(cx); + MOZ_TRY( + XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data)); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> uniqueData; + if (mode == XDR_DECODE) { + uniqueData.emplace(cx, data); + } + + uint32_t firstFrameSlot; + uint32_t nextFrameSlot; + if (mode == XDR_ENCODE) { + firstFrameSlot = scope->firstFrameSlot(); + nextFrameSlot = data->slotInfo.nextFrameSlot; + } + + MOZ_TRY(xdr->codeUint32(&data->slotInfo.constStart)); + MOZ_TRY(xdr->codeUint32(&firstFrameSlot)); + MOZ_TRY(xdr->codeUint32(&nextFrameSlot)); + + if (mode == XDR_DECODE) { + scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, + enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + + // nextFrameSlot is used only for this correctness check. + MOZ_ASSERT(nextFrameSlot == + scope->as<LexicalScope>().data().slotInfo.nextFrameSlot); + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + LexicalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +template + /* static */ + XDRResult + LexicalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +static void SetCanonicalFunction(FunctionScope::RuntimeData& data, + HandleFunction fun) { + data.canonicalFunction.init(fun); +} + +static void SetCanonicalFunction(FunctionScope::ParserData& data, + HandleFunction fun) {} + +template <typename AtomT, typename ShapeT> +bool FunctionScope::prepareForScopeCreation( + JSContext* cx, + typename MaybeRootedScopeData<FunctionScope, AtomT>::MutableHandleType data, + bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, + ShapeT envShape) { + uint32_t firstFrameSlot = 0; + AbstractBindingIter<AtomT> bi(*data, hasParameterExprs); + if (!PrepareScopeData<FunctionScope, AtomT, CallObject>( + cx, bi, data, firstFrameSlot, envShape)) { + return false; + } + + if (hasParameterExprs) { + data->slotInfo.setHasParameterExprs(); + } + SetCanonicalFunction(*data, 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 or async function + // Also see |FunctionBox::needsExtraBodyVarEnvironmentRegardlessOfBindings()|. + return updateEnvShapeIfRequired<CallObject>(cx, envShape, needsEnvironment); +} + +/* static */ +FunctionScope* FunctionScope::createWithData( + JSContext* cx, MutableHandle<UniquePtr<RuntimeData>> data, + bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, + HandleScope enclosing) { + MOZ_ASSERT(data); + MOZ_ASSERT(fun->isTenured()); + + RootedShape envShape(cx); + + if (!prepareForScopeCreation<JSAtom>(cx, data, hasParameterExprs, + needsEnvironment, fun, &envShape)) { + return nullptr; + } + + return Scope::create<FunctionScope>(cx, ScopeKind::Function, enclosing, + envShape, data); +} + +JSScript* FunctionScope::script() const { + return canonicalFunction()->nonLazyScript(); +} + +/* static */ +bool FunctionScope::isSpecialName(JSContext* cx, JSAtom* name) { + return name == cx->names().arguments || name == cx->names().dotThis || + name == cx->names().dotGenerator; +} + +/* static */ +bool FunctionScope::isSpecialName(JSContext* cx, + frontend::TaggedParserAtomIndex name) { + return name == frontend::TaggedParserAtomIndex::arguments() || + name == frontend::TaggedParserAtomIndex::dotThis() || + name == frontend::TaggedParserAtomIndex::dotGenerator(); +} + +/* static */ +FunctionScope* FunctionScope::clone(JSContext* cx, Handle<FunctionScope*> scope, + HandleFunction fun, HandleScope enclosing) { + MOZ_ASSERT(fun != scope->canonicalFunction()); + + RootedShape envShape(cx); + if (scope->environmentShape()) { + envShape = scope->maybeCloneEnvironmentShape(cx); + if (!envShape) { + return nullptr; + } + } + + Rooted<RuntimeData*> dataOriginal(cx, &scope->as<FunctionScope>().data()); + Rooted<UniquePtr<RuntimeData>> dataClone( + cx, CopyScopeData<FunctionScope>(cx, dataOriginal)); + if (!dataClone) { + return nullptr; + } + + dataClone->canonicalFunction = fun; + + return Scope::create<FunctionScope>(cx, scope->kind(), enclosing, envShape, + &dataClone); +} + +template <XDRMode mode> +/* static */ +XDRResult FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, + HandleScope enclosing, MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + Rooted<RuntimeData*> data(cx); + MOZ_TRY(XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), + &data)); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> 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->slotInfo.hasParameterExprs(); + nextFrameSlot = data->slotInfo.nextFrameSlot; + } + MOZ_TRY(xdr->codeUint8(&needsEnvironment)); + MOZ_TRY(xdr->codeUint8(&hasParameterExprs)); + MOZ_TRY(xdr->codeUint16(&data->slotInfo.nonPositionalFormalStart)); + MOZ_TRY(xdr->codeUint16(&data->slotInfo.varStart)); + MOZ_TRY(xdr->codeUint32(&nextFrameSlot)); + + if (mode == XDR_DECODE) { + if (!data->slotInfo.length) { + MOZ_ASSERT(!data->slotInfo.nonPositionalFormalStart); + MOZ_ASSERT(!data->slotInfo.varStart); + MOZ_ASSERT(!data->slotInfo.nextFrameSlot); + } + + scope.set(createWithData(cx, &uniqueData.ref(), hasParameterExprs, + needsEnvironment, fun, enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + + // nextFrameSlot is used only for this correctness check. + MOZ_ASSERT(nextFrameSlot == + scope->as<FunctionScope>().data().slotInfo.nextFrameSlot); + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + FunctionScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleFunction fun, + HandleScope enclosing, MutableHandleScope scope); + +template + /* static */ + XDRResult + FunctionScope::XDR(XDRState<XDR_DECODE>* xdr, HandleFunction fun, + HandleScope enclosing, MutableHandleScope scope); + +template <typename AtomT, typename ShapeT> +bool VarScope::prepareForScopeCreation( + JSContext* cx, ScopeKind kind, + typename MaybeRootedScopeData<VarScope, AtomT>::MutableHandleType data, + uint32_t firstFrameSlot, bool needsEnvironment, ShapeT envShape) { + AbstractBindingIter<AtomT> bi(*data, firstFrameSlot); + if (!PrepareScopeData<VarScope, AtomT, VarEnvironmentObject>( + cx, bi, data, firstFrameSlot, envShape)) { + return false; + } + + // An environment may be needed regardless of existence of any closed over + // bindings: + // - Extensible scopes (i.e., due to direct eval) + // - Being a generator + return updateEnvShapeIfRequired<VarEnvironmentObject>(cx, envShape, + needsEnvironment); +} + +/* static */ +VarScope* VarScope::createWithData(JSContext* cx, ScopeKind kind, + MutableHandle<UniquePtr<RuntimeData>> data, + uint32_t firstFrameSlot, + bool needsEnvironment, + HandleScope enclosing) { + MOZ_ASSERT(data); + + RootedShape envShape(cx); + if (!prepareForScopeCreation<JSAtom>(cx, kind, data, firstFrameSlot, + needsEnvironment, &envShape)) { + return nullptr; + } + + return Scope::create<VarScope>(cx, kind, enclosing, envShape, data); +} + +template <XDRMode mode> +/* static */ +XDRResult VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + Rooted<RuntimeData*> data(cx); + MOZ_TRY(XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data)); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> 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->firstFrameSlot(); + nextFrameSlot = data->slotInfo.nextFrameSlot; + } + MOZ_TRY(xdr->codeUint8(&needsEnvironment)); + MOZ_TRY(xdr->codeUint32(&firstFrameSlot)); + MOZ_TRY(xdr->codeUint32(&nextFrameSlot)); + + if (mode == XDR_DECODE) { + if (!data->slotInfo.length) { + MOZ_ASSERT(!data->slotInfo.nextFrameSlot); + } + + scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, + needsEnvironment, enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + + // nextFrameSlot is used only for this correctness check. + MOZ_ASSERT(nextFrameSlot == + scope->as<VarScope>().data().slotInfo.nextFrameSlot); + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + VarScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +template + /* static */ + XDRResult + VarScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +/* static */ +GlobalScope* GlobalScope::create(JSContext* cx, ScopeKind kind, + Handle<RuntimeData*> dataArg) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<RuntimeData>> data( + cx, dataArg ? CopyScopeData<GlobalScope>(cx, dataArg) + : NewEmptyScopeData<GlobalScope, JSAtom>(cx)); + if (!data) { + return nullptr; + } + + return createWithData(cx, kind, &data); +} + +/* static */ +GlobalScope* GlobalScope::createWithData( + JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<RuntimeData>> 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. + return Scope::create<GlobalScope>(cx, kind, nullptr, nullptr, data); +} + +/* static */ +GlobalScope* GlobalScope::clone(JSContext* cx, Handle<GlobalScope*> scope, + ScopeKind kind) { + Rooted<RuntimeData*> dataOriginal(cx, &scope->as<GlobalScope>().data()); + Rooted<UniquePtr<RuntimeData>> dataClone( + cx, CopyScopeData<GlobalScope>(cx, dataOriginal)); + if (!dataClone) { + return nullptr; + } + return Scope::create<GlobalScope>(cx, kind, nullptr, nullptr, &dataClone); +} + +template <XDRMode mode> +/* static */ +XDRResult GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, + MutableHandleScope scope) { + MOZ_ASSERT((mode == XDR_DECODE) == !scope); + + JSContext* cx = xdr->cx(); + Rooted<RuntimeData*> data(cx); + MOZ_TRY( + XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data)); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> uniqueData; + if (mode == XDR_DECODE) { + uniqueData.emplace(cx, data); + } + + MOZ_TRY(xdr->codeUint32(&data->slotInfo.letStart)); + MOZ_TRY(xdr->codeUint32(&data->slotInfo.constStart)); + + if (mode == XDR_DECODE) { + if (!data->slotInfo.length) { + MOZ_ASSERT(!data->slotInfo.letStart); + MOZ_ASSERT(!data->slotInfo.constStart); + } + + scope.set(createWithData(cx, kind, &uniqueData.ref())); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + GlobalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, + MutableHandleScope scope); + +template + /* static */ + XDRResult + GlobalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, + MutableHandleScope scope); + +/* static */ +WithScope* WithScope::create(JSContext* cx, HandleScope enclosing) { + Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr); + return static_cast<WithScope*>(scope); +} + +template <XDRMode mode> +/* static */ +XDRResult WithScope::XDR(XDRState<mode>* xdr, HandleScope enclosing, + MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + if (mode == XDR_DECODE) { + scope.set(create(cx, enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + WithScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleScope enclosing, + MutableHandleScope scope); + +template + /* static */ + XDRResult + WithScope::XDR(XDRState<XDR_DECODE>* xdr, HandleScope enclosing, + MutableHandleScope scope); + +template <typename AtomT, typename ShapeT> +bool EvalScope::prepareForScopeCreation( + JSContext* cx, ScopeKind scopeKind, + typename MaybeRootedScopeData<EvalScope, AtomT>::MutableHandleType data, + ShapeT envShape) { + if (scopeKind == ScopeKind::StrictEval) { + uint32_t firstFrameSlot = 0; + AbstractBindingIter<AtomT> bi(*data, true); + if (!PrepareScopeData<EvalScope, AtomT, VarEnvironmentObject>( + cx, bi, data, firstFrameSlot, envShape)) { + return false; + } + } + + // Strict eval and direct eval in parameter expressions always get their own + // var environment even if there are no bindings. + bool needsEnvironment = (scopeKind == ScopeKind::StrictEval); + + return updateEnvShapeIfRequired<VarEnvironmentObject>(cx, envShape, + needsEnvironment); +} + +/* static */ +EvalScope* EvalScope::createWithData(JSContext* cx, ScopeKind scopeKind, + MutableHandle<UniquePtr<RuntimeData>> data, + HandleScope enclosing) { + MOZ_ASSERT(data); + + RootedShape envShape(cx); + if (!prepareForScopeCreation<JSAtom>(cx, scopeKind, data, &envShape)) { + return nullptr; + } + + return Scope::create<EvalScope>(cx, scopeKind, enclosing, envShape, data); +} + +/* static */ +Scope* EvalScope::nearestVarScopeForDirectEval(Scope* scope) { + for (ScopeIter si(scope); si; si++) { + switch (si.kind()) { + case ScopeKind::Function: + case ScopeKind::FunctionBodyVar: + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + return scope; + default: + break; + } + } + return nullptr; +} + +template <XDRMode mode> +/* static */ +XDRResult EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + Rooted<RuntimeData*> data(cx); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> uniqueData; + if (mode == XDR_DECODE) { + uniqueData.emplace(cx, data); + } + + MOZ_TRY(XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data)); + + if (mode == XDR_DECODE) { + if (!data->slotInfo.length) { + MOZ_ASSERT(!data->slotInfo.nextFrameSlot); + } + scope.set(createWithData(cx, kind, &uniqueData.ref(), enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + EvalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +template + /* static */ + XDRResult + EvalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, + HandleScope enclosing, MutableHandleScope scope); + +ModuleScope::RuntimeData::RuntimeData(size_t nameCount) + : trailingNames(nameCount) {} + +static void InitModule(ModuleScope::RuntimeData& data, + HandleModuleObject module) { + data.module.init(module); +} + +static void InitModule(ModuleScope::ParserData& data, + HandleModuleObject module) {} + +/* static */ +template <typename AtomT, typename ShapeT> +bool ModuleScope::prepareForScopeCreation( + JSContext* cx, + typename MaybeRootedScopeData<ModuleScope, AtomT>::MutableHandleType data, + HandleModuleObject module, ShapeT envShape) { + uint32_t firstFrameSlot = 0; + AbstractBindingIter<AtomT> bi(*data); + if (!PrepareScopeData<ModuleScope, AtomT, ModuleEnvironmentObject>( + cx, bi, data, firstFrameSlot, envShape)) { + return false; + } + + InitModule(*data, module); + + // Modules always need an environment object for now. + bool needsEnvironment = true; + + return updateEnvShapeIfRequired<ModuleEnvironmentObject>(cx, envShape, + needsEnvironment); +} + +/* static */ +ModuleScope* ModuleScope::createWithData( + JSContext* cx, MutableHandle<UniquePtr<RuntimeData>> data, + HandleModuleObject module, HandleScope enclosing) { + MOZ_ASSERT(data); + MOZ_ASSERT(enclosing->is<GlobalScope>()); + + RootedShape envShape(cx); + if (!prepareForScopeCreation<JSAtom>(cx, data, module, &envShape)) { + return nullptr; + } + + return Scope::create<ModuleScope>(cx, ScopeKind::Module, enclosing, envShape, + data); +} + +template <size_t ArrayLength> +static JSAtom* GenerateWasmName(JSContext* cx, + const char (&prefix)[ArrayLength], + uint32_t index) { + StringBuffer sb(cx); + if (!sb.append(prefix)) { + return nullptr; + } + if (!NumberValueToStringBuffer(cx, Int32Value(index), sb)) { + return nullptr; + } + + return sb.finishAtom(); +} + +template <XDRMode mode> +/* static */ +XDRResult ModuleScope::XDR(XDRState<mode>* xdr, HandleModuleObject module, + HandleScope enclosing, MutableHandleScope scope) { + JSContext* cx = xdr->cx(); + Rooted<RuntimeData*> data(cx); + MOZ_TRY( + XDRSizedBindingNames<ModuleScope>(xdr, scope.as<ModuleScope>(), &data)); + + { + Maybe<Rooted<UniquePtr<RuntimeData>>> uniqueData; + if (mode == XDR_DECODE) { + uniqueData.emplace(cx, data); + } + + uint32_t nextFrameSlot; + if (mode == XDR_ENCODE) { + nextFrameSlot = data->slotInfo.nextFrameSlot; + } + + MOZ_TRY(xdr->codeUint32(&data->slotInfo.varStart)); + MOZ_TRY(xdr->codeUint32(&data->slotInfo.letStart)); + MOZ_TRY(xdr->codeUint32(&data->slotInfo.constStart)); + MOZ_TRY(xdr->codeUint32(&nextFrameSlot)); + + if (mode == XDR_DECODE) { + if (!data->slotInfo.length) { + MOZ_ASSERT(!data->slotInfo.varStart); + MOZ_ASSERT(!data->slotInfo.letStart); + MOZ_ASSERT(!data->slotInfo.constStart); + MOZ_ASSERT(!data->slotInfo.nextFrameSlot); + } + + scope.set(createWithData(cx, &uniqueData.ref(), module, enclosing)); + if (!scope) { + return xdr->fail(JS::TranscodeResult_Throw); + } + + // nextFrameSlot is used only for this correctness check. + MOZ_ASSERT(nextFrameSlot == + scope->as<ModuleScope>().data().slotInfo.nextFrameSlot); + } + } + + return Ok(); +} + +template + /* static */ + XDRResult + ModuleScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleModuleObject module, + HandleScope enclosing, MutableHandleScope scope); + +template + /* static */ + XDRResult + ModuleScope::XDR(XDRState<XDR_DECODE>* xdr, HandleModuleObject module, + HandleScope enclosing, MutableHandleScope scope); + +static void InitializeTrailingName( + AbstractTrailingNamesArray<JSAtom>& trailingNames, size_t i, JSAtom* name) { + void* trailingName = &trailingNames[i]; + new (trailingName) BindingName(name, false); +} + +template <class DataT> +static void InitializeNextTrailingName(const Rooted<UniquePtr<DataT>>& data, + JSAtom* name) { + InitializeTrailingName(data->trailingNames, data->slotInfo.length, name); + data->slotInfo.length++; +} + +WasmInstanceScope::RuntimeData::RuntimeData(size_t nameCount) + : trailingNames(nameCount) {} + +/* static */ +WasmInstanceScope* WasmInstanceScope::create(JSContext* cx, + WasmInstanceObject* instance) { + size_t namesCount = 0; + if (instance->instance().memory()) { + namesCount++; + } + size_t globalsStart = namesCount; + size_t globalsCount = instance->instance().metadata().globals.length(); + namesCount += globalsCount; + + Rooted<UniquePtr<RuntimeData>> data( + cx, NewEmptyScopeData<WasmInstanceScope, JSAtom>(cx, namesCount)); + if (!data) { + return nullptr; + } + + if (instance->instance().memory()) { + JSAtom* wasmName = GenerateWasmName(cx, "memory", /* index = */ 0); + if (!wasmName) { + return nullptr; + } + + InitializeNextTrailingName(data, wasmName); + } + + for (size_t i = 0; i < globalsCount; i++) { + JSAtom* wasmName = GenerateWasmName(cx, "global", i); + if (!wasmName) { + return nullptr; + } + + InitializeNextTrailingName(data, wasmName); + } + + MOZ_ASSERT(data->slotInfo.length == namesCount); + + data->instance.init(instance); + data->slotInfo.globalsStart = globalsStart; + + RootedScope enclosing(cx, &cx->global()->emptyGlobalScope()); + return Scope::create<WasmInstanceScope>(cx, ScopeKind::WasmInstance, + enclosing, + /* envShape = */ nullptr, &data); +} + +/* static */ +WasmFunctionScope* WasmFunctionScope::create(JSContext* cx, + HandleScope enclosing, + uint32_t funcIndex) { + MOZ_ASSERT(enclosing->is<WasmInstanceScope>()); + + Rooted<WasmFunctionScope*> wasmFunctionScope(cx); + + Rooted<WasmInstanceObject*> instance( + cx, enclosing->as<WasmInstanceScope>().instance()); + + // TODO pull the local variable names from the wasm function definition. + wasm::ValTypeVector locals; + size_t argsLength; + wasm::StackResults unusedStackResults; + if (!instance->instance().debug().debugGetLocalTypes( + funcIndex, &locals, &argsLength, &unusedStackResults)) { + return nullptr; + } + uint32_t namesCount = locals.length(); + + Rooted<UniquePtr<RuntimeData>> data( + cx, NewEmptyScopeData<WasmFunctionScope, JSAtom>(cx, namesCount)); + if (!data) { + return nullptr; + } + + for (size_t i = 0; i < namesCount; i++) { + JSAtom* wasmName = GenerateWasmName(cx, "var", i); + if (!wasmName) { + return nullptr; + } + + InitializeNextTrailingName(data, wasmName); + } + MOZ_ASSERT(data->slotInfo.length == namesCount); + + return Scope::create<WasmFunctionScope>(cx, ScopeKind::WasmFunction, + enclosing, + /* envShape = */ nullptr, &data); +} + +ScopeIter::ScopeIter(JSScript* script) : scope_(script->bodyScope()) {} + +bool ScopeIter::hasSyntacticEnvironment() const { + return scope()->hasEnvironment() && + scope()->kind() != ScopeKind::NonSyntactic; +} + +AbstractBindingIter<JSAtom>::AbstractBindingIter(ScopeKind kind, + BaseScopeData* data, + uint32_t firstFrameSlot) + : BaseAbstractBindingIter<JSAtom>() { + switch (kind) { + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + case ScopeKind::ClassBody: + init(*static_cast<LexicalScope::RuntimeData*>(data), firstFrameSlot, 0); + break; + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + init(*static_cast<LexicalScope::RuntimeData*>(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 (static_cast<FunctionScope::RuntimeData*>(data) + ->slotInfo.hasParameterExprs()) { + flags |= HasFormalParameterExprs; + } + init(*static_cast<FunctionScope::RuntimeData*>(data), flags); + break; + } + case ScopeKind::FunctionBodyVar: + init(*static_cast<VarScope::RuntimeData*>(data), firstFrameSlot); + break; + case ScopeKind::Eval: + case ScopeKind::StrictEval: + init(*static_cast<EvalScope::RuntimeData*>(data), + kind == ScopeKind::StrictEval); + break; + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + init(*static_cast<GlobalScope::RuntimeData*>(data)); + break; + case ScopeKind::Module: + init(*static_cast<ModuleScope::RuntimeData*>(data)); + break; + case ScopeKind::WasmInstance: + init(*static_cast<WasmInstanceScope::RuntimeData*>(data)); + break; + case ScopeKind::WasmFunction: + init(*static_cast<WasmFunctionScope::RuntimeData*>(data)); + break; + } +} + +AbstractBindingIter<JSAtom>::AbstractBindingIter(Scope* scope) + : AbstractBindingIter<JSAtom>(scope->kind(), scope->rawData(), + scope->firstFrameSlot()) {} + +AbstractBindingIter<JSAtom>::AbstractBindingIter(JSScript* script) + : AbstractBindingIter<JSAtom>(script->bodyScope()) {} + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + LexicalScope::AbstractData<NameT>& data, uint32_t firstFrameSlot, + uint8_t flags) { + auto& slotInfo = data.slotInfo; + + // 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, CanHaveEnvironmentSlots | flags, firstFrameSlot, + JSSLOT_FREE(&LexicalEnvironmentObject::class_), + data.trailingNames.start(), slotInfo.length); + } else { + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, 0) + // lets - [0, slotInfo.constStart) + // consts - [slotInfo.constStart, slotInfo.length) + init(0, 0, 0, 0, slotInfo.constStart, + CanHaveFrameSlots | CanHaveEnvironmentSlots | flags, firstFrameSlot, + JSSLOT_FREE(&LexicalEnvironmentObject::class_), + data.trailingNames.start(), slotInfo.length); + } +} + +template void BaseAbstractBindingIter<JSAtom>::init( + LexicalScope::AbstractData<JSAtom>&, uint32_t, uint8_t); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + LexicalScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_t, + uint8_t); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + FunctionScope::AbstractData<NameT>& data, uint8_t flags) { + flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags; + if (!(flags & HasFormalParameterExprs)) { + flags |= CanHaveArgumentSlots; + } + + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, slotInfo.nonPositionalFormalStart) + // other formals - [slotInfo.nonPositionalParamStart, slotInfo.varStart) + // vars - [slotInfo.varStart, slotInfo.length) + // lets - [slotInfo.length, slotInfo.length) + // consts - [slotInfo.length, slotInfo.length) + init(0, slotInfo.nonPositionalFormalStart, slotInfo.varStart, slotInfo.length, + slotInfo.length, flags, 0, JSSLOT_FREE(&CallObject::class_), + data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + FunctionScope::AbstractData<JSAtom>&, uint8_t); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + FunctionScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint8_t); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init(VarScope::AbstractData<NameT>& data, + uint32_t firstFrameSlot) { + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, slotInfo.length) + // lets - [slotInfo.length, slotInfo.length) + // consts - [slotInfo.length, slotInfo.length) + init(0, 0, 0, slotInfo.length, slotInfo.length, + CanHaveFrameSlots | CanHaveEnvironmentSlots, firstFrameSlot, + JSSLOT_FREE(&VarEnvironmentObject::class_), data.trailingNames.start(), + slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + VarScope::AbstractData<JSAtom>&, uint32_t); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + VarScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_t); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + GlobalScope::AbstractData<NameT>& data) { + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, slotInfo.letStart) + // lets - [slotInfo.letStart, slotInfo.constStart) + // consts - [slotInfo.constStart, slotInfo.length) + init(0, 0, 0, slotInfo.letStart, slotInfo.constStart, CannotHaveSlots, + UINT32_MAX, UINT32_MAX, data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + GlobalScope::AbstractData<JSAtom>&); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + GlobalScope::AbstractData<frontend::TaggedParserAtomIndex>&); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init(EvalScope::AbstractData<NameT>& 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; + } + + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, slotInfo.length) + // lets - [slotInfo.length, slotInfo.length) + // consts - [slotInfo.length, slotInfo.length) + init(0, 0, 0, slotInfo.length, slotInfo.length, flags, firstFrameSlot, + firstEnvironmentSlot, data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + EvalScope::AbstractData<JSAtom>&, bool); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + EvalScope::AbstractData<frontend::TaggedParserAtomIndex>&, bool); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + ModuleScope::AbstractData<NameT>& data) { + auto& slotInfo = data.slotInfo; + + // imports - [0, slotInfo.varStart) + // positional formals - [slotInfo.varStart, slotInfo.varStart) + // other formals - [slotInfo.varStart, slotInfo.varStart) + // vars - [slotInfo.varStart, slotInfo.letStart) + // lets - [slotInfo.letStart, slotInfo.constStart) + // consts - [slotInfo.constStart, slotInfo.length) + init(slotInfo.varStart, slotInfo.varStart, slotInfo.varStart, + slotInfo.letStart, slotInfo.constStart, + CanHaveFrameSlots | CanHaveEnvironmentSlots, 0, + JSSLOT_FREE(&ModuleEnvironmentObject::class_), + data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + ModuleScope::AbstractData<JSAtom>&); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + ModuleScope::AbstractData<frontend::TaggedParserAtomIndex>&); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + WasmInstanceScope::AbstractData<NameT>& data) { + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, slotInfo.length) + // lets - [slotInfo.length, slotInfo.length) + // consts - [slotInfo.length, slotInfo.length) + init(0, 0, 0, slotInfo.length, slotInfo.length, + CanHaveFrameSlots | CanHaveEnvironmentSlots, UINT32_MAX, UINT32_MAX, + data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + WasmInstanceScope::AbstractData<JSAtom>&); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + WasmInstanceScope::AbstractData<frontend::TaggedParserAtomIndex>&); + +template <typename NameT> +void BaseAbstractBindingIter<NameT>::init( + WasmFunctionScope::AbstractData<NameT>& data) { + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, slotInfo.length) + // lets - [slotInfo.length, slotInfo.length) + // consts - [slotInfo.length, slotInfo.length) + init(0, 0, 0, slotInfo.length, slotInfo.length, + CanHaveFrameSlots | CanHaveEnvironmentSlots, UINT32_MAX, UINT32_MAX, + data.trailingNames.start(), slotInfo.length); +} +template void BaseAbstractBindingIter<JSAtom>::init( + WasmFunctionScope::AbstractData<JSAtom>&); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + WasmFunctionScope::AbstractData<frontend::TaggedParserAtomIndex>&); + +PositionalFormalParameterIter::PositionalFormalParameterIter(Scope* scope) + : BindingIter(scope) { + // Reinit with flags = 0, i.e., iterate over all positional parameters. + if (scope->is<FunctionScope>()) { + init(scope->as<FunctionScope>().data(), /* flags = */ 0); + } + settle(); +} + +PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script) + : PositionalFormalParameterIter(script->bodyScope()) {} + +void js::DumpBindings(JSContext* cx, Scope* scopeArg) { + RootedScope scope(cx, scopeArg); + for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { + UniqueChars bytes = AtomToPrintableString(cx, bi.name()); + if (!bytes) { + return; + } + fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.get()); + switch (bi.location().kind()) { + case BindingLocation::Kind::Global: + if (bi.isTopLevelFunction()) { + fprintf(stderr, "global function\n"); + } else { + 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); +} + +template <typename... Args> +/* static */ bool ScopeStencil::appendScopeStencilAndData( + JSContext* cx, CompilationState& compilationState, + BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args) { + *indexOut = compilationState.scopeData.length(); + if (uint32_t(*indexOut) >= TaggedScriptThingIndex::IndexLimit) { + ReportAllocationOverflow(cx); + return false; + } + + if (!compilationState.scopeData.emplaceBack(std::forward<Args>(args)...)) { + js::ReportOutOfMemory(cx); + return false; + } + if (!compilationState.scopeNames.append(data)) { + js::ReportOutOfMemory(cx); + return false; + } + + return true; +} + +/* static */ +bool ScopeStencil::createForFunctionScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, + FunctionScope::ParserData* data, bool hasParameterExprs, + bool needsEnvironment, ScriptIndex functionIndex, bool isArrow, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) { + auto kind = ScopeKind::Function; + using ScopeType = FunctionScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + // We do not initialize the canonical function while the data is owned by the + // ScopeStencil. It gets set in ScopeStencil::releaseData. + RootedFunction fun(cx, nullptr); + + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + if (!FunctionScope::prepareForScopeCreation<frontend::TaggedParserAtomIndex>( + cx, &data, hasParameterExprs, needsEnvironment, fun, &envShape)) { + return false; + } + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape, + mozilla::Some(functionIndex), isArrow); +} + +/* static */ +bool ScopeStencil::createForLexicalScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, ScopeKind kind, + LexicalScope::ParserData* data, uint32_t firstFrameSlot, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) { + using ScopeType = LexicalScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + mozilla::Maybe<uint32_t> envShape; + if (!LexicalScope::prepareForScopeCreation<frontend::TaggedParserAtomIndex>( + cx, kind, firstFrameSlot, &data, &envShape)) { + return false; + } + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +bool ScopeStencil::createForVarScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, ScopeKind kind, + VarScope::ParserData* data, uint32_t firstFrameSlot, bool needsEnvironment, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) { + using ScopeType = VarScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + mozilla::Maybe<uint32_t> envShape; + if (!VarScope::prepareForScopeCreation<frontend::TaggedParserAtomIndex>( + cx, kind, &data, firstFrameSlot, needsEnvironment, &envShape)) { + return false; + } + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForGlobalScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, ScopeKind kind, + GlobalScope::ParserData* data, ScopeIndex* index) { + using ScopeType = GlobalScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + // 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. + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + + mozilla::Maybe<ScopeIndex> enclosing; + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForEvalScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, ScopeKind kind, + EvalScope::ParserData* data, mozilla::Maybe<ScopeIndex> enclosing, + ScopeIndex* index) { + using ScopeType = EvalScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + if (!EvalScope::prepareForScopeCreation<frontend::TaggedParserAtomIndex>( + cx, kind, &data, &envShape)) { + return false; + } + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForModuleScope( + JSContext* cx, frontend::CompilationStencil& stencil, + frontend::CompilationState& compilationState, ModuleScope::ParserData* data, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) { + auto kind = ScopeKind::Module; + using ScopeType = ModuleScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(cx, data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(cx, stencil.alloc); + if (!data) { + return false; + } + } + + MOZ_ASSERT(enclosing.isNothing()); + + // We do not initialize the canonical module while the data is owned by the + // ScopeStencil. It gets set in ScopeStencil::releaseData. + RootedModuleObject module(cx, nullptr); + + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + if (!ModuleScope::prepareForScopeCreation<frontend::TaggedParserAtomIndex>( + cx, &data, module, &envShape)) { + return false; + } + + return appendScopeStencilAndData(cx, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +template <typename SpecificEnvironmentT> +bool ScopeStencil::createSpecificShape(JSContext* cx, ScopeKind kind, + BaseScopeData* scopeData, + MutableHandleShape shape) const { + const JSClass* cls = &SpecificEnvironmentT::class_; + uint32_t baseShapeFlags = SpecificEnvironmentT::BASESHAPE_FLAGS; + + if (hasEnvironmentShape()) { + if (numEnvironmentSlots() > 0) { + BindingIter bi(kind, scopeData, firstFrameSlot_); + shape.set(CreateEnvironmentShape(cx, bi, cls, numEnvironmentSlots(), + baseShapeFlags)); + return shape; + } + + shape.set(EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), baseShapeFlags)); + return shape; + } + + return true; +} + +/* static */ +bool ScopeStencil::createForWithScope(JSContext* cx, + CompilationStencil& stencil, + CompilationState& compilationState, + mozilla::Maybe<ScopeIndex> enclosing, + ScopeIndex* index) { + auto kind = ScopeKind::With; + MOZ_ASSERT(matchScopeKind<WithScope>(kind)); + + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + + return appendScopeStencilAndData(cx, compilationState, nullptr, index, kind, + enclosing, firstFrameSlot, envShape); +} + +template <typename SpecificScopeT> +UniquePtr<typename SpecificScopeT::RuntimeData> +ScopeStencil::createSpecificScopeData(JSContext* cx, + CompilationAtomCache& atomCache, + CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const { + return LiftParserScopeData<SpecificScopeT>(cx, atomCache, baseData); +} + +template <> +UniquePtr<FunctionScope::RuntimeData> +ScopeStencil::createSpecificScopeData<FunctionScope>( + JSContext* cx, CompilationAtomCache& atomCache, + CompilationGCOutput& gcOutput, BaseParserScopeData* baseData) const { + // Allocate a new vm function-scope. + UniquePtr<FunctionScope::RuntimeData> data = + LiftParserScopeData<FunctionScope>(cx, atomCache, baseData); + if (!data) { + return nullptr; + } + + // Initialize the HeapPtr in the FunctionScope::RuntimeData. + data->canonicalFunction = gcOutput.functions[functionIndex()]; + + return data; +} + +template <> +UniquePtr<ModuleScope::RuntimeData> +ScopeStencil::createSpecificScopeData<ModuleScope>( + JSContext* cx, CompilationAtomCache& atomCache, + CompilationGCOutput& gcOutput, BaseParserScopeData* baseData) const { + // Allocate a new vm module-scope. + UniquePtr<ModuleScope::RuntimeData> data = + LiftParserScopeData<ModuleScope>(cx, atomCache, baseData); + if (!data) { + return nullptr; + } + + // Initialize the HeapPtr in the ModuleScope::RuntimeData. + data->module = gcOutput.module; + + return data; +} + +// WithScope does not use binding data. +template <> +Scope* ScopeStencil::createSpecificScope<WithScope, std::nullptr_t>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const { + RootedScope enclosingScope(cx, enclosingExistingScope(input, gcOutput)); + return Scope::create(cx, ScopeKind::With, enclosingScope, nullptr); +} + +// GlobalScope has bindings but no environment shape. +template <> +Scope* ScopeStencil::createSpecificScope<GlobalScope, std::nullptr_t>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const { + Rooted<UniquePtr<GlobalScope::RuntimeData>> rootedData( + cx, createSpecificScopeData<GlobalScope>(cx, input.atomCache, gcOutput, + baseData)); + if (!rootedData) { + return nullptr; + } + + MOZ_ASSERT(!enclosing_); + MOZ_ASSERT(!input.enclosingScope); + + // Because we already baked the data here, we needn't do it again. + return Scope::create<GlobalScope>(cx, kind(), nullptr, nullptr, &rootedData); +} + +template <typename SpecificScopeT, typename SpecificEnvironmentT> +Scope* ScopeStencil::createSpecificScope(JSContext* cx, CompilationInput& input, + CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const { + Rooted<UniquePtr<typename SpecificScopeT::RuntimeData>> rootedData( + cx, createSpecificScopeData<SpecificScopeT>(cx, input.atomCache, gcOutput, + baseData)); + if (!rootedData) { + return nullptr; + } + + RootedShape shape(cx); + if (!createSpecificShape<SpecificEnvironmentT>( + cx, kind(), rootedData.get().get(), &shape)) { + return nullptr; + } + + RootedScope enclosingScope(cx, enclosingExistingScope(input, gcOutput)); + + // Because we already baked the data here, we needn't do it again. + return Scope::create<SpecificScopeT>(cx, kind(), enclosingScope, shape, + &rootedData); +} + +template Scope* ScopeStencil::createSpecificScope<FunctionScope, CallObject>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<LexicalScope, LexicalEnvironmentObject>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<EvalScope, VarEnvironmentObject>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<VarScope, VarEnvironmentObject>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<ModuleScope, ModuleEnvironmentObject>( + JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, + BaseParserScopeData* baseData) const; |