Open github-actions[bot] opened 1 year ago
/js/src/frontend/CompilationStencil.h
/js/src/frontend/Stencil.cpp
/js/src/vm/BytecodeUtil.cpp
/js/src/vm/BytecodeUtil.h
/js/src/vm/Opcodes.h
--- 20edf5ff2283dfc74fdf23f9b6e2f5100c7354d4/js/src/frontend/CompilationStencil.h +++ 65de67ceb4f8f8551175cf981eceeb13bdbcbb29/js/src/frontend/CompilationStencil.h @@ -3,68 +3,83 @@ * 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_CompilationStencil_h #define frontend_CompilationStencil_h #include "mozilla/AlreadyAddRefed.h" // already_AddRefed -#include "mozilla/Assertions.h" // MOZ_ASSERT -#include "mozilla/Atomics.h" // mozilla::Atomic -#include "mozilla/Attributes.h" // MOZ_RAII -#include "mozilla/HashTable.h" // mozilla::HashMap -#include "mozilla/Maybe.h" // mozilla::Maybe +#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_RELEASE_ASSERT, MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE +#include "mozilla/Atomics.h" // mozilla::Atomic +#include "mozilla/Attributes.h" // MOZ_RAII, MOZ_STACK_CLASS +#include "mozilla/HashTable.h" // mozilla::HashMap, mozilla::DefaultHasher +#include "mozilla/Maybe.h" // mozilla::Maybe #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf #include "mozilla/RefPtr.h" // RefPtr -#include "mozilla/Span.h" -#include "mozilla/Variant.h" // mozilla::Variant +#include "mozilla/Span.h" // mozilla::Span +#include "mozilla/Variant.h" // mozilla::Variant -#include "ds/LifoAlloc.h" -#include "frontend/FrontendContext.h" // AutoReportFrontendContext -#include "frontend/NameAnalysisTypes.h" // EnvironmentCoordinate -#include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex -#include "frontend/ScriptIndex.h" // ScriptIndex -#include "frontend/SharedContext.h" -#include "frontend/Stencil.h" +#include <algorithm> // std::swap +#include <stddef.h> // size_t +#include <stdint.h> // uint32_t, uintptr_t +#include <type_traits> // std::is_pointer_v +#include <utility> // std::forward, std::move + +#include "ds/LifoAlloc.h" // LifoAlloc, LifoAllocScope +#include "frontend/FrontendContext.h" // FrontendContext +#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind +#include "frontend/NameAnalysisTypes.h" // NameLocation +#include "frontend/ParserAtom.h" // ParserAtomsTable, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomSpan +#include "frontend/ScopeIndex.h" // ScopeIndex +#include "frontend/ScriptIndex.h" // ScriptIndex +#include "frontend/SharedContext.h" // ThisBinding, InheritThis, Directives +#include "frontend/Stencil.h" // ScriptStencil, ScriptStencilExtra, ScopeStencil, RegExpStencil, BigIntStencil, ObjLiteralStencil, BaseParserScopeData, StencilModuleMetadata #include "frontend/TaggedParserAtomIndexHasher.h" // TaggedParserAtomIndexHasher -#include "frontend/UsedNameTracker.h" -#include "js/GCVector.h" -#include "js/HashTable.h" -#include "js/RefCounted.h" // AtomicRefCounted -#include "js/Transcoding.h" -#include "js/UniquePtr.h" // js::UniquePtr -#include "js/Vector.h" -#include "js/WasmModule.h" -#include "vm/GlobalObject.h" // GlobalObject -#include "vm/JSContext.h" -#include "vm/JSFunction.h" // JSFunction -#include "vm/JSScript.h" // SourceExtent -#include "vm/Realm.h" +#include "frontend/UsedNameTracker.h" // UsedNameTracker +#include "js/AllocPolicy.h" // SystemAllocPolicy, ReportOutOfMemory +#include "js/GCVector.h" // JS::GCVector +#include "js/RefCounted.h" // AtomicRefCounted +#include "js/RootingAPI.h" // JS::Handle +#include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange +#include "js/UniquePtr.h" // js::UniquePtr +#include "js/Vector.h" // Vector +#include "js/WasmModule.h" // JS::WasmModule +#include "vm/FunctionFlags.h" // FunctionFlags +#include "vm/GlobalObject.h" // GlobalObject +#include "vm/JSContext.h" // JSContext +#include "vm/JSFunction.h" // JSFunction +#include "vm/JSScript.h" // BaseScript, ScriptSource, SourceExtent +#include "vm/Realm.h" // JSContext::global +#include "vm/Scope.h" // Scope, ModuleScope #include "vm/ScopeKind.h" // ScopeKind -#include "vm/SharedStencil.h" // SharedImmutableScriptData +#include "vm/SharedStencil.h" // ImmutableScriptFlags, MemberInitializers, SharedImmutableScriptData, RO_IMMUTABLE_SCRIPT_FLAGS class JSAtom; +class JSFunction; +class JSObject; class JSString; +class JSTracer; namespace JS { class JS_PUBLIC_API ReadOnlyCompileOptions; } namespace js { class AtomSet; class JSONPrinter; class ModuleObject; namespace frontend { struct CompilationInput; struct CompilationStencil; struct CompilationGCOutput; +struct PreallocatedCompilationGCOutput; class ScriptStencilIterable; struct InputName; class ScopeBindingCache; // When delazifying modules' inner functions, the actual global scope is used. // However, when doing a delazification the global scope is not available. We // use this dummy type to be a placeholder to be used as part of the InputScope // variants to mimic what the Global scope would be used for. @@ -674,19 +689,19 @@ struct CompilationInput { if (!initScriptSource(fc)) { return false; } enclosingScope = InputScope(&cx->global()->emptyGlobalScope()); return true; } bool initForStandaloneFunctionInNonSyntacticScope( - FrontendContext* fc, Handle<Scope*> functionEnclosingScope); + FrontendContext* fc, JS::Handle<Scope*> functionEnclosingScope); - bool initForEval(FrontendContext* fc, Handle<Scope*> evalEnclosingScope) { + bool initForEval(FrontendContext* fc, JS::Handle<Scope*> evalEnclosingScope) { target = CompilationTarget::Eval; if (!initScriptSource(fc)) { return false; } enclosingScope = InputScope(evalEnclosingScope); return true; } @@ -904,18 +919,19 @@ class CompilationSyntaxParseCache { CompilationAtomCache& atomCache, const ScriptStencilRef& lazy); }; // AsmJS scripts are very rare on-average, so we use a HashMap to associate // data with a ScriptStencil. The ScriptStencil has a flag to indicate if we // need to even do this lookup. using StencilAsmJSMap = - HashMap<ScriptIndex, RefPtr<const JS::WasmModule>, - mozilla::DefaultHasher<ScriptIndex>, js::SystemAllocPolicy>; + mozilla::HashMap<ScriptIndex, RefPtr<const JS::WasmModule>, + mozilla::DefaultHasher<ScriptIndex>, + js::SystemAllocPolicy>; struct StencilAsmJSContainer : public js::AtomicRefCounted<StencilAsmJSContainer> { StencilAsmJSMap moduleMap; StencilAsmJSContainer() = default; size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { @@ -932,18 +948,19 @@ struct SharedDataContainer { // updating or clearing the pointer. using SingleSharedDataPtr = SharedImmutableScriptData*; using SharedDataVector = Vector<RefPtr<js::SharedImmutableScriptData>, 0, js::SystemAllocPolicy>; using SharedDataVectorPtr = SharedDataVector*; using SharedDataMap = - HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>, - mozilla::DefaultHasher<ScriptIndex>, js::SystemAllocPolicy>; + mozilla::HashMap<ScriptIndex, RefPtr<js::SharedImmutableScriptData>, + mozilla::DefaultHasher<ScriptIndex>, + js::SystemAllocPolicy>; using SharedDataMapPtr = SharedDataMap*; private: enum { SingleTag = 0, VectorTag = 1, MapTag = 2, BorrowTag = 3, @@ -1192,33 +1209,37 @@ struct CompilationStencil { [[nodiscard]] static bool instantiateStencilAfterPreparation( JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, CompilationGCOutput& gcOutput); [[nodiscard]] static bool prepareForInstantiate( FrontendContext* fc, CompilationAtomCache& atomCache, const CompilationStencil& stencil, CompilationGCOutput& gcOutput); + [[nodiscard]] static bool prepareForInstantiate( + FrontendContext* fc, CompilationAtomCache& atomCache, + const CompilationStencil& stencil, + PreallocatedCompilationGCOutput& gcOutput); [[nodiscard]] static bool instantiateStencils( JSContext* cx, CompilationInput& input, const CompilationStencil& stencil, CompilationGCOutput& gcOutput); // Decode the special self-hosted stencil [[nodiscard]] bool instantiateSelfHostedAtoms( JSContext* cx, AtomSet& atomSet, CompilationAtomCache& atomCache) const; [[nodiscard]] JSScript* instantiateSelfHostedTopLevelForRealm( JSContext* cx, CompilationInput& input); [[nodiscard]] JSFunction* instantiateSelfHostedLazyFunction( JSContext* cx, CompilationAtomCache& atomCache, ScriptIndex index, - Handle<JSAtom*> name); + JS::Handle<JSAtom*> name); [[nodiscard]] bool delazifySelfHostedFunction(JSContext* cx, CompilationAtomCache& atomCache, ScriptIndexRange range, - HandleFunction fun); + JS::Handle<JSFunction*> fun); [[nodiscard]] bool serializeStencils(JSContext* cx, CompilationInput& input, JS::TranscodeBuffer& buf, bool* succeededOut = nullptr) const; [[nodiscard]] bool deserializeStencils( FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, const JS::TranscodeRange& range, bool* succeededOut = nullptr); @@ -1548,108 +1569,240 @@ inline size_t ExtensibleCompilationStenc regExpData.sizeOfExcludingThis(mallocSizeOf) + bigIntData.sizeOfExcludingThis(mallocSizeOf) + objLiteralData.sizeOfExcludingThis(mallocSizeOf) + parserAtoms.sizeOfExcludingThis(mallocSizeOf) + sharedData.sizeOfExcludingThis(mallocSizeOf) + moduleMetadataSize + asmJSSize; } +// A PreAllocateableGCArray is an array of GC thing pointers. +// +// The array's internal buffer can be allocated ahead of time, possibly off +// main thread. +template <typename T> +struct PreAllocateableGCArray { + private: + size_t length_ = 0; + + // Inline element for the case when length_ == 1. + T inlineElem_; + + // Heap-allocated elements for the case when length_ > 1; + T* elems_ = nullptr; + + public: + struct Preallocated { + private: + size_t length_ = 0; + uintptr_t* elems_ = nullptr; + + friend struct PreAllocateableGCArray<T>; + + public: + Preallocated() = default; + ~Preallocated(); + + bool empty() const { return length_ == 0; } + + size_t length() const { return length_; } + + private: + bool isInline() const { return length_ == 1; } + + public: + bool allocate(size_t length); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return sizeof(uintptr_t) * length_; + } + }; + + PreAllocateableGCArray() { + static_assert(std::is_pointer_v<T>, + "PreAllocateableGCArray element must be a pointer"); + } + ~PreAllocateableGCArray(); + + bool empty() const { return length_ == 0; } + + size_t length() const { return length_; } + + private: + bool isInline() const { return length_ == 1; } + + public: + bool allocate(size_t length); + bool allocateWith(T init, size_t length); + + // Steal pre-allocated buffer. + void steal(Preallocated&& buffer); + + T& operator[](size_t index) { + MOZ_ASSERT(index < length_); + + if (isInline()) { + return inlineElem_; + } + + return elems_[index]; + } + const T& operator[](size_t index) const { + MOZ_ASSERT(index < length_); + + if (isInline()) { + return inlineElem_; + } + + return elems_[index]; + } + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + if (!elems_) { + return 0; + } + + return sizeof(T) * length_; + } + + void trace(JSTracer* trc); +}; + +struct CompilationGCOutput; + +// Pre-allocated storage for CompilationGCOutput. +struct PreallocatedCompilationGCOutput { + private: + PreAllocateableGCArray<JSFunction*>::Preallocated functions; + PreAllocateableGCArray<js::Scope*>::Preallocated scopes; + + friend struct CompilationGCOutput; + + public: + PreallocatedCompilationGCOutput() = default; + + [[nodiscard]] bool allocate(FrontendContext* fc, size_t scriptDataLength, + size_t scopeDataLength) { + if (!functions.allocate(scriptDataLength)) { + ReportOutOfMemory(fc); + return false; + } + if (!scopes.allocate(scopeDataLength)) { + ReportOutOfMemory(fc); + return false; + } + return true; + } + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { + return functions.sizeOfExcludingThis(mallocSizeOf) + + scopes.sizeOfExcludingThis(mallocSizeOf); + } +}; + // The output of GC allocation from stencil. struct CompilationGCOutput { // The resulting outermost script for the compilation powered // by this CompilationStencil. JSScript* script = nullptr; // The resulting module object if there is one. ModuleObject* module = nullptr; - // A Rooted vector to handle tracing of JSFunction* and Atoms within. + // An array to handle tracing of JSFunction* and Atoms within. // // If the top level script isn't a function, the item at TopLevelIndex is // nullptr. - JS::GCVector<JSFunction*, 1, js::SystemAllocPolicy> functions; + PreAllocateableGCArray<JSFunction*> functions; // References to scopes are controlled via AbstractScopePtr, which holds onto // an index (and CompilationStencil reference). - JS::GCVector<js::Scope*, 1, js::SystemAllocPolicy> scopes; + PreAllocateableGCArray<js::Scope*> scopes; // The result ScriptSourceObject. This is unused in delazifying parses. ScriptSourceObject* sourceObject = nullptr; private: // If we are only instantiating part of a stencil, we can reduce allocations - // by setting a base index and reserving only the vector capacity we need. + // by setting a base index and allocating only the array elements we need. // This applies to both the `functions` and `scopes` arrays. These fields are - // initialized by `ensureReservedWithBaseIndex` which also reserves the vector - // sizes appropriately. + // initialized by `ensureAllocatedWithBaseIndex` which also allocates the + // array appropriately. // // Note: These are only used for self-hosted delazification currently. ScriptIndex functionsBaseIndex{}; ScopeIndex scopesBaseIndex{}; // End of fields. public: CompilationGCOutput() = default; - // Helper to access the `functions` vector. The NoBaseIndex version is used if + // Helper to access the `functions` array. The NoBaseIndex version is used if // the caller never uses a base index. JSFunction*& getFunction(ScriptIndex index) { return functions[index - functionsBaseIndex]; } JSFunction*& getFunctionNoBaseIndex(ScriptIndex index) { MOZ_ASSERT(!functionsBaseIndex); return functions[index]; } - // Helper accessors for the `scopes` vector. + // Helper accessors for the `scopes` array. js::Scope*& getScope(ScopeIndex index) { return scopes[index - scopesBaseIndex]; } js::Scope*& getScopeNoBaseIndex(ScopeIndex index) { MOZ_ASSERT(!scopesBaseIndex); return scopes[index]; } js::Scope* getScopeNoBaseIndex(ScopeIndex index) const { MOZ_ASSERT(!scopesBaseIndex); return scopes[index]; } - // Reserve output vector capacity. This may be called before instantiate to do - // allocations ahead of time (off thread). The stencil instantiation code will - // also run this to ensure the vectors are ready. - [[nodiscard]] bool ensureReserved(FrontendContext* fc, - size_t scriptDataLength, - size_t scopeDataLength) { - if (!functions.reserve(scriptDataLength)) { - ReportOutOfMemory(fc); - return false; + // Allocate output arrays. + [[nodiscard]] bool ensureAllocated(FrontendContext* fc, + size_t scriptDataLength, + size_t scopeDataLength) { + if (functions.empty()) { + if (!functions.allocate(scriptDataLength)) { + ReportOutOfMemory(fc); + return false; + } } - if (!scopes.reserve(scopeDataLength)) { - ReportOutOfMemory(fc); - return false; + if (scopes.empty()) { + if (!scopes.allocate(scopeDataLength)) { + ReportOutOfMemory(fc); + return false; + } } return true; } - // A variant of `ensureReserved` that sets a base index for the function and + // Steal output arrays' buffer. + void steal(PreallocatedCompilationGCOutput&& pre) { + functions.steal(std::move(pre.functions)); + scopes.steal(std::move(pre.scopes)); + } + + // A variant of `ensureAllocated` that sets a base index for the function and // scope arrays. This is used when instantiating only a subset of the stencil. // Currently this only applies to self-hosted delazification. The ranges // include the start index and exclude the limit index. - [[nodiscard]] bool ensureReservedWithBaseIndex(FrontendContext* fc, - ScriptIndex scriptStart, - ScriptIndex scriptLimit, - ScopeIndex scopeStart, - ScopeIndex scopeLimit) { + [[nodiscard]] bool ensureAllocatedWithBaseIndex(FrontendContext* fc, + ScriptIndex scriptStart, + ScriptIndex scriptLimit, + ScopeIndex scopeStart, + ScopeIndex scopeLimit) { this->functionsBaseIndex = scriptStart; this->scopesBaseIndex = scopeStart; - return ensureReserved(fc, scriptLimit - scriptStart, - scopeLimit - scopeStart); + return ensureAllocated(fc, scriptLimit - scriptStart, + scopeLimit - scopeStart); } // Size of dynamic data. Note that GC data is counted by GC and not here. size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { return functions.sizeOfExcludingThis(mallocSizeOf) + scopes.sizeOfExcludingThis(mallocSizeOf); } @@ -1771,18 +1924,19 @@ struct CompilationStencilMerger { // Delazifications are merged into this. // // If any failure happens during merge operation, this field is reset to // nullptr. UniquePtr<ExtensibleCompilationStencil> initial_; // A Map from function key to the ScriptIndex in the initial stencil. using FunctionKeyToScriptIndexMap = - HashMap<FunctionKey, ScriptIndex, mozilla::DefaultHasher<FunctionKey>, - js::SystemAllocPolicy>; + mozilla::HashMap<FunctionKey, ScriptIndex, + mozilla::DefaultHasher<FunctionKey>, + js::SystemAllocPolicy>; FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_; [[nodiscard]] bool buildFunctionKeyToIndex(FrontendContext* fc); ScriptIndex getInitialScriptIndexFor( const CompilationStencil& delazification) const; // A map from delazification's ParserAtomIndex to
--- ff723d87099783cccd9b6e6def5d03e4effd3bf8/js/src/frontend/Stencil.cpp +++ 65de67ceb4f8f8551175cf981eceeb13bdbcbb29/js/src/frontend/Stencil.cpp @@ -10,16 +10,18 @@ #include "mozilla/Assertions.h" // MOZ_RELEASE_ASSERT #include "mozilla/Maybe.h" // mozilla::Maybe #include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull #include "mozilla/PodOperations.h" // mozilla::PodCopy #include "mozilla/RefPtr.h" // RefPtr #include "mozilla/ScopeExit.h" // mozilla::ScopeExit #include "mozilla/Sprintf.h" // SprintfLiteral +#include <algorithm> // std::fill + #include "ds/LifoAlloc.h" // LifoAlloc #include "frontend/AbstractScopePtr.h" // ScopeIndex #include "frontend/BytecodeCompiler.h" // CompileGlobalScriptToStencil, InstantiateStencils, CanLazilyParse, ParseModuleToStencil #include "frontend/BytecodeSection.h" // EmitScriptThingsVector #include "frontend/CompilationStencil.h" // CompilationStencil, CompilationState, ExtensibleCompilationStencil, CompilationGCOutput, CompilationStencilMerger #include "frontend/FrontendContext.h" #include "frontend/NameAnalysisTypes.h" // EnvironmentCoordinate #include "frontend/ScopeBindingCache.h" // ScopeBindingCache @@ -29,16 +31,17 @@ #include "gc/Tracer.h" // TraceNullableRoot #include "js/CallArgs.h" // JSNative #include "js/CompileOptions.h" // JS::DecodeOptions #include "js/experimental/JSStencil.h" // JS::Stencil #include "js/GCAPI.h" // JS::AutoCheckCannotGC #include "js/Printer.h" // js::Fprinter #include "js/RootingAPI.h" // Rooted #include "js/Transcoding.h" // JS::TranscodeBuffer +#include "js/Utility.h" // js_malloc, js_calloc, js_free #include "js/Value.h" // ObjectValue #include "js/WasmModule.h" // JS::WasmModule #include "vm/BigIntType.h" // ParseBigIntLiteral, BigIntLiteralIsZero #include "vm/BindingKind.h" // BindingKind #include "vm/EnvironmentObject.h" #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind #include "vm/JSContext.h" // JSContext #include "vm/JSFunction.h" // JSFunction, GetFunctionPrototype, NewFunctionWithProto @@ -1682,32 +1685,140 @@ bool CompilationSyntaxParseCache::copyCl closedOverBindings[i] = parserAtom; } closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length); return true; } +template <typename T> +PreAllocateableGCArray<T>::~PreAllocateableGCArray() { + if (elems_) { + js_free(elems_); + elems_ = nullptr; + } +} + +template <typename T> +bool PreAllocateableGCArray<T>::allocate(size_t length) { + MOZ_ASSERT(empty()); + + length_ = length; + + if (isInline()) { + inlineElem_ = nullptr; + return true; + } + + elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_)); + if (!elems_) { + return false; + } + + return true; +} + +template <typename T> +bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) { + MOZ_ASSERT(empty()); + + length_ = length; + + if (isInline()) { + inlineElem_ = init; + return true; + } + + elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_)); + if (!elems_) { + return false; + } + + std::fill(elems_, elems_ + length_, init); + return true; +} + +template <typename T> +void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) { + MOZ_ASSERT(empty()); + + length_ = buffer.length_; + buffer.length_ = 0; + + if (isInline()) { + inlineElem_ = nullptr; + return; + } + + elems_ = reinterpret_cast<T*>(buffer.elems_); + buffer.elems_ = nullptr; + +#ifdef DEBUG + for (size_t i = 0; i < length_; i++) { + MOZ_ASSERT(elems_[i] == nullptr); + } +#endif +} + +template <typename T> +void PreAllocateableGCArray<T>::trace(JSTracer* trc) { + if (empty()) { + return; + } + + if (isInline()) { + TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_"); + return; + } + + for (size_t i = 0; i < length_; i++) { + TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_"); + } +} + +template <typename T> +PreAllocateableGCArray<T>::Preallocated::~Preallocated() { + if (elems_) { + js_free(elems_); + elems_ = nullptr; + } +} + +template <typename T> +bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) { + MOZ_ASSERT(empty()); + + length_ = length; + + if (isInline()) { + return true; + } + + elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_)); + if (!elems_) { + return false; + } + + return true; +} + +template struct js::frontend::PreAllocateableGCArray<JSFunction*>; +template struct js::frontend::PreAllocateableGCArray<js::Scope*>; + void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); } void CompilationGCOutput::trace(JSTracer* trc) { TraceNullableRoot(trc, &script, "compilation-gc-output-script"); TraceNullableRoot(trc, &module, "compilation-gc-output-module"); TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source"); functions.trace(trc); scopes.trace(trc); } -void JS::InstantiationStorage::trace(JSTracer* trc) { - if (gcOutput_) { - gcOutput_->trace(trc); - } -} - RegExpObject* RegExpStencil::createRegExp( JSContext* cx, const CompilationAtomCache& atomCache) const { Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_)); return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject); } RegExpObject* RegExpStencil::createRegExpAndEnsureAtom( JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms, @@ -2011,20 +2122,17 @@ static bool InstantiateModuleObject(JSCo // Instantiate JSFunctions for each FunctionBox. static bool InstantiateFunctions(JSContext* cx, FrontendContext* fc, CompilationAtomCache& atomCache, const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { using ImmutableFlags = ImmutableScriptFlagsEnum; - if (!gcOutput.functions.resize(stencil.scriptData.size())) { - ReportOutOfMemory(fc); - return false; - } + MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size()); // Most JSFunctions will be have the same Shape so we can compute it now to // allow fast object creation. Generators / Async will use the slow path // instead. Rooted<SharedShape*> functionShape( cx, GlobalObject::getFunctionShapeWithDefaultProto( cx, /* extended = */ false)); if (!functionShape) { @@ -2093,29 +2201,29 @@ static bool InstantiateScopes(JSContext* // While allocating Scope object from ScopeStencil, Scope object for the // enclosing Scope should already be allocated. // // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope* // pointer. // // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier // element in stencil.scopeData, because enclosing_ field holds - // index into it, and newly created ScopeStencil is pushed back to the vector. + // index into it, and newly created ScopeStencil is pushed back to the array. // // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope. MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size()); size_t scopeCount = stencil.scopeData.size(); for (size_t i = 0; i < scopeCount; i++) { Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput, stencil.scopeNames[i]); if (!scope) { return false; } - gcOutput.scopes.infallibleAppend(scope); + gcOutput.scopes[i] = scope; } return true; } // Instantiate js::BaseScripts from ScriptStencils for inner functions of the // compilation. Note that standalone functions and functions being delazified // are handled below with other top-levels. @@ -2371,25 +2479,27 @@ static void AssertDelazificationFieldsMa } #endif // DEBUG // When delazifying, use the existing JSFunctions. The initial and delazifying // parse are required to generate the same sequence of functions for lazy // parsing to work at all. static void FunctionsFromExistingLazy(CompilationInput& input, CompilationGCOutput& gcOutput) { - MOZ_ASSERT(gcOutput.functions.empty()); - gcOutput.functions.infallibleAppend(input.function()); + MOZ_ASSERT(!gcOutput.functions[0]); + + size_t instantiatedFunIndex = 0; + gcOutput.functions[instantiatedFunIndex++] = input.function(); for (JS::GCCellPtr elem : input.lazyOuterBaseScript()->gcthings()) { if (!elem.is<JSObject>()) { continue; } JSFunction* fun = &elem.as<JSObject>().as<JSFunction>(); - gcOutput.functions.infallibleAppend(fun); + gcOutput.functions[instantiatedFunIndex++] = fun; } } void CompilationStencil::borrowFromExtensibleCompilationStencil( ExtensibleCompilationStencil& extensibleStencil) { canLazilyParse = extensibleStencil.canLazilyParse; functionKey = extensibleStencil.functionKey; @@ -2597,17 +2707,18 @@ JSScript* CompilationStencil::instantiat // we do not want to instantiate those functions here since they are instead // created on demand from the stencil. Create a dummy function and populate // the functions array of the CompilationGCOutput with references to it. RootedFunction dummy( cx, NewNativeFunction(cx, SelfHostedDummyFunction, 0, nullptr)); if (!dummy) { return nullptr; } - if (!gcOutput.get().functions.appendN(dummy, scriptData.size())) { + + if (!gcOutput.get().functions.allocateWith(dummy, scriptData.size())) { ReportOutOfMemory(cx); return nullptr; } if (!InstantiateTopLevel(cx, input, *this, gcOutput.get())) { return nullptr; } @@ -2675,68 +2786,71 @@ bool CompilationStencil::delazifySelfHos auto gcthings = scriptData[scriptIndex].gcthings(*this); return gcthings[GCThingIndex::outermostScopeIndex()].toScope(); }; ScopeIndex scopeIndex = getOutermostScope(range.start); ScopeIndex scopeLimit = (range.limit < scriptData.size()) ? getOutermostScope(range.limit) : ScopeIndex(scopeData.size()); - // Prepare to instantiate by reserving the output vectors. We also set a base + // Prepare to instantiate by allocating the output arrays. We also set a base // index to avoid allocations in most cases. AutoReportFrontendContext fc(cx); Rooted<CompilationGCOutput> gcOutput(cx); - if (!gcOutput.get().ensureReservedWithBaseIndex(&fc, range.start, range.limit, - scopeIndex, scopeLimit)) { + if (!gcOutput.get().ensureAllocatedWithBaseIndex( + &fc, range.start, range.limit, scopeIndex, scopeLimit)) { return false; } // Phase 1: Instantiate JSAtoms. // NOTE: The self-hosted atoms are all "permanent" and the // CompilationAtomCache is already stored on the JSRuntime. // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions. // Get the corresponding ScriptSourceObject to use in current realm. gcOutput.get().sourceObject = SelfHostingScriptSourceObject(cx); if (!gcOutput.get().sourceObject) { return false; } + size_t instantiatedFunIndex = 0; + // Delazification target function. - gcOutput.get().functions.infallibleAppend(fun); + gcOutput.get().functions[instantiatedFunIndex++] = fun; // Allocate inner functions. Self-hosted functions do not allocate these with // the initial function. for (size_t i = range.start + 1; i < range.limit; i++) { JSFunction* innerFun = CreateFunction(cx, atomCache, *this, scriptData[i], scriptExtra[i], ScriptIndex(i)); if (!innerFun) { return false; } - gcOutput.get().functions.infallibleAppend(innerFun); + gcOutput.get().functions[instantiatedFunIndex++] = innerFun; } // Phase 3: Instantiate js::Scopes. // NOTE: When the enclosing scope is not a stencil, directly use the // `emptyGlobalScope` instead of reading from CompilationInput. This is // a special case for self-hosted delazification that allows us to reuse // the CompilationInput between different realms. + size_t instantiatedScopeIndex = 0; for (size_t i = scopeIndex; i < scopeLimit; i++) { ScopeStencil& data = scopeData[i]; Rooted<Scope*> enclosingScope( cx, data.hasEnclosing() ? gcOutput.get().getScope(data.enclosing()) : &cx->global()->emptyGlobalScope()); js::Scope* scope = data.createScope(cx, atomCache, enclosingScope, scopeNames[i]); if (!scope) { return false; } - gcOutput.get().scopes.infallibleAppend(scope); + gcOutput.get().scopes[instantiatedScopeIndex++] = scope; } // Phase 4: Instantiate (inner) BaseScripts. ScriptIndex innerStart(range.start + 1); for (size_t i = innerStart; i < range.limit; i++) { if (!JSScript::fromStencil(cx, atomCache, *this, gcOutput.get(), ScriptIndex(i))) { return false; @@ -2758,19 +2872,32 @@ bool CompilationStencil::delazifySelfHos return true; } /* static */ bool CompilationStencil::prepareForInstantiate( FrontendContext* fc, CompilationAtomCache& atomCache, const CompilationStencil& stencil, CompilationGCOutput& gcOutput) { - // Reserve the `gcOutput` vectors. - if (!gcOutput.ensureReserved(fc, stencil.scriptData.size(), - stencil.scopeData.size())) { + // Allocate the `gcOutput` arrays. + if (!gcOutput.ensureAllocated(fc, stencil.scriptData.size(), + stencil.scopeData.size())) { + return false; + } + + return atomCache.allocate(fc, stencil.parserAtomData.size()); +} + +/* static */ +bool CompilationStencil::prepareForInstantiate( + FrontendContext* fc, CompilationAtomCache& atomCache, + const CompilationStencil& stencil, + PreallocatedCompilationGCOutput& gcOutput) { + if (!gcOutput.allocate(fc, stencil.scriptData.size(), + stencil.scopeData.size())) { return false; } return atomCache.allocate(fc, stencil.parserAtomData.size()); } bool CompilationStencil::serializeStencils(JSContext* cx, CompilationInput& input, @@ -5342,44 +5469,48 @@ JS_PUBLIC_API JSScript* JS::InstantiateG JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil, JS::InstantiationStorage* storage) { MOZ_ASSERT_IF(storage, storage->isValid()); CompileOptions compileOptions(cx); options.copyTo(compileOptions); Rooted<CompilationInput> input(cx, CompilationInput(compileOptions)); Rooted<CompilationGCOutput> gcOutput(cx); - CompilationGCOutput& output = storage ? *storage->gcOutput_ : gcOutput.get(); + if (storage) { + gcOutput.get().steal(std::move(*storage->gcOutput_)); + } - if (!InstantiateStencils(cx, input.get(), *stencil, output)) { + if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { return nullptr; } - return output.script; + return gcOutput.get().script; } JS_PUBLIC_API bool JS::StencilIsBorrowed(Stencil* stencil) { return stencil->storageType == CompilationStencil::StorageType::Borrowed; } JS_PUBLIC_API JSObject* JS::InstantiateModuleStencil( JSContext* cx, const JS::InstantiateOptions& options, JS::Stencil* stencil, JS::InstantiationStorage* storage) { MOZ_ASSERT_IF(storage, storage->isValid()); CompileOptions compileOptions(cx); options.copyTo(compileOptions); compileOptions.setModule(); Rooted<CompilationInput> input(cx, CompilationInput(compileOptions)); Rooted<CompilationGCOutput> gcOutput(cx); - CompilationGCOutput& output = storage ? *storage->gcOutput_ : gcOutput.get(); + if (storage) { + gcOutput.get().steal(std::move(*storage->gcOutput_)); + } - if (!InstantiateStencils(cx, input.get(), *stencil, output)) { + if (!InstantiateStencils(cx, input.get(), *stencil, gcOutput.get())) { return nullptr; } - return output.module; + return gcOutput.get().module; } JS::TranscodeResult JS::EncodeStencil(JSContext* cx, JS::Stencil* stencil, TranscodeBuffer& buffer) { AutoReportFrontendContext fc(cx); XDRStencilEncoder encoder(&fc, buffer); XDRResult res = encoder.codeStencil(*stencil); if (res.isErr()) {
Files
/js/src/frontend/CompilationStencil.h
/js/src/frontend/Stencil.cpp
/js/src/vm/BytecodeUtil.cpp
/js/src/vm/BytecodeUtil.h
/js/src/vm/Opcodes.h
Changesets
Diffs
/js/src/frontend/CompilationStencil.h
/js/src/frontend/Stencil.cpp
/js/src/vm/BytecodeUtil.cpp
/js/src/vm/BytecodeUtil.h
/js/src/vm/Opcodes.h