diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/frontend/FullParseHandler.h | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/frontend/FullParseHandler.h')
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h new file mode 100644 index 000000000..add881900 --- /dev/null +++ b/js/src/frontend/FullParseHandler.h @@ -0,0 +1,977 @@ +/* -*- 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. + */ + const Rooted<LazyScript*> lazyOuterFunction_; + size_t lazyInnerFunctionIndex; + size_t lazyClosedOverBindingIndex; + + const TokenPos& pos() { + return tokenStream.currentToken().pos; + } + + 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; + + 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_(cx, lazyOuterFunction), + lazyInnerFunctionIndex(0), + lazyClosedOverBindingIndex(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, const TokenPos& pos, ExclusiveContext* cx) + { + return new_<NameNode>(PNK_NAME, JSOP_GETNAME, name, 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); + } + + 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; + } + + void 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); + } + + 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); + } + + ParseNode* newDelete(uint32_t begin, ParseNode* expr) { + if (expr->isKind(PNK_NAME)) { + 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* newUpdate(ParseNodeKind kind, uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_<UnaryNode>(kind, JSOP_NOP, pos, kid); + } + + ParseNode* newSpread(uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_<UnaryNode>(PNK_SPREAD, JSOP_NOP, pos, kid); + } + + ParseNode* newArrayPush(uint32_t begin, ParseNode* kid) { + TokenPos pos(begin, kid->pn_pos.end); + return new_<UnaryNode>(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, 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* 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; + } + + MOZ_MUST_USE 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; + } + + MOZ_MUST_USE 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); + } + + MOZ_MUST_USE 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; + } + + MOZ_MUST_USE 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; + } + + MOZ_MUST_USE 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; + } + + MOZ_MUST_USE 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; + } + + MOZ_MUST_USE 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); + } + + ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { + TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); + return new_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen); + } + + // Statements + + ParseNode* newStatementList(const TokenPos& pos) { + return new_<ListNode>(PNK_STATEMENTLIST, pos); + } + + MOZ_MUST_USE bool isFunctionStmt(ParseNode* stmt) { + while (stmt->isKind(PNK_LABEL)) + stmt = stmt->as<LabeledStatement>().statement(); + return stmt->isKind(PNK_FUNCTION); + } + + void addStatementToList(ParseNode* list, ParseNode* stmt) { + MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); + + list->append(stmt); + + if (isFunctionStmt(stmt)) { + // 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; + } + } + + void addCaseStatementToList(ParseNode* list, ParseNode* casepn) { + MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); + MOZ_ASSERT(casepn->isKind(PNK_CASE)); + MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST)); + + list->append(casepn); + + if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) + list->pn_xflags |= PNX_FUNCDEFS; + } + + MOZ_MUST_USE 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); + 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); + 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* newComprehensionBinding(ParseNode* kid) { + MOZ_ASSERT(kid->isKind(PNK_NAME)); + return new_<ListNode>(PNK_LET, JSOP_NOP, kid); + } + + ParseNode* newForHead(ParseNode* init, ParseNode* test, ParseNode* update, + const TokenPos& pos) + { + return new_<TernaryNode>(PNK_FORHEAD, JSOP_NOP, init, test, update, pos); + } + + ParseNode* newForInOrOfHead(ParseNodeKind kind, ParseNode* target, ParseNode* iteratedExpr, + const TokenPos& pos) + { + MOZ_ASSERT(kind == PNK_FORIN || kind == PNK_FOROF); + return new_<TernaryNode>(kind, JSOP_NOP, target, nullptr, iteratedExpr, 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) { + return new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end), + expr, body); + } + + 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 MOZ_MUST_USE bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope, + ParseNode* catchName, ParseNode* catchGuard, + ParseNode* catchBody); + + inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn, + ParseNode* pn); + inline void setLastFunctionFormalParameterDestructuring(ParseNode* funcpn, ParseNode* pn); + + ParseNode* newFunctionDefinition() { + return new_<CodeNode>(PNK_FUNCTION, pos()); + } + bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) { + MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST)); + ParseNode* paramsBody = newList(PNK_PARAMSBODY, body); + if (!paramsBody) + return false; + setFunctionFormalParametersAndBody(pn, paramsBody); + return true; + } + void setFunctionFormalParametersAndBody(ParseNode* pn, ParseNode* kid) { + MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY)); + pn->pn_body = kid; + } + void setFunctionBox(ParseNode* pn, FunctionBox* funbox) { + MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); + pn->pn_funbox = funbox; + funbox->functionNode = pn; + } + void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) { + pn->pn_body->append(argpn); + } + void setFunctionBody(ParseNode* fn, ParseNode* body) { + MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY)); + fn->pn_body->append(body); + } + + ParseNode* newModule() { + return new_<CodeNode>(PNK_MODULE, pos()); + } + + ParseNode* newLexicalScope(LexicalScope::Data* bindings, ParseNode* body) { + return new_<LexicalScopeNode>(bindings, body); + } + + ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs, + 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 isUnparenthesizedUnaryExpression(ParseNode* node) { + if (!node->isInParens()) { + ParseNodeKind kind = node->getKind(); + return kind == PNK_VOID || kind == PNK_NOT || kind == PNK_BITNOT || kind == PNK_POS || + kind == PNK_NEG || IsTypeofKind(kind) || IsDeleteKind(kind); + } + 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 MOZ_MUST_USE bool finishInitializerAssignment(ParseNode* pn, ParseNode* init); + + 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; + } + + bool isDeclarationKind(ParseNodeKind kind) { + return kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST; + } + + ParseNode* newList(ParseNodeKind kind, JSOp op = JSOP_NOP) { + MOZ_ASSERT(!isDeclarationKind(kind)); + return new_<ListNode>(kind, op, pos()); + } + + ParseNode* newList(ParseNodeKind kind, uint32_t begin, JSOp op = JSOP_NOP) { + MOZ_ASSERT(!isDeclarationKind(kind)); + return new_<ListNode>(kind, op, TokenPos(begin, begin + 1)); + } + + ParseNode* newList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { + MOZ_ASSERT(!isDeclarationKind(kind)); + return new_<ListNode>(kind, op, kid); + } + + ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) { + MOZ_ASSERT(isDeclarationKind(kind)); + return new_<ListNode>(kind, op, pos()); + } + + ParseNode* newDeclarationList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) { + MOZ_ASSERT(isDeclarationKind(kind)); + return new_<ListNode>(kind, op, kid); + } + + bool isDeclarationList(ParseNode* node) { + return isDeclarationKind(node->getKind()); + } + + ParseNode* singleBindingFromDeclaration(ParseNode* decl) { + MOZ_ASSERT(isDeclarationList(decl)); + MOZ_ASSERT(decl->pn_count == 1); + return decl->pn_head; + } + + 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 setListFlag(ParseNode* pn, unsigned flag) { + MOZ_ASSERT(pn->isArity(PN_LIST)); + pn->pn_xflags |= flag; + } + MOZ_MUST_USE ParseNode* parenthesize(ParseNode* pn) { + pn->setInParens(true); + return pn; + } + MOZ_MUST_USE ParseNode* setLikelyIIFE(ParseNode* pn) { + return parenthesize(pn); + } + void setPrologue(ParseNode* pn) { + pn->pn_prologue = true; + } + + bool isConstant(ParseNode* pn) { + return pn->isConstant(); + } + + bool isUnparenthesizedName(ParseNode* node) { + return node->isKind(PNK_NAME) && !node->isInParens(); + } + + bool isNameAnyParentheses(ParseNode* node) { + return node->isKind(PNK_NAME); + } + + bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + return node->pn_atom == cx->names().eval; + } + + const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + if (nameIsEvalAnyParentheses(node, cx)) + return js_eval_str; + if (node->pn_atom == cx->names().arguments) + return js_arguments_str; + return nullptr; + } + + bool nameIsUnparenthesizedAsync(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + return node->pn_atom == cx->names().async; + } + + 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 adjustGetToSet(ParseNode* node) { + node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME); + } + + void disableSyntaxParser() { + syntaxParser = nullptr; + } + + bool canSkipLazyInnerFunctions() { + return !!lazyOuterFunction_; + } + bool canSkipLazyClosedOverBindings() { + return !!lazyOuterFunction_; + } + LazyScript* lazyOuterFunction() { + return lazyOuterFunction_; + } + JSFunction* nextLazyInnerFunction() { + MOZ_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions()); + return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++]; + } + JSAtom* nextLazyClosedOverBinding() { + MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction()->numClosedOverBindings()); + return lazyOuterFunction()->closedOverBindings()[lazyClosedOverBindingIndex++]; + } +}; + +inline bool +FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope, + ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody) +{ + ParseNode* catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody); + if (!catchpn) + return false; + catchList->append(lexicalScope); + lexicalScope->setScopeBody(catchpn); + return true; +} + +inline bool +FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue) +{ + ParseNode* arg = funcpn->pn_body->last(); + ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue, JSOP_NOP); + if (!pn) + return false; + + 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; + + return true; +} + +inline bool +FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init) +{ + pn->pn_expr = init; + pn->setOp(JSOP_SETNAME); + + /* The declarator's position must include the initializer. */ + pn->pn_pos.end = init->pn_pos.end; + return true; +} + +} // namespace frontend +} // namespace js + +#endif /* frontend_FullParseHandler_h */ |