Source code

Revision control

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 */
#ifndef frontend_ParseContext_h
#define frontend_ParseContext_h
#include "ds/Nestable.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/CompilationInfo.h"
#include "frontend/ErrorReporter.h"
#include "frontend/NameCollections.h"
#include "frontend/SharedContext.h"
#include "frontend/UsedNameTracker.h"
#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
namespace js {
namespace frontend {
class ParserBase;
const char* DeclarationKindString(DeclarationKind kind);
// Returns true if the declaration is `var` or equivalent.
bool DeclarationKindIsVar(DeclarationKind kind);
bool DeclarationKindIsParameter(DeclarationKind kind);
* The struct ParseContext stores information about the current parsing context,
* which is part of the parser state (see the field Parser::pc). The current
* parsing context is either the global context, or the function currently being
* parsed. When the parser encounters a function definition, it creates a new
* ParseContext, makes it the new current context.
class ParseContext : public Nestable<ParseContext> {
// The intra-function statement stack.
// Used for early error checking that depend on the nesting structure of
// statements, such as continue/break targets, labels, and unbraced
// lexical declarations.
class Statement : public Nestable<Statement> {
StatementKind kind_;
using Nestable<Statement>::enclosing;
using Nestable<Statement>::findNearest;
Statement(ParseContext* pc, StatementKind kind)
: Nestable<Statement>(&pc->innermostStatement_), kind_(kind) {}
template <typename T>
inline bool is() const;
template <typename T>
inline T& as();
StatementKind kind() const { return kind_; }
void refineForKind(StatementKind newForKind) {
MOZ_ASSERT(kind_ == StatementKind::ForLoop);
MOZ_ASSERT(newForKind == StatementKind::ForInLoop ||
newForKind == StatementKind::ForOfLoop);
kind_ = newForKind;
class LabelStatement : public Statement {
RootedAtom label_;
LabelStatement(ParseContext* pc, JSAtom* label)
: Statement(pc, StatementKind::Label), label_(pc->sc_->cx_, label) {}
HandleAtom label() const { return label_; }
struct ClassStatement : public Statement {
FunctionBox* constructorBox;
explicit ClassStatement(ParseContext* pc)
: Statement(pc, StatementKind::Class), constructorBox(nullptr) {}
// The intra-function scope stack.
// Tracks declared and used names within a scope.
class Scope : public Nestable<Scope> {
// Names declared in this scope. Corresponds to the union of
// VarDeclaredNames and LexicallyDeclaredNames in the ES spec.
// A 'var' declared name is a member of the declared name set of every
// scope in its scope contour.
// A lexically declared name is a member only of the declared name set of
// the scope in which it is declared.
PooledMapPtr<DeclaredNameMap> declared_;
// FunctionBoxes in this scope that need to be considered for Annex
// B.3.3 semantics. This is checked on Scope exit, as by then we have
// all the declared names and would know if Annex B.3.3 is applicable.
using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>;
PooledVectorPtr<FunctionBoxVector> possibleAnnexBFunctionBoxes_;
// Monotonically increasing id.
uint32_t id_;
bool maybeReportOOM(ParseContext* pc, bool result) {
if (!result) {
return result;
using DeclaredNamePtr = DeclaredNameMap::Ptr;
using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
using Nestable<Scope>::enclosing;
explicit inline Scope(ParserBase* parser);
explicit inline Scope(JSContext* cx, ParseContext* pc,
UsedNameTracker& usedNames);
void dump(ParseContext* pc);
uint32_t id() const { return id_; }
MOZ_MUST_USE bool init(ParseContext* pc) {
if (id_ == UINT32_MAX) {
pc->errorReporter_.errorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
return declared_.acquire(pc->sc()->cx_);
bool isEmpty() const { return declared_->all().empty(); }
DeclaredNamePtr lookupDeclaredName(JSAtom* name) {
return declared_->lookup(name);
AddDeclaredNamePtr lookupDeclaredNameForAdd(JSAtom* name) {
return declared_->lookupForAdd(name);
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p,
JSAtom* name, DeclarationKind kind,
uint32_t pos) {
return maybeReportOOM(
pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
// Add a FunctionBox as a possible candidate for Annex B.3.3 semantics.
MOZ_MUST_USE bool addPossibleAnnexBFunctionBox(ParseContext* pc,
FunctionBox* funbox);
// Check if the candidate function boxes for Annex B.3.3 should in
// fact get Annex B semantics. Checked on Scope exit.
MOZ_MUST_USE bool propagateAndMarkAnnexBFunctionBoxes(ParseContext* pc);
// Add and remove catch parameter names. Used to implement the odd
// semantics of catch bodies.
bool addCatchParameters(ParseContext* pc, Scope& catchParamScope);
void removeCatchParameters(ParseContext* pc, Scope& catchParamScope);
void useAsVarScope(ParseContext* pc) {
pc->varScope_ = this;
// An iterator for the set of names a scope binds: the set of all
// declared names for 'var' scopes, and the set of lexically declared
// names for non-'var' scopes.
class BindingIter {
friend class Scope;
DeclaredNameMap::Range declaredRange_;
mozilla::DebugOnly<uint32_t> count_;
bool isVarScope_;
BindingIter(Scope& scope, bool isVarScope)
: declaredRange_(scope.declared_->all()),
isVarScope_(isVarScope) {
void settle() {
// Both var and lexically declared names are binding in a var
// scope.
if (isVarScope_) {
// Otherwise, pop only lexically declared names are
// binding. Pop the range until we find such a name.
while (!declaredRange_.empty()) {
if (BindingKindIsLexical(kind())) {
bool done() const { return declaredRange_.empty(); }
explicit operator bool() const { return !done(); }
JSAtom* name() {
return declaredRange_.front().key();
DeclarationKind declarationKind() {
return declaredRange_.front().value()->kind();
BindingKind kind() {
return DeclarationKindToBindingKind(declarationKind());
bool closedOver() {
return declaredRange_.front().value()->closedOver();
void setClosedOver() {
return declaredRange_.front().value()->setClosedOver();
void operator++(int) {
MOZ_ASSERT(count_ != UINT32_MAX);
inline BindingIter bindings(ParseContext* pc);
class VarScope : public Scope {
explicit inline VarScope(ParserBase* parser);
explicit inline VarScope(JSContext* cx, ParseContext* pc,
UsedNameTracker& usedNames);
// Trace logging of parsing time.
AutoFrontendTraceLog traceLog_;
// Context shared between parsing and bytecode generation.
SharedContext* sc_;
// A mechanism used for error reporting.
ErrorReporter& errorReporter_;
// The innermost statement, i.e., top of the statement stack.
Statement* innermostStatement_;
// The innermost scope, i.e., top of the scope stack.
// The outermost scope in the stack is usually varScope_. In the case of
// functions, the outermost scope is functionScope_, which may be
// varScope_. See comment above functionScope_.
Scope* innermostScope_;
// If isFunctionBox() and the function is a named lambda, the DeclEnv
// scope for named lambdas.
mozilla::Maybe<Scope> namedLambdaScope_;
// If isFunctionBox(), the scope for the function. If there are no
// parameter expressions, this is scope for the entire function. If there
// are parameter expressions, this holds the special function names
// ('.this', 'arguments') and the formal parameters.
mozilla::Maybe<Scope> functionScope_;
// The body-level scope. This always exists, but not necessarily at the
// beginning of parsing the script in the case of functions with parameter
// expressions.
Scope* varScope_;
// Simple formal parameter names, in order of appearance. Only used when
// isFunctionBox().
PooledVectorPtr<AtomVector> positionalFormalParameterNames_;
// Closed over binding names, in order of appearance. Null-delimited
// between scopes. Only used when syntax parsing.
PooledVectorPtr<AtomVector> closedOverBindingsForLazy_;
// All inner functions in this context. Only used when syntax parsing.
// The Functions (or FunctionCreateionDatas) are traced as part of the
// CompilationInfo function vector.
Vector<FunctionIndex> innerFunctionIndexesForLazy;
// In a function context, points to a Directive struct that can be updated
// to reflect new directives encountered in the Directive Prologue that
// require reparsing the function. In global/module/generator-tail contexts,
// we don't need to reparse when encountering a DirectivePrologue so this
// pointer may be nullptr.
Directives* newDirectives;
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
// lastAwaitOffset stores the offset of the last await that was parsed.
// NoAwaitOffset is its initial value.
static const uint32_t NoAwaitOffset = UINT32_MAX;
uint32_t lastAwaitOffset;
// Monotonically increasing id.
uint32_t scriptId_;
// Set when encountering a inside a method. We need to mark
// the nearest super scope as needing a home object.
bool superScopeNeedsHomeObject_;
ParseContext(JSContext* cx, ParseContext*& parent, SharedContext* sc,
ErrorReporter& errorReporter, CompilationInfo& compilationInfo,
Directives* newDirectives, bool isFull);
MOZ_MUST_USE bool init();
SharedContext* sc() { return sc_; }
// `true` if we are in the body of a function definition.
bool isFunctionBox() const { return sc_->isFunctionBox(); }
FunctionBox* functionBox() { return sc_->asFunctionBox(); }
Statement* innermostStatement() { return innermostStatement_; }
Scope* innermostScope() {
// There is always at least one scope: the 'var' scope.
return innermostScope_;
Scope& namedLambdaScope() {
return *namedLambdaScope_;
Scope& functionScope() {
return *functionScope_;
Scope& varScope() {
return *varScope_;
bool isFunctionExtraBodyVarScopeInnermost() {
return isFunctionBox() && functionBox()->hasParameterExprs &&
innermostScope() == varScope_;
template <typename Predicate /* (Statement*) -> bool */>
Statement* findInnermostStatement(Predicate predicate) {
return Statement::findNearest(innermostStatement_, predicate);
template <typename T, typename Predicate /* (Statement*) -> bool */>
T* findInnermostStatement(Predicate predicate) {
return Statement::findNearest<T>(innermostStatement_, predicate);
template <typename T>
T* findInnermostStatement() {
return Statement::findNearest<T>(innermostStatement_);
AtomVector& positionalFormalParameterNames() {
return *positionalFormalParameterNames_;
AtomVector& closedOverBindingsForLazy() {
return *closedOverBindingsForLazy_;
enum class BreakStatementError {
// Unlabeled break must be inside loop or switch.
// Return Err(true) if we have encountered at least one loop,
// Err(false) otherwise.
MOZ_MUST_USE inline JS::Result<Ok, BreakStatementError> checkBreakStatement(
PropertyName* label);
enum class ContinueStatementError {
MOZ_MUST_USE inline JS::Result<Ok, ContinueStatementError>
checkContinueStatement(PropertyName* label);
// True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level:
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
bool atBodyLevel() { return !innermostStatement_; }
bool atGlobalLevel() { return atBodyLevel() && sc_->isGlobalContext(); }
// True if we are at the topmost level of a module only.
bool atModuleLevel() { return atBodyLevel() && sc_->isModuleContext(); }
// True if we are at the topmost level of an entire script or module. For
// example, in the comment on |atBodyLevel()| above, we would encounter |f1|
// and the outermost |if (cond)| at top level, and everything else would not
// be at top level.
bool atTopLevel() { return atBodyLevel() && sc_->isTopLevelContext(); }
void setSuperScopeNeedsHomeObject() {
superScopeNeedsHomeObject_ = true;
bool superScopeNeedsHomeObject() const { return superScopeNeedsHomeObject_; }
bool useAsmOrInsideUseAsm() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
// A generator is marked as a generator before its body is parsed.
GeneratorKind generatorKind() const {
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind()
: GeneratorKind::NotGenerator;
bool isGenerator() const {
return generatorKind() == GeneratorKind::Generator;
bool isAsync() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
bool needsDotGeneratorName() const { return isGenerator() || isAsync(); }
FunctionAsyncKind asyncKind() const {
return isAsync() ? FunctionAsyncKind::AsyncFunction
: FunctionAsyncKind::SyncFunction;
bool isArrowFunction() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isArrow();
bool isMethod() const {
return sc_->isFunctionBox() && sc_->asFunctionBox()->isMethod();
bool isGetterOrSetter() const {
return sc_->isFunctionBox() && (sc_->asFunctionBox()->isGetter() ||
uint32_t scriptId() const { return scriptId_; }
bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos);
bool hasUsedName(const UsedNameTracker& usedNames, HandlePropertyName name);
bool hasUsedFunctionSpecialName(const UsedNameTracker& usedNames,
HandlePropertyName name);
bool declareFunctionThis(const UsedNameTracker& usedNames,
bool canSkipLazyClosedOverBindings);
bool declareFunctionArgumentsObject(const UsedNameTracker& usedNames,
bool canSkipLazyClosedOverBindings);
bool declareDotGeneratorName();
mozilla::Maybe<DeclarationKind> isVarRedeclaredInInnermostScope(
HandlePropertyName name, DeclarationKind kind);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
enum DryRunOption { NotDryRun, DryRunInnermostScopeOnly };
template <DryRunOption dryRunOption>
bool tryDeclareVarHelper(HandlePropertyName name, DeclarationKind kind,
uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind,
uint32_t* prevPos);
} // namespace frontend
} // namespace js
#endif // frontend_ParseContext_h