author | Matthew Gaudet <mgaudet@mozilla.com> |
Mon, 06 Jan 2020 17:02:14 +0000 | |
changeset 508936 | 47592189b30514c1c224ac1af185792c9d73c312 |
parent 508935 | 1410db0871ba5a00a43906437e0050b19bc363bc |
child 508937 | 2a39a8e425b0824c670ba03452c437c3748c990b |
push id | 36986 |
push user | nerli@mozilla.com |
push date | Mon, 06 Jan 2020 21:54:03 +0000 |
treeherder | mozilla-central@e6427fac5ee8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | tcampbell |
bugs | 1592102 |
milestone | 73.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/AbstractScope.cpp +++ b/js/src/frontend/AbstractScope.cpp @@ -3,46 +3,149 @@ * 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 "frontend/AbstractScope.h" #include "mozilla/Maybe.h" +#include "frontend/ParseInfo.h" +#include "frontend/Stencil.h" #include "js/GCPolicyAPI.h" -#include "vm/JSFunction.h" -#include "vm/Scope.h" +#include "js/GCVariant.h" using namespace js; using namespace js::frontend; -Scope* AbstractScope::maybeScope() const { return scope_; } +MutableHandle<ScopeCreationData> AbstractScope::scopeCreationData() const { + const Deferred& data = scope_.as<Deferred>(); + return data.parseInfo.scopeCreationData[data.index.index]; +} + +// This is used during allocation of the scopes to ensure that we only +// allocate GC scopes with GC-enclosing scopes. This can recurse through +// the scope chain. +// +// Once all ScopeCreation for a compilation tree is centralized, this +// will go away, to be replaced with a single top down GC scope allocation. +// +// This uses an outparam to disambiguate between the case where we have a +// real nullptr scope and we failed to allocate a new scope because of OOM. +bool AbstractScope::getOrCreateScope(JSContext* cx, MutableHandleScope scope) { + if (isScopeCreationData()) { + MutableHandle<ScopeCreationData> scd = scopeCreationData(); + if (scd.get().hasScope()) { + scope.set(scd.get().getScope()); + return true; + } + + scope.set(scd.get().createScope(cx)); + return scope; + } + + scope.set(this->scope()); + return true; +} + +Scope* AbstractScope::getExistingScope() const { + if (scope_.is<HeapPtrScope>()) { + return scope_.as<HeapPtrScope>(); + } + MOZ_ASSERT(isScopeCreationData()); + // This should only be called post-reification, as it needs to return a real + // Scope* unless it represents nullptr (in which case the variant should be + // in HeapPtrScope and handled above) + MOZ_ASSERT(scopeCreationData().get().getScope()); + return scopeCreationData().get().getScope(); +} ScopeKind AbstractScope::kind() const { - MOZ_ASSERT(maybeScope()); - return maybeScope()->kind(); + MOZ_ASSERT(!isNullptr()); + if (isScopeCreationData()) { + return scopeCreationData().get().kind(); + } + return scope()->kind(); } AbstractScope AbstractScope::enclosing() const { - MOZ_ASSERT(maybeScope()); - return AbstractScope(maybeScope()->enclosing()); + MOZ_ASSERT(!isNullptr()); + if (isScopeCreationData()) { + return scopeCreationData().get().enclosing(); + } + return AbstractScope(scope()->enclosing()); } bool AbstractScope::hasEnvironment() const { - MOZ_ASSERT(maybeScope()); - return maybeScope()->hasEnvironment(); + MOZ_ASSERT(!isNullptr()); + if (isScopeCreationData()) { + return scopeCreationData().get().hasEnvironment(); + } + return scope()->hasEnvironment(); +} + +bool AbstractScope::isArrow() const { + // nullptr will also fail the below assert, so effectively also checking + // !isNullptr() + MOZ_ASSERT(is<FunctionScope>()); + if (isScopeCreationData()) { + return scopeCreationData().get().isArrow(); + } + return scope()->as<FunctionScope>().canonicalFunction()->isArrow(); +} + +JSFunction* AbstractScope::canonicalFunction() const { + // nullptr will also fail the below assert, so effectively also checking + // !isNullptr() + MOZ_ASSERT(is<FunctionScope>()); + if (isScopeCreationData()) { + return scopeCreationData().get().canonicalFunction(); + } + return scope()->as<FunctionScope>().canonicalFunction(); } -bool AbstractScope::isArrow() const { return canonicalFunction()->isArrow(); } +uint32_t AbstractScope::nextFrameSlot() const { + if (isScopeCreationData()) { + return scopeCreationData().get().nextFrameSlot(); + } -JSFunction* AbstractScope::canonicalFunction() const { - MOZ_ASSERT(is<FunctionScope>()); - MOZ_ASSERT(maybeScope()); - return maybeScope()->as<FunctionScope>().canonicalFunction(); + switch (kind()) { + case ScopeKind::Function: + return scope()->as<FunctionScope>().nextFrameSlot(); + case ScopeKind::FunctionBodyVar: + case ScopeKind::ParameterExpressionVar: + return scope()->as<VarScope>().nextFrameSlot(); + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + return scope()->as<LexicalScope>().nextFrameSlot(); + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + // Named lambda scopes cannot have frame slots. + return 0; + case ScopeKind::Eval: + case ScopeKind::StrictEval: + return scope()->as<EvalScope>().nextFrameSlot(); + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + return 0; + case ScopeKind::Module: + return scope()->as<ModuleScope>().nextFrameSlot(); + case ScopeKind::WasmInstance: + MOZ_CRASH("WasmInstanceScope doesn't have nextFrameSlot()"); + return 0; + case ScopeKind::WasmFunction: + MOZ_CRASH("WasmFunctionScope doesn't have nextFrameSlot()"); + return 0; + case ScopeKind::With: + MOZ_CRASH("With Scopes don't get nextFrameSlot()"); + return 0; + } + MOZ_CRASH("Not an enclosing intra-frame scope"); } void AbstractScope::trace(JSTracer* trc) { JS::GCPolicy<ScopeType>::trace(trc, &scope_, "AbstractScope"); } bool AbstractScopeIter::hasSyntacticEnvironment() const { return abstractScope().hasEnvironment() &&
--- a/js/src/frontend/AbstractScope.h +++ b/js/src/frontend/AbstractScope.h @@ -4,72 +4,121 @@ * 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/. */ #ifndef frontend_AbstractScope_h #define frontend_AbstractScope_h #include "mozilla/Variant.h" +#include "frontend/TypedIndex.h" #include "gc/Barrier.h" #include "gc/Rooting.h" #include "gc/Tracer.h" #include "vm/Scope.h" - #include "vm/ScopeKind.h" // For ScopeKind namespace js { class Scope; class GlobalScope; class EvalScope; class GCMarker; +class ScopeCreationData; + +namespace frontend { +struct ParseInfo; +class FunctionBox; +} // namespace frontend + +using ScopeIndex = frontend::TypedIndex<Scope>; using HeapPtrScope = HeapPtr<Scope*>; // An interface class to support Scope queries in the frontend without requiring // a GC Allocated scope to necessarily exist. // // This abstracts Scope* (and a future ScopeCreationData type used within the // frontend before the Scope is allocated) +// +// Because a AbstractScope may hold onto a Scope, it must be rooted if a GC may +// occur to ensure that the scope is traced. class AbstractScope { public: - using ScopeType = HeapPtrScope; + // Used to hold index and the parseInfo together to avoid having a + // potentially nullable parseInfo. + struct Deferred { + ScopeIndex index; + frontend::ParseInfo& parseInfo; + }; + + // To make writing code and managing invariants easier, we require that + // any nullptr scopes be stored on the HeapPtrScope arm of the variant. + using ScopeType = mozilla::Variant<HeapPtrScope, Deferred>; private: - ScopeType scope_ = {}; + ScopeType scope_ = ScopeType(HeapPtrScope()); + + // Extract the Scope* represented by this; may be nullptr, and will + // forward through to the ScopeCreationData if it has a Scope* + // + // Should only be used after getOrCreate() has been used to reify this into a + // Scope. + Scope* getExistingScope() const; public: friend class js::Scope; + friend class js::frontend::FunctionBox; - AbstractScope() {} + AbstractScope() = default; + + explicit AbstractScope(Scope* scope) : scope_(HeapPtrScope(scope)) {} + + AbstractScope(frontend::ParseInfo& parseInfo, ScopeIndex scope) + : scope_(Deferred{scope, parseInfo}) {} - explicit AbstractScope(Scope* scope) : scope_(scope) {} + bool isNullptr() const { + if (isScopeCreationData()) { + return false; + } + return scope_.as<HeapPtrScope>() == nullptr; + } // Return true if this AbstractScope represents a Scope, either existant // or to be reified. This indicates that queries can be executed on this // scope data. Returning false is the equivalent to a nullptr, and usually // indicates the end of the scope chain. - explicit operator bool() const { return maybeScope(); } + explicit operator bool() const { return !isNullptr(); } + + bool isScopeCreationData() const { return scope_.is<Deferred>(); } + + // Note: this handle is rooted in the ParseInfo. + MutableHandle<ScopeCreationData> scopeCreationData() const; - Scope* maybeScope() const; + Scope* scope() const { return scope_.as<HeapPtrScope>(); } - // This allows us to check whether or not this abstract scope wraps + // Get a Scope*, creating it from a ScopeCreationData if required. + // Used to allow us to ensure that Scopes are always allocated with + // real GC allocated Enclosing scopes. + bool getOrCreateScope(JSContext* cx, MutableHandleScope scope); + + // This allows us to check whether or not this provider wraps // or otherwise would reify to a particular scope type. template <typename T> bool is() const { static_assert(std::is_base_of<Scope, T>::value, "Trying to ask about non-Scope type"); - if (!maybeScope()) { + if (isNullptr()) { return false; } return kind() == T::classScopeKind_; } ScopeKind kind() const; AbstractScope enclosing() const; bool hasEnvironment() const; + uint32_t nextFrameSlot() const; // Valid iff is<FunctionScope> bool isArrow() const; JSFunction* canonicalFunction() const; bool hasOnChain(ScopeKind kind) const { for (AbstractScope it = *this; it; it = it.enclosing()) { if (it.kind() == kind) { return true; @@ -79,23 +128,23 @@ class AbstractScope { } void trace(JSTracer* trc); }; // Specializations of AbstractScope::is template <> inline bool AbstractScope::is<GlobalScope>() const { - return maybeScope() && + return !isNullptr() && (kind() == ScopeKind::Global || kind() == ScopeKind::NonSyntactic); } template <> inline bool AbstractScope::is<EvalScope>() const { - return maybeScope() && + return !isNullptr() && (kind() == ScopeKind::Eval || kind() == ScopeKind::StrictEval); } // Iterate over abstract scopes rather than scopes. class AbstractScopeIter { AbstractScope scope_; public: @@ -126,9 +175,15 @@ class AbstractScopeIter { if (scope_) { scope_.trace(trc); } }; }; } // namespace js -#endif // frontend_AbstractScope_h \ No newline at end of file +namespace JS { +template <> +struct GCPolicy<js::AbstractScope::Deferred> + : JS::IgnoreGCPolicy<js::AbstractScope::Deferred> {}; +} // namespace JS + +#endif // frontend_AbstractScope_h
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -100,17 +100,17 @@ BytecodeEmitter::BytecodeEmitter( ParseInfo& parseInfo, EmitterMode emitterMode, FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */) : sc(sc), cx(sc->cx_), parent(parent), script(cx, script), lazyScript(cx, lazyScript), bytecodeSection_(cx, line), - perScriptData_(cx), + perScriptData_(cx, parseInfo), fieldInitializers_(fieldInitializers), parseInfo(parseInfo), firstLine(line), firstColumn(column), emitterMode(emitterMode) { MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript); if (sc->isFunctionBox()) {
--- a/js/src/frontend/BytecodeSection.cpp +++ b/js/src/frontend/BytecodeSection.cpp @@ -8,16 +8,17 @@ #include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/PodOperations.h" // PodZero #include "mozilla/ReverseIterator.h" // mozilla::Reversed #include "frontend/ParseInfo.h" #include "frontend/ParseNode.h" // ObjectBox #include "frontend/SharedContext.h" // FunctionBox +#include "frontend/Stencil.h" // ScopeCreationData #include "vm/BytecodeUtil.h" // INDEX_LIMIT, StackUses, StackDefs #include "vm/JSContext.h" // JSContext #include "vm/RegExpObject.h" // RegexpObject using namespace js; using namespace js::frontend; bool GCThingList::append(ObjectBox* objbox, uint32_t* index) { @@ -82,16 +83,28 @@ bool GCThingList::finish(JSContext* cx, bool operator()(ObjLiteralCreationData& data) { JSObject* obj = data.create(cx); if (!obj) { return false; } array[i] = JS::GCCellPtr(obj); return true; } + + bool operator()(ScopeIndex& index) { + MutableHandle<ScopeCreationData> data = + parseInfo.scopeCreationData[index]; + Scope* scope = data.get().createScope(cx); + if (!scope) { + return false; + } + + array[i] = JS::GCCellPtr(scope); + return true; + } }; for (uint32_t i = 0; i < length(); i++) { Matcher m{cx, parseInfo, i, array}; if (!vector[i].get().match(m)) { return false; } } @@ -192,12 +205,12 @@ void BytecodeSection::updateDepth(Byteco MOZ_ASSERT(stackDepth_ >= 0); stackDepth_ += ndefs; if (uint32_t(stackDepth_) > maxStackDepth_) { maxStackDepth_ = stackDepth_; } } -PerScriptData::PerScriptData(JSContext* cx) - : gcThingList_(cx), atomIndices_(cx->frontendCollectionPool()) {} +PerScriptData::PerScriptData(JSContext* cx, frontend::ParseInfo& parseInfo) + : gcThingList_(cx, parseInfo), atomIndices_(cx->frontendCollectionPool()) {} bool PerScriptData::init(JSContext* cx) { return atomIndices_.acquire(cx); }
--- a/js/src/frontend/BytecodeSection.h +++ b/js/src/frontend/BytecodeSection.h @@ -17,17 +17,16 @@ #include "jstypes.h" // JS_PUBLIC_API #include "NamespaceImports.h" // ValueVector #include "frontend/AbstractScope.h" // AbstractScope #include "frontend/BytecodeOffset.h" // BytecodeOffset #include "frontend/JumpList.h" // JumpTarget #include "frontend/NameCollections.h" // AtomIndexMap, PooledMapPtr #include "frontend/ObjLiteral.h" // ObjLiteralCreationData -#include "frontend/ParseInfo.h" // ParseInfo #include "frontend/ParseNode.h" // BigIntLiteral #include "frontend/SourceNotes.h" // jssrcnote #include "frontend/Stencil.h" // Stencils #include "gc/Barrier.h" // GCPtrObject, GCPtrScope, GCPtrValue #include "gc/Rooting.h" // JS::Rooted #include "js/GCVariant.h" // GCPolicy<mozilla::Variant> #include "js/GCVector.h" // GCVector #include "js/TypeDecls.h" // jsbytecode, JSContext @@ -43,28 +42,41 @@ class Scope; using BigIntVector = JS::GCVector<js::BigInt*>; namespace frontend { class BigIntLiteral; class ObjectBox; struct MOZ_STACK_CLASS GCThingList { - using ListType = mozilla::Variant<JS::GCCellPtr, BigIntIndex, - ObjLiteralCreationData, RegExpIndex>; + using ListType = + mozilla::Variant<JS::GCCellPtr, BigIntIndex, ObjLiteralCreationData, + RegExpIndex, ScopeIndex>; + ParseInfo& parseInfo; JS::RootedVector<ListType> vector; // Last emitted object. ObjectBox* lastbox = nullptr; // Index of the first scope in the vector. mozilla::Maybe<uint32_t> firstScopeIndex; - explicit GCThingList(JSContext* cx) : vector(cx) {} + explicit GCThingList(JSContext* cx, ParseInfo& parseInfo) + : parseInfo(parseInfo), vector(cx) {} + MOZ_MUST_USE bool append(ScopeIndex scope, uint32_t* index) { + *index = vector.length(); + if (!vector.append(mozilla::AsVariant(scope))) { + return false; + } + if (!firstScopeIndex) { + firstScopeIndex.emplace(*index); + } + return true; + } MOZ_MUST_USE bool append(Scope* scope, uint32_t* index) { *index = vector.length(); if (!vector.append(mozilla::AsVariant(JS::GCCellPtr(scope)))) { return false; } if (!firstScopeIndex) { firstScopeIndex.emplace(*index); } @@ -93,17 +105,20 @@ struct MOZ_STACK_CLASS GCThingList { uint32_t length() const { return vector.length(); } MOZ_MUST_USE bool finish(JSContext* cx, ParseInfo& parseInfo, mozilla::Span<JS::GCCellPtr> array); void finishInnerFunctions(); AbstractScope getScope(size_t index) const { auto& elem = vector[index].get(); - return AbstractScope(&elem.as<JS::GCCellPtr>().as<Scope>()); + if (elem.is<JS::GCCellPtr>()) { + return AbstractScope(&elem.as<JS::GCCellPtr>().as<Scope>()); + } + return AbstractScope(parseInfo, elem.as<ScopeIndex>()); } AbstractScope firstScope() const { MOZ_ASSERT(firstScopeIndex.isSome()); return getScope(*firstScopeIndex); } }; @@ -368,17 +383,17 @@ class BytecodeSection { // Number of JOF_TYPESET opcodes generated. uint32_t numTypeSets_ = 0; }; // Data that is not directly associated with specific opcode/index inside // bytecode, but referred from bytecode is stored in this class. class PerScriptData { public: - explicit PerScriptData(JSContext* cx); + explicit PerScriptData(JSContext* cx, frontend::ParseInfo& parseInfo); MOZ_MUST_USE bool init(JSContext* cx); GCThingList& gcThingList() { return gcThingList_; } const GCThingList& gcThingList() const { return gcThingList_; } PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; } const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; }
--- a/js/src/frontend/EmitterScope.cpp +++ b/js/src/frontend/EmitterScope.cpp @@ -331,34 +331,61 @@ NameLocation EmitterScope::searchAndCach bce->cx->recoverFromOutOfMemory(); } return *loc; } template <typename ScopeCreator> bool EmitterScope::internScope(BytecodeEmitter* bce, ScopeCreator createScope) { - RootedScope enclosing(bce->cx, enclosingScope(bce).maybeScope()); + RootedScope enclosing(bce->cx); + if (!enclosingScope(bce).getOrCreateScope(bce->cx, &enclosing)) { + return false; + } + Scope* scope = createScope(bce->cx, enclosing); if (!scope) { return false; } hasEnvironment_ = scope->hasEnvironment(); + return bce->perScriptData().gcThingList().append(scope, &scopeIndex_); } template <typename ScopeCreator> +bool EmitterScope::internScopeCreationData(BytecodeEmitter* bce, + ScopeCreator createScope) { + Rooted<AbstractScope> enclosing(bce->cx, enclosingScope(bce)); + ScopeIndex index; + if (!createScope(bce->cx, enclosing, &index)) { + return false; + } + auto scope = bce->parseInfo.scopeCreationData[index.index]; + hasEnvironment_ = scope.get().hasEnvironment(); + return bce->perScriptData().gcThingList().append(index, &scopeIndex_); +} + +template <typename ScopeCreator> bool EmitterScope::internBodyScope(BytecodeEmitter* bce, ScopeCreator createScope) { MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX, "There can be only one body scope"); bce->bodyScopeIndex = bce->perScriptData().gcThingList().length(); return internScope(bce, createScope); } +template <typename ScopeCreator> +bool EmitterScope::internBodyScopeCreationData(BytecodeEmitter* bce, + ScopeCreator createScope) { + MOZ_ASSERT(bce->bodyScopeIndex == UINT32_MAX, + "There can be only one body scope"); + bce->bodyScopeIndex = bce->perScriptData().gcThingList().length(); + return internScopeCreationData(bce, createScope); +} + bool EmitterScope::appendScopeNote(BytecodeEmitter* bce) { MOZ_ASSERT(ScopeKindIsInBody(scope(bce).kind()) && enclosingInFrame(), "Scope notes are not needed for body-level scopes."); noteIndex_ = bce->bytecodeSection().scopeNoteList().length(); return bce->bytecodeSection().scopeNoteList().append( index(), bce->bytecodeSection().offset(), enclosingInFrame() ? enclosingInFrame()->noteIndex() : ScopeNote::NoScopeNoteIndex); @@ -485,23 +512,37 @@ bool EmitterScope::enterLexical(Bytecode if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ)) { return false; } } updateFrameFixedSlots(bce, bi); - // Create and intern the VM scope. - auto createScope = [kind, bindings, firstFrameSlot](JSContext* cx, - HandleScope enclosing) { - return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing); - }; - if (!internScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + auto createScope = [kind, bindings, firstFrameSlot, bce]( + JSContext* cx, Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create(cx, bce->parseInfo, kind, bindings, + firstFrameSlot, enclosing, index); + }; + if (!internScopeCreationData(bce, createScope)) { + return false; + } + + } else { + // Create and intern the VM scope. + auto createScope = [kind, bindings, firstFrameSlot](JSContext* cx, + HandleScope enclosing) { + return LexicalScope::create(cx, kind, bindings, firstFrameSlot, + enclosing); + }; + if (!internScope(bce, createScope)) { + return false; + } } if (ScopeKindIsInBody(kind) && hasEnvironment()) { // After interning the VM scope we can get the scope index. if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV)) { return false; } } @@ -545,24 +586,38 @@ bool EmitterScope::enterNamedLambda(Byte NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location()); if (!putNameInCache(bce, bi.name(), loc)) { return false; } bi++; MOZ_ASSERT(!bi, "There should be exactly one binding in a NamedLambda scope"); - auto createScope = [funbox](JSContext* cx, HandleScope enclosing) { - ScopeKind scopeKind = funbox->strict() ? ScopeKind::StrictNamedLambda - : ScopeKind::NamedLambda; - return LexicalScope::create(cx, scopeKind, funbox->namedLambdaBindings(), - LOCALNO_LIMIT, enclosing); - }; - if (!internScope(bce, createScope)) { - return false; + ScopeKind scopeKind = + funbox->strict() ? ScopeKind::StrictNamedLambda : ScopeKind::NamedLambda; + if (bce->parseInfo.isDeferred()) { + auto createScope = [funbox, scopeKind, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create(cx, bce->parseInfo, scopeKind, + funbox->namedLambdaBindings(), + LOCALNO_LIMIT, enclosing, index); + }; + if (!internScopeCreationData(bce, createScope)) { + return false; + } + } else { + auto createScope = [funbox, scopeKind](JSContext* cx, + HandleScope enclosing) { + return LexicalScope::create(cx, scopeKind, funbox->namedLambdaBindings(), + LOCALNO_LIMIT, enclosing); + }; + if (!internScope(bce, createScope)) { + return false; + } } return checkEnvironmentChainLength(bce); } bool EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox) { MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); @@ -636,25 +691,40 @@ bool EmitterScope::enterFunction(Bytecod } } if (!deadZoneFrameSlotRange(bce, 0, paramFrameSlotEnd)) { return false; } } - // Create and intern the VM scope. - auto createScope = [funbox](JSContext* cx, HandleScope enclosing) { - RootedFunction fun(cx, funbox->function()); - return FunctionScope::create( - cx, funbox->functionScopeBindings(), funbox->hasParameterExprs, - funbox->needsCallObjectRegardlessOfBindings(), fun, enclosing); - }; - if (!internBodyScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + auto createScope = [funbox, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create( + cx, bce->parseInfo, funbox->functionScopeBindings(), + funbox->hasParameterExprs, + funbox->needsCallObjectRegardlessOfBindings(), funbox, enclosing, + index); + }; + if (!internBodyScopeCreationData(bce, createScope)) { + return false; + } + } else { + // Create and intern the VM scope. + auto createScope = [funbox](JSContext* cx, HandleScope enclosing) { + RootedFunction fun(cx, funbox->function()); + return FunctionScope::create( + cx, funbox->functionScopeBindings(), funbox->hasParameterExprs, + funbox->needsCallObjectRegardlessOfBindings(), fun, enclosing); + }; + if (!internBodyScope(bce, createScope)) { + return false; + } } return checkEnvironmentChainLength(bce); } bool EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce, FunctionBox* funbox) { MOZ_ASSERT(funbox->hasParameterExprs); @@ -693,26 +763,43 @@ bool EmitterScope::enterFunctionExtraBod // If the extra var scope may be extended at runtime due to sloppy // direct eval, any names beyond the var scope must be accessed // dynamically as we don't know if the name will become a 'var' binding // due to direct eval. if (funbox->hasExtensibleScope()) { fallbackFreeNameLocation_ = Some(NameLocation::Dynamic()); } - // Create and intern the VM scope. - auto createScope = [funbox, firstFrameSlot](JSContext* cx, - HandleScope enclosing) { - return VarScope::create( - cx, ScopeKind::FunctionBodyVar, funbox->extraVarScopeBindings(), - firstFrameSlot, - funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), enclosing); - }; - if (!internScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + // Create and intern the VM scope. + auto createScope = [funbox, firstFrameSlot, bce]( + JSContext* cx, Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create( + cx, bce->parseInfo, ScopeKind::FunctionBodyVar, + funbox->extraVarScopeBindings(), firstFrameSlot, + funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), enclosing, + index); + }; + if (!internScopeCreationData(bce, createScope)) { + return false; + } + } else { + // Create and intern the VM scope. + auto createScope = [funbox, firstFrameSlot](JSContext* cx, + HandleScope enclosing) { + return VarScope::create( + cx, ScopeKind::FunctionBodyVar, funbox->extraVarScopeBindings(), + firstFrameSlot, + funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), + enclosing); + }; + if (!internScope(bce, createScope)) { + return false; + } } if (hasEnvironment()) { if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) { return false; } } @@ -732,23 +819,37 @@ bool EmitterScope::enterParameterExpress } // Parameter expressions var scopes have no pre-set bindings and are // always extensible, as they are needed for eval. fallbackFreeNameLocation_ = Some(NameLocation::Dynamic()); // Create and intern the VM scope. uint32_t firstFrameSlot = frameSlotStart(); - auto createScope = [firstFrameSlot](JSContext* cx, HandleScope enclosing) { - return VarScope::create(cx, ScopeKind::ParameterExpressionVar, - /* data = */ nullptr, firstFrameSlot, - /* needsEnvironment = */ true, enclosing); - }; - if (!internScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + auto createScope = [firstFrameSlot, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create( + cx, bce->parseInfo, ScopeKind::ParameterExpressionVar, + /* dataArg = */ nullptr, firstFrameSlot, + /* needsEnvironment = */ true, enclosing, index); + }; + if (!internScopeCreationData(bce, createScope)) { + return false; + } + } else { + auto createScope = [firstFrameSlot](JSContext* cx, HandleScope enclosing) { + return VarScope::create(cx, ScopeKind::ParameterExpressionVar, + /* data = */ nullptr, firstFrameSlot, + /* needsEnvironment = */ true, enclosing); + }; + if (!internScope(bce, createScope)) { + return false; + } } MOZ_ASSERT(hasEnvironment()); if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) { return false; } // The extra var scope needs a note to be mapped from a pc. @@ -835,18 +936,29 @@ bool EmitterScope::enterGlobal(BytecodeE // global scopes. They are assumed to be global vars in the syntactic // global scope, dynamic accesses under non-syntactic global scope. if (globalsc->scopeKind() == ScopeKind::Global) { fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var)); } else { fallbackFreeNameLocation_ = Some(NameLocation::Dynamic()); } + if (bce->parseInfo.isDeferred()) { + auto createScope = [globalsc, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + MOZ_ASSERT(!enclosing.get()); + return ScopeCreationData::create( + cx, bce->parseInfo, globalsc->scopeKind(), globalsc->bindings, index); + }; + return internBodyScopeCreationData(bce, createScope); + } + auto createScope = [globalsc](JSContext* cx, HandleScope enclosing) { - MOZ_ASSERT(!enclosing); + MOZ_ASSERT(!enclosing.get()); return GlobalScope::create(cx, globalsc->scopeKind(), globalsc->bindings); }; return internBodyScope(bce, createScope); } bool EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc) { MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); @@ -856,25 +968,38 @@ bool EmitterScope::enterEval(BytecodeEmi return false; } // For simplicity, treat all free name lookups in eval scripts as dynamic. fallbackFreeNameLocation_ = Some(NameLocation::Dynamic()); // Create the `var` scope. Note that there is also a lexical scope, created // separately in emitScript(). - auto createScope = [evalsc](JSContext* cx, HandleScope enclosing) { - ScopeKind scopeKind = - evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval; - return EvalScope::create(cx, scopeKind, evalsc->bindings, enclosing); - }; - if (!internBodyScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + auto createScope = [evalsc, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + ScopeKind scopeKind = + evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval; + return ScopeCreationData::create(cx, bce->parseInfo, scopeKind, + evalsc->bindings, enclosing, index); + }; + if (!internBodyScopeCreationData(bce, createScope)) { + return false; + } + } else { + auto createScope = [evalsc](JSContext* cx, HandleScope enclosing) { + ScopeKind scopeKind = + evalsc->strict() ? ScopeKind::StrictEval : ScopeKind::Eval; + return EvalScope::create(cx, scopeKind, evalsc->bindings, enclosing); + }; + if (!internBodyScope(bce, createScope)) { + return false; + } } - if (hasEnvironment()) { if (!bce->emitInternedScopeOp(index(), JSOP_PUSHVARENV)) { return false; } } else { // Resolve binding names and emit DEFVAR prologue ops if we don't have // an environment (i.e., a sloppy eval not in a parameter expression). // Eval scripts always have their own lexical scope, but non-strict @@ -955,16 +1080,31 @@ bool EmitterScope::enterModule(BytecodeE // Put lexical frame slots in TDZ. Environment slots are poisoned during // environment creation. if (firstLexicalFrameSlot) { if (!deadZoneFrameSlotRange(bce, *firstLexicalFrameSlot, frameSlotEnd())) { return false; } } + if (bce->parseInfo.isDeferred()) { + // Create and intern the VM scope creation data. + auto createScope = [modulesc, bce](JSContext* cx, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create(cx, bce->parseInfo, modulesc->bindings, + modulesc->module(), enclosing, index); + }; + if (!internBodyScopeCreationData(bce, createScope)) { + return false; + } + + return checkEnvironmentChainLength(bce); + } + // Create and intern the VM scope. auto createScope = [modulesc](JSContext* cx, HandleScope enclosing) { return ModuleScope::create(cx, modulesc->bindings, modulesc->module(), enclosing); }; if (!internBodyScope(bce, createScope)) { return false; } @@ -977,21 +1117,31 @@ bool EmitterScope::enterWith(BytecodeEmi if (!ensureCache(bce)) { return false; } // 'with' make all accesses dynamic and unanalyzable. fallbackFreeNameLocation_ = Some(NameLocation::Dynamic()); - auto createScope = [](JSContext* cx, HandleScope enclosing) { - return WithScope::create(cx, enclosing); - }; - if (!internScope(bce, createScope)) { - return false; + if (bce->parseInfo.isDeferred()) { + auto createScope = [bce](JSContext* cx, Handle<AbstractScope> enclosing, + ScopeIndex* index) { + return ScopeCreationData::create(cx, bce->parseInfo, enclosing, index); + }; + if (!internScopeCreationData(bce, createScope)) { + return false; + } + } else { + auto createScope = [](JSContext* cx, HandleScope enclosing) { + return WithScope::create(cx, enclosing); + }; + if (!internScope(bce, createScope)) { + return false; + } } if (!bce->emitInternedScopeOp(index(), JSOP_ENTERWITH)) { return false; } if (!appendScopeNote(bce)) { return false;
--- a/js/src/frontend/EmitterScope.h +++ b/js/src/frontend/EmitterScope.h @@ -83,18 +83,26 @@ class EmitterScope : public Nestable<Emi static NameLocation searchInEnclosingScope(JSAtom* name, Scope* scope, uint8_t hops); NameLocation searchAndCache(BytecodeEmitter* bce, JSAtom* name); template <typename ScopeCreator> MOZ_MUST_USE bool internScope(BytecodeEmitter* bce, ScopeCreator createScope); template <typename ScopeCreator> + MOZ_MUST_USE bool internScopeCreationData(BytecodeEmitter* bce, + ScopeCreator createScope); + + template <typename ScopeCreator> MOZ_MUST_USE bool internBodyScope(BytecodeEmitter* bce, ScopeCreator createScope); + + template <typename ScopeCreator> + MOZ_MUST_USE bool internBodyScopeCreationData(BytecodeEmitter* bce, + ScopeCreator createScope); MOZ_MUST_USE bool appendScopeNote(BytecodeEmitter* bce); MOZ_MUST_USE bool deadZoneFrameSlotRange(BytecodeEmitter* bce, uint32_t slotStart, uint32_t slotEnd) const; public: explicit EmitterScope(BytecodeEmitter* bce);
--- a/js/src/frontend/ParseInfo.h +++ b/js/src/frontend/ParseInfo.h @@ -17,47 +17,57 @@ #include "js/RealmOptions.h" #include "js/Vector.h" #include "vm/JSContext.h" #include "vm/Realm.h" namespace js { namespace frontend { -class ParserBase; - // ParseInfo owns a number of pieces of information about a parse, // as well as controls the lifetime of parse nodes and other data // by controling the mark and reset of the LifoAlloc. struct MOZ_RAII ParseInfo { // ParseInfo's mode can be eager or deferred: // - // - In Eager mode, allocation happens right away and the Function Tree is not - // constructed. + // - In Eager mode, allocation happens right away and the Function Tree is + // not constructed. // - In Deferred mode, allocation is deferred as late as possible. enum Mode { Eager, Deferred }; UsedNameTracker usedNames; LifoAllocScope& allocScope; FunctionTreeHolder treeHolder; Mode mode; - // Hold onto the RegExpCreationData and BigIntCreationDatas that are allocated - // during parse to ensure correct destruction. + // Hold onto the RegExpCreationData and BigIntCreationDatas that are + // allocated during parse to ensure correct destruction. Vector<RegExpCreationData> regExpData; Vector<BigIntCreationData> bigIntData; + // A rooted list of scopes created during this parse. + // + // To ensure that ScopeCreationData's destructors fire, and thus our HeapPtr + // barriers, we store the scopeCreationData at this level so that they + // can be safely destroyed, rather than LifoAllocing them with the rest of + // the parser data structures. + // + // References to scopes are controlled via AbstractScope, which holds onto + // an index (and ParseInfo reference). + JS::RootedVector<ScopeCreationData> scopeCreationData; + ParseInfo(JSContext* cx, LifoAllocScope& alloc) : usedNames(cx), allocScope(alloc), treeHolder(cx), mode(cx->realm()->behaviors().deferredParserAlloc() ? ParseInfo::Mode::Deferred : ParseInfo::Mode::Eager), regExpData(cx), - bigIntData(cx) {} + bigIntData(cx), + scopeCreationData(cx) {} // To avoid any misuses, make sure this is neither copyable, // movable or assignable. ParseInfo(const ParseInfo&) = delete; ParseInfo(ParseInfo&&) = delete; ParseInfo& operator=(const ParseInfo&) = delete; ParseInfo& operator=(ParseInfo&&) = delete;
--- a/js/src/frontend/SharedContext.cpp +++ b/js/src/frontend/SharedContext.cpp @@ -314,17 +314,17 @@ void FunctionBox::setEnclosingScopeForIn MOZ_ASSERT(!enclosingScope_); enclosingScope_ = enclosingScope; } void FunctionBox::finish() { if (isInterpretedLazy()) { // Lazy inner functions need to record their enclosing scope for when they // eventually are compiled. - function()->setEnclosingScope(enclosingScope_.maybeScope()); + function()->setEnclosingScope(enclosingScope_.getExistingScope()); } else { // Non-lazy inner functions don't use the enclosingScope_ field. MOZ_ASSERT(!enclosingScope_); } } ModuleSharedContext::ModuleSharedContext(JSContext* cx, ModuleObject* module, Scope* enclosingScope,
--- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -514,17 +514,17 @@ class FunctionBox : public ObjectBox, pu setIsInterpretedLazy(function->isInterpretedLazy()); } Scope* compilationEnclosingScope() const override { // This is used when emitting code for the current FunctionBox and therefore // the enclosingScope_ must have be set correctly during initalization. MOZ_ASSERT(enclosingScope_); - return enclosingScope_.maybeScope(); + return enclosingScope_.scope(); } bool needsCallObjectRegardlessOfBindings() const { return hasExtensibleScope() || needsHomeObject() || isDerivedClassConstructor() || isGenerator() || isAsync(); } bool hasExtraBodyVarScope() const {
new file mode 100644 --- /dev/null +++ b/js/src/frontend/Stencil.cpp @@ -0,0 +1,163 @@ +/* -*- 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 "frontend/Stencil.h" + +#include "frontend/SharedContext.h" +#include "js/TracingAPI.h" +#include "vm/EnvironmentObject.h" +#include "vm/Scope.h" + +using namespace js; +using namespace js::frontend; + +Scope* ScopeCreationData::createScope(JSContext* cx) { + // If we've already created a scope, best just return it. + if (scope_) { + return scope_; + } + + Scope* scope = nullptr; + switch (kind()) { + case ScopeKind::Function: { + scope = createSpecificScope<FunctionScope>(cx); + break; + } + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + case ScopeKind::FunctionLexical: { + scope = createSpecificScope<LexicalScope>(cx); + break; + } + case ScopeKind::FunctionBodyVar: + case ScopeKind::ParameterExpressionVar: { + scope = createSpecificScope<VarScope>(cx); + break; + } + case ScopeKind::Global: + case ScopeKind::NonSyntactic: { + scope = createSpecificScope<GlobalScope>(cx); + break; + } + case ScopeKind::Eval: + case ScopeKind::StrictEval: { + scope = createSpecificScope<EvalScope>(cx); + break; + } + case ScopeKind::Module: { + scope = createSpecificScope<ModuleScope>(cx); + break; + } + case ScopeKind::With: { + scope = createSpecificScope<WithScope>(cx); + break; + } + default: { + MOZ_CRASH("Unexpected deferred type"); + } + } + return scope; +} + +void ScopeCreationData::trace(JSTracer* trc) { + if (enclosing_) { + enclosing_.trace(trc); + } + if (environmentShape_) { + TraceEdge(trc, &environmentShape_, "ScopeCreationData Environment Shape"); + } + if (scope_) { + TraceEdge(trc, &scope_, "ScopeCreationData Scope"); + } + if (funbox_) { + funbox_->trace(trc); + } + + // Trace Datas + if (data_) { + switch (kind()) { + case ScopeKind::Function: { + data<FunctionScope>().trace(trc); + break; + } + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + case ScopeKind::FunctionLexical: { + data<LexicalScope>().trace(trc); + break; + } + case ScopeKind::FunctionBodyVar: + case ScopeKind::ParameterExpressionVar: { + data<VarScope>().trace(trc); + break; + } + case ScopeKind::Global: + case ScopeKind::NonSyntactic: { + data<GlobalScope>().trace(trc); + break; + } + case ScopeKind::Eval: + case ScopeKind::StrictEval: { + data<EvalScope>().trace(trc); + break; + } + case ScopeKind::Module: { + data<ModuleScope>().trace(trc); + break; + } + case ScopeKind::With: + default: + MOZ_CRASH("Unexpected data type"); + } + } +} + +uint32_t ScopeCreationData::nextFrameSlot() const { + switch (kind()) { + case ScopeKind::Function: + return nextFrameSlot<FunctionScope>(); + case ScopeKind::FunctionBodyVar: + case ScopeKind::ParameterExpressionVar: + return nextFrameSlot<VarScope>(); + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::FunctionLexical: + return nextFrameSlot<LexicalScope>(); + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + // Named lambda scopes cannot have frame slots. + return 0; + case ScopeKind::Eval: + case ScopeKind::StrictEval: + return nextFrameSlot<EvalScope>(); + case ScopeKind::Global: + case ScopeKind::NonSyntactic: + return 0; + case ScopeKind::Module: + return nextFrameSlot<ModuleScope>(); + case ScopeKind::WasmInstance: + case ScopeKind::WasmFunction: + case ScopeKind::With: + MOZ_CRASH( + "With, WasmInstance and WasmFunction Scopes don't get " + "nextFrameSlot()"); + return 0; + } + MOZ_CRASH("Not an enclosing intra-frame scope"); +} + +bool ScopeCreationData::isArrow() const { return funbox_->isArrow(); } + +JSFunction* ScopeCreationData::canonicalFunction() const { + return funbox_->function(); +}
--- a/js/src/frontend/Stencil.h +++ b/js/src/frontend/Stencil.h @@ -2,38 +2,29 @@ * 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/. */ #ifndef frontend_Stencil_h #define frontend_Stencil_h +#include "frontend/AbstractScope.h" +#include "frontend/TypedIndex.h" #include "gc/AllocKind.h" #include "gc/Rooting.h" #include "js/RegExpFlags.h" #include "vm/BigIntType.h" #include "vm/JSFunction.h" #include "vm/JSScript.h" +#include "vm/Scope.h" namespace js { namespace frontend { -// TypedIndex allows discrimination in variants between different -// index types. Used as a typesafe index for various stencil arrays. -template <typename Tag> -struct TypedIndex { - explicit TypedIndex(uint32_t index) : index(index){}; - - uint32_t index = 0; - - // For Vector::operator[] - operator size_t() const { return index; } -}; - // [SMDOC] Script Stencil (Frontend Representation) // // Stencils are GC object free representations of artifacts created during // parsing and bytecode emission that are being used as part of Project // Stencil (https://bugzilla.mozilla.org/show_bug.cgi?id=stencil) to revamp // the frontend. // // Renaming to use the term stencil more broadly is still in progress. @@ -185,11 +176,157 @@ class BigIntCreationData { mozilla::Range<const char16_t> source(buf_.get(), length_); return js::BigIntLiteralIsZero(source); } }; using BigIntIndex = TypedIndex<BigIntCreationData>; } /* namespace frontend */ + +class ScopeCreationData { + friend class AbstractScope; + friend class GCMarker; + + // The enclosing scope if it exists + AbstractScope enclosing_; + + // The kind determines data_. + ScopeKind kind_; + + // If there are any aliased bindings, the shape for the + // EnvironmentObject. Otherwise nullptr. + HeapPtr<Shape*> environmentShape_ = {}; + + // Once we've produced a scope from a scope creation data, there may still be + // AbstractScopes refering to this ScopeCreationData, and if reification is + // requested multiple times, we should return the same scope rather than + // creating multiple sopes. + // + // As well, any queries that require data() to answer must be redirected to + // the scope once the scope has been reified, as the ScopeCreationData loses + // ownership of the data on reification. + HeapPtr<Scope*> scope_ = {}; + + // For FunctionScopes we need the funbox; nullptr otherwise. + frontend::FunctionBox* funbox_ = nullptr; + + UniquePtr<BaseScopeData> data_; + + public: + ScopeCreationData(JSContext* cx, ScopeKind kind, + Handle<AbstractScope> enclosing, + UniquePtr<BaseScopeData> data = {}, + Shape* environmentShape = nullptr, + frontend::FunctionBox* funbox = nullptr) + : enclosing_(enclosing), + kind_(kind), + environmentShape_(environmentShape), + funbox_(funbox), + data_(std::move(data)) {} + + ScopeKind kind() const { return kind_; } + AbstractScope enclosing() { return enclosing_; } + bool getOrCreateEnclosingScope(JSContext* cx, MutableHandleScope scope) { + return enclosing_.getOrCreateScope(cx, scope); + } + + // FunctionScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<FunctionScope::Data*> dataArg, + bool hasParameterExprs, bool needsEnvironment, + frontend::FunctionBox* funbox, + Handle<AbstractScope> enclosing, ScopeIndex* index); + + // LexicalScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<LexicalScope::Data*> dataArg, + uint32_t firstFrameSlot, Handle<AbstractScope> enclosing, + ScopeIndex* index); + // VarScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<VarScope::Data*> dataArg, + uint32_t firstFrameSlot, bool needsEnvironment, + Handle<AbstractScope> enclosing, ScopeIndex* index); + + // GlobalScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<GlobalScope::Data*> dataArg, + ScopeIndex* index); + + // EvalScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<EvalScope::Data*> dataArg, + Handle<AbstractScope> enclosing, ScopeIndex* index); + + // ModuleScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<ModuleScope::Data*> dataArg, + HandleModuleObject module, Handle<AbstractScope> enclosing, + ScopeIndex* index); + + // WithScope + static bool create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<AbstractScope> enclosing, ScopeIndex* index); + + bool hasEnvironment() const { + return Scope::hasEnvironment(kind(), environmentShape_); + } + + // Valid for functions; + bool isArrow() const; + JSFunction* canonicalFunction() const; + + bool hasScope() const { return scope_ != nullptr; } + + Scope* getScope() const { + MOZ_ASSERT(hasScope()); + return scope_; + } + + Scope* createScope(JSContext* cx); + + void trace(JSTracer* trc); + + uint32_t nextFrameSlot() const; + + private: + // Non owning reference to data + template <typename SpecificScopeType> + typename SpecificScopeType::Data& data() const { + MOZ_ASSERT(data_.get()); + return *static_cast<typename SpecificScopeType::Data*>(data_.get()); + } + + // Transfer ownership into a new UniquePtr. + template <typename SpecificScopeType> + UniquePtr<typename SpecificScopeType::Data> releaseData() { + return UniquePtr<typename SpecificScopeType::Data>( + static_cast<typename SpecificScopeType::Data*>(data_.release())); + } + + template <typename SpecificScopeType> + Scope* createSpecificScope(JSContext* cx); + + template <typename SpecificScopeType> + uint32_t nextFrameSlot() const { + // If a scope has been allocated for the ScopeCreationData we no longer own + // data, so defer to scope + if (hasScope()) { + return getScope()->template as<SpecificScopeType>().nextFrameSlot(); + } + return data<SpecificScopeType>().nextFrameSlot; + } +}; + } /* namespace js */ +namespace JS { +template <> +struct GCPolicy<js::ScopeCreationData*> { + static void trace(JSTracer* trc, js::ScopeCreationData** data, + const char* name) { + (*data)->trace(trc); + } +}; + +} // namespace JS #endif /* frontend_Stencil_h */ \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/js/src/frontend/TypedIndex.h @@ -0,0 +1,36 @@ +/* -*- 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/. */ + +#ifndef frontend_TypedIndex_h +#define frontend_TypedIndex_h + +#include <cstdint> + +namespace js { +namespace frontend { + +// TypedIndex allows discrimination in variants between different +// index types. Used as a typesafe index for various stencil arrays. +template <typename Tag> +struct TypedIndex { + TypedIndex() = default; + explicit TypedIndex(uint32_t index) : index(index){}; + + uint32_t index = 0; + + // For Vector::operator[] + operator size_t() const { return index; } + + TypedIndex& operator=(size_t idx) { + index = idx; + return *this; + } +}; + +} // namespace frontend +} // namespace js + +#endif
--- a/js/src/frontend/moz.build +++ b/js/src/frontend/moz.build @@ -47,16 +47,17 @@ UNIFIED_SOURCES += [ 'NameOpEmitter.cpp', 'ObjectEmitter.cpp', 'ObjLiteral.cpp', 'ParseContext.cpp', 'ParseNode.cpp', 'ParseNodeVerify.cpp', 'PropOpEmitter.cpp', 'SharedContext.cpp', + 'Stencil.cpp', 'SwitchEmitter.cpp', 'TDZCheckCache.cpp', 'TokenStream.cpp', 'TryEmitter.cpp', 'WhileEmitter.cpp', ] # Parser.cpp cannot be built in unified mode because of explicit
--- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -7,17 +7,19 @@ #include "vm/Scope.h" #include "mozilla/ScopeExit.h" #include <memory> #include <new> #include "builtin/ModuleObject.h" -#include "frontend/AbstractScope.h" +#include "frontend/ParseInfo.h" +#include "frontend/SharedContext.h" +#include "frontend/Stencil.h" #include "gc/Allocator.h" #include "util/StringBuffer.h" #include "vm/EnvironmentObject.h" #include "vm/JSScript.h" #include "wasm/WasmInstance.h" #include "gc/FreeOp-inl.h" #include "gc/ObjectKind-inl.h" @@ -506,60 +508,35 @@ uint32_t LexicalScope::firstFrameSlot() } return 0; } /* static */ uint32_t LexicalScope::nextFrameSlot(const AbstractScope& scope) { for (AbstractScopeIter si(scope); si; si++) { switch (si.kind()) { + case ScopeKind::With: + continue; case ScopeKind::Function: - MOZ_ASSERT(si.abstractScope().maybeScope()); - return si.abstractScope() - .maybeScope() - ->as<FunctionScope>() - .nextFrameSlot(); case ScopeKind::FunctionBodyVar: case ScopeKind::ParameterExpressionVar: - MOZ_ASSERT(si.abstractScope().maybeScope()); - return si.abstractScope().maybeScope()->as<VarScope>().nextFrameSlot(); case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::FunctionLexical: - MOZ_ASSERT(si.abstractScope().maybeScope()); - return si.abstractScope() - .maybeScope() - ->as<LexicalScope>() - .nextFrameSlot(); case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: - // Named lambda scopes cannot have frame slots. - return 0; - case ScopeKind::With: - continue; case ScopeKind::Eval: case ScopeKind::StrictEval: - MOZ_ASSERT(si.abstractScope().maybeScope()); - return si.abstractScope().maybeScope()->as<EvalScope>().nextFrameSlot(); case ScopeKind::Global: case ScopeKind::NonSyntactic: - return 0; case ScopeKind::Module: - MOZ_ASSERT(si.abstractScope().maybeScope()); - return si.abstractScope() - .maybeScope() - ->as<ModuleScope>() - .nextFrameSlot(); case ScopeKind::WasmInstance: - // TODO return si.scope()->as<WasmInstanceScope>().nextFrameSlot(); - return 0; case ScopeKind::WasmFunction: - // TODO return si.scope()->as<WasmFunctionScope>().nextFrameSlot(); - return 0; + return si.abstractScope().nextFrameSlot(); } } MOZ_CRASH("Not an enclosing intra-frame Scope"); } /* static */ LexicalScope* LexicalScope::create(JSContext* cx, ScopeKind kind, Handle<Data*> data, uint32_t firstFrameSlot, @@ -574,24 +551,24 @@ LexicalScope* LexicalScope::create(JSCon return nullptr; } return createWithData(cx, kind, ©, firstFrameSlot, enclosing); } bool LexicalScope::prepareForScopeCreation(JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, - HandleScope enclosing, + Handle<AbstractScope> enclosing, MutableHandle<UniquePtr<Data>> data, MutableHandleShape envShape) { bool isNamedLambda = kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda; MOZ_ASSERT_IF(!isNamedLambda && firstFrameSlot != 0, - firstFrameSlot == nextFrameSlot(AbstractScope(enclosing))); + firstFrameSlot == nextFrameSlot(enclosing)); MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT); BindingIter bi(*data, firstFrameSlot, isNamedLambda); if (!PrepareScopeData<LexicalScope>( cx, bi, data, &LexicalEnvironmentObject::class_, BaseShape::NOT_EXTENSIBLE | BaseShape::DELEGATE, envShape)) { return false; } @@ -599,17 +576,19 @@ bool LexicalScope::prepareForScopeCreati } /* static */ LexicalScope* LexicalScope::createWithData(JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data, uint32_t firstFrameSlot, HandleScope enclosing) { RootedShape envShape(cx); - if (!prepareForScopeCreation(cx, kind, firstFrameSlot, enclosing, data, + Rooted<AbstractScope> abstractScope(cx, enclosing); + + if (!prepareForScopeCreation(cx, kind, firstFrameSlot, abstractScope, data, &envShape)) { return nullptr; } auto scope = Scope::create<LexicalScope>(cx, kind, enclosing, envShape, data); if (!scope) { return nullptr; } @@ -1047,17 +1026,16 @@ GlobalScope* GlobalScope::createWithData GlobalScope* GlobalScope::clone(JSContext* cx, Handle<GlobalScope*> scope, ScopeKind kind) { Rooted<Data*> dataOriginal(cx, &scope->as<GlobalScope>().data()); Rooted<UniquePtr<Data>> dataClone( cx, CopyScopeData<GlobalScope>(cx, dataOriginal)); if (!dataClone) { return nullptr; } - return Scope::create<GlobalScope>(cx, kind, nullptr, nullptr, &dataClone); } template <XDRMode mode> /* static */ XDRResult GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope) { MOZ_ASSERT((mode == XDR_DECODE) == !scope); @@ -1110,17 +1088,16 @@ WithScope* WithScope::create(JSContext* return static_cast<WithScope*>(scope); } template <XDRMode mode> /* static */ XDRResult WithScope::XDR(XDRState<mode>* xdr, HandleScope enclosing, MutableHandleScope scope) { JSContext* cx = xdr->cx(); - if (mode == XDR_DECODE) { scope.set(create(cx, enclosing)); if (!scope) { return xdr->fail(JS::TranscodeResult_Throw); } } return Ok(); @@ -1231,17 +1208,16 @@ XDRResult EvalScope::XDR(XDRState<mode>* } MOZ_TRY(XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data)); if (mode == XDR_DECODE) { if (!data->length) { MOZ_ASSERT(!data->nextFrameSlot); } - scope.set(createWithData(cx, kind, &uniqueData.ref(), enclosing)); if (!scope) { return xdr->fail(JS::TranscodeResult_Throw); } } } return Ok(); @@ -1463,20 +1439,19 @@ WasmInstanceScope* WasmInstanceScope::cr } MOZ_ASSERT(data->length == namesCount); data->instance.init(instance); data->memoriesStart = 0; data->globalsStart = globalsStart; - Rooted<Scope*> enclosingScope(cx, &cx->global()->emptyGlobalScope()); - + RootedScope enclosing(cx, &cx->global()->emptyGlobalScope()); return Scope::create<WasmInstanceScope>(cx, ScopeKind::WasmInstance, - enclosingScope, + enclosing, /* envShape = */ nullptr, &data); } /* static */ Shape* WasmInstanceScope::getEmptyEnvironmentShape(JSContext* cx) { const JSClass* cls = &WasmInstanceEnvironmentObject::class_; return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), WasmInstanceEnvShapeFlags); @@ -1824,8 +1799,233 @@ JSAtom* js::FrameSlotName(JSScript* scri 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); } + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<FunctionScope::Data*> dataArg, + bool hasParameterExprs, bool needsEnvironment, + frontend::FunctionBox* funbox, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<FunctionScope::Data>> data( + cx, dataArg ? CopyScopeData<FunctionScope>(cx, dataArg) + : NewEmptyScopeData<FunctionScope>(cx)); + if (!data) { + return false; + } + + RootedShape envShape(cx); + RootedFunction fun(cx, funbox->function()); + if (!FunctionScope::prepareForScopeCreation( + cx, &data, hasParameterExprs, + dataArg ? dataArg->isFieldInitializer : IsFieldInitializer::No, + needsEnvironment, fun, &envShape)) { + return false; + } + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack( + cx, ScopeKind::Function, enclosing, std::move(data.get()), envShape, + funbox); +} + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, + Handle<LexicalScope::Data*> dataArg, + uint32_t firstFrameSlot, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<LexicalScope::Data>> data( + cx, CopyScopeData<LexicalScope>(cx, dataArg)); + if (!data) { + return false; + } + + RootedShape envShape(cx); + if (!LexicalScope::prepareForScopeCreation(cx, kind, firstFrameSlot, + enclosing, &data, &envShape)) { + return false; + } + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack( + cx, kind, enclosing, std::move(data.get()), envShape); +} + +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<VarScope::Data*> dataArg, + uint32_t firstFrameSlot, bool needsEnvironment, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<VarScope::Data>> data( + cx, dataArg ? CopyScopeData<VarScope>(cx, dataArg) + : NewEmptyVarScopeData(cx, firstFrameSlot)); + if (!data) { + return false; + } + + RootedShape envShape(cx); + if (!VarScope::prepareForScopeCreation(cx, kind, &data, firstFrameSlot, + needsEnvironment, &envShape)) { + return false; + } + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack( + cx, kind, enclosing, std::move(data.get()), envShape); +} + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, + Handle<GlobalScope::Data*> dataArg, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<GlobalScope::Data>> data( + cx, dataArg ? CopyScopeData<GlobalScope>(cx, dataArg) + : NewEmptyScopeData<GlobalScope>(cx)); + 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. + Rooted<AbstractScope> enclosing(cx); + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack(cx, kind, enclosing, + std::move(data.get())); +} + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + ScopeKind kind, Handle<EvalScope::Data*> dataArg, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<EvalScope::Data>> data( + cx, dataArg ? CopyScopeData<EvalScope>(cx, dataArg) + : NewEmptyScopeData<EvalScope>(cx)); + if (!data) { + return false; + } + + RootedShape envShape(cx); + if (!EvalScope::prepareForScopeCreation(cx, kind, &data, &envShape)) { + return false; + } + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack( + cx, kind, enclosing, std::move(data.get()), envShape); +} + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<ModuleScope::Data*> dataArg, + HandleModuleObject module, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + // The data that's passed in is from the frontend and is LifoAlloc'd. + // Copy it now that we're creating a permanent VM scope. + Rooted<UniquePtr<ModuleScope::Data>> data( + cx, dataArg ? CopyScopeData<ModuleScope>(cx, dataArg) + : NewEmptyScopeData<ModuleScope>(cx)); + if (!data) { + return false; + } + + MOZ_ASSERT(enclosing.get().is<GlobalScope>()); + + // 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. + RootedShape envShape(cx); + if (!ModuleScope::prepareForScopeCreation(cx, &data, module, &envShape)) { + return false; + } + + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack( + cx, ScopeKind::Module, enclosing, std::move(data.get()), envShape); +} + +/* static */ +bool ScopeCreationData::create(JSContext* cx, frontend::ParseInfo& parseInfo, + Handle<AbstractScope> enclosing, + ScopeIndex* index) { + *index = parseInfo.scopeCreationData.length(); + return parseInfo.scopeCreationData.emplaceBack(cx, ScopeKind::With, + enclosing); +} + +// WithScopes are unique because they don't go through the +// Scope::create<ConcreteType> path. +template <> +Scope* ScopeCreationData::createSpecificScope<WithScope>(JSContext* cx) { + RootedScope enclosingScope(cx); + if (!getOrCreateEnclosingScope(cx, &enclosingScope)) { + return nullptr; + } + + WithScope* scope = static_cast<WithScope*>( + Scope::create(cx, ScopeKind::With, enclosingScope, nullptr)); + + if (!scope) { + return nullptr; + } + + scope_ = scope; + + return scope; +} + +template <class SpecificScopeType> +Scope* ScopeCreationData::createSpecificScope(JSContext* cx) { + Rooted<UniquePtr<typename SpecificScopeType::Data>> rootedData( + cx, releaseData<SpecificScopeType>()); + RootedShape shape(cx, environmentShape_); + + RootedScope enclosingScope(cx); + if (!getOrCreateEnclosingScope(cx, &enclosingScope)) { + return nullptr; + } + + // Because we already baked the data here, we needn't do it again. + SpecificScopeType* scope = Scope::create<SpecificScopeType>( + cx, kind(), enclosingScope, shape, &rootedData); + if (!scope) { + return nullptr; + } + + scope_ = scope; + + return scope; +} + +template Scope* ScopeCreationData::createSpecificScope<FunctionScope>( + JSContext* cx); +template Scope* ScopeCreationData::createSpecificScope<LexicalScope>( + JSContext* cx); +template Scope* ScopeCreationData::createSpecificScope<EvalScope>( + JSContext* cx); +template Scope* ScopeCreationData::createSpecificScope<GlobalScope>( + JSContext* cx); +template Scope* ScopeCreationData::createSpecificScope<VarScope>(JSContext* cx); +template Scope* ScopeCreationData::createSpecificScope<ModuleScope>( + JSContext* cx); \ No newline at end of file
--- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -22,17 +22,16 @@ #include "vm/JSObject.h" #include "vm/ScopeKind.h" #include "vm/Xdr.h" namespace js { class BaseScopeData; class ModuleObject; -class Scope; class AbstractScope; enum class BindingKind : uint8_t { Import, FormalParameter, Var, Let, Const, @@ -235,16 +234,17 @@ class WrappedPtrOperations<Scope*, Wrapp } }; // // The base class of all Scopes. // class Scope : public js::gc::TenuredCell { friend class GCMarker; + friend class ScopeCreationData; // The enclosing scope or nullptr. const GCPtrScope enclosing_; // The kind determines data_. const ScopeKind kind_; // If there are any aliased bindings, the shape for the @@ -258,36 +258,36 @@ class Scope : public js::gc::TenuredCell : enclosing_(enclosing), kind_(kind), environmentShape_(environmentShape), data_(nullptr) {} static Scope* create(JSContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape); - template <typename ConcreteScope> - static ConcreteScope* create( - JSContext* cx, ScopeKind kind, HandleScope enclosing, - HandleShape envShape, - MutableHandle<UniquePtr<typename ConcreteScope::Data>> data); - template <typename ConcreteScope, XDRMode mode> static XDRResult XDRSizedBindingNames( XDRState<mode>* xdr, Handle<ConcreteScope*> scope, MutableHandle<typename ConcreteScope::Data*> data); Shape* maybeCloneEnvironmentShape(JSContext* cx); template <typename ConcreteScope> void initData(MutableHandle<UniquePtr<typename ConcreteScope::Data>> data); template <typename F> void applyScopeDataTyped(F&& f); public: + template <typename ConcreteScope> + static ConcreteScope* create( + JSContext* cx, ScopeKind kind, HandleScope enclosing, + HandleShape envShape, + MutableHandle<UniquePtr<typename ConcreteScope::Data>> data); + static const JS::TraceKind TraceKind = JS::TraceKind::Scope; template <typename T> bool is() const { return kind_ == T::classScopeKind_; } template <typename T> @@ -303,28 +303,32 @@ class Scope : public js::gc::TenuredCell } ScopeKind kind() const { return kind_; } Scope* enclosing() const { return enclosing_; } Shape* environmentShape() const { return environmentShape_; } - bool hasEnvironment() const { - switch (kind()) { + static bool hasEnvironment(ScopeKind kind, Shape* environmentShape) { + switch (kind) { case ScopeKind::With: case ScopeKind::Global: case ScopeKind::NonSyntactic: return true; default: // If there's a shape, an environment must be created for this scope. - return environmentShape_ != nullptr; + return environmentShape != nullptr; } } + bool hasEnvironment() const { + return hasEnvironment(kind_, environmentShape_); + } + uint32_t chainLength() const; uint32_t environmentChainLength() const; template <typename T> bool hasOnChain() const { for (const Scope* it = this; it; it = it->enclosing()) { if (it->is<T>()) { return true; @@ -384,16 +388,17 @@ inline size_t SizeOfData(uint32_t numBin // // All kinds of LexicalScopes correspond to LexicalEnvironmentObjects on the // environment chain. // class LexicalScope : public Scope { friend class Scope; friend class BindingIter; friend class GCMarker; + friend class ScopeCreationData; public: // Data is public because it is created by the frontend. See // Parser<FullParseHandler>::newLexicalScopeData. struct Data : public BaseScopeData { // Bindings are sorted by kind in both frames and environments. // // lets - [0, constStart) @@ -425,17 +430,17 @@ class LexicalScope : public Scope { private: static LexicalScope* createWithData(JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data, uint32_t firstFrameSlot, HandleScope enclosing); static bool prepareForScopeCreation(JSContext* cx, ScopeKind kind, uint32_t firstFrameSlot, - HandleScope enclosing, + Handle<AbstractScope> enclosing, MutableHandle<UniquePtr<Data>> data, MutableHandleShape envShape); Data& data() { return *static_cast<Data*>(data_); } const Data& data() const { return *static_cast<Data*>(data_); } static uint32_t nextFrameSlot(const AbstractScope& start); @@ -612,16 +617,17 @@ class FunctionScope : public Scope { // }; // // Corresponds to VarEnvironmentObject on environment chain. // class VarScope : public Scope { friend class GCMarker; friend class BindingIter; friend class Scope; + friend class ScopeCreationData; public: // Data is public because it is created by the // frontend. See Parser<FullParseHandler>::newVarScopeData. struct Data : public BaseScopeData { // All bindings are vars. uint32_t length = 0; @@ -786,16 +792,17 @@ class WithScope : public Scope { // A sloppy eval. This is an empty scope, used only in the frontend, to // detect redeclaration errors. It has no Environment. Any `var`s declared // in the eval code are bound on the nearest enclosing var environment. // class EvalScope : public Scope { friend class Scope; friend class BindingIter; friend class GCMarker; + friend class ScopeCreationData; public: // Data is public because it is created by the frontend. See // Parser<FullParseHandler>::newEvalScopeData. struct Data : public BaseScopeData { // All bindings in an eval script are 'var' bindings. The implicit // lexical scope around the eval is present regardless of strictness // and is its own LexicalScope. @@ -874,16 +881,17 @@ inline bool Scope::is<EvalScope>() const // // Corresponds to a ModuleEnvironmentObject on the environment chain. // class ModuleScope : public Scope { friend class GCMarker; friend class BindingIter; friend class Scope; friend class AbstractScope; + friend class ScopeCreationData; static const ScopeKind classScopeKind_ = ScopeKind::Module; public: // Data is public because it is created by the frontend. See // Parser<FullParseHandler>::newModuleScopeData. struct Data : BaseScopeData { // The module of the scope. GCPtr<ModuleObject*> module = {};