Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/frontend/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 188 kB image not shown  

Quelle  Stencil.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "frontend/Stencil.h"

#include "mozilla/AlreadyAddRefed.h"        // already_AddRefed
#include "mozilla/Assertions.h"             // MOZ_RELEASE_ASSERT
#include "mozilla/CheckedInt.h"             // mozilla::CheckedInt
#include "mozilla/Maybe.h"                  // mozilla::Maybe
#include "mozilla/OperatorNewExtensions.h"  // mozilla::KnownNotNull
#include "mozilla/PodOperations.h"          // mozilla::PodCopy
#include "mozilla/RefPtr.h"                 // RefPtr
#include "mozilla/ScopeExit.h"              // mozilla::ScopeExit
#include "mozilla/Sprintf.h"                // SprintfLiteral

#include <algorithm>  // std::fill
#include <string.h>   // strlen

#include "ds/LifoAlloc.h"               // LifoAlloc
#include "frontend/AbstractScopePtr.h"  // ScopeIndex
#include "frontend/BytecodeCompiler.h"  // CompileGlobalScriptToStencil, InstantiateStencils, CanLazilyParse, ParseModuleToStencil
#include "frontend/BytecodeSection.h"   // EmitScriptThingsVector
#include "frontend/CompilationStencil.h"  // CompilationStencil, CompilationState, ExtensibleCompilationStencil, CompilationGCOutput, CompilationStencilMerger
#include "frontend/FrontendContext.h"
#include "frontend/NameAnalysisTypes.h"  // EnvironmentCoordinate
#include "frontend/ParserAtom.h"  // ParserAtom, ParserAtomIndex, TaggedParserAtomIndex, ParserAtomsTable, Length{1,2,3}StaticParserString, InstantiateMarkedAtoms, InstantiateMarkedAtomsAsPermanent, GetWellKnownAtom
#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, JS::ReadOnlyDecodeOptions
#include "js/experimental/CompileScript.h"  // JS::PrepareForInstantiate
#include "js/experimental/JSStencil.h"      // JS::Stencil
#include "js/GCAPI.h"                       // JS::AutoCheckCannotGC
#include "js/Printer.h"                     // js::Fprinter
#include "js/RealmOptions.h"                // JS::RealmBehaviors
#include "js/RootingAPI.h"                  // Rooted
#include "js/Transcoding.h"                 // JS::TranscodeBuffer
#include "js/Utility.h"                     // js_malloc, js_calloc, js_free
#include "js/Value.h"                       // ObjectValue
#include "js/WasmModule.h"                  // JS::WasmModule
#include "vm/BigIntType.h"   // ParseBigIntLiteral, BigInt::createFromInt64
#include "vm/BindingKind.h"  // BindingKind
#include "vm/EnvironmentObject.h"
#include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
#include "vm/JSContext.h"              // JSContext
#include "vm/JSFunction.h"  // JSFunction, GetFunctionPrototype, NewFunctionWithProto
#include "vm/JSObject.h"      // JSObject, TenuredObject
#include "vm/JSONPrinter.h"   // js::JSONPrinter
#include "vm/JSScript.h"      // BaseScript, JSScript
#include "vm/Realm.h"         // JS::Realm
#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 "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
// arguments. The auto argument type is set by the Variant.match function of the
// InputScope variant. Thus dispatching to either a Scope* or to a
// ScopeStencilRef. This function can then be used as a way to specialize the
// code within the lambda without duplicating the code.
//
// Identically, an InputName is constructed using the scope type and the
// matching binding name type. This way, functions which are called by this
// lambda can manipulate an InputName and do not have to be duplicated.
//
// for (InputScopeIter si(...); si; si++) {
//   si.scope().match([](auto& scope) {
//     for (auto bi = InputBindingIter(scope); bi; bi++) {
//       InputName name(scope, bi.name());
//     }
//   });
// }
static js::BindingIter InputBindingIter(Scope* ptr) {
  return js::BindingIter(ptr);
}

static ParserBindingIter InputBindingIter(const ScopeStencilRef& ref) {
  return ParserBindingIter(ref);
}

static ParserBindingIter InputBindingIter(const FakeStencilGlobalScope&) {
  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No bindings on empty global.");
}

InputName InputScript::displayAtom() const {
  return script_.match(
      [](BaseScript* ptr) {
        return InputName(ptr, ptr->function()->fullDisplayAtom());
      },
      [](const ScriptStencilRef& ref) {
        return InputName(ref, ref.scriptData().functionAtom);
      });
}

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

bool InputName::isEqualTo(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                          CompilationAtomCache& atomCache,
                          TaggedParserAtomIndex other,
                          JSAtom** otherCached) const {
  return variant_.match(
      [&](const JSAtom* ptr) -> bool {
        if (ptr->hash() != parserAtoms.hash(other)) {
          return false;
        }

        // JSAtom variant is used only on the main thread delazification,
        // where JSContext is always available.
        JSContext* cx = fc->maybeCurrentJSContext();
        MOZ_ASSERT(cx);

        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, fc, 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_);
      });
}

GenericAtom::GenericAtom(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                         CompilationAtomCache& atomCache,
                         TaggedParserAtomIndex index)
    : ref(EmitterName(fc, 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.fc == other.fc);
              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 {
              // JSAtom variant is used only on the main thread delazification,
              // where JSContext is always available.
              JSContext* cx = name.fc->maybeCurrentJSContext();
              MOZ_ASSERT(cx);
              AutoEnterOOMUnsafeRegion oomUnsafe;
              JSAtom* namePtr = name.parserAtoms.toJSAtom(
                  cx, name.fc, 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 {
              // JSAtom variant is used only on the main thread delazification,
              // where JSContext is always available.
              JSContext* cx = other.fc->maybeCurrentJSContext();
              MOZ_ASSERT(cx);
              AutoEnterOOMUnsafeRegion oomUnsafe;
              JSAtom* otherPtr = other.parserAtoms.toJSAtom(
                  cx, other.fc, 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");
}

bool ScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
  MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
}

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");
}

BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::createCacheFor(
    const FakeStencilGlobalScope& ref) {
  MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
}

BindingMap<TaggedParserAtomIndex>* ScopeBindingCache::lookupScope(
    const FakeStencilGlobalScope& ref, CacheGeneration gen) {
  MOZ_CRASH("Unexpected scope chain type: FakeStencilGlobalScope");
}

bool NoScopeBindingCache::canCacheFor(Scope* ptr) { return false; }

bool NoScopeBindingCache::canCacheFor(ScopeStencilRef ref) { return false; }

bool NoScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& 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();
}

static AbstractBaseScopeData<TaggedParserAtomIndex>
    moduleGlobalAbstractScopeData;

bool StencilScopeBindingCache::canCacheFor(const FakeStencilGlobalScope& ref) {
  return true;
}

BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::createCacheFor(
    const FakeStencilGlobalScope& ref) {
  auto* dataPtr = &moduleGlobalAbstractScopeData;
  BindingMap<TaggedParserAtomIndex> bindingCache;
  if (!scopeMap.putNew(dataPtr, std::move(bindingCache))) {
    return nullptr;
  }

  return lookupScope(ref, 1);
}

BindingMap<TaggedParserAtomIndex>* StencilScopeBindingCache::lookupScope(
    const FakeStencilGlobalScope& ref, CacheGeneration gen) {
  auto* dataPtr = &moduleGlobalAbstractScopeData;
  auto ptr = scopeMap.lookup(dataPtr);
  if (!ptr) {
    return nullptr;
  }
  return &ptr->value();
}

bool ScopeContext::init(FrontendContext* fc, 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.
  //
  // NOTE: This is used to compute the ThisBinding kind and to allow access to
  //       private fields and methods, while other contextual information only
  //       uses the actual scope passed to the compile.
  auto effectiveScope =
      determineEffectiveScope(maybeNonDefaultEnclosingScope, enclosingEnv);

  if (inheritThis == InheritThis::Yes) {
    computeThisBinding(effectiveScope);
    computeThisEnvironment(maybeNonDefaultEnclosingScope);
  }
  computeInScope(maybeNonDefaultEnclosingScope);

  cacheEnclosingScope(input.enclosingScope);

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

  return true;
}

void ScopeContext::computeThisEnvironment(const InputScope& enclosingScope) {
  uint32_t envCount = 0;
  for (InputScopeIter si(enclosingScope); si; si++) {
    if (si.kind() == ScopeKind::Function) {
      // Arrow function inherit the "this" environment of the enclosing script,
      // so continue ignore them.
      if (!si.scope().isArrow()) {
        allowNewTarget = true;

        if (si.scope().allowSuperProperty()) {
          allowSuperProperty = true;
          enclosingThisEnvironmentHops = envCount;
        }

        if (si.scope().isClassConstructor()) {
          memberInitializers =
              si.scope().useMemberInitializers()
                  ? mozilla::Some(si.scope().getMemberInitializers())
                  : mozilla::Some(MemberInitializers::Empty());
          MOZ_ASSERT(memberInitializers->valid);
        } else {
          if (si.scope().isSyntheticFunction()) {
            allowArguments = false;
          }
        }

        if (si.scope().isDerivedClassConstructor()) {
          allowSuperCall = true;
        }

        // Found the effective "this" environment, so stop.
        return;
      }
    }

    if (si.scope().hasEnvironment()) {
      envCount++;
    }
  }
}

void ScopeContext::computeThisBinding(const InputScope& scope) {
  // Inspect the scope-chain.
  for (InputScopeIter si(scope); si; si++) {
    if (si.kind() == ScopeKind::Module) {
      thisBinding = ThisBinding::Module;
      return;
    }

    if (si.kind() == ScopeKind::Function) {
      // Arrow functions don't have their own `this` binding.
      if (si.scope().isArrow()) {
        continue;
      }

      // Derived class constructors (and their nested arrow functions and evals)
      // use ThisBinding::DerivedConstructor, which ensures TDZ checks happen
      // when accessing |this|.
      if (si.scope().isDerivedClassConstructor()) {
        thisBinding = ThisBinding::DerivedConstructor;
      } else {
        thisBinding = ThisBinding::Function;
      }

      return;
    }
  }

  thisBinding = ThisBinding::Global;
}

void ScopeContext::computeInScope(const InputScope& enclosingScope) {
  for (InputScopeIter si(enclosingScope); si; si++) {
    if (si.kind() == ScopeKind::ClassBody) {
      inClass = true;
    }

    if (si.kind() == ScopeKind::With) {
      inWith = true;
    }
  }
}

void ScopeContext::cacheEnclosingScope(const InputScope& enclosingScope) {
  if (enclosingScope.isNull()) {
    return;
  }

  enclosingScopeEnvironmentChainLength =
      enclosingScope.environmentChainLength();
  enclosingScopeKind = enclosingScope.kind();

  if (enclosingScopeKind == ScopeKind::Function) {
    enclosingScopeIsArrow = enclosingScope.isArrow();
  }

  enclosingScopeHasEnvironment = enclosingScope.hasEnvironment();

#ifdef DEBUG
  hasNonSyntacticScopeOnChain =
      enclosingScope.hasOnChain(ScopeKind::NonSyntactic);

  // This computes a general answer for the query "does the enclosing scope
  // have a function scope that needs a home object?", but it's only asserted
  // if the parser parses eval body that contains `super` that needs a home
  // object.
  for (InputScopeIter si(enclosingScope); si; si++) {
    if (si.kind() == ScopeKind::Function) {
      if (si.scope().isArrow()) {
        continue;
      }
      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 setCatchAll = [&](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()) {
            setCatchAll(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) {
            setCatchAll(NameLocation::Global(BindingKind::Var));
            return;
          }
        }

        setCatchAll(NameLocation::Dynamic());
        return;

      case ScopeKind::Global:
        setCatchAll(NameLocation::Global(BindingKind::Var));
        return;

      case ScopeKind::With:
      case ScopeKind::NonSyntactic:
        setCatchAll(NameLocation::Dynamic());
        return;

      case ScopeKind::WasmInstance:
      case ScopeKind::WasmFunction:
        MOZ_CRASH("No direct eval inside wasm functions");
    }
  }

  MOZ_CRASH("Malformed scope chain");
}

// Given an input scope, possibly refine this to a more precise scope.
// This is used during eval in the debugger to provide the appropriate scope and
// ThisBinding kind and environment, which is key to making private field eval
// work correctly.
//
// The trick here is that an eval may have a non-syntatic scope but nevertheless
// have an 'interesting' environment which can be traversed to find the
// appropriate scope the the eval to function as desired. See the diagram below.
//
// Eval Scope    Eval Env         Frame Env    Frame Scope
// ============  =============    =========    =============
//
// NonSyntactic
//    |
//    v
//   null        DebugEnvProxy                 LexicalScope
//                     |                            |
//                     v                            v
//               DebugEnvProxy --> CallObj --> FunctionScope
//                     |              |             |
//                     v              v             v
//                    ...            ...           ...
//
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)) {
    JSObject* env = environment;
    while (env) {
      // Look at target of any DebugEnvironmentProxy, but be sure to use
      // enclosingEnvironment() of the proxy itself.
      JSObject* unwrapped = env;
      if (env->is<DebugEnvironmentProxy>()) {
        unwrapped = &env->as<DebugEnvironmentProxy>().environment();
#ifdef DEBUG
        enclosingEnvironmentIsDebugProxy_ = true;
#endif
      }

      if (unwrapped->is<CallObject>()) {
        JSFunction* callee = &unwrapped->as<CallObject>().callee();
        return InputScope(callee->nonLazyScript()->bodyScope());
      }

      env = env->enclosingEnvironment();
      effectiveScopeHops++;
    }
  }

  return scope;
}

static uint32_t DepthOfNearestVarScopeForDirectEval(const InputScope& scope) {
  uint32_t depth = 0;
  if (scope.isNull()) {
    return depth;
  }
  for (InputScopeIter si(scope); si; si++) {
    depth++;
    switch (si.scope().kind()) {
      case ScopeKind::Function:
      case ScopeKind::FunctionBodyVar:
      case ScopeKind::Global:
      case ScopeKind::NonSyntactic:
        return depth;
      default:
        break;
    }
  }
  return depth;
}

bool ScopeContext::cacheEnclosingScopeBindingForEval(
    FrontendContext* fc, 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++) {
        switch (bi.kind()) {
          case BindingKind::Let: {
            // Annex B.3.5 allows redeclaring simple (non-destructured)
            // 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(
                      fc, parserAtoms, input.atomCache, binding, kind)) {
                return false;
              }
            }
            break;
          }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
          // TODO: Optimize cache population for `using` bindings. (Bug 1899502)
          case BindingKind::Using:
            break;
#endif
          case BindingKind::Const: {
            InputName binding(scope_ref, bi.name());
            if (!addToEnclosingLexicalBindingCache(
                    fc, parserAtoms, input.atomCache, binding,
                    EnclosingLexicalBindingKind::Const)) {
              return false;
            }
            break;
          }

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

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

          case BindingKind::Import:
          case BindingKind::FormalParameter:
          case BindingKind::Var:
          case BindingKind::NamedLambdaCallee:
            break;
        }
      }
      return true;
    });
    if (!success) {
      return false;
    }

    if (++depth == varScopeDepth) {
      break;
    }
  }

  return true;
}

bool ScopeContext::addToEnclosingLexicalBindingCache(
    FrontendContext* fc, ParserAtomsTable& parserAtoms,
    CompilationAtomCache& atomCache, InputName& name,
    EnclosingLexicalBindingKind kind) {
  TaggedParserAtomIndex parserName =
      name.internInto(fc, 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(fc);
      return false;
    }
  }

  return true;
}

static bool IsPrivateField(Scope*, JSAtom* atom) {
  MOZ_ASSERT(atom->length() > 0);

  JS::AutoCheckCannotGC nogc;
  if (atom->hasLatin1Chars()) {
    return atom->latin1Chars(nogc)[0] == '#';
  }

  return atom->twoByteChars(nogc)[0] == '#';
}

static bool IsPrivateField(ScopeStencilRef& scope, TaggedParserAtomIndex atom) {
  if (atom.isParserAtomIndex()) {
    const CompilationStencil& context = scope.context_;
    ParserAtom* parserAtom = context.parserAtomData[atom.toParserAtomIndex()];
    return parserAtom->isPrivateName();
  }

#ifdef DEBUG
  if (atom.isWellKnownAtomId()) {
    const auto& info = GetWellKnownAtomInfo(atom.toWellKnownAtomId());
    // #constructor is a well-known term, but it is invalid private name.
    MOZ_ASSERT(!(info.length > 1 && info.content[0] == '#'));
  } else if (atom.isLength2StaticParserString()) {
    char content[2];
    ParserAtomsTable::getLength2Content(atom.toLength2StaticParserString(),
                                        content);
    // # character is not part of the allowed character of static strings.
    MOZ_ASSERT(content[0] != '#');
  }
#endif

  return false;
}

static bool IsPrivateField(const FakeStencilGlobalScope&,
                           TaggedParserAtomIndex) {
  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("No private fields on empty global.");
}

bool ScopeContext::cachePrivateFieldsForEval(FrontendContext* fc,
                                             CompilationInput& input,
                                             JSObject* enclosingEnvironment,
                                             const InputScope& effectiveScope,
                                             ParserAtomsTable& parserAtoms) {
  effectiveScopePrivateFieldCache_.emplace();

  // We compute an environment coordinate relative to the effective scope
  // environment. In order to safely consume these environment coordinates,
  // we re-map them to include the hops to get the to the effective scope:
  // see EmitterScope::lookupPrivate
  uint32_t hops = effectiveScopeHops;
  for (InputScopeIter si(effectiveScope); si; si++) {
    if (si.scope().kind() == ScopeKind::ClassBody) {
      uint32_t slots = 0;
      bool success = si.scope().match([&](auto& scope_ref) {
        for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
          if (bi.kind() == BindingKind::PrivateMethod ||
              (bi.kind() == BindingKind::Synthetic &&
               IsPrivateField(scope_ref, bi.name()))) {
            InputName binding(scope_ref, bi.name());
            auto parserName =
                binding.internInto(fc, parserAtoms, input.atomCache);
            if (!parserName) {
              return false;
            }

            NameLocation loc = NameLocation::DebugEnvironmentCoordinate(
                bi.kind(), hops, slots);

            if (!effectiveScopePrivateFieldCache_->put(parserName, loc)) {
              ReportOutOfMemory(fc);
              return false;
            }
          }
          slots++;
        }
        return true;
      });
      if (!success) {
        return false;
      }
    }

    // Hops is only consumed by GetAliasedDebugVar, which uses this to
    // traverse the debug environment chain. See the [SMDOC] for Debug
    // Environment Chain, which explains why we don't check for
    // isEnvironment when computing hops here (basically, debug proxies
    // pretend all scopes have environments, even if they were actually
    // optimized out).
    hops++;
  }

  return true;
}

#ifdef DEBUG
static bool NameIsOnEnvironment(FrontendContext* fc,
                                ParserAtomsTable& parserAtoms,
                                CompilationAtomCache& atomCache,
                                InputScope& scope, TaggedParserAtomIndex name) {
  JSAtom* jsname = nullptr;
  return scope.match([&](auto& scope_ref) {
    if (std::is_same_v<decltype(scope_ref), FakeStencilGlobalScope&>) {
      // This condition is added to handle the FakeStencilGlobalScope which is
      // used to emulate the global object when delazifying while executing, and
      // which is not provided by the Stencil.
      return true;
    }
    for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
      // If found, the name must already be on the environment or an import,
      // or else there is a bug in the closed-over name analysis in the
      // Parser.
      InputName binding(scope_ref, bi.name());
      if (binding.isEqualTo(fc, parserAtoms, atomCache, name, &jsname)) {
        BindingLocation::Kind kind = bi.location().kind();

        if (bi.hasArgumentSlot()) {
          // The following is equivalent to
          // functionScope.script()->functionAllowsParameterRedeclaration()
          if (scope.hasMappedArgsObj()) {
            // Check for duplicate positional formal parameters.
            using InputBindingIter = decltype(bi);
            for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
                 bi2++) {
              InputName binding2(scope_ref, bi2.name());
              if (binding2.isEqualTo(fc, parserAtoms, atomCache, name,
                                     &jsname)) {
                kind = bi2.location().kind();
              }
            }
          }
        }

        return kind == BindingLocation::Kind::Global ||
               kind == BindingLocation::Kind::Environment ||
               kind == BindingLocation::Kind::Import;
      }
    }

    // If not found, assume it's on the global or dynamically accessed.
    return true;
  });
}
#endif

NameLocation ScopeContext::searchInEnclosingScope(FrontendContext* fc,
                                                  CompilationInput& input,
                                                  ParserAtomsTable& parserAtoms,
                                                  TaggedParserAtomIndex name) {
  MOZ_ASSERT(input.target ==
                 CompilationInput::CompilationTarget::Delazification ||
             input.target == CompilationInput::CompilationTarget::Eval);

  MOZ_ASSERT(scopeCache);
  if (scopeCacheGen != scopeCache->getCurrentGeneration()) {
    return searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
  }

#ifdef DEBUG
  // Catch assertion failures in the NoCache variant before looking at the
  // cached content.
  NameLocation expect =
      searchInEnclosingScopeNoCache(fc, input, parserAtoms, name);
#endif

  NameLocation found =
      searchInEnclosingScopeWithCache(fc, input, parserAtoms, name);
  MOZ_ASSERT(expect == found);
  return found;
}

NameLocation ScopeContext::searchInEnclosingScopeWithCache(
    FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
    TaggedParserAtomIndex name) {
  MOZ_ASSERT(input.target ==
                 CompilationInput::CompilationTarget::Delazification ||
             input.target == CompilationInput::CompilationTarget::Eval);

  // Generic atom of the looked up name.
  GenericAtom genName(fc, parserAtoms, input.atomCache, name);
  mozilla::Maybe<NameLocation> found;

  // Number of enclosing scope we walked over.
  uint8_t hops = 0;

  for (InputScopeIter si(input.enclosingScope); si; si++) {
    MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
                                   name));

    // If the result happens to be in the cached content of the scope that we
    // are iterating over, then return it.
    si.scope().match([&](auto& scope_ref) {
      using BindingMapPtr =
          decltype(scopeCache->lookupScope(scope_ref, scopeCacheGen));
      BindingMapPtr bindingMapPtr =
          scopeCache->lookupScope(scope_ref, scopeCacheGen);
      MOZ_ASSERT(bindingMapPtr);

      auto& bindingMap = *bindingMapPtr;
      if (bindingMap.catchAll.isSome()) {
        found = bindingMap.catchAll;
        return;
      }

      // The scope_ref is given as argument to know where to lookup the key
      // index of the hash table if the names have to be compared.
      using Lookup = typename std::remove_pointer_t<BindingMapPtr>::Lookup;
      Lookup ctxName(scope_ref, genName);
      auto ptr = bindingMap.hashMap.lookup(ctxName);
      if (!ptr) {
        return;
      }

      found.emplace(ptr->value());
    });

    if (found.isSome()) {
      // Cached entries do not store the number of hops, as it might be reused
      // by multiple inner functions, which might different number of hops.
      found = found.map([&hops](NameLocation loc) {
        if (loc.kind() != NameLocation::Kind::EnvironmentCoordinate) {
          return loc;
        }
        return loc.addHops(hops);
      });
      return found.value();
    }

    bool hasEnv = si.hasSyntacticEnvironment();

    if (hasEnv) {
      MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
      hops++;
    }
  }

  MOZ_CRASH("Malformed scope chain");
}

NameLocation ScopeContext::searchInEnclosingScopeNoCache(
    FrontendContext* fc, CompilationInput& input, ParserAtomsTable& parserAtoms,
    TaggedParserAtomIndex name) {
  MOZ_ASSERT(input.target ==
                 CompilationInput::CompilationTarget::Delazification ||
             input.target == CompilationInput::CompilationTarget::Eval);

  // Cached JSAtom equivalent of the TaggedParserAtomIndex `name` argument.
  JSAtom* jsname = nullptr;

  // NameLocation which contains relative locations to access `name`.
  mozilla::Maybe<NameLocation> result;

  // Number of enclosing scoep we walked over.
  uint8_t hops = 0;

  for (InputScopeIter si(input.enclosingScope); si; si++) {
    MOZ_ASSERT(NameIsOnEnvironment(fc, parserAtoms, input.atomCache, si.scope(),
                                   name));

    bool hasEnv = si.hasSyntacticEnvironment();
    switch (si.kind()) {
      case ScopeKind::Function:
        if (hasEnv) {
          if (si.scope().funHasExtensibleScope()) {
            return NameLocation::Dynamic();
          }

          si.scope().match([&](auto& scope_ref) {
            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
              InputName binding(scope_ref, bi.name());
              if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
                                     &jsname)) {
                continue;
              }

              BindingLocation bindLoc = bi.location();
              // hasMappedArgsObj == script.functionAllowsParameterRedeclaration
              if (bi.hasArgumentSlot() && si.scope().hasMappedArgsObj()) {
                // Check for duplicate positional formal parameters.
                using InputBindingIter = decltype(bi);
                for (InputBindingIter bi2(bi); bi2 && bi2.hasArgumentSlot();
                     bi2++) {
                  if (bi.name() == bi2.name()) {
                    bindLoc = bi2.location();
                  }
                }
              }

              MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
              result.emplace(NameLocation::EnvironmentCoordinate(
                  bi.kind(), hops, bindLoc.slot()));
              return;
            }
          });
        }
        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) {
            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
              InputName binding(scope_ref, bi.name());
              if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
                                     &jsname)) {
                continue;
              }

              // The name must already have been marked as closed
              // over. If this assertion is hit, there is a bug in the
              // name analysis.
              BindingLocation bindLoc = bi.location();
              MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
              result.emplace(NameLocation::EnvironmentCoordinate(
                  bi.kind(), hops, bindLoc.slot()));
              return;
            }
          });
        }
        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) {
            for (auto bi = InputBindingIter(scope_ref); bi; bi++) {
              InputName binding(scope_ref, bi.name());
              if (!binding.isEqualTo(fc, parserAtoms, input.atomCache, name,
                                     &jsname)) {
                continue;
              }

              BindingLocation bindLoc = bi.location();

              // Imports are on the environment but are indirect
              // bindings and must be accessed dynamically instead of
              // using an EnvironmentCoordinate.
              if (bindLoc.kind() == BindingLocation::Kind::Import) {
                MOZ_ASSERT(si.kind() == ScopeKind::Module);
                result.emplace(NameLocation::Import());
                return;
              }

              MOZ_ASSERT(bindLoc.kind() == BindingLocation::Kind::Environment);
              result.emplace(NameLocation::EnvironmentCoordinate(
                  bi.kind(), hops, bindLoc.slot()));
              return;
            }
          });
        }
        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) {
            return NameLocation::Global(BindingKind::Var);
          }
        }
        return NameLocation::Dynamic();

      case ScopeKind::Global:
        return NameLocation::Global(BindingKind::Var);

      case ScopeKind::With:
      case ScopeKind::NonSyntactic:
        return NameLocation::Dynamic();

      case ScopeKind::WasmInstance:
      case ScopeKind::WasmFunction:
        MOZ_CRASH("No direct eval inside wasm functions");
    }

    if (result.isSome()) {
      return result.value();
    }

    if (hasEnv) {
      MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
      hops++;
    }
  }

  MOZ_CRASH("Malformed scope chain");
}

mozilla::Maybe<ScopeContext::EnclosingLexicalBindingKind>
ScopeContext::lookupLexicalBindingInEnclosingScope(TaggedParserAtomIndex name) {
  auto p = enclosingLexicalBindingCache_->lookup(name);
  if (!p) {
    return mozilla::Nothing();
  }

  return mozilla::Some(p->value());
}

bool ScopeContext::effectiveScopePrivateFieldCacheHas(
    TaggedParserAtomIndex name) {
  return effectiveScopePrivateFieldCache_->has(name);
}

mozilla::Maybe<NameLocation> ScopeContext::getPrivateFieldLocation(
    TaggedParserAtomIndex name) {
  // The locations returned by this method are only valid for
  // traversing debug environments.
  //
  // See the comment in cachePrivateFieldsForEval
  MOZ_ASSERT(enclosingEnvironmentIsDebugProxy_);
  auto p = effectiveScopePrivateFieldCache_->lookup(name);
  if (!p) {
    return mozilla::Nothing();
  }
  return mozilla::Some(p->value());
}

bool CompilationInput::initScriptSource(FrontendContext* fc) {
  source = do_AddRef(fc->getAllocator()->new_<ScriptSource>());
  if (!source) {
    return false;
  }

  return source->initFromOptions(fc, options);
}

bool CompilationInput::initForStandaloneFunctionInNonSyntacticScope(
    FrontendContext* fc, Handle<Scope*> functionEnclosingScope) {
  MOZ_ASSERT(!functionEnclosingScope->as<GlobalScope>().isSyntactic());

  target = CompilationTarget::StandaloneFunctionInNonSyntacticScope;
  if (!initScriptSource(fc)) {
    return false;
  }
  enclosingScope = InputScope(functionEnclosingScope);
  return true;
}

FunctionSyntaxKind CompilationInput::functionSyntaxKind() const {
  if (functionFlags().isClassConstructor()) {
    if (functionFlags().hasBaseScript() && isDerivedClassConstructor()) {
      return FunctionSyntaxKind::DerivedClassConstructor;
    }
    return FunctionSyntaxKind::ClassConstructor;
  }
  if (functionFlags().isMethod()) {
    if (functionFlags().hasBaseScript() && isSyntheticFunction()) {
      // return FunctionSyntaxKind::FieldInitializer;
      MOZ_ASSERT_UNREACHABLE(
          "Lazy parsing of class field initializers not supported (yet)");
    }
    return FunctionSyntaxKind::Method;
  }
  if (functionFlags().isGetter()) {
    return FunctionSyntaxKind::Getter;
  }
  if (functionFlags().isSetter()) {
    return FunctionSyntaxKind::Setter;
  }
  if (functionFlags().isArrow()) {
    return FunctionSyntaxKind::Arrow;
  }
  return FunctionSyntaxKind::Statement;
}

bool CompilationInput::internExtraBindings(FrontendContext* fc,
                                           ParserAtomsTable& parserAtoms) {
  MOZ_ASSERT(hasExtraBindings());

  for (auto& bindingInfo : *maybeExtraBindings_) {
    if (bindingInfo.isShadowed) {
      continue;
    }

    const char* chars = bindingInfo.nameChars.get();
    auto index = parserAtoms.internUtf8(
        fc, reinterpret_cast<const mozilla::Utf8Unit*>(chars), strlen(chars));
    if (!index) {
      return false;
    }

    bindingInfo.nameIndex = index;
  }

  return true;
}

void InputScope::trace(JSTracer* trc) {
  using ScopePtr = Scope*;
  if (scope_.is<ScopePtr>()) {
    ScopePtr* ptrAddr = &scope_.as<ScopePtr>();
    TraceNullableRoot(trc, ptrAddr, "compilation-input-scope");
  }
}

void InputScript::trace(JSTracer* trc) {
  using ScriptPtr = BaseScript*;
  if (script_.is<ScriptPtr>()) {
    ScriptPtr* ptrAddr = &script_.as<ScriptPtr>();
    TraceNullableRoot(trc, ptrAddr, "compilation-input-lazy");
  }
}

void CompilationInput::trace(JSTracer* trc) {
  atomCache.trace(trc);
  lazy_.trace(trc);
  enclosingScope.trace(trc);
}

bool CompilationSyntaxParseCache::init(FrontendContext* fc, LifoAlloc& alloc,
                                       ParserAtomsTable& parseAtoms,
                                       CompilationAtomCache& atomCache,
                                       const InputScript& lazy) {
  if (!copyFunctionInfo(fc, parseAtoms, atomCache, lazy)) {
    return false;
  }
  bool success = lazy.raw().match([&](auto& ref) {
    if (!copyScriptInfo(fc, alloc, parseAtoms, atomCache, ref)) {
      return false;
    }
    if (!copyClosedOverBindings(fc, alloc, parseAtoms, atomCache, ref)) {
      return false;
    }
    return true;
  });
  if (!success) {
    return false;
  }
#ifdef DEBUG
  isInitialized = true;
#endif
  return true;
}

bool CompilationSyntaxParseCache::copyFunctionInfo(
    FrontendContext* fc, ParserAtomsTable& parseAtoms,
    CompilationAtomCache& atomCache, const InputScript& lazy) {
  InputName name = lazy.displayAtom();
  if (!name.isNull()) {
    displayAtom_ = name.internInto(fc, parseAtoms, atomCache);
    if (!displayAtom_) {
      return false;
    }
  }

  funExtra_.immutableFlags = lazy.immutableFlags();
  funExtra_.extent = lazy.extent();
  if (funExtra_.useMemberInitializers()) {
    funExtra_.setMemberInitializers(lazy.getMemberInitializers());
  }

  return true;
}

bool CompilationSyntaxParseCache::copyScriptInfo(
    FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
    CompilationAtomCache& atomCache, BaseScript* lazy) {
  using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
  using ScriptDataSpan = mozilla::Span<ScriptStencil>;
  using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
  cachedGCThings_ = GCThingsSpan(nullptr);
  cachedScriptData_ = ScriptDataSpan(nullptr);
  cachedScriptExtra_ = ScriptExtraSpan(nullptr);

  auto gcthings = lazy->gcthings();
  size_t length = gcthings.Length();
  if (length == 0) {
    return true;
  }

  // Reduce the length to the first element which is not a function.
  for (size_t i = 0; i < length; i++) {
    gc::Cell* cell = gcthings[i].asCell();
    if (!cell || !cell->is<JSObject>()) {
      length = i;
      break;
    }
    MOZ_ASSERT(cell->as<JSObject>()->is<JSFunction>());
  }

  TaggedScriptThingIndex* gcThingsData =
      alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
  ScriptStencil* scriptData =
      alloc.newArrayUninitialized<ScriptStencil>(length);
  ScriptStencilExtra* scriptExtra =
      alloc.newArrayUninitialized<ScriptStencilExtra>(length);
  if (!gcThingsData || !scriptData || !scriptExtra) {
    ReportOutOfMemory(fc);
    return false;
  }

  for (size_t i = 0; i < length; i++) {
    gc::Cell* cell = gcthings[i].asCell();
    JSFunction* fun = &cell->as<JSObject>()->as<JSFunction>();
    gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
    new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
    ScriptStencil& data = scriptData[i];
    new (mozilla::KnownNotNull, &scriptExtra[i]) ScriptStencilExtra();
    ScriptStencilExtra& extra = scriptExtra[i];

    if (fun->fullDisplayAtom()) {
      TaggedParserAtomIndex displayAtom =
          parseAtoms.internJSAtom(fc, atomCache, fun->fullDisplayAtom());
      if (!displayAtom) {
        return false;
      }
      data.functionAtom = displayAtom;
    }
    data.functionFlags = fun->flags();

    BaseScript* lazy = fun->baseScript();
    extra.immutableFlags = lazy->immutableFlags();
    extra.extent = lazy->extent();

    // Info derived from parent compilation should not be set yet for our inner
    // lazy functions. Instead that info will be updated when we finish our
    // compilation.
    MOZ_ASSERT(lazy->hasEnclosingScript());
  }

  cachedGCThings_ = GCThingsSpan(gcThingsData, length);
  cachedScriptData_ = ScriptDataSpan(scriptData, length);
  cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
  return true;
}

bool CompilationSyntaxParseCache::copyScriptInfo(
    FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
    CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
  using GCThingsSpan = mozilla::Span<TaggedScriptThingIndex>;
  using ScriptDataSpan = mozilla::Span<ScriptStencil>;
  using ScriptExtraSpan = mozilla::Span<ScriptStencilExtra>;
  cachedGCThings_ = GCThingsSpan(nullptr);
  cachedScriptData_ = ScriptDataSpan(nullptr);
  cachedScriptExtra_ = ScriptExtraSpan(nullptr);

  size_t offset = lazy.scriptData().gcThingsOffset.index;
  size_t length = lazy.scriptData().gcThingsLength;
  if (length == 0) {
    return true;
  }

  // Reduce the length to the first element which is not a function.
  for (size_t i = offset; i < offset + length; i++) {
    if (!lazy.context_.gcThingData[i].isFunction()) {
      length = i - offset;
      break;
    }
  }

  TaggedScriptThingIndex* gcThingsData =
      alloc.newArrayUninitialized<TaggedScriptThingIndex>(length);
  ScriptStencil* scriptData =
      alloc.newArrayUninitialized<ScriptStencil>(length);
  ScriptStencilExtra* scriptExtra =
      alloc.newArrayUninitialized<ScriptStencilExtra>(length);
  if (!gcThingsData || !scriptData || !scriptExtra) {
    ReportOutOfMemory(fc);
    return false;
  }

  for (size_t i = 0; i < length; i++) {
    ScriptStencilRef inner{lazy.context_,
                           lazy.context_.gcThingData[i + offset].toFunction()};
    gcThingsData[i] = TaggedScriptThingIndex(ScriptIndex(i));
    new (mozilla::KnownNotNull, &scriptData[i]) ScriptStencil();
    ScriptStencil& data = scriptData[i];
    ScriptStencilExtra& extra = scriptExtra[i];

    InputName name{inner, inner.scriptData().functionAtom};
    if (!name.isNull()) {
      auto displayAtom = name.internInto(fc, parseAtoms, atomCache);
      if (!displayAtom) {
        return false;
      }
      data.functionAtom = displayAtom;
    }
    data.functionFlags = inner.scriptData().functionFlags;

    extra = inner.scriptExtra();
  }

  cachedGCThings_ = GCThingsSpan(gcThingsData, length);
  cachedScriptData_ = ScriptDataSpan(scriptData, length);
  cachedScriptExtra_ = ScriptExtraSpan(scriptExtra, length);
  return true;
}

bool CompilationSyntaxParseCache::copyClosedOverBindings(
    FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
    CompilationAtomCache& atomCache, BaseScript* lazy) {
  using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
  closedOverBindings_ = ClosedOverBindingsSpan(nullptr);

  // The gcthings() array contains the inner function list followed by the
  // closed-over bindings data. Skip the inner function list, as it is already
  // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
  size_t start = cachedGCThings_.Length();
  auto gcthings = lazy->gcthings();
  size_t length = gcthings.Length();
  MOZ_ASSERT(start <= length);
  if (length - start == 0) {
    return true;
  }

  TaggedParserAtomIndex* closedOverBindings =
      alloc.newArrayUninitialized<TaggedParserAtomIndex>(length - start);
  if (!closedOverBindings) {
    ReportOutOfMemory(fc);
    return false;
  }

  for (size_t i = start; i < length; i++) {
    gc::Cell* cell = gcthings[i].asCell();
    if (!cell) {
      closedOverBindings[i - start] = TaggedParserAtomIndex::null();
      continue;
    }

    MOZ_ASSERT(cell->as<JSString>()->isAtom());

    auto name = static_cast<JSAtom*>(cell);
    auto parserAtom = parseAtoms.internJSAtom(fc, atomCache, name);
    if (!parserAtom) {
      return false;
    }

    closedOverBindings[i - start] = parserAtom;
  }

  closedOverBindings_ =
      ClosedOverBindingsSpan(closedOverBindings, length - start);
  return true;
}

bool CompilationSyntaxParseCache::copyClosedOverBindings(
    FrontendContext* fc, LifoAlloc& alloc, ParserAtomsTable& parseAtoms,
    CompilationAtomCache& atomCache, const ScriptStencilRef& lazy) {
  using ClosedOverBindingsSpan = mozilla::Span<TaggedParserAtomIndex>;
  closedOverBindings_ = ClosedOverBindingsSpan(nullptr);

  // The gcthings array contains the inner function list followed by the
  // closed-over bindings data. Skip the inner function list, as it is already
  // cached in cachedGCThings_. See also: BaseScript::CreateLazy.
  size_t offset = lazy.scriptData().gcThingsOffset.index;
  size_t length = lazy.scriptData().gcThingsLength;
  size_t start = cachedGCThings_.Length();
  MOZ_ASSERT(start <= length);
  if (length - start == 0) {
    return true;
  }
  length -= start;
  start += offset;

  // Atoms from the lazy.context (CompilationStencil) are not registered in the
  // the parseAtoms table. Thus we create a new span which will contain all the
  // interned atoms.
  TaggedParserAtomIndex* closedOverBindings =
      alloc.newArrayUninitialized<TaggedParserAtomIndex>(length);
  if (!closedOverBindings) {
    ReportOutOfMemory(fc);
    return false;
  }

  for (size_t i = 0; i < length; i++) {
    auto gcThing = lazy.context_.gcThingData[i + start];
    if (gcThing.isNull()) {
      closedOverBindings[i] = TaggedParserAtomIndex::null();
      continue;
    }

    MOZ_ASSERT(gcThing.isAtom());
    InputName name(lazy, gcThing.toAtom());
    auto parserAtom = name.internInto(fc, parseAtoms, atomCache);
    if (!parserAtom) {
      return false;
    }

    closedOverBindings[i] = parserAtom;
  }

  closedOverBindings_ = ClosedOverBindingsSpan(closedOverBindings, length);
  return true;
}

template <typename T>
PreAllocateableGCArray<T>::~PreAllocateableGCArray() {
  if (elems_) {
    js_free(elems_);
    elems_ = nullptr;
  }
}

template <typename T>
bool PreAllocateableGCArray<T>::allocate(size_t length) {
  MOZ_ASSERT(empty());

  length_ = length;

  if (isInline()) {
    inlineElem_ = nullptr;
    return true;
  }

  elems_ = reinterpret_cast<T*>(js_calloc(sizeof(T) * length_));
  if (!elems_) {
    return false;
  }

  return true;
}

template <typename T>
bool PreAllocateableGCArray<T>::allocateWith(T init, size_t length) {
  MOZ_ASSERT(empty());

  length_ = length;

  if (isInline()) {
    inlineElem_ = init;
    return true;
  }

  elems_ = reinterpret_cast<T*>(js_malloc(sizeof(T) * length_));
  if (!elems_) {
    return false;
  }

  std::fill(elems_, elems_ + length_, init);
  return true;
}

template <typename T>
void PreAllocateableGCArray<T>::steal(Preallocated&& buffer) {
  MOZ_ASSERT(empty());

  length_ = buffer.length_;
  buffer.length_ = 0;

  if (isInline()) {
    inlineElem_ = nullptr;
    return;
  }

  elems_ = reinterpret_cast<T*>(buffer.elems_);
  buffer.elems_ = nullptr;

#ifdef DEBUG
  for (size_t i = 0; i < length_; i++) {
    MOZ_ASSERT(elems_[i] == nullptr);
  }
#endif
}

template <typename T>
void PreAllocateableGCArray<T>::trace(JSTracer* trc) {
  if (empty()) {
    return;
  }

  if (isInline()) {
    TraceNullableRoot(trc, &inlineElem_, "PreAllocateableGCArray::inlineElem_");
    return;
  }

  for (size_t i = 0; i < length_; i++) {
    TraceNullableRoot(trc, &elems_[i], "PreAllocateableGCArray::elems_");
  }
}

template <typename T>
PreAllocateableGCArray<T>::Preallocated::~Preallocated() {
  if (elems_) {
    js_free(elems_);
    elems_ = nullptr;
  }
}

template <typename T>
bool PreAllocateableGCArray<T>::Preallocated::allocate(size_t length) {
  MOZ_ASSERT(empty());

  length_ = length;

  if (isInline()) {
    return true;
  }

  elems_ = reinterpret_cast<uintptr_t*>(js_calloc(sizeof(uintptr_t) * length_));
  if (!elems_) {
    return false;
  }

  return true;
}

template struct js::frontend::PreAllocateableGCArray<JSFunction*>;
template struct js::frontend::PreAllocateableGCArray<js::Scope*>;

void CompilationAtomCache::trace(JSTracer* trc) { atoms_.trace(trc); }

void CompilationGCOutput::trace(JSTracer* trc) {
  TraceNullableRoot(trc, &script, "compilation-gc-output-script");
  TraceNullableRoot(trc, &module, "compilation-gc-output-module");
  TraceNullableRoot(trc, &sourceObject, "compilation-gc-output-source");
  functions.trace(trc);
  scopes.trace(trc);
}

RegExpObject* RegExpStencil::createRegExp(
    JSContext* cx, const CompilationAtomCache& atomCache) const {
  Rooted<JSAtom*> atom(cx, atomCache.getExistingAtomAt(cx, atom_));
  return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
}

RegExpObject* RegExpStencil::createRegExpAndEnsureAtom(
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=88 H=93 G=90

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.