/* -*- 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 // std::nullptr_t #include #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(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 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()) { lazyClosedOverBindingIndex++; } else { break; } } } } 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 // 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_(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(const ParserAtom* atom, const TokenPos& pos) { return new_(ParseNodeKind::ObjectPropertyName, atom, pos); } NameNodeType newPrivateName(const ParserAtom* 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, BaseCompilationStencil& stencil, const TokenPos& pos) { return new_(index, stencil, pos); } BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) { return new_(cond, pos); } NameNodeType newStringLiteral(const ParserAtom* atom, const TokenPos& pos) { return new_(ParseNodeKind::StringExpr, atom, pos); } NameNodeType newTemplateStringLiteral(const ParserAtom* 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)); } MOZ_MUST_USE 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; } 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_(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)); } ClassNodeType newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock, const TokenPos& pos) { return new_(name, heritage, memberBlock, 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); } BinaryNodeType newNewTarget(NullaryNodeType newHolder, NullaryNodeType targetHolder) { return new_(ParseNodeKind::NewTargetExpr, newHolder, targetHolder); } NullaryNodeType newPosHolder(const TokenPos& pos) { return new_(ParseNodeKind::PosHolder, pos); } UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) { return new_(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_(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 initializerIfPrivate) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); checkAndSetIsDirectRHSAnonFunction(funNode); if (initializerIfPrivate.isSome()) { return new_(key, funNode, atype, isStatic, initializerIfPrivate.value()); } return new_(key, funNode, atype, isStatic, nullptr); } MOZ_MUST_USE ClassField* newClassFieldDefinition(Node name, FunctionNodeType initializer, bool isStatic) { MOZ_ASSERT(isUsableAsObjectPropertyName(name)); return new_(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().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_(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); } MOZ_MUST_USE 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(); } } 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_(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 newImportDeclaration(Node importSpecSet, Node moduleSpec, const TokenPos& pos) { return new_(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_(ParseNodeKind::ExportStmt, pos, kid); } BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) { BinaryNode* decl = new_(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_(ParseNodeKind::ExportDefaultStmt, pos, kid, maybeBinding); } BinaryNodeType newExportSpec(Node bindingName, Node exportName) { return newBinary(ParseNodeKind::ExportSpec, bindingName, 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); } UnaryNodeType newExprStatement(Node expr, uint32_t end) { MOZ_ASSERT_IF(sourceKind() == SourceKind::Text, expr->pn_pos.end <= end); return new_(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_(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(const ParserName* label, const TokenPos& pos) { return new_(label, pos); } BreakStatementType newBreakStatement(const ParserName* label, const TokenPos& pos) { return new_(label, pos); } UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT_IF(expr && sourceKind() == SourceKind::Text, 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(const ParserName* label, Node stmt, uint32_t begin) { return new_(label, stmt, begin); } UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) { MOZ_ASSERT_IF(sourceKind() == SourceKind::Text, 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(const ParserName* 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); } 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; } 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_(syntaxKind, pos); } BinaryNodeType newObjectMethodOrPropertyDefinition(Node key, Node value, AccessorType atype) { MOZ_ASSERT(isUsableAsObjectPropertyName(key)); return new_(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_(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_(pos); } LexicalScopeNodeType newLexicalScope(LexicalScope::ParserData* bindings, Node body, ScopeKind kind = ScopeKind::Lexical) { return new_(bindings, body, kind); } 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 || kind == ParseNodeKind::InitExpr) && lhs->isKind(ParseNodeKind::Name) && !lhs->isInParens()) { checkAndSetIsDirectRHSAnonFunction(rhs); } return new_(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_(kind, pos); } public: ListNodeType newList(ParseNodeKind kind, Node kid) { MOZ_ASSERT(!isDeclarationKind(kind)); return new_(kind, kid); } ListNodeType newDeclarationList(ParseNodeKind kind, const TokenPos& pos) { MOZ_ASSERT(isDeclarationKind(kind)); return new_(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_(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 MOZ_MUST_USE NodeType parenthesize(NodeType node) { node->setInParens(true); return node; } template 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().atom() == cx->parserNames().arguments; } bool isEvalName(Node node, JSContext* cx) { return node->isKind(ParseNodeKind::Name) && node->as().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().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(); if (isPrivateName(&pbv.key())) { return true; } } if (node->isKind(ParseNodeKind::OptionalChain)) { return isPrivateField(node->as().kid()); } return false; } const ParserName* maybeDottedProperty(Node pn) { return pn->is() ? pn->as().name() : nullptr; } const ParserAtom* isStringExprStatement(Node pn, TokenPos* pos) { if (pn->is()) { UnaryNode* unary = &pn->as(); 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() .as(); } 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()->isAtom()); return static_cast(cell); } void setPrivateNameKind(Node node, PrivateNameKind kind) { MOZ_ASSERT(node->is()); node->as().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 */