/* -*- 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 #include #include "builtin/ModuleObject.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 "gc/Allocator.h" #include "gc/MaybeRooted.h" #include "util/StringBuffer.h" #include "vm/EnvironmentObject.h" #include "vm/ErrorReporting.h" // MaybePrintAndClearPendingException #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"; 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"); } Shape* 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 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); } Shape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags) { Rooted 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(); cx->markAtom(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); } Shape* js::CreateEnvironmentShape( JSContext* cx, frontend::CompilationAtomCache& atomCache, AbstractBindingIter& bi, const JSClass* cls, uint32_t numSlots, ObjectFlags objectFlags) { Rooted map(cx); uint32_t mapLength = 0; RootedId id(cx); for (; bi; bi++) { BindingLocation loc = bi.location(); if (loc.kind() == BindingLocation::Kind::Environment) { JSAtom* name = atomCache.getExistingAtomAt(cx, bi.name()); MOZ_ASSERT(name); cx->markAtom(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 inline size_t SizeOfAllocatedData(DataT* data) { return SizeOfScopeData(data->length); } template static UniquePtr 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. auto names = GetScopeDataTrailingNames(data); for (auto binding : names) { if (JSAtom* name = binding.name()) { cx->markAtom(name); } } size_t size = SizeOfAllocatedData(data); void* bytes = cx->pod_malloc(size); if (!bytes) { return nullptr; } auto* dataCopy = new (bytes) Data(*data); std::uninitialized_copy_n(GetScopeDataTrailingNamesPointer(data), data->length, GetScopeDataTrailingNamesPointer(dataCopy)); return UniquePtr(dataCopy); } template static void MarkParserScopeData(JSContext* cx, 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); } } static bool SetEnvironmentShape(JSContext* cx, BindingIter& freshBi, BindingIter& bi, const JSClass* cls, uint32_t firstFrameSlot, ObjectFlags objectFlags, MutableHandleShape envShape) { envShape.set(CreateEnvironmentShape(cx, freshBi, cls, bi.nextEnvironmentSlot(), objectFlags)); return envShape; } static bool SetEnvironmentShape(JSContext* cx, ParserBindingIter& freshBi, ParserBindingIter& bi, const JSClass* cls, uint32_t firstFrameSlot, ObjectFlags objectFlags, mozilla::Maybe* envShape) { envShape->emplace(bi.nextEnvironmentSlot()); return true; } template static bool PrepareScopeData( JSContext* cx, AbstractBindingIter& bi, typename MaybeRootedScopeData::HandleType data, uint32_t firstFrameSlot, ShapeT envShape) { const JSClass* cls = &EnvironmentT::class_; constexpr ObjectFlags objectFlags = EnvironmentT::OBJECT_FLAGS; // Copy a fresh BindingIter for use below. AbstractBindingIter 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, objectFlags, envShape)) { return false; } } return true; } template static typename ConcreteScope::ParserData* NewEmptyParserScopeData( JSContext* cx, LifoAlloc& alloc, uint32_t length = 0) { using Data = typename ConcreteScope::ParserData; size_t dataSize = SizeOfScopeData(length); void* raw = alloc.alloc(dataSize); if (!raw) { js::ReportOutOfMemory(cx); return nullptr; } return new (raw) Data(length); } template static UniquePtr> NewEmptyScopeData( JSContext* cx, uint32_t length = 0) { using Data = AbstractScopeData; size_t dataSize = SizeOfScopeData(length); uint8_t* bytes = cx->pod_malloc(dataSize); auto data = reinterpret_cast(bytes); if (data) { new (data) Data(length); } return UniquePtr(data); } template static UniquePtr LiftParserScopeData( JSContext* cx, frontend::CompilationAtomCache& atomCache, BaseParserScopeData* baseData) { using ConcreteData = typename ConcreteScope::RuntimeData; auto* data = static_cast(baseData); // Convert all scope ParserAtoms to rooted JSAtoms. // Rooting is necessary as conversion can gc. JS::RootedVector 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 scopeData( NewEmptyScopeData(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, HandleScope enclosing, HandleShape envShape) { Scope* scope = Allocate(cx); if (scope) { new (scope) Scope(kind, enclosing, envShape); } return scope; } template /* static */ ConcreteScope* Scope::create( JSContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape, MutableHandle> 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(data); return &scope->as(); } template inline void Scope::initData( MutableHandle> data) { MOZ_ASSERT(!rawData()); AddCellMemory(this, SizeOfAllocatedData(data.get().get()), MemoryUse::ScopeData); setHeaderPtr(data.get().release()); } template bool Scope::updateEnvShapeIfRequired(JSContext* cx, MutableHandleShape envShape, bool needsEnvironment) { if (!envShape && needsEnvironment) { envShape.set(EmptyEnvironmentShape(cx)); if (!envShape) { return false; } } return true; } template bool Scope::updateEnvShapeIfRequired(JSContext* cx, mozilla::Maybe* 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: // For intra-frame scopes, find the enclosing scope's next frame slot. MOZ_ASSERT(is()); 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()); return ClassBodyScope::nextFrameSlot(enclosing()); case ScopeKind::FunctionBodyVar: if (enclosing()->is()) { return enclosing()->as().nextFrameSlot(); } break; default: break; } return 0; } uint32_t Scope::chainLength() const { uint32_t length = 0; for (ScopeIter si(const_cast(this)); si; si++) { length++; } return length; } uint32_t Scope::environmentChainLength() const { uint32_t length = 0; for (ScopeIter si(const_cast(this)); si; si++) { if (si.hasSyntacticEnvironment()) { length++; } } return length; } 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() { JSContext* cx = TlsContext.get(); if (!cx) { fprintf(stderr, "*** can't get JSContext for current thread\n"); return; } for (Rooted 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, GenericPrinter& out, const char* indent) { if (!out.put(ScopeKindString(scope->kind()))) { return false; } if (!out.put(" {")) { return false; } size_t i = 0; for (Rooted 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 NextFrameSlot(Scope* scope) { for (ScopeIter si(scope); si; si++) { switch (si.kind()) { case ScopeKind::With: continue; case ScopeKind::Function: return si.scope()->as().nextFrameSlot(); case ScopeKind::FunctionBodyVar: return si.scope()->as().nextFrameSlot(); case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::FunctionLexical: return si.scope()->as().nextFrameSlot(); case ScopeKind::ClassBody: return si.scope()->as().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().nextFrameSlot(); case ScopeKind::Global: case ScopeKind::NonSyntactic: return 0; case ScopeKind::Module: return si.scope()->as().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); } template bool LexicalScope::prepareForScopeCreation( JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, typename MaybeRootedScopeData::MutableHandleType data, ShapeT envShape) { bool isNamedLambda = kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda; MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT); AbstractBindingIter bi(*data, firstFrameSlot, isNamedLambda); if (!PrepareScopeData( cx, bi, data, firstFrameSlot, envShape)) { return false; } return true; } /* static */ LexicalScope* LexicalScope::createWithData( JSContext* cx, ScopeKind kind, MutableHandle> data, uint32_t firstFrameSlot, HandleScope enclosing) { RootedShape envShape(cx); if (!prepareForScopeCreation(cx, kind, firstFrameSlot, data, &envShape)) { return nullptr; } auto scope = Scope::create(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), ObjectFlags()); } template bool ClassBodyScope::prepareForScopeCreation( JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, typename MaybeRootedScopeData::MutableHandleType data, ShapeT envShape) { MOZ_ASSERT(kind == ScopeKind::ClassBody); AbstractBindingIter bi(*data, firstFrameSlot); return PrepareScopeData( cx, bi, data, firstFrameSlot, envShape); } /* static */ ClassBodyScope* ClassBodyScope::createWithData( JSContext* cx, ScopeKind kind, MutableHandle> data, uint32_t firstFrameSlot, HandleScope enclosing) { RootedShape envShape(cx); if (!prepareForScopeCreation(cx, kind, firstFrameSlot, data, &envShape)) { return nullptr; } auto* scope = Scope::create(cx, kind, enclosing, envShape, data); if (!scope) { return nullptr; } MOZ_ASSERT(scope->firstFrameSlot() == firstFrameSlot); return scope; } static void SetCanonicalFunction(FunctionScope::RuntimeData& data, HandleFunction fun) { data.canonicalFunction.init(fun); } static void SetCanonicalFunction(FunctionScope::ParserData& data, HandleFunction fun) {} template bool FunctionScope::prepareForScopeCreation( JSContext* cx, typename MaybeRootedScopeData::MutableHandleType data, bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, ShapeT envShape) { uint32_t firstFrameSlot = 0; AbstractBindingIter bi(*data, hasParameterExprs); if (!PrepareScopeData( 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(cx, envShape, needsEnvironment); } /* static */ FunctionScope* FunctionScope::createWithData( JSContext* cx, MutableHandle> data, bool hasParameterExprs, bool needsEnvironment, HandleFunction fun, HandleScope enclosing) { MOZ_ASSERT(data); MOZ_ASSERT(fun->isTenured()); RootedShape envShape(cx); if (!prepareForScopeCreation(cx, data, hasParameterExprs, needsEnvironment, fun, &envShape)) { return nullptr; } return Scope::create(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::WellKnown::arguments() || name == frontend::TaggedParserAtomIndex::WellKnown::dotThis() || name == frontend::TaggedParserAtomIndex::WellKnown::dotGenerator(); } template bool VarScope::prepareForScopeCreation( JSContext* cx, ScopeKind kind, typename MaybeRootedScopeData::MutableHandleType data, uint32_t firstFrameSlot, bool needsEnvironment, ShapeT envShape) { AbstractBindingIter bi(*data, firstFrameSlot); if (!PrepareScopeData( 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(cx, envShape, needsEnvironment); } /* static */ VarScope* VarScope::createWithData(JSContext* cx, ScopeKind kind, MutableHandle> data, uint32_t firstFrameSlot, bool needsEnvironment, HandleScope enclosing) { MOZ_ASSERT(data); RootedShape envShape(cx); if (!prepareForScopeCreation(cx, kind, data, firstFrameSlot, needsEnvironment, &envShape)) { return nullptr; } return Scope::create(cx, kind, enclosing, envShape, data); } /* static */ GlobalScope* GlobalScope::create(JSContext* cx, ScopeKind kind, Handle 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> data( cx, dataArg ? CopyScopeData(cx, dataArg) : NewEmptyScopeData(cx)); if (!data) { return nullptr; } return createWithData(cx, kind, &data); } /* static */ GlobalScope* GlobalScope::createWithData( JSContext* cx, ScopeKind kind, MutableHandle> 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(cx, kind, nullptr, nullptr, data); } /* static */ WithScope* WithScope::create(JSContext* cx, HandleScope enclosing) { Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr); return static_cast(scope); } template bool EvalScope::prepareForScopeCreation( JSContext* cx, ScopeKind scopeKind, typename MaybeRootedScopeData::MutableHandleType data, ShapeT envShape) { if (scopeKind == ScopeKind::StrictEval) { uint32_t firstFrameSlot = 0; AbstractBindingIter bi(*data, true); if (!PrepareScopeData( cx, bi, data, firstFrameSlot, envShape)) { return false; } } return true; } /* static */ EvalScope* EvalScope::createWithData(JSContext* cx, ScopeKind scopeKind, MutableHandle> data, HandleScope enclosing) { MOZ_ASSERT(data); RootedShape envShape(cx); if (!prepareForScopeCreation(cx, scopeKind, data, &envShape)) { return nullptr; } return Scope::create(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; } ModuleScope::RuntimeData::RuntimeData(size_t length) { PoisonNames(this, length); } static void InitModule(ModuleScope::RuntimeData& data, HandleModuleObject module) { data.module.init(module); } static void InitModule(ModuleScope::ParserData& data, HandleModuleObject module) {} /* static */ template bool ModuleScope::prepareForScopeCreation( JSContext* cx, typename MaybeRootedScopeData::MutableHandleType data, HandleModuleObject module, ShapeT envShape) { uint32_t firstFrameSlot = 0; AbstractBindingIter bi(*data); if (!PrepareScopeData( cx, bi, data, firstFrameSlot, envShape)) { return false; } InitModule(*data, module); // Modules always need an environment object for now. bool needsEnvironment = true; return updateEnvShapeIfRequired(cx, envShape, needsEnvironment); } /* static */ ModuleScope* ModuleScope::createWithData( JSContext* cx, MutableHandle> data, HandleModuleObject module, HandleScope enclosing) { MOZ_ASSERT(data); MOZ_ASSERT(enclosing->is()); RootedShape envShape(cx); if (!prepareForScopeCreation(cx, data, module, &envShape)) { return nullptr; } return Scope::create(cx, ScopeKind::Module, enclosing, envShape, data); } template 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(); } static void InitializeTrailingName(AbstractBindingName* trailingNames, size_t i, JSAtom* name) { void* trailingName = &trailingNames[i]; new (trailingName) BindingName(name, false); } template static void InitializeNextTrailingName(const Rooted>& 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; if (instance->instance().memory()) { namesCount++; } size_t globalsStart = namesCount; size_t globalsCount = instance->instance().metadata().globals.length(); namesCount += globalsCount; Rooted> data( cx, NewEmptyScopeData(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->length == namesCount); data->instance.init(instance); data->slotInfo.globalsStart = globalsStart; RootedScope enclosing(cx, &cx->global()->emptyGlobalScope()); return Scope::create(cx, ScopeKind::WasmInstance, enclosing, /* envShape = */ nullptr, &data); } /* static */ WasmFunctionScope* WasmFunctionScope::create(JSContext* cx, HandleScope enclosing, uint32_t funcIndex) { MOZ_ASSERT(enclosing->is()); Rooted wasmFunctionScope(cx); Rooted instance( cx, enclosing->as().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> data( cx, NewEmptyScopeData(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(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::AbstractBindingIter(ScopeKind kind, BaseScopeData* data, uint32_t firstFrameSlot) : BaseAbstractBindingIter() { switch (kind) { case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::FunctionLexical: init(*static_cast(data), firstFrameSlot, 0); break; case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: init(*static_cast(data), LOCALNO_LIMIT, IsNamedLambda); break; case ScopeKind::ClassBody: init(*static_cast(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(data) ->slotInfo.hasParameterExprs()) { flags |= HasFormalParameterExprs; } init(*static_cast(data), flags); break; } case ScopeKind::FunctionBodyVar: init(*static_cast(data), firstFrameSlot); break; case ScopeKind::Eval: case ScopeKind::StrictEval: init(*static_cast(data), kind == ScopeKind::StrictEval); break; case ScopeKind::Global: case ScopeKind::NonSyntactic: init(*static_cast(data)); break; case ScopeKind::Module: init(*static_cast(data)); break; case ScopeKind::WasmInstance: init(*static_cast(data)); break; case ScopeKind::WasmFunction: init(*static_cast(data)); break; } } AbstractBindingIter::AbstractBindingIter(Scope* scope) : AbstractBindingIter(scope->kind(), scope->rawData(), scope->firstFrameSlot()) {} AbstractBindingIter::AbstractBindingIter(JSScript* script) : AbstractBindingIter(script->bodyScope()) {} AbstractBindingIter::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(data), scope.firstFrameSlot(), 0); break; case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: init(*static_cast(data), LOCALNO_LIMIT, IsNamedLambda); break; case ScopeKind::ClassBody: init(*static_cast(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(data) ->slotInfo.hasParameterExprs()) { flags |= HasFormalParameterExprs; } init(*static_cast(data), flags); break; } case ScopeKind::FunctionBodyVar: init(*static_cast(data), scope.firstFrameSlot()); break; case ScopeKind::Eval: case ScopeKind::StrictEval: init(*static_cast(data), scope.kind() == ScopeKind::StrictEval); break; case ScopeKind::Global: case ScopeKind::NonSyntactic: init(*static_cast(data)); break; case ScopeKind::Module: init(*static_cast(data)); break; case ScopeKind::WasmInstance: init(*static_cast(data)); break; case ScopeKind::WasmFunction: init(*static_cast(data)); break; } } template void BaseAbstractBindingIter::init( LexicalScope::AbstractData& 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::init( LexicalScope::AbstractData&, uint32_t, uint8_t); template void BaseAbstractBindingIter::init( LexicalScope::AbstractData&, uint32_t, uint8_t); template void BaseAbstractBindingIter::init( ClassBodyScope::AbstractData& 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::init( ClassBodyScope::AbstractData&, uint32_t); template void BaseAbstractBindingIter::init( ClassBodyScope::AbstractData&, uint32_t); template void BaseAbstractBindingIter::init( FunctionScope::AbstractData& 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::init( FunctionScope::AbstractData&, uint8_t); template void BaseAbstractBindingIter::init( FunctionScope::AbstractData&, uint8_t); template void BaseAbstractBindingIter::init(VarScope::AbstractData& 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::init( VarScope::AbstractData&, uint32_t); template void BaseAbstractBindingIter::init( VarScope::AbstractData&, uint32_t); template void BaseAbstractBindingIter::init( GlobalScope::AbstractData& 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::init( GlobalScope::AbstractData&); template void BaseAbstractBindingIter::init( GlobalScope::AbstractData&); template void BaseAbstractBindingIter::init(EvalScope::AbstractData& 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::init( EvalScope::AbstractData&, bool); template void BaseAbstractBindingIter::init( EvalScope::AbstractData&, bool); template void BaseAbstractBindingIter::init( ModuleScope::AbstractData& 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::init( ModuleScope::AbstractData&); template void BaseAbstractBindingIter::init( ModuleScope::AbstractData&); template void BaseAbstractBindingIter::init( WasmInstanceScope::AbstractData& 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::init( WasmInstanceScope::AbstractData&); template void BaseAbstractBindingIter::init( WasmInstanceScope::AbstractData&); template void BaseAbstractBindingIter::init( WasmFunctionScope::AbstractData& 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::init( WasmFunctionScope::AbstractData&); template void BaseAbstractBindingIter::init( WasmFunctionScope::AbstractData&); PositionalFormalParameterIter::PositionalFormalParameterIter(Scope* scope) : BindingIter(scope) { // Reinit with flags = 0, i.e., iterate over all positional parameters. if (scope->is()) { init(scope->as().data(), /* flags = */ 0); } settle(); } PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script) : PositionalFormalParameterIter(script->bodyScope()) {} void js::DumpBindings(JSContext* cx, Scope* scopeArg) { RootedScope scope(cx, scopeArg); for (Rooted 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() && !si.scope()->is()) { 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::size( mozilla::MallocSizeOf mallocSizeOf) const { return js::gc::Arena::thingSize(get().asTenured().getAllocKind()) + get().sizeOfExcludingThis(mallocSizeOf); } template /* static */ bool ScopeStencil::appendScopeStencilAndData( JSContext* cx, CompilationState& compilationState, BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args) { *indexOut = ScopeIndex(compilationState.scopeData.length()); if (uint32_t(*indexOut) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(cx); return false; } if (!compilationState.scopeData.emplaceBack(std::forward(args)...)) { js::ReportOutOfMemory(cx); return false; } if (!compilationState.scopeNames.append(data)) { compilationState.scopeData.popBack(); MOZ_ASSERT(compilationState.scopeData.length() == compilationState.scopeNames.length()); js::ReportOutOfMemory(cx); return false; } return true; } /* static */ bool ScopeStencil::createForFunctionScope( JSContext* cx, frontend::CompilationState& compilationState, FunctionScope::ParserData* data, bool hasParameterExprs, bool needsEnvironment, ScriptIndex functionIndex, bool isArrow, mozilla::Maybe enclosing, ScopeIndex* index) { auto kind = ScopeKind::Function; using ScopeType = FunctionScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.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 envShape; if (!FunctionScope::prepareForScopeCreation( 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::CompilationState& compilationState, ScopeKind kind, LexicalScope::ParserData* data, uint32_t firstFrameSlot, mozilla::Maybe enclosing, ScopeIndex* index) { using ScopeType = LexicalScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.alloc); if (!data) { return false; } } mozilla::Maybe envShape; if (!ScopeType::prepareForScopeCreation( cx, kind, firstFrameSlot, &data, &envShape)) { return false; } return appendScopeStencilAndData(cx, compilationState, data, index, kind, enclosing, firstFrameSlot, envShape); } /* static */ bool ScopeStencil::createForClassBodyScope( JSContext* cx, frontend::CompilationState& compilationState, ScopeKind kind, ClassBodyScope::ParserData* data, uint32_t firstFrameSlot, mozilla::Maybe enclosing, ScopeIndex* index) { using ScopeType = ClassBodyScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.alloc); if (!data) { return false; } } mozilla::Maybe envShape; if (!ScopeType::prepareForScopeCreation( cx, kind, firstFrameSlot, &data, &envShape)) { return false; } return appendScopeStencilAndData(cx, compilationState, data, index, kind, enclosing, firstFrameSlot, envShape); } bool ScopeStencil::createForVarScope( JSContext* cx, frontend::CompilationState& compilationState, ScopeKind kind, VarScope::ParserData* data, uint32_t firstFrameSlot, bool needsEnvironment, mozilla::Maybe enclosing, ScopeIndex* index) { using ScopeType = VarScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.alloc); if (!data) { return false; } } mozilla::Maybe envShape; if (!VarScope::prepareForScopeCreation( 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::CompilationState& compilationState, ScopeKind kind, GlobalScope::ParserData* data, ScopeIndex* index) { using ScopeType = GlobalScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, 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 envShape; mozilla::Maybe enclosing; return appendScopeStencilAndData(cx, compilationState, data, index, kind, enclosing, firstFrameSlot, envShape); } /* static */ bool ScopeStencil::createForEvalScope( JSContext* cx, frontend::CompilationState& compilationState, ScopeKind kind, EvalScope::ParserData* data, mozilla::Maybe enclosing, ScopeIndex* index) { using ScopeType = EvalScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.alloc); if (!data) { return false; } } uint32_t firstFrameSlot = 0; mozilla::Maybe envShape; if (!EvalScope::prepareForScopeCreation( cx, kind, &data, &envShape)) { return false; } return appendScopeStencilAndData(cx, compilationState, data, index, kind, enclosing, firstFrameSlot, envShape); } /* static */ bool ScopeStencil::createForModuleScope( JSContext* cx, frontend::CompilationState& compilationState, ModuleScope::ParserData* data, mozilla::Maybe enclosing, ScopeIndex* index) { auto kind = ScopeKind::Module; using ScopeType = ModuleScope; MOZ_ASSERT(matchScopeKind(kind)); if (data) { MarkParserScopeData(cx, data, compilationState); } else { data = NewEmptyParserScopeData(cx, compilationState.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 envShape; if (!ModuleScope::prepareForScopeCreation( cx, &data, module, &envShape)) { return false; } return appendScopeStencilAndData(cx, compilationState, data, index, kind, enclosing, firstFrameSlot, envShape); } template bool ScopeStencil::createSpecificShape(JSContext* cx, ScopeKind kind, BaseScopeData* scopeData, MutableHandleShape 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(JSContext* cx, CompilationState& compilationState, mozilla::Maybe enclosing, ScopeIndex* index) { auto kind = ScopeKind::With; MOZ_ASSERT(matchScopeKind(kind)); uint32_t firstFrameSlot = 0; mozilla::Maybe envShape; return appendScopeStencilAndData(cx, compilationState, nullptr, index, kind, enclosing, firstFrameSlot, envShape); } template UniquePtr ScopeStencil::createSpecificScopeData(JSContext* cx, CompilationAtomCache& atomCache, BaseParserScopeData* baseData) const { return LiftParserScopeData(cx, atomCache, baseData); } template <> UniquePtr ScopeStencil::createSpecificScopeData( JSContext* cx, CompilationAtomCache& atomCache, BaseParserScopeData* baseData) const { // Allocate a new vm function-scope. UniquePtr data = LiftParserScopeData(cx, atomCache, baseData); if (!data) { return nullptr; } return data; } template <> UniquePtr ScopeStencil::createSpecificScopeData( JSContext* cx, CompilationAtomCache& atomCache, BaseParserScopeData* baseData) const { // Allocate a new vm module-scope. UniquePtr data = LiftParserScopeData(cx, atomCache, baseData); if (!data) { return nullptr; } return data; } // WithScope does not use binding data. template <> Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const { return Scope::create(cx, ScopeKind::With, enclosingScope, nullptr); } // GlobalScope has bindings but no environment shape. template <> Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const { Rooted> rootedData( cx, createSpecificScopeData(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(cx, kind(), nullptr, nullptr, &rootedData); } template Scope* ScopeStencil::createSpecificScope(JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const { Rooted> rootedData( cx, createSpecificScopeData(cx, atomCache, baseData)); if (!rootedData) { return nullptr; } RootedShape shape(cx); if (!createSpecificShape( cx, kind(), rootedData.get().get(), &shape)) { return nullptr; } // Because we already baked the data here, we needn't do it again. return Scope::create(cx, kind(), enclosingScope, shape, &rootedData); } template Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const; template Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const; template Scope* ScopeStencil::createSpecificScope< ClassBodyScope, BlockLexicalEnvironmentObject>( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const; template Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const; template Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const; template Scope* ScopeStencil::createSpecificScope( JSContext* cx, CompilationAtomCache& atomCache, HandleScope enclosingScope, BaseParserScopeData* baseData) const;