1442 lines
54 KiB
C++
1442 lines
54 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* 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/. */
|
|
|
|
/* JS parser. */
|
|
|
|
#ifndef frontend_Parser_h
|
|
#define frontend_Parser_h
|
|
|
|
#include "mozilla/Array.h"
|
|
#include "mozilla/Maybe.h"
|
|
|
|
#include "jspubtd.h"
|
|
|
|
#include "frontend/BytecodeCompiler.h"
|
|
#include "frontend/FullParseHandler.h"
|
|
#include "frontend/NameAnalysisTypes.h"
|
|
#include "frontend/NameCollections.h"
|
|
#include "frontend/SharedContext.h"
|
|
#include "frontend/SyntaxParseHandler.h"
|
|
|
|
namespace js {
|
|
|
|
class ModuleObject;
|
|
|
|
namespace frontend {
|
|
|
|
/*
|
|
* 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>
|
|
{
|
|
public:
|
|
// 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_;
|
|
|
|
public:
|
|
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_;
|
|
|
|
public:
|
|
LabelStatement(ParseContext* pc, JSAtom* label)
|
|
: Statement(pc, StatementKind::Label),
|
|
label_(pc->sc_->context, label)
|
|
{ }
|
|
|
|
HandleAtom label() const {
|
|
return label_;
|
|
}
|
|
};
|
|
|
|
// 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_;
|
|
|
|
// Monotonically increasing id.
|
|
uint32_t id_;
|
|
|
|
bool maybeReportOOM(ParseContext* pc, bool result) {
|
|
if (!result)
|
|
ReportOutOfMemory(pc->sc()->context);
|
|
return result;
|
|
}
|
|
|
|
public:
|
|
using DeclaredNamePtr = DeclaredNameMap::Ptr;
|
|
using AddDeclaredNamePtr = DeclaredNameMap::AddPtr;
|
|
|
|
using Nestable<Scope>::enclosing;
|
|
|
|
template <typename ParseHandler>
|
|
explicit Scope(Parser<ParseHandler>* parser)
|
|
: Nestable<Scope>(&parser->pc->innermostScope_),
|
|
declared_(parser->context->frontendCollectionPool()),
|
|
id_(parser->usedNames.nextScopeId())
|
|
{ }
|
|
|
|
void dump(ParseContext* pc);
|
|
|
|
uint32_t id() const {
|
|
return id_;
|
|
}
|
|
|
|
MOZ_MUST_USE bool init(ParseContext* pc) {
|
|
if (id_ == UINT32_MAX) {
|
|
pc->tokenStream_.reportError(JSMSG_NEED_DIET, js_script_str);
|
|
return false;
|
|
}
|
|
|
|
return declared_.acquire(pc->sc()->context);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind)));
|
|
}
|
|
|
|
// Remove all VarForAnnexBLexicalFunction declarations of a certain
|
|
// name from all scopes in pc's scope stack.
|
|
static void removeVarForAnnexBLexicalFunction(ParseContext* pc, JSAtom* name);
|
|
|
|
// 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) {
|
|
MOZ_ASSERT(!pc->varScope_);
|
|
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()),
|
|
count_(0),
|
|
isVarScope_(isVarScope)
|
|
{
|
|
settle();
|
|
}
|
|
|
|
void settle() {
|
|
// Both var and lexically declared names are binding in a var
|
|
// scope.
|
|
if (isVarScope_)
|
|
return;
|
|
|
|
// Otherwise, pop only lexically declared names are
|
|
// binding. Pop the range until we find such a name.
|
|
while (!declaredRange_.empty()) {
|
|
if (BindingKindIsLexical(kind()))
|
|
break;
|
|
declaredRange_.popFront();
|
|
}
|
|
}
|
|
|
|
public:
|
|
bool done() const {
|
|
return declaredRange_.empty();
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
return !done();
|
|
}
|
|
|
|
JSAtom* name() {
|
|
MOZ_ASSERT(!done());
|
|
return declaredRange_.front().key();
|
|
}
|
|
|
|
DeclarationKind declarationKind() {
|
|
MOZ_ASSERT(!done());
|
|
return declaredRange_.front().value()->kind();
|
|
}
|
|
|
|
BindingKind kind() {
|
|
return DeclarationKindToBindingKind(declarationKind());
|
|
}
|
|
|
|
bool closedOver() {
|
|
MOZ_ASSERT(!done());
|
|
return declaredRange_.front().value()->closedOver();
|
|
}
|
|
|
|
void setClosedOver() {
|
|
MOZ_ASSERT(!done());
|
|
return declaredRange_.front().value()->setClosedOver();
|
|
}
|
|
|
|
void operator++(int) {
|
|
MOZ_ASSERT(!done());
|
|
MOZ_ASSERT(count_ != UINT32_MAX);
|
|
declaredRange_.popFront();
|
|
settle();
|
|
}
|
|
};
|
|
|
|
inline BindingIter bindings(ParseContext* pc);
|
|
};
|
|
|
|
class VarScope : public Scope
|
|
{
|
|
public:
|
|
template <typename ParseHandler>
|
|
explicit VarScope(Parser<ParseHandler>* parser)
|
|
: Scope(parser)
|
|
{
|
|
useAsVarScope(parser->pc);
|
|
}
|
|
};
|
|
|
|
private:
|
|
// Context shared between parsing and bytecode generation.
|
|
SharedContext* sc_;
|
|
|
|
// TokenStream used for error reporting.
|
|
TokenStream& tokenStream_;
|
|
|
|
// 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 parameers.
|
|
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_;
|
|
|
|
// Inner function boxes in this context to try Annex B.3.3 semantics
|
|
// on. Only used when full parsing.
|
|
PooledVectorPtr<FunctionBoxVector> innerFunctionBoxesForAnnexB_;
|
|
|
|
// 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_;
|
|
|
|
// Monotonically increasing id.
|
|
uint32_t scriptId_;
|
|
|
|
// Set when compiling a function using Parser::standaloneFunctionBody via
|
|
// the Function or Generator constructor.
|
|
bool isStandaloneFunctionBody_;
|
|
|
|
// Set when encountering a super.property inside a method. We need to mark
|
|
// the nearest super scope as needing a home object.
|
|
bool superScopeNeedsHomeObject_;
|
|
|
|
public:
|
|
// 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;
|
|
|
|
// All inner functions in this context. Only used when syntax parsing.
|
|
Rooted<GCVector<JSFunction*, 8>> innerFunctionsForLazy;
|
|
|
|
// 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;
|
|
|
|
// Set when parsing a declaration-like destructuring pattern. This flag
|
|
// causes PrimaryExpr to create PN_NAME parse nodes for variable references
|
|
// which are not hooked into any definition's use chain, added to any tree
|
|
// context's AtomList, etc. etc. checkDestructuring will do that work
|
|
// later.
|
|
//
|
|
// The comments atop checkDestructuring explain the distinction between
|
|
// assignment-like and declaration-like destructuring patterns, and why
|
|
// they need to be treated differently.
|
|
mozilla::Maybe<DeclarationKind> inDestructuringDecl;
|
|
|
|
// Set when parsing a function and it has 'return <expr>;'
|
|
bool funHasReturnExpr;
|
|
|
|
// Set when parsing a function and it has 'return;'
|
|
bool funHasReturnVoid;
|
|
|
|
public:
|
|
template <typename ParseHandler>
|
|
ParseContext(Parser<ParseHandler>* prs, SharedContext* sc, Directives* newDirectives)
|
|
: Nestable<ParseContext>(&prs->pc),
|
|
sc_(sc),
|
|
tokenStream_(prs->tokenStream),
|
|
innermostStatement_(nullptr),
|
|
innermostScope_(nullptr),
|
|
varScope_(nullptr),
|
|
innerFunctionBoxesForAnnexB_(prs->context->frontendCollectionPool()),
|
|
positionalFormalParameterNames_(prs->context->frontendCollectionPool()),
|
|
closedOverBindingsForLazy_(prs->context->frontendCollectionPool()),
|
|
scriptId_(prs->usedNames.nextScriptId()),
|
|
isStandaloneFunctionBody_(false),
|
|
superScopeNeedsHomeObject_(false),
|
|
lastYieldOffset(NoYieldOffset),
|
|
lastAwaitOffset(NoAwaitOffset),
|
|
innerFunctionsForLazy(prs->context, GCVector<JSFunction*, 8>(prs->context)),
|
|
newDirectives(newDirectives),
|
|
funHasReturnExpr(false),
|
|
funHasReturnVoid(false)
|
|
{
|
|
if (isFunctionBox()) {
|
|
if (functionBox()->function()->isNamedLambda())
|
|
namedLambdaScope_.emplace(prs);
|
|
functionScope_.emplace(prs);
|
|
}
|
|
}
|
|
|
|
~ParseContext();
|
|
|
|
MOZ_MUST_USE bool init();
|
|
|
|
SharedContext* sc() {
|
|
return sc_;
|
|
}
|
|
|
|
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.
|
|
MOZ_ASSERT(innermostScope_);
|
|
return innermostScope_;
|
|
}
|
|
|
|
Scope& namedLambdaScope() {
|
|
MOZ_ASSERT(functionBox()->function()->isNamedLambda());
|
|
return *namedLambdaScope_;
|
|
}
|
|
|
|
Scope& functionScope() {
|
|
MOZ_ASSERT(isFunctionBox());
|
|
return *functionScope_;
|
|
}
|
|
|
|
Scope& varScope() {
|
|
MOZ_ASSERT(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);
|
|
}
|
|
|
|
AtomVector& positionalFormalParameterNames() {
|
|
return *positionalFormalParameterNames_;
|
|
}
|
|
|
|
AtomVector& closedOverBindingsForLazy() {
|
|
return *closedOverBindingsForLazy_;
|
|
}
|
|
|
|
MOZ_MUST_USE bool addInnerFunctionBoxForAnnexB(FunctionBox* funbox);
|
|
void removeInnerFunctionBoxesForAnnexB(JSAtom* name);
|
|
void finishInnerFunctionBoxesForAnnexB();
|
|
|
|
// 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();
|
|
}
|
|
|
|
void setIsStandaloneFunctionBody() {
|
|
isStandaloneFunctionBody_ = true;
|
|
}
|
|
|
|
bool isStandaloneFunctionBody() const {
|
|
return isStandaloneFunctionBody_;
|
|
}
|
|
|
|
void setSuperScopeNeedsHomeObject() {
|
|
MOZ_ASSERT(sc_->allowSuperProperty());
|
|
superScopeNeedsHomeObject_ = true;
|
|
}
|
|
|
|
bool superScopeNeedsHomeObject() const {
|
|
return superScopeNeedsHomeObject_;
|
|
}
|
|
|
|
bool useAsmOrInsideUseAsm() const {
|
|
return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
|
|
}
|
|
|
|
// Most functions start off being parsed as non-generators.
|
|
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
|
|
// An ES6 generator is marked as a "star generator" before its body is parsed.
|
|
GeneratorKind generatorKind() const {
|
|
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
|
|
}
|
|
|
|
bool isGenerator() const {
|
|
return generatorKind() != NotGenerator;
|
|
}
|
|
|
|
bool isLegacyGenerator() const {
|
|
return generatorKind() == LegacyGenerator;
|
|
}
|
|
|
|
bool isStarGenerator() const {
|
|
return generatorKind() == StarGenerator;
|
|
}
|
|
|
|
bool isAsync() const {
|
|
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
|
|
}
|
|
|
|
FunctionAsyncKind asyncKind() const {
|
|
return isAsync() ? AsyncFunction : SyncFunction;
|
|
}
|
|
|
|
bool isArrowFunction() const {
|
|
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isArrow();
|
|
}
|
|
|
|
bool isMethod() const {
|
|
return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isMethod();
|
|
}
|
|
|
|
uint32_t scriptId() const {
|
|
return scriptId_;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
inline bool
|
|
ParseContext::Statement::is<ParseContext::LabelStatement>() const
|
|
{
|
|
return kind_ == StatementKind::Label;
|
|
}
|
|
|
|
template <typename T>
|
|
inline T&
|
|
ParseContext::Statement::as()
|
|
{
|
|
MOZ_ASSERT(is<T>());
|
|
return static_cast<T&>(*this);
|
|
}
|
|
|
|
inline ParseContext::Scope::BindingIter
|
|
ParseContext::Scope::bindings(ParseContext* pc)
|
|
{
|
|
// In function scopes with parameter expressions, function special names
|
|
// (like '.this') are declared as vars in the function scope, despite its
|
|
// not being the var scope.
|
|
return BindingIter(*this, pc->varScope_ == this || pc->functionScope_.ptrOr(nullptr) == this);
|
|
}
|
|
|
|
inline
|
|
Directives::Directives(ParseContext* parent)
|
|
: strict_(parent->sc()->strict()),
|
|
asmJS_(parent->useAsmOrInsideUseAsm())
|
|
{}
|
|
|
|
enum VarContext { HoistVars, DontHoistVars };
|
|
enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
|
|
enum class PropertyType {
|
|
Normal,
|
|
Shorthand,
|
|
CoverInitializedName,
|
|
Getter,
|
|
GetterNoExpressionClosure,
|
|
Setter,
|
|
SetterNoExpressionClosure,
|
|
Method,
|
|
GeneratorMethod,
|
|
AsyncMethod,
|
|
Constructor,
|
|
DerivedConstructor
|
|
};
|
|
|
|
// Specify a value for an ES6 grammar parametrization. We have no enum for
|
|
// [Return] because its behavior is exactly equivalent to checking whether
|
|
// we're in a function box -- easier and simpler than passing an extra
|
|
// parameter everywhere.
|
|
enum YieldHandling { YieldIsName, YieldIsKeyword };
|
|
enum InHandling { InAllowed, InProhibited };
|
|
enum DefaultHandling { NameRequired, AllowDefaultName };
|
|
enum TripledotHandling { TripledotAllowed, TripledotProhibited };
|
|
|
|
// A data structure for tracking used names per parsing session in order to
|
|
// compute which bindings are closed over. Scripts and scopes are numbered
|
|
// monotonically in textual order and name uses are tracked by lists of
|
|
// (script id, scope id) pairs of their use sites.
|
|
//
|
|
// Intuitively, in a pair (P,S), P tracks the most nested function that has a
|
|
// use of u, and S tracks the most nested scope that is still being parsed.
|
|
//
|
|
// P is used to answer the question "is u used by a nested function?"
|
|
// S is used to answer the question "is u used in any scopes currently being
|
|
// parsed?"
|
|
//
|
|
// The algorithm:
|
|
//
|
|
// Let Used by a map of names to lists.
|
|
//
|
|
// 1. Number all scopes in monotonic increasing order in textual order.
|
|
// 2. Number all scripts in monotonic increasing order in textual order.
|
|
// 3. When an identifier u is used in scope numbered S in script numbered P,
|
|
// and u is found in Used,
|
|
// a. Append (P,S) to Used[u].
|
|
// b. Otherwise, assign the the list [(P,S)] to Used[u].
|
|
// 4. When we finish parsing a scope S in script P, for each declared name d in
|
|
// Declared(S):
|
|
// a. If d is found in Used, mark d as closed over if there is a value
|
|
// (P_d, S_d) in Used[d] such that P_d > P and S_d > S.
|
|
// b. Remove all values (P_d, S_d) in Used[d] such that S_d are >= S.
|
|
//
|
|
// Steps 1 and 2 are implemented by UsedNameTracker::next{Script,Scope}Id.
|
|
// Step 3 is implemented by UsedNameTracker::noteUsedInScope.
|
|
// Step 4 is implemented by UsedNameTracker::noteBoundInScope and
|
|
// Parser::propagateFreeNamesAndMarkClosedOverBindings.
|
|
class UsedNameTracker
|
|
{
|
|
public:
|
|
struct Use
|
|
{
|
|
uint32_t scriptId;
|
|
uint32_t scopeId;
|
|
};
|
|
|
|
class UsedNameInfo
|
|
{
|
|
friend class UsedNameTracker;
|
|
|
|
Vector<Use, 6> uses_;
|
|
|
|
void resetToScope(uint32_t scriptId, uint32_t scopeId);
|
|
|
|
public:
|
|
explicit UsedNameInfo(ExclusiveContext* cx)
|
|
: uses_(cx)
|
|
{ }
|
|
|
|
UsedNameInfo(UsedNameInfo&& other)
|
|
: uses_(mozilla::Move(other.uses_))
|
|
{ }
|
|
|
|
bool noteUsedInScope(uint32_t scriptId, uint32_t scopeId) {
|
|
if (uses_.empty() || uses_.back().scopeId < scopeId)
|
|
return uses_.append(Use { scriptId, scopeId });
|
|
return true;
|
|
}
|
|
|
|
void noteBoundInScope(uint32_t scriptId, uint32_t scopeId, bool* closedOver) {
|
|
*closedOver = false;
|
|
while (!uses_.empty()) {
|
|
Use& innermost = uses_.back();
|
|
if (innermost.scopeId < scopeId)
|
|
break;
|
|
if (innermost.scriptId > scriptId)
|
|
*closedOver = true;
|
|
uses_.popBack();
|
|
}
|
|
}
|
|
|
|
bool isUsedInScript(uint32_t scriptId) const {
|
|
return !uses_.empty() && uses_.back().scriptId >= scriptId;
|
|
}
|
|
};
|
|
|
|
using UsedNameMap = HashMap<JSAtom*,
|
|
UsedNameInfo,
|
|
DefaultHasher<JSAtom*>>;
|
|
|
|
private:
|
|
// The map of names to chains of uses.
|
|
UsedNameMap map_;
|
|
|
|
// Monotonically increasing id for all nested scripts.
|
|
uint32_t scriptCounter_;
|
|
|
|
// Monotonically increasing id for all nested scopes.
|
|
uint32_t scopeCounter_;
|
|
|
|
public:
|
|
explicit UsedNameTracker(ExclusiveContext* cx)
|
|
: map_(cx),
|
|
scriptCounter_(0),
|
|
scopeCounter_(0)
|
|
{ }
|
|
|
|
MOZ_MUST_USE bool init() {
|
|
return map_.init();
|
|
}
|
|
|
|
uint32_t nextScriptId() {
|
|
MOZ_ASSERT(scriptCounter_ != UINT32_MAX,
|
|
"ParseContext::Scope::init should have prevented wraparound");
|
|
return scriptCounter_++;
|
|
}
|
|
|
|
uint32_t nextScopeId() {
|
|
MOZ_ASSERT(scopeCounter_ != UINT32_MAX);
|
|
return scopeCounter_++;
|
|
}
|
|
|
|
UsedNameMap::Ptr lookup(JSAtom* name) const {
|
|
return map_.lookup(name);
|
|
}
|
|
|
|
MOZ_MUST_USE bool noteUse(ExclusiveContext* cx, JSAtom* name,
|
|
uint32_t scriptId, uint32_t scopeId);
|
|
|
|
struct RewindToken
|
|
{
|
|
private:
|
|
friend class UsedNameTracker;
|
|
uint32_t scriptId;
|
|
uint32_t scopeId;
|
|
};
|
|
|
|
RewindToken getRewindToken() const {
|
|
RewindToken token;
|
|
token.scriptId = scriptCounter_;
|
|
token.scopeId = scopeCounter_;
|
|
return token;
|
|
}
|
|
|
|
// Resets state so that scriptId and scopeId are the innermost script and
|
|
// scope, respectively. Used for rewinding state on syntax parse failure.
|
|
void rewind(RewindToken token);
|
|
|
|
// Resets state to beginning of compilation.
|
|
void reset() {
|
|
map_.clear();
|
|
RewindToken token;
|
|
token.scriptId = 0;
|
|
token.scopeId = 0;
|
|
rewind(token);
|
|
}
|
|
};
|
|
|
|
template <typename ParseHandler>
|
|
class Parser final : private JS::AutoGCRooter, public StrictModeGetter
|
|
{
|
|
private:
|
|
using Node = typename ParseHandler::Node;
|
|
|
|
/*
|
|
* A class for temporarily stashing errors while parsing continues.
|
|
*
|
|
* The ability to stash an error is useful for handling situations where we
|
|
* aren't able to verify that an error has occurred until later in the parse.
|
|
* For instance | ({x=1}) | is always parsed as an object literal with
|
|
* a SyntaxError, however, in the case where it is followed by '=>' we rewind
|
|
* and reparse it as a valid arrow function. Here a PossibleError would be
|
|
* set to 'pending' when the initial SyntaxError was encountered then 'resolved'
|
|
* just before rewinding the parser.
|
|
*
|
|
* There are currently two kinds of PossibleErrors: Expression and
|
|
* Destructuring errors. Expression errors are used to mark a possible
|
|
* syntax error when a grammar production is used in an expression context.
|
|
* For example in |{x = 1}|, we mark the CoverInitializedName |x = 1| as a
|
|
* possible expression error, because CoverInitializedName productions
|
|
* are disallowed when an actual ObjectLiteral is expected.
|
|
* Destructuring errors are used to record possible syntax errors in
|
|
* destructuring contexts. For example in |[...rest, ] = []|, we initially
|
|
* mark the trailing comma after the spread expression as a possible
|
|
* destructuring error, because the ArrayAssignmentPattern grammar
|
|
* production doesn't allow a trailing comma after the rest element.
|
|
*
|
|
* When using PossibleError one should set a pending error at the location
|
|
* where an error occurs. From that point, the error may be resolved
|
|
* (invalidated) or left until the PossibleError is checked.
|
|
*
|
|
* Ex:
|
|
* PossibleError possibleError(*this);
|
|
* possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
|
|
* // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
|
|
* if (!possibleError.checkForExpressionError())
|
|
* return false; // we reach this point with a pending exception
|
|
*
|
|
* PossibleError possibleError(*this);
|
|
* possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID);
|
|
* // Returns true, no error is reported.
|
|
* if (!possibleError.checkForDestructuringError())
|
|
* return false; // not reached, no pending exception
|
|
*
|
|
* PossibleError possibleError(*this);
|
|
* // Returns true, no error is reported.
|
|
* if (!possibleError.checkForExpressionError())
|
|
* return false; // not reached, no pending exception
|
|
*/
|
|
class MOZ_STACK_CLASS PossibleError
|
|
{
|
|
private:
|
|
enum class ErrorKind { Expression, Destructuring };
|
|
|
|
enum class ErrorState { None, Pending };
|
|
|
|
struct Error {
|
|
ErrorState state_ = ErrorState::None;
|
|
|
|
// Error reporting fields.
|
|
uint32_t offset_;
|
|
unsigned errorNumber_;
|
|
};
|
|
|
|
Parser<ParseHandler>& parser_;
|
|
Error exprError_;
|
|
Error destructuringError_;
|
|
|
|
// Returns the error report.
|
|
Error& error(ErrorKind kind);
|
|
|
|
// Return true if an error is pending without reporting
|
|
bool hasError(ErrorKind kind);
|
|
|
|
// Resolve any pending error.
|
|
void setResolved(ErrorKind kind);
|
|
|
|
// Set a pending error. Only a single error may be set per instance and
|
|
// error kind.
|
|
void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber);
|
|
|
|
// If there is a pending error, report it and return false, otherwise
|
|
// return true.
|
|
bool checkForError(ErrorKind kind);
|
|
|
|
// Transfer an existing error to another instance.
|
|
void transferErrorTo(ErrorKind kind, PossibleError* other);
|
|
|
|
public:
|
|
explicit PossibleError(Parser<ParseHandler>& parser);
|
|
|
|
// Set a pending destructuring error. Only a single error may be set
|
|
// per instance, i.e. subsequent calls to this method are ignored and
|
|
// won't overwrite the existing pending error.
|
|
void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber);
|
|
|
|
// Set a pending expression error. Only a single error may be set per
|
|
// instance, i.e. subsequent calls to this method are ignored and won't
|
|
// overwrite the existing pending error.
|
|
void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber);
|
|
|
|
// If there is a pending destructuring error, report it and return
|
|
// false, otherwise return true. Clears any pending expression error.
|
|
bool checkForDestructuringError();
|
|
|
|
// If there is a pending expression error, report it and return false,
|
|
// otherwise return true. Clears any pending destructuring error.
|
|
bool checkForExpressionError();
|
|
|
|
// Pass pending errors between possible error instances. This is useful
|
|
// for extending the lifetime of a pending error beyond the scope of
|
|
// the PossibleError where it was initially set (keeping in mind that
|
|
// PossibleError is a MOZ_STACK_CLASS).
|
|
void transferErrorsTo(PossibleError* other);
|
|
};
|
|
|
|
public:
|
|
ExclusiveContext* const context;
|
|
|
|
LifoAlloc& alloc;
|
|
|
|
TokenStream tokenStream;
|
|
LifoAlloc::Mark tempPoolMark;
|
|
|
|
/* list of parsed objects for GC tracing */
|
|
ObjectBox* traceListHead;
|
|
|
|
/* innermost parse context (stack-allocated) */
|
|
ParseContext* pc;
|
|
|
|
// For tracking used names in this parsing session.
|
|
UsedNameTracker& usedNames;
|
|
|
|
/* Compression token for aborting. */
|
|
SourceCompressionTask* sct;
|
|
|
|
ScriptSource* ss;
|
|
|
|
/* Root atoms and objects allocated for the parsed tree. */
|
|
AutoKeepAtoms keepAtoms;
|
|
|
|
/* Perform constant-folding; must be true when interfacing with the emitter. */
|
|
const bool foldConstants:1;
|
|
|
|
private:
|
|
#if DEBUG
|
|
/* Our fallible 'checkOptions' member function has been called. */
|
|
bool checkOptionsCalled:1;
|
|
#endif
|
|
|
|
/*
|
|
* Not all language constructs can be handled during syntax parsing. If it
|
|
* is not known whether the parse succeeds or fails, this bit is set and
|
|
* the parse will return false.
|
|
*/
|
|
bool abortedSyntaxParse:1;
|
|
|
|
/* Unexpected end of input, i.e. TOK_EOF not at top-level. */
|
|
bool isUnexpectedEOF_:1;
|
|
|
|
public:
|
|
/* State specific to the kind of parse being performed. */
|
|
ParseHandler handler;
|
|
|
|
void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
|
|
void freeTree(Node node) { handler.freeTree(node); }
|
|
|
|
public:
|
|
bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
|
|
|
|
/* Report the given error at the current offset. */
|
|
void error(unsigned errorNumber, ...);
|
|
|
|
/* Report the given error at the given offset. */
|
|
void errorAt(uint32_t offset, unsigned errorNumber, ...);
|
|
|
|
/*
|
|
* Handle a strict mode error at the current offset. Report an error if in
|
|
* strict mode code, or warn if not, using the given error number and
|
|
* arguments.
|
|
*/
|
|
MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...);
|
|
|
|
/*
|
|
* Handle a strict mode error at the given offset. Report an error if in
|
|
* strict mode code, or warn if not, using the given error number and
|
|
* arguments.
|
|
*/
|
|
MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...);
|
|
|
|
/* Report the given warning at the current offset. */
|
|
MOZ_MUST_USE bool warning(unsigned errorNumber, ...);
|
|
|
|
/* Report the given warning at the given offset. */
|
|
MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...);
|
|
|
|
/*
|
|
* If extra warnings are enabled, report the given warning at the current
|
|
* offset.
|
|
*/
|
|
MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...);
|
|
|
|
Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
|
const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
|
|
Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
|
|
~Parser();
|
|
|
|
bool checkOptions();
|
|
|
|
// A Parser::Mark is the extension of the LifoAlloc::Mark to the entire
|
|
// Parser's state. Note: clients must still take care that any ParseContext
|
|
// that points into released ParseNodes is destroyed.
|
|
class Mark
|
|
{
|
|
friend class Parser;
|
|
LifoAlloc::Mark mark;
|
|
ObjectBox* traceListHead;
|
|
};
|
|
Mark mark() const {
|
|
Mark m;
|
|
m.mark = alloc.mark();
|
|
m.traceListHead = traceListHead;
|
|
return m;
|
|
}
|
|
void release(Mark m) {
|
|
alloc.release(m.mark);
|
|
traceListHead = m.traceListHead;
|
|
}
|
|
|
|
friend void js::frontend::MarkParser(JSTracer* trc, JS::AutoGCRooter* parser);
|
|
|
|
const char* getFilename() const { return tokenStream.getFilename(); }
|
|
JSVersion versionNumber() const { return tokenStream.versionNumber(); }
|
|
|
|
/*
|
|
* Parse a top-level JS script.
|
|
*/
|
|
Node parse();
|
|
|
|
/*
|
|
* Allocate a new parsed object or function container from
|
|
* cx->tempLifoAlloc.
|
|
*/
|
|
ObjectBox* newObjectBox(JSObject* obj);
|
|
FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart,
|
|
Directives directives,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
bool tryAnnexB);
|
|
|
|
/*
|
|
* Create a new function object given a name (which is optional if this is
|
|
* a function expression).
|
|
*/
|
|
JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
HandleObject proto);
|
|
|
|
void trace(JSTracer* trc);
|
|
|
|
bool hadAbortedSyntaxParse() {
|
|
return abortedSyntaxParse;
|
|
}
|
|
void clearAbortedSyntaxParse() {
|
|
abortedSyntaxParse = false;
|
|
}
|
|
|
|
bool isUnexpectedEOF() const { return isUnexpectedEOF_; }
|
|
|
|
bool checkUnescapedName();
|
|
|
|
private:
|
|
Parser* thisForCtor() { return this; }
|
|
|
|
JSAtom* stopStringCompression();
|
|
|
|
Node stringLiteral();
|
|
Node noSubstitutionTemplate();
|
|
Node templateLiteral(YieldHandling yieldHandling);
|
|
bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt);
|
|
bool appendToCallSiteObj(Node callSiteObj);
|
|
bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
|
|
TokenKind* ttp);
|
|
bool checkStatementsEOF();
|
|
|
|
inline Node newName(PropertyName* name);
|
|
inline Node newName(PropertyName* name, TokenPos pos);
|
|
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
|
|
inline Node newAwaitExpression(uint32_t begin, Node expr);
|
|
|
|
inline bool abortIfSyntaxParser();
|
|
|
|
public:
|
|
/* Public entry points for parsing. */
|
|
Node statement(YieldHandling yieldHandling);
|
|
Node statementListItem(YieldHandling yieldHandling, bool canHaveDirectives = false);
|
|
|
|
bool maybeParseDirective(Node list, Node pn, bool* cont);
|
|
|
|
// Parse the body of an eval.
|
|
//
|
|
// Eval scripts are distinguished from global scripts in that in ES6, per
|
|
// 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh
|
|
// lexical scope.
|
|
Node evalBody(EvalSharedContext* evalsc);
|
|
|
|
// Parse the body of a global script.
|
|
Node globalBody(GlobalSharedContext* globalsc);
|
|
|
|
// Parse a module.
|
|
Node moduleBody(ModuleSharedContext* modulesc);
|
|
|
|
// Parse a function, used for the Function, GeneratorFunction, and
|
|
// AsyncFunction constructors.
|
|
Node standaloneFunction(HandleFunction fun, HandleScope enclosingScope,
|
|
mozilla::Maybe<uint32_t> parameterListEnd,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
Directives inheritedDirectives, Directives* newDirectives);
|
|
|
|
// Parse a function, given only its arguments and body. Used for lazily
|
|
// parsed functions.
|
|
Node standaloneLazyFunction(HandleFunction fun, bool strict,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
|
|
|
|
// Parse an inner function given an enclosing ParseContext and a
|
|
// FunctionBox for the inner function.
|
|
bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t preludeStart,
|
|
InHandling inHandling, YieldHandling yieldHandling,
|
|
FunctionSyntaxKind kind,
|
|
Directives inheritedDirectives, Directives* newDirectives);
|
|
|
|
// Parse a function's formal parameters and its body assuming its function
|
|
// ParseContext is already on the stack.
|
|
bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
|
|
Node pn, FunctionSyntaxKind kind,
|
|
mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing());
|
|
|
|
|
|
// Determine whether |yield| is a valid name in the current context, or
|
|
// whether it's prohibited due to strictness, JS version, or occurrence
|
|
// inside a star generator.
|
|
bool yieldExpressionsSupported() {
|
|
return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
|
|
}
|
|
|
|
// Match the current token against the BindingIdentifier production with
|
|
// the given Yield parameter. If there is no match, report a syntax
|
|
// error.
|
|
PropertyName* bindingIdentifier(YieldHandling yieldHandling);
|
|
|
|
virtual bool strictMode() { return pc->sc()->strict(); }
|
|
bool setLocalStrictMode(bool strict) {
|
|
MOZ_ASSERT(tokenStream.debugHasNoLookahead());
|
|
return pc->sc()->setLocalStrictMode(strict);
|
|
}
|
|
|
|
const ReadOnlyCompileOptions& options() const {
|
|
return tokenStream.options();
|
|
}
|
|
|
|
private:
|
|
enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
|
|
enum ForInitLocation { InForInit, NotInForInit };
|
|
|
|
private:
|
|
/*
|
|
* JS parsers, from lowest to highest precedence.
|
|
*
|
|
* Each parser must be called during the dynamic scope of a ParseContext
|
|
* object, pointed to by this->pc.
|
|
*
|
|
* Each returns a parse node tree or null on error.
|
|
*
|
|
* Parsers whose name has a '1' suffix leave the TokenStream state
|
|
* pointing to the token one past the end of the parsed fragment. For a
|
|
* number of the parsers this is convenient and avoids a lot of
|
|
* unnecessary ungetting and regetting of tokens.
|
|
*
|
|
* Some parsers have two versions: an always-inlined version (with an 'i'
|
|
* suffix) and a never-inlined version (with an 'n' suffix).
|
|
*/
|
|
Node functionStmt(uint32_t preludeStart,
|
|
YieldHandling yieldHandling, DefaultHandling defaultHandling,
|
|
FunctionAsyncKind asyncKind = SyncFunction);
|
|
Node functionExpr(uint32_t preludeStart, InvokedPrediction invoked = PredictUninvoked,
|
|
FunctionAsyncKind asyncKind = SyncFunction);
|
|
|
|
Node statementList(YieldHandling yieldHandling);
|
|
|
|
Node blockStatement(YieldHandling yieldHandling,
|
|
unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
|
|
Node doWhileStatement(YieldHandling yieldHandling);
|
|
Node whileStatement(YieldHandling yieldHandling);
|
|
|
|
Node forStatement(YieldHandling yieldHandling);
|
|
bool forHeadStart(YieldHandling yieldHandling,
|
|
ParseNodeKind* forHeadKind,
|
|
Node* forInitialPart,
|
|
mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope,
|
|
Node* forInOrOfExpression);
|
|
Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
|
|
|
|
Node switchStatement(YieldHandling yieldHandling);
|
|
Node continueStatement(YieldHandling yieldHandling);
|
|
Node breakStatement(YieldHandling yieldHandling);
|
|
Node returnStatement(YieldHandling yieldHandling);
|
|
Node withStatement(YieldHandling yieldHandling);
|
|
Node throwStatement(YieldHandling yieldHandling);
|
|
Node tryStatement(YieldHandling yieldHandling);
|
|
Node catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope);
|
|
Node debuggerStatement();
|
|
|
|
Node variableStatement(YieldHandling yieldHandling);
|
|
|
|
Node labeledStatement(YieldHandling yieldHandling);
|
|
Node labeledItem(YieldHandling yieldHandling);
|
|
|
|
Node ifStatement(YieldHandling yieldHandling);
|
|
Node consequentOrAlternative(YieldHandling yieldHandling);
|
|
|
|
// While on a |let| TOK_NAME token, examine |next|. Indicate whether
|
|
// |next|, the next token already gotten with modifier TokenStream::None,
|
|
// continues a LexicalDeclaration.
|
|
bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling);
|
|
|
|
Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
|
|
|
|
Node importDeclaration();
|
|
Node exportDeclaration();
|
|
Node expressionStatement(YieldHandling yieldHandling,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
|
|
// Declaration parsing. The main entrypoint is Parser::declarationList,
|
|
// with sub-functionality split out into the remaining methods.
|
|
|
|
// |blockScope| may be non-null only when |kind| corresponds to a lexical
|
|
// declaration (that is, PNK_LET or PNK_CONST).
|
|
//
|
|
// The for* parameters, for normal declarations, should be null/ignored.
|
|
// They should be non-null only when Parser::forHeadStart parses a
|
|
// declaration at the start of a for-loop head.
|
|
//
|
|
// In this case, on success |*forHeadKind| is PNK_FORHEAD, PNK_FORIN, or
|
|
// PNK_FOROF, corresponding to the three for-loop kinds. The precise value
|
|
// indicates what was parsed.
|
|
//
|
|
// If parsing recognized a for(;;) loop, the next token is the ';' within
|
|
// the loop-head that separates the init/test parts.
|
|
//
|
|
// Otherwise, for for-in/of loops, the next token is the ')' ending the
|
|
// loop-head. Additionally, the expression that the loop iterates over was
|
|
// parsed into |*forInOrOfExpression|.
|
|
Node declarationList(YieldHandling yieldHandling,
|
|
ParseNodeKind kind,
|
|
ParseNodeKind* forHeadKind = nullptr,
|
|
Node* forInOrOfExpression = nullptr);
|
|
|
|
// The items in a declaration list are either patterns or names, with or
|
|
// without initializers. These two methods parse a single pattern/name and
|
|
// any associated initializer -- and if parsing an |initialDeclaration|
|
|
// will, if parsing in a for-loop head (as specified by |forHeadKind| being
|
|
// non-null), consume additional tokens up to the closing ')' in a
|
|
// for-in/of loop head, returning the iterated expression in
|
|
// |*forInOrOfExpression|. (An "initial declaration" is the first
|
|
// declaration in a declaration list: |a| but not |b| in |var a, b|, |{c}|
|
|
// but not |d| in |let {c} = 3, d|.)
|
|
Node declarationPattern(Node decl, DeclarationKind declKind, TokenKind tt,
|
|
bool initialDeclaration, YieldHandling yieldHandling,
|
|
ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
|
|
Node declarationName(Node decl, DeclarationKind declKind, TokenKind tt,
|
|
bool initialDeclaration, YieldHandling yieldHandling,
|
|
ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
|
|
|
|
// Having parsed a name (not found in a destructuring pattern) declared by
|
|
// a declaration, with the current token being the '=' separating the name
|
|
// from its initializer, parse and bind that initializer -- and possibly
|
|
// consume trailing in/of and subsequent expression, if so directed by
|
|
// |forHeadKind|.
|
|
bool initializerInNameDeclaration(Node decl, Node binding, Handle<PropertyName*> name,
|
|
DeclarationKind declKind, bool initialDeclaration,
|
|
YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
|
|
Node* forInOrOfExpression);
|
|
|
|
Node expr(InHandling inHandling, YieldHandling yieldHandling,
|
|
TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
|
|
TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
|
|
Node yieldExpression(InHandling inHandling);
|
|
Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
|
|
TripledotHandling tripledotHandling,
|
|
PossibleError* possibleError,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
|
|
TripledotHandling tripledotHandling,
|
|
PossibleError* possibleError,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
|
PossibleError* possibleError = nullptr,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
|
TokenKind tt, bool allowCallSyntax = true,
|
|
PossibleError* possibleError = nullptr,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
|
TokenKind tt, PossibleError* possibleError,
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
|
|
TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr);
|
|
|
|
bool tryNewTarget(Node& newTarget);
|
|
bool checkAndMarkSuperScope();
|
|
|
|
Node methodDefinition(uint32_t preludeStart, PropertyType propType, HandleAtom funName);
|
|
|
|
/*
|
|
* Additional JS parsers.
|
|
*/
|
|
bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
|
|
Node funcpn);
|
|
|
|
Node functionDefinition(uint32_t preludeStart, Node pn,
|
|
InHandling inHandling, YieldHandling yieldHandling, HandleAtom name,
|
|
FunctionSyntaxKind kind,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
bool tryAnnexB = false);
|
|
|
|
// Parse a function body. Pass StatementListBody if the body is a list of
|
|
// statements; pass ExpressionBody if the body is a single expression.
|
|
enum FunctionBodyType { StatementListBody, ExpressionBody };
|
|
Node functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind,
|
|
FunctionBodyType type);
|
|
|
|
Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin);
|
|
|
|
Node condition(InHandling inHandling, YieldHandling yieldHandling);
|
|
|
|
/* comprehensions */
|
|
Node generatorComprehensionLambda(unsigned begin);
|
|
Node comprehensionFor(GeneratorKind comprehensionKind);
|
|
Node comprehensionIf(GeneratorKind comprehensionKind);
|
|
Node comprehensionTail(GeneratorKind comprehensionKind);
|
|
Node comprehension(GeneratorKind comprehensionKind);
|
|
Node arrayComprehension(uint32_t begin);
|
|
Node generatorComprehension(uint32_t begin);
|
|
|
|
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
|
|
PossibleError* possibleError = nullptr);
|
|
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
|
|
TokenKind tt);
|
|
Node destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind, YieldHandling yieldHandling,
|
|
TokenKind tt);
|
|
|
|
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
|
|
bool checkExportedName(JSAtom* exportName);
|
|
bool checkExportedNamesForDeclaration(Node node);
|
|
|
|
enum ClassContext { ClassStatement, ClassExpression };
|
|
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
|
|
DefaultHandling defaultHandling);
|
|
|
|
PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling,
|
|
bool yieldTokenizedAsName);
|
|
|
|
PropertyName* labelIdentifier(YieldHandling yieldHandling) {
|
|
return labelOrIdentifierReference(yieldHandling, false);
|
|
}
|
|
|
|
PropertyName* identifierReference(YieldHandling yieldHandling,
|
|
bool yieldTokenizedAsName = false)
|
|
{
|
|
return labelOrIdentifierReference(yieldHandling, yieldTokenizedAsName);
|
|
}
|
|
|
|
PropertyName* importedBinding() {
|
|
return bindingIdentifier(YieldIsName);
|
|
}
|
|
|
|
Node identifierReference(Handle<PropertyName*> name);
|
|
|
|
bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
|
|
|
|
bool allowsForEachIn() {
|
|
#if !JS_HAS_FOR_EACH_IN
|
|
return false;
|
|
#else
|
|
return versionNumber() >= JSVERSION_1_6;
|
|
#endif
|
|
}
|
|
|
|
bool matchInOrOf(bool* isForInp, bool* isForOfp);
|
|
|
|
bool hasUsedFunctionSpecialName(HandlePropertyName name);
|
|
bool declareFunctionArgumentsObject();
|
|
bool declareFunctionThis();
|
|
Node newInternalDotName(HandlePropertyName name);
|
|
Node newThisName();
|
|
Node newDotGeneratorName();
|
|
bool declareDotGeneratorName();
|
|
|
|
bool skipLazyInnerFunction(Node pn, uint32_t preludeStart, FunctionSyntaxKind kind,
|
|
bool tryAnnexB);
|
|
bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t preludeStart,
|
|
InHandling inHandling, YieldHandling yieldHandling,
|
|
FunctionSyntaxKind kind,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
|
|
Directives inheritedDirectives, Directives* newDirectives);
|
|
bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t preludeStart,
|
|
InHandling inHandling, YieldHandling yieldHandling,
|
|
FunctionSyntaxKind kind,
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
bool tryAnnexB,
|
|
Directives inheritedDirectives, Directives* newDirectives);
|
|
bool finishFunctionScopes();
|
|
bool finishFunction();
|
|
bool leaveInnerFunction(ParseContext* outerpc);
|
|
|
|
public:
|
|
enum FunctionCallBehavior {
|
|
PermitAssignmentToFunctionCalls,
|
|
ForbidAssignmentToFunctionCalls
|
|
};
|
|
|
|
bool isValidSimpleAssignmentTarget(Node node,
|
|
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
|
|
|
|
private:
|
|
bool checkIncDecOperand(Node operand, uint32_t operandOffset);
|
|
bool checkStrictAssignment(Node lhs);
|
|
bool checkStrictBinding(PropertyName* name, TokenPos pos);
|
|
|
|
bool hasValidSimpleStrictParameterNames();
|
|
|
|
bool isValidStrictBinding(PropertyName* name);
|
|
|
|
void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
|
|
bool notePositionalFormalParameter(Node fn, HandlePropertyName name,
|
|
bool disallowDuplicateParams, bool* duplicatedParam);
|
|
bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
|
|
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
|
|
DeclarationKind kind);
|
|
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
|
|
mozilla::Maybe<DeclarationKind>* redeclaredKind);
|
|
bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB);
|
|
bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
|
|
DeclarationKind kind, TokenPos pos);
|
|
bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
|
|
bool noteUsedName(HandlePropertyName name);
|
|
bool hasUsedName(HandlePropertyName name);
|
|
|
|
// Required on Scope exit.
|
|
bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
|
|
|
|
mozilla::Maybe<GlobalScope::Data*> newGlobalScopeData(ParseContext::Scope& scope);
|
|
mozilla::Maybe<ModuleScope::Data*> newModuleScopeData(ParseContext::Scope& scope);
|
|
mozilla::Maybe<EvalScope::Data*> newEvalScopeData(ParseContext::Scope& scope);
|
|
mozilla::Maybe<FunctionScope::Data*> newFunctionScopeData(ParseContext::Scope& scope,
|
|
bool hasParameterExprs);
|
|
mozilla::Maybe<VarScope::Data*> newVarScopeData(ParseContext::Scope& scope);
|
|
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
|
|
Node finishLexicalScope(ParseContext::Scope& scope, Node body);
|
|
|
|
Node propertyName(YieldHandling yieldHandling, Node propList,
|
|
PropertyType* propType, MutableHandleAtom propAtom);
|
|
Node computedPropertyName(YieldHandling yieldHandling, Node literal);
|
|
Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError);
|
|
Node newRegExp();
|
|
|
|
Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
|
|
|
|
// Top-level entrypoint into destructuring pattern checking/name-analyzing.
|
|
bool checkDestructuringPattern(Node pattern, mozilla::Maybe<DeclarationKind> maybeDecl,
|
|
PossibleError* possibleError = nullptr);
|
|
|
|
// Recursive methods for checking/name-analyzing subcomponents of a
|
|
// destructuring pattern. The array/object methods *must* be passed arrays
|
|
// or objects. The name method may be passed anything but will report an
|
|
// error if not passed a name.
|
|
bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
|
|
bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl);
|
|
bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> maybeDecl);
|
|
|
|
Node newNumber(const Token& tok) {
|
|
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
|
|
}
|
|
|
|
static Node null() { return ParseHandler::null(); }
|
|
|
|
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
|
|
|
|
TokenPos pos() const { return tokenStream.currentToken().pos; }
|
|
|
|
bool asmJS(Node list);
|
|
|
|
bool warnOnceAboutExprClosure();
|
|
bool warnOnceAboutForEach();
|
|
};
|
|
|
|
} /* namespace frontend */
|
|
} /* namespace js */
|
|
|
|
#endif /* frontend_Parser_h */
|