| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * 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/PodOperations.h" |
| |
| #include "frontend/ParseNode.h" |
| #include "frontend/SharedContext.h" |
| |
| namespace js { |
| namespace frontend { |
| |
| template <typename ParseHandler> |
| class Parser; |
| |
| class SyntaxParseHandler; |
| |
| // Parse handler used when generating a full parse tree for all code which the |
| // parser encounters. |
| class FullParseHandler |
| { |
| ParseNodeAllocator allocator; |
| TokenStream& tokenStream; |
| |
| ParseNode* allocParseNode(size_t size) { |
| MOZ_ASSERT(size == sizeof(ParseNode)); |
| return static_cast<ParseNode*>(allocator.allocNode()); |
| } |
| |
| ParseNode* cloneNode(const ParseNode& other) { |
| ParseNode* node = allocParseNode(sizeof(ParseNode)); |
| if (!node) |
| return nullptr; |
| mozilla::PodAssign(node, &other); |
| return node; |
| } |
| |
| /* |
| * If this is a full parse to construct the bytecode for a function that |
| * was previously lazily parsed, that lazy function and the current index |
| * into its inner functions. We do not want to reparse the inner functions. |
| */ |
| LazyScript * const lazyOuterFunction_; |
| size_t lazyInnerFunctionIndex; |
| |
| const TokenPos& pos() { |
| return tokenStream.currentToken().pos; |
| } |
| |
| inline ParseNode* makeAssignmentFromArg(ParseNode* arg, ParseNode* lhs, ParseNode* rhs); |
| inline void replaceLastFunctionArgument(ParseNode* funcpn, ParseNode* pn); |
| |
| public: |
| |
| /* |
| * If non-nullptr, points to a syntax parser which can be used for inner |
| * functions. Cleared if language features not handled by the syntax parser |
| * are encountered, in which case all future activity will use the full |
| * parser. |
| */ |
| Parser<SyntaxParseHandler>* syntaxParser; |
| |
| /* new_ methods for creating parse nodes. These report OOM on context. */ |
| JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline) |
| |
| typedef ParseNode* Node; |
| typedef Definition* DefinitionNode; |
| |
| bool isPropertyAccess(ParseNode* node) { |
| return node->isKind(PNK_DOT) || node->isKind(PNK_ELEM); |
| } |
| |
| bool isFunctionCall(ParseNode* node) { |
| // Note: super() is a special form, *not* a function call. |
| return node->isKind(PNK_CALL); |
| } |
| |
| static bool isUnparenthesizedDestructuringPattern(ParseNode* node) { |
| return !node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY)); |
| } |
| |
| static bool isParenthesizedDestructuringPattern(ParseNode* 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(PNK_OBJECT) || node->isKind(PNK_ARRAY)); |
| } |
| |
| static bool isDestructuringPatternAnyParentheses(ParseNode* node) { |
| return isUnparenthesizedDestructuringPattern(node) || |
| isParenthesizedDestructuringPattern(node); |
| } |
| |
| FullParseHandler(ExclusiveContext* cx, LifoAlloc& alloc, |
| TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser, |
| LazyScript* lazyOuterFunction) |
| : allocator(cx, alloc), |
| tokenStream(tokenStream), |
| lazyOuterFunction_(lazyOuterFunction), |
| lazyInnerFunctionIndex(0), |
| syntaxParser(syntaxParser) |
| {} |
| |
| static ParseNode* null() { return nullptr; } |
| |
| ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); } |
| void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); } |
| const Token& currentToken() { return tokenStream.currentToken(); } |
| |
| ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, |
| ExclusiveContext* cx) |
| { |
| return new_<NameNode>(PNK_NAME, JSOP_GETNAME, name, blockid, pos); |
| } |
| |
| ParseNode* newComputedName(ParseNode* expr, uint32_t begin, uint32_t end) { |
| TokenPos pos(begin, end); |
| return new_<UnaryNode>(PNK_COMPUTED_NAME, JSOP_NOP, pos, expr); |
| } |
| |
| Definition* newPlaceholder(JSAtom* atom, uint32_t blockid, const TokenPos& pos) { |
| Definition* dn = |
| (Definition*) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, blockid, pos); |
| if (!dn) |
| return nullptr; |
| dn->setDefn(true); |
| dn->pn_dflags |= PND_PLACEHOLDER; |
| return dn; |
| } |
| |
| ParseNode* newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) { |
| return new_<NullaryNode>(PNK_OBJECT_PROPERTY_NAME, JSOP_NOP, pos, atom); |
| } |
| |
| ParseNode* newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { |
| ParseNode* pn = new_<NullaryNode>(PNK_NUMBER, pos); |
| if (!pn) |
| return nullptr; |
| pn->initNumber(value, decimalPoint); |
| return pn; |
| } |
| |
| ParseNode* newBooleanLiteral(bool cond, const TokenPos& pos) { |
| return new_<BooleanLiteral>(cond, pos); |
| } |
| |
| ParseNode* newStringLiteral(JSAtom* atom, const TokenPos& pos) { |
| return new_<NullaryNode>(PNK_STRING, JSOP_NOP, pos, atom); |
| } |
| |
| ParseNode* newTemplateStringLiteral(JSAtom* atom, const TokenPos& pos) { |
| return new_<NullaryNode>(PNK_TEMPLATE_STRING, JSOP_NOP, pos, atom); |
| } |
| |
| ParseNode* newCallSiteObject(uint32_t begin) { |
| ParseNode* callSite = new_<CallSiteNode>(begin); |
| if (!callSite) |
| return null(); |
| |
| Node propExpr = newArrayLiteral(getPosition(callSite).begin); |
| if (!propExpr) |
| return null(); |
| |
| addArrayElement(callSite, propExpr); |
| |
| return callSite; |
| } |
| |
| bool addToCallSiteObject(ParseNode* callSiteObj, ParseNode* rawNode, ParseNode* cookedNode) { |
| MOZ_ASSERT(callSiteObj->isKind(PNK_CALLSITEOBJ)); |
| |
| addArrayElement(callSiteObj, cookedNode); |
| addArrayElement(callSiteObj->pn_head, 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->pn_head); |
| return true; |
| } |
| |
| ParseNode* newThisLiteral(const TokenPos& pos, ParseNode* thisName) { |
| return new_<ThisLiteral>(pos, thisName); |
| } |
| |
| ParseNode* newNullLiteral(const TokenPos& pos) { |
| return new_<NullLiteral>(pos); |
| } |
| |
| // The Boxer object here is any object that can allocate ObjectBoxes. |
| // Specifically, a Boxer has a .newObjectBox(T) method that accepts a |
| // Rooted<RegExpObject*> argument and returns an ObjectBox*. |
| template <class Boxer> |
| ParseNode* newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { |
| ObjectBox* objbox = boxer.newObjectBox(reobj); |
| if (!objbox) |
| return null(); |
| return new_<RegExpLiteral>(objbox, pos); |
| } |
| |
| ParseNode* newConditional(ParseNode* cond, ParseNode* thenExpr, ParseNode* elseExpr) { |
| return new_<ConditionalExpression>(cond, thenExpr, elseExpr); |
| } |
| |
| void markAsSetCall(ParseNode* pn) { |
| pn->pn_xflags |= PNX_SETCALL; |
| } |
| |
| ParseNode* newDelete(uint32_t begin, ParseNode* expr) { |
| if (expr->isKind(PNK_NAME)) { |
| expr->pn_dflags |= PND_DEOPTIMIZED; |
| expr->setOp(JSOP_DELNAME); |
| return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr); |
| } |
| |
| if (expr->isKind(PNK_DOT)) |
| return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr); |
| |
| if (expr->isKind(PNK_ELEM)) |
| return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr); |
| |
| return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr); |
| } |
| |
| ParseNode* newTypeof(uint32_t begin, ParseNode* kid) { |
| TokenPos pos(begin, kid->pn_pos.end); |
| ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR; |
| return new_<UnaryNode>(kind, JSOP_NOP, pos, kid); |
| } |
| |
| ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { |
| return new_<NullaryNode>(kind, op, pos); |
| } |
| |
| ParseNode* newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, ParseNode* kid) { |
| TokenPos pos(begin, kid ? kid->pn_pos.end : begin + 1); |
| return new_<UnaryNode>(kind, op, pos, kid); |
| } |
| |
| ParseNode* newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { |
| return new_<BinaryNode>(kind, op, pos(), (ParseNode*) nullptr, (ParseNode*) nullptr); |
| } |
| ParseNode* newBinary(ParseNodeKind kind, ParseNode* left, |
| JSOp op = JSOP_NOP) { |
| return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode*) nullptr); |
| } |
| ParseNode* newBinary(ParseNodeKind kind, ParseNode* left, ParseNode* right, |
| JSOp op = JSOP_NOP) { |
| TokenPos pos(left->pn_pos.begin, right->pn_pos.end); |
| return new_<BinaryNode>(kind, op, pos, left, right); |
| } |
| ParseNode* appendOrCreateList(ParseNodeKind kind, ParseNode* left, ParseNode* right, |
| ParseContext<FullParseHandler>* pc, JSOp op = JSOP_NOP) |
| { |
| return ParseNode::appendOrCreateList(kind, op, left, right, this, pc); |
| } |
| |
| ParseNode* newTernary(ParseNodeKind kind, |
| ParseNode* first, ParseNode* second, ParseNode* third, |
| JSOp op = JSOP_NOP) |
| { |
| return new_<TernaryNode>(kind, op, first, second, third); |
| } |
| |
| // Expressions |
| |
| ParseNode* newArrayComprehension(ParseNode* body, const TokenPos& pos) { |
| MOZ_ASSERT(pos.begin <= body->pn_pos.begin); |
| MOZ_ASSERT(body->pn_pos.end <= pos.end); |
| ParseNode* pn = new_<ListNode>(PNK_ARRAYCOMP, pos); |
| if (!pn) |
| return nullptr; |
| pn->append(body); |
| return pn; |
| } |
| |
| ParseNode* newArrayLiteral(uint32_t begin) { |
| ParseNode* literal = new_<ListNode>(PNK_ARRAY, TokenPos(begin, begin + 1)); |
| // Later in this stack: remove dependency on this opcode. |
| if (literal) |
| literal->setOp(JSOP_NEWINIT); |
| return literal; |
| } |
| |
| bool addElision(ParseNode* literal, const TokenPos& pos) { |
| ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos); |
| if (!elision) |
| return false; |
| literal->append(elision); |
| literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; |
| return true; |
| } |
| |
| bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) { |
| TokenPos pos(begin, inner->pn_pos.end); |
| ParseNode* spread = new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, inner); |
| if (!spread) |
| return null(); |
| literal->append(spread); |
| literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; |
| return true; |
| } |
| |
| void addArrayElement(ParseNode* literal, ParseNode* element) { |
| if (!element->isConstant()) |
| literal->pn_xflags |= PNX_NONCONST; |
| literal->append(element); |
| } |
| |
| ParseNode* newCall() { |
| return newList(PNK_CALL, JSOP_CALL); |
| } |
| |
| ParseNode* newTaggedTemplate() { |
| return newList(PNK_TAGGED_TEMPLATE, JSOP_CALL); |
| } |
| |
| ParseNode* newObjectLiteral(uint32_t begin) { |
| ParseNode* literal = new_<ListNode>(PNK_OBJECT, TokenPos(begin, begin + 1)); |
| // Later in this stack: remove dependency on this opcode. |
| if (literal) |
| literal->setOp(JSOP_NEWINIT); |
| return literal; |
| } |
| |
| ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock) { |
| return new_<ClassNode>(name, heritage, methodBlock); |
| } |
| ParseNode* newClassMethodList(uint32_t begin) { |
| return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1)); |
| } |
| ParseNode* newClassNames(ParseNode* outer, ParseNode* inner, const TokenPos& pos) { |
| return new_<ClassNames>(outer, inner, pos); |
| } |
| ParseNode* newNewTarget(ParseNode* newHolder, ParseNode* targetHolder) { |
| return new_<BinaryNode>(PNK_NEWTARGET, JSOP_NOP, newHolder, targetHolder); |
| } |
| ParseNode* newPosHolder(const TokenPos& pos) { |
| return new_<NullaryNode>(PNK_POSHOLDER, pos); |
| } |
| ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) { |
| return new_<UnaryNode>(PNK_SUPERBASE, JSOP_NOP, pos, thisName); |
| } |
| |
| bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) { |
| // Object literals with mutated [[Prototype]] are non-constant so that |
| // singleton objects will have Object.prototype as their [[Prototype]]. |
| setListFlag(literal, PNX_NONCONST); |
| |
| ParseNode* mutation = newUnary(PNK_MUTATEPROTO, JSOP_NOP, begin, expr); |
| if (!mutation) |
| return false; |
| literal->append(mutation); |
| return true; |
| } |
| |
| bool addPropertyDefinition(ParseNode* literal, ParseNode* key, ParseNode* val) { |
| MOZ_ASSERT(literal->isKind(PNK_OBJECT)); |
| MOZ_ASSERT(literal->isArity(PN_LIST)); |
| MOZ_ASSERT(key->isKind(PNK_NUMBER) || |
| key->isKind(PNK_OBJECT_PROPERTY_NAME) || |
| key->isKind(PNK_STRING) || |
| key->isKind(PNK_COMPUTED_NAME)); |
| |
| ParseNode* propdef = newBinary(PNK_COLON, key, val, JSOP_INITPROP); |
| if (!propdef) |
| return false; |
| literal->append(propdef); |
| return true; |
| } |
| |
| bool addShorthand(ParseNode* literal, ParseNode* name, ParseNode* expr) { |
| MOZ_ASSERT(literal->isKind(PNK_OBJECT)); |
| MOZ_ASSERT(literal->isArity(PN_LIST)); |
| MOZ_ASSERT(name->isKind(PNK_OBJECT_PROPERTY_NAME)); |
| MOZ_ASSERT(expr->isKind(PNK_NAME)); |
| MOZ_ASSERT(name->pn_atom == expr->pn_atom); |
| |
| setListFlag(literal, PNX_NONCONST); |
| ParseNode* propdef = newBinary(PNK_SHORTHAND, name, expr, JSOP_INITPROP); |
| if (!propdef) |
| return false; |
| literal->append(propdef); |
| return true; |
| } |
| |
| bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, JSOp op) |
| { |
| MOZ_ASSERT(literal->isArity(PN_LIST)); |
| MOZ_ASSERT(key->isKind(PNK_NUMBER) || |
| key->isKind(PNK_OBJECT_PROPERTY_NAME) || |
| key->isKind(PNK_STRING) || |
| key->isKind(PNK_COMPUTED_NAME)); |
| literal->pn_xflags |= PNX_NONCONST; |
| |
| ParseNode* propdef = newBinary(PNK_COLON, key, fn, op); |
| if (!propdef) |
| return false; |
| literal->append(propdef); |
| return true; |
| } |
| |
| bool addClassMethodDefinition(ParseNode* methodList, ParseNode* key, ParseNode* fn, JSOp op, |
| bool isStatic) |
| { |
| MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST)); |
| MOZ_ASSERT(key->isKind(PNK_NUMBER) || |
| key->isKind(PNK_OBJECT_PROPERTY_NAME) || |
| key->isKind(PNK_STRING) || |
| key->isKind(PNK_COMPUTED_NAME)); |
| |
| ParseNode* classMethod = new_<ClassMethod>(key, fn, op, isStatic); |
| if (!classMethod) |
| return false; |
| methodList->append(classMethod); |
| return true; |
| } |
| |
| ParseNode* newYieldExpression(uint32_t begin, ParseNode* value, ParseNode* gen, |
| JSOp op = JSOP_YIELD) { |
| TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); |
| return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen); |
| } |
| |
| ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { |
| TokenPos pos(begin, value->pn_pos.end); |
| return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen); |
| } |
| |
| // Statements |
| |
| ParseNode* newStatementList(unsigned blockid, const TokenPos& pos) { |
| ParseNode* pn = new_<ListNode>(PNK_STATEMENTLIST, pos); |
| if (pn) |
| pn->pn_blockid = blockid; |
| return pn; |
| } |
| |
| template <typename PC> |
| void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) { |
| MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); |
| |
| if (stmt->isKind(PNK_FUNCTION)) { |
| if (pc->atBodyLevel()) { |
| // PNX_FUNCDEFS notifies the emitter that the block contains |
| // body-level function definitions that should be processed |
| // before the rest of nodes. |
| list->pn_xflags |= PNX_FUNCDEFS; |
| } else { |
| // General deoptimization was done in Parser::functionDef. |
| MOZ_ASSERT_IF(pc->sc->isFunctionBox(), |
| pc->sc->asFunctionBox()->hasExtensibleScope()); |
| } |
| } |
| |
| list->append(stmt); |
| } |
| |
| bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) { |
| MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); |
| |
| TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1); |
| ParseNode* makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos); |
| if (!makeGen) |
| return false; |
| |
| MOZ_ASSERT(genName->getOp() == JSOP_GETNAME); |
| genName->setOp(JSOP_SETNAME); |
| genName->markAsAssigned(); |
| ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen); |
| if (!genInit) |
| return false; |
| |
| ParseNode* initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit, |
| JSOP_INITIALYIELD); |
| if (!initialYield) |
| return false; |
| |
| stmtList->prepend(initialYield); |
| return true; |
| } |
| |
| ParseNode* newSetThis(ParseNode* thisName, ParseNode* val) { |
| MOZ_ASSERT(thisName->getOp() == JSOP_GETNAME); |
| thisName->setOp(JSOP_SETNAME); |
| thisName->markAsAssigned(); |
| return newBinary(PNK_SETTHIS, thisName, val); |
| } |
| |
| ParseNode* newEmptyStatement(const TokenPos& pos) { |
| return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode*) nullptr); |
| } |
| |
| ParseNode* newImportDeclaration(ParseNode* importSpecSet, |
| ParseNode* moduleSpec, const TokenPos& pos) |
| { |
| ParseNode* pn = new_<BinaryNode>(PNK_IMPORT, JSOP_NOP, pos, |
| importSpecSet, moduleSpec); |
| if (!pn) |
| return null(); |
| return pn; |
| } |
| |
| ParseNode* newExportDeclaration(ParseNode* kid, const TokenPos& pos) { |
| return new_<UnaryNode>(PNK_EXPORT, JSOP_NOP, pos, kid); |
| } |
| |
| ParseNode* newExportFromDeclaration(uint32_t begin, ParseNode* exportSpecSet, |
| ParseNode* moduleSpec) |
| { |
| ParseNode* pn = new_<BinaryNode>(PNK_EXPORT_FROM, JSOP_NOP, exportSpecSet, moduleSpec); |
| if (!pn) |
| return null(); |
| pn->pn_pos.begin = begin; |
| return pn; |
| } |
| |
| ParseNode* newExportDefaultDeclaration(ParseNode* kid, ParseNode* maybeBinding, |
| const TokenPos& pos) { |
| return new_<BinaryNode>(PNK_EXPORT_DEFAULT, JSOP_NOP, pos, kid, maybeBinding); |
| } |
| |
| ParseNode* newExprStatement(ParseNode* expr, uint32_t end) { |
| MOZ_ASSERT(expr->pn_pos.end <= end); |
| return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr); |
| } |
| |
| ParseNode* newIfStatement(uint32_t begin, ParseNode* cond, ParseNode* thenBranch, |
| ParseNode* elseBranch) |
| { |
| ParseNode* pn = new_<TernaryNode>(PNK_IF, JSOP_NOP, cond, thenBranch, elseBranch); |
| if (!pn) |
| return null(); |
| pn->pn_pos.begin = begin; |
| return pn; |
| } |
| |
| ParseNode* newDoWhileStatement(ParseNode* body, ParseNode* cond, const TokenPos& pos) { |
| return new_<BinaryNode>(PNK_DOWHILE, JSOP_NOP, pos, body, cond); |
| } |
| |
| ParseNode* newWhileStatement(uint32_t begin, ParseNode* cond, ParseNode* body) { |
| TokenPos pos(begin, body->pn_pos.end); |
| return new_<BinaryNode>(PNK_WHILE, JSOP_NOP, pos, cond, body); |
| } |
| |
| ParseNode* newForStatement(uint32_t begin, ParseNode* forHead, ParseNode* body, |
| unsigned iflags) |
| { |
| /* A FOR node is binary, left is loop control and right is the body. */ |
| JSOp op = forHead->isKind(PNK_FORIN) ? JSOP_ITER : JSOP_NOP; |
| BinaryNode* pn = new_<BinaryNode>(PNK_FOR, op, TokenPos(begin, body->pn_pos.end), |
| forHead, body); |
| if (!pn) |
| return null(); |
| pn->pn_iflags = iflags; |
| return pn; |
| } |
| |
| ParseNode* newComprehensionFor(uint32_t begin, ParseNode* forHead, ParseNode* body) { |
| // A PNK_COMPREHENSIONFOR node is binary: left is loop control, right |
| // is the body. |
| MOZ_ASSERT(forHead->isKind(PNK_FORIN) || forHead->isKind(PNK_FOROF)); |
| JSOp op = forHead->isKind(PNK_FORIN) ? JSOP_ITER : JSOP_NOP; |
| BinaryNode* pn = new_<BinaryNode>(PNK_COMPREHENSIONFOR, op, |
| TokenPos(begin, body->pn_pos.end), forHead, body); |
| if (!pn) |
| return null(); |
| pn->pn_iflags = JSOP_ITER; |
| return pn; |
| } |
| |
| ParseNode* newForHead(ParseNodeKind kind, ParseNode* pn1, ParseNode* pn2, ParseNode* pn3, |
| const TokenPos& pos) |
| { |
| MOZ_ASSERT(kind == PNK_FORIN || kind == PNK_FOROF || kind == PNK_FORHEAD); |
| return new_<TernaryNode>(kind, JSOP_NOP, pn1, pn2, pn3, pos); |
| } |
| |
| ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) { |
| TokenPos pos(begin, caseList->pn_pos.end); |
| return new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList); |
| } |
| |
| ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) { |
| return new_<CaseClause>(expr, body, begin); |
| } |
| |
| ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) { |
| return new_<ContinueStatement>(label, pos); |
| } |
| |
| ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) { |
| return new_<BreakStatement>(label, pos); |
| } |
| |
| ParseNode* newReturnStatement(ParseNode* expr, const TokenPos& pos) { |
| MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos)); |
| return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr); |
| } |
| |
| ParseNode* newWithStatement(uint32_t begin, ParseNode* expr, ParseNode* body, |
| ObjectBox* staticWith) { |
| return new_<BinaryObjNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end), |
| expr, body, staticWith); |
| } |
| |
| ParseNode* newLabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin) { |
| return new_<LabeledStatement>(label, stmt, begin); |
| } |
| |
| ParseNode* newThrowStatement(ParseNode* expr, const TokenPos& pos) { |
| MOZ_ASSERT(pos.encloses(expr->pn_pos)); |
| return new_<UnaryNode>(PNK_THROW, JSOP_THROW, pos, expr); |
| } |
| |
| ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchList, |
| ParseNode* finallyBlock) { |
| TokenPos pos(begin, (finallyBlock ? finallyBlock : catchList)->pn_pos.end); |
| return new_<TernaryNode>(PNK_TRY, JSOP_NOP, body, catchList, finallyBlock, pos); |
| } |
| |
| ParseNode* newDebuggerStatement(const TokenPos& pos) { |
| return new_<DebuggerStatement>(pos); |
| } |
| |
| ParseNode* newPropertyAccess(ParseNode* pn, PropertyName* name, uint32_t end) { |
| return new_<PropertyAccess>(pn, name, pn->pn_pos.begin, end); |
| } |
| |
| ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) { |
| return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end); |
| } |
| |
| inline bool addCatchBlock(ParseNode* catchList, ParseNode* letBlock, |
| ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody); |
| |
| inline bool setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* pn); |
| inline void setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* pn); |
| |
| ParseNode* newFunctionDefinition() { |
| return new_<CodeNode>(PNK_FUNCTION, pos()); |
| } |
| void setFunctionBody(ParseNode* pn, ParseNode* kid) { |
| pn->pn_body = kid; |
| } |
| void setFunctionBox(ParseNode* pn, FunctionBox* funbox) { |
| MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); |
| pn->pn_funbox = funbox; |
| } |
| void addFunctionArgument(ParseNode* pn, ParseNode* argpn) { |
| pn->pn_body->append(argpn); |
| } |
| void setDerivedClassConstructor(ParseNode* pn) { |
| MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); |
| pn->pn_funbox->setDerivedClassConstructor(); |
| } |
| |
| ParseNode* newModule() { |
| return new_<CodeNode>(PNK_MODULE, pos()); |
| } |
| void setModuleBox(ParseNode* pn, ModuleBox* modulebox) { |
| MOZ_ASSERT(pn->isKind(PNK_MODULE)); |
| pn->pn_modulebox = modulebox; |
| } |
| |
| ParseNode* newLexicalScope(ObjectBox* blockBox) { |
| return new_<LexicalScopeNode>(blockBox, pos()); |
| } |
| void setLexicalScopeBody(ParseNode* block, ParseNode* body) { |
| block->pn_expr = body; |
| } |
| |
| ParseNode* newLetBlock(ParseNode* vars, ParseNode* block, const TokenPos& pos) { |
| ParseNode* letBlock = newBinary(PNK_LETBLOCK, vars, block); |
| if (!letBlock) |
| return nullptr; |
| letBlock->pn_pos = pos; |
| return letBlock; |
| } |
| |
| ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs, |
| ParseContext<FullParseHandler>* pc, JSOp op) |
| { |
| return newBinary(kind, lhs, rhs, op); |
| } |
| |
| bool isUnparenthesizedYieldExpression(ParseNode* node) { |
| return node->isKind(PNK_YIELD) && !node->isInParens(); |
| } |
| |
| bool isUnparenthesizedCommaExpression(ParseNode* node) { |
| return node->isKind(PNK_COMMA) && !node->isInParens(); |
| } |
| |
| bool isUnparenthesizedAssignment(Node node) { |
| if (node->isKind(PNK_ASSIGN) && !node->isInParens()) { |
| // PNK_ASSIGN is also (mis)used for things like |var name = expr;|. |
| // But this method is only called on actual expressions, so we can |
| // just assert the node's op is the one used for plain assignment. |
| MOZ_ASSERT(node->isOp(JSOP_NOP)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool isReturnStatement(ParseNode* node) { |
| return node->isKind(PNK_RETURN); |
| } |
| |
| bool isStatementPermittedAfterReturnStatement(ParseNode *node) { |
| ParseNodeKind kind = node->getKind(); |
| return kind == PNK_FUNCTION || kind == PNK_VAR || kind == PNK_BREAK || kind == PNK_THROW || |
| (kind == PNK_SEMI && !node->pn_kid); |
| } |
| |
| bool isSuperBase(ParseNode* node) { |
| return node->isKind(PNK_SUPERBASE); |
| } |
| |
| inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op); |
| inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op); |
| |
| void setBeginPosition(ParseNode* pn, ParseNode* oth) { |
| setBeginPosition(pn, oth->pn_pos.begin); |
| } |
| void setBeginPosition(ParseNode* pn, uint32_t begin) { |
| pn->pn_pos.begin = begin; |
| MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); |
| } |
| |
| void setEndPosition(ParseNode* pn, ParseNode* oth) { |
| setEndPosition(pn, oth->pn_pos.end); |
| } |
| void setEndPosition(ParseNode* pn, uint32_t end) { |
| pn->pn_pos.end = end; |
| MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); |
| } |
| |
| void setPosition(ParseNode* pn, const TokenPos& pos) { |
| pn->pn_pos = pos; |
| } |
| TokenPos getPosition(ParseNode* pn) { |
| return pn->pn_pos; |
| } |
| |
| ParseNode* newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { |
| MOZ_ASSERT(kind != PNK_VAR); |
| return new_<ListNode>(kind, op, pos()); |
| } |
| ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { |
| return new_<ListNode>(kind, op, TokenPos(begin, begin + 1)); |
| } |
| ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { |
| MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); |
| return new_<ListNode>(kind, op, pos()); |
| } |
| |
| /* New list with one initial child node. kid must be non-null. */ |
| ParseNode* newList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { |
| MOZ_ASSERT(kind != PNK_VAR); |
| return new_<ListNode>(kind, op, kid); |
| } |
| ParseNode* newDeclarationList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { |
| MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET); |
| return new_<ListNode>(kind, op, kid); |
| } |
| |
| ParseNode* newCatchList() { |
| return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos()); |
| } |
| |
| ParseNode* newCommaExpressionList(ParseNode* kid) { |
| return newList(PNK_COMMA, kid, JSOP_NOP); |
| } |
| |
| void addList(ParseNode* list, ParseNode* kid) { |
| list->append(kid); |
| } |
| |
| void setOp(ParseNode* pn, JSOp op) { |
| pn->setOp(op); |
| } |
| void setBlockId(ParseNode* pn, unsigned blockid) { |
| pn->pn_blockid = blockid; |
| } |
| void setFlag(ParseNode* pn, unsigned flag) { |
| pn->pn_dflags |= flag; |
| } |
| void setListFlag(ParseNode* pn, unsigned flag) { |
| MOZ_ASSERT(pn->isArity(PN_LIST)); |
| pn->pn_xflags |= flag; |
| } |
| MOZ_WARN_UNUSED_RESULT ParseNode* parenthesize(ParseNode* pn) { |
| pn->setInParens(true); |
| return pn; |
| } |
| MOZ_WARN_UNUSED_RESULT ParseNode* setLikelyIIFE(ParseNode* pn) { |
| return parenthesize(pn); |
| } |
| void setPrologue(ParseNode* pn) { |
| pn->pn_prologue = true; |
| } |
| |
| bool isConstant(ParseNode* pn) { |
| return pn->isConstant(); |
| } |
| |
| PropertyName* maybeUnparenthesizedName(ParseNode* pn) { |
| if (!pn->isInParens() && pn->isKind(PNK_NAME)) |
| return pn->pn_atom->asPropertyName(); |
| return nullptr; |
| } |
| |
| PropertyName* maybeParenthesizedName(ParseNode* pn) { |
| if (pn->isInParens() && pn->isKind(PNK_NAME)) |
| return pn->pn_atom->asPropertyName(); |
| return nullptr; |
| } |
| |
| PropertyName* maybeNameAnyParentheses(ParseNode* node) { |
| if (PropertyName* name = maybeUnparenthesizedName(node)) |
| return name; |
| return maybeParenthesizedName(node); |
| } |
| |
| bool isCall(ParseNode* pn) { |
| return pn->isKind(PNK_CALL); |
| } |
| PropertyName* maybeDottedProperty(ParseNode* pn) { |
| return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr; |
| } |
| JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) { |
| if (JSAtom* atom = pn->isStringExprStatement()) { |
| *pos = pn->pn_kid->pn_pos; |
| return atom; |
| } |
| return nullptr; |
| } |
| |
| void markAsAssigned(ParseNode* node) { node->markAsAssigned(); } |
| void adjustGetToSet(ParseNode* node) { |
| node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); |
| } |
| void maybeDespecializeSet(ParseNode* node) { |
| if (!(CodeSpec[node->getOp()].format & JOF_SET)) |
| node->setOp(JSOP_SETNAME); |
| } |
| |
| inline ParseNode* makeAssignment(ParseNode* pn, ParseNode* rhs); |
| |
| static Definition* getDefinitionNode(Definition* dn) { |
| return dn; |
| } |
| static Definition::Kind getDefinitionKind(Definition* dn) { |
| return dn->kind(); |
| } |
| static bool isPlaceholderDefinition(Definition* dn) { |
| return dn->isPlaceholder(); |
| } |
| void linkUseToDef(ParseNode* pn, Definition* dn) |
| { |
| MOZ_ASSERT(!pn->isUsed()); |
| MOZ_ASSERT(!pn->isDefn()); |
| MOZ_ASSERT(pn != dn->dn_uses); |
| MOZ_ASSERT(dn->isDefn()); |
| pn->pn_link = dn->dn_uses; |
| dn->dn_uses = pn; |
| dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS; |
| pn->setUsed(true); |
| pn->pn_lexdef = dn; |
| } |
| Definition* resolve(Definition* dn) { |
| return dn->resolve(); |
| } |
| void deoptimizeUsesWithin(Definition* dn, const TokenPos& pos) |
| { |
| for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { |
| MOZ_ASSERT(pnu->isUsed()); |
| MOZ_ASSERT(!pnu->isDefn()); |
| if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) |
| pnu->pn_dflags |= PND_DEOPTIMIZED; |
| } |
| } |
| bool dependencyCovered(ParseNode* pn, unsigned blockid, bool functionScope) { |
| return pn->pn_blockid >= blockid; |
| } |
| void markMaybeUninitializedLexicalUseInSwitch(ParseNode* pn, Definition* dn, |
| uint16_t firstDominatingLexicalSlot) |
| { |
| MOZ_ASSERT(pn->isUsed()); |
| if (dn->isLexical() && dn->pn_scopecoord.slot() < firstDominatingLexicalSlot) |
| pn->pn_dflags |= PND_LEXICAL; |
| } |
| |
| static uintptr_t definitionToBits(Definition* dn) { |
| return uintptr_t(dn); |
| } |
| static Definition* definitionFromBits(uintptr_t bits) { |
| return (Definition*) bits; |
| } |
| static Definition* nullDefinition() { |
| return nullptr; |
| } |
| void disableSyntaxParser() { |
| syntaxParser = nullptr; |
| } |
| |
| LazyScript* lazyOuterFunction() { |
| return lazyOuterFunction_; |
| } |
| JSFunction* nextLazyInnerFunction() { |
| MOZ_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions()); |
| return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++]; |
| } |
| }; |
| |
| inline bool |
| FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* letBlock, |
| ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody) |
| { |
| ParseNode* catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody); |
| if (!catchpn) |
| return false; |
| |
| catchList->append(letBlock); |
| letBlock->pn_expr = catchpn; |
| return true; |
| } |
| |
| inline ParseNode* |
| FullParseHandler::makeAssignmentFromArg(ParseNode* arg, ParseNode* lhs, ParseNode* rhs) |
| { |
| return newBinary(PNK_ASSIGN, lhs, rhs, JSOP_NOP); |
| } |
| |
| inline void |
| FullParseHandler::replaceLastFunctionArgument(ParseNode* funcpn, ParseNode* pn) |
| { |
| funcpn->pn_body->pn_pos.end = pn->pn_pos.end; |
| ParseNode* pnchild = funcpn->pn_body->pn_head; |
| ParseNode* pnlast = funcpn->pn_body->last(); |
| MOZ_ASSERT(pnchild); |
| if (pnchild == pnlast) { |
| funcpn->pn_body->pn_head = pn; |
| } else { |
| while (pnchild->pn_next != pnlast) { |
| MOZ_ASSERT(pnchild->pn_next); |
| pnchild = pnchild->pn_next; |
| } |
| pnchild->pn_next = pn; |
| } |
| funcpn->pn_body->pn_tail = &pn->pn_next; |
| } |
| |
| inline bool |
| FullParseHandler::setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* defaultValue) |
| { |
| ParseNode* arg = funcpn->pn_body->last(); |
| MOZ_ASSERT(arg->isKind(PNK_NAME)); |
| ParseNode* lhs = arg->pn_expr ? arg->pn_expr : arg; |
| ParseNode* pn = makeAssignmentFromArg(arg, lhs, defaultValue); |
| if (!pn) |
| return false; |
| |
| if (arg->pn_expr) |
| arg->pn_expr = pn; |
| else |
| replaceLastFunctionArgument(funcpn, pn); |
| return true; |
| } |
| |
| inline void |
| FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* destruct) |
| { |
| ParseNode* arg = funcpn->pn_body->last(); |
| MOZ_ASSERT(arg->isKind(PNK_NAME)); |
| MOZ_ASSERT(!arg->isUsed()); |
| MOZ_ASSERT(arg->isDefn()); |
| arg->pn_expr = destruct; |
| } |
| |
| inline bool |
| FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op) |
| { |
| if (pn->isUsed()) { |
| pn = makeAssignment(pn, init); |
| if (!pn) |
| return false; |
| } else { |
| pn->pn_expr = init; |
| } |
| |
| if (pn->pn_dflags & PND_BOUND) |
| pn->setOp(JSOP_SETLOCAL); |
| else |
| pn->setOp(JSOP_SETNAME); |
| |
| pn->markAsAssigned(); |
| |
| /* The declarator's position must include the initializer. */ |
| pn->pn_pos.end = init->pn_pos.end; |
| return true; |
| } |
| |
| inline void |
| FullParseHandler::setLexicalDeclarationOp(ParseNode* pn, JSOp op) |
| { |
| if (op == JSOP_DEFLET || op == JSOP_DEFCONST) { |
| // Subtlety here. Lexical definitions that are PND_BOUND but whose |
| // scope coordinates are free are global lexicals. They cannot use |
| // scope coordinate lookup because we rely on being able to clone |
| // scripts to run on multiple globals. However, they always go on the |
| // global lexical scope, so in that sense they are bound. |
| pn->setOp(pn->pn_scopecoord.isFree() ? JSOP_INITGLEXICAL : JSOP_INITLEXICAL); |
| } |
| } |
| |
| inline ParseNode* |
| FullParseHandler::makeAssignment(ParseNode* pn, ParseNode* rhs) |
| { |
| ParseNode* lhs = cloneNode(*pn); |
| if (!lhs) |
| return nullptr; |
| |
| if (pn->isUsed()) { |
| Definition* dn = pn->pn_lexdef; |
| ParseNode** pnup = &dn->dn_uses; |
| |
| while (*pnup != pn) |
| pnup = &(*pnup)->pn_link; |
| *pnup = lhs; |
| lhs->pn_link = pn->pn_link; |
| pn->pn_link = nullptr; |
| } |
| |
| pn->setKind(PNK_ASSIGN); |
| pn->setOp(JSOP_NOP); |
| pn->setArity(PN_BINARY); |
| pn->setInParens(false); |
| pn->setUsed(false); |
| pn->setDefn(false); |
| pn->pn_left = lhs; |
| pn->pn_right = rhs; |
| pn->pn_pos.end = rhs->pn_pos.end; |
| return lhs; |
| } |
| |
| } // namespace frontend |
| } // namespace js |
| |
| #endif /* frontend_FullParseHandler_h */ |