2017-04-19 07:56:45 +00:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2017-08-20 04:16:16 +00:00
|
|
|
/* JS parser. */
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
#ifndef frontend_Parser_h
|
|
|
|
#define frontend_Parser_h
|
|
|
|
|
2017-08-20 04:16:16 +00:00
|
|
|
#include "mozilla/Maybe.h"
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
#include "jspubtd.h"
|
|
|
|
|
|
|
|
#include "frontend/BytecodeCompiler.h"
|
|
|
|
#include "frontend/FullParseHandler.h"
|
|
|
|
#include "frontend/ParseMaps.h"
|
|
|
|
#include "frontend/ParseNode.h"
|
|
|
|
#include "frontend/SharedContext.h"
|
|
|
|
#include "frontend/SyntaxParseHandler.h"
|
|
|
|
|
|
|
|
namespace js {
|
|
|
|
|
|
|
|
class ModuleObject;
|
|
|
|
class StaticFunctionBoxScopeObject;
|
|
|
|
|
|
|
|
namespace frontend {
|
|
|
|
|
|
|
|
struct StmtInfoPC : public StmtInfoBase
|
|
|
|
{
|
|
|
|
static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
|
|
|
|
|
|
|
|
StmtInfoPC* enclosing;
|
|
|
|
StmtInfoPC* enclosingScope;
|
|
|
|
|
|
|
|
uint32_t blockid; /* for simplified dominance computation */
|
|
|
|
uint32_t innerBlockScopeDepth; /* maximum depth of nested block scopes, in slots */
|
|
|
|
|
|
|
|
// Lexical declarations inside switches are tricky because the block id
|
|
|
|
// doesn't convey dominance information. Record what index the current
|
|
|
|
// case's lexical declarations start at so we may generate dead zone
|
|
|
|
// checks for other cases' declarations.
|
|
|
|
//
|
|
|
|
// Only valid if type is StmtType::SWITCH.
|
|
|
|
uint16_t firstDominatingLexicalInCase;
|
|
|
|
|
|
|
|
explicit StmtInfoPC(ExclusiveContext* cx)
|
|
|
|
: StmtInfoBase(cx),
|
|
|
|
blockid(BlockIdLimit),
|
|
|
|
innerBlockScopeDepth(0),
|
|
|
|
firstDominatingLexicalInCase(0)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SharedContext;
|
|
|
|
|
|
|
|
typedef Vector<Definition*, 16> DeclVector;
|
|
|
|
|
|
|
|
struct GenericParseContext
|
|
|
|
{
|
|
|
|
// Enclosing function or global context.
|
|
|
|
GenericParseContext* parent;
|
|
|
|
|
|
|
|
// Context shared between parsing and bytecode generation.
|
|
|
|
SharedContext* sc;
|
|
|
|
|
|
|
|
// The following flags are set when a particular code feature is detected
|
|
|
|
// in a function.
|
|
|
|
|
|
|
|
// Function has 'return <expr>;'
|
|
|
|
bool funHasReturnExpr:1;
|
|
|
|
|
|
|
|
// Function has 'return;'
|
|
|
|
bool funHasReturnVoid:1;
|
|
|
|
|
|
|
|
GenericParseContext(GenericParseContext* parent, SharedContext* sc)
|
|
|
|
: parent(parent),
|
|
|
|
sc(sc),
|
|
|
|
funHasReturnExpr(false),
|
|
|
|
funHasReturnVoid(false)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename ParseHandler>
|
|
|
|
bool
|
|
|
|
GenerateBlockId(TokenStream& ts, ParseContext<ParseHandler>* pc, uint32_t& blockid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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, and sets its parent to the
|
|
|
|
* context in which it encountered the definition.
|
|
|
|
*/
|
|
|
|
template <typename ParseHandler>
|
|
|
|
struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
|
|
|
{
|
|
|
|
typedef typename ParseHandler::Node Node;
|
|
|
|
typedef typename ParseHandler::DefinitionNode DefinitionNode;
|
|
|
|
|
|
|
|
uint32_t bodyid; /* block number of program/function body */
|
|
|
|
|
|
|
|
StmtInfoStack<StmtInfoPC> stmtStack;
|
|
|
|
|
|
|
|
Node maybeFunction; /* sc->isFunctionBox, the pn where pn->pn_funbox == sc */
|
|
|
|
|
|
|
|
// If sc->isFunctionBox(), this is used to temporarily link up the
|
|
|
|
// FunctionBox with the JSFunction so the static scope chain may be walked
|
|
|
|
// without a JSScript.
|
|
|
|
mozilla::Maybe<JSFunction::AutoParseUsingFunctionBox> parseUsingFunctionBox;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2019-08-18 21:26:54 +00:00
|
|
|
// 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;
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
// 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; }
|
|
|
|
|
2018-09-15 17:35:22 +00:00
|
|
|
bool isAsync() const {
|
|
|
|
return sc->isFunctionBox() && sc->asFunctionBox()->isAsync();
|
|
|
|
}
|
|
|
|
|
2019-08-18 21:26:54 +00:00
|
|
|
FunctionAsyncKind asyncKind() const {
|
|
|
|
return isAsync() ? AsyncFunction : SyncFunction;
|
|
|
|
}
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
bool isArrowFunction() const {
|
|
|
|
return sc->isFunctionBox() && sc->asFunctionBox()->function()->isArrow();
|
|
|
|
}
|
|
|
|
bool isMethod() const {
|
|
|
|
return sc->isFunctionBox() && sc->asFunctionBox()->function()->isMethod();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t blockScopeDepth; /* maximum depth of nested block scopes, in slots */
|
|
|
|
Node blockNode; /* parse node for a block with let declarations
|
|
|
|
(block with its own lexical scope) */
|
|
|
|
|
|
|
|
private:
|
|
|
|
AtomDecls<ParseHandler> decls_; /* function, const, and var declarations */
|
|
|
|
DeclVector args_; /* argument definitions */
|
|
|
|
DeclVector vars_; /* var/const definitions */
|
|
|
|
DeclVector bodyLevelLexicals_; /* lexical definitions at body-level */
|
|
|
|
|
|
|
|
bool checkLocalsOverflow(TokenStream& ts);
|
|
|
|
|
|
|
|
public:
|
|
|
|
const AtomDecls<ParseHandler>& decls() const {
|
|
|
|
return decls_;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t numArgs() const {
|
|
|
|
MOZ_ASSERT(sc->isFunctionBox());
|
|
|
|
return args_.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function adds a definition to the lexical scope represented by this
|
|
|
|
* ParseContext.
|
|
|
|
*
|
|
|
|
* Pre-conditions:
|
|
|
|
* + The caller must have already taken care of name collisions:
|
|
|
|
* - For non-let definitions, this means 'name' isn't in 'decls'.
|
|
|
|
* - For let definitions, this means 'name' isn't already a name in the
|
|
|
|
* current block.
|
|
|
|
* + The given 'pn' is either a placeholder (created by a previous unbound
|
|
|
|
* use) or an un-bound un-linked name node.
|
|
|
|
* + The given 'kind' is one of ARG, CONST, VAR, or LET. In particular,
|
|
|
|
* NAMED_LAMBDA is handled in an ad hoc special case manner (see
|
|
|
|
* LeaveFunction) that we should consider rewriting.
|
|
|
|
*
|
|
|
|
* Post-conditions:
|
|
|
|
* + pc->decls().lookupFirst(name) == pn
|
|
|
|
* + The given name 'pn' has been converted in-place into a
|
|
|
|
* non-placeholder definition.
|
|
|
|
* + If this is a function scope (sc->inFunction), 'pn' is bound to a
|
|
|
|
* particular local/argument slot.
|
|
|
|
* + PND_CONST is set for Definition::COSNT
|
|
|
|
* + Pre-existing uses of pre-existing placeholders have been linked to
|
|
|
|
* 'pn' if they are in the scope of 'pn'.
|
|
|
|
* + Pre-existing placeholders in the scope of 'pn' have been removed.
|
|
|
|
*/
|
|
|
|
bool define(TokenStream& ts, HandlePropertyName name, Node pn, Definition::Kind);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let definitions may shadow same-named definitions in enclosing scopes.
|
|
|
|
* To represesent this, 'decls' is not a plain map, but actually:
|
|
|
|
* decls :: name -> stack of definitions
|
|
|
|
* New bindings are pushed onto the stack, name lookup always refers to the
|
|
|
|
* top of the stack, and leaving a block scope calls popLetDecl for each
|
|
|
|
* name in the block's scope.
|
|
|
|
*/
|
|
|
|
void popLetDecl(JSAtom* atom);
|
|
|
|
|
|
|
|
/* See the sad story in defineArg. */
|
|
|
|
void prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl);
|
|
|
|
|
|
|
|
/* See the sad story in MakeDefIntoUse. */
|
|
|
|
void updateDecl(TokenStream& ts, JSAtom* atom, Node newDecl);
|
|
|
|
|
|
|
|
// After a script has been parsed, the parser generates the code's
|
|
|
|
// "bindings". Bindings are a data-structure, ultimately stored in the
|
|
|
|
// compiled JSScript, that serve three purposes:
|
|
|
|
//
|
|
|
|
// - After parsing, the ParseContext is destroyed and 'decls' along with
|
|
|
|
// it. Mostly, the emitter just uses the binding information stored in
|
|
|
|
// the use/def nodes, but the emitter occasionally needs 'bindings' for
|
|
|
|
// various scope-related queries.
|
|
|
|
//
|
|
|
|
// - For functions, bindings provide the initial js::Shape to use when
|
|
|
|
// creating a dynamic scope object (js::CallObject). This shape is used
|
|
|
|
// during dynamic name lookup.
|
|
|
|
//
|
|
|
|
// - Sometimes a script's bindings are accessed at runtime to retrieve the
|
|
|
|
// contents of the lexical scope (e.g., from the debugger).
|
|
|
|
//
|
|
|
|
// - For global and eval scripts, ES6 15.1.8 specifies that if there are
|
|
|
|
// name conflicts in the script, *no* bindings from the script are
|
|
|
|
// instantiated. So, record the vars and lexical bindings to check for
|
|
|
|
// redeclarations in the prologue.
|
|
|
|
bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
|
|
|
|
MutableHandle<Bindings> bindings) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
ParseContext** parserPC; /* this points to the Parser's active pc
|
|
|
|
and holds either |this| or one of
|
|
|
|
|this|'s descendents */
|
|
|
|
|
|
|
|
// Value for parserPC to restore at the end. Use 'parent' instead for
|
|
|
|
// information about the parse chain, this may be nullptr if
|
|
|
|
// parent != nullptr.
|
|
|
|
ParseContext<ParseHandler>* oldpc;
|
|
|
|
|
|
|
|
public:
|
|
|
|
OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */
|
|
|
|
|
|
|
|
// All inner functions in this context. Only filled in when parsing syntax.
|
|
|
|
Rooted<TraceableVector<JSFunction*>> innerFunctions;
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
bool inDeclDestructuring:1;
|
|
|
|
|
|
|
|
ParseContext(Parser<ParseHandler>* prs, GenericParseContext* parent,
|
|
|
|
Node maybeFunction, SharedContext* sc, Directives* newDirectives)
|
|
|
|
: GenericParseContext(parent, sc),
|
|
|
|
bodyid(0), // initialized in init()
|
|
|
|
stmtStack(prs->context),
|
|
|
|
maybeFunction(maybeFunction),
|
|
|
|
lastYieldOffset(NoYieldOffset),
|
2019-08-18 21:26:54 +00:00
|
|
|
lastAwaitOffset(NoAwaitOffset),
|
2017-04-19 07:56:45 +00:00
|
|
|
blockScopeDepth(0),
|
|
|
|
blockNode(ParseHandler::null()),
|
|
|
|
decls_(prs->context, prs->alloc),
|
|
|
|
args_(prs->context),
|
|
|
|
vars_(prs->context),
|
|
|
|
bodyLevelLexicals_(prs->context),
|
|
|
|
parserPC(&prs->pc),
|
|
|
|
oldpc(prs->pc),
|
|
|
|
lexdeps(prs->context),
|
|
|
|
innerFunctions(prs->context, TraceableVector<JSFunction*>(prs->context)),
|
|
|
|
newDirectives(newDirectives),
|
|
|
|
inDeclDestructuring(false)
|
|
|
|
{
|
|
|
|
prs->pc = this;
|
|
|
|
if (sc->isFunctionBox())
|
|
|
|
parseUsingFunctionBox.emplace(prs->context, sc->asFunctionBox());
|
|
|
|
}
|
|
|
|
|
|
|
|
~ParseContext();
|
|
|
|
|
|
|
|
bool init(Parser<ParseHandler>& parser);
|
|
|
|
|
|
|
|
unsigned blockid() { return stmtStack.innermost() ? stmtStack.innermost()->blockid : bodyid; }
|
|
|
|
|
|
|
|
StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); }
|
|
|
|
StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
|
2015-12-18 21:18:19 +00:00
|
|
|
StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
|
2017-04-19 07:56:45 +00:00
|
|
|
JSObject* innermostStaticScope() const {
|
|
|
|
if (StmtInfoPC* stmt = innermostScopeStmt())
|
|
|
|
return stmt->staticScope;
|
|
|
|
return sc->staticScope();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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() { } } } }
|
|
|
|
//
|
2015-12-18 21:18:19 +00:00
|
|
|
bool atBodyLevel(StmtInfoPC* stmt) {
|
2017-04-19 07:56:45 +00:00
|
|
|
// 'eval' and non-syntactic scripts are always under an invisible
|
|
|
|
// lexical scope, but since it is not syntactic, it should still be
|
|
|
|
// considered at body level.
|
|
|
|
if (sc->staticScope()->is<StaticEvalObject>()) {
|
2015-12-18 21:18:19 +00:00
|
|
|
bool bl = !stmt->enclosing;
|
|
|
|
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
|
|
|
|
MOZ_ASSERT_IF(bl, stmt->staticScope
|
|
|
|
->template as<StaticBlockObject>()
|
|
|
|
.enclosingStaticScope() == sc->staticScope());
|
2017-04-19 07:56:45 +00:00
|
|
|
return bl;
|
|
|
|
}
|
2015-12-18 21:18:19 +00:00
|
|
|
return !stmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool atBodyLevel() {
|
|
|
|
return atBodyLevel(innermostStmt());
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool atGlobalLevel() {
|
|
|
|
return atBodyLevel() && sc->isGlobalContext() && !innermostScopeStmt();
|
|
|
|
}
|
|
|
|
|
|
|
|
// True if we are at the topmost level of a module only.
|
|
|
|
bool atModuleLevel() {
|
|
|
|
return atBodyLevel() && sc->isModuleBox();
|
|
|
|
}
|
|
|
|
|
|
|
|
// True if the current lexical scope is the topmost level of a module.
|
|
|
|
bool atModuleScope() {
|
|
|
|
return sc->isModuleBox() && !innermostScopeStmt();
|
|
|
|
}
|
|
|
|
|
|
|
|
// True if this is the ParseContext for the body of a function created by
|
|
|
|
// the Function constructor.
|
|
|
|
bool isFunctionConstructorBody() const {
|
|
|
|
return sc->isFunctionBox() && !parent && sc->asFunctionBox()->function()->isLambda();
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool useAsmOrInsideUseAsm() const {
|
|
|
|
return sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename ParseHandler>
|
|
|
|
inline
|
|
|
|
Directives::Directives(ParseContext<ParseHandler>* parent)
|
|
|
|
: strict_(parent->sc->strict()),
|
|
|
|
asmJS_(parent->useAsmOrInsideUseAsm())
|
|
|
|
{}
|
|
|
|
|
|
|
|
template <typename ParseHandler>
|
|
|
|
struct BindData;
|
|
|
|
|
|
|
|
class CompExprTransplanter;
|
|
|
|
|
|
|
|
enum VarContext { HoistVars, DontHoistVars };
|
|
|
|
enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
|
|
|
|
enum class PropertyType {
|
|
|
|
Normal,
|
|
|
|
Shorthand,
|
2017-08-24 03:48:44 +00:00
|
|
|
CoverInitializedName,
|
2017-04-19 07:56:45 +00:00
|
|
|
Getter,
|
|
|
|
GetterNoExpressionClosure,
|
|
|
|
Setter,
|
|
|
|
SetterNoExpressionClosure,
|
|
|
|
Method,
|
|
|
|
GeneratorMethod,
|
2019-08-18 21:26:54 +00:00
|
|
|
AsyncMethod,
|
2017-04-19 07:56:45 +00:00
|
|
|
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 };
|
|
|
|
|
|
|
|
template <typename ParseHandler>
|
|
|
|
class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
|
|
|
{
|
|
|
|
class MOZ_STACK_CLASS AutoPushStmtInfoPC
|
|
|
|
{
|
|
|
|
Parser<ParseHandler>& parser_;
|
|
|
|
StmtInfoPC stmt_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
AutoPushStmtInfoPC(Parser<ParseHandler>& parser, StmtType type);
|
|
|
|
AutoPushStmtInfoPC(Parser<ParseHandler>& parser, StmtType type,
|
|
|
|
NestedScopeObject& staticScope);
|
|
|
|
~AutoPushStmtInfoPC();
|
|
|
|
|
|
|
|
bool generateBlockId();
|
|
|
|
bool makeInnermostLexicalScope(StaticBlockObject& blockObj);
|
|
|
|
|
|
|
|
StmtInfoPC& operator*() { return stmt_; }
|
|
|
|
StmtInfoPC* operator->() { return &stmt_; }
|
|
|
|
operator StmtInfoPC*() { return &stmt_; }
|
|
|
|
};
|
|
|
|
|
2017-08-24 03:48:44 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
|
|
|
|
* // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
|
|
|
|
* possibleError.checkForExprErrors();
|
|
|
|
*
|
|
|
|
* PossibleError possibleError(*this);
|
|
|
|
* possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
|
|
|
|
* possibleError.setResolved();
|
|
|
|
* // Returns true, no error is reported.
|
|
|
|
* possibleError.checkForExprErrors();
|
|
|
|
*
|
|
|
|
* PossibleError possibleError(*this);
|
|
|
|
* // Returns true, no error is reported.
|
|
|
|
* possibleError.checkForExprErrors();
|
|
|
|
*/
|
|
|
|
class MOZ_STACK_CLASS PossibleError
|
|
|
|
{
|
|
|
|
enum ErrorState { None, Pending };
|
|
|
|
ErrorState state_;
|
|
|
|
|
|
|
|
// Error reporting fields.
|
|
|
|
uint32_t offset_;
|
|
|
|
unsigned errorNumber_;
|
|
|
|
ParseReportKind reportKind_;
|
|
|
|
Parser<ParseHandler>& parser_;
|
|
|
|
bool strict_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit PossibleError(Parser<ParseHandler>& parser);
|
|
|
|
|
|
|
|
// Set a pending error.
|
|
|
|
void setPending(ParseReportKind kind, unsigned errorNumber, bool strict);
|
|
|
|
|
|
|
|
// Resolve any pending error.
|
|
|
|
void setResolved();
|
|
|
|
|
|
|
|
// Return true if an error is pending without reporting
|
|
|
|
bool hasError();
|
|
|
|
|
|
|
|
// If there is a pending error report it and return false, otherwise return
|
|
|
|
// true.
|
|
|
|
bool checkForExprErrors();
|
|
|
|
|
|
|
|
// 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 transferErrorTo(PossibleError* other);
|
|
|
|
};
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
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<ParseHandler>* pc;
|
|
|
|
|
|
|
|
// List of all block scopes.
|
|
|
|
AutoObjectVector blockScopes;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
typedef typename ParseHandler::Node Node;
|
|
|
|
typedef typename ParseHandler::DefinitionNode DefinitionNode;
|
|
|
|
|
|
|
|
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); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
|
|
|
|
unsigned errorNumber, va_list args);
|
|
|
|
public:
|
|
|
|
bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
|
|
|
|
bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...);
|
|
|
|
bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
|
|
|
|
...);
|
|
|
|
|
|
|
|
Parser(ExclusiveContext* cx, LifoAlloc* alloc, const ReadOnlyCompileOptions& options,
|
|
|
|
const char16_t* chars, size_t length, bool foldConstants,
|
|
|
|
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, ParseContext<ParseHandler>* outerpc,
|
|
|
|
Directives directives, GeneratorKind generatorKind,
|
2018-09-15 17:35:22 +00:00
|
|
|
FunctionAsyncKind asyncKind,
|
2017-04-19 07:56:45 +00:00
|
|
|
JSObject* enclosingStaticScope);
|
|
|
|
|
|
|
|
// Use when the funbox is the outermost.
|
|
|
|
FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
|
2018-09-15 17:35:22 +00:00
|
|
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
|
|
|
HandleObject enclosingStaticScope)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
return newFunctionBox(fn, fun, nullptr, directives, generatorKind,
|
2018-09-15 17:35:22 +00:00
|
|
|
asyncKind,
|
2017-04-19 07:56:45 +00:00
|
|
|
enclosingStaticScope);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use when the funbox should be linked to the outerpc's innermost scope.
|
|
|
|
FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
|
2018-09-15 17:35:22 +00:00
|
|
|
Directives directives, GeneratorKind generatorKind,
|
|
|
|
FunctionAsyncKind asyncKind)
|
2017-04-19 07:56:45 +00:00
|
|
|
{
|
|
|
|
RootedObject enclosing(context, outerpc->innermostStaticScope());
|
2018-09-15 17:35:22 +00:00
|
|
|
return newFunctionBox(fn, fun, outerpc, directives, generatorKind, asyncKind, enclosing);
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ModuleBox* newModuleBox(Node pn, HandleModuleObject module);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
2018-09-15 18:05:08 +00:00
|
|
|
FunctionAsyncKind asyncKind, HandleObject proto);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
bool generateBlockId(JSObject* staticScope, uint32_t* blockIdOut) {
|
|
|
|
if (blockScopes.length() == StmtInfoPC::BlockIdLimit) {
|
|
|
|
tokenStream.reportError(JSMSG_NEED_DIET, "program");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(blockScopes.length() < StmtInfoPC::BlockIdLimit);
|
|
|
|
*blockIdOut = blockScopes.length();
|
|
|
|
return blockScopes.append(staticScope);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
|
2019-08-18 21:26:54 +00:00
|
|
|
inline Node newAwaitExpression(uint32_t begin, Node expr);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
inline bool abortIfSyntaxParser();
|
|
|
|
|
|
|
|
public:
|
|
|
|
/* Public entry points for parsing. */
|
|
|
|
Node statement(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();
|
|
|
|
|
|
|
|
// Parse the body of a global script.
|
|
|
|
Node globalBody();
|
|
|
|
|
|
|
|
// Parse a module.
|
|
|
|
Node standaloneModule(Handle<ModuleObject*> module);
|
|
|
|
|
|
|
|
// Parse a function, given only its body. Used for the Function and
|
|
|
|
// Generator constructors.
|
|
|
|
Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
|
|
|
|
GeneratorKind generatorKind,
|
2018-09-15 18:05:08 +00:00
|
|
|
FunctionAsyncKind asyncKind,
|
2017-04-19 07:56:45 +00:00
|
|
|
Directives inheritedDirectives, Directives* newDirectives,
|
|
|
|
HandleObject enclosingStaticScope);
|
|
|
|
|
|
|
|
// Parse a function, given only its arguments and body. Used for lazily
|
|
|
|
// parsed functions.
|
2018-09-15 18:05:08 +00:00
|
|
|
Node standaloneLazyFunction(HandleFunction fun, bool strict,
|
|
|
|
GeneratorKind generatorKind,
|
|
|
|
FunctionAsyncKind asyncKind);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
bool functionArgsAndBodyGeneric(InHandling inHandling, YieldHandling yieldHandling, Node pn,
|
|
|
|
HandleFunction fun, FunctionSyntaxKind kind);
|
|
|
|
|
|
|
|
// 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 checkYieldNameValidity();
|
|
|
|
bool yieldExpressionsSupported() {
|
2019-08-18 21:26:54 +00:00
|
|
|
return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
|
2017-04-19 07:56:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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).
|
|
|
|
*/
|
2019-08-18 21:26:54 +00:00
|
|
|
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
|
|
|
|
FunctionAsyncKind asyncKind = SyncFunction);
|
|
|
|
Node functionExpr(InvokedPrediction invoked = PredictUninvoked,
|
|
|
|
FunctionAsyncKind asyncKind = SyncFunction);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node statements(YieldHandling yieldHandling);
|
|
|
|
|
|
|
|
Node blockStatement(YieldHandling yieldHandling);
|
|
|
|
Node ifStatement(YieldHandling yieldHandling);
|
|
|
|
Node doWhileStatement(YieldHandling yieldHandling);
|
|
|
|
Node whileStatement(YieldHandling yieldHandling);
|
2017-08-20 04:16:16 +00:00
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
Node forStatement(YieldHandling yieldHandling);
|
2017-08-20 04:16:16 +00:00
|
|
|
bool forHeadStart(YieldHandling yieldHandling,
|
|
|
|
ParseNodeKind* forHeadKind,
|
|
|
|
Node* forInitialPart,
|
|
|
|
mozilla::Maybe<AutoPushStmtInfoPC>& letStmt,
|
|
|
|
MutableHandle<StaticBlockObject*> blockObj,
|
|
|
|
Node* forLetImpliedBlock,
|
|
|
|
Node* forInOrOfExpression);
|
|
|
|
bool validateForInOrOfLHSExpression(Node target);
|
|
|
|
Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);
|
|
|
|
|
|
|
|
void assertCurrentLexicalStaticBlockIs(ParseContext<ParseHandler>* pc,
|
|
|
|
Handle<StaticBlockObject*> blockObj);
|
|
|
|
|
2017-04-19 07:56:45 +00:00
|
|
|
Node switchStatement(YieldHandling yieldHandling);
|
|
|
|
Node continueStatement(YieldHandling yieldHandling);
|
|
|
|
Node breakStatement(YieldHandling yieldHandling);
|
|
|
|
Node returnStatement(YieldHandling yieldHandling);
|
|
|
|
Node withStatement(YieldHandling yieldHandling);
|
|
|
|
Node labeledStatement(YieldHandling yieldHandling);
|
|
|
|
Node throwStatement(YieldHandling yieldHandling);
|
|
|
|
Node tryStatement(YieldHandling yieldHandling);
|
|
|
|
Node debuggerStatement();
|
|
|
|
|
|
|
|
Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst);
|
|
|
|
Node importDeclaration();
|
|
|
|
Node exportDeclaration();
|
|
|
|
Node expressionStatement(YieldHandling yieldHandling,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-08-20 04:16:16 +00:00
|
|
|
|
|
|
|
// Declaration parsing. The main entrypoint is Parser::declarationList,
|
|
|
|
// with sub-functionality split out into the remaining methods.
|
|
|
|
|
|
|
|
// |blockObj| 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,
|
|
|
|
StaticBlockObject* blockObj = nullptr,
|
|
|
|
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, TokenKind tt, BindData<ParseHandler>* data,
|
|
|
|
bool initialDeclaration, YieldHandling yieldHandling,
|
|
|
|
ParseNodeKind* forHeadKind, Node* forInOrOfExpression);
|
|
|
|
Node declarationName(Node decl, TokenKind tt, BindData<ParseHandler>* data,
|
|
|
|
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,
|
|
|
|
BindData<ParseHandler>* data, bool initialDeclaration,
|
|
|
|
YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
|
|
|
|
Node* forInOrOfExpression);
|
|
|
|
|
2017-08-24 03:48:44 +00:00
|
|
|
Node expr(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
|
|
|
PossibleError* possibleError,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node expr(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-08-24 03:48:44 +00:00
|
|
|
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
|
|
|
PossibleError* possibleError,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2019-08-18 21:26:54 +00:00
|
|
|
Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node yieldExpression(InHandling inHandling);
|
|
|
|
Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
2017-08-24 03:48:44 +00:00
|
|
|
PossibleError* possibleError,
|
2017-04-19 07:56:45 +00:00
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
|
|
|
Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
2017-08-24 03:48:44 +00:00
|
|
|
PossibleError* possibleError,
|
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
2017-08-24 03:48:44 +00:00
|
|
|
PossibleError* possibleError,
|
2017-04-19 07:56:45 +00:00
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-08-24 03:48:44 +00:00
|
|
|
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
|
|
|
PossibleError* possibleError, TokenKind tt,
|
|
|
|
bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
|
|
|
|
bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
|
2017-08-24 03:48:44 +00:00
|
|
|
Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
|
|
|
|
PossibleError* possibleError, TokenKind tt,
|
2017-04-19 07:56:45 +00:00
|
|
|
InvokedPrediction invoked = PredictUninvoked);
|
2017-08-24 03:48:44 +00:00
|
|
|
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling,
|
|
|
|
PossibleError* possibleError);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
|
|
|
|
TripledotHandling tripledotHandling);
|
|
|
|
|
|
|
|
bool tryNewTarget(Node& newTarget);
|
|
|
|
bool checkAndMarkSuperScope();
|
|
|
|
|
|
|
|
Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
|
|
|
|
HandlePropertyName funName);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Additional JS parsers.
|
|
|
|
*/
|
|
|
|
bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
|
|
|
|
Node funcpn, bool* hasRest);
|
|
|
|
|
|
|
|
Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name,
|
|
|
|
FunctionSyntaxKind kind, GeneratorKind generatorKind,
|
2018-09-15 18:05:08 +00:00
|
|
|
FunctionAsyncKind asyncKind,
|
2015-12-18 21:18:19 +00:00
|
|
|
InvokedPrediction invoked = PredictUninvoked,
|
|
|
|
Node* assignmentForAnnexBOut = nullptr);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
|
|
|
|
FunctionSyntaxKind kind, GeneratorKind generatorKind,
|
2018-09-15 18:05:08 +00:00
|
|
|
FunctionAsyncKind asyncKind,
|
2017-04-19 07:56:45 +00:00
|
|
|
Directives inheritedDirectives, Directives* newDirectives);
|
|
|
|
|
|
|
|
Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin);
|
|
|
|
|
|
|
|
Node condition(InHandling inHandling, YieldHandling yieldHandling);
|
|
|
|
|
|
|
|
/* comprehensions */
|
|
|
|
Node legacyComprehensionTail(Node kid, unsigned blockid, GeneratorKind comprehensionKind,
|
|
|
|
ParseContext<ParseHandler>* outerpc,
|
|
|
|
unsigned innerBlockScopeDepth);
|
|
|
|
Node legacyArrayComprehension(Node array);
|
|
|
|
Node generatorComprehensionLambda(GeneratorKind comprehensionKind, unsigned begin,
|
|
|
|
Node innerStmt);
|
|
|
|
Node legacyGeneratorExpr(Node kid);
|
|
|
|
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);
|
|
|
|
Node destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
|
|
|
|
TokenKind tt);
|
2019-08-18 21:26:54 +00:00
|
|
|
Node destructuringExprWithoutYieldOrAwait(YieldHandling yieldHandling, BindData<ParseHandler>* data,
|
|
|
|
TokenKind tt);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
Node newBoundImportForCurrentName();
|
|
|
|
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
|
|
|
|
bool addExportName(JSAtom* exportName);
|
|
|
|
|
|
|
|
enum ClassContext { ClassStatement, ClassExpression };
|
|
|
|
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling);
|
|
|
|
|
|
|
|
Node identifierName(YieldHandling yieldHandling);
|
|
|
|
|
|
|
|
bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
|
|
|
|
|
|
|
|
bool allowsForEachIn() {
|
|
|
|
#if !JS_HAS_FOR_EACH_IN
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
return versionNumber() >= JSVERSION_1_6;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
enum AssignmentFlavor {
|
|
|
|
PlainAssignment,
|
|
|
|
CompoundAssignment,
|
|
|
|
KeyedDestructuringAssignment,
|
|
|
|
IncrementAssignment,
|
2017-08-20 04:16:16 +00:00
|
|
|
DecrementAssignment,
|
|
|
|
ForInOrOfTarget
|
2017-04-19 07:56:45 +00:00
|
|
|
};
|
|
|
|
|
2017-08-24 03:48:44 +00:00
|
|
|
bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
|
|
|
|
PossibleError* possibleError=nullptr);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool matchInOrOf(bool* isForInp, bool* isForOfp);
|
|
|
|
|
|
|
|
bool checkFunctionArguments();
|
|
|
|
|
|
|
|
bool defineFunctionThis();
|
|
|
|
Node newThisName();
|
|
|
|
|
|
|
|
bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
|
2015-12-18 21:18:19 +00:00
|
|
|
bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn);
|
|
|
|
bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
|
2015-12-18 21:18:19 +00:00
|
|
|
bool* pbodyProcessed, Node* assignmentForAnnexBOut);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
|
|
|
|
bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
|
|
|
|
|
|
|
|
// Use when the current token is TOK_NAME and is known to be 'let'.
|
|
|
|
bool shouldParseLetDeclaration(bool* parseDeclOut);
|
|
|
|
|
|
|
|
// Use when the lookahead token is TOK_NAME and is known to be 'let'. If a
|
|
|
|
// let declaration should be parsed, the TOK_NAME token of 'let' is
|
|
|
|
// consumed. Otherwise, the current token remains the TOK_NAME token of
|
|
|
|
// 'let'.
|
|
|
|
bool peekShouldParseLetDeclaration(bool* parseDeclOut, TokenStream::Modifier modifier);
|
|
|
|
|
|
|
|
public:
|
|
|
|
enum FunctionCallBehavior {
|
|
|
|
PermitAssignmentToFunctionCalls,
|
|
|
|
ForbidAssignmentToFunctionCalls
|
|
|
|
};
|
|
|
|
|
|
|
|
bool isValidSimpleAssignmentTarget(Node node,
|
|
|
|
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool reportIfArgumentsEvalTarget(Node nameNode);
|
|
|
|
bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
|
|
|
|
|
|
|
|
bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
|
|
|
|
|
|
|
|
bool checkStrictAssignment(Node lhs);
|
|
|
|
|
|
|
|
bool checkStrictBinding(PropertyName* name, Node pn);
|
|
|
|
bool defineArg(Node funcpn, HandlePropertyName name,
|
|
|
|
bool disallowDuplicateArgs = false, Node* duplicatedArg = nullptr);
|
|
|
|
Node pushLexicalScope(AutoPushStmtInfoPC& stmt);
|
|
|
|
Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, AutoPushStmtInfoPC& stmt);
|
|
|
|
Node pushLetScope(Handle<StaticBlockObject*> blockObj, AutoPushStmtInfoPC& stmt);
|
|
|
|
bool noteNameUse(HandlePropertyName name, Node pn);
|
|
|
|
Node propertyName(YieldHandling yieldHandling, Node propList,
|
|
|
|
PropertyType* propType, MutableHandleAtom propAtom);
|
|
|
|
Node computedPropertyName(YieldHandling yieldHandling, Node literal);
|
|
|
|
Node arrayInitializer(YieldHandling yieldHandling);
|
|
|
|
Node newRegExp();
|
|
|
|
|
2017-08-24 03:48:44 +00:00
|
|
|
Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
2015-12-18 21:18:19 +00:00
|
|
|
enum PrepareLexicalKind {
|
|
|
|
PrepareLet,
|
|
|
|
PrepareConst,
|
|
|
|
PrepareFunction
|
|
|
|
};
|
|
|
|
bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
|
|
|
|
bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
|
|
|
PrepareLexicalKind prepareWhat,
|
2015-12-18 21:18:19 +00:00
|
|
|
ParseNode* pn, const TokenPos& pos);
|
2015-12-18 21:18:19 +00:00
|
|
|
Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat,
|
|
|
|
const TokenPos& pos);
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
|
|
|
|
|
|
|
|
// Top-level entrypoint into destructuring pattern checking/name-analyzing.
|
|
|
|
bool checkDestructuringPattern(BindData<ParseHandler>* data, Node pattern);
|
|
|
|
|
|
|
|
// 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(BindData<ParseHandler>* data, Node arrayPattern);
|
|
|
|
bool checkDestructuringObject(BindData<ParseHandler>* data, Node objectPattern);
|
|
|
|
bool checkDestructuringName(BindData<ParseHandler>* data, Node expr);
|
|
|
|
|
2015-12-18 21:18:19 +00:00
|
|
|
bool bindInitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool bindInitialized(BindData<ParseHandler>* data, Node pn);
|
2015-12-18 21:18:19 +00:00
|
|
|
bool bindUninitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
|
2017-04-19 07:56:45 +00:00
|
|
|
bool bindUninitialized(BindData<ParseHandler>* data, Node pn);
|
|
|
|
bool makeSetCall(Node node, unsigned errnum);
|
2017-08-20 04:16:16 +00:00
|
|
|
|
|
|
|
Node cloneForInOrOfDeclarationForAssignment(Node decl);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node cloneLeftHandSide(Node opn);
|
2017-08-20 04:16:16 +00:00
|
|
|
Node cloneDestructuringDefault(Node opn);
|
2017-04-19 07:56:45 +00:00
|
|
|
Node cloneParseTree(Node opn);
|
|
|
|
|
|
|
|
Node newNumber(const Token& tok) {
|
|
|
|
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
bindDestructuringArg(BindData<ParseHandler>* data,
|
|
|
|
HandlePropertyName name, Parser<ParseHandler>* parser);
|
|
|
|
|
|
|
|
static bool
|
|
|
|
bindLexical(BindData<ParseHandler>* data,
|
|
|
|
HandlePropertyName name, Parser<ParseHandler>* parser);
|
|
|
|
|
|
|
|
static bool
|
|
|
|
bindVar(BindData<ParseHandler>* data,
|
|
|
|
HandlePropertyName name, Parser<ParseHandler>* parser);
|
|
|
|
|
|
|
|
static Node null() { return ParseHandler::null(); }
|
|
|
|
|
|
|
|
bool reportRedeclaration(Node pn, Definition::Kind redeclKind, HandlePropertyName name);
|
|
|
|
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
|
|
|
|
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler>* pc, JSAtom* atom);
|
|
|
|
|
|
|
|
bool leaveFunction(Node fn, ParseContext<ParseHandler>* outerpc,
|
|
|
|
FunctionSyntaxKind kind = Expression);
|
|
|
|
|
|
|
|
TokenPos pos() const { return tokenStream.currentToken().pos; }
|
|
|
|
|
|
|
|
bool asmJS(Node list);
|
|
|
|
|
|
|
|
void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
|
|
|
|
|
|
|
|
bool warnOnceAboutExprClosure();
|
2019-08-18 21:26:54 +00:00
|
|
|
bool warnOnceAboutAsyncFuncs();
|
2017-04-19 07:56:45 +00:00
|
|
|
|
|
|
|
friend class LegacyCompExprTransplanter;
|
|
|
|
friend struct BindData<ParseHandler>;
|
|
|
|
};
|
|
|
|
|
|
|
|
} /* namespace frontend */
|
|
|
|
} /* namespace js */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convenience macro to access Parser.tokenStream as a pointer.
|
|
|
|
*/
|
|
|
|
#define TS(p) (&(p)->tokenStream)
|
|
|
|
|
|
|
|
#endif /* frontend_Parser_h */
|