/* -*- 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/Maybe.h" // mozilla::Maybe #include "mozilla/Result.h" // mozilla::Result, mozilla::UnusedZero #include "mozilla/Try.h" // MOZ_TRY* #include // std::nullptr_t #include #include "jstypes.h" #include "frontend/CompilationStencil.h" // CompilationState #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind #include "frontend/NameAnalysisTypes.h" // PrivateNameKind #include "frontend/ParseNode.h" #include "frontend/Parser-macros.h" // MOZ_TRY_VAR_OR_RETURN #include "frontend/ParserAtom.h" // TaggedParserAtomIndex #include "frontend/SharedContext.h" #include "frontend/Stencil.h" template <> struct mozilla::detail::UnusedZero { static const bool value = true; }; #define DEFINE_UNUSED_ZERO(typeName) \ template <> \ struct mozilla::detail::UnusedZero { \ static const bool value = true; \ }; FOR_EACH_PARSENODE_SUBCLASS(DEFINE_UNUSED_ZERO) #undef DEFINE_UNUSED_ZERO namespace js { namespace frontend { class TokenStreamAnyChars; // 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(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: // // - reuseGCThings if ture it means that the following fields are valid. // - gcThingsData holds an incomplete stencil-like copy of inner functions as // well as atoms. // - scriptData and scriptExtra_ hold information necessary to locate inner // functions to skip over each. // - lazyInnerFunctionIndex is used as we skip over inner functions // (see skipLazyInnerFunction), // - lazyClosedOverBindingIndex is used to synchronize binding computation // with the scope traversal. // (see propagateFreeNamesAndMarkClosedOverBindings), const CompilationSyntaxParseCache& previousParseCache_; size_t lazyInnerFunctionIndex; size_t lazyClosedOverBindingIndex; bool reuseGCThings; /* new_ methods for creating parse nodes. These report OOM on context. */ JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline) public: using NodeError = ParseNodeError; using Node = ParseNode*; using NodeResult = ParseNodeResult; using NodeErrorResult = mozilla::GenericErrorResult; #define DECLARE_TYPE(typeName) \ using typeName##Type = typeName*; \ using typeName##Result = mozilla::Result; FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) #undef DECLARE_TYPE template inline mozilla::Result newResult(Args&&... args) { auto* node = new_(std::forward(args)...); if (!node) { return mozilla::Result(NodeError()); } return node; } using NullNode = std::nullptr_t; bool isPropertyOrPrivateMemberAccess(Node node) { return node->isKind(ParseNodeKind::DotExpr) || node->isKind(ParseNodeKind::ElemExpr) || node->isKind(ParseNodeKind::PrivateMemberExpr); } bool isOptionalPropertyOrPrivateMemberAccess(Node node) { return node->isKind(ParseNodeKind::OptionalDotExpr) || node->isKind(ParseNodeKind::OptionalElemExpr) || node->isKind(ParseNodeKind::PrivateMemberExpr); } 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(FrontendContext* fc, CompilationState& compilationState) : allocator(fc, compilationState.parserAllocScope.alloc()), previousParseCache_(compilationState.previousParseCache), lazyInnerFunctionIndex(0), lazyClosedOverBindingIndex(0), reuseGCThings(compilationState.input.isDelazifying()) {} static NullNode null() { return NullNode(); } static constexpr NodeErrorResult errorResult() { return NodeErrorResult(NodeError()); } #define DECLARE_AS(typeName) \ static typeName##Type as##typeName(Node node) { \ return &node->as(); \ } FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) #undef DECLARE_AS NameNodeResult newName(TaggedParserAtomIndex name, const TokenPos& pos) { return newResult(ParseNodeKind::Name, name, pos); } UnaryNodeResult newComputedName(Node expr, uint32_t begin, uint32_t end) { TokenPos pos(begin, end); return newResult(ParseNodeKind::ComputedName, pos, expr); } UnaryNodeResult newSyntheticComputedName(Node expr, uint32_t begin, uint32_t end) { TokenPos pos(begin, end); UnaryNode* node; MOZ_TRY_VAR(node, newResult(ParseNodeKind::ComputedName, pos, expr)); node->setSyntheticComputedName(); return node; } NameNodeResult newObjectLiteralPropertyName(TaggedParserAtomIndex atom, const TokenPos& pos) { return newResult(ParseNodeKind::ObjectPropertyName, atom, pos); } NameNodeResult newPrivateName(TaggedParserAtomIndex atom, const TokenPos& pos) { return newResult(ParseNodeKind::PrivateName, atom, pos); } NumericLiteralResult newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return newResult(value, decimalPoint, pos); } BigIntLiteralResult newBigInt(BigIntIndex index, bool isZero, const TokenPos& pos) { return newResult(index, isZero, pos); } BooleanLiteralResult newBooleanLiteral(bool cond, const TokenPos& pos) { return newResult(cond, pos); } NameNodeResult newStringLiteral(TaggedParserAtomIndex atom, const TokenPos& pos) { return newResult(ParseNodeKind::StringExpr, atom, pos); } NameNodeResult newTemplateStringLiteral(TaggedParserAtomIndex atom, const TokenPos& pos) { return newResult(ParseNodeKind::TemplateStringExpr, atom, pos); } CallSiteNodeResult newCallSiteObject(uint32_t begin) { CallSiteNode* callSiteObj; MOZ_TRY_VAR(callSiteObj, newResult(begin)); ListNode* rawNodes; MOZ_TRY_VAR(rawNodes, newArrayLiteral(callSiteObj->pn_pos.begin)); 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()); } ThisLiteralResult newThisLiteral(const TokenPos& pos, Node thisName) { return newResult(pos, thisName); } NullLiteralResult newNullLiteral(const TokenPos& pos) { return newResult(pos); } RawUndefinedLiteralResult newRawUndefinedLiteral(const TokenPos& pos) { return newResult(pos); } RegExpLiteralResult newRegExp(RegExpIndex index, const TokenPos& pos) { return newResult(index, pos); } ConditionalExpressionResult newConditional(Node cond, Node thenExpr, Node elseExpr) { return newResult(cond, thenExpr, elseExpr); } UnaryNodeResult 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().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); } UnaryNodeResult newTypeof(uint32_t begin, Node kid) { ParseNodeKind pnk = kid->isKind(ParseNodeKind::Name) ? ParseNodeKind::TypeOfNameExpr : ParseNodeKind::TypeOfExpr; return newUnary(pnk, begin, kid); } UnaryNodeResult newUnary(ParseNodeKind kind, uint32_t begin, Node kid) { TokenPos pos(begin, kid->pn_pos.end); return newResult(kind, pos, kid); } UnaryNodeResult newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) { TokenPos pos(begin, kid->pn_pos.end); return newResult(kind, pos, kid); } UnaryNodeResult newSpread(uint32_t begin, Node kid) { TokenPos pos(begin, kid->pn_pos.end); return newResult(ParseNodeKind::Spread, pos, kid); } private: BinaryNodeResult newBinary(ParseNodeKind kind, Node left, Node right) { TokenPos pos(left->pn_pos.begin, right->pn_pos.end); return newResult(kind, pos, left, right); } public: NodeResult appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) { return ParseNode::appendOrCreateList(kind, left, right, this, pc); } // Expressions ListNodeResult newArrayLiteral(uint32_t begin) { return newResult(ParseNodeKind::ArrayExpr, TokenPos(begin, begin + 1)); } [[nodiscard]] bool addElision(ListNodeType literal, const TokenPos& pos) { MOZ_ASSERT(literal->isKind(ParseNodeKind::ArrayExpr)); NullaryNode* elision; MOZ_TRY_VAR_OR_RETURN( elision, newResult(ParseNodeKind::Elision, pos), false); addList(/* list = */ literal, /* kid = */ elision); literal->setHasNonConstInitializer(); return true; } [[nodiscard]] bool addSpreadElement(ListNodeType literal, uint32_t begin, Node inner) { MOZ_ASSERT( literal->isKind(ParseNodeKind::ArrayExpr) || IF_RECORD_TUPLE(literal->isKind(ParseNodeKind::TupleExpr), false)); UnaryNodeType spread; MOZ_TRY_VAR_OR_RETURN(spread, newSpread(begin, inner), false); addList(/* list = */ literal, /* kid = */ spread); literal->setHasNonConstInitializer(); return true; } void addArrayElement(ListNodeType literal, Node element) { MOZ_ASSERT( literal->isKind(ParseNodeKind::ArrayExpr) || literal->isKind(ParseNodeKind::CallSiteObj) || IF_RECORD_TUPLE(literal->isKind(ParseNodeKind::TupleExpr), false)); if (!element->isConstant()) { literal->setHasNonConstInitializer(); } addList(/* list = */ literal, /* kid = */ element); } CallNodeResult newCall(Node callee, ListNodeType args, JSOp callOp) { return newResult(ParseNodeKind::CallExpr, callOp, callee, args); } CallNodeResult newOptionalCall(Node callee, ListNodeType args, JSOp callOp) { return newResult(ParseNodeKind::OptionalCallExpr, callOp, callee, args); } ListNodeResult newArguments(const TokenPos& pos) { return newResult(ParseNodeKind::Arguments, pos); } CallNodeResult newSuperCall(Node callee, ListNodeType args, bool isSpread) { return newResult( ParseNodeKind::SuperCallExpr, isSpread ? JSOp::SpreadSuperCall : JSOp::SuperCall, callee, args); } CallNodeResult newTaggedTemplate(Node tag, ListNodeType args, JSOp callOp) { return newResult(ParseNodeKind::TaggedTemplateExpr, callOp, tag, args); } ListNodeResult newObjectLiteral(uint32_t begin) { return newResult(ParseNodeKind::ObjectExpr, TokenPos(begin, begin + 1)); } #ifdef ENABLE_RECORD_TUPLE ListNodeResult newRecordLiteral(uint32_t begin) { return newResult(ParseNodeKind::RecordExpr, TokenPos(begin, begin + 1)); } ListNodeResult newTupleLiteral(uint32_t begin) { return newResult(ParseNodeKind::TupleExpr, TokenPos(begin, begin + 1)); } #endif ClassNodeResult newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock, #ifdef ENABLE_DECORATORS ListNodeType decorators, FunctionNodeType addInitializerFunction, #endif const TokenPos& pos) { return newResult(name, heritage, memberBlock, #ifdef ENABLE_DECORATORS decorators, addInitializerFunction, #endif pos); } ListNodeResult newClassMemberList(uint32_t begin) { return newResult(ParseNodeKind::ClassMemberList, TokenPos(begin, begin + 1)); } ClassNamesResult newClassNames(Node outer, Node inner, const TokenPos& pos) { return newResult(outer, inner, pos); } NewTargetNodeResult newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder, NameNodeType newTargetName) { return newResult(newHolder, targetHolder, newTargetName); } NullaryNodeResult newPosHolder(const TokenPos& pos) { return newResult(ParseNodeKind::PosHolder, pos); } UnaryNodeResult newSuperBase(Node thisName, const TokenPos& pos) { return newResult(ParseNodeKind::SuperBase, pos, thisName); } [[nodiscard]] 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; MOZ_TRY_VAR_OR_RETURN( mutation, newUnary(ParseNodeKind::MutateProto, begin, expr), false); addList(/* list = */ literal, /* kid = */ mutation); return true; } BinaryNodeResult newPropertyDefinition(Node key, Node val) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(val); return newResult(key, val, AccessorType::None); } void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) { MOZ_ASSERT( literal->isKind(ParseNodeKind::ObjectExpr) || IF_RECORD_TUPLE(literal->isKind(ParseNodeKind::RecordExpr), false)); MOZ_ASSERT(propdef->isKind(ParseNodeKind::PropertyDefinition)); if (!propdef->right()->isConstant()) { literal->setHasNonConstInitializer(); } addList(/* list = */ literal, /* kid = */ propdef); } [[nodiscard]] bool addPropertyDefinition(ListNodeType literal, Node key, Node val) { BinaryNode* propdef; MOZ_TRY_VAR_OR_RETURN(propdef, newPropertyDefinition(key, val), false); addPropertyDefinition(literal, propdef); return true; } [[nodiscard]] bool addShorthand(ListNodeType literal, NameNodeType name, NameNodeType expr) { MOZ_ASSERT( literal->isKind(ParseNodeKind::ObjectExpr) || IF_RECORD_TUPLE(literal->isKind(ParseNodeKind::RecordExpr), false)); MOZ_ASSERT(name->isKind(ParseNodeKind::ObjectPropertyName)); MOZ_ASSERT(expr->isKind(ParseNodeKind::Name)); MOZ_ASSERT(name->atom() == expr->atom()); literal->setHasNonConstInitializer(); BinaryNode* propdef; MOZ_TRY_VAR_OR_RETURN( propdef, newBinary(ParseNodeKind::Shorthand, name, expr), false); addList(/* list = */ literal, /* kid = */ propdef); return true; } [[nodiscard]] bool addSpreadProperty(ListNodeType literal, uint32_t begin, Node inner) { MOZ_ASSERT( literal->isKind(ParseNodeKind::ObjectExpr) || IF_RECORD_TUPLE(literal->isKind(ParseNodeKind::RecordExpr), false)); literal->setHasNonConstInitializer(); ParseNode* spread; MOZ_TRY_VAR_OR_RETURN(spread, newSpread(begin, inner), false); addList(/* list = */ literal, /* kid = */ spread); return true; } [[nodiscard]] bool addObjectMethodDefinition(ListNodeType literal, Node key, FunctionNodeType funNode, AccessorType atype) { literal->setHasNonConstInitializer(); checkAndSetIsDirectRHSAnonFunction(funNode); ParseNode* propdef; MOZ_TRY_VAR_OR_RETURN( propdef, newObjectMethodOrPropertyDefinition(key, funNode, atype), false); addList(/* list = */ literal, /* kid = */ propdef); return true; } [[nodiscard]] ClassMethodResult newDefaultClassConstructor( Node key, FunctionNodeType funNode) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(funNode); return newResult( ParseNodeKind::DefaultConstructor, key, funNode, AccessorType::None, /* isStatic = */ false, /* initializeIfPrivate = */ nullptr #ifdef ENABLE_DECORATORS , /* decorators = */ nullptr #endif ); } [[nodiscard]] ClassMethodResult newClassMethodDefinition( Node key, FunctionNodeType funNode, AccessorType atype, bool isStatic, mozilla::Maybe initializerIfPrivate #ifdef ENABLE_DECORATORS , ListNodeType decorators #endif ) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(funNode); if (initializerIfPrivate.isSome()) { return newResult(ParseNodeKind::ClassMethod, key, funNode, atype, isStatic, initializerIfPrivate.value() #ifdef ENABLE_DECORATORS , decorators #endif ); } return newResult(ParseNodeKind::ClassMethod, key, funNode, atype, isStatic, /* initializeIfPrivate = */ nullptr #ifdef ENABLE_DECORATORS , decorators #endif ); } [[nodiscard]] ClassFieldResult newClassFieldDefinition( Node name, FunctionNodeType initializer, bool isStatic #ifdef ENABLE_DECORATORS , ListNodeType decorators, ClassMethodType accessorGetterNode, ClassMethodType accessorSetterNode #endif ) { MOZ_ASSERT(isUsableAsObjectPropertyName(name)); return newResult(name, initializer, isStatic #if ENABLE_DECORATORS , decorators, accessorGetterNode, accessorSetterNode #endif ); } [[nodiscard]] StaticClassBlockResult newStaticClassBlock( FunctionNodeType block) { return newResult(block); } [[nodiscard]] bool addClassMemberDefinition(ListNodeType memberList, Node member) { MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList)); // Constructors can be surrounded by LexicalScopes. MOZ_ASSERT(member->isKind(ParseNodeKind::DefaultConstructor) || member->isKind(ParseNodeKind::ClassMethod) || member->isKind(ParseNodeKind::ClassField) || member->isKind(ParseNodeKind::StaticClassBlock) || (member->isKind(ParseNodeKind::LexicalScope) && member->as().scopeBody()->is())); addList(/* list = */ memberList, /* kid = */ member); return true; } UnaryNodeResult newInitialYieldExpression(uint32_t begin, Node gen) { TokenPos pos(begin, begin + 1); return newResult(ParseNodeKind::InitialYield, pos, gen); } UnaryNodeResult newYieldExpression(uint32_t begin, Node value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); return newResult(ParseNodeKind::YieldExpr, pos, value); } UnaryNodeResult newYieldStarExpression(uint32_t begin, Node value) { TokenPos pos(begin, value->pn_pos.end); return newResult(ParseNodeKind::YieldStarExpr, pos, value); } UnaryNodeResult newAwaitExpression(uint32_t begin, Node value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); return newResult(ParseNodeKind::AwaitExpr, pos, value); } UnaryNodeResult newOptionalChain(uint32_t begin, Node value) { TokenPos pos(begin, value->pn_pos.end); return newResult(ParseNodeKind::OptionalChain, pos, value); } // Statements ListNodeResult newStatementList(const TokenPos& pos) { return newResult(ParseNodeKind::StatementList, pos); } [[nodiscard]] bool isFunctionStmt(Node stmt) { while (stmt->isKind(ParseNodeKind::LabelStmt)) { stmt = stmt->as().statement(); } return stmt->is(); } 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(); } } [[nodiscard]] 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; MOZ_TRY_VAR_OR_RETURN( makeGen, newResult(ParseNodeKind::Generator, yieldPos), false); ParseNode* genInit; MOZ_TRY_VAR_OR_RETURN( genInit, newAssignment(ParseNodeKind::AssignExpr, /* lhs = */ genName, /* rhs = */ makeGen), false); UnaryNode* initialYield; MOZ_TRY_VAR_OR_RETURN(initialYield, newInitialYieldExpression(yieldPos.begin, genInit), false); stmtList->prepend(initialYield); return true; } BinaryNodeResult newSetThis(Node thisName, Node value) { return newBinary(ParseNodeKind::SetThis, thisName, value); } NullaryNodeResult newEmptyStatement(const TokenPos& pos) { return newResult(ParseNodeKind::EmptyStmt, pos); } BinaryNodeResult newImportAttribute(Node keyNode, Node valueNode) { return newBinary(ParseNodeKind::ImportAttribute, keyNode, valueNode); } BinaryNodeResult newModuleRequest(Node moduleSpec, Node importAttributeList, const TokenPos& pos) { return newResult(ParseNodeKind::ImportModuleRequest, pos, moduleSpec, importAttributeList); } BinaryNodeResult newImportDeclaration(Node importSpecSet, Node moduleRequest, const TokenPos& pos) { return newResult(ParseNodeKind::ImportDecl, pos, importSpecSet, moduleRequest); } BinaryNodeResult newImportSpec(Node importNameNode, Node bindingName) { return newBinary(ParseNodeKind::ImportSpec, importNameNode, bindingName); } UnaryNodeResult newImportNamespaceSpec(uint32_t begin, Node bindingName) { return newUnary(ParseNodeKind::ImportNamespaceSpec, begin, bindingName); } UnaryNodeResult newExportDeclaration(Node kid, const TokenPos& pos) { return newResult(ParseNodeKind::ExportStmt, pos, kid); } BinaryNodeResult newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleRequest) { BinaryNode* decl; MOZ_TRY_VAR(decl, newResult(ParseNodeKind::ExportFromStmt, exportSpecSet, moduleRequest)); decl->pn_pos.begin = begin; return decl; } BinaryNodeResult newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) { if (maybeBinding) { MOZ_ASSERT(maybeBinding->isKind(ParseNodeKind::Name)); MOZ_ASSERT(!maybeBinding->isInParens()); checkAndSetIsDirectRHSAnonFunction(kid); } return newResult(ParseNodeKind::ExportDefaultStmt, pos, kid, maybeBinding); } BinaryNodeResult newExportSpec(Node bindingName, Node exportName) { return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName); } UnaryNodeResult newExportNamespaceSpec(uint32_t begin, Node exportName) { return newUnary(ParseNodeKind::ExportNamespaceSpec, begin, exportName); } NullaryNodeResult newExportBatchSpec(const TokenPos& pos) { return newResult(ParseNodeKind::ExportBatchSpecStmt, pos); } BinaryNodeResult newImportMeta(NullaryNodeType importHolder, NullaryNodeType metaHolder) { return newResult(ParseNodeKind::ImportMetaExpr, importHolder, metaHolder); } BinaryNodeResult newCallImport(NullaryNodeType importHolder, Node singleArg) { return newResult(ParseNodeKind::CallImportExpr, importHolder, singleArg); } BinaryNodeResult newCallImportSpec(Node specifierArg, Node optionalArg) { return newResult(ParseNodeKind::CallImportSpec, specifierArg, optionalArg); } UnaryNodeResult newExprStatement(Node expr, uint32_t end) { MOZ_ASSERT(expr->pn_pos.end <= end); return newResult(ParseNodeKind::ExpressionStmt, TokenPos(expr->pn_pos.begin, end), expr); } TernaryNodeResult newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) { TernaryNode* node; MOZ_TRY_VAR(node, newResult(ParseNodeKind::IfStmt, cond, thenBranch, elseBranch)); node->pn_pos.begin = begin; return node; } BinaryNodeResult newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return newResult(ParseNodeKind::DoWhileStmt, pos, body, cond); } BinaryNodeResult newWhileStatement(uint32_t begin, Node cond, Node body) { TokenPos pos(begin, body->pn_pos.end); return newResult(ParseNodeKind::WhileStmt, pos, cond, body); } ForNodeResult newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) { return newResult(TokenPos(begin, body->pn_pos.end), forHead, body, iflags); } TernaryNodeResult newForHead(Node init, Node test, Node update, const TokenPos& pos) { return newResult(ParseNodeKind::ForHead, init, test, update, pos); } TernaryNodeResult newForInOrOfHead(ParseNodeKind kind, Node target, Node iteratedExpr, const TokenPos& pos) { MOZ_ASSERT(kind == ParseNodeKind::ForIn || kind == ParseNodeKind::ForOf); return newResult(kind, target, nullptr, iteratedExpr, pos); } SwitchStatementResult newSwitchStatement( uint32_t begin, Node discriminant, LexicalScopeNodeType lexicalForCaseList, bool hasDefault) { return newResult(begin, discriminant, lexicalForCaseList, hasDefault); } CaseClauseResult newCaseOrDefault(uint32_t begin, Node expr, Node body) { return newResult(expr, body, begin); } ContinueStatementResult newContinueStatement(TaggedParserAtomIndex label, const TokenPos& pos) { return newResult(label, pos); } BreakStatementResult newBreakStatement(TaggedParserAtomIndex label, const TokenPos& pos) { return newResult(label, pos); } UnaryNodeResult newReturnStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos)); return newResult(ParseNodeKind::ReturnStmt, pos, expr); } UnaryNodeResult newExpressionBody(Node expr) { return newResult(ParseNodeKind::ReturnStmt, expr->pn_pos, expr); } BinaryNodeResult newWithStatement(uint32_t begin, Node expr, Node body) { return newResult(ParseNodeKind::WithStmt, TokenPos(begin, body->pn_pos.end), expr, body); } LabeledStatementResult newLabeledStatement(TaggedParserAtomIndex label, Node stmt, uint32_t begin) { return newResult(label, stmt, begin); } UnaryNodeResult newThrowStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT(pos.encloses(expr->pn_pos)); return newResult(ParseNodeKind::ThrowStmt, pos, expr); } TernaryNodeResult newTryStatement(uint32_t begin, Node body, LexicalScopeNodeType catchScope, Node finallyBlock) { return newResult(begin, body, catchScope, finallyBlock); } DebuggerStatementResult newDebuggerStatement(const TokenPos& pos) { return newResult(pos); } NameNodeResult newPropertyName(TaggedParserAtomIndex name, const TokenPos& pos) { return newResult(ParseNodeKind::PropertyNameExpr, name, pos); } PropertyAccessResult newPropertyAccess(Node expr, NameNodeType key) { return newResult(expr, key, expr->pn_pos.begin, key->pn_pos.end); } PropertyByValueResult newPropertyByValue(Node lhs, Node index, uint32_t end) { return newResult(lhs, index, lhs->pn_pos.begin, end); } OptionalPropertyAccessResult newOptionalPropertyAccess(Node expr, NameNodeType key) { return newResult(expr, key, expr->pn_pos.begin, key->pn_pos.end); } OptionalPropertyByValueResult newOptionalPropertyByValue(Node lhs, Node index, uint32_t end) { return newResult(lhs, index, lhs->pn_pos.begin, end); } PrivateMemberAccessResult newPrivateMemberAccess(Node lhs, NameNodeType privateName, uint32_t end) { return newResult(lhs, privateName, lhs->pn_pos.begin, end); } OptionalPrivateMemberAccessResult newOptionalPrivateMemberAccess( Node lhs, NameNodeType privateName, uint32_t end) { return newResult(lhs, privateName, lhs->pn_pos.begin, end); } bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName, Node catchBody) { BinaryNode* catchClause; if (catchName) { MOZ_TRY_VAR_OR_RETURN( catchClause, newResult(ParseNodeKind::Catch, catchName, catchBody), false); } else { MOZ_TRY_VAR_OR_RETURN( catchClause, newResult(ParseNodeKind::Catch, catchBody->pn_pos, catchName, catchBody), false); } lexicalScope->setScopeBody(catchClause); return true; } [[nodiscard]] inline bool setLastFunctionFormalParameterDefault( FunctionNodeType funNode, Node defaultValue); void checkAndSetIsDirectRHSAnonFunction(Node pn) { if (IsAnonymousFunctionDefinition(pn)) { pn->setDirectRHSAnonFunction(true); } } ParamsBodyNodeResult newParamsBody(const TokenPos& pos) { return newResult(pos); } FunctionNodeResult newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { return newResult(syntaxKind, pos); } BinaryNodeResult newObjectMethodOrPropertyDefinition(Node key, Node value, AccessorType atype) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); return newResult(key, value, atype); } void setFunctionFormalParametersAndBody(FunctionNodeType funNode, ParamsBodyNodeType 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) { addList(/* list = */ funNode->body(), /* kid = */ body); } ModuleNodeResult newModule(const TokenPos& pos) { return newResult(pos); } LexicalScopeNodeResult newLexicalScope(LexicalScope::ParserData* bindings, Node body, ScopeKind kind = ScopeKind::Lexical) { return newResult(bindings, body, kind); } ClassBodyScopeNodeResult newClassBodyScope( ClassBodyScope::ParserData* bindings, ListNodeType body) { return newResult(bindings, body); } CallNodeResult newNewExpression(uint32_t begin, Node ctor, ListNodeType args, bool isSpread) { return newResult(ParseNodeKind::NewExpr, isSpread ? JSOp::SpreadNew : JSOp::New, TokenPos(begin, args->pn_pos.end), ctor, args); } AssignmentNodeResult newAssignment(ParseNodeKind kind, Node lhs, Node rhs) { if ((kind == ParseNodeKind::AssignExpr || kind == ParseNodeKind::CoalesceAssignExpr || kind == ParseNodeKind::OrAssignExpr || kind == ParseNodeKind::AndAssignExpr) && lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) { checkAndSetIsDirectRHSAnonFunction(rhs); } return newResult(kind, lhs, rhs); } BinaryNodeResult newInitExpr(Node lhs, Node rhs) { TokenPos pos(lhs->pn_pos.begin, rhs->pn_pos.end); return newResult(ParseNodeKind::InitExpr, pos, 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); } AssignmentNodeResult 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; } ListNodeResult newList(ParseNodeKind kind, const TokenPos& pos) { auto list = newResult(kind, pos); MOZ_ASSERT_IF(list.isOk(), !list.unwrap()->is()); MOZ_ASSERT_IF(list.isOk(), !list.unwrap()->is()); return list; } ListNodeResult newList(ParseNodeKind kind, Node kid) { auto list = newResult(kind, kid); MOZ_ASSERT_IF(list.isOk(), !list.unwrap()->is()); MOZ_ASSERT_IF(list.isOk(), !list.unwrap()->is()); return list; } DeclarationListNodeResult newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { return newResult(kind, pos); } ListNodeResult newCommaExpressionList(Node kid) { return newResult(ParseNodeKind::CommaExpr, kid); } void addList(ListNodeType list, Node kid) { list->append(kid); } void setListHasNonConstInitializer(ListNodeType literal) { literal->setHasNonConstInitializer(); } // NOTE: This is infallible. template [[nodiscard]] NodeType parenthesize(NodeType node) { node->setInParens(true); return node; } // NOTE: This is infallible. template [[nodiscard]] NodeType setLikelyIIFE(NodeType node) { return parenthesize(node); } bool isName(Node node) { return node->isKind(ParseNodeKind::Name); } bool isArgumentsName(Node node) { return node->isKind(ParseNodeKind::Name) && node->as().atom() == TaggedParserAtomIndex::WellKnown::arguments(); } bool isEvalName(Node node) { return node->isKind(ParseNodeKind::Name) && node->as().atom() == TaggedParserAtomIndex::WellKnown::eval(); } bool isAsyncKeyword(Node node) { return node->isKind(ParseNodeKind::Name) && node->pn_pos.begin + strlen("async") == node->pn_pos.end && node->as().atom() == TaggedParserAtomIndex::WellKnown::async(); } bool isPrivateName(Node node) { return node->isKind(ParseNodeKind::PrivateName); } bool isPrivateMemberAccess(Node node) { if (node->isKind(ParseNodeKind::OptionalChain)) { return isPrivateMemberAccess(node->as().kid()); } return node->is(); } TaggedParserAtomIndex maybeDottedProperty(Node pn) { return pn->is() ? pn->as().name() : TaggedParserAtomIndex::null(); } TaggedParserAtomIndex isStringExprStatement(Node pn, TokenPos* pos) { if (pn->is()) { UnaryNode* unary = &pn->as(); if (auto atom = unary->isStringExprStatement()) { *pos = unary->kid()->pn_pos; return atom; } } return TaggedParserAtomIndex::null(); } bool reuseLazyInnerFunctions() { return reuseGCThings; } bool reuseClosedOverBindings() { return reuseGCThings; } bool reuseRegexpSyntaxParse() { return reuseGCThings; } void nextLazyInnerFunction() { lazyInnerFunctionIndex++; } TaggedParserAtomIndex nextLazyClosedOverBinding() { // Trailing nullptrs were elided in PerHandlerParser::finishFunction(). auto closedOverBindings = previousParseCache_.closedOverBindings(); if (lazyClosedOverBindingIndex >= closedOverBindings.Length()) { return TaggedParserAtomIndex::null(); } return closedOverBindings[lazyClosedOverBindingIndex++]; } const ScriptStencil& cachedScriptData() const { // lazyInnerFunctionIndex is incremented with nextLazyInnferFunction before // reading the content, thus we need -1 to access the element that we just // skipped. return previousParseCache_.scriptData(lazyInnerFunctionIndex - 1); } const ScriptStencilExtra& cachedScriptExtra() const { // lazyInnerFunctionIndex is incremented with nextLazyInnferFunction before // reading the content, thus we need -1 to access the element that we just // skipped. return previousParseCache_.scriptExtra(lazyInnerFunctionIndex - 1); } void setPrivateNameKind(Node node, PrivateNameKind kind) { MOZ_ASSERT(node->is()); node->as().setPrivateNameKind(kind); } }; inline bool FullParseHandler::setLastFunctionFormalParameterDefault( FunctionNodeType funNode, Node defaultValue) { ParamsBodyNode* body = funNode->body(); ParseNode* arg = body->last(); ParseNode* pn; MOZ_TRY_VAR_OR_RETURN( pn, newAssignment(ParseNodeKind::AssignExpr, arg, defaultValue), false); body->replaceLast(pn); return true; } } // namespace frontend } // namespace js #endif /* frontend_FullParseHandler_h */