From 7e04d0a1686226b98e68e13ad233e3527e8181f0 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Thu, 4 Nov 2021 18:25:42 +0000 Subject: [PATCH] Bug 1730881 - Initialize CompilationSyntaxParseCache from InputScript variants. r=arai This change clone some of the functions used to initialize the CompilationSyntaxParseCache. These are specialized to copy the minimal set of information needed for skipping inner functions and for iterating over closed-over-bindings. Unforutnately, as opposed to what this structure was initialy designed for, we are not yet able to reuse the Stencils from the InputScript as ParseAtomIndex of the InputScript are in the context of the InputScript and not of the CompilationState which wraps the CompilationSyntaxParseCache. Until we are capable of reusing the same indexes of a previous compilation, we would have to duplicate the Stencil structures. Thus, copyScriptInfo and copyClosedOVerBindings are copied from the original functions and adapted to work with Stencil inputs. Differential Revision: https://phabricator.services.mozilla.com/D128180 --- js/src/frontend/CompilationStencil.h | 18 +++- js/src/frontend/Stencil.cpp | 150 ++++++++++++++++++++++++--- 2 files changed, 148 insertions(+), 20 deletions(-) diff --git a/js/src/frontend/CompilationStencil.h b/js/src/frontend/CompilationStencil.h index 215f4b1a34b2..65246d4d1ff2 100644 --- a/js/src/frontend/CompilationStencil.h +++ b/js/src/frontend/CompilationStencil.h @@ -657,7 +657,8 @@ struct CompilationInput { // The BaseScript* is needed when instantiating a lazy function. // See InstantiateTopLevel and FunctionsFromExistingLazy. - BaseScript* lazyOuterScript() { return lazy_.raw().as(); } + InputScript lazyOuterScript() { return lazy_; } + BaseScript* lazyOuterBaseScript() { return lazy_.raw().as(); } // The JSFunction* is needed when instantiating a lazy function. // See FunctionsFromExistingLazy. @@ -774,7 +775,8 @@ class CompilationSyntaxParseCache { // used for reporting allocation errors. [[nodiscard]] bool init(JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, - CompilationAtomCache& atomCache, BaseScript* lazy); + CompilationAtomCache& atomCache, + const InputScript& lazy); private: // Return the script index of a given inner function. @@ -794,15 +796,23 @@ class CompilationSyntaxParseCache { [[nodiscard]] bool copyFunctionInfo(JSContext* cx, ParserAtomsTable& parseAtoms, CompilationAtomCache& atomCache, - BaseScript* lazy); + const InputScript& lazy); [[nodiscard]] bool copyScriptInfo(JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, CompilationAtomCache& atomCache, BaseScript* lazy); + [[nodiscard]] bool copyScriptInfo(JSContext* cx, LifoAlloc& alloc, + ParserAtomsTable& parseAtoms, + CompilationAtomCache& atomCache, + const ScriptStencilRef& lazy); [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, CompilationAtomCache& atomCache, BaseScript* lazy); + [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, LifoAlloc& alloc, + ParserAtomsTable& parseAtoms, + CompilationAtomCache& atomCache, + const ScriptStencilRef& lazy); }; // AsmJS scripts are very rare on-average, so we use a HashMap to associate @@ -1284,7 +1294,7 @@ struct MOZ_RAII CompilationState : public ExtensibleCompilationStencil { // gcThings is later used by the full parser initialization. if (input.isDelazifying()) { - BaseScript* lazy = input.lazyOuterScript(); + InputScript lazy = input.lazyOuterScript(); auto& atomCache = input.atomCache; if (!previousParseCache.init(cx, alloc, parserAtoms, atomCache, lazy)) { return false; diff --git a/js/src/frontend/Stencil.cpp b/js/src/frontend/Stencil.cpp index eb999a09c05a..945f5dbd9f81 100644 --- a/js/src/frontend/Stencil.cpp +++ b/js/src/frontend/Stencil.cpp @@ -860,14 +860,20 @@ void CompilationInput::trace(JSTracer* trc) { bool CompilationSyntaxParseCache::init(JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, CompilationAtomCache& atomCache, - BaseScript* lazy) { + const InputScript& lazy) { if (!copyFunctionInfo(cx, parseAtoms, atomCache, lazy)) { return false; } - if (!copyScriptInfo(cx, alloc, parseAtoms, atomCache, lazy)) { - return false; - } - if (!copyClosedOverBindings(cx, alloc, parseAtoms, atomCache, lazy)) { + bool success = lazy.raw().match([&](auto& ref) { + if (!copyScriptInfo(cx, alloc, parseAtoms, atomCache, ref)) { + return false; + } + if (!copyClosedOverBindings(cx, alloc, parseAtoms, atomCache, ref)) { + return false; + } + return true; + }); + if (!success) { return false; } #ifdef DEBUG @@ -878,20 +884,19 @@ bool CompilationSyntaxParseCache::init(JSContext* cx, LifoAlloc& alloc, bool CompilationSyntaxParseCache::copyFunctionInfo( JSContext* cx, ParserAtomsTable& parseAtoms, - CompilationAtomCache& atomCache, BaseScript* lazy) { - if (lazy->function()->displayAtom()) { - displayAtom_ = - parseAtoms.internJSAtom(cx, atomCache, lazy->function()->displayAtom()); + CompilationAtomCache& atomCache, const InputScript& lazy) { + InputName name = lazy.displayAtom(); + if (!name.isNull()) { + displayAtom_ = name.internInto(cx, parseAtoms, atomCache); if (!displayAtom_) { return false; } } - funExtra_.immutableFlags = lazy->immutableFlags(); - funExtra_.extent = lazy->extent(); + funExtra_.immutableFlags = lazy.immutableFlags(); + funExtra_.extent = lazy.extent(); if (funExtra_.useMemberInitializers()) { - funExtra_.setMemberInitializers( - lazy->function()->baseScript()->getMemberInitializers()); + funExtra_.setMemberInitializers(lazy.getMemberInitializers()); } return true; @@ -969,6 +974,68 @@ bool CompilationSyntaxParseCache::copyScriptInfo( return true; } +bool CompilationSyntaxParseCache::copyScriptInfo( + JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, + CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) { + using GCThingsSpan = mozilla::Span; + using ScriptDataSpan = mozilla::Span; + using ScriptExtraSpan = mozilla::Span; + cachedGCThings_ = GCThingsSpan(nullptr); + cachedScriptData_ = ScriptDataSpan(nullptr); + cachedScriptExtra_ = ScriptExtraSpan(nullptr); + + size_t offset = lazy.scriptData().gcThingsOffset.index; + size_t length = lazy.scriptData().gcThingsLength; + if (length == 0) { + return true; + } + + // Reduce the length to the first element which is not a function. + for (size_t i = offset; i < offset + length; i++) { + if (!lazy.context_.gcThingData[i].isFunction()) { + length = i - offset; + break; + } + } + + TaggedScriptThingIndex* gcThingsData = + alloc.newArrayUninitialized(length); + ScriptStencil* scriptData = + alloc.newArrayUninitialized(length); + ScriptStencilExtra* scriptExtra = + alloc.newArrayUninitialized(length); + if (!gcThingsData || !scriptData || !scriptExtra) { + ReportOutOfMemory(cx); + return false; + } + + for (size_t i = 0; i < length; i++) { + ScriptStencilRef inner{lazy.context_, + lazy.context_.gcThingData[i + offset].toFunction()}; + gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i)); + new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil(); + ScriptStencil& data = scriptData[i]; + ScriptStencilExtra& extra = scriptExtra[i]; + + InputName name{inner, inner.scriptData().functionAtom}; + if (!name.isNull()) { + auto displayAtom = name.internInto(cx, parseAtoms, atomCache); + if (!displayAtom) { + return false; + } + data.functionAtom = displayAtom; + } + data.functionFlags = inner.scriptData().functionFlags; + + extra = inner.scriptExtra(); + } + + cachedGCThings_ = GCThingsSpan(gcThingsData, length); + cachedScriptData_ = ScriptDataSpan(scriptData, length); + cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length); + return true; +} + bool CompilationSyntaxParseCache::copyClosedOverBindings( JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, CompilationAtomCache& atomCache, BaseScript* lazy) { @@ -1015,6 +1082,56 @@ bool CompilationSyntaxParseCache::copyClosedOverBindings( return true; } +bool CompilationSyntaxParseCache::copyClosedOverBindings( + JSContext* cx, LifoAlloc& alloc, ParserAtomsTable& parseAtoms, + CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) { + using ClosedOverBindingsSpan = mozilla::Span; + closedOverBindings_ = ClosedOverBindingsSpan(nullptr); + + // The gcthings array contains the inner function list followed by the + // closed-over bindings data. Skip the inner function list, as it is already + // cached in cachedGCThings_. See also: BaseScript::CreateLazy. + size_t offset = lazy.scriptData().gcThingsOffset.index; + size_t length = lazy.scriptData().gcThingsLength; + size_t start = cachedGCThings_.Length(); + MOZ_ASSERT(start <= length); + if (length - start == 0) { + return true; + } + length -= start; + start += offset; + + // Atoms from the lazy.context (CompilationStencil) are not registered in the + // the parseAtoms table. Thus we create a new span which will contain all the + // interned atoms. + TaggedParserAtomIndex* closedOverBindings = + alloc.newArrayUninitialized(length); + if (!closedOverBindings) { + ReportOutOfMemory(cx); + return false; + } + + for (size_t i = 0; i < length; i++) { + auto gcThing = lazy.context_.gcThingData[i + start]; + if (gcThing.isNull()) { + closedOverBindings[i] = TaggedParserAtomIndex::null(); + continue; + } + + MOZ_ASSERT(gcThing.isAtom()); + InputName name(lazy, gcThing.toAtom()); + auto parserAtom = name.internInto(cx, parseAtoms, atomCache); + if (!parserAtom) { + return false; + } + + closedOverBindings[i] = parserAtom; + } + + closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length); + return true; +} + void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); } void CompilationGCOutput::trace(JSTracer* trc) { @@ -1514,8 +1631,9 @@ static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input, MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex)); if (!stencil.isInitialStencil()) { - MOZ_ASSERT(input.lazyOuterScript()); - RootedScript script(cx, JSScript::CastFromLazy(input.lazyOuterScript())); + MOZ_ASSERT(input.lazyOuterBaseScript()); + RootedScript script(cx, + JSScript::CastFromLazy(input.lazyOuterBaseScript())); if (!JSScript::fullyInitFromStencil(cx, input.atomCache, stencil, gcOutput, script, CompilationStencil::TopLevelIndex)) { @@ -1713,7 +1831,7 @@ static void FunctionsFromExistingLazy(CompilationInput& input, MOZ_ASSERT(gcOutput.functions.empty()); gcOutput.functions.infallibleAppend(input.function()); - for (JS::GCCellPtr elem : input.lazyOuterScript()->gcthings()) { + for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) { if (!elem.is()) { continue; }