diff options
Diffstat (limited to 'js/src/frontend/FullParseHandler.h')
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 1136 |
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 */ |