Bug 1678449 - Part 5: Use span in CompilationStencil.parserAtomData. r=tcampbell
authorTooru Fujisawa <arai_a@mac.com>
Wed, 06 Jan 2021 06:49:58 +0000
changeset 562207 967faaa4f81cc63fdc0eab259fa3063c224c150e
parent 562206 a6686e0981733694192f19c501cf99d46bce93d7
child 562208 371c029d8743f9684895189a107f3857b44cfee7
push id38084
push usernbeleuzu@mozilla.com
push dateThu, 07 Jan 2021 16:36:34 +0000
treeherdermozilla-central@81e34ce5a390 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1678449
milestone86.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
Bug 1678449 - Part 5: Use span in CompilationStencil.parserAtomData. r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D100747
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/CompilationInfo.h
js/src/frontend/EmitterScope.cpp
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/ParserAtom.cpp
js/src/frontend/ParserAtom.h
js/src/frontend/Stencil.cpp
js/src/jsapi-tests/testParserAtom.cpp
js/src/vm/Scope.cpp
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3790,16 +3790,20 @@ static bool reflect_parse(JSContext* cx,
   Parser<FullParseHandler, char16_t> parser(
       cx, options, chars.begin().get(), chars.length(),
       /* foldConstants = */ false, compilationInfo.get(), compilationState,
       nullptr, nullptr);
   if (!parser.checkOptions()) {
     return false;
   }
 
+  if (!compilationState.finish(cx, compilationInfo.get())) {
+    return false;
+  }
+
   serialize.setParser(&parser);
 
   ParseNode* pn;
   if (target == ParseGoal::Script) {
     pn = parser.parse();
     if (!pn) {
       return false;
     }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7561,18 +7561,17 @@ bool BytecodeEmitter::isRestParameter(Pa
           bindings
               ->trailingNames[bindings->slotInfo.nonPositionalFormalStart - 1]
               .name();
       if (index.isNull()) {
         // Rest parameter name can be null when the rest destructuring syntax is
         // used: `function f(...[]) {}`.
         return false;
       }
-      const ParserAtom* paramName =
-          compilationInfo.stencil.getParserAtomAt(cx, index);
+      const ParserAtom* paramName = compilationState.getParserAtomAt(cx, index);
       return name == paramName;
     }
   }
 
   return false;
 }
 
 /* A version of emitCalleeAndThis for the optional cases:
--- a/js/src/frontend/CompilationInfo.h
+++ b/js/src/frontend/CompilationInfo.h
@@ -251,16 +251,19 @@ struct MOZ_RAII CompilationState {
   CompilationState(JSContext* cx, LifoAllocScope& frontendAllocScope,
                    const JS::ReadOnlyCompileOptions& options,
                    CompilationInfo& compilationInfo,
                    InheritThis inheritThis = InheritThis::No,
                    Scope* enclosingScope = nullptr,
                    JSObject* enclosingEnv = nullptr);
 
   bool finish(JSContext* cx, CompilationInfo& compilationInfo);
+
+  const ParserAtom* getParserAtomAt(JSContext* cx,
+                                    TaggedParserAtomIndex taggedIndex) const;
 };
 
 // Store shared data for non-lazy script.
 struct SharedDataContainer {
   using SingleSharedData = RefPtr<js::SharedImmutableScriptData>;
   using SharedDataVector =
       Vector<RefPtr<js::SharedImmutableScriptData>, 0, js::SystemAllocPolicy>;
   using SharedDataMap =
@@ -311,17 +314,17 @@ struct CompilationStencil {
   // AsmJS modules generated by parsing.
   HashMap<ScriptIndex, RefPtr<const JS::WasmModule>,
           mozilla::DefaultHasher<ScriptIndex>, js::SystemAllocPolicy>
       asmJS;
 
   // List of parser atoms for this compilation.
   // This may contain nullptr entries when round-tripping with XDR if the atom
   // was generated in original parse but not used by stencil.
-  ParserAtomVector parserAtomData;
+  ParserAtomSpan parserAtomData;
 
   CompilationStencil() = default;
 
   // We need a move-constructor to work with Rooted.
   CompilationStencil(CompilationStencil&& other) = default;
 
   const ParserAtom* getParserAtomAt(JSContext* cx,
                                     TaggedParserAtomIndex taggedIndex) const;
--- a/js/src/frontend/EmitterScope.cpp
+++ b/js/src/frontend/EmitterScope.cpp
@@ -511,25 +511,23 @@ bool EmitterScope::enterLexical(Bytecode
   ParserBindingIter bi(*bindings, firstFrameSlot, /* isNamedLambda = */ false);
   for (; bi; bi++) {
     if (!checkSlotLimits(bce, bi)) {
       return false;
     }
 
     NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
     if (!putNameInCache(
-            bce,
-            bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
+            bce, bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
             loc)) {
       return false;
     }
 
     if (!tdzCache->noteTDZCheck(
-            bce,
-            bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
+            bce, bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
             CheckTDZ)) {
       return false;
     }
   }
 
   updateFrameFixedSlots(bce, bi);
 
   auto createScope = [kind, bindings, firstFrameSlot, bce](
@@ -577,19 +575,19 @@ bool EmitterScope::enterNamedLambda(Byte
 
   ParserBindingIter bi(*funbox->namedLambdaBindings(), LOCALNO_LIMIT,
                        /* isNamedLambda = */ true);
   MOZ_ASSERT(bi.kind() == BindingKind::NamedLambdaCallee);
 
   // The lambda name, if not closed over, is accessed via JSOp::Callee and
   // not a frame slot. Do not update frame slot information.
   NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
-  if (!putNameInCache(
-          bce, bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
-          loc)) {
+  if (!putNameInCache(bce,
+                      bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
+                      loc)) {
     return false;
   }
 
   bi++;
   MOZ_ASSERT(!bi, "There should be exactly one binding in a NamedLambda scope");
 
   ScopeKind scopeKind =
       funbox->strict() ? ScopeKind::StrictNamedLambda : ScopeKind::NamedLambda;
@@ -628,33 +626,32 @@ bool EmitterScope::enterFunction(Bytecod
     ParserBindingIter bi(*bindings, funbox->hasParameterExprs);
     for (; bi; bi++) {
       if (!checkSlotLimits(bce, bi)) {
         return false;
       }
 
       NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
       NameLocationMap::AddPtr p = cache.lookupForAdd(
-          bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()));
+          bce->compilationState.getParserAtomAt(bce->cx, bi.name()));
 
       // The only duplicate bindings that occur are simple formal
       // parameters, in which case the last position counts, so update the
       // location.
       if (p) {
         MOZ_ASSERT(bi.kind() == BindingKind::FormalParameter);
         MOZ_ASSERT(!funbox->hasDestructuringArgs);
         MOZ_ASSERT(!funbox->hasRest());
         p->value() = loc;
         continue;
       }
 
-      if (!cache.add(
-              p,
-              bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
-              loc)) {
+      if (!cache.add(p,
+                     bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
+                     loc)) {
         ReportOutOfMemory(bce->cx);
         return false;
       }
     }
 
     updateFrameFixedSlots(bce, bi);
   } else {
     nextFrameSlot_ = 0;
@@ -726,18 +723,17 @@ bool EmitterScope::enterFunctionExtraBod
     for (; bi; bi++) {
       if (!checkSlotLimits(bce, bi)) {
         return false;
       }
 
       NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
       MOZ_ASSERT(bi.kind() == BindingKind::Var);
       if (!putNameInCache(
-              bce,
-              bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
+              bce, bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
               loc)) {
         return false;
       }
     }
 
     uint32_t priorEnd = bce->maxFixedSlots;
     updateFrameFixedSlots(bce, bi);
 
@@ -838,17 +834,17 @@ bool EmitterScope::enterGlobal(BytecodeE
   // Resolve binding names.
   //
   // NOTE: BytecodeEmitter::emitDeclarationInstantiation will emit the
   //       redeclaration check and initialize these bindings.
   if (globalsc->bindings) {
     for (ParserBindingIter bi(*globalsc->bindings); bi; bi++) {
       NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
       const ParserAtom* name =
-          bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name());
+          bce->compilationState.getParserAtomAt(bce->cx, bi.name());
       if (!putNameInCache(bce, name, loc)) {
         return false;
       }
     }
   }
 
   // Note that to save space, we don't add free names to the cache for
   // global scopes. They are assumed to be global vars in the syntactic
@@ -926,32 +922,29 @@ bool EmitterScope::enterModule(BytecodeE
     ParserBindingIter bi(*bindings);
     for (; bi; bi++) {
       if (!checkSlotLimits(bce, bi)) {
         return false;
       }
 
       NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
       if (!putNameInCache(
-              bce,
-              bce->compilationInfo.stencil.getParserAtomAt(bce->cx, bi.name()),
+              bce, bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
               loc)) {
         return false;
       }
 
       if (BindingKindIsLexical(bi.kind())) {
         if (loc.kind() == NameLocation::Kind::FrameSlot &&
             !firstLexicalFrameSlot) {
           firstLexicalFrameSlot = Some(loc.frameSlot());
         }
 
         if (!tdzCache->noteTDZCheck(
-                bce,
-                bce->compilationInfo.stencil.getParserAtomAt(bce->cx,
-                                                             bi.name()),
+                bce, bce->compilationState.getParserAtomAt(bce->cx, bi.name()),
                 CheckTDZ)) {
           return false;
         }
       }
     }
 
     updateFrameFixedSlots(bce, bi);
   } else {
--- a/js/src/frontend/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -476,17 +476,17 @@ bool FunctionScriptEmitter::emitExtraBod
   // parameters which have been redeclared as vars. For example, in the
   // following, the var y in the body scope is 42:
   //
   //   function f(x, y = 42) { var y; }
   //
   const ParserAtom* name = nullptr;
   for (ParserBindingIter bi(*funbox_->functionScopeBindings(), true); bi;
        bi++) {
-    name = bce_->compilationInfo.stencil.getParserAtomAt(bce_->cx, bi.name());
+    name = bce_->compilationState.getParserAtomAt(bce_->cx, bi.name());
 
     // There may not be a var binding of the same name.
     if (!bce_->locationOfNameBoundInScope(name,
                                           extraBodyVarEmitterScope_.ptr())) {
       continue;
     }
 
     // The '.this' and '.generator' function special
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1770,17 +1770,17 @@ ModuleNode* Parser<FullParseHandler, Uni
     return null();
   }
 
   // Check exported local bindings exist and mark them as closed over.
   StencilModuleMetadata& moduleMetadata =
       *this->compilationInfo_.stencil.moduleMetadata;
   for (auto entry : moduleMetadata.localExportEntries) {
     const ParserAtom* nameId =
-        this->compilationInfo_.stencil.getParserAtomAt(cx_, entry.localName);
+        this->compilationState_.getParserAtomAt(cx_, entry.localName);
     MOZ_ASSERT(nameId);
 
     DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(nameId);
     if (!p) {
       UniqueChars str = ParserAtomToPrintableString(cx_, nameId);
       if (!str) {
         return null();
       }
--- a/js/src/frontend/ParserAtom.cpp
+++ b/js/src/frontend/ParserAtom.cpp
@@ -1,16 +1,17 @@
 /* -*- 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/ParserAtom.h"
 
+#include <memory>  // std::uninitialized_fill_n
 #include <type_traits>
 
 #include "jsnum.h"
 
 #include "frontend/CompilationInfo.h"
 #include "frontend/NameCollections.h"
 #include "vm/JSContext.h"
 #include "vm/Printer.h"
@@ -323,21 +324,18 @@ void ParserAtomEntry::dumpCharsNoQuote(j
   if (hasLatin1Chars()) {
     JSString::dumpCharsNoQuote<Latin1Char>(latin1Chars(), length(), out);
   } else {
     JSString::dumpCharsNoQuote<char16_t>(twoByteChars(), length(), out);
   }
 }
 #endif
 
-ParserAtomsTable::ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc,
-                                   ParserAtomVector& entries)
-    : wellKnownTable_(*rt->commonParserNames),
-      alloc_(alloc),
-      entries_(entries) {}
+ParserAtomsTable::ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc)
+    : wellKnownTable_(*rt->commonParserNames), alloc_(alloc) {}
 
 const ParserAtom* ParserAtomsTable::addEntry(JSContext* cx,
                                              EntrySet::AddPtr& addPtr,
                                              ParserAtomEntry* entry) {
   MOZ_ASSERT(!addPtr);
   ParserAtomIndex index = ParserAtomIndex(entries_.length());
   if (size_t(index) >= TaggedParserAtomIndex::IndexLimit) {
     ReportAllocationOverflow(cx);
@@ -398,29 +396,35 @@ const ParserAtom* ParserAtomsTable::inte
   auto addPtr = entrySet_.lookupForAdd(lookup);
   if (addPtr) {
     return (*addPtr)->asAtom();
   }
 
   return internChar16Seq<Latin1Char>(cx, addPtr, lookup.hash(), seq, length);
 }
 
-ParserAtomVectorBuilder::ParserAtomVectorBuilder(JSRuntime* rt,
-                                                 ParserAtomVector& entries)
+ParserAtomSpanBuilder::ParserAtomSpanBuilder(JSRuntime* rt,
+                                             ParserAtomSpan& entries)
     : wellKnownTable_(*rt->commonParserNames), entries_(entries) {}
 
-bool ParserAtomVectorBuilder::resize(JSContext* cx, size_t count) {
+bool ParserAtomSpanBuilder::allocate(JSContext* cx, LifoAlloc& alloc,
+                                     size_t count) {
   if (count >= TaggedParserAtomIndex::IndexLimit) {
     ReportAllocationOverflow(cx);
     return false;
   }
-  if (!entries_.resize(count)) {
-    ReportOutOfMemory(cx);
+
+  auto* p = alloc.newArrayUninitialized<ParserAtomEntry*>(count);
+  if (!p) {
+    js::ReportOutOfMemory(cx);
     return false;
   }
+  std::uninitialized_fill_n(p, count, nullptr);
+
+  entries_ = mozilla::Span(p, count);
   return true;
 }
 
 const ParserAtom* ParserAtomsTable::internUtf8(JSContext* cx,
                                                const mozilla::Utf8Unit* utf8Ptr,
                                                uint32_t nbyte) {
   // Check for tiny strings which are abundant in minified code.
   // NOTE: The tiny atoms are all ASCII-only so we can directly look at the
@@ -616,32 +620,32 @@ const ParserAtom* WellKnownParserAtoms::
   return WellKnownParserAtoms::rom_.length1Table[size_t(s)].asAtom();
 }
 
 /* static */
 const ParserAtom* WellKnownParserAtoms::getStatic2(StaticParserString2 s) {
   return WellKnownParserAtoms::rom_.length2Table[size_t(s)].asAtom();
 }
 
-const ParserAtom* ParserAtomVectorBuilder::getWellKnown(
+const ParserAtom* ParserAtomSpanBuilder::getWellKnown(
     WellKnownAtomId atomId) const {
   return wellKnownTable_.getWellKnown(atomId);
 }
 
-const ParserAtom* ParserAtomVectorBuilder::getStatic1(
+const ParserAtom* ParserAtomSpanBuilder::getStatic1(
     StaticParserString1 s) const {
   return WellKnownParserAtoms::getStatic1(s);
 }
 
-const ParserAtom* ParserAtomVectorBuilder::getStatic2(
+const ParserAtom* ParserAtomSpanBuilder::getStatic2(
     StaticParserString2 s) const {
   return WellKnownParserAtoms::getStatic2(s);
 }
 
-const ParserAtom* ParserAtomVectorBuilder::getParserAtom(
+const ParserAtom* ParserAtomSpanBuilder::getParserAtom(
     ParserAtomIndex index) const {
   return entries_[index]->asAtom();
 }
 
 template <class T>
 const ParserAtom* GetParserAtom(T self, TaggedParserAtomIndex index) {
   if (index.isParserAtomIndex()) {
     return self->getParserAtom(index.toParserAtomIndex());
@@ -658,17 +662,17 @@ const ParserAtom* GetParserAtom(T self, 
   if (index.isStaticParserString2()) {
     return self->getStatic2(index.toStaticParserString2());
   }
 
   MOZ_ASSERT(index.isNull());
   return nullptr;
 }
 
-const ParserAtom* ParserAtomVectorBuilder::getParserAtom(
+const ParserAtom* ParserAtomSpanBuilder::getParserAtom(
     TaggedParserAtomIndex index) const {
   return GetParserAtom(this, index);
 }
 
 const ParserAtom* ParserAtomsTable::getWellKnown(WellKnownAtomId atomId) const {
   return wellKnownTable_.getWellKnown(atomId);
 }
 
@@ -684,17 +688,17 @@ const ParserAtom* ParserAtomsTable::getP
   return entries_[index]->asAtom();
 }
 
 const ParserAtom* ParserAtomsTable::getParserAtom(
     TaggedParserAtomIndex index) const {
   return GetParserAtom(this, index);
 }
 
-bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomVector& entries,
+bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomSpan& entries,
                             CompilationAtomCache& atomCache) {
   for (const auto& entry : entries) {
     if (!entry) {
       continue;
     }
     if (entry->isUsedByStencil() && entry->isParserAtomIndex() &&
         !atomCache.hasAtomAt(entry->toParserAtomIndex())) {
       if (!entry->instantiate(cx, atomCache)) {
--- a/js/src/frontend/ParserAtom.h
+++ b/js/src/frontend/ParserAtom.h
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef frontend_ParserAtom_h
 #define frontend_ParserAtom_h
 
 #include "mozilla/DebugOnly.h"      // mozilla::DebugOnly
 #include "mozilla/HashFunctions.h"  // HashString
 #include "mozilla/Range.h"          // mozilla::Range
+#include "mozilla/Span.h"           // mozilla::Span
+#include "mozilla/Variant.h"        // mozilla::Variant
 
 #include "ds/LifoAlloc.h"         // LifoAlloc
 #include "frontend/TypedIndex.h"  // TypedIndex
 #include "js/HashTable.h"         // HashSet
 #include "js/UniquePtr.h"         // js::UniquePtr
 #include "js/Vector.h"            // Vector
 #include "vm/CommonPropertyNames.h"
 #include "vm/StringType.h"  // CompareChars, StringEqualsAscii
@@ -599,16 +601,17 @@ class WellKnownParserAtoms_ROM {
     }
 
     // No match on tiny Atoms
     return nullptr;
   }
 };
 
 using ParserAtomVector = Vector<ParserAtomEntry*, 0, js::SystemAllocPolicy>;
+using ParserAtomSpan = mozilla::Span<ParserAtomEntry*>;
 
 /**
  * WellKnownParserAtoms reserves a set of common ParserAtoms on the JSRuntime
  * in a read-only format to be used by parser. These reserved atoms can be
  * translated to equivalent JSAtoms in constant time.
  *
  * The common-names set allows the parser to lookup up specific atoms in
  * constant time.
@@ -660,37 +663,37 @@ class WellKnownParserAtoms {
     return rom_.lookupTiny(chars, length);
   }
 
   const ParserAtom* getWellKnown(WellKnownAtomId atomId) const;
   static const ParserAtom* getStatic1(StaticParserString1 s);
   static const ParserAtom* getStatic2(StaticParserString2 s);
 };
 
-bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomVector& entries,
+bool InstantiateMarkedAtoms(JSContext* cx, const ParserAtomSpan& entries,
                             CompilationAtomCache& atomCache);
 
 /**
  * A ParserAtomsTable owns and manages the vector of ParserAtom entries
  * associated with a given compile session.
  */
 class ParserAtomsTable {
  private:
   const WellKnownParserAtoms& wellKnownTable_;
 
   LifoAlloc& alloc_;
 
   // The ParserAtomEntry are owned by the LifoAlloc.
   using EntrySet = HashSet<const ParserAtomEntry*, ParserAtomLookupHasher,
                            js::SystemAllocPolicy>;
   EntrySet entrySet_;
-  ParserAtomVector& entries_;
+  ParserAtomVector entries_;
 
  public:
-  ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc, ParserAtomVector& entries);
+  ParserAtomsTable(JSRuntime* rt, LifoAlloc& alloc);
   ParserAtomsTable(ParserAtomsTable&&) = default;
 
  private:
   // Internal APIs for interning to the table after well-known atoms cases have
   // been tested.
   const ParserAtom* addEntry(JSContext* cx, EntrySet::AddPtr& addPtr,
                              ParserAtomEntry* entry);
   template <typename AtomCharT, typename SeqCharT>
@@ -719,31 +722,33 @@ class ParserAtomsTable {
   const ParserAtom* concatAtoms(JSContext* cx,
                                 mozilla::Range<const ParserAtom*> atoms);
 
   const ParserAtom* getWellKnown(WellKnownAtomId atomId) const;
   const ParserAtom* getStatic1(StaticParserString1 s) const;
   const ParserAtom* getStatic2(StaticParserString2 s) const;
   const ParserAtom* getParserAtom(ParserAtomIndex index) const;
   const ParserAtom* getParserAtom(TaggedParserAtomIndex index) const;
+
+  const ParserAtomVector& entries() const { return entries_; }
 };
 
 // Lightweight version of ParserAtomsTable.
 // This doesn't support deduplication.
 // Used while decoding XDR.
-class ParserAtomVectorBuilder {
+class ParserAtomSpanBuilder {
  private:
   const WellKnownParserAtoms& wellKnownTable_;
-  ParserAtomVector& entries_;
+  ParserAtomSpan& entries_;
 
  public:
-  ParserAtomVectorBuilder(JSRuntime* rt, ParserAtomVector& entries);
+  ParserAtomSpanBuilder(JSRuntime* rt, ParserAtomSpan& entries);
 
-  bool resize(JSContext* cx, size_t count);
-  size_t length() const { return entries_.length(); }
+  bool allocate(JSContext* cx, LifoAlloc& alloc, size_t count);
+  size_t size() const { return entries_.size(); }
 
   void set(ParserAtomIndex index, const ParserAtomEntry* atom) {
     entries_[index] = const_cast<ParserAtomEntry*>(atom);
   }
 
  public:
   const ParserAtom* getWellKnown(WellKnownAtomId atomId) const;
   const ParserAtom* getStatic1(StaticParserString1 s) const;
--- a/js/src/frontend/Stencil.cpp
+++ b/js/src/frontend/Stencil.cpp
@@ -768,17 +768,17 @@ bool CompilationInfoVector::instantiateS
   }
 
   return true;
 }
 
 /* static */
 bool CompilationInfo::prepareInputAndStencilForInstantiate(
     JSContext* cx, CompilationInput& input, CompilationStencil& stencil) {
-  if (!input.atomCache.allocate(cx, stencil.parserAtomData.length())) {
+  if (!input.atomCache.allocate(cx, stencil.parserAtomData.size())) {
     return false;
   }
 
   return true;
 }
 
 /* static */
 bool CompilationInfo::prepareGCOutputForInstantiate(
@@ -819,18 +819,18 @@ bool CompilationInfoVector::prepareForIn
   if (!CompilationInfo::prepareForInstantiate(cx, initial, gcOutput)) {
     return false;
   }
 
   size_t maxScriptDataLength = 0;
   size_t maxScopeDataLength = 0;
   size_t maxParserAtomDataLength = 0;
   for (auto& delazification : delazifications) {
-    if (maxParserAtomDataLength < delazification.parserAtomData.length()) {
-      maxParserAtomDataLength = delazification.parserAtomData.length();
+    if (maxParserAtomDataLength < delazification.parserAtomData.size()) {
+      maxParserAtomDataLength = delazification.parserAtomData.size();
     }
     if (maxScriptDataLength < delazification.scriptData.size()) {
       maxScriptDataLength = delazification.scriptData.size();
     }
     if (maxScopeDataLength < delazification.scopeData.size()) {
       maxScopeDataLength = delazification.scopeData.size();
     }
   }
@@ -918,18 +918,17 @@ CompilationState::CompilationState(
     InheritThis inheritThis /* = InheritThis::No */,
     Scope* enclosingScope /* = nullptr */,
     JSObject* enclosingEnv /* = nullptr */)
     : directives(options.forceStrictMode()),
       scopeContext(cx, inheritThis, enclosingScope, enclosingEnv),
       usedNames(cx),
       allocScope(frontendAllocScope),
       input(compilationInfo.input),
-      parserAtoms(cx->runtime(), compilationInfo.alloc,
-                  compilationInfo.stencil.parserAtomData) {}
+      parserAtoms(cx->runtime(), compilationInfo.alloc) {}
 
 bool SharedDataContainer::prepareStorageFor(JSContext* cx,
                                             size_t nonLazyScriptCount,
                                             size_t allScriptCount) {
   if (nonLazyScriptCount <= 1) {
     MOZ_ASSERT(storage.is<SingleSharedData>());
     return true;
   }
@@ -1051,16 +1050,22 @@ bool CompilationState::finish(JSContext*
     return false;
   }
 
   if (!CopyVectorToSpan(cx, compilationInfo.alloc,
                         compilationInfo.stencil.scopeData, scopeData)) {
     return false;
   }
 
+  if (!CopyVectorToSpan(cx, compilationInfo.alloc,
+                        compilationInfo.stencil.parserAtomData,
+                        parserAtoms.entries())) {
+    return false;
+  }
+
   return true;
 }
 
 #if defined(DEBUG) || defined(JS_JITSPEW)
 
 void frontend::DumpTaggedParserAtomIndex(
     js::JSONPrinter& json, TaggedParserAtomIndex taggedIndex,
     CompilationStencil* compilationStencil) {
@@ -1912,30 +1917,48 @@ void CompilationAtomCache::stealBuffer(A
   // Destroy elements, without unreserving.
   atoms_.clear();
 }
 
 void CompilationAtomCache::returnBuffer(AtomCacheVector& atoms) {
   atoms = std::move(atoms_);
 }
 
-const ParserAtom* CompilationStencil::getParserAtomAt(
-    JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
-  if (taggedIndex.isParserAtomIndex()) {
-    auto index = taggedIndex.toParserAtomIndex();
-    MOZ_ASSERT(index < parserAtomData.length());
-    return parserAtomData[index]->asAtom();
-  }
+const ParserAtom* GetWellKnownParserAtomAt(JSContext* cx,
+                                           TaggedParserAtomIndex taggedIndex) {
+  MOZ_ASSERT(!taggedIndex.isParserAtomIndex());
 
   if (taggedIndex.isWellKnownAtomId()) {
     auto index = taggedIndex.toWellKnownAtomId();
     return cx->runtime()->commonParserNames->getWellKnown(index);
   }
 
   if (taggedIndex.isStaticParserString1()) {
     auto index = taggedIndex.toStaticParserString1();
     return WellKnownParserAtoms::getStatic1(index);
   }
 
   MOZ_ASSERT(taggedIndex.isStaticParserString2());
   auto index = taggedIndex.toStaticParserString2();
   return WellKnownParserAtoms::getStatic2(index);
 }
+
+const ParserAtom* CompilationState::getParserAtomAt(
+    JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
+  if (taggedIndex.isParserAtomIndex()) {
+    auto index = taggedIndex.toParserAtomIndex();
+    MOZ_ASSERT(index < parserAtoms.entries().length());
+    return parserAtoms.entries()[index]->asAtom();
+  }
+
+  return GetWellKnownParserAtomAt(cx, taggedIndex);
+}
+
+const ParserAtom* CompilationStencil::getParserAtomAt(
+    JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
+  if (taggedIndex.isParserAtomIndex()) {
+    auto index = taggedIndex.toParserAtomIndex();
+    MOZ_ASSERT(index < parserAtomData.size());
+    return parserAtomData[index]->asAtom();
+  }
+
+  return GetWellKnownParserAtomAt(cx, taggedIndex);
+}
--- a/js/src/jsapi-tests/testParserAtom.cpp
+++ b/js/src/jsapi-tests/testParserAtom.cpp
@@ -15,18 +15,17 @@
 
 // Test empty strings behave consistently.
 BEGIN_TEST(testParserAtom_empty) {
   using js::frontend::ParserAtom;
   using js::frontend::ParserAtomsTable;
   using js::frontend::ParserAtomVector;
 
   js::LifoAlloc alloc(512);
-  ParserAtomVector atoms;
-  ParserAtomsTable atomTable(cx->runtime(), alloc, atoms);
+  ParserAtomsTable atomTable(cx->runtime(), alloc);
 
   const char ascii[] = {};
   const JS::Latin1Char latin1[] = {};
   const mozilla::Utf8Unit utf8[] = {};
   const char16_t char16[] = {};
 
   const uint8_t bytes[] = {};
   const js::LittleEndianChars leTwoByte(bytes);
@@ -53,18 +52,17 @@ END_TEST(testParserAtom_empty)
 
 // Test length-1 fast-path is consistent across entry points.
 BEGIN_TEST(testParserAtom_tiny1) {
   using js::frontend::ParserAtom;
   using js::frontend::ParserAtomsTable;
   using js::frontend::ParserAtomVector;
 
   js::LifoAlloc alloc(512);
-  ParserAtomVector atoms;
-  ParserAtomsTable atomTable(cx->runtime(), alloc, atoms);
+  ParserAtomsTable atomTable(cx->runtime(), alloc);
 
   char16_t a = 'a';
   const char ascii[] = {'a'};
   JS::Latin1Char latin1[] = {'a'};
   const mozilla::Utf8Unit utf8[] = {mozilla::Utf8Unit('a')};
   char16_t char16[] = {'a'};
 
   const uint8_t bytes[] = {'a', 0};
@@ -95,18 +93,17 @@ END_TEST(testParserAtom_tiny1)
 
 // Test length-2 fast-path is consistent across entry points.
 BEGIN_TEST(testParserAtom_tiny2) {
   using js::frontend::ParserAtom;
   using js::frontend::ParserAtomsTable;
   using js::frontend::ParserAtomVector;
 
   js::LifoAlloc alloc(512);
-  ParserAtomVector atoms;
-  ParserAtomsTable atomTable(cx->runtime(), alloc, atoms);
+  ParserAtomsTable atomTable(cx->runtime(), alloc);
 
   const char ascii[] = {'a', '0'};
   JS::Latin1Char latin1[] = {'a', '0'};
   const mozilla::Utf8Unit utf8[] = {mozilla::Utf8Unit('a'),
                                     mozilla::Utf8Unit('0')};
   char16_t char16[] = {'a', '0'};
 
   const uint8_t bytes[] = {'a', 0, '0', 0};
@@ -136,18 +133,17 @@ BEGIN_TEST(testParserAtom_tiny2) {
 END_TEST(testParserAtom_tiny2)
 
 BEGIN_TEST(testParserAtom_concat) {
   using js::frontend::ParserAtom;
   using js::frontend::ParserAtomsTable;
   using js::frontend::ParserAtomVector;
 
   js::LifoAlloc alloc(512);
-  ParserAtomVector atoms;
-  ParserAtomsTable atomTable(cx->runtime(), alloc, atoms);
+  ParserAtomsTable atomTable(cx->runtime(), alloc);
 
   auto CheckConcat = [&](const char16_t* exp,
                          std::initializer_list<const char16_t*> args) -> bool {
     // Intern each argument literal
     std::vector<const ParserAtom*> inputs;
     for (const char16_t* arg : args) {
       size_t len = std::char_traits<char16_t>::length(arg);
       const ParserAtom* atom = atomTable.internChar16(cx, arg, len);
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -211,25 +211,25 @@ static UniquePtr<typename ConcreteScope:
                             dataCopy->trailingNames.start());
 
   return UniquePtr<Data>(dataCopy);
 }
 
 template <typename ConcreteScope>
 static void MarkParserScopeData(JSContext* cx,
                                 typename ConcreteScope::ParserData* data,
-                                frontend::CompilationStencil& stencil) {
+                                frontend::CompilationState& compilationState) {
   auto* names = data->trailingNames.start();
   uint32_t length = data->slotInfo.length;
   for (size_t i = 0; i < length; i++) {
     auto index = names[i].name();
     if (!index) {
       continue;
     }
-    stencil.getParserAtomAt(cx, index)->markUsedByStencil();
+    compilationState.getParserAtomAt(cx, index)->markUsedByStencil();
   }
 }
 
 static bool SetEnvironmentShape(JSContext* cx, BindingIter& freshBi,
                                 BindingIter& bi, const JSClass* cls,
                                 uint32_t firstFrameSlot,
                                 uint32_t baseShapeFlags,
                                 MutableHandleShape envShape) {
@@ -1972,19 +1972,18 @@ JS::ubi::Node::Size JS::ubi::Concrete<Sc
 
 /* static */
 bool ScopeStencil::createForFunctionScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState,
     FunctionScope::ParserData* data, bool hasParameterExprs,
     bool needsEnvironment, ScriptIndex functionIndex, bool isArrow,
     mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<FunctionScope>(cx, data, stencil);
+    MarkParserScopeData<FunctionScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<FunctionScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   // We do not initialize the canonical function while the data is owned by the
@@ -2013,19 +2012,18 @@ bool ScopeStencil::createForFunctionScop
 }
 
 /* static */
 bool ScopeStencil::createForLexicalScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState, ScopeKind kind,
     LexicalScope::ParserData* data, uint32_t firstFrameSlot,
     mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<LexicalScope>(cx, data, stencil);
+    MarkParserScopeData<LexicalScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<LexicalScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   mozilla::Maybe<uint32_t> envShape;
@@ -2047,19 +2045,18 @@ bool ScopeStencil::createForLexicalScope
   return true;
 }
 
 bool ScopeStencil::createForVarScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState, ScopeKind kind,
     VarScope::ParserData* data, uint32_t firstFrameSlot, bool needsEnvironment,
     mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<VarScope>(cx, data, stencil);
+    MarkParserScopeData<VarScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<VarScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   mozilla::Maybe<uint32_t> envShape;
@@ -2081,19 +2078,18 @@ bool ScopeStencil::createForVarScope(
   return true;
 }
 
 /* static */
 bool ScopeStencil::createForGlobalScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState, ScopeKind kind,
     GlobalScope::ParserData* data, ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<GlobalScope>(cx, data, stencil);
+    MarkParserScopeData<GlobalScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<GlobalScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   // The global scope has no environment shape. Its environment is the
@@ -2119,19 +2115,18 @@ bool ScopeStencil::createForGlobalScope(
 }
 
 /* static */
 bool ScopeStencil::createForEvalScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState, ScopeKind kind,
     EvalScope::ParserData* data, mozilla::Maybe<ScopeIndex> enclosing,
     ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<EvalScope>(cx, data, stencil);
+    MarkParserScopeData<EvalScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<EvalScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   uint32_t firstFrameSlot = 0;
@@ -2154,19 +2149,18 @@ bool ScopeStencil::createForEvalScope(
   return true;
 }
 
 /* static */
 bool ScopeStencil::createForModuleScope(
     JSContext* cx, frontend::CompilationInfo& compilationInfo,
     frontend::CompilationState& compilationState, ModuleScope::ParserData* data,
     mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
-  frontend::CompilationStencil& stencil = compilationInfo.stencil;
   if (data) {
-    MarkParserScopeData<ModuleScope>(cx, data, stencil);
+    MarkParserScopeData<ModuleScope>(cx, data, compilationState);
   } else {
     data = NewEmptyParserScopeData<ModuleScope>(cx, compilationInfo.alloc);
     if (!data) {
       return false;
     }
   }
 
   MOZ_ASSERT(enclosing.isNothing());
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -296,17 +296,17 @@ static XDRResult AtomTable(XDRState<mode
 
   return Ok();
 }
 
 template <XDRMode mode>
 static XDRResult ParserAtomTable(XDRState<mode>* xdr,
                                  frontend::CompilationStencil& stencil) {
   if (mode == XDR_ENCODE) {
-    uint32_t atomVectorLength = stencil.parserAtomData.length();
+    uint32_t atomVectorLength = stencil.parserAtomData.size();
     MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
 
     uint32_t atomCount = 0;
     for (const auto& entry : stencil.parserAtomData) {
       if (!entry) {
         continue;
       }
       if (entry->isUsedByStencil()) {
@@ -327,17 +327,18 @@ static XDRResult ParserAtomTable(XDRStat
     }
 
     return Ok();
   }
 
   uint32_t atomVectorLength;
   MOZ_TRY(XDRAtomCount(xdr, &atomVectorLength));
 
-  if (!xdr->frontendAtoms().resize(xdr->cx(), atomVectorLength)) {
+  if (!xdr->frontendAtoms().allocate(xdr->cx(), xdr->stencilAlloc(),
+                                     atomVectorLength)) {
     return xdr->fail(JS::TranscodeResult_Throw);
   }
 
   uint32_t atomCount;
   MOZ_TRY(XDRAtomCount(xdr, &atomCount));
   MOZ_ASSERT(!xdr->hasAtomTable());
 
   for (uint32_t i = 0; i < atomCount; i++) {
@@ -732,17 +733,17 @@ XDRResult XDRIncrementalStencilEncoder::
 void XDRDecoder::trace(JSTracer* trc) { atomTable_.trace(trc); }
 
 void XDRIncrementalEncoder::trace(JSTracer* trc) { atomMap_.trace(trc); }
 
 XDRResult XDRStencilDecoder::codeStencils(
     frontend::CompilationInfoVector& compilationInfos) {
   MOZ_ASSERT(compilationInfos.delazifications.length() == 0);
 
-  frontend::ParserAtomVectorBuilder parserAtomBuilder(
+  frontend::ParserAtomSpanBuilder parserAtomBuilder(
       cx()->runtime(), compilationInfos.initial.stencil.parserAtomData);
   parserAtomBuilder_ = &parserAtomBuilder;
   stencilAlloc_ = &compilationInfos.initial.alloc;
 
   MOZ_TRY(codeStencil(compilationInfos.initial));
 
   if (!compilationInfos.delazifications.reserve(nchunks_ - 1)) {
     ReportOutOfMemory(cx());
@@ -753,17 +754,17 @@ XDRResult XDRStencilDecoder::codeStencil
   stencilAlloc_ = &compilationInfos.allocForDelazifications;
 
   for (size_t i = 1; i < nchunks_; i++) {
     compilationInfos.delazifications.infallibleEmplaceBack();
     auto& delazification = compilationInfos.delazifications[i - 1];
 
     hasFinishedAtomTable_ = false;
 
-    frontend::ParserAtomVectorBuilder parserAtomBuilder(
+    frontend::ParserAtomSpanBuilder parserAtomBuilder(
         cx()->runtime(), delazification.parserAtomData);
     parserAtomBuilder_ = &parserAtomBuilder;
 
     MOZ_TRY(codeFunctionStencil(delazification));
   }
 
   return Ok();
 }
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -279,17 +279,17 @@ class XDRState : public XDRCoderBase {
   virtual XDRAtomMap& atomMap() { MOZ_CRASH("does not have atomMap"); }
   virtual uint32_t& natoms() { MOZ_CRASH("does not have atomMap."); }
 
   // The number of chunks (CompilationStencils) in the buffer.
   virtual uint32_t& nchunks() { MOZ_CRASH("does not have atomMap."); }
 
   virtual bool hasAtomTable() const { return false; }
   virtual XDRAtomTable& atomTable() { MOZ_CRASH("does not have atomTable"); }
-  virtual frontend::ParserAtomVectorBuilder& frontendAtoms() {
+  virtual frontend::ParserAtomSpanBuilder& frontendAtoms() {
     MOZ_CRASH("does not have frontendAtoms");
   }
   virtual LifoAlloc& stencilAlloc() { MOZ_CRASH("does not have stencilAlloc"); }
   virtual void finishAtomTable() { MOZ_CRASH("does not have atomTable"); }
 
   virtual bool isMainBuf() { return true; }
 
   virtual void switchToAtomBuf() { MOZ_CRASH("cannot switch to atom buffer."); }
@@ -546,31 +546,31 @@ class XDRStencilDecoder : public XDRDeco
     MOZ_ASSERT(options_);
   }
 
   uint32_t& nchunks() override { return nchunks_; }
 
   bool isForStencil() const override { return true; }
 
   bool hasAtomTable() const override { return hasFinishedAtomTable_; }
-  frontend::ParserAtomVectorBuilder& frontendAtoms() override {
+  frontend::ParserAtomSpanBuilder& frontendAtoms() override {
     return *parserAtomBuilder_;
   }
   LifoAlloc& stencilAlloc() override { return *stencilAlloc_; }
   void finishAtomTable() override { hasFinishedAtomTable_ = true; }
 
   bool hasOptions() const override { return true; }
   const JS::ReadOnlyCompileOptions& options() override { return *options_; }
 
   XDRResult codeStencils(frontend::CompilationInfoVector& compilationInfos);
 
  private:
   const JS::ReadOnlyCompileOptions* options_;
   bool hasFinishedAtomTable_ = false;
-  frontend::ParserAtomVectorBuilder* parserAtomBuilder_ = nullptr;
+  frontend::ParserAtomSpanBuilder* parserAtomBuilder_ = nullptr;
   LifoAlloc* stencilAlloc_ = nullptr;
 };
 
 class XDROffThreadDecoder : public XDRDecoder {
   ScriptSourceObject** sourceObjectOut_;
   bool isMultiDecode_;
 
  public: