1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998-2011
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "frontend/ParseNode.h"
42 :
43 : #include "jsscriptinlines.h"
44 :
45 : #include "frontend/ParseMaps-inl.h"
46 : #include "frontend/ParseNode-inl.h"
47 :
48 : using namespace js;
49 :
50 : /*
51 : * Asserts to verify assumptions behind pn_ macros.
52 : */
53 : #define pn_offsetof(m) offsetof(ParseNode, m)
54 :
55 : JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
56 :
57 : #undef pn_offsetof
58 :
59 : void
60 326 : ParseNode::become(ParseNode *pn2)
61 : {
62 326 : JS_ASSERT(!pn_defn);
63 326 : JS_ASSERT(!pn2->isDefn());
64 :
65 326 : JS_ASSERT(!pn_used);
66 326 : if (pn2->isUsed()) {
67 0 : ParseNode **pnup = &pn2->pn_lexdef->dn_uses;
68 0 : while (*pnup != pn2)
69 0 : pnup = &(*pnup)->pn_link;
70 0 : *pnup = this;
71 0 : pn_link = pn2->pn_link;
72 0 : pn_used = true;
73 0 : pn2->pn_link = NULL;
74 0 : pn2->pn_used = false;
75 : }
76 :
77 326 : pn_type = pn2->pn_type;
78 326 : pn_op = pn2->pn_op;
79 326 : pn_arity = pn2->pn_arity;
80 326 : pn_parens = pn2->pn_parens;
81 326 : pn_u = pn2->pn_u;
82 :
83 : /*
84 : * If any pointers are pointing to pn2, change them to point to this
85 : * instead, since pn2 will be cleared and probably recycled.
86 : */
87 326 : if (this->isKind(PNK_FUNCTION) && isArity(PN_FUNC)) {
88 : /* Function node: fix up the pn_funbox->node back-pointer. */
89 9 : JS_ASSERT(pn_funbox->node == pn2);
90 9 : pn_funbox->node = this;
91 317 : } else if (pn_arity == PN_LIST && !pn_head) {
92 : /* Empty list: fix up the pn_tail pointer. */
93 18 : JS_ASSERT(pn_count == 0);
94 18 : JS_ASSERT(pn_tail == &pn2->pn_head);
95 18 : pn_tail = &pn_head;
96 : }
97 :
98 326 : pn2->clear();
99 326 : }
100 :
101 : void
102 326 : ParseNode::clear()
103 : {
104 326 : pn_type = PNK_LIMIT;
105 326 : setOp(JSOP_NOP);
106 326 : pn_used = pn_defn = false;
107 326 : pn_arity = PN_NULLARY;
108 326 : pn_parens = false;
109 326 : }
110 :
111 : bool
112 136171 : FunctionBox::inAnyDynamicScope() const
113 : {
114 282643 : for (const FunctionBox *funbox = this; funbox; funbox = funbox->parent) {
115 147130 : if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_EXTENSIBLE_SCOPE))
116 658 : return true;
117 : }
118 135513 : return false;
119 : }
120 :
121 : bool
122 8403 : FunctionBox::scopeIsExtensible() const
123 : {
124 8403 : return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
125 : }
126 :
127 : /* Add |node| to |parser|'s free node list. */
128 : void
129 1905242 : ParseNodeAllocator::freeNode(ParseNode *pn)
130 : {
131 : /* Catch back-to-back dup recycles. */
132 1905242 : JS_ASSERT(pn != freelist);
133 :
134 : /*
135 : * It's too hard to clear these nodes from the AtomDefnMaps, etc. that
136 : * hold references to them, so we never free them. It's our caller's job to
137 : * recognize and process these, since their children do need to be dealt
138 : * with.
139 : */
140 1905242 : JS_ASSERT(!pn->isUsed());
141 1905242 : JS_ASSERT(!pn->isDefn());
142 :
143 1905242 : if (pn->isArity(PN_NAMESET) && pn->pn_names.hasMap())
144 9 : pn->pn_names.releaseMap(cx);
145 :
146 : #ifdef DEBUG
147 : /* Poison the node, to catch attempts to use it without initializing it. */
148 1905242 : memset(pn, 0xab, sizeof(*pn));
149 : #endif
150 :
151 1905242 : pn->pn_next = freelist;
152 1905242 : freelist = pn;
153 1905242 : }
154 :
155 : /*
156 : * A work pool of ParseNodes. The work pool is a stack, chained together
157 : * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
158 : * when recycling deep parse trees.
159 : *
160 : * Since parse nodes are probably allocated in something close to the order
161 : * they appear in a depth-first traversal of the tree, making the work pool
162 : * a stack should give us pretty good locality.
163 : */
164 : class NodeStack {
165 : public:
166 342702 : NodeStack() : top(NULL) { }
167 8010050 : bool empty() { return top == NULL; }
168 1507857 : void push(ParseNode *pn) {
169 1507857 : pn->pn_next = top;
170 1507857 : top = pn;
171 1507857 : }
172 1871995 : void pushUnlessNull(ParseNode *pn) { if (pn) push(pn); }
173 : /* Push the children of the PN_LIST node |pn| on the stack. */
174 565130 : void pushList(ParseNode *pn) {
175 : /* This clobbers pn->pn_head if the list is empty; should be okay. */
176 565130 : *pn->pn_tail = top;
177 565130 : top = pn->pn_head;
178 565130 : }
179 3833674 : ParseNode *pop() {
180 3833674 : JS_ASSERT(!empty());
181 3833674 : ParseNode *hold = top; /* my kingdom for a prog1 */
182 3833674 : top = top->pn_next;
183 3833674 : return hold;
184 : }
185 : private:
186 : ParseNode *top;
187 : };
188 :
189 : /*
190 : * Push the children of |pn| on |stack|. Return true if |pn| itself could be
191 : * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
192 : * nodes, and all function nodes; see comments for CleanFunctionList in
193 : * SemanticAnalysis.cpp). Some callers want to free |pn|; others
194 : * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and
195 : * just need to take care of its children.
196 : */
197 : static bool
198 4176376 : PushNodeChildren(ParseNode *pn, NodeStack *stack)
199 : {
200 4176376 : switch (pn->getArity()) {
201 : case PN_FUNC:
202 : /*
203 : * Function nodes are linked into the function box tree, and may appear
204 : * on method lists. Both of those lists are singly-linked, so trying to
205 : * update them now could result in quadratic behavior when recycling
206 : * trees containing many functions; and the lists can be very long. So
207 : * we put off cleaning the lists up until just before function
208 : * analysis, when we call CleanFunctionList.
209 : *
210 : * In fact, we can't recycle the parse node yet, either: it may appear
211 : * on a method list, and reusing the node would corrupt that. Instead,
212 : * we clear its pn_funbox pointer to mark it as deleted;
213 : * CleanFunctionList recycles it as well.
214 : *
215 : * We do recycle the nodes around it, though, so we must clear pointers
216 : * to them to avoid leaving dangling references where someone can find
217 : * them.
218 : */
219 146471 : pn->pn_funbox = NULL;
220 146471 : stack->pushUnlessNull(pn->pn_body);
221 146471 : pn->pn_body = NULL;
222 146471 : return false;
223 :
224 : case PN_NAME:
225 : /*
226 : * Because used/defn nodes appear in AtomDefnMaps and elsewhere, we
227 : * don't recycle them. (We'll recover their storage when we free the
228 : * temporary arena.) However, we do recycle the nodes around them, so
229 : * clean up the pointers to avoid dangling references. The top-level
230 : * decls table carries references to them that later iterations through
231 : * the compileScript loop may find, so they need to be neat.
232 : *
233 : * pn_expr and pn_lexdef share storage; the latter isn't an owning
234 : * reference.
235 : */
236 2224787 : if (!pn->isUsed()) {
237 416411 : stack->pushUnlessNull(pn->pn_expr);
238 416411 : pn->pn_expr = NULL;
239 : }
240 2224787 : return !pn->isUsed() && !pn->isDefn();
241 :
242 : case PN_LIST:
243 565130 : stack->pushList(pn);
244 565130 : break;
245 : case PN_TERNARY:
246 165544 : stack->pushUnlessNull(pn->pn_kid1);
247 165544 : stack->pushUnlessNull(pn->pn_kid2);
248 165544 : stack->pushUnlessNull(pn->pn_kid3);
249 165544 : break;
250 : case PN_BINARY:
251 253468 : if (pn->pn_left != pn->pn_right)
252 253441 : stack->pushUnlessNull(pn->pn_left);
253 253468 : stack->pushUnlessNull(pn->pn_right);
254 253468 : break;
255 : case PN_UNARY:
256 305572 : stack->pushUnlessNull(pn->pn_kid);
257 305572 : break;
258 : case PN_NULLARY:
259 : /*
260 : * E4X function namespace nodes are PN_NULLARY, but can appear on use
261 : * lists.
262 : */
263 462320 : return !pn->isUsed() && !pn->isDefn();
264 : default:
265 : ;
266 : }
267 :
268 1342798 : return true;
269 : }
270 :
271 : /*
272 : * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
273 : * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
274 : * metadata structures (the function box tree).
275 : */
276 : void
277 541 : ParseNodeAllocator::prepareNodeForMutation(ParseNode *pn)
278 : {
279 541 : if (!pn->isArity(PN_NULLARY)) {
280 27 : if (pn->isArity(PN_FUNC)) {
281 : /*
282 : * Since this node could be turned into anything, we can't
283 : * ensure it won't be subsequently recycled, so we must
284 : * disconnect it from the funbox tree entirely.
285 : *
286 : * Note that pn_funbox may legitimately be NULL. functionDef
287 : * applies MakeDefIntoUse to definition nodes, which can come
288 : * from prior iterations of the big loop in compileScript. In
289 : * such cases, the defn nodes have been visited by the recycler
290 : * (but not actually recycled!), and their funbox pointers
291 : * cleared. But it's fine to mutate them into uses of some new
292 : * definition.
293 : */
294 27 : if (pn->pn_funbox)
295 0 : pn->pn_funbox->node = NULL;
296 : }
297 :
298 : /* Put |pn|'s children (but not |pn| itself) on a work stack. */
299 27 : NodeStack stack;
300 27 : PushNodeChildren(pn, &stack);
301 : /*
302 : * For each node on the work stack, push its children on the work stack,
303 : * and free the node if we can.
304 : */
305 54 : while (!stack.empty()) {
306 0 : pn = stack.pop();
307 0 : if (PushNodeChildren(pn, &stack))
308 0 : freeNode(pn);
309 : }
310 : }
311 541 : }
312 :
313 : /*
314 : * Return the nodes in the subtree |pn| to the parser's free node list, for
315 : * reallocation.
316 : *
317 : * Note that all functions in |pn| that are not enclosed by other functions
318 : * in |pn| must be direct children of |tc|, because we only clean up |tc|'s
319 : * function and method lists. You must not reach into a function and
320 : * recycle some part of it (unless you've updated |tc|->functionList, the
321 : * way js_FoldConstants does).
322 : */
323 : ParseNode *
324 342720 : ParseNodeAllocator::freeTree(ParseNode *pn)
325 : {
326 342720 : if (!pn)
327 45 : return NULL;
328 :
329 342675 : ParseNode *savedNext = pn->pn_next;
330 :
331 342675 : NodeStack stack;
332 3833674 : for (;;) {
333 4176349 : if (PushNodeChildren(pn, &stack))
334 1905242 : freeNode(pn);
335 4176349 : if (stack.empty())
336 : break;
337 3833674 : pn = stack.pop();
338 : }
339 :
340 342675 : return savedNext;
341 : }
342 :
343 : /*
344 : * Allocate a ParseNode from parser's node freelist or, failing that, from
345 : * cx's temporary arena.
346 : */
347 : void *
348 7727093 : ParseNodeAllocator::allocNode()
349 : {
350 7727093 : if (ParseNode *pn = freelist) {
351 1146957 : freelist = pn->pn_next;
352 1146957 : return pn;
353 : }
354 :
355 6580136 : void *p = cx->tempLifoAlloc().alloc(sizeof (ParseNode));
356 6580136 : if (!p)
357 0 : js_ReportOutOfMemory(cx);
358 6580136 : return p;
359 : }
360 :
361 : /* used only by static create methods of subclasses */
362 :
363 : ParseNode *
364 6630626 : ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, TreeContext *tc)
365 : {
366 6630626 : Parser *parser = tc->parser;
367 6630626 : const Token &tok = parser->tokenStream.currentToken();
368 6630626 : return parser->new_<ParseNode>(kind, JSOP_NOP, arity, tok.pos);
369 : }
370 :
371 : ParseNode *
372 1487068 : ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right)
373 : {
374 1487068 : if (!left || !right)
375 0 : return NULL;
376 :
377 1487068 : JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
378 :
379 1487068 : if (left->pn_arity != PN_LIST) {
380 6820 : ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
381 6820 : left->setArity(PN_LIST);
382 6820 : left->pn_parens = false;
383 6820 : left->initList(pn1);
384 6820 : left->append(pn2);
385 6820 : if (kind == PNK_ADD) {
386 6109 : if (pn1->isKind(PNK_STRING))
387 4653 : left->pn_xflags |= PNX_STRCAT;
388 1456 : else if (!pn1->isKind(PNK_NUMBER))
389 1420 : left->pn_xflags |= PNX_CANTFOLD;
390 6109 : if (pn2->isKind(PNK_STRING))
391 1267 : left->pn_xflags |= PNX_STRCAT;
392 4842 : else if (!pn2->isKind(PNK_NUMBER))
393 4797 : left->pn_xflags |= PNX_CANTFOLD;
394 : }
395 : }
396 1487068 : left->append(right);
397 1487068 : left->pn_pos.end = right->pn_pos.end;
398 1487068 : if (kind == PNK_ADD) {
399 1486015 : if (right->isKind(PNK_STRING))
400 7587 : left->pn_xflags |= PNX_STRCAT;
401 1478428 : else if (!right->isKind(PNK_NUMBER))
402 1478347 : left->pn_xflags |= PNX_CANTFOLD;
403 : }
404 :
405 1487068 : return left;
406 : }
407 :
408 : ParseNode *
409 2075492 : ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
410 : TreeContext *tc)
411 : {
412 2075492 : if (!left || !right)
413 0 : return NULL;
414 :
415 : /*
416 : * Flatten a left-associative (left-heavy) tree of a given operator into
417 : * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
418 : */
419 2075492 : if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
420 1487068 : return append(kind, op, left, right);
421 :
422 : /*
423 : * Fold constant addition immediately, to conserve node space and, what's
424 : * more, so js::FoldConstants never sees mixed addition and concatenation
425 : * operations with more than one leading non-string operand in a PN_LIST
426 : * generated for expressions such as 1 + 2 + "pt" (which should evaluate
427 : * to "3pt", not "12pt").
428 : */
429 630971 : if (kind == PNK_ADD &&
430 41879 : left->isKind(PNK_NUMBER) &&
431 668 : right->isKind(PNK_NUMBER) &&
432 : tc->parser->foldConstants)
433 : {
434 315 : left->pn_dval += right->pn_dval;
435 315 : left->pn_pos.end = right->pn_pos.end;
436 315 : tc->freeTree(right);
437 315 : return left;
438 : }
439 :
440 588109 : return tc->parser->new_<BinaryNode>(kind, op, left, right);
441 : }
442 :
443 : NameNode *
444 3556426 : NameNode::create(ParseNodeKind kind, JSAtom *atom, TreeContext *tc)
445 : {
446 3556426 : ParseNode *pn = ParseNode::create(kind, PN_NAME, tc);
447 3556426 : if (pn) {
448 3556426 : pn->pn_atom = atom;
449 3556426 : ((NameNode *)pn)->initCommon(tc);
450 : }
451 3556426 : return (NameNode *)pn;
452 : }
453 :
454 : const char js_argument_str[] = "argument";
455 : const char js_variable_str[] = "variable";
456 : const char js_unknown_str[] = "unknown";
457 :
458 : const char *
459 0 : Definition::kindString(Kind kind)
460 : {
461 : static const char *table[] = {
462 : js_var_str, js_const_str, js_let_str,
463 : js_function_str, js_argument_str, js_unknown_str
464 : };
465 :
466 0 : JS_ASSERT(unsigned(kind) <= unsigned(ARG));
467 0 : return table[kind];
468 : }
469 :
470 : #if JS_HAS_DESTRUCTURING
471 :
472 : /*
473 : * This function assumes the cloned tree is for use in the same statement and
474 : * binding context as the original tree.
475 : */
476 : static ParseNode *
477 108 : CloneParseTree(ParseNode *opn, TreeContext *tc)
478 : {
479 108 : JS_CHECK_RECURSION(tc->parser->context, return NULL);
480 :
481 : ParseNode *pn = tc->parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
482 108 : opn->pn_pos);
483 108 : if (!pn)
484 0 : return NULL;
485 108 : pn->setInParens(opn->isInParens());
486 108 : pn->setDefn(opn->isDefn());
487 108 : pn->setUsed(opn->isUsed());
488 :
489 108 : switch (pn->getArity()) {
490 : #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
491 :
492 : case PN_FUNC:
493 0 : NULLCHECK(pn->pn_funbox =
494 : tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
495 0 : NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
496 0 : pn->pn_cookie = opn->pn_cookie;
497 0 : pn->pn_dflags = opn->pn_dflags;
498 0 : pn->pn_blockid = opn->pn_blockid;
499 0 : break;
500 :
501 : case PN_LIST:
502 0 : pn->makeEmpty();
503 0 : for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
504 : ParseNode *pn2;
505 0 : NULLCHECK(pn2 = CloneParseTree(opn2, tc));
506 0 : pn->append(pn2);
507 : }
508 0 : pn->pn_xflags = opn->pn_xflags;
509 0 : break;
510 :
511 : case PN_TERNARY:
512 0 : NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
513 0 : NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
514 0 : NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
515 0 : break;
516 :
517 : case PN_BINARY:
518 0 : NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
519 0 : if (opn->pn_right != opn->pn_left)
520 0 : NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
521 : else
522 0 : pn->pn_right = pn->pn_left;
523 0 : pn->pn_pval = opn->pn_pval;
524 0 : pn->pn_iflags = opn->pn_iflags;
525 0 : break;
526 :
527 : case PN_UNARY:
528 0 : NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
529 0 : pn->pn_hidden = opn->pn_hidden;
530 0 : break;
531 :
532 : case PN_NAME:
533 : // PN_NAME could mean several arms in pn_u, so copy the whole thing.
534 0 : pn->pn_u = opn->pn_u;
535 0 : if (opn->isUsed()) {
536 : /*
537 : * The old name is a use of its pn_lexdef. Make the clone also be a
538 : * use of that definition.
539 : */
540 0 : Definition *dn = pn->pn_lexdef;
541 :
542 0 : pn->pn_link = dn->dn_uses;
543 0 : dn->dn_uses = pn;
544 0 : } else if (opn->pn_expr) {
545 0 : NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
546 :
547 : /*
548 : * If the old name is a definition, the new one has pn_defn set.
549 : * Make the old name a use of the new node.
550 : */
551 0 : if (opn->isDefn()) {
552 0 : opn->setDefn(false);
553 0 : LinkUseToDef(opn, (Definition *) pn, tc);
554 : }
555 : }
556 0 : break;
557 :
558 : case PN_NAMESET:
559 0 : pn->pn_names = opn->pn_names;
560 0 : NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
561 0 : break;
562 :
563 : case PN_NULLARY:
564 : // Even PN_NULLARY may have data (xmlpi for E4X -- what a botch).
565 108 : pn->pn_u = opn->pn_u;
566 108 : break;
567 :
568 : #undef NULLCHECK
569 : }
570 108 : return pn;
571 : }
572 :
573 : #endif /* JS_HAS_DESTRUCTURING */
574 :
575 : /*
576 : * Used by Parser::forStatement and comprehensionTail to clone the TARGET in
577 : * for (var/const/let TARGET in EXPR)
578 : *
579 : * opn must be the pn_head of a node produced by Parser::variables, so its form
580 : * is known to be LHS = NAME | [LHS] | {id:LHS}.
581 : *
582 : * The cloned tree is for use only in the same statement and binding context as
583 : * the original tree.
584 : */
585 : ParseNode *
586 3988 : js::CloneLeftHandSide(ParseNode *opn, TreeContext *tc)
587 : {
588 : ParseNode *pn = tc->parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
589 3988 : opn->pn_pos);
590 3988 : if (!pn)
591 0 : return NULL;
592 3988 : pn->setInParens(opn->isInParens());
593 3988 : pn->setDefn(opn->isDefn());
594 3988 : pn->setUsed(opn->isUsed());
595 :
596 : #if JS_HAS_DESTRUCTURING
597 3988 : if (opn->isArity(PN_LIST)) {
598 216 : JS_ASSERT(opn->isKind(PNK_RB) || opn->isKind(PNK_RC));
599 216 : pn->makeEmpty();
600 603 : for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
601 : ParseNode *pn2;
602 387 : if (opn->isKind(PNK_RC)) {
603 108 : JS_ASSERT(opn2->isArity(PN_BINARY));
604 108 : JS_ASSERT(opn2->isKind(PNK_COLON));
605 :
606 108 : ParseNode *tag = CloneParseTree(opn2->pn_left, tc);
607 108 : if (!tag)
608 0 : return NULL;
609 108 : ParseNode *target = CloneLeftHandSide(opn2->pn_right, tc);
610 108 : if (!target)
611 0 : return NULL;
612 :
613 108 : pn2 = tc->parser->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
614 279 : } else if (opn2->isArity(PN_NULLARY)) {
615 0 : JS_ASSERT(opn2->isKind(PNK_COMMA));
616 0 : pn2 = CloneParseTree(opn2, tc);
617 : } else {
618 279 : pn2 = CloneLeftHandSide(opn2, tc);
619 : }
620 :
621 387 : if (!pn2)
622 0 : return NULL;
623 387 : pn->append(pn2);
624 : }
625 216 : pn->pn_xflags = opn->pn_xflags;
626 216 : return pn;
627 : }
628 : #endif
629 :
630 3772 : JS_ASSERT(opn->isArity(PN_NAME));
631 3772 : JS_ASSERT(opn->isKind(PNK_NAME));
632 :
633 : /* If opn is a definition or use, make pn a use. */
634 3772 : pn->pn_u.name = opn->pn_u.name;
635 3772 : pn->setOp(JSOP_SETNAME);
636 3772 : if (opn->isUsed()) {
637 117 : Definition *dn = pn->pn_lexdef;
638 :
639 117 : pn->pn_link = dn->dn_uses;
640 117 : dn->dn_uses = pn;
641 : } else {
642 3655 : pn->pn_expr = NULL;
643 3655 : if (opn->isDefn()) {
644 : /* We copied some definition-specific state into pn. Clear it out. */
645 3646 : pn->pn_cookie.makeFree();
646 3646 : pn->pn_dflags &= ~PND_BOUND;
647 3646 : pn->setDefn(false);
648 :
649 3646 : LinkUseToDef(pn, (Definition *) opn, tc);
650 : }
651 : }
652 3772 : return pn;
653 : }
654 :
655 : #ifdef DEBUG
656 : void
657 0 : js::DumpParseTree(ParseNode *pn, int indent)
658 : {
659 0 : if (pn == NULL)
660 0 : fprintf(stderr, "()");
661 : else
662 0 : pn->dump(indent);
663 0 : }
664 : #endif
|