Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "frontend/CompilationStencil.h" // ScopeContext
#include "frontend/Parser.h" // ParserBase
#include "js/friend/ErrorMessages.h" // JSMSG_*
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";
case DeclarationKind::Synthetic:
return "synthetic";
case DeclarationKind::PrivateMethod:
return "private method";
}
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(FrontendContext* fc, TaggedParserAtomIndex name,
NameVisibility visibility, uint32_t scriptId,
uint32_t scopeId,
mozilla::Maybe<TokenPos> tokenPosition) {
if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) {
p->value().maybeUpdatePos(tokenPosition);
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(fc, 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(
FrontendContext* fc, mozilla::Maybe<UnboundPrivateName>& maybeUnboundName) {
// We never saw any private names, so can just return early
if (!hasPrivateNames_) {
return true;
}
Vector<UnboundPrivateName, 8> unboundPrivateNames(fc);
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);
}
}
#if defined(DEBUG) || defined(JS_JITSPEW)
void UsedNameTracker::dump(ParserAtomsTable& table) {
js::Fprinter out(stderr);
out.printf("Used names:\n");
for (UsedNameMap::Range r = map_.all(); !r.empty(); r.popFront()) {
const auto& item = r.front();
const auto& name = item.key();
const auto& nameInfo = item.value();
out.put(" ");
table.dumpCharsNoQuote(out, name);
out.put("\n");
if (nameInfo.visibility_ == NameVisibility::Private) {
out.put(" visibility: private\n");
}
if (nameInfo.firstUsePos_) {
const auto& pos = *nameInfo.firstUsePos_;
out.printf(" first use pos: %u\n", pos.begin);
}
out.printf(" %zu user(s)", nameInfo.uses_.length());
bool first = true;
for (const auto& use : nameInfo.uses_) {
if (first) {
first = false;
out.put(" (");
} else {
out.put(", ");
}
out.printf("%u/%u", use.scriptId, use.scopeId);
}
if (!first) {
out.put(")");
}
out.put("\n");
}
}
#endif
void ParseContext::Scope::dump(ParseContext* pc, ParserBase* parser) {
fprintf(stdout, "ParseScope %p", this);
fprintf(stdout, "\n decls:\n");
for (DeclaredNameMap::Range r = declared_->all(); !r.empty(); r.popFront()) {
auto index = r.front().key();
UniqueChars bytes = parser->parserAtoms().toPrintableString(index);
if (!bytes) {
ReportOutOfMemory(pc->sc()->fc_);
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()->fc_)) {
return false;
}
}
return maybeReportOOM(pc, possibleAnnexBFunctionBoxes_->append(funbox));
}
bool ParseContext::Scope::propagateAndMarkAnnexBFunctionBoxes(
ParseContext* pc, ParserBase* parser) {
// 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, parser, &annexBApplies)) {
return false;
}
if (annexBApplies) {
if (!pc->tryDeclareVar(funbox->explicitName(), parser,
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, parser, &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));
auto 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()) {
auto name = r.front().key();
DeclaredNamePtr p = declared_->lookup(name);
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(FrontendContext* fc, ParseContext*& parent,
SharedContext* sc, ErrorReporter& errorReporter,
CompilationState& compilationState,
Directives* newDirectives, bool isFull)
: Nestable<ParseContext>(&parent),
sc_(sc),
errorReporter_(errorReporter),
innermostStatement_(nullptr),
innermostScope_(nullptr),
varScope_(nullptr),
positionalFormalParameterNames_(fc->nameCollectionPool()),
closedOverBindingsForLazy_(fc->nameCollectionPool()),
innerFunctionIndexesForLazy(sc->fc_),
newDirectives(newDirectives),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
scriptId_(compilationState.usedNames.nextScriptId()),
superScopeNeedsHomeObject_(false) {
if (isFunctionBox()) {
if (functionBox()->isNamedLambda()) {
namedLambdaScope_.emplace(fc, parent, compilationState.usedNames);
}
functionScope_.emplace(fc, parent, compilationState.usedNames);
}
}
bool ParseContext::init() {
if (scriptId_ == UINT32_MAX) {
errorReporter_.errorNoOffset(JSMSG_NEED_DIET, "script");
return false;
}
FrontendContext* fc = sc()->fc_;
if (isFunctionBox()) {
// Named lambdas always need a binding for their own name. If this
// binding is closed over when we finish parsing the function iNn
// 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(fc)) {
return false;
}
}
if (!closedOverBindingsForLazy_.acquire(fc)) {
return false;
}
return true;
}
bool ParseContext::computeAnnexBAppliesToLexicalFunctionInInnermostScope(
FunctionBox* funbox, ParserBase* parser, bool* annexBApplies) {
MOZ_ASSERT(!sc()->strict());
TaggedParserAtomIndex name = funbox->explicitName();
Maybe<DeclarationKind> redeclaredKind;
if (!isVarRedeclaredInInnermostScope(
name, parser, 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(name));
}
}
}
}
// 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(
TaggedParserAtomIndex name, ParserBase* parser, DeclarationKind kind,
mozilla::Maybe<DeclarationKind>* out) {
uint32_t unused;
return tryDeclareVarHelper<DryRunInnermostScopeOnly>(
name, parser, kind, DeclaredNameInfo::npos, out, &unused);
}
bool ParseContext::isVarRedeclaredInEval(TaggedParserAtomIndex name,
ParserBase* parser,
DeclarationKind kind,
Maybe<DeclarationKind>* out) {
auto maybeKind = parser->getCompilationState()
.scopeContext.lookupLexicalBindingInEnclosingScope(name);
if (!maybeKind) {
*out = Nothing();
return true;
}
switch (*maybeKind) {
case ScopeContext::EnclosingLexicalBindingKind::Let:
*out = Some(DeclarationKind::Let);
break;
case ScopeContext::EnclosingLexicalBindingKind::Const:
*out = Some(DeclarationKind::Const);
break;
case ScopeContext::EnclosingLexicalBindingKind::CatchParameter:
*out = Some(DeclarationKind::CatchParameter);
break;
case ScopeContext::EnclosingLexicalBindingKind::Synthetic:
*out = Some(DeclarationKind::Synthetic);
break;
case ScopeContext::EnclosingLexicalBindingKind::PrivateMethod:
*out = Some(DeclarationKind::PrivateMethod);
break;
}
return true;
}
bool ParseContext::tryDeclareVar(TaggedParserAtomIndex name, ParserBase* parser,
DeclarationKind kind, uint32_t beginPos,
Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos) {
return tryDeclareVarHelper<NotDryRun>(name, parser, kind, beginPos,
redeclaredKind, prevPos);
}
template <ParseContext::DryRunOption dryRunOption>
bool ParseContext::tryDeclareVarHelper(TaggedParserAtomIndex name,
ParserBase* parser, 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, parser, kind, redeclaredKind)) {
return false;
}
// We don't have position information at runtime.
*prevPos = DeclaredNameInfo::npos;
}
return true;
}
bool ParseContext::hasUsedName(const UsedNameTracker& usedNames,
TaggedParserAtomIndex name) {
if (auto p = usedNames.lookup(name)) {
return p->value().isUsedInScript(scriptId());
}
return false;
}
bool ParseContext::hasClosedOverName(const UsedNameTracker& usedNames,
TaggedParserAtomIndex name) {
if (auto p = usedNames.lookup(name)) {
return p->value().isClosedOver(scriptId());
}
return false;
}
bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames,
TaggedParserAtomIndex name) {
MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments() ||
name == TaggedParserAtomIndex::WellKnown::dot_this_() ||
name == TaggedParserAtomIndex::WellKnown::dot_newTarget_());
return hasUsedName(usedNames, name) ||
functionBox()->bindingsAccessedDynamically();
}
bool ParseContext::hasClosedOverFunctionSpecialName(
const UsedNameTracker& usedNames, TaggedParserAtomIndex name) {
MOZ_ASSERT(name == TaggedParserAtomIndex::WellKnown::arguments());
return hasClosedOverName(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. Class field initializers implicitly read `.this`.
// Therefore we unconditionally declare `.this` in all class constructors.
FunctionBox* funbox = functionBox();
auto dotThis = TaggedParserAtomIndex::WellKnown::dot_this_();
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 hasExtraBodyVarScope = &funScope != &_varScope;
// Time to implement the odd semantics of 'arguments'.
auto argumentsName = TaggedParserAtomIndex::WellKnown::arguments();
bool tryDeclareArguments = false;
bool needsArgsObject = false;
// When delazifying simply defer to the function box.
if (canSkipLazyClosedOverBindings) {
tryDeclareArguments = funbox->shouldDeclareArguments();
needsArgsObject = funbox->needsArgsObj();
} else {
// We cannot compute these values when delazifying, hence why we need to
// rely on the function box flags instead.
bool bindingClosedOver =
hasClosedOverFunctionSpecialName(usedNames, argumentsName);
bool bindingUsedOnlyHere =
hasUsedFunctionSpecialName(usedNames, argumentsName) &&
!bindingClosedOver;
// Declare arguments if there's a closed-over consumer of the binding, or if
// there is a non-length use and we will reference the binding during
// bytecode emission.
tryDeclareArguments =
!funbox->isEligibleForArgumentsLength() || bindingClosedOver;
// If we have a use and the binding isn't closed over, then we will do
// bytecode emission with the arguments intrinsic.
if (bindingUsedOnlyHere && funbox->isEligibleForArgumentsLength()) {
// If we're using the intrinsic we should not be declaring the binding.
MOZ_ASSERT(!tryDeclareArguments);
funbox->setUsesArgumentsIntrinsics();
} else if (tryDeclareArguments) {
needsArgsObject = true;
}
}
// 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) {
// While there is a binding in the var scope, we should declare
// the binding in the function scope.
tryDeclareArguments = true;
} else {
// A binding in the function scope (since varScope and functionScope are
// the same) exists, so arguments is used.
if (needsArgsObject) {
funbox->setNeedsArgsObj();
}
// There is no point in continuing on below: We know we already have
// a declaration of arguments in the function scope.
return true;
}
}
if (tryDeclareArguments) {
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
if (!p) {
if (!funScope.addDeclaredName(this, p, argumentsName,
DeclarationKind::Var,
DeclaredNameInfo::npos)) {
return false;
}
funbox->setShouldDeclareArguments();
if (needsArgsObject) {
funbox->setNeedsArgsObj();
}
}
}
return true;
}
bool ParseContext::declareNewTarget(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;
}
FunctionBox* funbox = functionBox();
auto dotNewTarget = TaggedParserAtomIndex::WellKnown::dot_newTarget_();
bool declareNewTarget;
if (canSkipLazyClosedOverBindings) {
declareNewTarget = funbox->functionHasNewTargetBinding();
} else {
declareNewTarget = hasUsedFunctionSpecialName(usedNames, dotNewTarget);
}
if (declareNewTarget) {
ParseContext::Scope& funScope = functionScope();
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotNewTarget);
MOZ_ASSERT(!p);
if (!funScope.addDeclaredName(this, p, dotNewTarget, DeclarationKind::Var,
DeclaredNameInfo::npos)) {
return false;
}
funbox->setFunctionHasNewTargetBinding();
}
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();
auto dotGenerator = TaggedParserAtomIndex::WellKnown::dot_generator_();
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();
auto dotGenerator = TaggedParserAtomIndex::WellKnown::dot_generator_();
AddDeclaredNamePtr p = modScope.lookupDeclaredNameForAdd(dotGenerator);
return p ||
modScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
DeclaredNameInfo::npos, ClosedOver::Yes);
}
} // namespace frontend
} // namespace js