js/src/frontend/FullParseHandler.h
author Tooru Fujisawa <arai_a@mac.com>
Wed, 13 Mar 2019 04:29:44 +0000
changeset 521653 123ecc5c9586791c2ebae62c4a4e3cb600274975
parent 519586 d10717796a290bd4e4e63ca7ea019ad8e52b309d
child 524254 579548b422c67343d524825027c6f684b6e81267
permissions -rw-r--r--
Bug 1505343 - Part 1: Rename binsource => binast. r=Yoric Differential Revision: https://phabricator.services.mozilla.com/D23097

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef frontend_FullParseHandler_h
#define frontend_FullParseHandler_h

#include "mozilla/Attributes.h"
#include "mozilla/PodOperations.h"

#include <cstddef>  // std::nullptr_t
#include <string.h>

#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "vm/JSContext.h"

namespace js {

class RegExpObject;

namespace frontend {

enum class SourceKind {
  // We are parsing from a text source (Parser.h)
  Text,
  // We are parsing from a binary source (BinASTParser.h)
  Binary,
};

// Parse handler used when generating a full parse tree for all code which the
// parser encounters.
class FullParseHandler {
  ParseNodeAllocator allocator;

  ParseNode* allocParseNode(size_t size) {
    return static_cast<ParseNode*>(allocator.allocNode(size));
  }

  /*
   * If this is a full parse to construct the bytecode for a function that
   * was previously lazily parsed, that lazy function and the current index
   * into its inner functions. We do not want to reparse the inner functions.
   */
  const Rooted<LazyScript*> lazyOuterFunction_;
  size_t lazyInnerFunctionIndex;
  size_t lazyClosedOverBindingIndex;

  const SourceKind sourceKind_;

 public:
  /* new_ methods for creating parse nodes. These report OOM on context. */
  JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)

  // FIXME: Use ListNode instead of ListNodeType as an alias (bug 1489008).
  using Node = ParseNode*;

#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
  using longTypeName = typeName*;
  FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
#undef DECLARE_TYPE

  using NullNode = std::nullptr_t;

  bool isPropertyAccess(Node node) {
    return node->isKind(ParseNodeKind::DotExpr) ||
           node->isKind(ParseNodeKind::ElemExpr);
  }

  bool isFunctionCall(Node node) {
    // Note: super() is a special form, *not* a function call.
    return node->isKind(ParseNodeKind::CallExpr);
  }

  static bool isUnparenthesizedDestructuringPattern(Node node) {
    return !node->isInParens() && (node->isKind(ParseNodeKind::ObjectExpr) ||
                                   node->isKind(ParseNodeKind::ArrayExpr));
  }

  static bool isParenthesizedDestructuringPattern(Node node) {
    // Technically this isn't a destructuring pattern at all -- the grammar
    // doesn't treat it as such.  But we need to know when this happens to
    // consider it a SyntaxError rather than an invalid-left-hand-side
    // ReferenceError.
    return node->isInParens() && (node->isKind(ParseNodeKind::ObjectExpr) ||
                                  node->isKind(ParseNodeKind::ArrayExpr));
  }

  FullParseHandler(JSContext* cx, LifoAlloc& alloc,
                   LazyScript* lazyOuterFunction,
                   SourceKind kind = SourceKind::Text)
      : allocator(cx, alloc),
        lazyOuterFunction_(cx, lazyOuterFunction),
        lazyInnerFunctionIndex(0),
        lazyClosedOverBindingIndex(0),
        sourceKind_(SourceKind::Text) {}

  static NullNode null() { return NullNode(); }

#define DECLARE_AS(typeName, longTypeName, asMethodName) \
  static longTypeName asMethodName(Node node) { return &node->as<typeName>(); }
  FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
#undef DECLARE_AS

  // The FullParseHandler may be used to create nodes for text sources
  // (from Parser.h) or for binary sources (from BinASTParser.h). In the latter
  // case, some common assumptions on offsets are incorrect, e.g. in `a + b`,
  // `a`, `b` and `+` may be stored in any order. We use `sourceKind()`
  // to determine whether we need to check these assumptions.
  SourceKind sourceKind() const { return sourceKind_; }

  NameNodeType newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
    return new_<NameNode>(ParseNodeKind::Name, JSOP_GETNAME, name, pos);
  }

  UnaryNodeType newComputedName(Node expr, uint32_t begin, uint32_t end) {
    TokenPos pos(begin, end);
    return new_<UnaryNode>(ParseNodeKind::ComputedName, pos, expr);
  }

  NameNodeType newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
    return new_<NameNode>(ParseNodeKind::ObjectPropertyName, JSOP_NOP, atom,
                          pos);
  }

  NumericLiteralType newNumber(double value, DecimalPoint decimalPoint,
                               const TokenPos& pos) {
    return new_<NumericLiteral>(value, decimalPoint, pos);
  }

  // The Boxer object here is any object that can allocate BigIntBoxes.
  // Specifically, a Boxer has a .newBigIntBox(T) method that accepts a
  // BigInt* argument and returns a BigIntBox*.
  template <class Boxer>
  BigIntLiteralType newBigInt(BigInt* bi, const TokenPos& pos, Boxer& boxer) {
    BigIntBox* box = boxer.newBigIntBox(bi);
    if (!box) {
      return null();
    }
    return new_<BigIntLiteral>(box, pos);
  }

  BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) {
    return new_<BooleanLiteral>(cond, pos);
  }

  NameNodeType newStringLiteral(JSAtom* atom, const TokenPos& pos) {
    return new_<NameNode>(ParseNodeKind::StringExpr, JSOP_NOP, atom, pos);
  }

  NameNodeType newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) {
    return new_<NameNode>(ParseNodeKind::TemplateStringExpr, JSOP_NOP, atom,
                          pos);
  }

  CallSiteNodeType newCallSiteObject(uint32_t begin) {
    CallSiteNode* callSiteObj = new_<CallSiteNode>(begin);
    if (!callSiteObj) {
      return null();
    }

    ListNode* rawNodes = newArrayLiteral(callSiteObj->pn_pos.begin);
    if (!rawNodes) {
      return null();
    }

    addArrayElement(callSiteObj, rawNodes);

    return callSiteObj;
  }

  void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode,
                           Node cookedNode) {
    MOZ_ASSERT(callSiteObj->isKind(ParseNodeKind::CallSiteObj));

    addArrayElement(callSiteObj, cookedNode);
    addArrayElement(callSiteObj->rawNodes(), rawNode);

    /*
     * We don't know when the last noSubstTemplate will come in, and we
     * don't want to deal with this outside this method
     */
    setEndPosition(callSiteObj, callSiteObj->rawNodes());
  }

  ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) {
    return new_<ThisLiteral>(pos, thisName);
  }

  NullLiteralType newNullLiteral(const TokenPos& pos) {
    return new_<NullLiteral>(pos);
  }

  RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) {
    return new_<RawUndefinedLiteral>(pos);
  }

  // The Boxer object here is any object that can allocate ObjectBoxes.
  // Specifically, a Boxer has a .newObjectBox(T) method that accepts a
  // Rooted<RegExpObject*> argument and returns an ObjectBox*.
  template <class Boxer>
  RegExpLiteralType newRegExp(RegExpObject* reobj, const TokenPos& pos,
                              Boxer& boxer) {
    ObjectBox* objbox = boxer.newObjectBox(reobj);
    if (!objbox) {
      return null();
    }
    return new_<RegExpLiteral>(objbox, pos);
  }

  ConditionalExpressionType newConditional(Node cond, Node thenExpr,
                                           Node elseExpr) {
    return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
  }

  UnaryNodeType newDelete(uint32_t begin, Node expr) {
    if (expr->isKind(ParseNodeKind::Name)) {
      expr->setOp(JSOP_DELNAME);
      return newUnary(ParseNodeKind::DeleteNameExpr, begin, expr);
    }

    if (expr->isKind(ParseNodeKind::DotExpr)) {
      return newUnary(ParseNodeKind::DeletePropExpr, begin, expr);
    }

    if (expr->isKind(ParseNodeKind::ElemExpr)) {
      return newUnary(ParseNodeKind::DeleteElemExpr, begin, expr);
    }

    return newUnary(ParseNodeKind::DeleteExpr, begin, expr);
  }

  UnaryNodeType newTypeof(uint32_t begin, Node kid) {
    ParseNodeKind pnk = kid->isKind(ParseNodeKind::Name)
                            ? ParseNodeKind::TypeOfNameExpr
                            : ParseNodeKind::TypeOfExpr;
    return newUnary(pnk, begin, kid);
  }

  UnaryNodeType newUnary(ParseNodeKind kind, uint32_t begin, Node kid) {
    TokenPos pos(begin, kid->pn_pos.end);
    return new_<UnaryNode>(kind, pos, kid);
  }

  UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
    TokenPos pos(begin, kid->pn_pos.end);
    return new_<UnaryNode>(kind, pos, kid);
  }

  UnaryNodeType newSpread(uint32_t begin, Node kid) {
    TokenPos pos(begin, kid->pn_pos.end);
    return new_<UnaryNode>(ParseNodeKind::Spread, pos, kid);
  }

 private:
  BinaryNodeType newBinary(ParseNodeKind kind, Node left, Node right,
                           JSOp op = JSOP_NOP) {
    TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
    return new_<BinaryNode>(kind, op, pos, left, right);
  }

 public:
  Node appendOrCreateList(ParseNodeKind kind, Node left, Node right,
                          ParseContext* pc) {
    return ParseNode::appendOrCreateList(kind, left, right, this, pc);
  }

  // Expressions

  ListNodeType newArrayLiteral(uint32_t begin) {
    return new_<ListNode>(ParseNodeKind::ArrayExpr, TokenPos(begin, begin + 1));
  }

  MOZ_MUST_USE bool addElision(ListNodeType literal, const TokenPos& pos) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ArrayExpr));

    NullaryNode* elision = new_<NullaryNode>(ParseNodeKind::Elision, pos);
    if (!elision) {
      return false;
    }
    addList(/* list = */ literal, /* kid = */ elision);
    literal->setHasArrayHoleOrSpread();
    literal->setHasNonConstInitializer();
    return true;
  }

  MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin,
                                     Node inner) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ArrayExpr));

    ParseNode* spread = newSpread(begin, inner);
    if (!spread) {
      return false;
    }
    addList(/* list = */ literal, /* kid = */ spread);
    literal->setHasArrayHoleOrSpread();
    literal->setHasNonConstInitializer();
    return true;
  }

  void addArrayElement(ListNodeType literal, Node element) {
    if (!element->isConstant()) {
      literal->setHasNonConstInitializer();
    }
    addList(/* list = */ literal, /* kid = */ element);
  }

  BinaryNodeType newCall(Node callee, Node args) {
    return new_<BinaryNode>(ParseNodeKind::CallExpr, JSOP_CALL, callee, args);
  }

  ListNodeType newArguments(const TokenPos& pos) {
    return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
  }

  BinaryNodeType newSuperCall(Node callee, Node args) {
    return new_<BinaryNode>(ParseNodeKind::SuperCallExpr, JSOP_SUPERCALL,
                            callee, args);
  }

  BinaryNodeType newTaggedTemplate(Node tag, Node args) {
    return new_<BinaryNode>(ParseNodeKind::TaggedTemplateExpr, JSOP_CALL, tag,
                            args);
  }

  ListNodeType newObjectLiteral(uint32_t begin) {
    return new_<ListNode>(ParseNodeKind::ObjectExpr,
                          TokenPos(begin, begin + 1));
  }

  ClassNodeType newClass(Node name, Node heritage, Node memberBlock,
                         const TokenPos& pos) {
    return new_<ClassNode>(name, heritage, memberBlock, pos);
  }
  ListNodeType newClassMemberList(uint32_t begin) {
    return new_<ListNode>(ParseNodeKind::ClassMemberList,
                          TokenPos(begin, begin + 1));
  }
  ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
    return new_<ClassNames>(outer, inner, pos);
  }
  BinaryNodeType newNewTarget(NullaryNodeType newHolder,
                              NullaryNodeType targetHolder) {
    return new_<BinaryNode>(ParseNodeKind::NewTargetExpr, JSOP_NOP, newHolder,
                            targetHolder);
  }
  NullaryNodeType newPosHolder(const TokenPos& pos) {
    return new_<NullaryNode>(ParseNodeKind::PosHolder, pos);
  }
  UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) {
    return new_<UnaryNode>(ParseNodeKind::SuperBase, pos, thisName);
  }
  MOZ_MUST_USE bool addPrototypeMutation(ListNodeType literal, uint32_t begin,
                                         Node expr) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ObjectExpr));

    // Object literals with mutated [[Prototype]] are non-constant so that
    // singleton objects will have Object.prototype as their [[Prototype]].
    literal->setHasNonConstInitializer();

    UnaryNode* mutation = newUnary(ParseNodeKind::MutateProto, begin, expr);
    if (!mutation) {
      return false;
    }
    addList(/* list = */ literal, /* kid = */ mutation);
    return true;
  }

  BinaryNodeType newPropertyDefinition(Node key, Node val) {
    MOZ_ASSERT(isUsableAsObjectPropertyName(key));
    checkAndSetIsDirectRHSAnonFunction(val);
    return newBinary(ParseNodeKind::Colon, key, val, JSOP_INITPROP);
  }

  void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ObjectExpr));
    MOZ_ASSERT(propdef->isKind(ParseNodeKind::Colon));

    if (!propdef->right()->isConstant()) {
      literal->setHasNonConstInitializer();
    }

    addList(/* list = */ literal, /* kid = */ propdef);
  }

  MOZ_MUST_USE bool addPropertyDefinition(ListNodeType literal, Node key,
                                          Node val) {
    BinaryNode* propdef = newPropertyDefinition(key, val);
    if (!propdef) {
      return false;
    }
    addPropertyDefinition(literal, propdef);
    return true;
  }

  MOZ_MUST_USE bool addShorthand(ListNodeType literal, NameNodeType name,
                                 NameNodeType expr) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ObjectExpr));
    MOZ_ASSERT(name->isKind(ParseNodeKind::ObjectPropertyName));
    MOZ_ASSERT(expr->isKind(ParseNodeKind::Name));
    MOZ_ASSERT(name->atom() == expr->atom());

    literal->setHasNonConstInitializer();
    BinaryNode* propdef =
        newBinary(ParseNodeKind::Shorthand, name, expr, JSOP_INITPROP);
    if (!propdef) {
      return false;
    }
    addList(/* list = */ literal, /* kid = */ propdef);
    return true;
  }

  MOZ_MUST_USE bool addSpreadProperty(ListNodeType literal, uint32_t begin,
                                      Node inner) {
    MOZ_ASSERT(literal->isKind(ParseNodeKind::ObjectExpr));

    literal->setHasNonConstInitializer();
    ParseNode* spread = newSpread(begin, inner);
    if (!spread) {
      return false;
    }
    addList(/* list = */ literal, /* kid = */ spread);
    return true;
  }

  MOZ_MUST_USE bool addObjectMethodDefinition(ListNodeType literal, Node key,
                                              FunctionNodeType funNode,
                                              AccessorType atype) {
    literal->setHasNonConstInitializer();

    checkAndSetIsDirectRHSAnonFunction(funNode);

    ParseNode* propdef =
        newObjectMethodOrPropertyDefinition(key, funNode, atype);
    if (!propdef) {
      return false;
    }

    addList(/* list = */ literal, /* kid = */ propdef);
    return true;
  }

  MOZ_MUST_USE bool addClassMethodDefinition(ListNodeType memberList, Node key,
                                             FunctionNodeType funNode,
                                             AccessorType atype,
                                             bool isStatic) {
    MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
    MOZ_ASSERT(isUsableAsObjectPropertyName(key));

    checkAndSetIsDirectRHSAnonFunction(funNode);

    ClassMethod* classMethod =
        new_<ClassMethod>(key, funNode, AccessorTypeToJSOp(atype), isStatic);
    if (!classMethod) {
      return false;
    }
    addList(/* list = */ memberList, /* kid = */ classMethod);
    return true;
  }

  MOZ_MUST_USE bool addClassFieldDefinition(ListNodeType memberList, Node name,
                                            FunctionNodeType initializer) {
    MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
    MOZ_ASSERT(isUsableAsObjectPropertyName(name));

    ClassField* classField = new_<ClassField>(name, initializer);

    if (!classField) {
      return false;
    }
    addList(/* list = */ memberList, /* kid = */ classField);
    return true;
  }

  UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) {
    TokenPos pos(begin, begin + 1);
    return new_<UnaryNode>(ParseNodeKind::InitialYield, pos, gen);
  }

  UnaryNodeType newYieldExpression(uint32_t begin, Node value) {
    TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
    return new_<UnaryNode>(ParseNodeKind::YieldExpr, pos, value);
  }

  UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) {
    TokenPos pos(begin, value->pn_pos.end);
    return new_<UnaryNode>(ParseNodeKind::YieldStarExpr, pos, value);
  }

  UnaryNodeType newAwaitExpression(uint32_t begin, Node value) {
    TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
    return new_<UnaryNode>(ParseNodeKind::AwaitExpr, pos, value);
  }

  // Statements

  ListNodeType newStatementList(const TokenPos& pos) {
    return new_<ListNode>(ParseNodeKind::StatementList, pos);
  }

  MOZ_MUST_USE bool isFunctionStmt(Node stmt) {
    while (stmt->isKind(ParseNodeKind::LabelStmt)) {
      stmt = stmt->as<LabeledStatement>().statement();
    }
    return stmt->is<FunctionNode>();
  }

  void addStatementToList(ListNodeType list, Node stmt) {
    MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));

    addList(/* list = */ list, /* kid = */ stmt);

    if (isFunctionStmt(stmt)) {
      // Notify the emitter that the block contains body-level function
      // definitions that should be processed before the rest of nodes.
      list->setHasTopLevelFunctionDeclarations();
    }
  }

  void setListEndPosition(ListNodeType list, const TokenPos& pos) {
    MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));
    list->pn_pos.end = pos.end;
  }

  void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {
    MOZ_ASSERT(list->isKind(ParseNodeKind::StatementList));

    addList(/* list = */ list, /* kid = */ caseClause);

    if (caseClause->statementList()->hasTopLevelFunctionDeclarations()) {
      list->setHasTopLevelFunctionDeclarations();
    }
  }

  MOZ_MUST_USE bool prependInitialYield(ListNodeType stmtList, Node genName) {
    MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));

    TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
    NullaryNode* makeGen =
        new_<NullaryNode>(ParseNodeKind::Generator, yieldPos);
    if (!makeGen) {
      return false;
    }

    MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
    genName->setOp(JSOP_SETNAME);
    ParseNode* genInit =
        newAssignment(ParseNodeKind::AssignExpr, /* lhs = */ genName,
                      /* rhs = */ makeGen);
    if (!genInit) {
      return false;
    }

    UnaryNode* initialYield =
        newInitialYieldExpression(yieldPos.begin, genInit);
    if (!initialYield) {
      return false;
    }

    stmtList->prepend(initialYield);
    return true;
  }

  BinaryNodeType newSetThis(Node thisName, Node value) {
    MOZ_ASSERT(thisName->getOp() == JSOP_GETNAME);
    thisName->setOp(JSOP_SETNAME);
    return newBinary(ParseNodeKind::SetThis, thisName, value);
  }

  NullaryNodeType newEmptyStatement(const TokenPos& pos) {
    return new_<NullaryNode>(ParseNodeKind::EmptyStmt, pos);
  }

  BinaryNodeType newImportDeclaration(Node importSpecSet, Node moduleSpec,
                                      const TokenPos& pos) {
    return new_<BinaryNode>(ParseNodeKind::ImportDecl, JSOP_NOP, pos,
                            importSpecSet, moduleSpec);
  }

  BinaryNodeType newImportSpec(Node importNameNode, Node bindingName) {
    return newBinary(ParseNodeKind::ImportSpec, importNameNode, bindingName);
  }

  UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) {
    return new_<UnaryNode>(ParseNodeKind::ExportStmt, pos, kid);
  }

  BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet,
                                          Node moduleSpec) {
    BinaryNode* decl = new_<BinaryNode>(ParseNodeKind::ExportFromStmt, JSOP_NOP,
                                        exportSpecSet, moduleSpec);
    if (!decl) {
      return nullptr;
    }
    decl->pn_pos.begin = begin;
    return decl;
  }

  BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding,
                                             const TokenPos& pos) {
    if (maybeBinding) {
      MOZ_ASSERT(maybeBinding->isKind(ParseNodeKind::Name));
      MOZ_ASSERT(!maybeBinding->isInParens());

      checkAndSetIsDirectRHSAnonFunction(kid);
    }

    return new_<BinaryNode>(ParseNodeKind::ExportDefaultStmt, JSOP_NOP, pos,
                            kid, maybeBinding);
  }

  BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
    return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName);
  }

  NullaryNodeType newExportBatchSpec(const TokenPos& pos) {
    return new_<NullaryNode>(ParseNodeKind::ExportBatchSpecStmt, JSOP_NOP, pos);
  }

  BinaryNodeType newImportMeta(NullaryNodeType importHolder,
                               NullaryNodeType metaHolder) {
    return new_<BinaryNode>(ParseNodeKind::ImportMetaExpr, JSOP_NOP,
                            importHolder, metaHolder);
  }

  BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) {
    return new_<BinaryNode>(ParseNodeKind::CallImportExpr, JSOP_DYNAMIC_IMPORT,
                            importHolder, singleArg);
  }

  UnaryNodeType newExprStatement(Node expr, uint32_t end) {
    MOZ_ASSERT(expr->pn_pos.end <= end);
    return new_<UnaryNode>(ParseNodeKind::ExpressionStmt,
                           TokenPos(expr->pn_pos.begin, end), expr);
  }

  TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch,
                                 Node elseBranch) {
    TernaryNode* node =
        new_<TernaryNode>(ParseNodeKind::IfStmt, cond, thenBranch, elseBranch);
    if (!node) {
      return nullptr;
    }
    node->pn_pos.begin = begin;
    return node;
  }

  BinaryNodeType newDoWhileStatement(Node body, Node cond,
                                     const TokenPos& pos) {
    return new_<BinaryNode>(ParseNodeKind::DoWhileStmt, JSOP_NOP, pos, body,
                            cond);
  }

  BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) {
    TokenPos pos(begin, body->pn_pos.end);
    return new_<BinaryNode>(ParseNodeKind::WhileStmt, JSOP_NOP, pos, cond,
                            body);
  }

  ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead,
                              Node body, unsigned iflags) {
    return new_<ForNode>(TokenPos(begin, body->pn_pos.end), forHead, body,
                         iflags);
  }

  TernaryNodeType newForHead(Node init, Node test, Node update,
                             const TokenPos& pos) {
    return new_<TernaryNode>(ParseNodeKind::ForHead, init, test, update, pos);
  }

  TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target,
                                   Node iteratedExpr, const TokenPos& pos) {
    MOZ_ASSERT(kind == ParseNodeKind::ForIn || kind == ParseNodeKind::ForOf);
    return new_<TernaryNode>(kind, target, nullptr, iteratedExpr, pos);
  }

  SwitchStatementType newSwitchStatement(
      uint32_t begin, Node discriminant,
      LexicalScopeNodeType lexicalForCaseList, bool hasDefault) {
    return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList,
                                 hasDefault);
  }

  CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) {
    return new_<CaseClause>(expr, body, begin);
  }

  ContinueStatementType newContinueStatement(PropertyName* label,
                                             const TokenPos& pos) {
    return new_<ContinueStatement>(label, pos);
  }

  BreakStatementType newBreakStatement(PropertyName* label,
                                       const TokenPos& pos) {
    return new_<BreakStatement>(label, pos);
  }

  UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) {
    MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
    return new_<UnaryNode>(ParseNodeKind::ReturnStmt, pos, expr);
  }

  UnaryNodeType newExpressionBody(Node expr) {
    return new_<UnaryNode>(ParseNodeKind::ReturnStmt, expr->pn_pos, expr);
  }

  BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) {
    return new_<BinaryNode>(ParseNodeKind::WithStmt, JSOP_NOP,
                            TokenPos(begin, body->pn_pos.end), expr, body);
  }

  LabeledStatementType newLabeledStatement(PropertyName* label, Node stmt,
                                           uint32_t begin) {
    return new_<LabeledStatement>(label, stmt, begin);
  }

  UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
    MOZ_ASSERT(pos.encloses(expr->pn_pos));
    return new_<UnaryNode>(ParseNodeKind::ThrowStmt, pos, expr);
  }

  TernaryNodeType newTryStatement(uint32_t begin, Node body,
                                  LexicalScopeNodeType catchScope,
                                  Node finallyBlock) {
    return new_<TryNode>(begin, body, catchScope, finallyBlock);
  }

  DebuggerStatementType newDebuggerStatement(const TokenPos& pos) {
    return new_<DebuggerStatement>(pos);
  }

  NameNodeType newPropertyName(PropertyName* name, const TokenPos& pos) {
    return new_<NameNode>(ParseNodeKind::PropertyNameExpr, JSOP_NOP, name, pos);
  }

  PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
    return new_<PropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
  }

  PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
    return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
  }

  bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName,
                       Node catchBody) {
    BinaryNode* catchClause;
    if (catchName) {
      catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP, catchName,
                                     catchBody);
    } else {
      catchClause = new_<BinaryNode>(ParseNodeKind::Catch, JSOP_NOP,
                                     catchBody->pn_pos, catchName, catchBody);
    }
    if (!catchClause) {
      return false;
    }
    lexicalScope->setScopeBody(catchClause);
    return true;
  }

  inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(
      FunctionNodeType funNode, Node defaultValue);

 private:
  void checkAndSetIsDirectRHSAnonFunction(Node pn) {
    if (IsAnonymousFunctionDefinition(pn)) {
      pn->setDirectRHSAnonFunction(true);
    }
  }

 public:
  FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind,
                               const TokenPos& pos) {
    return new_<FunctionNode>(syntaxKind, pos);
  }

  BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node value,
                                                     AccessorType atype) {
    MOZ_ASSERT(isUsableAsObjectPropertyName(key));

    return newBinary(ParseNodeKind::Colon, key, value,
                     AccessorTypeToJSOp(atype));
  }

  BinaryNodeType newShorthandPropertyDefinition(Node key, Node value) {
    MOZ_ASSERT(isUsableAsObjectPropertyName(key));

    return newBinary(ParseNodeKind::Shorthand, key, value, JSOP_INITPROP);
  }

  ListNodeType newParamsBody(const TokenPos& pos) {
    return new_<ListNode>(ParseNodeKind::ParamsBody, pos);
  }

  void setFunctionFormalParametersAndBody(FunctionNodeType funNode,
                                          ListNodeType paramsBody) {
    MOZ_ASSERT_IF(paramsBody, paramsBody->isKind(ParseNodeKind::ParamsBody));
    funNode->setBody(paramsBody);
  }
  void setFunctionBox(FunctionNodeType funNode, FunctionBox* funbox) {
    funNode->setFunbox(funbox);
    funbox->functionNode = funNode;
  }
  void addFunctionFormalParameter(FunctionNodeType funNode, Node argpn) {
    addList(/* list = */ funNode->body(), /* kid = */ argpn);
  }
  void setFunctionBody(FunctionNodeType funNode, LexicalScopeNodeType body) {
    MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
    addList(/* list = */ funNode->body(), /* kid = */ body);
  }

  ModuleNodeType newModule(const TokenPos& pos) {
    return new_<ModuleNode>(pos);
  }

  LexicalScopeNodeType newLexicalScope(LexicalScope::Data* bindings,
                                       Node body) {
    return new_<LexicalScopeNode>(bindings, body);
  }

  BinaryNodeType newNewExpression(uint32_t begin, Node ctor, Node args) {
    return new_<BinaryNode>(ParseNodeKind::NewExpr, JSOP_NEW,
                            TokenPos(begin, args->pn_pos.end), ctor, args);
  }

  AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
    if (kind == ParseNodeKind::AssignExpr && lhs->isKind(ParseNodeKind::Name) &&
        !lhs->isInParens()) {
      checkAndSetIsDirectRHSAnonFunction(rhs);
    }

    return new_<AssignmentNode>(kind, JSOP_NOP, lhs, rhs);
  }

  bool isUnparenthesizedAssignment(Node node) {
    if (node->isKind(ParseNodeKind::AssignExpr) && !node->isInParens()) {
      // ParseNodeKind::Assign is also (mis)used for things like
      // |var name = expr;|. But this method is only called on actual
      // expressions, so we can just assert the node's op is the one used
      // for plain assignment.
      MOZ_ASSERT(node->isOp(JSOP_NOP));
      return true;
    }

    return false;
  }

  bool isUnparenthesizedUnaryExpression(Node node) {
    if (!node->isInParens()) {
      ParseNodeKind kind = node->getKind();
      return kind == ParseNodeKind::VoidExpr ||
             kind == ParseNodeKind::NotExpr ||
             kind == ParseNodeKind::BitNotExpr ||
             kind == ParseNodeKind::PosExpr || kind == ParseNodeKind::NegExpr ||
             IsTypeofKind(kind) || IsDeleteKind(kind);
    }
    return false;
  }

  bool isReturnStatement(Node node) {
    return node->isKind(ParseNodeKind::ReturnStmt);
  }

  bool isStatementPermittedAfterReturnStatement(Node node) {
    ParseNodeKind kind = node->getKind();
    return kind == ParseNodeKind::Function || kind == ParseNodeKind::VarStmt ||
           kind == ParseNodeKind::BreakStmt ||
           kind == ParseNodeKind::ThrowStmt || kind == ParseNodeKind::EmptyStmt;
  }

  bool isSuperBase(Node node) { return node->isKind(ParseNodeKind::SuperBase); }

  bool isUsableAsObjectPropertyName(Node node) {
    return node->isKind(ParseNodeKind::NumberExpr) ||
           node->isKind(ParseNodeKind::ObjectPropertyName) ||
           node->isKind(ParseNodeKind::StringExpr) ||
           node->isKind(ParseNodeKind::ComputedName);
  }

  AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode,
                                                 Node init) {
    MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name));
    MOZ_ASSERT(!nameNode->isInParens());

    checkAndSetIsDirectRHSAnonFunction(init);

    return newAssignment(ParseNodeKind::AssignExpr, nameNode, init);
  }

  void setBeginPosition(Node pn, Node oth) {
    setBeginPosition(pn, oth->pn_pos.begin);
  }
  void setBeginPosition(Node pn, uint32_t begin) {
    pn->pn_pos.begin = begin;
    MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
  }

  void setEndPosition(Node pn, Node oth) {
    setEndPosition(pn, oth->pn_pos.end);
  }
  void setEndPosition(Node pn, uint32_t end) {
    pn->pn_pos.end = end;
    MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
  }

  uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) {
    return func->pn_pos.begin;
  }

  bool isDeclarationKind(ParseNodeKind kind) {
    return kind == ParseNodeKind::VarStmt || kind == ParseNodeKind::LetDecl ||
           kind == ParseNodeKind::ConstDecl;
  }

  ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) {
    MOZ_ASSERT(!isDeclarationKind(kind));
    return new_<ListNode>(kind, JSOP_NOP, pos);
  }

 public:
  ListNodeType newList(ParseNodeKind kind, Node kid) {
    MOZ_ASSERT(!isDeclarationKind(kind));
    return new_<ListNode>(kind, JSOP_NOP, kid);
  }

  ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
    MOZ_ASSERT(isDeclarationKind(kind));
    return new_<ListNode>(kind, JSOP_NOP, pos);
  }

  bool isDeclarationList(Node node) {
    return isDeclarationKind(node->getKind());
  }

  Node singleBindingFromDeclaration(ListNodeType decl) {
    MOZ_ASSERT(isDeclarationList(decl));
    MOZ_ASSERT(decl->count() == 1);
    return decl->head();
  }

  ListNodeType newCommaExpressionList(Node kid) {
    return new_<ListNode>(ParseNodeKind::CommaExpr, JSOP_NOP, kid);
  }

  void addList(ListNodeType list, Node kid) {
    if (sourceKind_ == SourceKind::Text) {
      list->append(kid);
    } else {
      list->appendWithoutOrderAssumption(kid);
    }
  }

  void setOp(Node pn, JSOp op) { pn->setOp(op); }
  void setListHasNonConstInitializer(ListNodeType literal) {
    literal->setHasNonConstInitializer();
  }
  template <typename NodeType>
  MOZ_MUST_USE NodeType parenthesize(NodeType node) {
    node->setInParens(true);
    return node;
  }
  template <typename NodeType>
  MOZ_MUST_USE NodeType setLikelyIIFE(NodeType node) {
    return parenthesize(node);
  }
  void setInDirectivePrologue(UnaryNodeType exprStmt) {
    exprStmt->setIsDirectivePrologueMember();
  }

  bool isName(Node node) { return node->isKind(ParseNodeKind::Name); }

  bool isArgumentsName(Node node, JSContext* cx) {
    return node->isKind(ParseNodeKind::Name) &&
           node->as<NameNode>().atom() == cx->names().arguments;
  }

  bool isEvalName(Node node, JSContext* cx) {
    return node->isKind(ParseNodeKind::Name) &&
           node->as<NameNode>().atom() == cx->names().eval;
  }

  bool isAsyncKeyword(Node node, JSContext* cx) {
    return node->isKind(ParseNodeKind::Name) &&
           node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
           node->as<NameNode>().atom() == cx->names().async;
  }

  PropertyName* maybeDottedProperty(Node pn) {
    return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name()
                                    : nullptr;
  }
  JSAtom* isStringExprStatement(Node pn, TokenPos* pos) {
    if (pn->is<UnaryNode>()) {
      UnaryNode* unary = &pn->as<UnaryNode>();
      if (JSAtom* atom = unary->isStringExprStatement()) {
        *pos = unary->kid()->pn_pos;
        return atom;
      }
    }
    return nullptr;
  }

  void adjustGetToSet(Node node) {
    node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
  }

  bool canSkipLazyInnerFunctions() { return !!lazyOuterFunction_; }
  bool canSkipLazyClosedOverBindings() { return !!lazyOuterFunction_; }
  JSFunction* nextLazyInnerFunction() {
    MOZ_ASSERT(lazyInnerFunctionIndex <
               lazyOuterFunction_->numInnerFunctions());
    return lazyOuterFunction_->innerFunctions()[lazyInnerFunctionIndex++];
  }
  JSAtom* nextLazyClosedOverBinding() {
    MOZ_ASSERT(lazyClosedOverBindingIndex <
               lazyOuterFunction_->numClosedOverBindings());
    return lazyOuterFunction_
        ->closedOverBindings()[lazyClosedOverBindingIndex++];
  }
};

inline bool FullParseHandler::setLastFunctionFormalParameterDefault(
    FunctionNodeType funNode, Node defaultValue) {
  ListNode* body = funNode->body();
  ParseNode* arg = body->last();
  ParseNode* pn = newAssignment(ParseNodeKind::AssignExpr, arg, defaultValue);
  if (!pn) {
    return false;
  }

  body->replaceLast(pn);
  return true;
}

}  // namespace frontend
}  // namespace js

#endif /* frontend_FullParseHandler_h */