/* * Used by Parser::forStatement and comprehensionTail to clone the TARGET in * for (var/const/let TARGET in EXPR) * * opn must be the pn_head of a node produced by Parser::variables, so its form * is known to be LHS = NAME | [LHS] | {id:LHS}. * * The cloned tree is for use only in the same statement and binding context as * the original tree. */ ParseNode * js::CloneLeftHandSide(ParseNode *opn, Parser *parser) { ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(), opn->pn_pos); if (!pn) return NULL; pn->setInParens(opn->isInParens()); pn->setDefn(opn->isDefn()); pn->setUsed(opn->isUsed()); #if JS_HAS_DESTRUCTURING if (opn->isArity(PN_LIST)) { JS_ASSERT(opn->isKind(PNK_RB) || opn->isKind(PNK_RC)); pn->makeEmpty(); for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { ParseNode *pn2; if (opn->isKind(PNK_RC)) { JS_ASSERT(opn2->isArity(PN_BINARY)); JS_ASSERT(opn2->isKind(PNK_COLON)); ParseNode *tag = CloneParseTree(opn2->pn_left, parser); if (!tag) return NULL; ParseNode *target = CloneLeftHandSide(opn2->pn_right, parser); if (!target) return NULL; pn2 = parser->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target); } else if (opn2->isArity(PN_NULLARY)) { JS_ASSERT(opn2->isKind(PNK_COMMA)); pn2 = CloneParseTree(opn2, parser); } else { pn2 = CloneLeftHandSide(opn2, parser); } if (!pn2) return NULL; pn->append(pn2); } pn->pn_xflags = opn->pn_xflags; return pn; } #endif JS_ASSERT(opn->isArity(PN_NAME)); JS_ASSERT(opn->isKind(PNK_NAME)); /* If opn is a definition or use, make pn a use. */ pn->pn_u.name = opn->pn_u.name; pn->setOp(JSOP_SETNAME); if (opn->isUsed()) { Definition *dn = pn->pn_lexdef; pn->pn_link = dn->dn_uses; dn->dn_uses = pn; } else { pn->pn_expr = NULL; if (opn->isDefn()) { /* We copied some definition-specific state into pn. Clear it out. */ pn->pn_cookie.makeFree(); pn->pn_dflags &= ~PND_BOUND; pn->setDefn(false); LinkUseToDef(pn, (Definition *) opn); } } return pn; }
/* * This function assumes the cloned tree is for use in the same statement and * binding context as the original tree. */ static ParseNode * CloneParseTree(ParseNode *opn, Parser *parser) { TreeContext *tc = parser->tc; JS_CHECK_RECURSION(tc->sc->context, return NULL); ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(), opn->pn_pos); if (!pn) return NULL; pn->setInParens(opn->isInParens()); pn->setDefn(opn->isDefn()); pn->setUsed(opn->isUsed()); switch (pn->getArity()) { #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO case PN_FUNC: NULLCHECK(pn->pn_funbox = parser->newFunctionBox(opn->pn_funbox->object, pn, tc, opn->pn_funbox->strictModeState)); NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, parser)); pn->pn_cookie = opn->pn_cookie; pn->pn_dflags = opn->pn_dflags; pn->pn_blockid = opn->pn_blockid; break; case PN_LIST: pn->makeEmpty(); for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { ParseNode *pn2; NULLCHECK(pn2 = CloneParseTree(opn2, parser)); pn->append(pn2); } pn->pn_xflags = opn->pn_xflags; break; case PN_TERNARY: NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, parser)); NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, parser)); NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, parser)); break; case PN_BINARY: NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, parser)); if (opn->pn_right != opn->pn_left) NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, parser)); else pn->pn_right = pn->pn_left; pn->pn_pval = opn->pn_pval; pn->pn_iflags = opn->pn_iflags; break; case PN_UNARY: NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, parser)); pn->pn_hidden = opn->pn_hidden; break; case PN_NAME: // PN_NAME could mean several arms in pn_u, so copy the whole thing. pn->pn_u = opn->pn_u; if (opn->isUsed()) { /* * The old name is a use of its pn_lexdef. Make the clone also be a * use of that definition. */ Definition *dn = pn->pn_lexdef; pn->pn_link = dn->dn_uses; dn->dn_uses = pn; } else if (opn->pn_expr) { NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, parser)); /* * If the old name is a definition, the new one has pn_defn set. * Make the old name a use of the new node. */ if (opn->isDefn()) { opn->setDefn(false); LinkUseToDef(opn, (Definition *) pn); } } break; case PN_NAMESET: pn->pn_names = opn->pn_names; NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, parser)); break; case PN_NULLARY: // Even PN_NULLARY may have data (xmlpi for E4X -- what a botch). pn->pn_u = opn->pn_u; break; #undef NULLCHECK } return pn; }