diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/Scope.cpp | 1698 |
1 files changed, 1698 insertions, 0 deletions
diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp new file mode 100644 index 0000000000..c48d0976d8 --- /dev/null +++ b/js/src/vm/Scope.cpp @@ -0,0 +1,1698 @@ +/* -*- 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 <new> + +#include "jsnum.h" + +#include "frontend/CompilationStencil.h" // ScopeStencilRef, CompilationStencil, CompilationState, CompilationAtomCache +#include "frontend/ParserAtom.h" // frontend::ParserAtomsTable, frontend::ParserAtom +#include "frontend/ScriptIndex.h" // ScriptIndex +#include "frontend/Stencil.h" +#include "util/StringBuffer.h" +#include "vm/EnvironmentObject.h" +#include "vm/ErrorReporting.h" // MaybePrintAndClearPendingException +#include "vm/JSScript.h" +#include "wasm/WasmDebug.h" +#include "wasm/WasmInstance.h" + +#include "gc/GCContext-inl.h" +#include "gc/ObjectKind-inl.h" +#include "gc/TraceMethods-inl.h" +#include "vm/JSContext-inl.h" +#include "wasm/WasmInstance-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"; + case BindingKind::Synthetic: + return "synthetic"; + case BindingKind::PrivateMethod: + return "private method"; + } + 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"); +} + +SharedShape* js::EmptyEnvironmentShape(JSContext* cx, const JSClass* cls, + uint32_t numSlots, + ObjectFlags objectFlags) { + // Put as many slots into the object header as possible. + uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots)); + return SharedShape::getInitialShape( + cx, cls, cx->realm(), TaggedProto(nullptr), numFixed, objectFlags); +} + +static bool AddToEnvironmentMap(JSContext* cx, const JSClass* clasp, + HandleId id, BindingKind bindKind, + uint32_t slot, + MutableHandle<SharedPropMap*> map, + uint32_t* mapLength, ObjectFlags* objectFlags) { + PropertyFlags propFlags = {PropertyFlag::Enumerable}; + switch (bindKind) { + case BindingKind::Const: + case BindingKind::NamedLambdaCallee: + // Non-writable. + break; + default: + propFlags.setFlag(PropertyFlag::Writable); + break; + } + + return SharedPropMap::addPropertyWithKnownSlot(cx, clasp, map, mapLength, id, + propFlags, slot, objectFlags); +} + +SharedShape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi, + const JSClass* cls, uint32_t numSlots, + ObjectFlags objectFlags) { + Rooted<SharedPropMap*> map(cx); + uint32_t mapLength = 0; + + RootedId id(cx); + for (; bi; bi++) { + BindingLocation loc = bi.location(); + if (loc.kind() == BindingLocation::Kind::Environment) { + JSAtom* name = bi.name(); + MOZ_ASSERT(AtomIsMarked(cx->zone(), name)); + id = NameToId(name->asPropertyName()); + if (!AddToEnvironmentMap(cx, cls, id, bi.kind(), loc.slot(), &map, + &mapLength, &objectFlags)) { + return nullptr; + } + } + } + + uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots)); + return SharedShape::getInitialOrPropMapShape(cx, cls, cx->realm(), + TaggedProto(nullptr), numFixed, + map, mapLength, objectFlags); +} + +template <class DataT> +inline size_t SizeOfAllocatedData(DataT* data) { + return SizeOfScopeData<DataT>(data->length); +} + +template <typename ConcreteScope> +static void MarkParserScopeData(typename ConcreteScope::ParserData* data, + frontend::CompilationState& compilationState) { + auto names = GetScopeDataTrailingNames(data); + for (auto& binding : names) { + auto index = binding.name(); + if (!index) { + continue; + } + compilationState.parserAtoms.markUsedByStencil( + index, frontend::ParserAtom::Atomize::Yes); + } +} + +template <typename ConcreteScope, typename EnvironmentT> +static void PrepareScopeData(ParserBindingIter& bi, + typename ConcreteScope::ParserData* data, + uint32_t firstFrameSlot, + mozilla::Maybe<uint32_t>* envShape) { + const JSClass* cls = &EnvironmentT::class_; + + // 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; + + // Make a new environment shape if any environment slots were used. + if (bi.nextEnvironmentSlot() != JSSLOT_FREE(cls)) { + envShape->emplace(bi.nextEnvironmentSlot()); + } +} + +template <typename ConcreteScope> +static typename ConcreteScope::ParserData* NewEmptyParserScopeData( + FrontendContext* fc, 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(fc); + 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->length)) { + return nullptr; + } + auto names = GetScopeDataTrailingNames(data); + for (size_t i = 0; i < names.size(); 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->length)); + if (!scopeData) { + return nullptr; + } + + // NOTE: There shouldn't be any fallible operation or GC between setting + // `length` and filling `trailingNames`. + scopeData.get()->length = data->length; + + memcpy(&scopeData.get()->slotInfo, &data->slotInfo, + sizeof(typename ConcreteScope::SlotInfo)); + + // Initialize new scoped names. + auto namesOut = GetScopeDataTrailingNames(scopeData.get()); + MOZ_ASSERT(data->length == namesOut.size()); + for (size_t i = 0; i < namesOut.size(); i++) { + namesOut[i] = names[i].copyWithNewAtom(jsatoms[i].get()); + } + + return scopeData; +} + +/* static */ +Scope* Scope::create(JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing, + Handle<SharedShape*> envShape) { + return cx->newCell<Scope>(kind, enclosing, envShape); +} + +template <typename ConcreteScope> +/* static */ +ConcreteScope* Scope::create( + JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing, + Handle<SharedShape*> 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()); +} + +void Scope::updateEnvShapeIfRequired(mozilla::Maybe<uint32_t>* envShape, + bool needsEnvironment) { + if (envShape->isNothing() && needsEnvironment) { + uint32_t numSlots = 0; + envShape->emplace(numSlots); + } +} + +uint32_t Scope::firstFrameSlot() const { + switch (kind()) { + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + // 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::ClassBody: + MOZ_ASSERT(is<ClassBodyScope>()); + return ClassBodyScope::nextFrameSlot(enclosing()); + + 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; +} + +void Scope::finalize(JS::GCContext* gcx) { + MOZ_ASSERT(CurrentThreadIsGCFinalizing()); + applyScopeDataTyped([this, gcx](auto data) { + gcx->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() { + JSContext* cx = TlsContext.get(); + if (!cx) { + fprintf(stderr, "*** can't get JSContext for current thread\n"); + return; + } + for (Rooted<ScopeIter> si(cx, ScopeIter(this)); si; si++) { + fprintf(stderr, "- %s [%p]\n", ScopeKindString(si.kind()), si.scope()); + DumpBindings(cx, si.scope()); + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +} + +#if defined(DEBUG) || defined(JS_JITSPEW) + +/* static */ +bool Scope::dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope, + GenericPrinter& out, const char* indent) { + out.put(ScopeKindString(scope->kind())); + out.put(" {"); + + size_t i = 0; + for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++, i++) { + if (i == 0) { + out.put("\n"); + } + UniqueChars bytes = AtomToPrintableString(cx, bi.name()); + if (!bytes) { + return false; + } + out.put(indent); + out.printf(" %2zu: %s %s ", i, BindingKindString(bi.kind()), bytes.get()); + switch (bi.location().kind()) { + case BindingLocation::Kind::Global: + if (bi.isTopLevelFunction()) { + out.put("(global function)\n"); + } else { + out.put("(global)\n"); + } + break; + case BindingLocation::Kind::Argument: + out.printf("(arg slot %u)\n", bi.location().argumentSlot()); + break; + case BindingLocation::Kind::Frame: + out.printf("(frame slot %u)\n", bi.location().slot()); + break; + case BindingLocation::Kind::Environment: + out.printf("(env slot %u)\n", bi.location().slot()); + break; + case BindingLocation::Kind::NamedLambdaCallee: + out.put("(named lambda callee)\n"); + break; + case BindingLocation::Kind::Import: + out.put("(import)\n"); + break; + } + } + if (i > 0) { + out.put(indent); + } + out.put("}"); + + ScopeIter si(scope); + si++; + for (; si; si++) { + out.put(" -> "); + out.put(ScopeKindString(si.kind())); + } + return true; +} + +#endif /* defined(DEBUG) || defined(JS_JITSPEW) */ + +static uint32_t 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: + return si.scope()->as<LexicalScope>().nextFrameSlot(); + + case ScopeKind::ClassBody: + return si.scope()->as<ClassBodyScope>().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"); +} + +/* static */ +uint32_t LexicalScope::nextFrameSlot(Scope* scope) { + return NextFrameSlot(scope); +} + +/* static */ +uint32_t ClassBodyScope::nextFrameSlot(Scope* scope) { + return NextFrameSlot(scope); +} + +/* static */ +void LexicalScope::prepareForScopeCreation(ScopeKind kind, + uint32_t firstFrameSlot, + LexicalScope::ParserData* data, + mozilla::Maybe<uint32_t>* envShape) { + bool isNamedLambda = + kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda; + + MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT); + + ParserBindingIter bi(*data, firstFrameSlot, isNamedLambda); + PrepareScopeData<LexicalScope, BlockLexicalEnvironmentObject>( + bi, data, firstFrameSlot, envShape); +} + +/* static */ +SharedShape* LexicalScope::getEmptyExtensibleEnvironmentShape(JSContext* cx) { + const JSClass* cls = &LexicalEnvironmentObject::class_; + return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), ObjectFlags()); +} + +/* static */ +void ClassBodyScope::prepareForScopeCreation( + ScopeKind kind, uint32_t firstFrameSlot, ClassBodyScope::ParserData* data, + mozilla::Maybe<uint32_t>* envShape) { + MOZ_ASSERT(kind == ScopeKind::ClassBody); + + ParserBindingIter bi(*data, firstFrameSlot); + PrepareScopeData<ClassBodyScope, BlockLexicalEnvironmentObject>( + bi, data, firstFrameSlot, envShape); +} + +/* static */ +void FunctionScope::prepareForScopeCreation( + FunctionScope::ParserData* data, bool hasParameterExprs, + bool needsEnvironment, mozilla::Maybe<uint32_t>* envShape) { + uint32_t firstFrameSlot = 0; + ParserBindingIter bi(*data, hasParameterExprs); + PrepareScopeData<FunctionScope, CallObject>(bi, data, firstFrameSlot, + envShape); + + if (hasParameterExprs) { + data->slotInfo.setHasParameterExprs(); + } + + // 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()|. + updateEnvShapeIfRequired(envShape, needsEnvironment); +} + +JSScript* FunctionScope::script() const { + return canonicalFunction()->nonLazyScript(); +} + +/* static */ +bool FunctionScope::isSpecialName(frontend::TaggedParserAtomIndex name) { + return name == frontend::TaggedParserAtomIndex::WellKnown::arguments() || + name == frontend::TaggedParserAtomIndex::WellKnown::dot_this_() || + name == frontend::TaggedParserAtomIndex::WellKnown::dot_newTarget_() || + name == frontend::TaggedParserAtomIndex::WellKnown::dot_generator_(); +} + +/* static */ +void VarScope::prepareForScopeCreation(ScopeKind kind, + VarScope::ParserData* data, + uint32_t firstFrameSlot, + bool needsEnvironment, + mozilla::Maybe<uint32_t>* envShape) { + ParserBindingIter bi(*data, firstFrameSlot); + PrepareScopeData<VarScope, VarEnvironmentObject>(bi, data, firstFrameSlot, + envShape); + + // An environment may be needed regardless of existence of any closed over + // bindings: + // - Extensible scopes (i.e., due to direct eval) + // - Being a generator + updateEnvShapeIfRequired(envShape, needsEnvironment); +} + +GlobalScope* GlobalScope::createEmpty(JSContext* cx, ScopeKind kind) { + Rooted<UniquePtr<RuntimeData>> data( + cx, 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 */ +WithScope* WithScope::create(JSContext* cx, Handle<Scope*> enclosing) { + Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr); + return static_cast<WithScope*>(scope); +} + +/* static */ +void EvalScope::prepareForScopeCreation(ScopeKind scopeKind, + EvalScope::ParserData* data, + mozilla::Maybe<uint32_t>* envShape) { + if (scopeKind == ScopeKind::StrictEval) { + uint32_t firstFrameSlot = 0; + ParserBindingIter bi(*data, true); + PrepareScopeData<EvalScope, VarEnvironmentObject>(bi, data, firstFrameSlot, + envShape); + } +} + +/* 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; +} + +ModuleScope::RuntimeData::RuntimeData(size_t length) { + PoisonNames(this, length); +} + +/* static */ +void ModuleScope::prepareForScopeCreation(ModuleScope::ParserData* data, + mozilla::Maybe<uint32_t>* envShape) { + uint32_t firstFrameSlot = 0; + ParserBindingIter bi(*data); + PrepareScopeData<ModuleScope, ModuleEnvironmentObject>( + bi, data, firstFrameSlot, envShape); + + // Modules always need an environment object for now. + bool needsEnvironment = true; + updateEnvShapeIfRequired(envShape, needsEnvironment); +} + +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(NumberValue(index), sb)) { + return nullptr; + } + + return sb.finishAtom(); +} + +static void InitializeTrailingName(AbstractBindingName<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(GetScopeDataTrailingNamesPointer(data.get().get()), + data->length, name); + data->length++; +} + +WasmInstanceScope::RuntimeData::RuntimeData(size_t length) { + PoisonNames(this, length); +} + +/* static */ +WasmInstanceScope* WasmInstanceScope::create(JSContext* cx, + WasmInstanceObject* instance) { + size_t namesCount = 0; + + size_t memoriesStart = namesCount; + size_t memoriesCount = instance->instance().metadata().memories.length(); + namesCount += memoriesCount; + + 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; + } + + Rooted<WasmInstanceObject*> rootedInstance(cx, instance); + for (size_t i = 0; i < memoriesCount; i++) { + JSAtom* wasmName = GenerateWasmName(cx, "memory", i); + 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->length == namesCount); + + data->instance.init(rootedInstance); + data->slotInfo.memoriesStart = memoriesStart; + data->slotInfo.globalsStart = globalsStart; + + Rooted<Scope*> enclosing(cx, &cx->global()->emptyGlobalScope()); + return Scope::create<WasmInstanceScope>(cx, ScopeKind::WasmInstance, + enclosing, + /* envShape = */ nullptr, &data); +} + +/* static */ +WasmFunctionScope* WasmFunctionScope::create(JSContext* cx, + Handle<Scope*> 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->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: + 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::ClassBody: + init(*static_cast<ClassBodyScope::RuntimeData*>(data), firstFrameSlot); + 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()) {} + +AbstractBindingIter<frontend::TaggedParserAtomIndex>::AbstractBindingIter( + const frontend::ScopeStencilRef& ref) + : Base() { + const ScopeStencil& scope = ref.scope(); + BaseParserScopeData* data = ref.context_.scopeNames[ref.scopeIndex_]; + switch (scope.kind()) { + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + init(*static_cast<LexicalScope::ParserData*>(data), + scope.firstFrameSlot(), 0); + break; + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + init(*static_cast<LexicalScope::ParserData*>(data), LOCALNO_LIMIT, + IsNamedLambda); + break; + case ScopeKind::ClassBody: + init(*static_cast<ClassBodyScope::ParserData*>(data), + scope.firstFrameSlot()); + 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::ParserData*>(data) + ->slotInfo.hasParameterExprs()) { + flags |= HasFormalParameterExprs; + } + init(*static_cast<FunctionScope::ParserData*>(data), flags); + break; + } + case ScopeKind::FunctionBodyVar: + init(*static_cast<VarScope::ParserData*>(data), scope.firstFrameSlot()); + break; + case ScopeKind::Eval: + case ScopeKind::StrictEval: + init(*static_cast<EvalScope::ParserData*>(data), + scope.kind() == ScopeKind::StrictEval); + break; + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + init(*static_cast<GlobalScope::ParserData*>(data)); + break; + case ScopeKind::Module: + init(*static_cast<ModuleScope::ParserData*>(data)); + break; + case ScopeKind::WasmInstance: + init(*static_cast<WasmInstanceScope::ParserData*>(data)); + break; + case ScopeKind::WasmFunction: + init(*static_cast<WasmFunctionScope::ParserData*>(data)); + break; + } +} + +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(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ 0, + /* constStart= */ 0, + /* syntheticStart= */ data.length, + /* privageMethodStart= */ data.length, + /* flags= */ CanHaveEnvironmentSlots | flags, + /* firstFrameSlot= */ firstFrameSlot, + /* firstEnvironmentSlot= */ + JSSLOT_FREE(&LexicalEnvironmentObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); + } else { + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, 0) + // lets - [0, slotInfo.constStart) + // consts - [slotInfo.constStart, data.length) + // synthetic - [data.length, data.length) + // private methods - [data.length, data.length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ 0, + /* constStart= */ slotInfo.constStart, + /* syntheticStart= */ data.length, + /* privateMethodStart= */ data.length, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots | flags, + /* firstFrameSlot= */ firstFrameSlot, + /* firstEnvironmentSlot= */ + JSSLOT_FREE(&LexicalEnvironmentObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); + } +} + +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( + ClassBodyScope::AbstractData<NameT>& data, uint32_t firstFrameSlot) { + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, 0) + // lets - [0, 0) + // consts - [0, 0) + // synthetic - [0, slotInfo.privateMethodStart) + // private methods - [slotInfo.privateMethodStart, data.length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ 0, + /* constStart= */ 0, + /* syntheticStart= */ 0, + /* privateMethodStart= */ slotInfo.privateMethodStart, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots, + /* firstFrameSlot= */ firstFrameSlot, + /* firstEnvironmentSlot= */ + JSSLOT_FREE(&ClassBodyLexicalEnvironmentObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); +} + +template void BaseAbstractBindingIter<JSAtom>::init( + ClassBodyScope::AbstractData<JSAtom>&, uint32_t); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + ClassBodyScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_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 length = data.length; + auto& slotInfo = data.slotInfo; + + // imports - [0, 0) + // positional formals - [0, slotInfo.nonPositionalFormalStart) + // other formals - [slotInfo.nonPositionalParamStart, slotInfo.varStart) + // vars - [slotInfo.varStart, length) + // lets - [length, length) + // consts - [length, length) + // synthetic - [length, length) + // private methods - [length, length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ slotInfo.nonPositionalFormalStart, + /* varStart= */ slotInfo.varStart, + /* letStart= */ length, + /* constStart= */ length, + /* syntheticStart= */ length, + /* privateMethodStart= */ length, + /* flags= */ flags, + /* firstFrameSlot= */ 0, + /* firstEnvironmentSlot= */ JSSLOT_FREE(&CallObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); +} +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 length = data.length; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, length) + // lets - [length, length) + // consts - [length, length) + // synthetic - [length, length) + // private methods - [length, length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ length, + /* constStart= */ length, + /* syntheticStart= */ length, + /* privateMethodStart= */ length, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots, + /* firstFrameSlot= */ firstFrameSlot, + /* firstEnvironmentSlot= */ JSSLOT_FREE(&VarEnvironmentObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); +} +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, data.length) + // synthetic - [data.length, data.length) + // private methods - [data.length, data.length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ slotInfo.letStart, + /* constStart= */ slotInfo.constStart, + /* syntheticStart= */ data.length, + /* privateMethoodStart= */ data.length, + /* flags= */ CannotHaveSlots, + /* firstFrameSlot= */ UINT32_MAX, + /* firstEnvironmentSlot= */ UINT32_MAX, + /* names= */ GetScopeDataTrailingNames(&data)); +} +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 length = data.length; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, length) + // lets - [length, length) + // consts - [length, length) + // synthetic - [length, length) + // private methods - [length, length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ length, + /* constStart= */ length, + /* syntheticStart= */ length, + /* privateMethodStart= */ length, + /* flags= */ flags, + /* firstFrameSlot= */ firstFrameSlot, + /* firstEnvironmentSlot= */ firstEnvironmentSlot, + /* names= */ GetScopeDataTrailingNames(&data)); +} +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, data.length) + // synthetic - [data.length, data.length) + // private methods - [data.length, data.length) + init( + /* positionalFormalStart= */ slotInfo.varStart, + /* nonPositionalFormalStart= */ slotInfo.varStart, + /* varStart= */ slotInfo.varStart, + /* letStart= */ slotInfo.letStart, + /* constStart= */ slotInfo.constStart, + /* syntheticStart= */ data.length, + /* privateMethodStart= */ data.length, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots, + /* firstFrameSlot= */ 0, + /* firstEnvironmentSlot= */ JSSLOT_FREE(&ModuleEnvironmentObject::class_), + /* names= */ GetScopeDataTrailingNames(&data)); +} +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 length = data.length; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, length) + // lets - [length, length) + // consts - [length, length) + // synthetic - [length, length) + // private methods - [length, length) + init(/* positionalFormalStart= */ 0, + /* nonPositionalFormalStart= */ 0, + /* varStart= */ 0, + /* letStart= */ length, + /* constStart= */ length, + /* syntheticStart= */ length, + /* privateMethodStart= */ length, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots, + /* firstFrameSlot= */ UINT32_MAX, + /* firstEnvironmentSlot= */ UINT32_MAX, + /* names= */ GetScopeDataTrailingNames(&data)); +} +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 length = data.length; + + // imports - [0, 0) + // positional formals - [0, 0) + // other formals - [0, 0) + // vars - [0, length) + // lets - [length, length) + // consts - [length, length) + // synthetic - [length, length) + // private methods - [length, length) + init(/* positionalFormalStart = */ 0, + /* nonPositionalFormalStart = */ 0, + /* varStart= */ 0, + /* letStart= */ length, + /* constStart= */ length, + /* syntheticStart= */ length, + /* privateMethodStart= */ length, + /* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots, + /* firstFrameSlot= */ UINT32_MAX, + /* firstEnvironmentSlot= */ UINT32_MAX, + /* names= */ GetScopeDataTrailingNames(&data)); +} +template void BaseAbstractBindingIter<JSAtom>::init( + WasmFunctionScope::AbstractData<JSAtom>&); +template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init( + WasmFunctionScope::AbstractData<frontend::TaggedParserAtomIndex>&); + +AbstractPositionalFormalParameterIter< + JSAtom>::AbstractPositionalFormalParameterIter(Scope* scope) + : Base(scope) { + // Reinit with flags = 0, i.e., iterate over all positional parameters. + if (scope->is<FunctionScope>()) { + init(scope->as<FunctionScope>().data(), /* flags = */ 0); + } + settle(); +} + +AbstractPositionalFormalParameterIter< + JSAtom>::AbstractPositionalFormalParameterIter(JSScript* script) + : AbstractPositionalFormalParameterIter(script->bodyScope()) {} + +void js::DumpBindings(JSContext* cx, Scope* scopeArg) { + Rooted<Scope*> scope(cx, scopeArg); + for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { + UniqueChars bytes = AtomToPrintableString(cx, bi.name()); + if (!bytes) { + MaybePrintAndClearPendingException(cx); + 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>() && !si.scope()->is<ClassBodyScope>()) { + continue; + } + + // Is the slot within bounds of the current lexical scope? + if (slot < si.scope()->firstFrameSlot()) { + continue; + } + if (slot >= LexicalScope::nextFrameSlot(si.scope())) { + break; + } + + // If so, get the name. + if (JSAtom* name = GetFrameSlotNameInScope(si.scope(), 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( + FrontendContext* fc, CompilationState& compilationState, + BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args) { + *indexOut = ScopeIndex(compilationState.scopeData.length()); + if (uint32_t(*indexOut) >= TaggedScriptThingIndex::IndexLimit) { + ReportAllocationOverflow(fc); + return false; + } + + if (!compilationState.scopeData.emplaceBack(std::forward<Args>(args)...)) { + js::ReportOutOfMemory(fc); + return false; + } + if (!compilationState.scopeNames.append(data)) { + compilationState.scopeData.popBack(); + MOZ_ASSERT(compilationState.scopeData.length() == + compilationState.scopeNames.length()); + + js::ReportOutOfMemory(fc); + return false; + } + + return true; +} + +/* static */ +bool ScopeStencil::createForFunctionScope( + FrontendContext* fc, 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>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + FunctionScope::prepareForScopeCreation(data, hasParameterExprs, + needsEnvironment, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape, + mozilla::Some(functionIndex), isArrow); +} + +/* static */ +bool ScopeStencil::createForLexicalScope( + FrontendContext* fc, 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>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + mozilla::Maybe<uint32_t> envShape; + ScopeType::prepareForScopeCreation(kind, firstFrameSlot, data, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForClassBodyScope( + FrontendContext* fc, frontend::CompilationState& compilationState, + ScopeKind kind, ClassBodyScope::ParserData* data, uint32_t firstFrameSlot, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) { + using ScopeType = ClassBodyScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + mozilla::Maybe<uint32_t> envShape; + ScopeType::prepareForScopeCreation(kind, firstFrameSlot, data, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +bool ScopeStencil::createForVarScope( + FrontendContext* fc, 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>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + mozilla::Maybe<uint32_t> envShape; + VarScope::prepareForScopeCreation(kind, data, firstFrameSlot, + needsEnvironment, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForGlobalScope( + FrontendContext* fc, frontend::CompilationState& compilationState, + ScopeKind kind, GlobalScope::ParserData* data, ScopeIndex* index) { + using ScopeType = GlobalScope; + MOZ_ASSERT(matchScopeKind<ScopeType>(kind)); + + if (data) { + MarkParserScopeData<ScopeType>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.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(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForEvalScope( + FrontendContext* fc, 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>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + uint32_t firstFrameSlot = 0; + mozilla::Maybe<uint32_t> envShape; + EvalScope::prepareForScopeCreation(kind, data, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +/* static */ +bool ScopeStencil::createForModuleScope( + FrontendContext* fc, 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>(data, compilationState); + } else { + data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc); + if (!data) { + return false; + } + } + + MOZ_ASSERT(enclosing.isNothing()); + + // 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; + ModuleScope::prepareForScopeCreation(data, &envShape); + + return appendScopeStencilAndData(fc, compilationState, data, index, kind, + enclosing, firstFrameSlot, envShape); +} + +template <typename SpecificEnvironmentT> +bool ScopeStencil::createSpecificShape( + JSContext* cx, ScopeKind kind, BaseScopeData* scopeData, + MutableHandle<SharedShape*> shape) const { + const JSClass* cls = &SpecificEnvironmentT::class_; + constexpr ObjectFlags objectFlags = SpecificEnvironmentT::OBJECT_FLAGS; + + if (hasEnvironmentShape()) { + if (numEnvironmentSlots() > 0) { + BindingIter bi(kind, scopeData, firstFrameSlot_); + shape.set(CreateEnvironmentShape(cx, bi, cls, numEnvironmentSlots(), + objectFlags)); + return shape; + } + + shape.set(EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), objectFlags)); + return shape; + } + + return true; +} + +/* static */ +bool ScopeStencil::createForWithScope(FrontendContext* fc, + 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(fc, compilationState, nullptr, index, kind, + enclosing, firstFrameSlot, envShape); +} + +template <typename SpecificScopeT> +UniquePtr<typename SpecificScopeT::RuntimeData> +ScopeStencil::createSpecificScopeData(JSContext* cx, + CompilationAtomCache& atomCache, + BaseParserScopeData* baseData) const { + return LiftParserScopeData<SpecificScopeT>(cx, atomCache, baseData); +} + +template <> +UniquePtr<FunctionScope::RuntimeData> +ScopeStencil::createSpecificScopeData<FunctionScope>( + JSContext* cx, CompilationAtomCache& atomCache, + BaseParserScopeData* baseData) const { + // Allocate a new vm function-scope. + UniquePtr<FunctionScope::RuntimeData> data = + LiftParserScopeData<FunctionScope>(cx, atomCache, baseData); + if (!data) { + return nullptr; + } + + return data; +} + +template <> +UniquePtr<ModuleScope::RuntimeData> +ScopeStencil::createSpecificScopeData<ModuleScope>( + JSContext* cx, CompilationAtomCache& atomCache, + BaseParserScopeData* baseData) const { + // Allocate a new vm module-scope. + UniquePtr<ModuleScope::RuntimeData> data = + LiftParserScopeData<ModuleScope>(cx, atomCache, baseData); + if (!data) { + return nullptr; + } + + return data; +} + +// WithScope does not use binding data. +template <> +Scope* ScopeStencil::createSpecificScope<WithScope, std::nullptr_t>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const { + 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, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const { + Rooted<UniquePtr<GlobalScope::RuntimeData>> rootedData( + cx, createSpecificScopeData<GlobalScope>(cx, atomCache, baseData)); + if (!rootedData) { + return nullptr; + } + + MOZ_ASSERT(!hasEnclosing()); + MOZ_ASSERT(!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, + CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, + BaseParserScopeData* baseData) const { + Rooted<UniquePtr<typename SpecificScopeT::RuntimeData>> rootedData( + cx, createSpecificScopeData<SpecificScopeT>(cx, atomCache, baseData)); + if (!rootedData) { + return nullptr; + } + + Rooted<SharedShape*> shape(cx); + if (!createSpecificShape<SpecificEnvironmentT>( + cx, kind(), rootedData.get().get(), &shape)) { + return nullptr; + } + + // 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, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<LexicalScope, BlockLexicalEnvironmentObject>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; +template Scope* ScopeStencil::createSpecificScope< + ClassBodyScope, BlockLexicalEnvironmentObject>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<EvalScope, VarEnvironmentObject>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<VarScope, VarEnvironmentObject>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; +template Scope* +ScopeStencil::createSpecificScope<ModuleScope, ModuleEnvironmentObject>( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const; |