summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/FullParseHandler.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/FullParseHandler.h')
-rw-r--r--js/src/frontend/FullParseHandler.h1136
1 files changed, 1136 insertions, 0 deletions
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
new file mode 100644
index 0000000000..5fba2d86ed
--- /dev/null
+++ b/js/src/frontend/FullParseHandler.h
@@ -0,0 +1,1136 @@
+/* -*- 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/Maybe.h" // mozilla::Maybe
+#include "mozilla/PodOperations.h"
+
+#include <cstddef> // std::nullptr_t
+#include <string.h>
+
+#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
+#include "frontend/NameAnalysisTypes.h" // PrivateNameKind
+#include "frontend/ParseNode.h"
+#include "frontend/SharedContext.h"
+#include "frontend/Stencil.h"
+#include "vm/JSContext.h"
+
+namespace js {
+
+class RegExpObject;
+
+namespace frontend {
+
+class TokenStreamAnyChars;
+
+enum class SourceKind {
+ // We are parsing from a text source (Parser.h)
+ Text,
+};
+
+// 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, we still don't want to full parse the
+ * inner functions. These members are used for this functionality:
+ *
+ * - lazyOuterFunction_ holds the lazyScript for this current parse
+ * - lazyInnerFunctionIndex is used as we skip over inner functions
+ * (see skipLazyInnerFunction),
+ *
+ * TODO-Stencil: We probably need to snapshot the atoms from the
+ * lazyOuterFunction here.
+ */
+ const Rooted<BaseScript*> 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 isOptionalPropertyAccess(Node node) {
+ return node->isKind(ParseNodeKind::OptionalDotExpr) ||
+ node->isKind(ParseNodeKind::OptionalElemExpr);
+ }
+
+ 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,
+ BaseScript* lazyOuterFunction,
+ SourceKind kind = SourceKind::Text)
+ : allocator(cx, alloc),
+ lazyOuterFunction_(cx, lazyOuterFunction),
+ lazyInnerFunctionIndex(0),
+ lazyClosedOverBindingIndex(0),
+ sourceKind_(kind) {
+ // The BaseScript::gcthings() array contains the inner function list
+ // followed by the closed-over bindings data. Advance the index for
+ // closed-over bindings to the end of the inner functions. The
+ // nextLazyInnerFunction / nextLazyClosedOverBinding accessors confirm we
+ // have the expected types. See also: BaseScript::CreateLazy.
+ if (lazyOuterFunction) {
+ for (JS::GCCellPtr gcThing : lazyOuterFunction->gcthings()) {
+ if (gcThing.is<JSObject>()) {
+ lazyClosedOverBindingIndex++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ 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). With previous binary source formats, 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(const ParserName* name, const TokenPos& pos,
+ JSContext* cx) {
+ return new_<NameNode>(ParseNodeKind::Name, name, pos);
+ }
+
+ UnaryNodeType newComputedName(Node expr, uint32_t begin, uint32_t end) {
+ TokenPos pos(begin, end);
+ return new_<UnaryNode>(ParseNodeKind::ComputedName, pos, expr);
+ }
+
+ UnaryNodeType newSyntheticComputedName(Node expr, uint32_t begin,
+ uint32_t end) {
+ TokenPos pos(begin, end);
+ UnaryNode* node = new_<UnaryNode>(ParseNodeKind::ComputedName, pos, expr);
+ if (!node) {
+ return nullptr;
+ }
+ node->setSyntheticComputedName();
+ return node;
+ }
+
+ NameNodeType newObjectLiteralPropertyName(const ParserAtom* atom,
+ const TokenPos& pos) {
+ return new_<NameNode>(ParseNodeKind::ObjectPropertyName, atom, pos);
+ }
+
+ NameNodeType newPrivateName(const ParserAtom* atom, const TokenPos& pos) {
+ return new_<NameNode>(ParseNodeKind::PrivateName, atom, pos);
+ }
+
+ NumericLiteralType newNumber(double value, DecimalPoint decimalPoint,
+ const TokenPos& pos) {
+ return new_<NumericLiteral>(value, decimalPoint, pos);
+ }
+
+ BigIntLiteralType newBigInt(BigIntIndex index,
+ BaseCompilationStencil& stencil,
+ const TokenPos& pos) {
+ return new_<BigIntLiteral>(index, stencil, pos);
+ }
+
+ BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) {
+ return new_<BooleanLiteral>(cond, pos);
+ }
+
+ NameNodeType newStringLiteral(const ParserAtom* atom, const TokenPos& pos) {
+ return new_<NameNode>(ParseNodeKind::StringExpr, atom, pos);
+ }
+
+ NameNodeType newTemplateStringLiteral(const ParserAtom* atom,
+ const TokenPos& pos) {
+ return new_<NameNode>(ParseNodeKind::TemplateStringExpr, 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));
+ MOZ_ASSERT(rawNode->isKind(ParseNodeKind::TemplateStringExpr));
+ MOZ_ASSERT(cookedNode->isKind(ParseNodeKind::TemplateStringExpr) ||
+ cookedNode->isKind(ParseNodeKind::RawUndefinedExpr));
+
+ 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);
+ }
+
+ RegExpLiteralType newRegExp(RegExpIndex index, const TokenPos& pos) {
+ return new_<RegExpLiteral>(index, 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)) {
+ 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);
+ }
+
+ if (expr->isKind(ParseNodeKind::OptionalChain)) {
+ Node kid = expr->as<UnaryNode>().kid();
+ // Handle property deletion explicitly. OptionalCall is handled
+ // via DeleteExpr.
+ if (kid->isKind(ParseNodeKind::DotExpr) ||
+ kid->isKind(ParseNodeKind::OptionalDotExpr) ||
+ kid->isKind(ParseNodeKind::ElemExpr) ||
+ kid->isKind(ParseNodeKind::OptionalElemExpr)) {
+ return newUnary(ParseNodeKind::DeleteOptionalChainExpr, begin, kid);
+ }
+ }
+
+ 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) {
+ TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
+ return new_<BinaryNode>(kind, 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->setHasNonConstInitializer();
+ return true;
+ }
+
+ MOZ_MUST_USE bool addSpreadElement(ListNodeType literal, uint32_t begin,
+ Node inner) {
+ MOZ_ASSERT(literal->isKind(ParseNodeKind::ArrayExpr));
+
+ UnaryNodeType spread = newSpread(begin, inner);
+ if (!spread) {
+ return false;
+ }
+ addList(/* list = */ literal, /* kid = */ spread);
+ literal->setHasNonConstInitializer();
+ return true;
+ }
+
+ void addArrayElement(ListNodeType literal, Node element) {
+ if (!element->isConstant()) {
+ literal->setHasNonConstInitializer();
+ }
+ addList(/* list = */ literal, /* kid = */ element);
+ }
+
+ CallNodeType newCall(Node callee, Node args, JSOp callOp) {
+ return new_<CallNode>(ParseNodeKind::CallExpr, callOp, callee, args);
+ }
+
+ OptionalCallNodeType newOptionalCall(Node callee, Node args, JSOp callOp) {
+ return new_<CallNode>(ParseNodeKind::OptionalCallExpr, callOp, callee,
+ args);
+ }
+
+ ListNodeType newArguments(const TokenPos& pos) {
+ return new_<ListNode>(ParseNodeKind::Arguments, pos);
+ }
+
+ CallNodeType newSuperCall(Node callee, Node args, bool isSpread) {
+ return new_<CallNode>(ParseNodeKind::SuperCallExpr,
+ isSpread ? JSOp::SpreadSuperCall : JSOp::SuperCall,
+ callee, args);
+ }
+
+ CallNodeType newTaggedTemplate(Node tag, Node args, JSOp callOp) {
+ return new_<CallNode>(ParseNodeKind::TaggedTemplateExpr, callOp, tag, args);
+ }
+
+ ListNodeType newObjectLiteral(uint32_t begin) {
+ return new_<ListNode>(ParseNodeKind::ObjectExpr,
+ TokenPos(begin, begin + 1));
+ }
+
+ ClassNodeType newClass(Node name, Node heritage,
+ LexicalScopeNodeType 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, 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 new_<PropertyDefinition>(key, val, AccessorType::None);
+ }
+
+ void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {
+ MOZ_ASSERT(literal->isKind(ParseNodeKind::ObjectExpr));
+ MOZ_ASSERT(propdef->isKind(ParseNodeKind::PropertyDefinition));
+
+ 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);
+ 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 ClassMethod* newClassMethodDefinition(
+ Node key, FunctionNodeType funNode, AccessorType atype, bool isStatic,
+ mozilla::Maybe<FunctionNodeType> initializerIfPrivate) {
+ MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+
+ checkAndSetIsDirectRHSAnonFunction(funNode);
+
+ if (initializerIfPrivate.isSome()) {
+ return new_<ClassMethod>(key, funNode, atype, isStatic,
+ initializerIfPrivate.value());
+ }
+ return new_<ClassMethod>(key, funNode, atype, isStatic, nullptr);
+ }
+
+ MOZ_MUST_USE ClassField* newClassFieldDefinition(Node name,
+ FunctionNodeType initializer,
+ bool isStatic) {
+ MOZ_ASSERT(isUsableAsObjectPropertyName(name));
+
+ return new_<ClassField>(name, initializer, isStatic);
+ }
+
+ MOZ_MUST_USE bool addClassMemberDefinition(ListNodeType memberList,
+ Node member) {
+ MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList));
+ // Constructors can be surrounded by LexicalScopes.
+ MOZ_ASSERT(member->isKind(ParseNodeKind::ClassMethod) ||
+ member->isKind(ParseNodeKind::ClassField) ||
+ (member->isKind(ParseNodeKind::LexicalScope) &&
+ member->as<LexicalScopeNode>().scopeBody()->isKind(
+ ParseNodeKind::ClassMethod)));
+
+ addList(/* list = */ memberList, /* kid = */ member);
+ 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);
+ }
+
+ UnaryNodeType newOptionalChain(uint32_t begin, Node value) {
+ TokenPos pos(begin, value->pn_pos.end);
+ return new_<UnaryNode>(ParseNodeKind::OptionalChain, 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;
+ }
+
+ 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) {
+ 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, 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,
+ 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, 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, pos);
+ }
+
+ BinaryNodeType newImportMeta(NullaryNodeType importHolder,
+ NullaryNodeType metaHolder) {
+ return new_<BinaryNode>(ParseNodeKind::ImportMetaExpr, importHolder,
+ metaHolder);
+ }
+
+ BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) {
+ return new_<BinaryNode>(ParseNodeKind::CallImportExpr, importHolder,
+ singleArg);
+ }
+
+ UnaryNodeType newExprStatement(Node expr, uint32_t end) {
+ MOZ_ASSERT_IF(sourceKind() == SourceKind::Text, expr->pn_pos.end <= end);
+ return new_<UnaryNode>(ParseNodeKind::ExpressionStmt,
+ TokenPos(expr->pn_pos.begin, end), expr);
+ }
+
+ UnaryNodeType newExprStatement(Node expr) {
+ return newExprStatement(expr, expr->pn_pos.end);
+ }
+
+ 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, pos, body, cond);
+ }
+
+ BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) {
+ TokenPos pos(begin, body->pn_pos.end);
+ return new_<BinaryNode>(ParseNodeKind::WhileStmt, 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(const ParserName* label,
+ const TokenPos& pos) {
+ return new_<ContinueStatement>(label, pos);
+ }
+
+ BreakStatementType newBreakStatement(const ParserName* label,
+ const TokenPos& pos) {
+ return new_<BreakStatement>(label, pos);
+ }
+
+ UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) {
+ MOZ_ASSERT_IF(expr && sourceKind() == SourceKind::Text,
+ 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,
+ TokenPos(begin, body->pn_pos.end), expr, body);
+ }
+
+ LabeledStatementType newLabeledStatement(const ParserName* label, Node stmt,
+ uint32_t begin) {
+ return new_<LabeledStatement>(label, stmt, begin);
+ }
+
+ UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
+ MOZ_ASSERT_IF(sourceKind() == SourceKind::Text, 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(const ParserName* name, const TokenPos& pos) {
+ return new_<NameNode>(ParseNodeKind::PropertyNameExpr, 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);
+ }
+
+ OptionalPropertyAccessType newOptionalPropertyAccess(Node expr,
+ NameNodeType key) {
+ return new_<OptionalPropertyAccess>(expr, key, expr->pn_pos.begin,
+ key->pn_pos.end);
+ }
+
+ OptionalPropertyByValueType newOptionalPropertyByValue(Node lhs, Node index,
+ uint32_t end) {
+ return new_<OptionalPropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
+ }
+
+ bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName,
+ Node catchBody) {
+ BinaryNode* catchClause;
+ if (catchName) {
+ catchClause =
+ new_<BinaryNode>(ParseNodeKind::Catch, catchName, catchBody);
+ } else {
+ catchClause = new_<BinaryNode>(ParseNodeKind::Catch, catchBody->pn_pos,
+ catchName, catchBody);
+ }
+ if (!catchClause) {
+ return false;
+ }
+ lexicalScope->setScopeBody(catchClause);
+ return true;
+ }
+
+ inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(
+ FunctionNodeType funNode, Node defaultValue);
+
+ void checkAndSetIsDirectRHSAnonFunction(Node pn) {
+ if (IsAnonymousFunctionDefinition(pn)) {
+ pn->setDirectRHSAnonFunction(true);
+ }
+ }
+
+ 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 new_<PropertyDefinition>(key, value, atype);
+ }
+
+ BinaryNodeType newShorthandPropertyDefinition(Node key, Node value) {
+ MOZ_ASSERT(isUsableAsObjectPropertyName(key));
+
+ return newBinary(ParseNodeKind::Shorthand, key, value);
+ }
+
+ 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::ParserData* bindings,
+ Node body,
+ ScopeKind kind = ScopeKind::Lexical) {
+ return new_<LexicalScopeNode>(bindings, body, kind);
+ }
+
+ CallNodeType newNewExpression(uint32_t begin, Node ctor, Node args,
+ bool isSpread) {
+ return new_<CallNode>(ParseNodeKind::NewExpr,
+ isSpread ? JSOp::SpreadNew : JSOp::New,
+ TokenPos(begin, args->pn_pos.end), ctor, args);
+ }
+
+ AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
+ if ((kind == ParseNodeKind::AssignExpr ||
+ kind == ParseNodeKind::CoalesceAssignExpr ||
+ kind == ParseNodeKind::OrAssignExpr ||
+ kind == ParseNodeKind::AndAssignExpr ||
+ kind == ParseNodeKind::InitExpr) &&
+ lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) {
+ checkAndSetIsDirectRHSAnonFunction(rhs);
+ }
+
+ return new_<AssignmentNode>(kind, lhs, rhs);
+ }
+
+ bool isUnparenthesizedAssignment(Node node) {
+ if ((node->isKind(ParseNodeKind::AssignExpr)) && !node->isInParens()) {
+ 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 ||
+ kind == ParseNodeKind::AwaitExpr || 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::BigIntExpr) ||
+ node->isKind(ParseNodeKind::ObjectPropertyName) ||
+ node->isKind(ParseNodeKind::StringExpr) ||
+ node->isKind(ParseNodeKind::ComputedName) ||
+ node->isKind(ParseNodeKind::PrivateName);
+ }
+
+ 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_IF(sourceKind() == SourceKind::Text,
+ 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_IF(sourceKind() == SourceKind::Text,
+ 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, pos);
+ }
+
+ public:
+ ListNodeType newList(ParseNodeKind kind, Node kid) {
+ MOZ_ASSERT(!isDeclarationKind(kind));
+ return new_<ListNode>(kind, kid);
+ }
+
+ ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) {
+ MOZ_ASSERT(isDeclarationKind(kind));
+ return new_<ListNode>(kind, 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, kid);
+ }
+
+ void addList(ListNodeType list, Node kid) {
+ if (sourceKind_ == SourceKind::Text) {
+ list->append(kid);
+ } else {
+ list->appendWithoutOrderAssumption(kid);
+ }
+ }
+
+ 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);
+ }
+
+ 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->parserNames().arguments;
+ }
+
+ bool isEvalName(Node node, JSContext* cx) {
+ return node->isKind(ParseNodeKind::Name) &&
+ node->as<NameNode>().atom() == cx->parserNames().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->parserNames().async;
+ }
+
+ bool isPrivateName(Node node) {
+ return node->isKind(ParseNodeKind::PrivateName);
+ }
+
+ bool isPrivateField(Node node) {
+ if (node->isKind(ParseNodeKind::ElemExpr) ||
+ node->isKind(ParseNodeKind::OptionalElemExpr)) {
+ PropertyByValueBase& pbv = node->as<PropertyByValueBase>();
+ if (isPrivateName(&pbv.key())) {
+ return true;
+ }
+ }
+ if (node->isKind(ParseNodeKind::OptionalChain)) {
+ return isPrivateField(node->as<UnaryNode>().kid());
+ }
+ return false;
+ }
+
+ const ParserName* maybeDottedProperty(Node pn) {
+ return pn->is<PropertyAccessBase>() ? pn->as<PropertyAccessBase>().name()
+ : nullptr;
+ }
+ const ParserAtom* isStringExprStatement(Node pn, TokenPos* pos) {
+ if (pn->is<UnaryNode>()) {
+ UnaryNode* unary = &pn->as<UnaryNode>();
+ if (const ParserAtom* atom = unary->isStringExprStatement()) {
+ *pos = unary->kid()->pn_pos;
+ return atom;
+ }
+ }
+ return nullptr;
+ }
+
+ bool canSkipLazyInnerFunctions() { return !!lazyOuterFunction_; }
+ bool canSkipLazyClosedOverBindings() { return !!lazyOuterFunction_; }
+ bool canSkipRegexpSyntaxParse() { return !!lazyOuterFunction_; }
+ JSFunction* nextLazyInnerFunction() {
+ return &lazyOuterFunction_->gcthings()[lazyInnerFunctionIndex++]
+ .as<JSObject>()
+ .as<JSFunction>();
+ }
+ JSAtom* nextLazyClosedOverBinding() {
+ auto gcthings = lazyOuterFunction_->gcthings();
+
+ // Trailing nullptrs were elided in PerHandlerParser::finishFunction().
+ if (lazyClosedOverBindingIndex >= gcthings.Length()) {
+ return nullptr;
+ }
+
+ // These entries are either JSAtom* or nullptr, so use the 'asCell()'
+ // accessor which is faster.
+ gc::Cell* cell = gcthings[lazyClosedOverBindingIndex++].asCell();
+ MOZ_ASSERT_IF(cell, cell->as<JSString>()->isAtom());
+ return static_cast<JSAtom*>(cell);
+ }
+
+ void setPrivateNameKind(Node node, PrivateNameKind kind) {
+ MOZ_ASSERT(node->is<NameNode>());
+ node->as<NameNode>().setPrivateNameKind(kind);
+ }
+};
+
+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 */