#include "frontend/ParseContext-inl.h"
#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";
}
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, JSAtom* name, uint32_t scriptId,
uint32_t scopeId) {
if (UsedNameMap::AddPtr p = map_.lookupForAdd(name)) {
if (!p || !p->value().noteUsedInScope(scriptId, scopeId)) {
return false;
}
} else {
UsedNameInfo info(cx);
if (!info.noteUsedInScope(scriptId, scopeId)) {
return false;
}
if (!map_.add(p, name, std::move(info))) {
return false;
}
}
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 = AtomToPrintableString(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) {
if (pc->sc()->strict() || !possibleAnnexBFunctionBoxes_ ||
possibleAnnexBFunctionBoxes_->empty()) {
return true;
}
if (this == &pc->varScope()) {
RootedPropertyName name(pc->sc()->cx_);
Maybe<DeclarationKind> redeclaredKind;
uint32_t unused;
for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
if (pc->annexBAppliesToLexicalFunctionInInnermostScope(funbox)) {
name = funbox->function()->explicitName()->asPropertyName();
if (!pc->tryDeclareVar(
name, DeclarationKind::VarForAnnexBLexicalFunction,
DeclaredNameInfo::npos, &redeclaredKind, &unused)) {
return false;
}
MOZ_ASSERT(!redeclaredKind);
funbox->isAnnexB = true;
}
}
} else {
for (FunctionBox* funbox : *possibleAnnexBFunctionBoxes_) {
if (pc->annexBAppliesToLexicalFunctionInInnermostScope(funbox)) {
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));
JSAtom* 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);
if (DeclarationKindIsCatchParameter(r.front().value()->kind())) {
declared_->remove(p);
}
}
}
ParseContext::ParseContext(JSContext* cx, ParseContext*& parent,
SharedContext* sc, ErrorReporter& errorReporter,
class UsedNameTracker& usedNames,
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()),
innerFunctionsForLazy(cx, GCVector<JSFunction*, 8>(cx)),
newDirectives(newDirectives),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
scriptId_(usedNames.nextScriptId()),
isStandaloneFunctionBody_(false),
superScopeNeedsHomeObject_(false) {
if (isFunctionBox()) {
if (functionBox()->function()->isNamedLambda()) {
namedLambdaScope_.emplace(cx, parent, usedNames);
}
functionScope_.emplace(cx, parent, usedNames);
}
}
bool ParseContext::init() {
if (scriptId_ == UINT32_MAX) {
errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
}
JSContext* cx = sc()->cx_;
if (isFunctionBox()) {
RootedFunction fun(cx, functionBox()->function());
if (fun->isNamedLambda()) {
if (!namedLambdaScope_->init(this)) {
return false;
}
AddDeclaredNamePtr p =
namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName());
MOZ_ASSERT(!p);
if (!namedLambdaScope_->addDeclaredName(this, p, fun->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::annexBAppliesToLexicalFunctionInInnermostScope(
FunctionBox* funbox) {
MOZ_ASSERT(!sc()->strict());
RootedPropertyName name(sc()->cx_,
funbox->function()->explicitName()->asPropertyName());
Maybe<DeclarationKind> redeclaredKind = isVarRedeclaredInInnermostScope(
name, DeclarationKind::VarForAnnexBLexicalFunction);
if (!redeclaredKind && isFunctionBox()) {
Scope& funScope = functionScope();
if (&funScope != &varScope()) {
if (AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(name)) {
DeclarationKind declaredKind = p->value()->kind();
if (DeclarationKindIsParameter(declaredKind)) {
redeclaredKind = Some(declaredKind);
} else {
MOZ_ASSERT(FunctionScope::isSpecialName(sc()->cx_, name));
}
}
}
}
return !redeclaredKind;
}
Maybe<DeclarationKind> ParseContext::isVarRedeclaredInInnermostScope(
HandlePropertyName name, DeclarationKind kind) {
Maybe<DeclarationKind> redeclaredKind;
uint32_t unused;
MOZ_ALWAYS_TRUE(tryDeclareVarHelper<DryRunInnermostScopeOnly>(
name, kind, DeclaredNameInfo::npos, &redeclaredKind, &unused));
return redeclaredKind;
}
Maybe<DeclarationKind> ParseContext::isVarRedeclaredInEval(
HandlePropertyName name, DeclarationKind kind) {
MOZ_ASSERT(DeclarationKindIsVar(kind));
MOZ_ASSERT(sc()->isEvalContext());
js::Scope* enclosingScope = sc()->compilationEnclosingScope();
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() != name) {
continue;
}
switch (bi.kind()) {
case BindingKind::Let: {
bool annexB35Allowance = si.kind() == ScopeKind::SimpleCatch;
if (!annexB35Allowance) {
return Some(ScopeKindIsCatch(si.kind())
? DeclarationKind::CatchParameter
: DeclarationKind::Let);
}
break;
}
case BindingKind::Const:
return Some(DeclarationKind::Const);
case BindingKind::Import:
case BindingKind::FormalParameter:
case BindingKind::Var:
case BindingKind::NamedLambdaCallee:
break;
}
}
if (si.scope() == varScope) {
break;
}
}
return Nothing();
}
bool ParseContext::tryDeclareVar(HandlePropertyName 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(HandlePropertyName name,
DeclarationKind kind, uint32_t beginPos,
Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos) {
MOZ_ASSERT(DeclarationKindIsVar(kind));
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)) {
bool annexB35Allowance =
declaredKind == DeclarationKind::SimpleCatchParameter;
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));
*redeclaredKind = Some(declaredKind);
return true;
}
} else if (dryRunOption == NotDryRun) {
if (!scope->addDeclaredName(this, p, name, kind, beginPos)) {
return false;
}
}
if (dryRunOption == DryRunInnermostScopeOnly) {
break;
}
}
if (!sc()->strict() && sc()->isEvalContext() &&
(dryRunOption == NotDryRun || innermostScope() == &varScope())) {
*redeclaredKind = isVarRedeclaredInEval(name, kind);
*prevPos = DeclaredNameInfo::npos;
}
return true;
}
bool ParseContext::hasUsedName(const UsedNameTracker& usedNames,
HandlePropertyName name) {
if (auto p = usedNames.lookup(name)) {
return p->value().isUsedInScript(scriptId());
}
return false;
}
bool ParseContext::hasUsedFunctionSpecialName(const UsedNameTracker& usedNames,
HandlePropertyName name) {
MOZ_ASSERT(name == sc()->cx_->names().arguments ||
name == sc()->cx_->names().dotThis);
return hasUsedName(usedNames, name) ||
functionBox()->bindingsAccessedDynamically();
}
bool ParseContext::declareFunctionThis(const UsedNameTracker& usedNames,
bool canSkipLazyClosedOverBindings) {
if (useAsmOrInsideUseAsm()) {
return true;
}
FunctionBox* funbox = functionBox();
HandlePropertyName dotThis = sc()->cx_->names().dotThis;
bool declareThis;
if (canSkipLazyClosedOverBindings) {
declareThis = funbox->function()->lazyScript()->hasThisBinding();
} else {
declareThis = hasUsedFunctionSpecialName(usedNames, dotThis) ||
funbox->isDerivedClassConstructor();
}
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->setHasThisBinding();
}
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;
HandlePropertyName argumentsName = sc()->cx_->names().arguments;
bool tryDeclareArguments;
if (canSkipLazyClosedOverBindings) {
tryDeclareArguments =
funbox->function()->lazyScript()->shouldDeclareArguments();
} else {
tryDeclareArguments = hasUsedFunctionSpecialName(usedNames, argumentsName);
}
DeclaredNamePtr p = _varScope.lookupDeclaredName(argumentsName);
if (p && p->value()->kind() == DeclarationKind::Var) {
if (hasExtraBodyVarScope) {
tryDeclareArguments = true;
} else {
funbox->usesArguments = true;
}
}
if (tryDeclareArguments) {
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
if (!p) {
if (!funScope.addDeclaredName(this, p, argumentsName,
DeclarationKind::Var,
DeclaredNameInfo::npos)) {
return false;
}
funbox->declaredArguments = true;
funbox->usesArguments = true;
} else if (hasExtraBodyVarScope) {
return true;
}
}
if (funbox->usesArguments) {
funbox->setArgumentsHasLocalBinding();
if (sc()->bindingsAccessedDynamically()) {
funbox->setDefinitelyNeedsArgsObj();
}
if (sc()->hasDebuggerStatement()) {
funbox->setDefinitelyNeedsArgsObj();
}
}
return true;
}
bool ParseContext::declareDotGeneratorName() {
ParseContext::Scope& funScope = functionScope();
HandlePropertyName dotGenerator = sc()->cx_->names().dotGenerator;
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
if (!p &&
!funScope.addDeclaredName(this, p, dotGenerator, DeclarationKind::Var,
DeclaredNameInfo::npos)) {
return false;
}
return true;
}
}
}