summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ParseContext.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/frontend/ParseContext.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/src/frontend/ParseContext.cpp720
1 files changed, 720 insertions, 0 deletions
diff --git a/js/src/frontend/ParseContext.cpp b/js/src/frontend/ParseContext.cpp
new file mode 100644
index 0000000000..498dacd0c3
--- /dev/null
+++ b/js/src/frontend/ParseContext.cpp
@@ -0,0 +1,720 @@
+/* -*- 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/ParseContext-inl.h"
+
+#include "js/friend/ErrorMessages.h" // JSMSG_*
+
+#include "vm/EnvironmentObject-inl.h"
+
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
+
+namespace js {
+namespace frontend {
+
+using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
+using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
+
+const char* DeclarationKindString(DeclarationKind kind) {
+ switch (kind) {
+ case DeclarationKind::PositionalFormalParameter:
+ case DeclarationKind::FormalParameter:
+ return "formal parameter";
+ case DeclarationKind::CoverArrowParameter:
+ return "cover arrow parameter";
+ case DeclarationKind::Var:
+ return "var";
+ case DeclarationKind::Let:
+ return "let";
+ case DeclarationKind::Const:
+ return "const";
+ case DeclarationKind::Class:
+ return "class";
+ case DeclarationKind::Import:
+ return "import";
+ case DeclarationKind::BodyLevelFunction:
+ case DeclarationKind::ModuleBodyLevelFunction:
+ case DeclarationKind::LexicalFunction:
+ case DeclarationKind::SloppyLexicalFunction:
+ return "function";
+ case DeclarationKind::VarForAnnexBLexicalFunction:
+ return "annex b var";
+ case DeclarationKind::SimpleCatchParameter:
+ case DeclarationKind::CatchParameter:
+ return "catch parameter";
+ case DeclarationKind::PrivateName:
+ return "private name";
+ }
+
+ MOZ_CRASH("Bad DeclarationKind");
+}
+
+bool DeclarationKindIsVar(DeclarationKind kind) {
+ return kind == DeclarationKind::Var ||
+ kind == DeclarationKind::BodyLevelFunction ||
+ kind == DeclarationKind::VarForAnnexBLexicalFunction;
+}
+
+bool DeclarationKindIsParameter(DeclarationKind kind) {
+ return kind == DeclarationKind::PositionalFormalParameter ||
+ kind == DeclarationKind::FormalParameter;
+}
+
+bool UsedNameTracker::noteUse(JSContext* cx, const ParserAtom* name,
+ NameVisibility visibility, uint32_t scriptId,
+ uint32_t scopeId,
+ mozilla::Maybe<TokenPos> tokenPosition) {
+ if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) {
+ if (!p->value().noteUsedInScope(scriptId, scopeId)) {
+ return false;
+ }
+ } else {
+ // We need a token position precisely where we have private visibility.
+ MOZ_ASSERT(tokenPosition.isSome() ==
+ (visibility == NameVisibility::Private));
+
+ if (visibility == NameVisibility::Private) {
+ // We have seen at least one private name
+ hasPrivateNames_ = true;
+ }
+
+ UsedNameInfo info(cx, visibility, tokenPosition);
+
+ if (!info.noteUsedInScope(scriptId, scopeId)) {
+ return false;
+ }
+ if (!map_.add(p, name, std::move(info))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool UsedNameTracker::getUnboundPrivateNames(
+ Vector<UnboundPrivateName, 8>& unboundPrivateNames) {
+ // We never saw any private names, so can just return early
+ if (!hasPrivateNames_) {
+ return true;
+ }
+
+ for (auto iter = map_.iter(); !iter.done(); iter.next()) {
+ // Don't care about public;
+ if (iter.get().value().isPublic()) {
+ continue;
+ }
+
+ // empty list means all bound
+ if (iter.get().value().empty()) {
+ continue;
+ }
+
+ if (!unboundPrivateNames.emplaceBack(iter.get().key(),
+ *iter.get().value().pos())) {
+ return false;
+ }
+ }
+
+ // Return a sorted list in ascendng order of position.
+ auto comparePosition = [](const auto& a, const auto& b) {
+ return a.position < b.position;
+ };
+ std::sort(unboundPrivateNames.begin(), unboundPrivateNames.end(),
+ comparePosition);
+
+ return true;
+}
+
+bool UsedNameTracker::hasUnboundPrivateNames(
+ JSContext* cx, mozilla::Maybe<UnboundPrivateName>& maybeUnboundName) {
+ // We never saw any private names, so can just return early
+ if (!hasPrivateNames_) {
+ return true;
+ }
+
+ Vector<UnboundPrivateName, 8> unboundPrivateNames(cx);
+ if (!getUnboundPrivateNames(unboundPrivateNames)) {
+ return false;
+ }
+
+ if (unboundPrivateNames.empty()) {
+ return true;
+ }
+
+ // GetUnboundPrivateNames returns the list sorted.
+ maybeUnboundName.emplace(unboundPrivateNames[0]);
+ return true;
+}
+
+void UsedNameTracker::UsedNameInfo::resetToScope(uint32_t scriptId,
+ uint32_t scopeId) {
+ while (!uses_.empty()) {
+ Use& innermost = uses_.back();
+ if (innermost.scopeId < scopeId) {
+ break;
+ }
+ MOZ_ASSERT(innermost.scriptId >= scriptId);
+ uses_.popBack();
+ }
+}
+
+void UsedNameTracker::rewind(RewindToken token) {
+ scriptCounter_ = token.scriptId;
+ scopeCounter_ = token.scopeId;
+
+ for (UsedNameMap::Range r = map_.all(); !r.empty(); r.popFront()) {
+ r.front().value().resetToScope(token.scriptId, token.scopeId);
+ }
+}
+
+void ParseContext::Scope::dump(ParseContext* pc) {
+ JSContext* cx = pc->sc()->cx_;
+
+ fprintf(stdout, "ParseScope %p", this);
+
+ fprintf(stdout, "\n decls:\n");
+ for (DeclaredNameMap::Range r = declared_->all(); !r.empty(); r.popFront()) {
+ UniqueChars bytes = QuoteString(cx, r.front().key());
+ if (!bytes) {
+ return;
+ }
+ DeclaredNameInfo& info = r.front().value().wrapped;
+ fprintf(stdout, " %s %s%s\n", DeclarationKindString(info.kind()),
+ bytes.get(), info.closedOver() ? " (closed over)" : "");
+ }
+
+ fprintf(stdout, "\n");
+}
+
+bool ParseContext::Scope::addPossibleAnnexBFunctionBox(ParseContext* pc,
+ FunctionBox* funbox) {
+ if (!possibleAnnexBFunctionBoxes_) {
+ if (!possibleAnnexBFunctionBoxes_.acquire(pc->sc()->cx_)) {
+ return false;
+ }
+ }
+
+ return maybeReportOOM(pc, possibleAnnexBFunctionBoxes_->append(funbox));
+}
+
+bool ParseContext::Scope::propagateAndMarkAnnexBFunctionBoxes(
+ ParseContext* pc) {
+ // Strict mode doesn't have wack Annex B function semantics.
+ if (pc->sc()->strict() || !possibleAnnexBFunctionBoxes_ ||
+ possibleAnnexBFunctionBoxes_->empty()) {
+ return true;
+ }
+
+ if (this == &pc->varScope()) {
+ // Base case: actually declare the Annex B vars and mark applicable
+ // function boxes as Annex B.
+ Maybe<DeclarationKind> redeclaredKind;
+ uint32_t unused;
+ for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
+ bool annexBApplies;
+ if (!pc->computeAnnexBAppliesToLexicalFunctionInInnermostScope(
+ funbox, &annexBApplies)) {
+ return false;
+ }
+ if (annexBApplies) {
+ const ParserName* name = funbox->explicitName()->asName();
+ if (!pc->tryDeclareVar(
+ name, DeclarationKind::VarForAnnexBLexicalFunction,
+ DeclaredNameInfo::npos, &redeclaredKind, &unused)) {
+ return false;
+ }
+
+ MOZ_ASSERT(!redeclaredKind);
+ funbox->isAnnexB = true;
+ }
+ }
+ } else {
+ // Inner scope case: propagate still applicable function boxes to the
+ // enclosing scope.
+ for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
+ bool annexBApplies;
+ if (!pc->computeAnnexBAppliesToLexicalFunctionInInnermostScope(
+ funbox, &annexBApplies)) {
+ return false;
+ }
+ if (annexBApplies) {
+ if (!enclosing()->addPossibleAnnexBFunctionBox(pc, funbox)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool DeclarationKindIsCatchParameter(DeclarationKind kind) {
+ return kind == DeclarationKind::SimpleCatchParameter ||
+ kind == DeclarationKind::CatchParameter;
+}
+
+bool ParseContext::Scope::addCatchParameters(ParseContext* pc,
+ Scope& catchParamScope) {
+ if (pc->useAsmOrInsideUseAsm()) {
+ return true;
+ }
+
+ for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty();
+ r.popFront()) {
+ DeclarationKind kind = r.front().value()->kind();
+ uint32_t pos = r.front().value()->pos();
+ MOZ_ASSERT(DeclarationKindIsCatchParameter(kind));
+ const ParserAtom* name = r.front().key();
+ AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name);
+ MOZ_ASSERT(!p);
+ if (!addDeclaredName(pc, p, name, kind, pos)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ParseContext::Scope::removeCatchParameters(ParseContext* pc,
+ Scope& catchParamScope) {
+ if (pc->useAsmOrInsideUseAsm()) {
+ return;
+ }
+
+ for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty();
+ r.popFront()) {
+ DeclaredNamePtr p = declared_->lookup(r.front().key());
+ MOZ_ASSERT(p);
+
+ // This check is needed because the catch body could have declared
+ // vars, which would have been added to catchParamScope.
+ if (DeclarationKindIsCatchParameter(r.front().value()->kind())) {
+ declared_->remove(p);
+ }
+ }
+}
+
+ParseContext::ParseContext(JSContext* cx, ParseContext*& parent,
+ SharedContext* sc, ErrorReporter& errorReporter,
+ CompilationState& compilationState,
+ Directives* newDirectives, bool isFull)
+ : Nestable<ParseContext>(&parent),
+ traceLog_(sc->cx_,
+ isFull ? TraceLogger_ParsingFull : TraceLogger_ParsingSyntax,
+ errorReporter),
+ sc_(sc),
+ errorReporter_(errorReporter),
+ innermostStatement_(nullptr),
+ innermostScope_(nullptr),
+ varScope_(nullptr),
+ positionalFormalParameterNames_(cx->frontendCollectionPool()),
+ closedOverBindingsForLazy_(cx->frontendCollectionPool()),
+ innerFunctionIndexesForLazy(cx),
+ newDirectives(newDirectives),
+ lastYieldOffset(NoYieldOffset),
+ lastAwaitOffset(NoAwaitOffset),
+ scriptId_(compilationState.usedNames.nextScriptId()),
+ superScopeNeedsHomeObject_(false) {
+ if (isFunctionBox()) {
+ if (functionBox()->isNamedLambda()) {
+ namedLambdaScope_.emplace(cx, parent, compilationState.usedNames);
+ }
+ functionScope_.emplace(cx, parent, compilationState.usedNames);
+ }
+}
+
+bool ParseContext::init() {
+ if (scriptId_ == UINT32_MAX) {
+ errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
+ return false;
+ }
+
+ JSContext* cx = sc()->cx_;
+
+ if (isFunctionBox()) {
+ // Named lambdas always need a binding for their own name. If this
+ // binding is closed over when we finish parsing the function in
+ // finishFunctionScopes, the function box needs to be marked as
+ // needing a dynamic DeclEnv object.
+ if (functionBox()->isNamedLambda()) {
+ if (!namedLambdaScope_->init(this)) {
+ return false;
+ }
+ AddDeclaredNamePtr p = namedLambdaScope_->lookupDeclaredNameForAdd(
+ functionBox()->explicitName());
+ MOZ_ASSERT(!p);
+ if (!namedLambdaScope_->addDeclaredName(
+ this, p, functionBox()->explicitName(), DeclarationKind::Const,
+ DeclaredNameInfo::npos)) {
+ return false;
+ }
+ }
+
+ if (!functionScope_->init(this)) {
+ return false;
+ }
+
+ if (!positionalFormalParameterNames_.acquire(cx)) {
+ return false;
+ }
+ }
+
+ if (!closedOverBindingsForLazy_.acquire(cx)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ParseContext::computeAnnexBAppliesToLexicalFunctionInInnermostScope(
+ FunctionBox* funbox, bool* annexBApplies) {
+ MOZ_ASSERT(!sc()->strict());
+
+ const ParserName* name = funbox->explicitName()->asName();
+ Maybe<DeclarationKind> redeclaredKind;
+ if (!isVarRedeclaredInInnermostScope(
+ name, DeclarationKind::VarForAnnexBLexicalFunction,
+ &redeclaredKind)) {
+ return false;
+ }
+
+ if (!redeclaredKind && isFunctionBox()) {
+ Scope& funScope = functionScope();
+ if (&funScope != &varScope()) {
+ // Annex B.3.3.1 disallows redeclaring parameter names. In the
+ // presence of parameter expressions, parameter names are on the
+ // function scope, which encloses the var scope. This means the
+ // isVarRedeclaredInInnermostScope call above would not catch this
+ // case, so test it manually.
+ if (DeclaredNamePtr p = funScope.lookupDeclaredName(name)) {
+ DeclarationKind declaredKind = p->value()->kind();
+ if (DeclarationKindIsParameter(declaredKind)) {
+ redeclaredKind = Some(declaredKind);
+ } else {
+ MOZ_ASSERT(FunctionScope::isSpecialName(sc()->cx_, name->toIndex()));
+ }
+ }
+ }
+ }
+
+ // If an early error would have occurred already, this function should not
+ // exhibit Annex B.3.3 semantics.
+ *annexBApplies = !redeclaredKind;
+ return true;
+}
+
+bool ParseContext::isVarRedeclaredInInnermostScope(
+ const ParserName* name, DeclarationKind kind,
+ mozilla::Maybe<DeclarationKind>* out) {
+ uint32_t unused;
+ return tryDeclareVarHelper<DryRunInnermostScopeOnly>(
+ name, kind, DeclaredNameInfo::npos, out, &unused);
+}
+
+bool ParseContext::isVarRedeclaredInEval(const ParserName* name,
+ DeclarationKind kind,
+ Maybe<DeclarationKind>* out) {
+ MOZ_ASSERT(out);
+ MOZ_ASSERT(DeclarationKindIsVar(kind));
+ MOZ_ASSERT(sc()->isEvalContext());
+
+ // TODO-Stencil: After scope snapshotting, this can be done away with.
+ JSAtom* nameAtom = name->toJSAtom(sc()->cx_, sc()->stencil().input.atomCache);
+ if (!nameAtom) {
+ return false;
+ }
+
+ // In the case of eval, we also need to check enclosing VM scopes to see
+ // if the var declaration is allowed in the context.
+ js::Scope* enclosingScope = sc()->stencil().input.enclosingScope;
+ js::Scope* varScope = EvalScope::nearestVarScopeForDirectEval(enclosingScope);
+ MOZ_ASSERT(varScope);
+ for (ScopeIter si(enclosingScope); si; si++) {
+ for (js::BindingIter bi(si.scope()); bi; bi++) {
+ if (bi.name() != nameAtom) {
+ continue;
+ }
+
+ 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) {
+ *out = Some(ScopeKindIsCatch(si.kind())
+ ? DeclarationKind::CatchParameter
+ : DeclarationKind::Let);
+ return true;
+ }
+ break;
+ }
+
+ case BindingKind::Const:
+ *out = Some(DeclarationKind::Const);
+ return true;
+
+ case BindingKind::Import:
+ case BindingKind::FormalParameter:
+ case BindingKind::Var:
+ case BindingKind::NamedLambdaCallee:
+ break;
+ }
+ }
+
+ if (si.scope() == varScope) {
+ break;
+ }
+ }
+
+ *out = Nothing();
+ return true;
+}
+
+bool ParseContext::tryDeclareVar(const ParserName* name, DeclarationKind kind,
+ uint32_t beginPos,
+ Maybe<DeclarationKind>* redeclaredKind,
+ uint32_t* prevPos) {
+ return tryDeclareVarHelper<NotDryRun>(name, kind, beginPos, redeclaredKind,
+ prevPos);
+}
+
+template <ParseContext::DryRunOption dryRunOption>
+bool ParseContext::tryDeclareVarHelper(const ParserName* name,
+ DeclarationKind kind, uint32_t beginPos,
+ Maybe<DeclarationKind>* redeclaredKind,
+ uint32_t* prevPos) {
+ MOZ_ASSERT(DeclarationKindIsVar(kind));
+
+ // It is an early error if a 'var' declaration appears inside a
+ // scope contour that has a lexical declaration of the same name. For
+ // example, the following are early errors:
+ //
+ // { let x; var x; }
+ // { { var x; } let x; }
+ //
+ // And the following are not:
+ //
+ // { var x; var x; }
+ // { { let x; } var x; }
+
+ for (ParseContext::Scope* scope = innermostScope();
+ scope != varScope().enclosing(); scope = scope->enclosing()) {
+ if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
+ DeclarationKind declaredKind = p->value()->kind();
+ if (DeclarationKindIsVar(declaredKind)) {
+ if (dryRunOption == NotDryRun) {
+ RedeclareVar(p, kind);
+ }
+ } else if (!DeclarationKindIsParameter(declaredKind)) {
+ // Annex B.3.5 allows redeclaring simple (non-destructured)
+ // catch parameters with var declarations.
+ bool annexB35Allowance =
+ declaredKind == DeclarationKind::SimpleCatchParameter;
+
+ // Annex B.3.3 allows redeclaring functions in the same block.
+ bool annexB33Allowance =
+ declaredKind == DeclarationKind::SloppyLexicalFunction &&
+ kind == DeclarationKind::VarForAnnexBLexicalFunction &&
+ scope == innermostScope();
+
+ if (!annexB35Allowance && !annexB33Allowance) {
+ *redeclaredKind = Some(declaredKind);
+ *prevPos = p->value()->pos();
+ return true;
+ }
+ } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) {
+ MOZ_ASSERT(DeclarationKindIsParameter(declaredKind));
+
+ // Annex B.3.3.1 disallows redeclaring parameter names.
+ // We don't need to set *prevPos here since this case is not
+ // an error.
+ *redeclaredKind = Some(declaredKind);
+ return true;
+ }
+ } else if (dryRunOption == NotDryRun) {
+ if (!scope->addDeclaredName(this, p, name, kind, beginPos)) {
+ return false;
+ }
+ }
+
+ // DryRunOption is used for propagating Annex B functions: we don't
+ // want to declare the synthesized Annex B vars until we exit the var
+ // scope and know that no early errors would have occurred. In order
+ // to avoid quadratic search, we only check for var redeclarations in
+ // the innermost scope when doing a dry run.
+ if (dryRunOption == DryRunInnermostScopeOnly) {
+ break;
+ }
+ }
+
+ if (!sc()->strict() && sc()->isEvalContext() &&
+ (dryRunOption == NotDryRun || innermostScope() == &varScope())) {
+ if (!isVarRedeclaredInEval(name, kind, redeclaredKind)) {
+ return false;
+ }
+ // We don't have position information at runtime.
+ *prevPos = DeclaredNameInfo::npos;
+ }
+
+ return true;
+}
+
+bool ParseContext::hasUsedName(const UsedNameTracker& usedNames,
+ const ParserName* name) {
+ if (auto p = usedNames.lookup(name)) {
+ return p->value().isUsedInScript(scriptId());
+ }
+ return false;
+}
+
+bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames,
+ const ParserName* name) {
+ MOZ_ASSERT(name == sc()->cx_->parserNames().arguments ||
+ name == sc()->cx_->parserNames().dotThis);
+ return hasUsedName(usedNames, name) ||
+ functionBox()->bindingsAccessedDynamically();
+}
+
+bool ParseContext::declareFunctionThis(const UsedNameTracker& usedNames,
+ bool canSkipLazyClosedOverBindings) {
+ // The asm.js validator does all its own symbol-table management so, as an
+ // optimization, avoid doing any work here.
+ if (useAsmOrInsideUseAsm()) {
+ return true;
+ }
+
+ // Derived class constructors emit JSOp::CheckReturn, which requires
+ // '.this' to be bound.
+ FunctionBox* funbox = functionBox();
+ const ParserName* dotThis = sc()->cx_->parserNames().dotThis;
+
+ bool declareThis;
+ if (canSkipLazyClosedOverBindings) {
+ declareThis = funbox->functionHasThisBinding();
+ } else {
+ declareThis = hasUsedFunctionSpecialName(usedNames, dotThis) ||
+ funbox->isClassConstructor();
+ }
+
+ if (declareThis) {
+ ParseContext::Scope& funScope = functionScope();
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
+ MOZ_ASSERT(!p);
+ if (!funScope.addDeclaredName(this, p, dotThis, DeclarationKind::Var,
+ DeclaredNameInfo::npos)) {
+ return false;
+ }
+ funbox->setFunctionHasThisBinding();
+ }
+
+ return true;
+}
+
+bool ParseContext::declareFunctionArgumentsObject(
+ const UsedNameTracker& usedNames, bool canSkipLazyClosedOverBindings) {
+ FunctionBox* funbox = functionBox();
+ ParseContext::Scope& funScope = functionScope();
+ ParseContext::Scope& _varScope = varScope();
+
+ bool usesArguments = false;
+ bool hasExtraBodyVarScope = &funScope != &_varScope;
+
+ // Time to implement the odd semantics of 'arguments'.
+ const ParserName* argumentsName = sc()->cx_->parserNames().arguments;
+
+ bool tryDeclareArguments;
+ if (canSkipLazyClosedOverBindings) {
+ tryDeclareArguments = funbox->shouldDeclareArguments();
+ } else {
+ tryDeclareArguments = hasUsedFunctionSpecialName(usedNames, argumentsName);
+ }
+
+ // ES 9.2.12 steps 19 and 20 say formal parameters, lexical bindings,
+ // and body-level functions named 'arguments' shadow the arguments
+ // object.
+ //
+ // So even if there wasn't a free use of 'arguments' but there is a var
+ // binding of 'arguments', we still might need the arguments object.
+ //
+ // If we have an extra var scope due to parameter expressions and the body
+ // declared 'var arguments', we still need to declare 'arguments' in the
+ // function scope.
+ DeclaredNamePtr p = _varScope.lookupDeclaredName(argumentsName);
+ if (p && p->value()->kind() == DeclarationKind::Var) {
+ if (hasExtraBodyVarScope) {
+ tryDeclareArguments = true;
+ } else {
+ usesArguments = true;
+ }
+ }
+
+ if (tryDeclareArguments) {
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
+ if (!p) {
+ if (!funScope.addDeclaredName(this, p, argumentsName,
+ DeclarationKind::Var,
+ DeclaredNameInfo::npos)) {
+ return false;
+ }
+ funbox->setShouldDeclareArguments();
+ usesArguments = true;
+ } else if (hasExtraBodyVarScope) {
+ // Formal parameters shadow the arguments object.
+ return true;
+ }
+ }
+
+ if (usesArguments) {
+ // There is an 'arguments' binding. Is the arguments object definitely
+ // needed?
+ //
+ // Also see the flags' comments in ContextFlags.
+ funbox->setArgumentsHasVarBinding();
+
+ // Dynamic scope access destroys all hope of optimization.
+ if (sc()->bindingsAccessedDynamically()) {
+ funbox->setAlwaysNeedsArgsObj();
+ }
+ }
+
+ return true;
+}
+
+bool ParseContext::declareDotGeneratorName() {
+ // The special '.generator' binding must be on the function scope, and must
+ // be marked closed-over, as generators expect to find it on the CallObject.
+ ParseContext::Scope& funScope = functionScope();
+ const ParserName* dotGenerator = sc()->cx_->parserNames().dotGenerator;
+ AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
+ if (!p) {
+ if (!funScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
+ DeclaredNameInfo::npos, ClosedOver::Yes)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ParseContext::declareTopLevelDotGeneratorName() {
+ // Provide a .generator binding on the module scope for compatibility with
+ // generator code, which expect to find it on the CallObject for normal
+ // generators.
+ MOZ_ASSERT(
+ sc()->isModuleContext(),
+ "Tried to declare top level dot generator in a non-module context.");
+ ParseContext::Scope& modScope = varScope();
+ const ParserName* dotGenerator = sc()->cx_->parserNames().dotGenerator;
+ AddDeclaredNamePtr p = modScope.lookupDeclaredNameForAdd(dotGenerator);
+ return p ||
+ modScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
+ DeclaredNameInfo::npos, ClosedOver::Yes);
+}
+
+} // namespace frontend
+
+} // namespace js