/* -*- 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/Maybe.h"

#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)
    {}
};

/*
 * 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;

    // 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 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 */

    typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> DeclaredNameSet;
    DeclaredNameSet bodyLevelLexicallyDeclaredNames_;

    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 kind,
                bool declaringVarInCatchBody = false);

    /*
     * 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. */
    MOZ_MUST_USE bool 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<GCVector<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),
        blockScopeDepth(0),
        blockNode(ParseHandler::null()),
        decls_(prs->context, prs->alloc),
        args_(prs->context),
        vars_(prs->context),
        bodyLevelLexicals_(prs->context),
        bodyLevelLexicallyDeclaredNames_(prs->alloc),
        parserPC(&prs->pc),
        oldpc(prs->pc),
        lexdeps(prs->context),
        innerFunctions(prs->context, GCVector<JSFunction*>(prs->context)),
        newDirectives(newDirectives),
        inDeclDestructuring(false)
    {
        prs->pc = this;
        if (sc->isFunctionBox())
            parseUsingFunctionBox.emplace(prs->context, sc->asFunctionBox());
    }

    ~ParseContext();

    MOZ_MUST_USE 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(); }
    StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
    JSObject* innermostStaticScope() const {
        if (StmtInfoPC* stmt = innermostScopeStmt())
            return stmt->staticScope;
        return sc->staticScope();
    }

    bool isBodyLevelLexicallyDeclaredName(HandleAtom name) {
        return bodyLevelLexicallyDeclaredNames_.has(name);
    }

    bool addBodyLevelLexicallyDeclaredName(TokenStream& ts, HandleAtom name) {
        if (!bodyLevelLexicallyDeclaredNames_.put(name))
            return ts.reportError(JSMSG_OUT_OF_MEMORY);
        return true;
    }

    // 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(StmtInfoPC* stmt) {
        // '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<StaticEvalScope>()) {
            bool bl = !stmt->enclosing;
            MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
            MOZ_ASSERT_IF(bl, stmt->staticScope
                                  ->template as<StaticBlockScope>()
                                  .enclosingStaticScope() == sc->staticScope());
            return bl;
        }
        return !stmt;
    }

    bool atBodyLevel() {
        return atBodyLevel(innermostStmt());
    }

    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 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,
    CoverInitializedName,
    Getter,
    GetterNoExpressionClosure,
    Setter,
    SetterNoExpressionClosure,
    Method,
    GeneratorMethod,
    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 final : 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,
                           NestedStaticScope& staticScope);
        ~AutoPushStmtInfoPC();

        bool generateBlockId();
        bool makeInnermostLexicalScope(StaticBlockScope& blockScope);

        StmtInfoPC& operator*() { return stmt_; }
        StmtInfoPC* operator->() { return &stmt_; }
        operator StmtInfoPC*() { return &stmt_; }
    };

    /*
     * 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. Only a single error may be set per instance.
          // Returns true on success or false on failure.
          bool 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);
    };

  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,
                                JSObject* enclosingStaticScope);

    // Use when the funbox is the outermost.
    FunctionBox* newFunctionBox(Node fn, HandleFunction fun, Directives directives,
                                GeneratorKind generatorKind, HandleObject enclosingStaticScope)
    {
        return newFunctionBox(fn, fun, nullptr, directives, generatorKind,
                              enclosingStaticScope);
    }

    // Use when the funbox should be linked to the outerpc's innermost scope.
    FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
                                Directives directives, GeneratorKind generatorKind)
    {
        RootedObject enclosing(context, outerpc->innermostStaticScope());
        return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
    }

    ModuleBox* newModuleBox(Node pn, HandleModuleObject module, ModuleBuilder& builder);

    /*
     * 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,
                            HandleObject proto);

    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);

    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, ModuleBuilder& builder);

    // Parse a function, given only its body. Used for the Function and
    // Generator constructors.
    Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
                                GeneratorKind generatorKind,
                                Directives inheritedDirectives, Directives* newDirectives,
                                HandleObject enclosingStaticScope);

    // Parse a function, given only its arguments and body. Used for lazily
    // parsed functions.
    Node standaloneLazyFunction(HandleFunction fun, bool strict, GeneratorKind generatorKind);

    /*
     * 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() {
        return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
    }

    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(YieldHandling yieldHandling, DefaultHandling defaultHandling);
    Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
    Node statements(YieldHandling yieldHandling);

    Node blockStatement(YieldHandling yieldHandling,
                        unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
    Node ifStatement(YieldHandling yieldHandling);
    Node doWhileStatement(YieldHandling yieldHandling);
    Node whileStatement(YieldHandling yieldHandling);

    Node forStatement(YieldHandling yieldHandling);
    bool forHeadStart(YieldHandling yieldHandling,
                      ParseNodeKind* forHeadKind,
                      Node* forInitialPart,
                      mozilla::Maybe<AutoPushStmtInfoPC>& letStmt,
                      MutableHandle<StaticBlockScope*> blockScope,
                      Node* forLetImpliedBlock,
                      Node* forInOrOfExpression);
    bool validateForInOrOfLHSExpression(Node target);
    Node expressionAfterForInOrOf(ParseNodeKind forHeadKind, YieldHandling yieldHandling);

    void assertCurrentLexicalStaticBlockIs(ParseContext<ParseHandler>* pc,
                                           Handle<StaticBlockScope*> blockScope);

    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);

    // 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,
                         StaticBlockScope* blockScope = 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);

    Node expr(InHandling inHandling, YieldHandling yieldHandling,
              TripledotHandling tripledotHandling,
              PossibleError* possibleError,
              InvokedPrediction invoked = PredictUninvoked);
    Node expr(InHandling inHandling, YieldHandling yieldHandling,
              TripledotHandling tripledotHandling,
              InvokedPrediction invoked = PredictUninvoked);
    Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                    TripledotHandling tripledotHandling,
                    PossibleError* possibleError,
                    InvokedPrediction invoked = PredictUninvoked);
    Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                    TripledotHandling tripledotHandling,
                    InvokedPrediction invoked = PredictUninvoked);
    Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
    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,
                   InvokedPrediction invoked = PredictUninvoked);
    Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                    PossibleError* possibleError, TokenKind tt,
                    bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
    Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                    bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
    Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                     PossibleError* possibleError, TokenKind tt,
                     InvokedPrediction invoked = PredictUninvoked);
    Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                      TripledotHandling tripledotHandling,
                      PossibleError* possibleError);
    Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                      TripledotHandling tripledotHandling);

    bool tryNewTarget(Node& newTarget);
    bool checkAndMarkSuperScope();

    Node methodDefinition(YieldHandling yieldHandling, PropertyType propType, HandleAtom funName);

    /*
     * Additional JS parsers.
     */
    bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                           Node funcpn, bool* hasRest);

    Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandleAtom name,
                     FunctionSyntaxKind kind, GeneratorKind generatorKind,
                     InvokedPrediction invoked = PredictUninvoked,
                     Node* assignmentForAnnexBOut = nullptr);
    bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
                             FunctionSyntaxKind kind, GeneratorKind generatorKind,
                             Directives inheritedDirectives, Directives* newDirectives);

    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);
    Node destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                           TokenKind tt);
    Node destructuringExprWithoutYield(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                                       TokenKind tt, unsigned msg);

    Node newBoundImportForCurrentName();
    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);

    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,
        DecrementAssignment,
        ForInOrOfTarget
    };

    bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
                                     PossibleError* possibleError=nullptr);
    bool matchInOrOf(bool* isForInp, bool* isForOfp);

    bool checkFunctionArguments();

    bool defineFunctionThis();
    Node newThisName();

    bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
    bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn);
    bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn);
    bool checkFunctionDefinition(HandleAtom funAtom, Node* pn, FunctionSyntaxKind kind,
                                 bool* pbodyProcessed, Node* assignmentForAnnexBOut);
    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<StaticBlockScope*> blockScope, AutoPushStmtInfoPC& stmt);
    Node pushLetScope(Handle<StaticBlockScope*> blockScope, 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();

    Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);

    enum PrepareLexicalKind {
        PrepareLet,
        PrepareConst,
        PrepareFunction
    };
    bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
    bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
                                                  PrepareLexicalKind prepareWhat,
                                                  ParseNode* pn, const TokenPos& pos);
    Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat,
                                       const TokenPos& pos);

    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);

    bool bindInitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
    bool bindInitialized(BindData<ParseHandler>* data, Node pn);
    bool bindUninitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
    bool bindUninitialized(BindData<ParseHandler>* data, Node pn);
    bool makeSetCall(Node node, unsigned errnum);

    Node cloneForInOrOfDeclarationForAssignment(Node decl);
    Node cloneLeftHandSide(Node opn);
    Node cloneDestructuringDefault(Node opn);
    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);

    JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);

    TokenPos pos() const { return tokenStream.currentToken().pos; }

    bool asmJS(Node list);

    void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);

    bool warnOnceAboutExprClosure();

    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 */
