arai-a / smoosh-sync

Automation to make jsparagus and SpiderMonkey bytecode in sync
2 stars 0 forks source link

/js/src/vm/Opcodes.h and 6 more files have been updated (818027e6) #288

Closed github-actions[bot] closed 1 year ago

github-actions[bot] commented 2 years ago

Files

Changesets

Diffs

/js/src/frontend/CompilationStencil.h

--- d73e753cf2bb5ff92c085c7d4e3ad2bb59047535/js/src/frontend/CompilationStencil.h
+++ 62177878bdfbb1a5332a8f16ec2d80caca12d841/js/src/frontend/CompilationStencil.h
@@ -21,48 +21,53 @@
 #include "ds/LifoAlloc.h"
 #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 "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
 #include "frontend/UsedNameTracker.h"
-#include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions
 #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/ErrorContext.h"  // MainThreadErrorContext
 #include "vm/GlobalObject.h"  // GlobalObject
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"  // JSFunction
 #include "vm/JSScript.h"    // SourceExtent
 #include "vm/Realm.h"
 #include "vm/ScopeKind.h"      // ScopeKind
 #include "vm/SharedStencil.h"  // SharedImmutableScriptData

 class JSAtom;
 class JSString;

+namespace JS {
+class JS_PUBLIC_API ReadOnlyCompileOptions;
+}
+
 namespace js {

 class AtomSet;
 class JSONPrinter;
 class ModuleObject;

 namespace frontend {

 struct CompilationInput;
 struct CompilationStencil;
 struct CompilationGCOutput;
 class ScriptStencilIterable;
 struct InputName;
+class ScopeBindingCache;

 // Reference to a Scope within a CompilationStencil.
 struct ScopeStencilRef {
   const CompilationStencil& context_;
   const ScopeIndex scopeIndex_;

   // Lookup the ScopeStencil referenced by this ScopeStencilRef.
   inline const ScopeStencil& scope() const;
@@ -351,40 +356,57 @@ struct InputName {
       : variant_(NameStencilRef{scope.context_, index}) {}
   InputName(BaseScript*, JSAtom* ptr) : variant_(ptr) {}
   InputName(const ScriptStencilRef& script, TaggedParserAtomIndex index)
       : variant_(NameStencilRef{script.context_, index}) {}

   // The InputName is either from an instantiated name, or from another
   // CompilationStencil. This method interns the current name in the parser atom
   // table of a CompilationState, which has a corresponding CompilationInput.
-  TaggedParserAtomIndex internInto(JSContext* cx, ParserAtomsTable& parserAtoms,
+  TaggedParserAtomIndex internInto(JSContext* cx, ErrorContext* ec,
+                                   ParserAtomsTable& parserAtoms,
                                    CompilationAtomCache& atomCache);

   // Compare an InputName, which is not yet interned, with `other` is either an
   // interned name or a well-known or static string.
   //
   // The `otherCached` argument should be a reference to a JSAtom*, initialized
   // to nullptr, which is used to cache the JSAtom representation of the `other`
   // argument if needed. If a different `other` parameter is provided, the
   // `otherCached` argument should be reset to nullptr.
-  bool isEqualTo(JSContext* cx, ParserAtomsTable& parserAtoms,
+  bool isEqualTo(JSContext* cx, ErrorContext* ec, ParserAtomsTable& parserAtoms,
                  CompilationAtomCache& atomCache, TaggedParserAtomIndex other,
                  JSAtom** otherCached) const;

   bool isNull() const {
     return variant_.match(
         [](JSAtom* ptr) { return !ptr; },
         [](const NameStencilRef& ref) { return !ref.atomIndex_; });
   }
 };

 // ScopeContext holds information derived from the scope and environment chains
 // to try to avoid the parser needing to traverse VM structures directly.
 struct ScopeContext {
+  // Cache: Scope -> (JSAtom/TaggedParserAtomIndex -> NameLocation)
+  //
+  // This cache maps the scope to a hash table which can lookup a name of the
+  // scope to the equivalent NameLocation.
+  ScopeBindingCache* scopeCache = nullptr;
+
+  // Generation number of the `scopeCache` collected before filling the cache
+  // with enclosing scope information.
+  //
+  // The generation number, obtained from `scopeCache->getCurrentGeneration()`
+  // is incremented each time the GC invalidate the content of the cache. The
+  // `scopeCache` can only be used when the generation number collected before
+  // filling the cache is identical to the generation number seen when querying
+  // the cached content.
+  size_t scopeCacheGen = 0;
+
   // Class field initializer info if we are nested within a class constructor.
   // We may be an combination of arrow and eval context within the constructor.
   mozilla::Maybe<MemberInitializers> memberInitializers = {};

   enum class EnclosingLexicalBindingKind {
     Let,
     Const,
     CatchParameter,
@@ -459,48 +481,59 @@ struct ScopeContext {
   // True if the enclosing scope has non-syntactic scope on chain.
   bool hasNonSyntacticScopeOnChain = false;

   // True if the enclosing scope has function scope where the function needs
   // home object.
   bool hasFunctionNeedsHomeObjectOnChain = false;
 #endif

-  bool init(JSContext* cx, CompilationInput& input,
-            ParserAtomsTable& parserAtoms, InheritThis inheritThis,
-            JSObject* enclosingEnv);
+  bool init(JSContext* cx, ErrorContext* ec, CompilationInput& input,
+            ParserAtomsTable& parserAtoms, ScopeBindingCache* scopeCache,
+            InheritThis inheritThis, JSObject* enclosingEnv);

   mozilla::Maybe<EnclosingLexicalBindingKind>
   lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name);

-  NameLocation searchInEnclosingScope(JSContext* cx, CompilationInput& input,
+  NameLocation searchInEnclosingScope(JSContext* cx, ErrorContext* ec,
+                                      CompilationInput& input,
                                       ParserAtomsTable& parserAtoms,
-                                      TaggedParserAtomIndex name, uint8_t hops);
+                                      TaggedParserAtomIndex name);

   bool effectiveScopePrivateFieldCacheHas(TaggedParserAtomIndex name);
   mozilla::Maybe<NameLocation> getPrivateFieldLocation(
       TaggedParserAtomIndex name);

  private:
   void computeThisBinding(const InputScope& scope);
   void computeThisEnvironment(const InputScope& enclosingScope);
   void computeInScope(const InputScope& enclosingScope);
   void cacheEnclosingScope(const InputScope& enclosingScope);
+  NameLocation searchInEnclosingScopeWithCache(JSContext* cx, ErrorContext* ec,
+                                               CompilationInput& input,
+                                               ParserAtomsTable& parserAtoms,
+                                               TaggedParserAtomIndex name);
+  NameLocation searchInEnclosingScopeNoCache(JSContext* cx, ErrorContext* ec,
+                                             CompilationInput& input,
+                                             ParserAtomsTable& parserAtoms,
+                                             TaggedParserAtomIndex name);

   InputScope determineEffectiveScope(InputScope& scope, JSObject* environment);

-  bool cachePrivateFieldsForEval(JSContext* cx, CompilationInput& input,
+  bool cachePrivateFieldsForEval(JSContext* cx, ErrorContext* ec,
+                                 CompilationInput& input,
                                  JSObject* enclosingEnvironment,
                                  const InputScope& effectiveScope,
                                  ParserAtomsTable& parserAtoms);

-  bool cacheEnclosingScopeBindingForEval(JSContext* cx, CompilationInput& input,
+  bool cacheEnclosingScopeBindingForEval(JSContext* cx, ErrorContext* ec,
+                                         CompilationInput& input,
                                          ParserAtomsTable& parserAtoms);

-  bool addToEnclosingLexicalBindingCache(JSContext* cx,
+  bool addToEnclosingLexicalBindingCache(JSContext* cx, ErrorContext* ec,
                                          ParserAtomsTable& parserAtoms,
                                          CompilationAtomCache& atomCache,
                                          InputName& name,
                                          EnclosingLexicalBindingKind kind);
 };

 struct CompilationAtomCache {
  public:
@@ -521,18 +554,18 @@ struct CompilationAtomCache {
   JSString* getStringAt(ParserAtomIndex index) const;

   JSAtom* getExistingAtomAt(ParserAtomIndex index) const;
   JSAtom* getExistingAtomAt(JSContext* cx,
                             TaggedParserAtomIndex taggedIndex) const;
   JSAtom* getAtomAt(ParserAtomIndex index) const;

   bool hasAtomAt(ParserAtomIndex index) const;
-  bool setAtomAt(JSContext* cx, ParserAtomIndex index, JSString* atom);
-  bool allocate(JSContext* cx, size_t length);
+  bool setAtomAt(ErrorContext* ec, ParserAtomIndex index, JSString* atom);
+  bool allocate(ErrorContext* ec, size_t length);

   bool empty() const { return atoms_.empty(); }

   void stealBuffer(AtomCacheVector& atoms);
   void releaseBuffer(AtomCacheVector& atoms);

   void trace(JSTracer* trc);

@@ -576,53 +609,55 @@ struct CompilationInput {
   //  * If the target is Delazification, the non-null enclosing scope of
   //    the function
   InputScope enclosingScope = InputScope(nullptr);

   explicit CompilationInput(const JS::ReadOnlyCompileOptions& options)
       : options(options) {}

  private:
-  bool initScriptSource(JSContext* cx);
+  bool initScriptSource(JSContext* cx, ErrorContext* ec);

  public:
-  bool initForGlobal(JSContext* cx) {
+  bool initForGlobal(JSContext* cx, ErrorContext* ec) {
     target = CompilationTarget::Global;
-    return initScriptSource(cx);
+    return initScriptSource(cx, ec);
   }

   bool initForSelfHostingGlobal(JSContext* cx) {
     target = CompilationTarget::SelfHosting;
-    return initScriptSource(cx);
+    MainThreadErrorContext ec(cx);
+    return initScriptSource(cx, &ec);
   }

-  bool initForStandaloneFunction(JSContext* cx) {
+  bool initForStandaloneFunction(JSContext* cx, ErrorContext* ec) {
     target = CompilationTarget::StandaloneFunction;
-    if (!initScriptSource(cx)) {
+    if (!initScriptSource(cx, ec)) {
       return false;
     }
     enclosingScope = InputScope(&cx->global()->emptyGlobalScope());
     return true;
   }

   bool initForStandaloneFunctionInNonSyntacticScope(
-      JSContext* cx, Handle<Scope*> functionEnclosingScope);
+      JSContext* cx, ErrorContext* ec, Handle<Scope*> functionEnclosingScope);

-  bool initForEval(JSContext* cx, Handle<Scope*> evalEnclosingScope) {
+  bool initForEval(JSContext* cx, ErrorContext* ec,
+                   Handle<Scope*> evalEnclosingScope) {
     target = CompilationTarget::Eval;
-    if (!initScriptSource(cx)) {
+    if (!initScriptSource(cx, ec)) {
       return false;
     }
     enclosingScope = InputScope(evalEnclosingScope);
     return true;
   }

-  bool initForModule(JSContext* cx) {
+  bool initForModule(JSContext* cx, ErrorContext* ec) {
     target = CompilationTarget::Module;
-    if (!initScriptSource(cx)) {
+    if (!initScriptSource(cx, ec)) {
       return false;
     }
     // The `enclosingScope` is the emptyGlobalScope.
     return true;
   }

   void initFromLazy(JSContext* cx, BaseScript* lazyScript, ScriptSource* ss) {
     MOZ_ASSERT(cx->compartment() == lazyScript->compartment());
@@ -781,17 +816,17 @@ class CompilationSyntaxParseCache {
   // Return the extra information about the function being delazified, if any.
   const ScriptStencilExtra& funExtra() const {
     MOZ_ASSERT(isInitialized);
     return funExtra_;
   }

   // Initialize the SynaxParse cache given a LifoAlloc. The JSContext is only
   // used for reporting allocation errors.
-  [[nodiscard]] bool init(JSContext* cx, LifoAlloc& alloc,
+  [[nodiscard]] bool init(JSContext* cx, ErrorContext* ec, LifoAlloc& alloc,
                           ParserAtomsTable& parseAtoms,
                           CompilationAtomCache& atomCache,
                           const InputScript& lazy);

  private:
   // Return the script index of a given inner function.
   //
   // WARNING: The ScriptIndex returned by this function corresponds to the index
@@ -801,33 +836,37 @@ class CompilationSyntaxParseCache {
   // not be confused with any ScriptIndex from the CompilationState.
   ScriptIndex scriptIndex(size_t functionIndex) const {
     MOZ_ASSERT(isInitialized);
     auto taggedScriptIndex = cachedGCThings_[functionIndex];
     MOZ_ASSERT(taggedScriptIndex.isFunction());
     return taggedScriptIndex.toFunction();
   }

-  [[nodiscard]] bool copyFunctionInfo(JSContext* cx,
+  [[nodiscard]] bool copyFunctionInfo(JSContext* cx, ErrorContext* ec,
                                       ParserAtomsTable& parseAtoms,
                                       CompilationAtomCache& atomCache,
                                       const InputScript& lazy);
-  [[nodiscard]] bool copyScriptInfo(JSContext* cx, LifoAlloc& alloc,
+  [[nodiscard]] bool copyScriptInfo(JSContext* cx, ErrorContext* ec,
+                                    LifoAlloc& alloc,
                                     ParserAtomsTable& parseAtoms,
                                     CompilationAtomCache& atomCache,
                                     BaseScript* lazy);
-  [[nodiscard]] bool copyScriptInfo(JSContext* cx, LifoAlloc& alloc,
+  [[nodiscard]] bool copyScriptInfo(JSContext* cx, ErrorContext* ec,
+                                    LifoAlloc& alloc,
                                     ParserAtomsTable& parseAtoms,
                                     CompilationAtomCache& atomCache,
                                     const ScriptStencilRef& lazy);
-  [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, LifoAlloc& alloc,
+  [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, ErrorContext* ec,
+                                            LifoAlloc& alloc,
                                             ParserAtomsTable& parseAtoms,
                                             CompilationAtomCache& atomCache,
                                             BaseScript* lazy);
-  [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, LifoAlloc& alloc,
+  [[nodiscard]] bool copyClosedOverBindings(JSContext* cx, ErrorContext* ec,
+                                            LifoAlloc& alloc,
                                             ParserAtomsTable& parseAtoms,
                                             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.
@@ -891,20 +930,22 @@ struct SharedDataContainer {
     std::swap(data_, other.data_);
     MOZ_ASSERT(other.isEmpty());
     return *this;
   }

   ~SharedDataContainer();

   [[nodiscard]] bool initVector(JSContext* cx);
+  [[nodiscard]] bool initVector(ErrorContext* ec);
   [[nodiscard]] bool initMap(JSContext* cx);
+  [[nodiscard]] bool initMap(ErrorContext* ec);

  private:
-  [[nodiscard]] bool convertFromSingleToMap(JSContext* cx);
+  [[nodiscard]] bool convertFromSingleToMap(ErrorContext* ec);

  public:
   bool isEmpty() const { return (data_) == SingleTag; }
   bool isSingle() const { return (data_ & TagMask) == SingleTag; }
   bool isVector() const { return (data_ & TagMask) == VectorTag; }
   bool isMap() const { return (data_ & TagMask) == MapTag; }
   bool isBorrow() const { return (data_ & TagMask) == BorrowTag; }

@@ -935,32 +976,35 @@ struct SharedDataContainer {
     MOZ_ASSERT(isMap());
     return reinterpret_cast<SharedDataMapPtr>(data_ & ~TagMask);
   }
   SharedDataContainer* asBorrow() const {
     MOZ_ASSERT(isBorrow());
     return reinterpret_cast<SharedDataContainer*>(data_ & ~TagMask);
   }

-  [[nodiscard]] bool prepareStorageFor(JSContext* cx, size_t nonLazyScriptCount,
+  [[nodiscard]] bool prepareStorageFor(ErrorContext* ec,
+                                       size_t nonLazyScriptCount,
                                        size_t allScriptCount);
-  [[nodiscard]] bool cloneFrom(JSContext* cx, const SharedDataContainer& other);
+  [[nodiscard]] bool cloneFrom(ErrorContext* ec,
+                               const SharedDataContainer& other);

   // Returns index-th script's shared data, or nullptr if it doesn't have.
   js::SharedImmutableScriptData* get(ScriptIndex index) const;

   // Add data for index-th script and share it with VM.
-  [[nodiscard]] bool addAndShare(JSContext* cx, ScriptIndex index,
+  [[nodiscard]] bool addAndShare(JSContext* cx, ErrorContext* ec,
+                                 ScriptIndex index,
                                  js::SharedImmutableScriptData* data);

   // Add data for index-th script without sharing it with VM.
   // The data should already be shared with VM.
   //
   // The data is supposed to be added from delazification.
-  [[nodiscard]] bool addExtraWithoutShare(JSContext* cx, ScriptIndex index,
+  [[nodiscard]] bool addExtraWithoutShare(ErrorContext* ec, ScriptIndex index,
                                           js::SharedImmutableScriptData* data);

   // Dynamic memory associated with this container. Does not include the
   // SharedImmutableScriptData since we are not the unique owner of it.
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     if (isVector()) {
       return asVector()->sizeOfIncludingThis(mallocSizeOf);
     }
@@ -1297,26 +1341,28 @@ struct ExtensibleCompilationStencil {
     functionKey = extent.toFunctionKey();
   }

   bool isInitialStencil() const {
     return functionKey == SourceExtent::NullFunctionKey;
   }

   // Steal CompilationStencil content.
-  [[nodiscard]] bool steal(JSContext* cx, RefPtr<CompilationStencil>&& other);
+  [[nodiscard]] bool steal(ErrorContext* ec,
+                           RefPtr<CompilationStencil>&& other);

   // Clone ExtensibleCompilationStencil content.
-  [[nodiscard]] bool cloneFrom(JSContext* cx, const CompilationStencil& other);
-  [[nodiscard]] bool cloneFrom(JSContext* cx,
+  [[nodiscard]] bool cloneFrom(ErrorContext* ec,
+                               const CompilationStencil& other);
+  [[nodiscard]] bool cloneFrom(ErrorContext* ec,
                                const ExtensibleCompilationStencil& other);

  private:
   template <typename Stencil>
-  [[nodiscard]] bool cloneFromImpl(JSContext* cx, const Stencil& other);
+  [[nodiscard]] bool cloneFromImpl(ErrorContext* ec, const Stencil& other);

  public:
   const ParserAtomVector& parserAtomsSpan() const {
     return parserAtoms.entries();
   }

   bool isModule() const;

@@ -1362,27 +1408,30 @@ struct MOZ_RAII CompilationState : publi
   // SharedDataContainer.prepareStorageFor *before* start emitting bytecode.
   size_t nonLazyFunctionCount = 0;

   // End of fields.

   CompilationState(JSContext* cx, LifoAllocScope& parserAllocScope,
                    CompilationInput& input);

-  bool init(JSContext* cx, InheritThis inheritThis = InheritThis::No,
+  bool init(JSContext* cx, ErrorContext* ec, ScopeBindingCache* scopeCache,
+            InheritThis inheritThis = InheritThis::No,
             JSObject* enclosingEnv = nullptr) {
-    if (!scopeContext.init(cx, input, parserAtoms, inheritThis, enclosingEnv)) {
+    if (!scopeContext.init(cx, ec, input, parserAtoms, scopeCache, inheritThis,
+                           enclosingEnv)) {
       return false;
     }

     // gcThings is later used by the full parser initialization.
     if (input.isDelazifying()) {
       InputScript lazy = input.lazyOuterScript();
       auto& atomCache = input.atomCache;
-      if (!previousParseCache.init(cx, alloc, parserAtoms, atomCache, lazy)) {
+      if (!previousParseCache.init(cx, ec, alloc, parserAtoms, atomCache,
+                                   lazy)) {
         return false;
       }
     }

     return true;
   }

   // Track the state of key allocations and roll them back as parts of parsing
@@ -1390,39 +1439,39 @@ struct MOZ_RAII CompilationState : publi
   // encounter discarded frontend state.
   struct CompilationStatePosition {
     // Temporarily share this token struct with CompilationState.
     size_t scriptDataLength = 0;

     size_t asmJSCount = 0;
   };

-  bool prepareSharedDataStorage(JSContext* cx);
+  bool prepareSharedDataStorage(ErrorContext* ec);

   CompilationStatePosition getPosition();
   void rewind(const CompilationStatePosition& pos);

   // When parsing arrow function, parameter is parsed twice, and if there are
   // functions inside parameter expression, stencils will be created for them.
   //
   // Those functions exist only for lazy parsing.
   // Mark them "ghost", so that they don't affect other parts.
   //
   // See GHOST_FUNCTION in FunctionFlags.h for more details.
   void markGhost(const CompilationStatePosition& pos);

   // Allocate space for `length` gcthings, and return the address of the
   // first element to `cursor` to initialize on the caller.
-  bool allocateGCThingsUninitialized(JSContext* cx, ScriptIndex scriptIndex,
-                                     size_t length,
+  bool allocateGCThingsUninitialized(JSContext* cx, ErrorContext* ec,
+                                     ScriptIndex scriptIndex, size_t length,
                                      TaggedScriptThingIndex** cursor);

-  bool appendScriptStencilAndData(JSContext* cx);
+  bool appendScriptStencilAndData(ErrorContext* ec);

-  bool appendGCThings(JSContext* cx, ScriptIndex scriptIndex,
+  bool appendGCThings(JSContext* cx, ErrorContext* ec, ScriptIndex scriptIndex,
                       mozilla::Span<const TaggedScriptThingIndex> things);
 };

 // A temporary CompilationStencil instance that borrows
 // ExtensibleCompilationStencil data.
 // Ensure that this instance does not outlive the ExtensibleCompilationStencil.
 class MOZ_STACK_CLASS BorrowingCompilationStencil : public CompilationStencil {
  public:
@@ -1526,42 +1575,42 @@ struct CompilationGCOutput {
   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(JSContext* cx, size_t scriptDataLength,
+  [[nodiscard]] bool ensureReserved(ErrorContext* ec, size_t scriptDataLength,
                                     size_t scopeDataLength) {
     if (!functions.reserve(scriptDataLength)) {
-      ReportOutOfMemory(cx);
+      ReportOutOfMemory(ec);
       return false;
     }
     if (!scopes.reserve(scopeDataLength)) {
-      ReportOutOfMemory(cx);
+      ReportOutOfMemory(ec);
       return false;
     }
     return true;
   }

   // A variant of `ensureReserved` 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(JSContext* cx,
+  [[nodiscard]] bool ensureReservedWithBaseIndex(ErrorContext* ec,
                                                  ScriptIndex scriptStart,
                                                  ScriptIndex scriptLimit,
                                                  ScopeIndex scopeStart,
                                                  ScopeIndex scopeLimit) {
     this->functionsBaseIndex = scriptStart;
     this->scopesBaseIndex = scopeStart;

-    return ensureReserved(cx, scriptLimit - scriptStart,
+    return ensureReserved(ec, 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);
   }
@@ -1688,39 +1737,43 @@ struct CompilationStencilMerger {
   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>;
   FunctionKeyToScriptIndexMap functionKeyToInitialScriptIndex_;

-  [[nodiscard]] bool buildFunctionKeyToIndex(JSContext* cx);
+  [[nodiscard]] bool buildFunctionKeyToIndex(ErrorContext* ec);

   ScriptIndex getInitialScriptIndexFor(
       const CompilationStencil& delazification) const;

   // A map from delazification's ParserAtomIndex to
   // initial's TaggedParserAtomIndex
   using AtomIndexMap = Vector<TaggedParserAtomIndex, 0, js::SystemAllocPolicy>;

-  [[nodiscard]] bool buildAtomIndexMap(JSContext* cx,
+  [[nodiscard]] bool buildAtomIndexMap(ErrorContext* ec,
                                        const CompilationStencil& delazification,
                                        AtomIndexMap& atomIndexMap);

  public:
   CompilationStencilMerger() = default;

   // Set the initial stencil and prepare for merging.
   [[nodiscard]] bool setInitial(
       JSContext* cx, UniquePtr<ExtensibleCompilationStencil>&& initial);
+  [[nodiscard]] bool setInitial(
+      ErrorContext* ec, UniquePtr<ExtensibleCompilationStencil>&& initial);

   // Merge the delazification stencil into the initial stencil.
   [[nodiscard]] bool addDelazification(
       JSContext* cx, const CompilationStencil& delazification);
+  [[nodiscard]] bool addDelazification(
+      ErrorContext* ec, const CompilationStencil& delazification);

   ExtensibleCompilationStencil& getResult() const { return *initial_; }
   UniquePtr<ExtensibleCompilationStencil> takeResult() {
     return std::move(initial_);
   }
 };

 const ScopeStencil& ScopeStencilRef::scope() const {

/js/src/frontend/Stencil.cpp


--- ee33ca7b2fc935113c95907f64c71b1058171782/js/src/frontend/Stencil.cpp
+++ 62177878bdfbb1a5332a8f16ec2d80caca12d841/js/src/frontend/Stencil.cpp
@@ -16,16 +16,17 @@

 #include "ds/LifoAlloc.h"               // LifoAlloc
 #include "frontend/AbstractScopePtr.h"  // ScopeIndex
 #include "frontend/BytecodeCompilation.h"  // CanLazilyParse, CompileGlobalScriptToStencil
 #include "frontend/BytecodeCompiler.h"    // ParseModuleToStencil
 #include "frontend/BytecodeSection.h"     // EmitScriptThingsVector
 #include "frontend/CompilationStencil.h"  // CompilationStencil, CompilationState, ExtensibleCompilationStencil, CompilationGCOutput, CompilationStencilMerger
 #include "frontend/NameAnalysisTypes.h"   // EnvironmentCoordinate
+#include "frontend/ScopeBindingCache.h"   // ScopeBindingCache
 #include "frontend/SharedContext.h"
 #include "frontend/StencilXdr.h"        // XDRStencilEncoder, XDRStencilDecoder
 #include "gc/AllocKind.h"               // gc::AllocKind
 #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
@@ -33,33 +34,30 @@
 #include "js/Transcoding.h"             // JS::TranscodeBuffer
 #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/ErrorContext.h"
 #include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
-#include "vm/HelperThreads.h"          // js::StartOffThreadParseScript
-#include "vm/HelperThreadState.h"
-#include "vm/JSContext.h"  // JSContext
+#include "vm/JSContext.h"              // JSContext
 #include "vm/JSFunction.h"  // JSFunction, GetFunctionPrototype, NewFunctionWithProto
 #include "vm/JSObject.h"      // JSObject, TenuredObject
 #include "vm/JSONPrinter.h"   // js::JSONPrinter
 #include "vm/JSScript.h"      // BaseScript, JSScript
 #include "vm/Printer.h"       // js::Fprinter
 #include "vm/RegExpObject.h"  // js::RegExpObject
 #include "vm/Scope.h"  // Scope, *Scope, ScopeKind::*, ScopeKindString, ScopeIter, ScopeKindIsCatch, BindingIter, GetScopeDataTrailingNames, SizeOfParserScopeData
 #include "vm/ScopeKind.h"    // ScopeKind
 #include "vm/SelfHosting.h"  // SetClonedSelfHostedFunctionName
 #include "vm/StaticStrings.h"
 #include "vm/StencilEnums.h"  // ImmutableScriptFlagsEnum
 #include "vm/StringType.h"    // JSAtom, js::CopyChars
 #include "wasm/AsmJS.h"       // InstantiateAsmJS
-#include "wasm/WasmModule.h"  // wasm::Module

 #include "vm/EnvironmentObject-inl.h"  // JSObject::enclosingEnvironment
 #include "vm/JSFunction-inl.h"         // JSFunction::create

 using namespace js;
 using namespace js::frontend;

 // These 2 functions are used to write the same code with lambda using auto
@@ -92,63 +90,257 @@ InputName InputScript::displayAtom() con
       [](BaseScript* ptr) {
         return InputName(ptr, ptr->function()->displayAtom());
       },
       [](const ScriptStencilRef& ref) {
         return InputName(ref, ref.scriptData().functionAtom);
       });
 }

-TaggedParserAtomIndex InputName::internInto(JSContext* cx,
+TaggedParserAtomIndex InputName::internInto(JSContext* cx, ErrorContext* ec,
                                             ParserAtomsTable& parserAtoms,
                                             CompilationAtomCache& atomCache) {
   return variant_.match(
       [&](JSAtom* ptr) -> TaggedParserAtomIndex {
-        return parserAtoms.internJSAtom(cx, atomCache, ptr);
+        return parserAtoms.internJSAtom(cx, ec, atomCache, ptr);
       },
       [&](NameStencilRef& ref) -> TaggedParserAtomIndex {
-        return parserAtoms.internExternalParserAtomIndex(cx, ref.context_,
+        return parserAtoms.internExternalParserAtomIndex(ec, ref.context_,
                                                          ref.atomIndex_);
       });
 }

-bool InputName::isEqualTo(JSContext* cx, ParserAtomsTable& parserAtoms,
+bool InputName::isEqualTo(JSContext* cx, ErrorContext* ec,
+                          ParserAtomsTable& parserAtoms,
                           CompilationAtomCache& atomCache,
                           TaggedParserAtomIndex other,
                           JSAtom** otherCached) const {
   return variant_.match(
       [&](const JSAtom* ptr) -> bool {
+        if (ptr->hash() != parserAtoms.hash(other)) {
+          return false;
+        }
+
         if (!*otherCached) {
           // TODO-Stencil:
           // Here, we convert our name into a JSAtom*, and hard-crash on failure
           // to allocate. This conversion should not be required as we should be
           // able to iterate up snapshotted scope chains that use parser atoms.
           //
           // This will be fixed when the enclosing scopes are snapshotted.
           //
           // See bug 1690277.
           AutoEnterOOMUnsafeRegion oomUnsafe;
-          *otherCached = parserAtoms.toJSAtom(cx, other, atomCache);
+          *otherCached = parserAtoms.toJSAtom(cx, ec, other, atomCache);
           if (!*otherCached) {
             oomUnsafe.crash("InputName::isEqualTo");
           }
         } else {
           MOZ_ASSERT(atomCache.getExistingAtomAt(cx, other) == *otherCached);
         }
         return ptr == *otherCached;
       },
       [&](const NameStencilRef& ref) -> bool {
         return parserAtoms.isEqualToExternalParserAtomIndex(other, ref.context_,
                                                             ref.atomIndex_);
       });
 }

-bool ScopeContext::init(JSContext* cx, CompilationInput& input,
-                        ParserAtomsTable& parserAtoms, InheritThis inheritThis,
+GenericAtom::GenericAtom(JSContext* cx, ErrorContext* ec,
+                         ParserAtomsTable& parserAtoms,
+                         CompilationAtomCache& atomCache,
+                         TaggedParserAtomIndex index)
+    : ref(EmitterName(cx, ec, parserAtoms, atomCache, index)) {
+  hash = parserAtoms.hash(index);
+}
+
+GenericAtom::GenericAtom(const CompilationStencil& context,
+                         TaggedParserAtomIndex index)
+    : ref(StencilName{context, index}) {
+  if (index.isParserAtomIndex()) {
+    ParserAtom* atom = context.parserAtomData[index.toParserAtomIndex()];
+    hash = atom->hash();
+  } else {
+    hash = index.staticOrWellKnownHash();
+  }
+}
+
+GenericAtom::GenericAtom(ScopeStencilRef& scope, TaggedParserAtomIndex index)
+    : GenericAtom(scope.context_, index) {}
+
+BindingHasher<TaggedParserAtomIndex>::Lookup::Lookup(ScopeStencilRef& scope_ref,
+                                                     const GenericAtom& other)
+    : keyStencil(scope_ref.context_), other(other) {}
+
+bool GenericAtom::operator==(const GenericAtom& other) const {
+  return ref.match(
+      [&other](const EmitterName& name) -> bool {
+        return other.ref.match(
+            [&name](const EmitterName& other) -> bool {
+              // We never have multiple Emitter context at the same time.
+              MOZ_ASSERT(name.cx == other.cx);
+              MOZ_ASSERT(&name.parserAtoms == &other.parserAtoms);
+              MOZ_ASSERT(&name.atomCache == &other.atomCache);
+              return name.index == other.index;
+            },
+            [&name](const StencilName& other) -> bool {
+              return name.parserAtoms.isEqualToExternalParserAtomIndex(
+                  name.index, other.stencil, other.index);
+            },
+            [&name](JSAtom* other) -> bool {
+              AutoEnterOOMUnsafeRegion oomUnsafe;
+              JSAtom* namePtr = name.parserAtoms.toJSAtom(
+                  name.cx, name.ec, name.index, name.atomCache);
+              if (!namePtr) {
+                oomUnsafe.crash("GenericAtom(EmitterName == JSAtom*)");
+              }
+              return namePtr == other;
+            });
+      },
+      [&other](const StencilName& name) -> bool {
+        return other.ref.match(
+            [&name](const EmitterName& other) -> bool {
+              return other.parserAtoms.isEqualToExternalParserAtomIndex(
+                  other.index, name.stencil, name.index);
+            },
+            [&name](const StencilName& other) -> bool {
+              // Technically it is possible to have multiple stencils, but in
+              // this particular case let's assume we never encounter a case
+              // where we are comparing names from different stencils.
+              //
+              // The reason this assumption is safe today is that we are only
+              // using this in the context of a stencil-delazification, where
+              // the only StencilNames are coming from the CompilationStencil
+              // provided to CompilationInput::initFromStencil.
+              MOZ_ASSERT(&name.stencil == &other.stencil);
+              return name.index == other.index;
+            },
+            [](JSAtom* other) -> bool {
+              MOZ_CRASH("Never used.");
+              return false;
+            });
+      },
+      [&other](JSAtom* name) -> bool {
+        return other.ref.match(
+            [&name](const EmitterName& other) -> bool {
+              AutoEnterOOMUnsafeRegion oomUnsafe;
+              JSAtom* otherPtr = other.parserAtoms.toJSAtom(
+                  other.cx, other.ec, other.index, other.atomCache);
+              if (!otherPtr) {
+                oomUnsafe.crash("GenericAtom(JSAtom* == EmitterName)");
+              }
+              return name == otherPtr;
+            },
+            [](const StencilName& other) -> bool {
+              MOZ_CRASH("Never used.");
+              return false;
+            },
+            [&name](JSAtom* other) -> bool { return name == other; });
+      });
+}
+
+#ifdef DEBUG
+template <typename SpanT, typename VecT>
+void AssertBorrowingSpan(const SpanT& span, const VecT& vec) {
+  MOZ_ASSERT(span.size() == vec.length());
+  MOZ_ASSERT(span.data() == vec.begin());
+}
+#endif
+
+bool ScopeBindingCache::canCacheFor(Scope* ptr) {
+  MOZ_CRASH("Unexpected scope chain type: Scope*");
+}
+
+bool ScopeBindingCache::canCacheFor(ScopeStencilRef ref) {
+  MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
+}
+
+BindingMap<JSAtom*>* ScopeBindingCache::createCacheFor(Scope* ptr) {
+  MOZ_CRASH("Unexpected scope chain type: Scope*");
+}
+
+BindingMap<JSAtom*>* ScopeBindingCache::lookupScope(Scope* ptr,
+                                                    CacheGeneration gen) {
+  MOZ_CRASH("Unexpected scope chain type: Scope*");
+}
+
+BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
+    ScopeStencilRef ref) {
+  MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
+}
+
+BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
+    ScopeStencilRef ref, CacheGeneration gen) {
+  MOZ_CRASH("Unexpected scope chain type: ScopeStencilRef");
+}
+
+bool NoScopeBindingCache::canCacheFor(Scope* ptr) { return false; }
+
+bool NoScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return false; }
+
+bool RuntimeScopeBindingCache::canCacheFor(Scope* ptr) { return true; }
+
+BindingMap<JSAtom*>* RuntimeScopeBindingCache::createCacheFor(Scope* ptr) {
+  BaseScopeData* dataPtr = ptr->rawData();
+  BindingMap<JSAtom*> bindingCache;
+  if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
+    return nullptr;
+  }
+
+  return lookupScope(ptr, cacheGeneration);
+}
+
+BindingMap<JSAtom*>* RuntimeScopeBindingCache::lookupScope(
+    Scope* ptr, CacheGeneration gen) {
+  MOZ_ASSERT(gen == cacheGeneration);
+  BaseScopeData* dataPtr = ptr->rawData();
+  auto valuePtr = scopeMap.lookup(dataPtr);
+  if (!valuePtr) {
+    return nullptr;
+  }
+  return &valuePtr->value();
+}
+
+bool StencilScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return true; }
+
+BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
+    ScopeStencilRef ref) {
+#ifdef DEBUG
+  AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
+#endif
+  auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
+  BindingMap<TaggedParserAtomIndex> bindingCache;
+  if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
+    return nullptr;
+  }
+
+  return lookupScope(ref, 1);
+}
+
+BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
+    ScopeStencilRef ref, CacheGeneration gen) {
+#ifdef DEBUG
+  AssertBorrowingSpan(ref.context_.scopeNames, merger_.getResult().scopeNames);
+#endif
+  auto* dataPtr = ref.context_.scopeNames[ref.scopeIndex_];
+  auto ptr = scopeMap.lookup(dataPtr);
+  if (!ptr) {
+    return nullptr;
+  }
+  return &ptr->value();
+}
+
+bool ScopeContext::init(JSContext* cx, ErrorContext* ec,
+                        CompilationInput& input, ParserAtomsTable& parserAtoms,
+                        ScopeBindingCache* scopeCache, InheritThis inheritThis,
                         JSObject* enclosingEnv) {
+  // Record the scopeCache to be used while looking up NameLocation bindings.
+  this->scopeCache = scopeCache;
+  scopeCacheGen = scopeCache->getCurrentGeneration();
+
   InputScope maybeNonDefaultEnclosingScope(
       input.maybeNonDefaultEnclosingScope());

   // If this eval is in response to Debugger.Frame.eval, we may have an
   // incomplete scope chain. In order to provide a better debugging experience,
   // we inspect the (optional) environment chain to determine it's enclosing
   // FunctionScope if there is one. If there is no such scope, we use the
   // orignal scope provided.
@@ -163,20 +355,20 @@ bool ScopeContext::init(JSContext* cx, C
     computeThisBinding(effectiveScope);
     computeThisEnvironment(maybeNonDefaultEnclosingScope);
   }
   computeInScope(maybeNonDefaultEnclosingScope);

   cacheEnclosingScope(input.enclosingScope);

   if (input.target == CompilationInput::CompilationTarget::Eval) {
-    if (!cacheEnclosingScopeBindingForEval(cx, input, parserAtoms)) {
+    if (!cacheEnclosingScopeBindingForEval(cx, ec, input, parserAtoms)) {
       return false;
     }
-    if (!cachePrivateFieldsForEval(cx, input, enclosingEnv, effectiveScope,
+    if (!cachePrivateFieldsForEval(cx, ec, input, enclosingEnv, effectiveScope,
                                    parserAtoms)) {
       return false;
     }
   }

   return true;
 }

@@ -293,16 +485,215 @@ void ScopeContext::cacheEnclosingScope(c
       }
       if (si.scope().allowSuperProperty() && si.scope().needsHomeObject()) {
         hasFunctionNeedsHomeObjectOnChain = true;
       }
       break;
     }
   }
 #endif
+
+  // Pre-fill the scope cache by iterating over all the names. Stop iterating
+  // as soon as we find a scope which already has a filled scope cache.
+  AutoEnterOOMUnsafeRegion oomUnsafe;
+  for (InputScopeIter si(enclosingScope); si; si++) {
+    // If the current scope already exists, then there is no need to go deeper
+    // as the scope which are encoded after this one should already be present
+    // in the cache.
+    bool hasScopeCache = si.scope().match([&](auto& scope_ref) -> bool {
+      MOZ_ASSERT(scopeCache->canCacheFor(scope_ref));
+      return scopeCache->lookupScope(scope_ref, scopeCacheGen);
+    });
+    if (hasScopeCache) {
+      return;
+    }
+
+    bool hasEnv = si.hasSyntacticEnvironment();
+    auto setCacthAll = [&](NameLocation loc) {
+      return si.scope().match([&](auto& scope_ref) {
+        using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
+        BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
+        if (!bindingMapPtr) {
+          oomUnsafe.crash(
+              "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
+          return;
+        }
+
+        bindingMapPtr->catchAll.emplace(loc);
+      });
+    };
+    auto createEmpty = [&]() {
+      return si.scope().match([&](auto& scope_ref) {
+        using BindingMapPtr = decltype(scopeCache->createCacheFor(scope_ref));
+        BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
+        if (!bindingMapPtr) {
+          oomUnsafe.crash(
+              "ScopeContext::cacheEnclosingScope: scopeCache->createCacheFor");
+          return;
+        }
+      });
+    };
+
+    switch (si.kind()) {
+      case ScopeKind::Function:
+        if (hasEnv) {
+          if (si.scope().funHasExtensibleScope()) {
+            setCacthAll(NameLocation::Dynamic());
+            return;
+          }
+
+          si.scope().match([&](auto& scope_ref) {
+            using BindingMapPtr =
+                decltype(scopeCache->createCacheFor(scope_ref));
+            using Lookup =
+                typename std::remove_pointer_t<BindingMapPtr>::Lookup;
+            BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
+            if (!bindingMapPtr) {
+              oomUnsafe.crash(
+                  "ScopeContext::cacheEnclosingScope: "
+                  "scopeCache->createCacheFor");
+              return;
+            }
+
+            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
+              NameLocation loc = bi.nameLocation();
+              if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
+                continue;
+              }
+              auto ctxFreeKey = bi.name();
+              GenericAtom ctxKey(scope_ref, ctxFreeKey);
+              Lookup ctxLookup(scope_ref, ctxKey);
+              if (!bindingMapPtr->hashMap.put(ctxLookup, ctxFreeKey, loc)) {
+                oomUnsafe.crash(
+                    "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
+                return;
+              }
+            }
+          });
+        } else {
+          createEmpty();
+        }
+        break;
+
+      case ScopeKind::StrictEval:
+      case ScopeKind::FunctionBodyVar:
+      case ScopeKind::Lexical:
+      case ScopeKind::NamedLambda:
+      case ScopeKind::StrictNamedLambda:
+      case ScopeKind::SimpleCatch:
+      case ScopeKind::Catch:
+      case ScopeKind::FunctionLexical:
+      case ScopeKind::ClassBody:
+        if (hasEnv) {
+          si.scope().match([&](auto& scope_ref) {
+            using BindingMapPtr =
+                decltype(scopeCache->createCacheFor(scope_ref));
+            using Lookup =
+                typename std::remove_pointer_t<BindingMapPtr>::Lookup;
+            BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
+            if (!bindingMapPtr) {
+              oomUnsafe.crash(
+                  "ScopeContext::cacheEnclosingScope: "
+                  "scopeCache->createCacheFor");
+              return;
+            }
+
+            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
+              NameLocation loc = bi.nameLocation();
+              if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
+                continue;
+              }
+              auto ctxFreeKey = bi.name();
+              GenericAtom ctxKey(scope_ref, ctxFreeKey);
+              Lookup ctxLookup(scope_ref, ctxKey);
+              if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
+                oomUnsafe.crash(
+                    "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
+                return;
+              }
+            }
+          });
+        } else {
+          createEmpty();
+        }
+        break;
+
+      case ScopeKind::Module:
+        // This case is used only when delazifying a function inside
+        // module.
+        // Initial compilation of module doesn't have enlcosing scope.
+        if (hasEnv) {
+          si.scope().match([&](auto& scope_ref) {
+            using BindingMapPtr =
+                decltype(scopeCache->createCacheFor(scope_ref));
+            using Lookup =
+                typename std::remove_pointer_t<BindingMapPtr>::Lookup;
+            BindingMapPtr bindingMapPtr = scopeCache->createCacheFor(scope_ref);
+            if (!bindingMapPtr) {
+              oomUnsafe.crash(
+                  "ScopeContext::cacheEnclosingScope: "
+                  "scopeCache->createCacheFor");
+              return;
+            }
+
+            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
+              // Imports are on the environment but are indirect
+              // bindings and must be accessed dynamically instead of
+              // using an EnvironmentCoordinate.
+              NameLocation loc = bi.nameLocation();
+              if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate &&
+                  loc.kind() != NameLocation::Kind::Import) {
+                continue;
+              }
+              auto ctxFreeKey = bi.name();
+              GenericAtom ctxKey(scope_ref, ctxFreeKey);
+              Lookup ctxLookup(scope_ref, ctxKey);
+              if (!bindingMapPtr->hashMap.putNew(ctxLookup, ctxFreeKey, loc)) {
+                oomUnsafe.crash(
+                    "ScopeContext::cacheEnclosingScope: bindingMapPtr->put");
+                return;
+              }
+            }
+          });
+        } else {
+          createEmpty();
+        }
+        break;
+
+      case ScopeKind::Eval:
+        // As an optimization, if the eval doesn't have its own var
+        // environment and its immediate enclosing scope is a global
+        // scope, all accesses are global.
+        if (!hasEnv) {
+          ScopeKind kind = si.scope().enclosing().kind();
+          if (kind == ScopeKind::Global || kind == ScopeKind::NonSyntactic) {
+            setCacthAll(NameLocation::Global(BindingKind::Var));
+            return;
+          }
+        }
+
+        setCacthAll(NameLocation::Dynamic());
+        return;
+
+      case ScopeKind::Global:
+        setCacthAll(NameLocation::Global(BindingKind::Var));
+        return;
+
+      case ScopeKind::With:
+      case ScopeKind::NonSyntactic:
+        setCacthAll(NameLocation::Dynamic());
+        return;
+
+      case ScopeKind::WasmInstance:
+      case ScopeKind::WasmFunction:
+        MOZ_CRASH("No direct eval inside wasm functions");
+    }
+  }
+
+  MOZ_CRASH("Malformed scope chain");
 }

 InputScope ScopeContext::determineEffectiveScope(InputScope& scope,
                                                  JSObject* environment) {
   MOZ_ASSERT(effectiveScopeHops == 0);
   // If the scope-chain is non-syntactic, we may still determine a more precise
   // effective-scope to use instead.
   if (environment && scope.hasOnChain(ScopeKind::NonSyntactic)) {
@@ -347,17 +738,18 @@ static uint32_t DepthOfNearestVarScopeFo
       default:
         break;
     }
   }
   return depth;
 }

 bool ScopeContext::cacheEnclosingScopeBindingForEval(
-    JSContext* cx, CompilationInput& input, ParserAtomsTable& parserAtoms) {
+    JSContext* cx, ErrorContext* ec, CompilationInput& input,
+    ParserAtomsTable& parserAtoms) {
   enclosingLexicalBindingCache_.emplace();

   uint32_t varScopeDepth =
       DepthOfNearestVarScopeForDirectEval(input.enclosingScope);
   uint32_t depth = 0;
   for (InputScopeIter si(input.enclosingScope); si; si++) {
     bool success = si.scope().match([&](auto& scope_ref) {
       for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
@@ -367,47 +759,47 @@ bool ScopeContext::cacheEnclosingScopeBi
             // catch parameters with var declarations.
             bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch;
             if (!annexB35Allowance) {
               auto kind = ScopeKindIsCatch(si.kind())
                               ? EnclosingLexicalBindingKind::CatchParameter
                               : EnclosingLexicalBindingKind::Let;
               InputName binding(scope_ref, bi.name());
               if (!addToEnclosingLexicalBindingCache(
-                      cx, parserAtoms, input.atomCache, binding, kind)) {
+                      cx, ec, parserAtoms, input.atomCache, binding, kind)) {
                 return false;
               }
             }
             break;
           }

           case BindingKind::Const: {
             InputName binding(scope_ref, bi.name());
             if (!addToEnclosingLexicalBindingCache(
-                    cx, parserAtoms, input.atomCache, binding,
+                    cx, ec, parserAtoms, input.atomCache, binding,
                     EnclosingLexicalBindingKind::Const)) {
               return false;
             }
             break;
           }

           case BindingKind::Synthetic: {
             InputName binding(scope_ref, bi.name());
             if (!addToEnclosingLexicalBindingCache(
-                    cx, parserAtoms, input.atomCache, binding,
+                    cx, ec, parserAtoms, input.atomCache, binding,
                     EnclosingLexicalBindingKind::Synthetic)) {
               return false;
             }
             break;
           }

           case BindingKind::PrivateMethod: {
             InputName binding(scope_ref, bi.name());
             if (!addToEnclosingLexicalBindingCache(
-                    cx, parserAtoms, input.atomCache, binding,
+                    cx, ec, parserAtoms, input.atomCache, binding,
                     EnclosingLexicalBindingKind::PrivateMethod)) {
               return false;
             }
             break;
           }

           case BindingKind::Import:
           case BindingKind::FormalParameter:
@@ -426,38 +818,38 @@ bool ScopeContext::cacheEnclosingScopeBi
       break;
     }
   }

   return true;
 }

 bool ScopeContext::addToEnclosingLexicalBindingCache(
-    JSContext* cx, ParserAtomsTable& parserAtoms,
+    JSContext* cx, ErrorContext* ec, ParserAtomsTable& parserAtoms,
     CompilationAtomCache& atomCache, InputName& name,
     EnclosingLexicalBindingKind kind) {
   TaggedParserAtomIndex parserName =
-      name.internInto(cx, parserAtoms, atomCache);
+      name.internInto(cx, ec, parserAtoms, atomCache);
   if (!parserName) {
     return false;
   }

   // Same lexical binding can appear multiple times across scopes.
   //
   // enclosingLexicalBindingCache_ map is used for detecting conflicting
   // `var` binding, and inner binding should be reported in the error.
   //
   // cacheEnclosingScopeBindingForEval iterates from inner scope, and
   // inner-most binding is added to the map first.
   //
   // Do not overwrite the value with outer bindings.
   auto p = enclosingLexicalBindingCache_->lookupForAdd(parserName);
   if (!p) {
     if (!enclosingLexicalBindingCache_->add(p, parserName, kind)) {
-      ReportOutOfMemory(cx);
+      ReportOutOfMemory(ec);
       return false;
     }
   }

   return true;
 }

 static bool IsPrivateField(Scope*, JSAtom* atom) {
@@ -490,17 +882,17 @@ static bool IsPrivateField(ScopeStencilR
     // # character is not part of the allowed character of static strings.
     MOZ_ASSERT(content[0] != '#');
   }
 #endif

   return false;
 }

-bool ScopeContext::cachePrivateFieldsForEval(JSContext* cx,
+bool ScopeContext::cachePrivateFieldsForEval(JSContext* cx, ErrorContext* ec,
                                              CompilationInput& input,
                                              JSObject* enclosingEnvironment,
                                              const InputScope& effectiveScope,
                                              ParserAtomsTable& parserAtoms) {
   effectiveScopePrivateFieldCache_.emplace();

   // We 
(comment length limit exceeeded)