| /* -*- 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/PodOperations.h" |
| |
| #include "ParseNode.h" |
| #include "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; |
| bool foldConstants; |
| |
| ParseNode *allocParseNode(size_t size) { |
| JS_ASSERT(size == sizeof(ParseNode)); |
| return static_cast<ParseNode *>(allocator.allocNode()); |
| } |
| |
| ParseNode *cloneNode(const ParseNode &other) { |
| ParseNode *node = allocParseNode(sizeof(ParseNode)); |
| if (!node) |
| return NULL; |
| 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; |
| } |
| |
| public: |
| |
| /* |
| * If non-NULL, 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; |
| |
| FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants, |
| Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction) |
| : allocator(cx), |
| tokenStream(tokenStream), |
| foldConstants(foldConstants), |
| lazyOuterFunction_(lazyOuterFunction), |
| lazyInnerFunctionIndex(0), |
| syntaxParser(syntaxParser) |
| {} |
| |
| static ParseNode *null() { return NULL; } |
| |
| ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); } |
| void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); } |
| const Token ¤tToken() { return tokenStream.currentToken(); } |
| |
| ParseNode *newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid, |
| const TokenPos &pos) |
| { |
| return new_<NameNode>(PNK_NAME, JSOP_NAME, name, inBlock, blockid, pos); |
| } |
| |
| Definition *newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid, |
| const TokenPos &pos) |
| { |
| Definition *dn = |
| (Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, inBlock, blockid, pos); |
| if (!dn) |
| return NULL; |
| dn->setDefn(true); |
| dn->pn_dflags |= PND_PLACEHOLDER; |
| return dn; |
| } |
| |
| ParseNode *newIdentifier(JSAtom *atom, const TokenPos &pos) { |
| return new_<NullaryNode>(PNK_NAME, JSOP_NOP, pos, atom); |
| } |
| |
| ParseNode *newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { |
| ParseNode *pn = new_<NullaryNode>(PNK_NUMBER, pos); |
| if (!pn) |
| return NULL; |
| 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_STRING, pos, atom); |
| } |
| |
| ParseNode *newThisLiteral(const TokenPos &pos) { |
| return new_<ThisLiteral>(pos); |
| } |
| |
| 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(HandleObject 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 *newElision() { |
| return new_<NullaryNode>(PNK_ELISION, pos()); |
| } |
| |
| void markAsSetCall(ParseNode *pn) { |
| pn->pn_xflags |= PNX_SETCALL; |
| } |
| |
| ParseNode *newDelete(uint32_t begin, ParseNode *expr) { |
| if (expr->getKind() == PNK_NAME) { |
| expr->pn_dflags |= PND_DEOPTIMIZED; |
| expr->setOp(JSOP_DELNAME); |
| } |
| return newUnary(PNK_DELETE, JSOP_NOP, begin, expr); |
| } |
| |
| 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 *) NULL, (ParseNode *) NULL); |
| } |
| ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, |
| JSOp op = JSOP_NOP) { |
| return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) NULL); |
| } |
| 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 *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right, |
| ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP) |
| { |
| return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants); |
| } |
| |
| ParseNode *newTernary(ParseNodeKind kind, |
| ParseNode *first, ParseNode *second, ParseNode *third, |
| JSOp op = JSOP_NOP) |
| { |
| return new_<TernaryNode>(kind, op, first, second, third); |
| } |
| |
| 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) { |
| JS_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. |
| JS_ASSERT_IF(pc->sc->isFunctionBox(), |
| pc->sc->asFunctionBox()->hasExtensibleScope()); |
| } |
| } |
| |
| list->append(stmt); |
| } |
| |
| ParseNode *newEmptyStatement(const TokenPos &pos) { |
| return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode *) NULL); |
| } |
| |
| ParseNode *newExprStatement(ParseNode *expr, uint32_t end) { |
| JS_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 *newForHead(bool isForInOrOf, ParseNode *pn1, ParseNode *pn2, ParseNode *pn3, |
| const TokenPos &pos) |
| { |
| ParseNodeKind kind = isForInOrOf ? PNK_FORIN : 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) { |
| TokenPos pos(begin, body->pn_pos.end); |
| return new_<BinaryNode>(expr ? PNK_CASE : PNK_DEFAULT, JSOP_NOP, pos, expr, body); |
| } |
| |
| 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) { |
| JS_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) { |
| JS_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); |
| } |
| |
| private: |
| /* |
| * Examine the RHS of a MemberExpression obj[pn], to optimize expressions |
| * like obj["name"] or obj["0"]. |
| * |
| * If pn is a number or string constant that's not an index, store the |
| * corresponding PropertyName in *namep. Otherwise store NULL in *namep. |
| * |
| * Also, if pn is a string constant whose value is the ToString of an |
| * index, morph it to a number. |
| * |
| * Return false on OOM. |
| */ |
| bool foldConstantIndex(ParseNode *pn, PropertyName **namep) { |
| if (pn->isKind(PNK_STRING)) { |
| JSAtom *atom = pn->pn_atom; |
| uint32_t index; |
| |
| if (atom->isIndex(&index)) { |
| pn->setKind(PNK_NUMBER); |
| pn->setOp(JSOP_DOUBLE); |
| pn->pn_dval = index; |
| } else { |
| *namep = atom->asPropertyName(); |
| return true; |
| } |
| } else if (pn->isKind(PNK_NUMBER)) { |
| double number = pn->pn_dval; |
| if (number != ToUint32(number)) { |
| JSContext *cx = tokenStream.getContext(); |
| JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number)); |
| if (!atom) |
| return false; |
| *namep = atom->asPropertyName(); |
| return true; |
| } |
| } |
| |
| *namep = NULL; |
| return true; |
| } |
| |
| public: |
| ParseNode *newPropertyByValue(ParseNode *lhs, ParseNode *index, uint32_t end) { |
| if (foldConstants) { |
| PropertyName *name; |
| if (!foldConstantIndex(index, &name)) |
| return null(); |
| if (name) |
| return newPropertyAccess(lhs, name, 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 void setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr); |
| |
| inline void setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *pn); |
| inline ParseNode *newFunctionDefinition(); |
| void setFunctionBody(ParseNode *pn, ParseNode *kid) { |
| pn->pn_body = kid; |
| } |
| void setFunctionBox(ParseNode *pn, FunctionBox *funbox) { |
| pn->pn_funbox = funbox; |
| } |
| void addFunctionArgument(ParseNode *pn, ParseNode *argpn) { |
| pn->pn_body->append(argpn); |
| } |
| inline ParseNode *newLexicalScope(ObjectBox *blockbox); |
| bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) { |
| return pn->isKind(kind) && !pn->isInParens(); |
| } |
| |
| inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, 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; |
| JS_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; |
| JS_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, ParseNode *kid = NULL, JSOp op = JSOP_NOP) { |
| ParseNode *pn = ListNode::create(kind, this); |
| if (!pn) |
| return NULL; |
| pn->setOp(op); |
| pn->makeEmpty(); |
| if (kid) { |
| pn->pn_pos.begin = kid->pn_pos.begin; |
| pn->append(kid); |
| } |
| return pn; |
| } |
| void addList(ParseNode *pn, ParseNode *kid) { |
| pn->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) { |
| JS_ASSERT(pn->isArity(PN_LIST)); |
| pn->pn_xflags |= flag; |
| } |
| ParseNode *setInParens(ParseNode *pn) { |
| pn->setInParens(true); |
| return pn; |
| } |
| void setPrologue(ParseNode *pn) { |
| pn->pn_prologue = true; |
| } |
| |
| bool isConstant(ParseNode *pn) { |
| return pn->isConstant(); |
| } |
| PropertyName *isName(ParseNode *pn) { |
| return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : NULL; |
| } |
| bool isCall(ParseNode *pn) { |
| return pn->isKind(PNK_CALL); |
| } |
| PropertyName *isGetProp(ParseNode *pn) { |
| return pn->isOp(JSOP_GETPROP) ? pn->pn_atom->asPropertyName() : NULL; |
| } |
| JSAtom *isStringExprStatement(ParseNode *pn, TokenPos *pos) { |
| if (JSAtom *atom = pn->isStringExprStatement()) { |
| *pos = pn->pn_kid->pn_pos; |
| return atom; |
| } |
| return NULL; |
| } |
| |
| inline ParseNode *makeAssignment(ParseNode *pn, ParseNode *rhs); |
| |
| static Definition *getDefinitionNode(Definition *dn) { |
| return dn; |
| } |
| static Definition::Kind getDefinitionKind(Definition *dn) { |
| return dn->kind(); |
| } |
| void linkUseToDef(ParseNode *pn, Definition *dn) |
| { |
| JS_ASSERT(!pn->isUsed()); |
| JS_ASSERT(!pn->isDefn()); |
| JS_ASSERT(pn != dn->dn_uses); |
| JS_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) { |
| JS_ASSERT(pnu->isUsed()); |
| JS_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; |
| } |
| |
| static uintptr_t definitionToBits(Definition *dn) { |
| return uintptr_t(dn); |
| } |
| static Definition *definitionFromBits(uintptr_t bits) { |
| return (Definition *) bits; |
| } |
| static Definition *nullDefinition() { |
| return NULL; |
| } |
| void disableSyntaxParser() { |
| syntaxParser = NULL; |
| } |
| |
| LazyScript *lazyOuterFunction() { |
| return lazyOuterFunction_; |
| } |
| JSFunction *nextLazyInnerFunction() { |
| JS_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 void |
| FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr) |
| { |
| JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK)); |
| if (leaveBlockExpr) |
| block->setOp(JSOP_LEAVEBLOCKEXPR); |
| block->pn_expr = kid; |
| } |
| |
| inline void |
| FullParseHandler::setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *defaultValue) |
| { |
| ParseNode *arg = funcpn->pn_body->last(); |
| arg->pn_dflags |= PND_DEFAULT; |
| arg->pn_expr = defaultValue; |
| } |
| |
| inline ParseNode * |
| FullParseHandler::newFunctionDefinition() |
| { |
| ParseNode *pn = CodeNode::create(PNK_FUNCTION, this); |
| if (!pn) |
| return NULL; |
| pn->pn_body = NULL; |
| pn->pn_funbox = NULL; |
| pn->pn_cookie.makeFree(); |
| pn->pn_dflags = 0; |
| return pn; |
| } |
| |
| inline ParseNode * |
| FullParseHandler::newLexicalScope(ObjectBox *blockbox) |
| { |
| ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, this); |
| if (!pn) |
| return NULL; |
| |
| pn->setOp(JSOP_LEAVEBLOCK); |
| pn->pn_objbox = blockbox; |
| pn->pn_cookie.makeFree(); |
| pn->pn_dflags = 0; |
| return pn; |
| } |
| |
| 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; |
| } |
| |
| pn->setOp((pn->pn_dflags & PND_BOUND) |
| ? JSOP_SETLOCAL |
| : (op == JSOP_DEFCONST) |
| ? JSOP_SETCONST |
| : JSOP_SETNAME); |
| |
| pn->markAsAssigned(); |
| |
| /* The declarator's position must include the initializer. */ |
| pn->pn_pos.end = init->pn_pos.end; |
| return true; |
| } |
| |
| inline ParseNode * |
| FullParseHandler::makeAssignment(ParseNode *pn, ParseNode *rhs) |
| { |
| ParseNode *lhs = cloneNode(*pn); |
| if (!lhs) |
| return NULL; |
| |
| 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 = NULL; |
| } |
| |
| 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; |
| } |
| |
| } // frontend |
| } // js |
| |
| #endif /* frontend_FullParseHandler_h */ |