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 331 : ParseNode::become(ParseNode *pn2)
61 : {
62 331 : JS_ASSERT(!pn_defn);
63 331 : JS_ASSERT(!pn2->isDefn());
64 :
65 331 : JS_ASSERT(!pn_used);
66 331 : 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 331 : pn_type = pn2->pn_type;
78 331 : pn_op = pn2->pn_op;
79 331 : pn_arity = pn2->pn_arity;
80 331 : pn_parens = pn2->pn_parens;
81 331 : 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 331 : 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 322 : } 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 331 : pn2->clear();
99 331 : }
100 :
101 : void
102 331 : ParseNode::clear()
103 : {
104 331 : pn_type = PNK_LIMIT;
105 331 : setOp(JSOP_NOP);
106 331 : pn_used = pn_defn = false;
107 331 : pn_arity = PN_NULLARY;
108 331 : pn_parens = false;
109 331 : }
110 :
111 : bool
112 0 : FunctionBox::joinable() const
113 : {
114 0 : return function()->isNullClosure() &&
115 : (tcflags & (TCF_FUN_USES_ARGUMENTS |
116 : TCF_FUN_USES_OWN_NAME |
117 0 : TCF_COMPILE_N_GO)) == TCF_COMPILE_N_GO;
118 : }
119 :
120 : bool
121 900444 : FunctionBox::inAnyDynamicScope() const
122 : {
123 2095732 : for (const FunctionBox *funbox = this; funbox; funbox = funbox->parent) {
124 1203774 : if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_EXTENSIBLE_SCOPE))
125 8486 : return true;
126 : }
127 891958 : return false;
128 : }
129 :
130 : bool
131 163731 : FunctionBox::scopeIsExtensible() const
132 : {
133 163731 : return tcflags & TCF_FUN_EXTENSIBLE_SCOPE;
134 : }
135 :
136 : /* Add |node| to |parser|'s free node list. */
137 : void
138 21828853 : ParseNodeAllocator::freeNode(ParseNode *pn)
139 : {
140 : /* Catch back-to-back dup recycles. */
141 21828853 : JS_ASSERT(pn != freelist);
142 :
143 : /*
144 : * It's too hard to clear these nodes from the AtomDefnMaps, etc. that
145 : * hold references to them, so we never free them. It's our caller's job to
146 : * recognize and process these, since their children do need to be dealt
147 : * with.
148 : */
149 21828853 : JS_ASSERT(!pn->isUsed());
150 21828853 : JS_ASSERT(!pn->isDefn());
151 :
152 21828853 : if (pn->isArity(PN_NAMESET) && pn->pn_names.hasMap())
153 9 : pn->pn_names.releaseMap(cx);
154 :
155 : #ifdef DEBUG
156 : /* Poison the node, to catch attempts to use it without initializing it. */
157 21828853 : memset(pn, 0xab, sizeof(*pn));
158 : #endif
159 :
160 21828853 : pn->pn_next = freelist;
161 21828853 : freelist = pn;
162 21828853 : }
163 :
164 : /*
165 : * A work pool of ParseNodes. The work pool is a stack, chained together
166 : * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
167 : * when recycling deep parse trees.
168 : *
169 : * Since parse nodes are probably allocated in something close to the order
170 : * they appear in a depth-first traversal of the tree, making the work pool
171 : * a stack should give us pretty good locality.
172 : */
173 : class NodeStack {
174 : public:
175 1141794 : NodeStack() : top(NULL) { }
176 53550136 : bool empty() { return top == NULL; }
177 15271724 : void push(ParseNode *pn) {
178 15271724 : pn->pn_next = top;
179 15271724 : top = pn;
180 15271724 : }
181 15863436 : void pushUnlessNull(ParseNode *pn) { if (pn) push(pn); }
182 : /* Push the children of the PN_LIST node |pn| on the stack. */
183 1335213 : void pushList(ParseNode *pn) {
184 : /* This clobbers pn->pn_head if the list is empty; should be okay. */
185 1335213 : *pn->pn_tail = top;
186 1335213 : top = pn->pn_head;
187 1335213 : }
188 26204171 : ParseNode *pop() {
189 26204171 : JS_ASSERT(!empty());
190 26204171 : ParseNode *hold = top; /* my kingdom for a prog1 */
191 26204171 : top = top->pn_next;
192 26204171 : return hold;
193 : }
194 : private:
195 : ParseNode *top;
196 : };
197 :
198 : /*
199 : * Push the children of |pn| on |stack|. Return true if |pn| itself could be
200 : * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
201 : * nodes, and all function nodes; see comments for CleanFunctionList in
202 : * SemanticAnalysis.cpp). Some callers want to free |pn|; others
203 : * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and
204 : * just need to take care of its children.
205 : */
206 : static bool
207 27345965 : PushNodeChildren(ParseNode *pn, NodeStack *stack)
208 : {
209 27345965 : switch (pn->getArity()) {
210 : case PN_FUNC:
211 : /*
212 : * Function nodes are linked into the function box tree, and may appear
213 : * on method lists. Both of those lists are singly-linked, so trying to
214 : * update them now could result in quadratic behavior when recycling
215 : * trees containing many functions; and the lists can be very long. So
216 : * we put off cleaning the lists up until just before function
217 : * analysis, when we call CleanFunctionList.
218 : *
219 : * In fact, we can't recycle the parse node yet, either: it may appear
220 : * on a method list, and reusing the node would corrupt that. Instead,
221 : * we clear its pn_funbox pointer to mark it as deleted;
222 : * CleanFunctionList recycles it as well.
223 : *
224 : * We do recycle the nodes around it, though, so we must clear pointers
225 : * to them to avoid leaving dangling references where someone can find
226 : * them.
227 : */
228 769403 : pn->pn_funbox = NULL;
229 769403 : stack->pushUnlessNull(pn->pn_body);
230 769403 : pn->pn_body = NULL;
231 769403 : return false;
232 :
233 : case PN_NAME:
234 : /*
235 : * Because used/defn nodes appear in AtomDefnMaps and elsewhere, we
236 : * don't recycle them. (We'll recover their storage when we free the
237 : * temporary arena.) However, we do recycle the nodes around them, so
238 : * clean up the pointers to avoid dangling references. The top-level
239 : * decls table carries references to them that later iterations through
240 : * the compileScript loop may find, so they need to be neat.
241 : *
242 : * pn_expr and pn_lexdef share storage; the latter isn't an owning
243 : * reference.
244 : */
245 7274514 : if (!pn->isUsed()) {
246 3187558 : stack->pushUnlessNull(pn->pn_expr);
247 3187558 : pn->pn_expr = NULL;
248 : }
249 7274514 : return !pn->isUsed() && !pn->isDefn();
250 :
251 : case PN_LIST:
252 1335213 : stack->pushList(pn);
253 1335213 : break;
254 : case PN_TERNARY:
255 194197 : stack->pushUnlessNull(pn->pn_kid1);
256 194197 : stack->pushUnlessNull(pn->pn_kid2);
257 194197 : stack->pushUnlessNull(pn->pn_kid3);
258 194197 : break;
259 : case PN_BINARY:
260 4438142 : if (pn->pn_left != pn->pn_right)
261 4438115 : stack->pushUnlessNull(pn->pn_left);
262 4438142 : stack->pushUnlessNull(pn->pn_right);
263 4438142 : break;
264 : case PN_UNARY:
265 2447627 : stack->pushUnlessNull(pn->pn_kid);
266 2447627 : break;
267 : case PN_NULLARY:
268 : /*
269 : * E4X function namespace nodes are PN_NULLARY, but can appear on use
270 : * lists.
271 : */
272 10305811 : return !pn->isUsed() && !pn->isDefn();
273 : default:
274 : ;
275 : }
276 :
277 8996237 : return true;
278 : }
279 :
280 : /*
281 : * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
282 : * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
283 : * metadata structures (the function box tree).
284 : */
285 : void
286 1608 : ParseNodeAllocator::prepareNodeForMutation(ParseNode *pn)
287 : {
288 1608 : if (!pn->isArity(PN_NULLARY)) {
289 23 : if (pn->isArity(PN_FUNC)) {
290 : /*
291 : * Since this node could be turned into anything, we can't
292 : * ensure it won't be subsequently recycled, so we must
293 : * disconnect it from the funbox tree entirely.
294 : *
295 : * Note that pn_funbox may legitimately be NULL. functionDef
296 : * applies MakeDefIntoUse to definition nodes, which can come
297 : * from prior iterations of the big loop in compileScript. In
298 : * such cases, the defn nodes have been visited by the recycler
299 : * (but not actually recycled!), and their funbox pointers
300 : * cleared. But it's fine to mutate them into uses of some new
301 : * definition.
302 : */
303 23 : if (pn->pn_funbox)
304 0 : pn->pn_funbox->node = NULL;
305 : }
306 :
307 : /* Put |pn|'s children (but not |pn| itself) on a work stack. */
308 23 : NodeStack stack;
309 23 : PushNodeChildren(pn, &stack);
310 : /*
311 : * For each node on the work stack, push its children on the work stack,
312 : * and free the node if we can.
313 : */
314 46 : while (!stack.empty()) {
315 0 : pn = stack.pop();
316 0 : if (PushNodeChildren(pn, &stack))
317 0 : freeNode(pn);
318 : }
319 : }
320 1608 : }
321 :
322 : /*
323 : * Return the nodes in the subtree |pn| to the parser's free node list, for
324 : * reallocation.
325 : *
326 : * Note that all functions in |pn| that are not enclosed by other functions
327 : * in |pn| must be direct children of |tc|, because we only clean up |tc|'s
328 : * function and method lists. You must not reach into a function and
329 : * recycle some part of it (unless you've updated |tc|->functionList, the
330 : * way js_FoldConstants does).
331 : */
332 : ParseNode *
333 1141819 : ParseNodeAllocator::freeTree(ParseNode *pn)
334 : {
335 1141819 : if (!pn)
336 48 : return NULL;
337 :
338 1141771 : ParseNode *savedNext = pn->pn_next;
339 :
340 1141771 : NodeStack stack;
341 26204171 : for (;;) {
342 27345942 : if (PushNodeChildren(pn, &stack))
343 21828853 : freeNode(pn);
344 27345942 : if (stack.empty())
345 : break;
346 26204171 : pn = stack.pop();
347 : }
348 :
349 1141771 : return savedNext;
350 : }
351 :
352 : /*
353 : * Allocate a ParseNode from parser's node freelist or, failing that, from
354 : * cx's temporary arena.
355 : */
356 : void *
357 80627913 : ParseNodeAllocator::allocNode()
358 : {
359 80627913 : if (ParseNode *pn = freelist) {
360 20313938 : freelist = pn->pn_next;
361 20313938 : return pn;
362 : }
363 :
364 60313975 : void *p = cx->tempLifoAlloc().alloc(sizeof (ParseNode));
365 60313975 : if (!p)
366 0 : js_ReportOutOfMemory(cx);
367 60313975 : return p;
368 : }
369 :
370 : /* used only by static create methods of subclasses */
371 :
372 : ParseNode *
373 58615682 : ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, TreeContext *tc)
374 : {
375 58615682 : Parser *parser = tc->parser;
376 58615682 : const Token &tok = parser->tokenStream.currentToken();
377 58615682 : return parser->new_<ParseNode>(kind, JSOP_NOP, arity, tok.pos);
378 : }
379 :
380 : ParseNode *
381 1986676 : ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right)
382 : {
383 1986676 : if (!left || !right)
384 0 : return NULL;
385 :
386 1986676 : JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
387 :
388 1986676 : if (left->pn_arity != PN_LIST) {
389 216277 : ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
390 216277 : left->setArity(PN_LIST);
391 216277 : left->pn_parens = false;
392 216277 : left->initList(pn1);
393 216277 : left->append(pn2);
394 216277 : if (kind == PNK_ADD) {
395 174368 : if (pn1->isKind(PNK_STRING))
396 152010 : left->pn_xflags |= PNX_STRCAT;
397 22358 : else if (!pn1->isKind(PNK_NUMBER))
398 22318 : left->pn_xflags |= PNX_CANTFOLD;
399 174368 : if (pn2->isKind(PNK_STRING))
400 40246 : left->pn_xflags |= PNX_STRCAT;
401 134122 : else if (!pn2->isKind(PNK_NUMBER))
402 134075 : left->pn_xflags |= PNX_CANTFOLD;
403 : }
404 : }
405 1986676 : left->append(right);
406 1986676 : left->pn_pos.end = right->pn_pos.end;
407 1986676 : if (kind == PNK_ADD) {
408 1925121 : if (right->isKind(PNK_STRING))
409 306165 : left->pn_xflags |= PNX_STRCAT;
410 1618956 : else if (!right->isKind(PNK_NUMBER))
411 1618861 : left->pn_xflags |= PNX_CANTFOLD;
412 : }
413 :
414 1986676 : return left;
415 : }
416 :
417 : ParseNode *
418 6003185 : ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
419 : TreeContext *tc)
420 : {
421 6003185 : if (!left || !right)
422 0 : return NULL;
423 :
424 : /*
425 : * Flatten a left-associative (left-heavy) tree of a given operator into
426 : * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
427 : */
428 6003185 : if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
429 1986676 : return append(kind, op, left, right);
430 :
431 : /*
432 : * Fold constant addition immediately, to conserve node space and, what's
433 : * more, so js::FoldConstants never sees mixed addition and concatenation
434 : * operations with more than one leading non-string operand in a PN_LIST
435 : * generated for expressions such as 1 + 2 + "pt" (which should evaluate
436 : * to "3pt", not "12pt").
437 : */
438 4387291 : if (kind == PNK_ADD &&
439 370023 : left->isKind(PNK_NUMBER) &&
440 759 : right->isKind(PNK_NUMBER) &&
441 : tc->parser->foldConstants)
442 : {
443 332 : left->pn_dval += right->pn_dval;
444 332 : left->pn_pos.end = right->pn_pos.end;
445 332 : tc->freeTree(right);
446 332 : return left;
447 : }
448 :
449 4016177 : return tc->parser->new_<BinaryNode>(kind, op, left, right);
450 : }
451 :
452 : NameNode *
453 19814859 : NameNode::create(ParseNodeKind kind, JSAtom *atom, TreeContext *tc)
454 : {
455 19814859 : ParseNode *pn = ParseNode::create(kind, PN_NAME, tc);
456 19814859 : if (pn) {
457 19814859 : pn->pn_atom = atom;
458 19814859 : ((NameNode *)pn)->initCommon(tc);
459 : }
460 19814859 : return (NameNode *)pn;
461 : }
462 :
463 : const char js_argument_str[] = "argument";
464 : const char js_variable_str[] = "variable";
465 : const char js_unknown_str[] = "unknown";
466 :
467 : const char *
468 5 : Definition::kindString(Kind kind)
469 : {
470 : static const char *table[] = {
471 : js_var_str, js_const_str, js_let_str,
472 : js_function_str, js_argument_str, js_unknown_str
473 : };
474 :
475 5 : JS_ASSERT(unsigned(kind) <= unsigned(ARG));
476 5 : return table[kind];
477 : }
478 :
479 : #if JS_HAS_DESTRUCTURING
480 :
481 : /*
482 : * This function assumes the cloned tree is for use in the same statement and
483 : * binding context as the original tree.
484 : */
485 : static ParseNode *
486 276 : CloneParseTree(ParseNode *opn, TreeContext *tc)
487 : {
488 276 : JS_CHECK_RECURSION(tc->parser->context, return NULL);
489 :
490 : ParseNode *pn = tc->parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
491 276 : opn->pn_pos);
492 276 : if (!pn)
493 0 : return NULL;
494 276 : pn->setInParens(opn->isInParens());
495 276 : pn->setDefn(opn->isDefn());
496 276 : pn->setUsed(opn->isUsed());
497 :
498 276 : switch (pn->getArity()) {
499 : #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
500 :
501 : case PN_FUNC:
502 0 : NULLCHECK(pn->pn_funbox =
503 : tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
504 0 : NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
505 0 : pn->pn_cookie = opn->pn_cookie;
506 0 : pn->pn_dflags = opn->pn_dflags;
507 0 : pn->pn_blockid = opn->pn_blockid;
508 0 : break;
509 :
510 : case PN_LIST:
511 0 : pn->makeEmpty();
512 0 : for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
513 : ParseNode *pn2;
514 0 : NULLCHECK(pn2 = CloneParseTree(opn2, tc));
515 0 : pn->append(pn2);
516 : }
517 0 : pn->pn_xflags = opn->pn_xflags;
518 0 : break;
519 :
520 : case PN_TERNARY:
521 0 : NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
522 0 : NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
523 0 : NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
524 0 : break;
525 :
526 : case PN_BINARY:
527 0 : NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
528 0 : if (opn->pn_right != opn->pn_left)
529 0 : NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
530 : else
531 0 : pn->pn_right = pn->pn_left;
532 0 : pn->pn_pval = opn->pn_pval;
533 0 : pn->pn_iflags = opn->pn_iflags;
534 0 : break;
535 :
536 : case PN_UNARY:
537 0 : NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
538 0 : pn->pn_hidden = opn->pn_hidden;
539 0 : break;
540 :
541 : case PN_NAME:
542 : // PN_NAME could mean several arms in pn_u, so copy the whole thing.
543 0 : pn->pn_u = opn->pn_u;
544 0 : if (opn->isUsed()) {
545 : /*
546 : * The old name is a use of its pn_lexdef. Make the clone also be a
547 : * use of that definition.
548 : */
549 0 : Definition *dn = pn->pn_lexdef;
550 :
551 0 : pn->pn_link = dn->dn_uses;
552 0 : dn->dn_uses = pn;
553 0 : } else if (opn->pn_expr) {
554 0 : NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
555 :
556 : /*
557 : * If the old name is a definition, the new one has pn_defn set.
558 : * Make the old name a use of the new node.
559 : */
560 0 : if (opn->isDefn()) {
561 0 : opn->setDefn(false);
562 0 : LinkUseToDef(opn, (Definition *) pn, tc);
563 : }
564 : }
565 0 : break;
566 :
567 : case PN_NAMESET:
568 0 : pn->pn_names = opn->pn_names;
569 0 : NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
570 0 : break;
571 :
572 : case PN_NULLARY:
573 : // Even PN_NULLARY may have data (xmlpi for E4X -- what a botch).
574 276 : pn->pn_u = opn->pn_u;
575 276 : break;
576 :
577 : #undef NULLCHECK
578 : }
579 276 : return pn;
580 : }
581 :
582 : #endif /* JS_HAS_DESTRUCTURING */
583 :
584 : /*
585 : * Used by Parser::forStatement and comprehensionTail to clone the TARGET in
586 : * for (var/const/let TARGET in EXPR)
587 : *
588 : * opn must be the pn_head of a node produced by Parser::variables, so its form
589 : * is known to be LHS = NAME | [LHS] | {id:LHS}.
590 : *
591 : * The cloned tree is for use only in the same statement and binding context as
592 : * the original tree.
593 : */
594 : ParseNode *
595 58468 : js::CloneLeftHandSide(ParseNode *opn, TreeContext *tc)
596 : {
597 : ParseNode *pn = tc->parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
598 58468 : opn->pn_pos);
599 58468 : if (!pn)
600 0 : return NULL;
601 58468 : pn->setInParens(opn->isInParens());
602 58468 : pn->setDefn(opn->isDefn());
603 58468 : pn->setUsed(opn->isUsed());
604 :
605 : #if JS_HAS_DESTRUCTURING
606 58468 : if (opn->isArity(PN_LIST)) {
607 3067 : JS_ASSERT(opn->isKind(PNK_RB) || opn->isKind(PNK_RC));
608 3067 : pn->makeEmpty();
609 9226 : for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
610 : ParseNode *pn2;
611 6159 : if (opn->isKind(PNK_RC)) {
612 274 : JS_ASSERT(opn2->isArity(PN_BINARY));
613 274 : JS_ASSERT(opn2->isKind(PNK_COLON));
614 :
615 274 : ParseNode *tag = CloneParseTree(opn2->pn_left, tc);
616 274 : if (!tag)
617 0 : return NULL;
618 274 : ParseNode *target = CloneLeftHandSide(opn2->pn_right, tc);
619 274 : if (!target)
620 0 : return NULL;
621 :
622 274 : pn2 = tc->parser->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
623 5885 : } else if (opn2->isArity(PN_NULLARY)) {
624 2 : JS_ASSERT(opn2->isKind(PNK_COMMA));
625 2 : pn2 = CloneParseTree(opn2, tc);
626 : } else {
627 5883 : pn2 = CloneLeftHandSide(opn2, tc);
628 : }
629 :
630 6159 : if (!pn2)
631 0 : return NULL;
632 6159 : pn->append(pn2);
633 : }
634 3067 : pn->pn_xflags = opn->pn_xflags;
635 3067 : return pn;
636 : }
637 : #endif
638 :
639 55401 : JS_ASSERT(opn->isArity(PN_NAME));
640 55401 : JS_ASSERT(opn->isKind(PNK_NAME));
641 :
642 : /* If opn is a definition or use, make pn a use. */
643 55401 : pn->pn_u.name = opn->pn_u.name;
644 55401 : pn->setOp(JSOP_SETNAME);
645 55401 : if (opn->isUsed()) {
646 1053 : Definition *dn = pn->pn_lexdef;
647 :
648 1053 : pn->pn_link = dn->dn_uses;
649 1053 : dn->dn_uses = pn;
650 : } else {
651 54348 : pn->pn_expr = NULL;
652 54348 : if (opn->isDefn()) {
653 : /* We copied some definition-specific state into pn. Clear it out. */
654 54339 : pn->pn_cookie.makeFree();
655 54339 : pn->pn_dflags &= ~PND_BOUND;
656 54339 : pn->setDefn(false);
657 :
658 54339 : LinkUseToDef(pn, (Definition *) opn, tc);
659 : }
660 : }
661 55401 : return pn;
662 : }
663 :
664 : #ifdef DEBUG
665 : void
666 0 : js::DumpParseTree(ParseNode *pn, int indent)
667 : {
668 0 : if (pn == NULL)
669 0 : fprintf(stderr, "()");
670 : else
671 0 : pn->dump(indent);
672 0 : }
673 : #endif
|