/* -*- 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 // 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/ParserAtom.h" // TaggedParserAtomIndex #include "frontend/SharedContext.h" #include "frontend/Stencil.h" 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; 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 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(); } #define DECLARE_AS(typeName, longTypeName, asMethodName) \ static longTypeName asMethodName(Node node) { return &node->as(); } FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS) #undef DECLARE_AS NameNodeType newName(TaggedParserAtomIndex name, const TokenPos& pos) { return new_(ParseNodeKind::Name, name, pos); } UnaryNodeType newComputedName(Node expr, uint32_t begin, uint32_t end) { TokenPos pos(begin, end); return new_(ParseNodeKind::ComputedName, pos, expr); } UnaryNodeType newSyntheticComputedName(Node expr, uint32_t begin, uint32_t end) { TokenPos pos(begin, end); UnaryNode* node = new_(ParseNodeKind::ComputedName, pos, expr); if (!node) { return nullptr; } node->setSyntheticComputedName(); return node; } NameNodeType newObjectLiteralPropertyName(TaggedParserAtomIndex atom, const TokenPos& pos) { return new_(ParseNodeKind::ObjectPropertyName, atom, pos); } NameNodeType newPrivateName(TaggedParserAtomIndex atom, const TokenPos& pos) { return new_(ParseNodeKind::PrivateName, atom, pos); } NumericLiteralType newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return new_(value, decimalPoint, pos); } BigIntLiteralType newBigInt(BigIntIndex index, bool isZero, const TokenPos& pos) { return new_(index, isZero, pos); } BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) { return new_(cond, pos); } NameNodeType newStringLiteral(TaggedParserAtomIndex atom, const TokenPos& pos) { return new_(ParseNodeKind::StringExpr, atom, pos); } NameNodeType newTemplateStringLiteral(TaggedParserAtomIndex atom, const TokenPos& pos) { return new_(ParseNodeKind::TemplateStringExpr, atom, pos); } CallSiteNodeType newCallSiteObject(uint32_t begin) { CallSiteNode* callSiteObj = new_(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_(pos, thisName); } NullLiteralType newNullLiteral(const TokenPos& pos) { return new_(pos); } RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) { return new_(pos); } RegExpLiteralType newRegExp(RegExpIndex index, const TokenPos& pos) { return new_(index, pos); } ConditionalExpressionType newConditional(Node cond, Node thenExpr, Node elseExpr) { return new_(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().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_(kind, pos, kid); } UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) { TokenPos pos(begin, kid->pn_pos.end); return new_(kind, pos, kid); } UnaryNodeType newSpread(uint32_t begin, Node kid) { TokenPos pos(begin, kid->pn_pos.end); return new_(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_(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_(ParseNodeKind::ArrayExpr, TokenPos(begin, begin + 1)); } [[nodiscard]] bool addElision(ListNodeType literal, const TokenPos& pos) { MOZ_ASSERT(literal->isKind(ParseNodeKind::ArrayExpr)); NullaryNode* elision = new_(ParseNodeKind::Elision, pos); if (!elision) { return 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 = newSpread(begin, inner); if (!spread) { return 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); } CallNodeType newCall(Node callee, Node args, JSOp callOp) { return new_(ParseNodeKind::CallExpr, callOp, callee, args); } OptionalCallNodeType newOptionalCall(Node callee, Node args, JSOp callOp) { return new_(ParseNodeKind::OptionalCallExpr, callOp, callee, args); } ListNodeType newArguments(const TokenPos& pos) { return new_(ParseNodeKind::Arguments, pos); } CallNodeType newSuperCall(Node callee, Node args, bool isSpread) { return new_(ParseNodeKind::SuperCallExpr, isSpread ? JSOp::SpreadSuperCall : JSOp::SuperCall, callee, args); } CallNodeType newTaggedTemplate(Node tag, Node args, JSOp callOp) { return new_(ParseNodeKind::TaggedTemplateExpr, callOp, tag, args); } ListNodeType newObjectLiteral(uint32_t begin) { return new_(ParseNodeKind::ObjectExpr, TokenPos(begin, begin + 1)); } #ifdef ENABLE_RECORD_TUPLE ListNodeType newRecordLiteral(uint32_t begin) { return new_(ParseNodeKind::RecordExpr, TokenPos(begin, begin + 1)); } ListNodeType newTupleLiteral(uint32_t begin) { return new_(ParseNodeKind::TupleExpr, TokenPos(begin, begin + 1)); } #endif ClassNodeType newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock, #ifdef ENABLE_DECORATORS ListNodeType decorators, #endif const TokenPos& pos) { return new_(name, heritage, memberBlock, #ifdef ENABLE_DECORATORS decorators, #endif pos); } ListNodeType newClassMemberList(uint32_t begin) { return new_(ParseNodeKind::ClassMemberList, TokenPos(begin, begin + 1)); } ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) { return new_(outer, inner, pos); } NewTargetNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder, NameNodeType newTargetName) { return new_(newHolder, targetHolder, newTargetName); } NullaryNodeType newPosHolder(const TokenPos& pos) { return new_(ParseNodeKind::PosHolder, pos); } UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return new_(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 = 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_(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 = newPropertyDefinition(key, val); if (!propdef) { return 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 = newBinary(ParseNodeKind::Shorthand, name, expr); if (!propdef) { return 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 = newSpread(begin, inner); if (!spread) { return 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 = newObjectMethodOrPropertyDefinition(key, funNode, atype); if (!propdef) { return false; } addList(/* list = */ literal, /* kid = */ propdef); return true; } [[nodiscard]] ClassMethod* newDefaultClassConstructor( Node key, FunctionNodeType funNode) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(funNode); return new_( ParseNodeKind::DefaultConstructor, key, funNode, AccessorType::None, /* isStatic = */ false, /* initializeIfPrivate = */ nullptr #ifdef ENABLE_DECORATORS , /* decorators = */ nullptr #endif ); } [[nodiscard]] ClassMethod* 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 new_(ParseNodeKind::ClassMethod, key, funNode, atype, isStatic, initializerIfPrivate.value() #ifdef ENABLE_DECORATORS , decorators #endif ); } return new_(ParseNodeKind::ClassMethod, key, funNode, atype, isStatic, /* initializeIfPrivate = */ nullptr #ifdef ENABLE_DECORATORS , decorators #endif ); } [[nodiscard]] ClassField* newClassFieldDefinition( Node name, FunctionNodeType initializer, bool isStatic #ifdef ENABLE_DECORATORS , ListNodeType decorators, bool hasAccessor #endif ) { MOZ_ASSERT(isUsableAsObjectPropertyName(name)); return new_(name, initializer, isStatic #if ENABLE_DECORATORS , decorators, hasAccessor #endif ); } [[nodiscard]] StaticClassBlock* newStaticClassBlock(FunctionNodeType block) { return new_(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; } UnaryNodeType newInitialYieldExpression(uint32_t begin, Node gen) { TokenPos pos(begin, begin + 1); return new_(ParseNodeKind::InitialYield, pos, gen); } UnaryNodeType newYieldExpression(uint32_t begin, Node value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); return new_(ParseNodeKind::YieldExpr, pos, value); } UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) { TokenPos pos(begin, value->pn_pos.end); return new_(ParseNodeKind::YieldStarExpr, pos, value); } UnaryNodeType newAwaitExpression(uint32_t begin, Node value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); return new_(ParseNodeKind::AwaitExpr, pos, value); } UnaryNodeType newOptionalChain(uint32_t begin, Node value) { TokenPos pos(begin, value->pn_pos.end); return new_(ParseNodeKind::OptionalChain, pos, value); } // Statements ListNodeType newStatementList(const TokenPos& pos) { return new_(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 = new_(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_(ParseNodeKind::EmptyStmt, pos); } BinaryNodeType newImportAssertion(Node keyNode, Node valueNode) { return newBinary(ParseNodeKind::ImportAssertion, keyNode, valueNode); } BinaryNodeType newModuleRequest(Node moduleSpec, Node importAssertionList, const TokenPos& pos) { return new_(ParseNodeKind::ImportModuleRequest, pos, moduleSpec, importAssertionList); } BinaryNodeType newImportDeclaration(Node importSpecSet, Node moduleRequest, const TokenPos& pos) { return new_(ParseNodeKind::ImportDecl, pos, importSpecSet, moduleRequest); } BinaryNodeType newImportSpec(Node importNameNode, Node bindingName) { return newBinary(ParseNodeKind::ImportSpec, importNameNode, bindingName); } UnaryNodeType newImportNamespaceSpec(uint32_t begin, Node bindingName) { return newUnary(ParseNodeKind::ImportNamespaceSpec, begin, bindingName); } UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) { return new_(ParseNodeKind::ExportStmt, pos, kid); } BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleRequest) { BinaryNode* decl = new_(ParseNodeKind::ExportFromStmt, exportSpecSet, moduleRequest); 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_(ParseNodeKind::ExportDefaultStmt, pos, kid, maybeBinding); } BinaryNodeType newExportSpec(Node bindingName, Node exportName) { return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName); } UnaryNodeType newExportNamespaceSpec(uint32_t begin, Node exportName) { return newUnary(ParseNodeKind::ExportNamespaceSpec, begin, exportName); } NullaryNodeType newExportBatchSpec(const TokenPos& pos) { return new_(ParseNodeKind::ExportBatchSpecStmt, pos); } BinaryNodeType newImportMeta(NullaryNodeType importHolder, NullaryNodeType metaHolder) { return new_(ParseNodeKind::ImportMetaExpr, importHolder, metaHolder); } BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) { return new_(ParseNodeKind::CallImportExpr, importHolder, singleArg); } BinaryNodeType newCallImportSpec(Node specifierArg, Node optionalArg) { return new_(ParseNodeKind::CallImportSpec, specifierArg, optionalArg); } UnaryNodeType newExprStatement(Node expr, uint32_t end) { MOZ_ASSERT(expr->pn_pos.end <= end); return new_(ParseNodeKind::ExpressionStmt, TokenPos(expr->pn_pos.begin, end), expr); } TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch, Node elseBranch) { TernaryNode* node = new_(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_(ParseNodeKind::DoWhileStmt, pos, body, cond); } BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) { TokenPos pos(begin, body->pn_pos.end); return new_(ParseNodeKind::WhileStmt, pos, cond, body); } ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead, Node body, unsigned iflags) { return new_(TokenPos(begin, body->pn_pos.end), forHead, body, iflags); } TernaryNodeType newForHead(Node init, Node test, Node update, const TokenPos& pos) { return new_(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_(kind, target, nullptr, iteratedExpr, pos); } SwitchStatementType newSwitchStatement( uint32_t begin, Node discriminant, LexicalScopeNodeType lexicalForCaseList, bool hasDefault) { return new_(begin, discriminant, lexicalForCaseList, hasDefault); } CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) { return new_(expr, body, begin); } ContinueStatementType newContinueStatement(TaggedParserAtomIndex label, const TokenPos& pos) { return new_(label, pos); } BreakStatementType newBreakStatement(TaggedParserAtomIndex label, const TokenPos& pos) { return new_(label, pos); } UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos)); return new_(ParseNodeKind::ReturnStmt, pos, expr); } UnaryNodeType newExpressionBody(Node expr) { return new_(ParseNodeKind::ReturnStmt, expr->pn_pos, expr); } BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) { return new_(ParseNodeKind::WithStmt, TokenPos(begin, body->pn_pos.end), expr, body); } LabeledStatementType newLabeledStatement(TaggedParserAtomIndex label, Node stmt, uint32_t begin) { return new_(label, stmt, begin); } UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT(pos.encloses(expr->pn_pos)); return new_(ParseNodeKind::ThrowStmt, pos, expr); } TernaryNodeType newTryStatement(uint32_t begin, Node body, LexicalScopeNodeType catchScope, Node finallyBlock) { return new_(begin, body, catchScope, finallyBlock); } DebuggerStatementType newDebuggerStatement(const TokenPos& pos) { return new_(pos); } NameNodeType newPropertyName(TaggedParserAtomIndex name, const TokenPos& pos) { return new_(ParseNodeKind::PropertyNameExpr, name, pos); } PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) { return new_(expr, key, expr->pn_pos.begin, key->pn_pos.end); } PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) { return new_(lhs, index, lhs->pn_pos.begin, end); } OptionalPropertyAccessType newOptionalPropertyAccess(Node expr, NameNodeType key) { return new_(expr, key, expr->pn_pos.begin, key->pn_pos.end); } OptionalPropertyByValueType newOptionalPropertyByValue(Node lhs, Node index, uint32_t end) { return new_(lhs, index, lhs->pn_pos.begin, end); } PrivateMemberAccessType newPrivateMemberAccess(Node lhs, NameNodeType privateName, uint32_t end) { return new_(lhs, privateName, lhs->pn_pos.begin, end); } OptionalPrivateMemberAccessType newOptionalPrivateMemberAccess( Node lhs, NameNodeType privateName, uint32_t end) { return new_(lhs, privateName, lhs->pn_pos.begin, end); } bool setupCatchScope(LexicalScopeNodeType lexicalScope, Node catchName, Node catchBody) { BinaryNode* catchClause; if (catchName) { catchClause = new_(ParseNodeKind::Catch, catchName, catchBody); } else { catchClause = new_(ParseNodeKind::Catch, catchBody->pn_pos, catchName, catchBody); } if (!catchClause) { return false; } lexicalScope->setScopeBody(catchClause); return true; } [[nodiscard]] inline bool setLastFunctionFormalParameterDefault( FunctionNodeType funNode, Node defaultValue); void checkAndSetIsDirectRHSAnonFunction(Node pn) { if (IsAnonymousFunctionDefinition(pn)) { pn->setDirectRHSAnonFunction(true); } } ParamsBodyNodeType newParamsBody(const TokenPos& pos) { return new_(pos); } FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind, const TokenPos& pos) { return new_(syntaxKind, pos); } BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node value, AccessorType atype) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); return new_(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); } ModuleNodeType newModule(const TokenPos& pos) { return new_(pos); } LexicalScopeNodeType newLexicalScope(LexicalScope::ParserData* bindings, Node body, ScopeKind kind = ScopeKind::Lexical) { return new_(bindings, body, kind); } ClassBodyScopeNodeType newClassBodyScope(ClassBodyScope::ParserData* bindings, ListNodeType body) { return new_(bindings, body); } CallNodeType newNewExpression(uint32_t begin, Node ctor, Node args, bool isSpread) { return new_(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) && lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) { checkAndSetIsDirectRHSAnonFunction(rhs); } return new_(kind, lhs, rhs); } BinaryNodeType newInitExpr(Node lhs, Node rhs) { TokenPos pos(lhs->pn_pos.begin, rhs->pn_pos.end); return new_(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); } AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode, Node init) { MOZ_ASSERT(nameNode->isKind(ParseNodeKind::Name)); MOZ_ASSERT(!nameNode->isInParens()); checkAndSetIsDirectRHSAnonFunction(init); return newAssignment(ParseNodeKind::AssignExpr, nameNode, init); } void setBeginPosition(Node pn, Node oth) { setBeginPosition(pn, oth->pn_pos.begin); } void setBeginPosition(Node pn, uint32_t begin) { pn->pn_pos.begin = begin; MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); } void setEndPosition(Node pn, Node oth) { setEndPosition(pn, oth->pn_pos.end); } void setEndPosition(Node pn, uint32_t end) { pn->pn_pos.end = end; MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); } uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) { return func->pn_pos.begin; } ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) { auto* list = new_(kind, pos); MOZ_ASSERT_IF(list, !list->is()); MOZ_ASSERT_IF(list, !list->is()); return list; } ListNodeType newList(ParseNodeKind kind, Node kid) { auto* list = new_(kind, kid); MOZ_ASSERT_IF(list, !list->is()); MOZ_ASSERT_IF(list, !list->is()); return list; } DeclarationListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { return new_(kind, pos); } ListNodeType newCommaExpressionList(Node kid) { return new_(ParseNodeKind::CommaExpr, kid); } void addList(ListNodeType list, Node kid) { list->append(kid); } void setListHasNonConstInitializer(ListNodeType literal) { literal->setHasNonConstInitializer(); } template [[nodiscard]] NodeType parenthesize(NodeType node) { node->setInParens(true); return node; } 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 = newAssignment(ParseNodeKind::AssignExpr, arg, defaultValue); if (!pn) { return false; } body->replaceLast(pn); return true; } } // namespace frontend } // namespace js #endif /* frontend_FullParseHandler_h */