Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsparse.cpp
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
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 of the GNU General Public License Version 2 or later (the "GPL"),
29  * or 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 /*
42  * JS parser.
43  *
44  * This is a recursive-descent parser for the JavaScript language specified by
45  * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic
46  * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
47  * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48  * After tree construction, it rewrites trees to fold constants and evaluate
49  * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to
50  * generate bytecode.
51  *
52  * This parser attempts no error recovery.
53  */
54 #include <stdlib.h>
55 #include <string.h>
56 #include <math.h>
57 #include "jstypes.h"
58 #include "jsstdint.h"
59 #include "jsarena.h"
60 #include "jsutil.h"
61 #include "jsapi.h"
62 #include "jsarray.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsversion.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jsiter.h"
70 #include "jslock.h"
71 #include "jsnum.h"
72 #include "jsobj.h"
73 #include "jsopcode.h"
74 #include "jsparse.h"
75 #include "jsscan.h"
76 #include "jsscope.h"
77 #include "jsscript.h"
78 #include "jsstr.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
81 #include "jsvector.h"
82
83 #if JS_HAS_XML_SUPPORT
84 #include "jsxml.h"
85 #endif
86
87 #if JS_HAS_DESTRUCTURING
88 #include "jsdhash.h"
89 #endif
90
91 #include "jsatominlines.h"
92 #include "jsinterpinlines.h"
93 #include "jsobjinlines.h"
94 #include "jsregexpinlines.h"
95 #include "jsscriptinlines.h"
96
97 // Grr, windows.h or something under it #defines CONST...
98 #ifdef CONST
99 #undef CONST
100 #endif
101
102 using namespace js;
103 using namespace js::gc;
104
105 /*
106  * Asserts to verify assumptions behind pn_ macros.
107  */
108 #define pn_offsetof(m)  offsetof(JSParseNode, m)
109
110 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
111 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
112
113 #undef pn_offsetof
114
115 /*
116  * Insist that the next token be of type tt, or report errno and return null.
117  * NB: this macro uses cx and ts from its lexical environment.
118  */
119 #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags)                                     \
120     JS_BEGIN_MACRO                                                                          \
121         if (tokenStream.getToken((__flags)) != tt) {                                        \
122             reportErrorNumber(NULL, JSREPORT_ERROR, errno);                                 \
123             return NULL;                                                                    \
124         }                                                                                   \
125     JS_END_MACRO
126 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
127
128 #ifdef METER_PARSENODES
129 static uint32 parsenodes = 0;
130 static uint32 maxparsenodes = 0;
131 static uint32 recyclednodes = 0;
132 #endif
133
134 void
135 JSParseNode::become(JSParseNode *pn2)
136 {
137     JS_ASSERT(!pn_defn);
138     JS_ASSERT(!pn2->pn_defn);
139
140     JS_ASSERT(!pn_used);
141     if (pn2->pn_used) {
142         JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
143         while (*pnup != pn2)
144             pnup = &(*pnup)->pn_link;
145         *pnup = this;
146         pn_link = pn2->pn_link;
147         pn_used = true;
148         pn2->pn_link = NULL;
149         pn2->pn_used = false;
150     }
151
152     pn_type = pn2->pn_type;
153     pn_op = pn2->pn_op;
154     pn_arity = pn2->pn_arity;
155     pn_parens = pn2->pn_parens;
156     pn_u = pn2->pn_u;
157
158     /*
159      * If any pointers are pointing to pn2, change them to point to this
160      * instead, since pn2 will be cleared and probably recycled.
161      */
162     if (PN_TYPE(this) == TOK_FUNCTION && pn_arity == PN_FUNC) {
163         /* Function node: fix up the pn_funbox->node back-pointer. */
164         JS_ASSERT(pn_funbox->node == pn2);
165         pn_funbox->node = this;
166     } else if (pn_arity == PN_LIST && !pn_head) {
167         /* Empty list: fix up the pn_tail pointer. */
168         JS_ASSERT(pn_count == 0);
169         JS_ASSERT(pn_tail == &pn2->pn_head);
170         pn_tail = &pn_head;
171     }
172
173     pn2->clear();
174 }
175
176 void
177 JSParseNode::clear()
178 {
179     pn_type = TOK_EOF;
180     pn_op = JSOP_NOP;
181     pn_used = pn_defn = false;
182     pn_arity = PN_NULLARY;
183     pn_parens = false;
184 }
185
186 Parser::Parser(JSContext *cx, JSPrincipals *prin, JSStackFrame *cfp)
187   : js::AutoGCRooter(cx, PARSER),
188     context(cx),
189     aleFreeList(NULL),
190     tokenStream(cx),
191     principals(NULL),
192     callerFrame(cfp),
193     callerVarObj(cfp ? &cfp->varobj(cx->containingSegment(cfp)) : NULL),
194     nodeList(NULL),
195     functionCount(0),
196     traceListHead(NULL),
197     tc(NULL),
198     keepAtoms(cx->runtime)
199 {
200     js::PodArrayZero(tempFreeList);
201     setPrincipals(prin);
202     JS_ASSERT_IF(cfp, cfp->isScriptFrame());
203 }
204
205 bool
206 Parser::init(const jschar *base, size_t length, const char *filename, uintN lineno,
207              JSVersion version)
208 {
209     JSContext *cx = context;
210     tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
211     if (!tokenStream.init(base, length, filename, lineno, version)) {
212         JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
213         return false;
214     }
215     return true;
216 }
217
218 Parser::~Parser()
219 {
220     JSContext *cx = context;
221
222     if (principals)
223         JSPRINCIPALS_DROP(cx, principals);
224     tokenStream.close();
225     JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
226 }
227
228 void
229 Parser::setPrincipals(JSPrincipals *prin)
230 {
231     JS_ASSERT(!principals);
232     if (prin)
233         JSPRINCIPALS_HOLD(context, prin);
234     principals = prin;
235 }
236
237 JSObjectBox *
238 Parser::newObjectBox(JSObject *obj)
239 {
240     JS_ASSERT(obj);
241
242     /*
243      * We use JSContext.tempPool to allocate parsed objects and place them on
244      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
245      * containing the entries must be alive until we are done with scanning,
246      * parsing and code generation for the whole script or top-level function.
247      */
248     JSObjectBox *objbox;
249     JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
250     if (!objbox) {
251         js_ReportOutOfScriptQuota(context);
252         return NULL;
253     }
254     objbox->traceLink = traceListHead;
255     traceListHead = objbox;
256     objbox->emitLink = NULL;
257     objbox->object = obj;
258     objbox->isFunctionBox = false;
259     return objbox;
260 }
261
262 JSFunctionBox *
263 Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
264 {
265     JS_ASSERT(obj);
266     JS_ASSERT(obj->isFunction());
267
268     /*
269      * We use JSContext.tempPool to allocate parsed objects and place them on
270      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
271      * containing the entries must be alive until we are done with scanning,
272      * parsing and code generation for the whole script or top-level function.
273      */
274     JSFunctionBox *funbox;
275     JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
276     if (!funbox) {
277         js_ReportOutOfScriptQuota(context);
278         return NULL;
279     }
280     funbox->traceLink = traceListHead;
281     traceListHead = funbox;
282     funbox->emitLink = NULL;
283     funbox->object = obj;
284     funbox->isFunctionBox = true;
285     funbox->node = fn;
286     funbox->siblings = tc->functionList;
287     tc->functionList = funbox;
288     ++tc->parser->functionCount;
289     funbox->kids = NULL;
290     funbox->parent = tc->funbox;
291     funbox->methods = NULL;
292     new (&funbox->bindings) Bindings(context);
293     funbox->queued = false;
294     funbox->inLoop = false;
295     for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
296         if (STMT_IS_LOOP(stmt)) {
297             funbox->inLoop = true;
298             break;
299         }
300     }
301     funbox->level = tc->staticLevel;
302     funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
303     if (tc->innermostWith)
304         funbox->tcflags |= TCF_IN_WITH;
305     return funbox;
306 }
307
308 bool
309 JSFunctionBox::joinable() const
310 {
311     return FUN_NULL_CLOSURE((JSFunction *) object) &&
312            !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
313 }
314
315 bool
316 JSFunctionBox::inAnyDynamicScope() const
317 {
318     for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
319         if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_CALLS_EVAL))
320             return true;
321     }
322     return false;
323 }
324
325 bool
326 JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
327 {
328     if (slowMethods != 0) {
329         for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
330             if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
331                 return true;
332             if (funbox->inLoop)
333                 return true;
334         }
335     }
336     return false;
337 }
338
339 void
340 Parser::trace(JSTracer *trc)
341 {
342     JSObjectBox *objbox = traceListHead;
343     while (objbox) {
344         MarkObject(trc, *objbox->object, "parser.object");
345         if (objbox->isFunctionBox)
346             static_cast<JSFunctionBox *>(objbox)->bindings.trace(trc);
347         objbox = objbox->traceLink;
348     }
349
350     for (JSTreeContext *tc = this->tc; tc; tc = tc->parent)
351         tc->trace(trc);
352 }
353
354 /* Add |node| to |parser|'s free node list. */
355 static inline void
356 AddNodeToFreeList(JSParseNode *pn, js::Parser *parser)
357 {
358     /* Catch back-to-back dup recycles. */
359     JS_ASSERT(pn != parser->nodeList);
360
361     /* 
362      * It's too hard to clear these nodes from the JSAtomLists, etc. that
363      * hold references to them, so we never free them. It's our caller's
364      * job to recognize and process these, since their children do need to
365      * be dealt with.
366      */
367     JS_ASSERT(!pn->pn_used);
368     JS_ASSERT(!pn->pn_defn);
369
370 #ifdef DEBUG
371     /* Poison the node, to catch attempts to use it without initializing it. */
372     memset(pn, 0xab, sizeof(*pn));
373 #endif
374
375     pn->pn_next = parser->nodeList;
376     parser->nodeList = pn;
377
378 #ifdef METER_PARSENODES
379     recyclednodes++;
380 #endif
381 }
382
383 /* Add |node| to |tc|'s parser's free node list. */
384 static inline void
385 AddNodeToFreeList(JSParseNode *pn, JSTreeContext *tc)
386 {
387     AddNodeToFreeList(pn, tc->parser);
388 }
389
390 /*
391  * Walk the function box list at |*funboxHead|, removing boxes for deleted
392  * functions and cleaning up method lists. We do this once, before
393  * performing function analysis, to avoid traversing possibly long function
394  * lists repeatedly when recycling nodes.
395  *
396  * There are actually three possible states for function boxes and their
397  * nodes:
398  *
399  * - Live: funbox->node points to the node, and funbox->node->pn_funbox
400  *   points back to the funbox.
401  *
402  * - Recycled: funbox->node points to the node, but funbox->node->pn_funbox
403  *   is NULL. When a function node is part of a tree that gets recycled, we
404  *   must avoid corrupting any method list the node is on, so we leave the
405  *   function node unrecycled until we call cleanFunctionList. At recycle
406  *   time, we clear such nodes' pn_funbox pointers to indicate that they
407  *   are deleted and should be recycled once we get here.
408  *
409  * - Mutated: funbox->node is NULL; the contents of the node itself could
410  *   be anything. When we mutate a function node into some other kind of
411  *   node, we lose all indication that the node was ever part of the
412  *   function box tree; it could later be recycled, reallocated, and turned
413  *   into anything at all. (Fortunately, method list members never get
414  *   mutated, so we don't have to worry about that case.)
415  *   PrepareNodeForMutation clears the node's function box's node pointer,
416  *   disconnecting it entirely from the function box tree, and marking the
417  *   function box to be trimmed out.
418  */
419 void
420 Parser::cleanFunctionList(JSFunctionBox **funboxHead)
421 {
422     JSFunctionBox **link = funboxHead;
423     while (JSFunctionBox *box = *link) {
424         if (!box->node) {
425             /*
426              * This funbox's parse node was mutated into something else. Drop the box,
427              * and stay at the same link.
428              */
429             *link = box->siblings;
430         } else if (!box->node->pn_funbox) {
431             /*
432              * This funbox's parse node is ready to be recycled. Drop the box, recycle
433              * the node, and stay at the same link.
434              */
435             *link = box->siblings;
436             AddNodeToFreeList(box->node, this);
437         } else {
438             /* The function is still live. */
439
440             /* First, remove nodes for deleted functions from our methods list. */
441             {
442                 JSParseNode **methodLink = &box->methods;
443                 while (JSParseNode *method = *methodLink) {
444                     /* Method nodes are never rewritten in place to be other kinds of nodes. */
445                     JS_ASSERT(method->pn_arity == PN_FUNC);
446                     if (!method->pn_funbox) {
447                         /* Deleted: drop the node, and stay on this link. */
448                         *methodLink = method->pn_link;
449                     } else {
450                         /* Live: keep the node, and move to the next link. */
451                         methodLink = &method->pn_link;
452                     }
453                 }
454             }
455
456             /* Second, remove boxes for deleted functions from our kids list. */
457             cleanFunctionList(&box->kids);
458
459             /* Keep the box on the list, and move to the next link. */
460             link = &box->siblings;
461         }
462     }
463 }
464
465 namespace js {
466
467 /*
468  * A work pool of JSParseNodes. The work pool is a stack, chained together
469  * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
470  * when recycling deep parse trees.
471  *
472  * Since parse nodes are probably allocated in something close to the order
473  * they appear in a depth-first traversal of the tree, making the work pool
474  * a stack should give us pretty good locality.
475  */
476 class NodeStack {
477   public:
478     NodeStack() : top(NULL) { }
479     bool empty() { return top == NULL; }
480     void push(JSParseNode *pn) {
481         pn->pn_next = top;
482         top = pn;
483     }
484     void pushUnlessNull(JSParseNode *pn) { if (pn) push(pn); }
485     /* Push the children of the PN_LIST node |pn| on the stack. */
486     void pushList(JSParseNode *pn) {
487         /* This clobbers pn->pn_head if the list is empty; should be okay. */
488         *pn->pn_tail = top;
489         top = pn->pn_head;
490     }
491     JSParseNode *pop() {
492         JS_ASSERT(!empty());
493         JSParseNode *hold = top; /* my kingdom for a prog1 */
494         top = top->pn_next;
495         return hold;
496     }
497   private:
498     JSParseNode *top;
499 };
500
501 } /* namespace js */
502
503 /*
504  * Push the children of |pn| on |stack|. Return true if |pn| itself could be
505  * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
506  * nodes, and all function nodes; see comments for
507  * js::Parser::cleanFunctionList). Some callers want to free |pn|; others
508  * (PrepareNodeForMutation) don't care about |pn|, and just need to take care of
509  * its children.
510  */
511 static bool
512 PushNodeChildren(JSParseNode *pn, NodeStack *stack)
513 {
514     switch (pn->pn_arity) {
515       case PN_FUNC:
516         /*
517          * Function nodes are linked into the function box tree, and may
518          * appear on method lists. Both of those lists are singly-linked,
519          * so trying to update them now could result in quadratic behavior
520          * when recycling trees containing many functions; and the lists
521          * can be very long. So we put off cleaning the lists up until just
522          * before function analysis, when we call
523          * js::Parser::cleanFunctionList.
524          *
525          * In fact, we can't recycle the parse node yet, either: it may
526          * appear on a method list, and reusing the node would corrupt
527          * that. Instead, we clear its pn_funbox pointer to mark it as
528          * deleted; js::Parser::cleanFunctionList recycles it as well.
529          *
530          * We do recycle the nodes around it, though, so we must clear
531          * pointers to them to avoid leaving dangling references where
532          * someone can find them.
533          */
534         pn->pn_funbox = NULL;
535         stack->pushUnlessNull(pn->pn_body);
536         pn->pn_body = NULL;
537         return false;
538
539       case PN_NAME:
540         /*
541          * Because used/defn nodes appear in JSAtomLists and elsewhere, we
542          * don't recycle them. (We'll recover their storage when we free
543          * the temporary arena.) However, we do recycle the nodes around
544          * them, so clean up the pointers to avoid dangling references. The
545          * top-level decls table carries references to them that later
546          * iterations through the compileScript loop may find, so they need
547          * to be neat.
548          *
549          * pn_expr and pn_lexdef share storage; the latter isn't an owning
550          * reference.
551          */
552         if (!pn->pn_used) {
553             stack->pushUnlessNull(pn->pn_expr);
554             pn->pn_expr = NULL;
555         }
556         return !pn->pn_used && !pn->pn_defn;
557
558       case PN_LIST:
559         stack->pushList(pn);
560         break;
561       case PN_TERNARY:
562         stack->pushUnlessNull(pn->pn_kid1);
563         stack->pushUnlessNull(pn->pn_kid2);
564         stack->pushUnlessNull(pn->pn_kid3);
565         break;
566       case PN_BINARY:
567         if (pn->pn_left != pn->pn_right)
568             stack->pushUnlessNull(pn->pn_left);
569         stack->pushUnlessNull(pn->pn_right);
570         break;
571       case PN_UNARY:
572         stack->pushUnlessNull(pn->pn_kid);
573         break;
574       case PN_NULLARY:
575         /* 
576          * E4X function namespace nodes are PN_NULLARY, but can appear on use
577          * lists.
578          */
579         return !pn->pn_used && !pn->pn_defn;
580     }
581
582     return true;
583 }
584
585 /*
586  * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
587  * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
588  * metadata structures (the function box tree).
589  */
590 static void
591 PrepareNodeForMutation(JSParseNode *pn, JSTreeContext *tc)
592 {
593     if (pn->pn_arity != PN_NULLARY) {
594         if (pn->pn_arity == PN_FUNC) {
595             /*
596              * Since this node could be turned into anything, we can't
597              * ensure it won't be subsequently recycled, so we must
598              * disconnect it from the funbox tree entirely.
599              *
600              * Note that pn_funbox may legitimately be NULL. functionDef
601              * applies MakeDefIntoUse to definition nodes, which can come
602              * from prior iterations of the big loop in compileScript. In
603              * such cases, the defn nodes have been visited by the recycler
604              * (but not actually recycled!), and their funbox pointers
605              * cleared. But it's fine to mutate them into uses of some new
606              * definition.
607              */
608             if (pn->pn_funbox)
609                 pn->pn_funbox->node = NULL;
610         }
611
612         /* Put |pn|'s children (but not |pn| itself) on a work stack. */
613         NodeStack stack;
614         PushNodeChildren(pn, &stack);
615         /*
616          * For each node on the work stack, push its children on the work stack,
617          * and free the node if we can.
618          */
619         while (!stack.empty()) {
620             pn = stack.pop();
621             if (PushNodeChildren(pn, &stack))
622                 AddNodeToFreeList(pn, tc);
623         }
624     }
625 }
626
627 /*
628  * Return the nodes in the subtree |pn| to the parser's free node list, for
629  * reallocation.
630  *
631  * Note that all functions in |pn| that are not enclosed by other functions
632  * in |pn| must be direct children of |tc|, because we only clean up |tc|'s
633  * function and method lists. You must not reach into a function and
634  * recycle some part of it (unless you've updated |tc|->functionList, the
635  * way js_FoldConstants does).
636  */
637 static JSParseNode *
638 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
639 {
640     if (!pn)
641         return NULL;
642
643     JSParseNode *savedNext = pn->pn_next;
644
645     NodeStack stack;
646     for (;;) {
647         if (PushNodeChildren(pn, &stack))
648             AddNodeToFreeList(pn, tc);
649         if (stack.empty())
650             break;
651         pn = stack.pop();
652     }
653
654     return savedNext;
655 }
656
657 /*
658  * Allocate a JSParseNode from tc's node freelist or, failing that, from
659  * cx's temporary arena.
660  */
661 static JSParseNode *
662 NewOrRecycledNode(JSTreeContext *tc)
663 {
664     JSParseNode *pn;
665
666     pn = tc->parser->nodeList;
667     if (!pn) {
668         JSContext *cx = tc->parser->context;
669
670         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
671         if (!pn)
672             js_ReportOutOfScriptQuota(cx);
673     } else {
674         tc->parser->nodeList = pn->pn_next;
675     }
676
677     if (pn) {
678 #ifdef METER_PARSENODES
679         parsenodes++;
680         if (parsenodes - recyclednodes > maxparsenodes)
681             maxparsenodes = parsenodes - recyclednodes;
682 #endif
683         pn->pn_used = pn->pn_defn = false;
684         memset(&pn->pn_u, 0, sizeof pn->pn_u);
685         pn->pn_next = NULL;
686     }
687     return pn;
688 }
689
690 /* used only by static create methods of subclasses */
691
692 JSParseNode *
693 JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc)
694 {
695     JSParseNode *pn = NewOrRecycledNode(tc);
696     if (!pn)
697         return NULL;
698     const Token &tok = tc->parser->tokenStream.currentToken();
699     pn->init(tok.type, JSOP_NOP, arity);
700     pn->pn_pos = tok.pos;
701     return pn;
702 }
703
704 JSParseNode *
705 JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParseNode *right,
706                                JSTreeContext *tc)
707 {
708     JSParseNode *pn, *pn1, *pn2;
709
710     if (!left || !right)
711         return NULL;
712
713     /*
714      * Flatten a left-associative (left-heavy) tree of a given operator into
715      * a list, to reduce js_FoldConstants and js_EmitTree recursion.
716      */
717     if (PN_TYPE(left) == tt &&
718         PN_OP(left) == op &&
719         (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
720         if (left->pn_arity != PN_LIST) {
721             pn1 = left->pn_left, pn2 = left->pn_right;
722             left->pn_arity = PN_LIST;
723             left->pn_parens = false;
724             left->initList(pn1);
725             left->append(pn2);
726             if (tt == TOK_PLUS) {
727                 if (pn1->pn_type == TOK_STRING)
728                     left->pn_xflags |= PNX_STRCAT;
729                 else if (pn1->pn_type != TOK_NUMBER)
730                     left->pn_xflags |= PNX_CANTFOLD;
731                 if (pn2->pn_type == TOK_STRING)
732                     left->pn_xflags |= PNX_STRCAT;
733                 else if (pn2->pn_type != TOK_NUMBER)
734                     left->pn_xflags |= PNX_CANTFOLD;
735             }
736         }
737         left->append(right);
738         left->pn_pos.end = right->pn_pos.end;
739         if (tt == TOK_PLUS) {
740             if (right->pn_type == TOK_STRING)
741                 left->pn_xflags |= PNX_STRCAT;
742             else if (right->pn_type != TOK_NUMBER)
743                 left->pn_xflags |= PNX_CANTFOLD;
744         }
745         return left;
746     }
747
748     /*
749      * Fold constant addition immediately, to conserve node space and, what's
750      * more, so js_FoldConstants never sees mixed addition and concatenation
751      * operations with more than one leading non-string operand in a PN_LIST
752      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
753      * to "3pt", not "12pt").
754      */
755     if (tt == TOK_PLUS &&
756         left->pn_type == TOK_NUMBER &&
757         right->pn_type == TOK_NUMBER) {
758         left->pn_dval += right->pn_dval;
759         left->pn_pos.end = right->pn_pos.end;
760         RecycleTree(right, tc);
761         return left;
762     }
763
764     pn = NewOrRecycledNode(tc);
765     if (!pn)
766         return NULL;
767     pn->init(tt, op, PN_BINARY);
768     pn->pn_pos.begin = left->pn_pos.begin;
769     pn->pn_pos.end = right->pn_pos.end;
770     pn->pn_left = left;
771     pn->pn_right = right;
772     return (BinaryNode *)pn;
773 }
774
775 namespace js {
776
777 inline void
778 NameNode::initCommon(JSTreeContext *tc)
779 {
780     pn_expr = NULL;
781     pn_cookie.makeFree();
782     pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
783                 ? PND_BLOCKCHILD
784                 : 0;
785     pn_blockid = tc->blockid();
786 }
787
788 NameNode *
789 NameNode::create(JSAtom *atom, JSTreeContext *tc)
790 {
791     JSParseNode *pn;
792
793     pn = JSParseNode::create(PN_NAME, tc);
794     if (pn) {
795         pn->pn_atom = atom;
796         ((NameNode *)pn)->initCommon(tc);
797     }
798     return (NameNode *)pn;
799 }
800
801 } /* namespace js */
802
803 static bool
804 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
805 {
806     if (tc->blockidGen == JS_BIT(20)) {
807         JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
808                              JSMSG_NEED_DIET, "program");
809         return false;
810     }
811     blockid = tc->blockidGen++;
812     return true;
813 }
814
815 static bool
816 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
817 {
818     JS_ASSERT(tc->topStmt);
819     JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
820     JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
821     if (!GenerateBlockId(tc, tc->topStmt->blockid))
822         return false;
823     pn->pn_blockid = tc->topStmt->blockid;
824     return true;
825 }
826
827 /*
828  * Parse a top-level JS script.
829  */
830 JSParseNode *
831 Parser::parse(JSObject *chain)
832 {
833     /*
834      * Protect atoms from being collected by a GC activation, which might
835      * - nest on this thread due to out of memory (the so-called "last ditch"
836      *   GC attempted within js_NewGCThing), or
837      * - run for any reason on another thread if this thread is suspended on
838      *   an object lock before it finishes generating bytecode into a script
839      *   protected from the GC by a root or a stack frame reference.
840      */
841     JSTreeContext globaltc(this);
842     globaltc.setScopeChain(chain);
843     if (!GenerateBlockId(&globaltc, globaltc.bodyid))
844         return NULL;
845
846     JSParseNode *pn = statements();
847     if (pn) {
848         if (!tokenStream.matchToken(TOK_EOF)) {
849             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
850             pn = NULL;
851         } else {
852             if (!js_FoldConstants(context, pn, &globaltc))
853                 pn = NULL;
854         }
855     }
856     return pn;
857 }
858
859 JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
860
861 static inline bool
862 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
863 {
864     /*
865      * This is a lot simpler than error-checking every UpvarCookie::set, and
866      * practically speaking it leaves more than enough room for upvars.
867      */
868     if (UpvarCookie::isLevelReserved(staticLevel)) {
869         JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
870                              JSMSG_TOO_DEEP, js_function_str);
871         return false;
872     }
873     tc->staticLevel = staticLevel;
874     return true;
875 }
876
877 /*
878  * Compile a top-level script.
879  */
880 Compiler::Compiler(JSContext *cx, JSPrincipals *prin, JSStackFrame *cfp)
881   : parser(cx, prin, cfp)
882 {
883 }
884
885 JSScript *
886 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
887                         JSPrincipals *principals, uint32 tcflags,
888                         const jschar *chars, size_t length,
889                         const char *filename, uintN lineno, JSVersion version,
890                         JSString *source /* = NULL */,
891                         uintN staticLevel /* = 0 */)
892 {
893     JSArenaPool codePool, notePool;
894     TokenKind tt;
895     JSParseNode *pn;
896     JSScript *script;
897     bool inDirectivePrologue;
898 #ifdef METER_PARSENODES
899     void *sbrk(ptrdiff_t), *before = sbrk(0);
900 #endif
901
902     JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT |
903                             TCF_COMPILE_FOR_EVAL)));
904
905     /*
906      * The scripted callerFrame can only be given for compile-and-go scripts
907      * and non-zero static level requires callerFrame.
908      */
909     JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
910     JS_ASSERT_IF(staticLevel != 0, callerFrame);
911
912     Compiler compiler(cx, principals, callerFrame);
913     if (!compiler.init(chars, length, filename, lineno, version))
914         return NULL;
915
916     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
917                      &cx->scriptStackQuota);
918     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
919                      &cx->scriptStackQuota);
920
921     Parser &parser = compiler.parser;
922     TokenStream &tokenStream = parser.tokenStream;
923
924     JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
925     if (!cg.init())
926         return NULL;
927
928     MUST_FLOW_THROUGH("out");
929
930     // We can specialize a bit for the given scope chain if that scope chain is the global object.
931     JSObject *globalObj = scopeChain && scopeChain == scopeChain->getGlobal()
932                         ? scopeChain->getGlobal()
933                         : NULL;
934     js::GlobalScope globalScope(cx, globalObj, &cg);
935     if (globalObj) {
936         JS_ASSERT(globalObj->isNative());
937         JS_ASSERT((globalObj->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS);
938     }
939
940     /* Null script early in case of error, to reduce our code footprint. */
941     script = NULL;
942
943     globalScope.cg = &cg;
944     cg.flags |= tcflags;
945     cg.setScopeChain(scopeChain);
946     compiler.globalScope = &globalScope;
947     if (!SetStaticLevel(&cg, staticLevel))
948         goto out;
949
950     /* If this is a direct call to eval, inherit the caller's strictness.  */
951     if (callerFrame &&
952         callerFrame->isScriptFrame() &&
953         callerFrame->script()->strictModeCode) {
954         cg.flags |= TCF_STRICT_MODE_CODE;
955         tokenStream.setStrictMode();
956     }
957
958     /*
959      * If funbox is non-null after we create the new script, callerFrame->fun
960      * was saved in the 0th object table entry.
961      */
962     JSObjectBox *funbox;
963     funbox = NULL;
964
965     if (tcflags & TCF_COMPILE_N_GO) {
966         if (source) {
967             /*
968              * Save eval program source in script->atomMap.vector[0] for the
969              * eval cache (see EvalCacheLookup in jsobj.cpp).
970              */
971             JSAtom *atom = js_AtomizeString(cx, source, 0);
972             if (!atom || !cg.atomList.add(&parser, atom))
973                 goto out;
974         }
975
976         if (callerFrame && callerFrame->isFunctionFrame()) {
977             /*
978              * An eval script in a caller frame needs to have its enclosing
979              * function captured in case it refers to an upvar, and someone
980              * wishes to decompile it while it's running.
981              */
982             funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun()));
983             if (!funbox)
984                 goto out;
985             funbox->emitLink = cg.objectList.lastbox;
986             cg.objectList.lastbox = funbox;
987             cg.objectList.length++;
988         }
989     }
990
991     /*
992      * Inline this->statements to emit as we go to save AST space. We must
993      * generate our script-body blockid since we aren't calling Statements.
994      */
995     uint32 bodyid;
996     if (!GenerateBlockId(&cg, bodyid))
997         goto out;
998     cg.bodyid = bodyid;
999
1000 #if JS_HAS_XML_SUPPORT
1001     pn = NULL;
1002     bool onlyXML;
1003     onlyXML = true;
1004 #endif
1005
1006     inDirectivePrologue = true;
1007     tokenStream.setOctalCharacterEscape(false);
1008     for (;;) {
1009         tt = tokenStream.peekToken(TSF_OPERAND);
1010         if (tt <= TOK_EOF) {
1011             if (tt == TOK_EOF)
1012                 break;
1013             JS_ASSERT(tt == TOK_ERROR);
1014             goto out;
1015         }
1016
1017         pn = parser.statement();
1018         if (!pn)
1019             goto out;
1020         JS_ASSERT(!cg.blockNode);
1021
1022         if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
1023             goto out;
1024
1025         if (!js_FoldConstants(cx, pn, &cg))
1026             goto out;
1027
1028         if (!parser.analyzeFunctions(&cg))
1029             goto out;
1030         cg.functionList = NULL;
1031
1032         if (!js_EmitTree(cx, &cg, pn))
1033             goto out;
1034
1035 #if JS_HAS_XML_SUPPORT
1036         if (PN_TYPE(pn) != TOK_SEMI ||
1037             !pn->pn_kid ||
1038             !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) {
1039             onlyXML = false;
1040         }
1041 #endif
1042         RecycleTree(pn, &cg);
1043     }
1044
1045 #if JS_HAS_XML_SUPPORT
1046     /*
1047      * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
1048      * For background, see:
1049      *
1050      * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
1051      */
1052     if (pn && onlyXML && !callerFrame) {
1053         parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
1054         goto out;
1055     }
1056 #endif
1057
1058     /*
1059      * Global variables (gvars) share the atom index space with locals. Due to
1060      * incremental code generation we need to patch the bytecode to adjust the
1061      * local references to skip the globals.
1062      */
1063     if (cg.hasSharps()) {
1064         jsbytecode *code, *end;
1065         JSOp op;
1066         const JSCodeSpec *cs;
1067         uintN len, slot;
1068
1069         code = CG_BASE(&cg);
1070         for (end = code + CG_OFFSET(&cg); code != end; code += len) {
1071             JS_ASSERT(code < end);
1072             op = (JSOp) *code;
1073             cs = &js_CodeSpec[op];
1074             len = (cs->length > 0)
1075                   ? (uintN) cs->length
1076                   : js_GetVariableBytecodeLength(code);
1077             if ((cs->format & JOF_SHARPSLOT) ||
1078                 JOF_TYPE(cs->format) == JOF_LOCAL ||
1079                 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
1080                 /*
1081                  * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
1082                  * emitted only for a function.
1083                  */
1084                 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
1085                              (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
1086                              (op == JSOP_GETLOCALPROP));
1087                 slot = GET_SLOTNO(code);
1088                 if (!(cs->format & JOF_SHARPSLOT))
1089                     slot += cg.sharpSlots();
1090                 if (slot >= SLOTNO_LIMIT)
1091                     goto too_many_slots;
1092                 SET_SLOTNO(code, slot);
1093             }
1094         }
1095     }
1096
1097 #ifdef METER_PARSENODES
1098     printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
1099            (char *)sbrk(0) - (char *)before,
1100            parsenodes,
1101            maxparsenodes,
1102            parsenodes - recyclednodes);
1103     before = sbrk(0);
1104 #endif
1105
1106     /*
1107      * Nowadays the threaded interpreter needs a stop instruction, so we
1108      * do have to emit that here.
1109      */
1110     if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
1111         goto out;
1112
1113 #ifdef METER_PARSENODES
1114     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
1115            (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
1116 #endif
1117
1118 #ifdef JS_ARENAMETER
1119     JS_DumpArenaStats(stdout);
1120 #endif
1121
1122     JS_ASSERT(cg.version() == version);
1123
1124     script = JSScript::NewScriptFromCG(cx, &cg);
1125     if (script && funbox)
1126         script->savedCallerFun = true;
1127
1128 #ifdef JS_SCOPE_DEPTH_METER
1129     if (script) {
1130         JSObject *obj = scopeChain;
1131         uintN depth = 1;
1132         while ((obj = obj->getParent()) != NULL)
1133             ++depth;
1134         JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1135     }
1136 #endif
1137
1138     if (!defineGlobals(cx, globalScope, script))
1139         goto late_error;
1140
1141   out:
1142     JS_FinishArenaPool(&codePool);
1143     JS_FinishArenaPool(&notePool);
1144     return script;
1145
1146   too_many_slots:
1147     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1148     /* Fall through. */
1149
1150   late_error:
1151     if (script) {
1152         js_DestroyScript(cx, script);
1153         script = NULL;
1154     }
1155     goto out;
1156 }
1157
1158 bool
1159 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
1160 {
1161     if (!globalScope.defs.length())
1162         return true;
1163
1164     JSObject *globalObj = globalScope.globalObj;
1165
1166     /* Define and update global properties. */
1167     for (size_t i = 0; i < globalScope.defs.length(); i++) {
1168         GlobalScope::GlobalDef &def = globalScope.defs[i];
1169
1170         /* Names that could be resolved ahead of time can be skipped. */
1171         if (!def.atom)
1172             continue;
1173
1174         jsid id = ATOM_TO_JSID(def.atom);
1175         Value rval;
1176
1177         if (def.funbox) {
1178             JSFunction *fun = (JSFunction *)def.funbox->object;
1179
1180             /*
1181              * No need to check for redeclarations or anything, global
1182              * optimizations only take place if the property is not defined.
1183              */
1184             rval.setObject(*fun);
1185         } else {
1186             rval.setUndefined();
1187         }
1188
1189         JSProperty *prop;
1190
1191         if (!js_DefineNativeProperty(cx, globalObj, id, rval, PropertyStub, StrictPropertyStub,
1192                                      JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, &prop)) {
1193             return false;
1194         }
1195
1196         JS_ASSERT(prop);
1197         const Shape *shape = (const Shape *)prop;
1198         def.knownSlot = shape->slot;
1199     }
1200
1201     js::Vector<JSScript *, 16, ContextAllocPolicy> worklist(cx);
1202     if (!worklist.append(script))
1203         return false;
1204
1205     /*
1206      * Recursively walk through all scripts we just compiled. For each script,
1207      * go through all global uses. Each global use indexes into globalScope->defs.
1208      * Use this information to repoint each use to the correct slot in the global
1209      * object.
1210      */
1211     while (worklist.length()) {
1212         JSScript *inner = worklist.back();
1213         worklist.popBack();
1214
1215         if (JSScript::isValidOffset(inner->objectsOffset)) {
1216             JSObjectArray *arr = inner->objects();
1217             for (size_t i = 0; i < arr->length; i++) {
1218                 JSObject *obj = arr->vector[i];
1219                 if (!obj->isFunction())
1220                     continue;
1221                 JSFunction *fun = obj->getFunctionPrivate();
1222                 JS_ASSERT(fun->isInterpreted());
1223                 JSScript *inner = fun->u.i.script;
1224                 if (!JSScript::isValidOffset(inner->globalsOffset) &&
1225                     !JSScript::isValidOffset(inner->objectsOffset)) {
1226                     continue;
1227                 }
1228                 if (!worklist.append(inner))
1229                     return false;
1230             }
1231         }
1232
1233         if (!JSScript::isValidOffset(inner->globalsOffset))
1234             continue;
1235
1236         GlobalSlotArray *globalUses = inner->globals();
1237         uint32 nGlobalUses = globalUses->length;
1238         for (uint32 i = 0; i < nGlobalUses; i++) {
1239             uint32 index = globalUses->vector[i].slot;
1240             JS_ASSERT(index < globalScope.defs.length());
1241             globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
1242         }
1243     }
1244
1245     return true;
1246 }
1247
1248 /*
1249  * Insist on a final return before control flows out of pn.  Try to be a bit
1250  * smart about loops: do {...; return e2;} while(0) at the end of a function
1251  * that contains an early return e1 will get a strict warning.  Similarly for
1252  * iloops: while (true){...} is treated as though ... returns.
1253  */
1254 #define ENDS_IN_OTHER   0
1255 #define ENDS_IN_RETURN  1
1256 #define ENDS_IN_BREAK   2
1257
1258 static int
1259 HasFinalReturn(JSParseNode *pn)
1260 {
1261     JSParseNode *pn2, *pn3;
1262     uintN rv, rv2, hasDefault;
1263
1264     switch (pn->pn_type) {
1265       case TOK_LC:
1266         if (!pn->pn_head)
1267             return ENDS_IN_OTHER;
1268         return HasFinalReturn(pn->last());
1269
1270       case TOK_IF:
1271         if (!pn->pn_kid3)
1272             return ENDS_IN_OTHER;
1273         return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1274
1275       case TOK_WHILE:
1276         pn2 = pn->pn_left;
1277         if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1278             return ENDS_IN_RETURN;
1279         if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1280             return ENDS_IN_RETURN;
1281         return ENDS_IN_OTHER;
1282
1283       case TOK_DO:
1284         pn2 = pn->pn_right;
1285         if (pn2->pn_type == TOK_PRIMARY) {
1286             if (pn2->pn_op == JSOP_FALSE)
1287                 return HasFinalReturn(pn->pn_left);
1288             if (pn2->pn_op == JSOP_TRUE)
1289                 return ENDS_IN_RETURN;
1290         }
1291         if (pn2->pn_type == TOK_NUMBER) {
1292             if (pn2->pn_dval == 0)
1293                 return HasFinalReturn(pn->pn_left);
1294             return ENDS_IN_RETURN;
1295         }
1296         return ENDS_IN_OTHER;
1297
1298       case TOK_FOR:
1299         pn2 = pn->pn_left;
1300         if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1301             return ENDS_IN_RETURN;
1302         return ENDS_IN_OTHER;
1303
1304       case TOK_SWITCH:
1305         rv = ENDS_IN_RETURN;
1306         hasDefault = ENDS_IN_OTHER;
1307         pn2 = pn->pn_right;
1308         if (pn2->pn_type == TOK_LEXICALSCOPE)
1309             pn2 = pn2->expr();
1310         for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1311             if (pn2->pn_type == TOK_DEFAULT)
1312                 hasDefault = ENDS_IN_RETURN;
1313             pn3 = pn2->pn_right;
1314             JS_ASSERT(pn3->pn_type == TOK_LC);
1315             if (pn3->pn_head) {
1316                 rv2 = HasFinalReturn(pn3->last());
1317                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1318                     /* Falling through to next case or default. */;
1319                 else
1320                     rv &= rv2;
1321             }
1322         }
1323         /* If a final switch has no default case, we judge it harshly. */
1324         rv &= hasDefault;
1325         return rv;
1326
1327       case TOK_BREAK:
1328         return ENDS_IN_BREAK;
1329
1330       case TOK_WITH:
1331         return HasFinalReturn(pn->pn_right);
1332
1333       case TOK_RETURN:
1334         return ENDS_IN_RETURN;
1335
1336       case TOK_COLON:
1337       case TOK_LEXICALSCOPE:
1338         return HasFinalReturn(pn->expr());
1339
1340       case TOK_THROW:
1341         return ENDS_IN_RETURN;
1342
1343       case TOK_TRY:
1344         /* If we have a finally block that returns, we are done. */
1345         if (pn->pn_kid3) {
1346             rv = HasFinalReturn(pn->pn_kid3);
1347             if (rv == ENDS_IN_RETURN)
1348                 return rv;
1349         }
1350
1351         /* Else check the try block and any and all catch statements. */
1352         rv = HasFinalReturn(pn->pn_kid1);
1353         if (pn->pn_kid2) {
1354             JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1355             for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1356                 rv &= HasFinalReturn(pn2);
1357         }
1358         return rv;
1359
1360       case TOK_CATCH:
1361         /* Check this catch block's body. */
1362         return HasFinalReturn(pn->pn_kid3);
1363
1364       case TOK_LET:
1365         /* Non-binary let statements are let declarations. */
1366         if (pn->pn_arity != PN_BINARY)
1367             return ENDS_IN_OTHER;
1368         return HasFinalReturn(pn->pn_right);
1369
1370       default:
1371         return ENDS_IN_OTHER;
1372     }
1373 }
1374
1375 static JSBool
1376 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1377                 uintN anonerrnum)
1378 {
1379     JSAutoByteString name;
1380     if (tc->fun()->atom) {
1381         if (!js_AtomToPrintableString(cx, tc->fun()->atom, &name))
1382             return false;
1383     } else {
1384         errnum = anonerrnum;
1385     }
1386     return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name.ptr());
1387 }
1388
1389 static JSBool
1390 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1391 {
1392     JS_ASSERT(tc->inFunction());
1393     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1394            ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1395                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1396 }
1397
1398 /*
1399  * Check that it is permitted to assign to lhs.  Strict mode code may not
1400  * assign to 'eval' or 'arguments'.
1401  */
1402 bool
1403 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1404 {
1405     if (tc->needStrictChecks() && lhs->pn_type == TOK_NAME) {
1406         JSAtom *atom = lhs->pn_atom;
1407         JSAtomState *atomState = &cx->runtime->atomState;
1408         if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1409             JSAutoByteString name;
1410             if (!js_AtomToPrintableString(cx, atom, &name) ||
1411                 !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
1412                                        name.ptr())) {
1413                 return false;
1414             }
1415         }
1416     }
1417     return true;
1418 }
1419
1420 /*
1421  * Check that it is permitted to introduce a binding for atom.  Strict mode
1422  * forbids introducing new definitions for 'eval', 'arguments', or for any
1423  * strict mode reserved keyword.  Use pn for reporting error locations, or use
1424  * tc's token stream if pn is NULL.
1425  */
1426 bool
1427 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
1428 {
1429     if (!tc->needStrictChecks())
1430         return true;
1431
1432     JSAtomState *atomState = &cx->runtime->atomState;
1433     if (atom == atomState->evalAtom ||
1434         atom == atomState->argumentsAtom ||
1435         FindKeyword(atom->charsZ(), atom->length()))
1436     {
1437         JSAutoByteString name;
1438         if (!js_AtomToPrintableString(cx, atom, &name))
1439             return false;
1440         return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name.ptr());
1441     }
1442
1443     return true;
1444 }
1445
1446 static bool
1447 ReportBadParameter(JSContext *cx, JSTreeContext *tc, JSAtom *name, uintN errorNumber)
1448 {
1449     JSDefinition *dn = ALE_DEFN(tc->decls.lookup(name));
1450     JSAutoByteString bytes;
1451     return js_AtomToPrintableString(cx, name, &bytes) &&
1452            ReportStrictModeError(cx, TS(tc->parser), tc, dn, errorNumber, bytes.ptr());
1453 }
1454
1455 /*
1456  * In strict mode code, all parameter names must be distinct, must not be
1457  * strict mode reserved keywords, and must not be 'eval' or 'arguments'.  We
1458  * must perform these checks here, and not eagerly during parsing, because a
1459  * function's body may turn on strict mode for the function head.
1460  */
1461 static bool
1462 CheckStrictParameters(JSContext *cx, JSTreeContext *tc)
1463 {
1464     JS_ASSERT(tc->inFunction());
1465
1466     if (!tc->needStrictChecks() || tc->bindings.countArgs() == 0)
1467         return true;
1468
1469     JSAtom *argumentsAtom = cx->runtime->atomState.argumentsAtom;
1470     JSAtom *evalAtom = cx->runtime->atomState.evalAtom;
1471
1472     /* name => whether we've warned about the name already */
1473     HashMap<JSAtom *, bool> parameters(cx);
1474     if (!parameters.init(tc->bindings.countArgs()))
1475         return false;
1476
1477     /* Start with lastVariable(), not lastArgument(), for destructuring. */
1478     for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
1479         jsid id = r.front().id;
1480         if (!JSID_IS_ATOM(id))
1481             continue;
1482
1483         JSAtom *name = JSID_TO_ATOM(id);
1484
1485         if (name == argumentsAtom || name == evalAtom) {
1486             if (!ReportBadParameter(cx, tc, name, JSMSG_BAD_BINDING))
1487                 return false;
1488         }
1489
1490         if (tc->inStrictMode() && FindKeyword(name->charsZ(), name->length())) {
1491             /*
1492              * JSOPTION_STRICT is supposed to warn about future keywords, too,
1493              * but we took care of that in the scanner.
1494              */
1495             JS_ALWAYS_TRUE(!ReportBadParameter(cx, tc, name, JSMSG_RESERVED_ID));
1496             return false;
1497         }
1498
1499         /*
1500          * Check for a duplicate parameter: warn or report an error exactly
1501          * once for each duplicated parameter.
1502          */
1503         if (HashMap<JSAtom *, bool>::AddPtr p = parameters.lookupForAdd(name)) {
1504             if (!p->value && !ReportBadParameter(cx, tc, name, JSMSG_DUPLICATE_FORMAL))
1505                 return false;
1506             p->value = true;
1507         } else {
1508             if (!parameters.add(p, name, false))
1509                 return false;
1510         }
1511     }
1512
1513     return true;
1514 }
1515
1516 JSParseNode *
1517 Parser::functionBody()
1518 {
1519     JSStmtInfo stmtInfo;
1520     uintN oldflags, firstLine;
1521     JSParseNode *pn;
1522
1523     JS_ASSERT(tc->inFunction());
1524     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1525     stmtInfo.flags = SIF_BODY_BLOCK;
1526
1527     oldflags = tc->flags;
1528     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1529
1530     /*
1531      * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1532      * later, because we may have not peeked in tokenStream yet, so statements
1533      * won't acquire a valid pn->pn_pos.begin from the current token.
1534      */
1535     firstLine = tokenStream.getLineno();
1536 #if JS_HAS_EXPR_CLOSURES
1537     if (tokenStream.currentToken().type == TOK_LC) {
1538         pn = statements();
1539     } else {
1540         pn = UnaryNode::create(tc);
1541         if (pn) {
1542             pn->pn_kid = assignExpr();
1543             if (!pn->pn_kid) {
1544                 pn = NULL;
1545             } else {
1546                 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1547                     ReportBadReturn(context, tc, JSREPORT_ERROR,
1548                                     JSMSG_BAD_GENERATOR_RETURN,
1549                                     JSMSG_BAD_ANON_GENERATOR_RETURN);
1550                     pn = NULL;
1551                 } else {
1552                     pn->pn_type = TOK_RETURN;
1553                     pn->pn_op = JSOP_RETURN;
1554                     pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1555                 }
1556             }
1557         }
1558     }
1559 #else
1560     pn = statements();
1561 #endif
1562
1563     if (pn) {
1564         JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1565         js_PopStatement(tc);
1566         pn->pn_pos.begin.lineno = firstLine;
1567
1568         /* Check for falling off the end of a function that returns a value. */
1569         if (context->hasStrictOption() && (tc->flags & TCF_RETURN_EXPR) &&
1570             !CheckFinalReturn(context, tc, pn)) {
1571             pn = NULL;
1572         }
1573     }
1574
1575     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1576     return pn;
1577 }
1578
1579 static JSAtomListElement *
1580 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1581 {
1582     JSAtomListElement *ale = tc->lexdeps.add(tc->parser, pn->pn_atom);
1583     if (!ale)
1584         return NULL;
1585
1586     JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
1587     if (!dn)
1588         return NULL;
1589
1590     ALE_SET_DEFN(ale, dn);
1591     dn->pn_type = TOK_NAME;
1592     dn->pn_op = JSOP_NOP;
1593     dn->pn_defn = true;
1594     dn->pn_dflags |= PND_PLACEHOLDER;
1595     return ale;
1596 }
1597
1598 static bool
1599 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1600 {
1601     JS_ASSERT(!pn->pn_used);
1602     JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1603
1604     JSHashEntry **hep;
1605     JSAtomListElement *ale = NULL;
1606     JSAtomList *list = NULL;
1607
1608     if (let)
1609         ale = (list = &tc->decls)->rawLookup(atom, hep);
1610     if (!ale)
1611         ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1612
1613     if (ale) {
1614         JSDefinition *dn = ALE_DEFN(ale);
1615         if (dn != pn) {
1616             JSParseNode **pnup = &dn->dn_uses;
1617             JSParseNode *pnu;
1618             uintN start = let ? pn->pn_blockid : tc->bodyid;
1619
1620             while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1621                 JS_ASSERT(pnu->pn_used);
1622                 pnu->pn_lexdef = (JSDefinition *) pn;
1623                 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1624                 pnup = &pnu->pn_link;
1625             }
1626
1627             if (pnu != dn->dn_uses) {
1628                 *pnup = pn->dn_uses;
1629                 pn->dn_uses = dn->dn_uses;
1630                 dn->dn_uses = pnu;
1631
1632                 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1633                     list->rawRemove(tc->parser, ale, hep);
1634             }
1635         }
1636     }
1637
1638     ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1639     if (!ale)
1640         return false;
1641     ALE_SET_DEFN(ale, pn);
1642     pn->pn_defn = true;
1643     pn->pn_dflags &= ~PND_PLACEHOLDER;
1644     if (!tc->parent)
1645         pn->pn_dflags |= PND_TOPLEVEL;
1646     return true;
1647 }
1648
1649 static void
1650 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1651 {
1652     JS_ASSERT(!pn->pn_used);
1653     JS_ASSERT(!pn->pn_defn);
1654     JS_ASSERT(pn != dn->dn_uses);
1655     pn->pn_link = dn->dn_uses;
1656     dn->dn_uses = pn;
1657     dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1658     pn->pn_used = true;
1659     pn->pn_lexdef = dn;
1660 }
1661
1662 static void
1663 ForgetUse(JSParseNode *pn)
1664 {
1665     if (!pn->pn_used) {
1666         JS_ASSERT(!pn->pn_defn);
1667         return;
1668     }
1669
1670     JSParseNode **pnup = &pn->lexdef()->dn_uses;
1671     JSParseNode *pnu;
1672     while ((pnu = *pnup) != pn)
1673         pnup = &pnu->pn_link;
1674     *pnup = pn->pn_link;
1675     pn->pn_used = false;
1676 }
1677
1678 static JSParseNode *
1679 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1680 {
1681     JSParseNode *lhs = NewOrRecycledNode(tc);
1682     if (!lhs)
1683         return NULL;
1684     *lhs = *pn;
1685
1686     if (pn->pn_used) {
1687         JSDefinition *dn = pn->pn_lexdef;
1688         JSParseNode **pnup = &dn->dn_uses;
1689
1690         while (*pnup != pn)
1691             pnup = &(*pnup)->pn_link;
1692         *pnup = lhs;
1693         lhs->pn_link = pn->pn_link;
1694         pn->pn_link = NULL;
1695     }
1696
1697     pn->pn_type = TOK_ASSIGN;
1698     pn->pn_op = JSOP_NOP;
1699     pn->pn_arity = PN_BINARY;
1700     pn->pn_parens = false;
1701     pn->pn_used = pn->pn_defn = false;
1702     pn->pn_left = lhs;
1703     pn->pn_right = rhs;
1704     return lhs;
1705 }
1706
1707 static JSParseNode *
1708 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1709 {
1710     /*
1711      * If dn is arg, or in [var, const, let] and has an initializer, then we
1712      * must rewrite it to be an assignment node, whose freshly allocated
1713      * left-hand side becomes a use of pn.
1714      */
1715     if (dn->isBindingForm()) {
1716         JSParseNode *rhs = dn->expr();
1717         if (rhs) {
1718             JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1719             if (!lhs)
1720                 return NULL;
1721             //pn->dn_uses = lhs;
1722             dn = (JSDefinition *) lhs;
1723         }
1724
1725         dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1726     } else if (dn->kind() == JSDefinition::FUNCTION) {
1727         JS_ASSERT(dn->pn_op == JSOP_NOP);
1728         PrepareNodeForMutation(dn, tc);
1729         dn->pn_type = TOK_NAME;
1730         dn->pn_arity = PN_NAME;
1731         dn->pn_atom = atom;
1732     }
1733
1734     /* Now make dn no longer a definition, rather a use of pn. */
1735     JS_ASSERT(dn->pn_type == TOK_NAME);
1736     JS_ASSERT(dn->pn_arity == PN_NAME);
1737     JS_ASSERT(dn->pn_atom == atom);
1738
1739     for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1740         JS_ASSERT(pnu->pn_used);
1741         JS_ASSERT(!pnu->pn_defn);
1742         pnu->pn_lexdef = (JSDefinition *) pn;
1743         pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1744     }
1745     pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1746     pn->dn_uses = dn;
1747
1748     dn->pn_defn = false;
1749     dn->pn_used = true;
1750     dn->pn_lexdef = (JSDefinition *) pn;
1751     dn->pn_cookie.makeFree();
1752     dn->pn_dflags &= ~PND_BOUND;
1753     return dn;
1754 }
1755
1756 static bool
1757 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1758 {
1759     JSParseNode *argpn, *argsbody;
1760
1761     /* Flag tc so we don't have to lookup arguments on every use. */
1762     if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1763         tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1764
1765     /*
1766      * Make an argument definition node, distinguished by being in tc->decls
1767      * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1768      * list node returned via pn->pn_body.
1769      */
1770     argpn = NameNode::create(atom, tc);
1771     if (!argpn)
1772         return false;
1773     JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1774
1775     /* Arguments are initialized by definition. */
1776     argpn->pn_dflags |= PND_INITIALIZED;
1777     if (!Define(argpn, atom, tc))
1778         return false;
1779
1780     argsbody = pn->pn_body;
1781     if (!argsbody) {
1782         argsbody = ListNode::create(tc);
1783         if (!argsbody)
1784             return false;
1785         argsbody->pn_type = TOK_ARGSBODY;
1786         argsbody->pn_op = JSOP_NOP;
1787         argsbody->makeEmpty();
1788         pn->pn_body = argsbody;
1789     }
1790     argsbody->append(argpn);
1791
1792     argpn->pn_op = JSOP_GETARG;
1793     argpn->pn_cookie.set(tc->staticLevel, i);
1794     argpn->pn_dflags |= PND_BOUND;
1795     return true;
1796 }
1797
1798 /*
1799  * Compile a JS function body, which might appear as the value of an event
1800  * handler attribute in an HTML <INPUT> tag.
1801  */
1802 bool
1803 Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1804                               Bindings *bindings, const jschar *chars, size_t length,
1805                               const char *filename, uintN lineno, JSVersion version)
1806 {
1807     Compiler compiler(cx, principals);
1808
1809     if (!compiler.init(chars, length, filename, lineno, version))
1810         return false;
1811
1812     /* No early return from after here until the js_FinishArenaPool calls. */
1813     JSArenaPool codePool, notePool;
1814     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1815                      &cx->scriptStackQuota);
1816     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
1817                      &cx->scriptStackQuota);
1818
1819     Parser &parser = compiler.parser;
1820     TokenStream &tokenStream = parser.tokenStream;
1821
1822     JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
1823     if (!funcg.init())
1824         return NULL;
1825
1826     funcg.flags |= TCF_IN_FUNCTION;
1827     funcg.setFunction(fun);
1828     funcg.bindings.transfer(cx, bindings);
1829     fun->setArgCount(funcg.bindings.countArgs());
1830     if (!GenerateBlockId(&funcg, funcg.bodyid))
1831         return NULL;
1832
1833     /* FIXME: make Function format the source for a function definition. */
1834     tokenStream.mungeCurrentToken(TOK_NAME);
1835     JSParseNode *fn = FunctionNode::create(&funcg);
1836     if (fn) {
1837         fn->pn_body = NULL;
1838         fn->pn_cookie.makeFree();
1839
1840         uintN nargs = fun->nargs;
1841         if (nargs) {
1842             /*
1843              * NB: do not use AutoLocalNameArray because it will release space
1844              * allocated from cx->tempPool by DefineArg.
1845              */
1846             jsuword *names = funcg.bindings.getLocalNameArray(cx, &cx->tempPool);
1847             if (!names) {
1848                 fn = NULL;
1849             } else {
1850                 for (uintN i = 0; i < nargs; i++) {
1851                     JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1852                     if (!DefineArg(fn, name, i, &funcg)) {
1853                         fn = NULL;
1854                         break;
1855                     }
1856                 }
1857             }
1858         }
1859     }
1860
1861     /*
1862      * Farble the body so that it looks like a block statement to js_EmitTree,
1863      * which is called from js_EmitFunctionBody (see jsemit.cpp).  After we're
1864      * done parsing, we must fold constants, analyze any nested functions, and
1865      * generate code for this function, including a stop opcode at the end.
1866      */
1867     tokenStream.mungeCurrentToken(TOK_LC);
1868     JSParseNode *pn = fn ? parser.functionBody() : NULL;
1869     if (pn) {
1870         if (!CheckStrictParameters(cx, &funcg)) {
1871             pn = NULL;
1872         } else if (!tokenStream.matchToken(TOK_EOF)) {
1873             parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1874             pn = NULL;
1875         } else if (!js_FoldConstants(cx, pn, &funcg)) {
1876             /* js_FoldConstants reported the error already. */
1877             pn = NULL;
1878         } else if (!parser.analyzeFunctions(&funcg)) {
1879             pn = NULL;
1880         } else {
1881             if (fn->pn_body) {
1882                 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1883                 fn->pn_body->append(pn);
1884                 fn->pn_body->pn_pos = pn->pn_pos;
1885                 pn = fn->pn_body;
1886             }
1887
1888             if (!js_EmitFunctionScript(cx, &funcg, pn))
1889                 pn = NULL;
1890         }
1891     }
1892
1893     /* Restore saved state and release code generation arenas. */
1894     JS_FinishArenaPool(&codePool);
1895     JS_FinishArenaPool(&notePool);
1896     return pn != NULL;
1897 }
1898
1899 /*
1900  * Parameter block types for the several Binder functions.  We use a common
1901  * helper function signature in order to share code among destructuring and
1902  * simple variable declaration parsers.  In the destructuring case, the binder
1903  * function is called indirectly from the variable declaration parser by way
1904  * of CheckDestructuring and its friends.
1905  */
1906 typedef JSBool
1907 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1908
1909 struct BindData {
1910     BindData() : fresh(true) {}
1911
1912     JSParseNode     *pn;        /* name node for definition processing and
1913                                    error source coordinates */
1914     JSOp            op;         /* prolog bytecode or nop */
1915     Binder          binder;     /* binder, discriminates u */
1916     union {
1917         struct {
1918             uintN   overflow;
1919         } let;
1920     };
1921     bool fresh;
1922 };
1923
1924 static bool
1925 BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSAtom *atom, BindingKind kind, bool isArg)
1926 {
1927     JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
1928
1929     /*
1930      * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1931      * Instead 'var arguments' always restates the predefined property of the
1932      * activation objects whose name is 'arguments'. Assignment to such a
1933      * variable must be handled specially.
1934      *
1935      * Special case: an argument named 'arguments' *does* shadow the predefined
1936      * arguments property.
1937      */
1938     if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1939         return true;
1940
1941     return tc->bindings.add(cx, atom, kind);
1942 }
1943
1944 #if JS_HAS_DESTRUCTURING
1945 static JSBool
1946 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1947 {
1948     /* Flag tc so we don't have to lookup arguments on every use. */
1949     if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1950         tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1951
1952     JS_ASSERT(tc->inFunction());
1953
1954     /*
1955      * NB: Check tc->decls rather than tc->bindings, because destructuring
1956      *     bindings aren't added to tc->bindings until after all arguments have
1957      *     been parsed.
1958      */
1959     if (tc->decls.lookup(atom)) {
1960         ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
1961                                  JSMSG_DESTRUCT_DUP_ARG);
1962         return JS_FALSE;
1963     }
1964
1965     JSParseNode *pn = data->pn;
1966
1967     /*
1968      * Distinguish destructured-to binding nodes as vars, not args, by setting
1969      * pn_op to JSOP_SETLOCAL. Parser::functionDef checks for this pn_op value
1970      * when processing the destructuring-assignment AST prelude induced by such
1971      * destructuring args in Parser::functionArguments.
1972      *
1973      * We must set the PND_BOUND flag too to prevent pn_op from being reset to
1974      * JSOP_SETNAME by BindDestructuringVar. The only field not initialized is
1975      * pn_cookie; it gets set in functionDef in the first "if (prelude)" block.
1976      * We have to wait to set the cookie until we can call JSFunction::addLocal
1977      * with kind = JSLOCAL_VAR, after all JSLOCAL_ARG locals have been added.
1978      *
1979      * Thus a destructuring formal parameter binds an ARG (as in arguments[i]
1980      * element) with a null atom name for the object or array passed in to be
1981      * destructured, and zero or more VARs (as in named local variables) for
1982      * the destructured-to identifiers in the property value positions within
1983      * the object or array destructuring pattern, and all ARGs for the formal
1984      * parameter list bound as locals before any VAR for a destructured name.
1985      */
1986     pn->pn_op = JSOP_SETLOCAL;
1987     pn->pn_dflags |= PND_BOUND;
1988
1989     return Define(pn, atom, tc);
1990 }
1991 #endif /* JS_HAS_DESTRUCTURING */
1992
1993 JSFunction *
1994 Parser::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1995 {
1996     JSObject *parent;
1997     JSFunction *fun;
1998
1999     JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
2000
2001     /*
2002      * Find the global compilation context in order to pre-set the newborn
2003      * function's parent slot to tc->scopeChain. If the global context is a
2004      * compile-and-go one, we leave the pre-set parent intact; otherwise we
2005      * clear parent and proto.
2006      */
2007     while (tc->parent)
2008         tc = tc->parent;
2009     parent = tc->inFunction() ? NULL : tc->scopeChain();
2010
2011     fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, parent, atom);
2012     if (fun && !tc->compileAndGo()) {
2013         FUN_OBJECT(fun)->clearParent();
2014         FUN_OBJECT(fun)->clearProto();
2015     }
2016     return fun;
2017 }
2018
2019 static JSBool
2020 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
2021 {
2022     TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
2023     if (tt == TOK_ERROR)
2024         return JS_FALSE;
2025     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2026         /* Advance the scanner for proper error location reporting. */
2027         ts->getToken(TSF_OPERAND);
2028         ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
2029         return JS_FALSE;
2030     }
2031     (void) ts->matchToken(TOK_SEMI);
2032     return JS_TRUE;
2033 }
2034
2035 bool
2036 Parser::analyzeFunctions(JSTreeContext *tc)
2037 {
2038     cleanFunctionList(&tc->functionList);
2039     if (!tc->functionList)
2040         return true;
2041     if (!markFunArgs(tc->functionList))
2042         return false;
2043     setFunctionKinds(tc->functionList, &tc->flags);
2044     return true;
2045 }
2046
2047 /*
2048  * Mark as funargs any functions that reach up to one or more upvars across an
2049  * already-known funarg. The parser will flag the o_m lambda as a funarg in:
2050  *
2051  *   function f(o, p) {
2052  *       o.m = function o_m(a) {
2053  *           function g() { return p; }
2054  *           function h() { return a; }
2055  *           return g() + h();
2056  *       }
2057  *   }
2058  *
2059  * but without this extra marking phase, function g will not be marked as a
2060  * funarg since it is called from within its parent scope. But g reaches up to
2061  * f's parameter p, so if o_m escapes f's activation scope, g does too and
2062  * cannot assume that p's stack slot is still alive. In contast function h
2063  * neither escapes nor uses an upvar "above" o_m's level.
2064  *
2065  * If function g itself contained lambdas that contained non-lambdas that reach
2066  * up above its level, then those non-lambdas would have to be marked too. This
2067  * process is potentially exponential in the number of functions, but generally
2068  * not so complex. But it can't be done during a single recursive traversal of
2069  * the funbox tree, so we must use a work queue.
2070  *
2071  * Return the minimal "skipmin" for funbox and its siblings. This is the delta
2072  * between the static level of the bodies of funbox and its peers (which must
2073  * be funbox->level + 1), and the static level of the nearest upvar among all
2074  * the upvars contained by funbox and its peers. If there are no upvars, return
2075  * FREE_STATIC_LEVEL. Thus this function never returns 0.
2076  */
2077 static uintN
2078 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
2079 {
2080     uintN allskipmin = UpvarCookie::FREE_LEVEL;
2081
2082     do {
2083         JSParseNode *fn = funbox->node;
2084         JS_ASSERT(fn->pn_arity == PN_FUNC);
2085         JSFunction *fun = (JSFunction *) funbox->object;
2086         int fnlevel = level;
2087
2088         /*
2089          * An eval can leak funbox, functions along its ancestor line, and its
2090          * immediate kids. Since FindFunArgs uses DFS and the parser propagates
2091          * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
2092          * already been marked as funargs by this point. Therefore we have to
2093          * flag only funbox->node and funbox->kids' nodes here.
2094          *
2095          * Generators need to be treated in the same way. Even if the value
2096          * of a generator function doesn't escape, anything defined or referred
2097          * to inside the generator can escape through a call to the generator.
2098          * We could imagine doing static analysis to track the calls and see
2099          * if any iterators or values returned by iterators escape, but that
2100          * would be hard, so instead we just assume everything might escape.
2101          */
2102         if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) {
2103             fn->setFunArg();
2104             for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
2105                 kid->node->setFunArg();
2106         }
2107
2108         /*
2109          * Compute in skipmin the least distance from fun's static level up to
2110          * an upvar, whether used directly by fun, or indirectly by a function
2111          * nested in fun.
2112          */
2113         uintN skipmin = UpvarCookie::FREE_LEVEL;
2114         JSParseNode *pn = fn->pn_body;
2115
2116         if (pn->pn_type == TOK_UPVARS) {
2117             JSAtomList upvars(pn->pn_names);
2118             JS_ASSERT(upvars.count != 0);
2119
2120             JSAtomListIterator iter(&upvars);
2121             JSAtomListElement *ale;
2122
2123             while ((ale = iter()) != NULL) {
2124                 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2125
2126                 if (!lexdep->isFreeVar()) {
2127                     uintN upvarLevel = lexdep->frameLevel();
2128
2129                     if (int(upvarLevel) <= fnlevel)
2130                         fn->setFunArg();
2131
2132                     uintN skip = (funbox->level + 1) - upvarLevel;
2133                     if (skip < skipmin)
2134                         skipmin = skip;
2135                 }
2136             }
2137         }
2138
2139         /*
2140          * If this function escapes, whether directly (the parser detects such
2141          * escapes) or indirectly (because this non-escaping function uses an
2142          * upvar that reaches across an outer function boundary where the outer
2143          * function escapes), enqueue it for further analysis, and bump fnlevel
2144          * to trap any non-escaping children.
2145          */
2146         if (fn->isFunArg()) {
2147             queue->push(funbox);
2148             fnlevel = int(funbox->level);
2149         }
2150
2151         /*
2152          * Now process the current function's children, and recalibrate their
2153          * cumulative skipmin to be relative to the current static level.
2154          */
2155         if (funbox->kids) {
2156             uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
2157
2158             JS_ASSERT(kidskipmin != 0);
2159             if (kidskipmin != UpvarCookie::FREE_LEVEL) {
2160                 --kidskipmin;
2161                 if (kidskipmin != 0 && kidskipmin < skipmin)
2162                     skipmin = kidskipmin;
2163             }
2164         }
2165
2166         /*
2167          * Finally, after we've traversed all of the current function's kids,
2168          * minimize fun's skipmin against our accumulated skipmin. Do likewise
2169          * with allskipmin, but minimize across funbox and all of its siblings,
2170          * to compute our return value.
2171          */
2172         if (skipmin != UpvarCookie::FREE_LEVEL) {
2173             fun->u.i.skipmin = skipmin;
2174             if (skipmin < allskipmin)
2175                 allskipmin = skipmin;
2176         }
2177     } while ((funbox = funbox->siblings) != NULL);
2178
2179     return allskipmin;
2180 }
2181
2182 bool
2183 Parser::markFunArgs(JSFunctionBox *funbox)
2184 {
2185     JSFunctionBoxQueue queue;
2186     if (!queue.init(functionCount))
2187         return false;
2188
2189     FindFunArgs(funbox, -1, &queue);
2190     while ((funbox = queue.pull()) != NULL) {
2191         JSParseNode *fn = funbox->node;
2192         JS_ASSERT(fn->isFunArg());
2193
2194         JSParseNode *pn = fn->pn_body;
2195         if (pn->pn_type == TOK_UPVARS) {
2196             JSAtomList upvars(pn->pn_names);
2197             JS_ASSERT(upvars.count != 0);
2198
2199             JSAtomListIterator iter(&upvars);
2200             JSAtomListElement *ale;
2201
2202             while ((ale = iter()) != NULL) {
2203                 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2204
2205                 if (!lexdep->isFreeVar() &&
2206                     !lexdep->isFunArg() &&
2207                     (lexdep->kind() == JSDefinition::FUNCTION ||
2208                      PN_OP(lexdep) == JSOP_CALLEE)) {
2209                     /*
2210                      * Mark this formerly-Algol-like function as an escaping
2211                      * function (i.e., as a funarg), because it is used from
2212                      * another funarg.
2213                      *
2214                      * Progress is guaranteed because we set the funarg flag
2215                      * here, which suppresses revisiting this function (thanks
2216                      * to the !lexdep->isFunArg() test just above).
2217                      */
2218                     lexdep->setFunArg();
2219
2220                     JSFunctionBox *afunbox;
2221                     if (PN_OP(lexdep) == JSOP_CALLEE) {
2222                         /*
2223                          * A named function expression will not appear to be a
2224                          * funarg if it is immediately applied. However, if its
2225                          * name is used in an escaping function nested within
2226                          * it, then it must become flagged as a funarg again.
2227                          * See bug 545980.
2228                          */
2229                         afunbox = funbox;
2230                         uintN calleeLevel = lexdep->pn_cookie.level();
2231                         uintN staticLevel = afunbox->level + 1U;
2232                         while (staticLevel != calleeLevel) {
2233                             afunbox = afunbox->parent;
2234                             --staticLevel;
2235                         }
2236                         JS_ASSERT(afunbox->level + 1U == calleeLevel);
2237                         afunbox->node->setFunArg();
2238                     } else {
2239                        afunbox = lexdep->pn_funbox;
2240                     }
2241                     queue.push(afunbox);
2242
2243                     /*
2244                      * Walk over nested functions again, now that we have
2245                      * changed the level across which it is unsafe to access
2246                      * upvars using the runtime dynamic link (frame chain).
2247                      */
2248                     if (afunbox->kids)
2249                         FindFunArgs(afunbox->kids, afunbox->level, &queue);
2250                 }
2251             }
2252         }
2253     }
2254     return true;
2255 }
2256
2257 static uint32
2258 MinBlockId(JSParseNode *fn, uint32 id)
2259 {
2260     if (fn->pn_blockid < id)
2261         return false;
2262     if (fn->pn_defn) {
2263         for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
2264             if (pn->pn_blockid < id)
2265                 return false;
2266         }
2267     }
2268     return true;
2269 }
2270
2271 static inline bool
2272 CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
2273 {
2274     /*
2275      * Consider the current function (the lambda, innermost below) using a var
2276      * x defined two static levels up:
2277      *
2278      *  function f() {
2279      *      // z = g();
2280      *      var x = 42;
2281      *      function g() {
2282      *          return function () { return x; };
2283      *      }
2284      *      return g();
2285      *  }
2286      *
2287      * So long as (1) the initialization in 'var x = 42' dominates all uses of
2288      * g and (2) x is not reassigned, it is safe to optimize the lambda to a
2289      * flat closure. Uncommenting the early call to g makes this optimization
2290      * unsafe (z could name a global setter that calls its argument).
2291      */
2292     JSFunctionBox *afunbox = funbox;
2293     uintN dnLevel = dn->frameLevel();
2294
2295     JS_ASSERT(dnLevel <= funbox->level);
2296     while (afunbox->level != dnLevel) {
2297         afunbox = afunbox->parent;
2298
2299         /*
2300          * NB: afunbox can't be null because we are sure to find a function box
2301          * whose level == dnLevel before we would try to walk above the root of
2302          * the funbox tree. See bug 493260 comments 16-18.
2303          *
2304          * Assert but check anyway, to protect future changes that bind eval
2305          * upvars in the parser.
2306          */
2307         JS_ASSERT(afunbox);
2308
2309         /*
2310          * If this function is reaching up across an enclosing funarg, then we
2311          * cannot copy dn's value into a flat closure slot (the display stops
2312          * working once the funarg escapes).
2313          */
2314         if (!afunbox || afunbox->node->isFunArg())
2315             return false;
2316
2317         /*
2318          * Reaching up for dn across a generator also means we can't flatten,
2319          * since the generator iterator does not run until later, in general.
2320          * See bug 563034.
2321          */
2322         if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
2323             return false;
2324     }
2325
2326     /*
2327      * If afunbox's function (which is at the same level as dn) is in a loop,
2328      * pessimistically assume the variable initializer may be in the same loop.
2329      * A flat closure would then be unsafe, as the captured variable could be
2330      * assigned after the closure is created. See bug 493232.
2331      */
2332     if (afunbox->inLoop)
2333         return false;
2334
2335     /*
2336      * |with| and eval used as an operator defeat lexical scoping: they can be
2337      * used to assign to any in-scope variable. Therefore they must disable
2338      * flat closures that use such upvars.  The parser detects these as special
2339      * forms and marks the function heavyweight.
2340      */
2341     if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
2342         return false;
2343
2344     /*
2345      * If afunbox's function is not a lambda, it will be hoisted, so it could
2346      * capture the undefined value that by default initializes var, let, and
2347      * const bindings. And if dn is a function that comes at (meaning a
2348      * function refers to its own name) or strictly after afunbox, we also
2349      * defeat the flat closure optimization for this dn.
2350      */
2351     JSFunction *afun = (JSFunction *) afunbox->object;
2352     if (!(afun->flags & JSFUN_LAMBDA)) {
2353         if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
2354             return false;
2355     }
2356
2357     if (!dn->isInitialized())
2358         return false;
2359
2360     JSDefinition::Kind dnKind = dn->kind();
2361     if (dnKind != JSDefinition::CONST) {
2362         if (dn->isAssigned())
2363             return false;
2364
2365         /*
2366          * Any formal could be mutated behind our back via the arguments
2367          * object, so deoptimize if the outer function uses arguments.
2368          *
2369          * In a Function constructor call where the final argument -- the body
2370          * source for the function to create -- contains a nested function
2371          * definition or expression, afunbox->parent will be null. The body
2372          * source might use |arguments| outside of any nested functions it may
2373          * contain, so we have to check the tcflags parameter that was passed
2374          * in from Compiler::compileFunctionBody.
2375          */
2376         if (dnKind == JSDefinition::ARG &&
2377             ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
2378             return false;
2379         }
2380     }
2381
2382     /*
2383      * Check quick-and-dirty dominance relation. Function definitions dominate
2384      * their uses thanks to hoisting.  Other binding forms hoist as undefined,
2385      * of course, so check forward-reference and blockid relations.
2386      */
2387     if (dnKind != JSDefinition::FUNCTION) {
2388         /*
2389          * Watch out for code such as
2390          *
2391          *   (function () {
2392          *   ...
2393          *   var jQuery = ... = function (...) {
2394          *       return new jQuery.foo.bar(baz);
2395          *   }
2396          *   ...
2397          *   })();
2398          *
2399          * where the jQuery variable is not reassigned, but of course is not
2400          * initialized at the time that the would-be-flat closure containing
2401          * the jQuery upvar is formed.
2402          */
2403         if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
2404             return false;
2405         if (!MinBlockId(afunbox->node, dn->pn_blockid))
2406             return false;
2407     }
2408     return true;
2409 }
2410
2411 static void
2412 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32 *tcflags)
2413 {
2414     uintN dnLevel = dn->frameLevel();
2415
2416     while ((funbox = funbox->parent) != NULL) {
2417         /*
2418          * Notice that funbox->level is the static level of the definition or
2419          * expression of the function parsed into funbox, not the static level
2420          * of its body. Therefore we must add 1 to match dn's level to find the
2421          * funbox whose body contains the dn definition.
2422          */
2423         if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
2424             funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2425             break;
2426         }
2427         funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
2428     }
2429
2430     if (!funbox && (*tcflags & TCF_IN_FUNCTION))
2431         *tcflags |= TCF_FUN_HEAVYWEIGHT;
2432 }
2433
2434 static bool
2435 DeoptimizeUsesWithin(JSDefinition *dn, const TokenPos &pos)
2436 {
2437     uintN ndeoptimized = 0;
2438
2439     for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2440         JS_ASSERT(pnu->pn_used);
2441         JS_ASSERT(!pnu->pn_defn);
2442         if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
2443             pnu->pn_dflags |= PND_DEOPTIMIZED;
2444             ++ndeoptimized;
2445         }
2446     }
2447
2448     return ndeoptimized != 0;
2449 }
2450
2451 void
2452 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
2453 {
2454 #define FUN_METER(x) JS_FUNCTION_METER(context, x)
2455
2456     for (;;) {
2457         JSParseNode *fn = funbox->node;
2458         JSParseNode *pn = fn->pn_body;
2459
2460         if (funbox->kids) {
2461             setFunctionKinds(funbox->kids, tcflags);
2462
2463             /*
2464              * We've unwound from recursively setting our kids' kinds, which
2465              * also classifies enclosing functions holding upvars referenced in
2466              * those descendants' bodies. So now we can check our "methods".
2467              *
2468              * Despecialize from branded method-identity-based shape to shape-
2469              * or slot-based shape if this function smells like a constructor
2470              * and too many of its methods are *not* joinable null closures
2471              * (i.e., they have one or more upvars fetched via the display).
2472              */
2473             JSParseNode *pn2 = pn;
2474             if (PN_TYPE(pn2) == TOK_UPVARS)
2475                 pn2 = pn2->pn_tree;
2476             if (PN_TYPE(pn2) == TOK_ARGSBODY)
2477                 pn2 = pn2->last();
2478
2479 #if JS_HAS_EXPR_CLOSURES
2480             if (PN_TYPE(pn2) == TOK_LC)
2481 #endif
2482             if (!(funbox->tcflags & TCF_RETURN_EXPR)) {
2483                 uintN methodSets = 0, slowMethodSets = 0;
2484
2485                 for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
2486                     JS_ASSERT(PN_OP(method) == JSOP_LAMBDA || PN_OP(method) == JSOP_LAMBDA_FC);
2487                     ++methodSets;
2488                     if (!method->pn_funbox->joinable())
2489                         ++slowMethodSets;
2490                 }
2491
2492                 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
2493                     funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
2494             }
2495         }
2496
2497         JSFunction *fun = (JSFunction *) funbox->object;
2498
2499         JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2500
2501         FUN_METER(allfun);
2502         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2503             FUN_METER(heavy);
2504         } else if (funbox->inAnyDynamicScope()) {
2505             JS_ASSERT(!FUN_NULL_CLOSURE(fun));
2506             FUN_METER(indynamicscope);
2507         } else if (pn->pn_type != TOK_UPVARS) {
2508             /*
2509              * No lexical dependencies => null closure, for best performance.
2510              * A null closure needs no scope chain, but alas we've coupled
2511              * principals-finding to scope (for good fundamental reasons, but
2512              * the implementation overloads the parent slot and we should fix
2513              * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2514              *
2515              * In more detail: the ES3 spec allows the implementation to create
2516              * "joined function objects", or not, at its discretion. But real-
2517              * world implementations always create unique function objects for
2518              * closures, and this can be detected via mutation. Open question:
2519              * do popular implementations create unique function objects for
2520              * null closures?
2521              *
2522              * FIXME: bug 476950.
2523              */
2524             FUN_METER(nofreeupvar);
2525             FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2526         } else {
2527             JSAtomList upvars(pn->pn_names);
2528             JS_ASSERT(upvars.count != 0);
2529
2530             JSAtomListIterator iter(&upvars);
2531             JSAtomListElement *ale;
2532
2533             if (!fn->isFunArg()) {
2534                 /*
2535                  * This function is Algol-like, it never escapes.
2536                  *
2537                  * Check that at least one outer lexical binding was assigned
2538                  * to (global variables don't count). This is conservative: we
2539                  * could limit assignments to those in the current function,
2540                  * but that's too much work. As with flat closures (handled
2541                  * below), we optimize for the case where outer bindings are
2542                  * not reassigned anywhere.
2543                  */
2544                 while ((ale = iter()) != NULL) {
2545                     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2546
2547                     if (!lexdep->isFreeVar()) {
2548                         JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2549                         break;
2550                     }
2551                 }
2552
2553                 if (!ale) {
2554                     FUN_METER(onlyfreevar);
2555                     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2556                 }
2557             } else {
2558                 uintN nupvars = 0, nflattened = 0;
2559
2560                 /*
2561                  * For each lexical dependency from this closure to an outer
2562                  * binding, analyze whether it is safe to copy the binding's
2563                  * value into a flat closure slot when the closure is formed.
2564                  */
2565                 while ((ale = iter()) != NULL) {
2566                     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2567
2568                     if (!lexdep->isFreeVar()) {
2569                         ++nupvars;
2570                         if (CanFlattenUpvar(lexdep, funbox, *tcflags)) {
2571                             ++nflattened;
2572                             continue;
2573                         }
2574
2575                         /*
2576                          * FIXME bug 545759: to test nflattened != 0 instead of
2577                          * nflattened == nupvars below, we'll need to avoid n^2
2578                          * bugs such as 617430 in uncommenting the following:
2579                          *
2580                          * if (DeoptimizeUsesWithin(lexdep, funbox->node->pn_body->pn_pos))
2581                          *     FlagHeavyweights(lexdep, funbox, tcflags);
2582                          *
2583                          * For now it's best to avoid this tedious, use-wise
2584                          * deoptimization and let fun remain an unoptimized
2585                          * closure. This is safe because we leave fun's kind
2586                          * set to interpreted, so all functions holding its
2587                          * upvars will be flagged as heavyweight.
2588                          */
2589                     }
2590                 }
2591
2592                 if (nupvars == 0) {
2593                     FUN_METER(onlyfreevar);
2594                     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2595                 } else if (nflattened == nupvars) {
2596                     /*
2597                      * We made it all the way through the upvar loop, so it's
2598                      * safe to optimize to a flat closure.
2599                      */
2600                     FUN_METER(flat);
2601                     FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2602                     switch (PN_OP(fn)) {
2603                       case JSOP_DEFFUN:
2604                         fn->pn_op = JSOP_DEFFUN_FC;
2605                         break;
2606                       case JSOP_DEFLOCALFUN:
2607                         fn->pn_op = JSOP_DEFLOCALFUN_FC;
2608                         break;
2609                       case JSOP_LAMBDA:
2610                         fn->pn_op = JSOP_LAMBDA_FC;
2611                         break;
2612                       default:
2613                         /* js_EmitTree's case TOK_FUNCTION: will select op. */
2614                         JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2615                     }
2616                 } else {
2617                     FUN_METER(badfunarg);
2618                 }
2619             }
2620         }
2621
2622         if (FUN_KIND(fun) == JSFUN_INTERPRETED && pn->pn_type == TOK_UPVARS) {
2623             /*
2624              * One or more upvars cannot be safely snapshot into a flat
2625              * closure's non-reserved slot (see JSOP_GETFCSLOT), so we loop
2626              * again over all upvars, and for each non-free upvar, ensure that
2627              * its containing function has been flagged as heavyweight.
2628              *
2629              * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2630              * generating any code for a tree of nested functions.
2631              */
2632             JSAtomList upvars(pn->pn_names);
2633             JS_ASSERT(upvars.count != 0);
2634
2635             JSAtomListIterator iter(&upvars);
2636             JSAtomListElement *ale;
2637
2638             while ((ale = iter()) != NULL) {
2639                 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2640                 if (!lexdep->isFreeVar())
2641                     FlagHeavyweights(lexdep, funbox, tcflags);
2642             }
2643         }
2644
2645         if (funbox->joinable())
2646             fun->setJoinable();
2647
2648         funbox = funbox->siblings;
2649         if (!funbox)
2650             break;
2651     }
2652
2653 #undef FUN_METER
2654 }
2655
2656 const char js_argument_str[] = "argument";
2657 const char js_variable_str[] = "variable";
2658 const char js_unknown_str[]  = "unknown";
2659
2660 const char *
2661 JSDefinition::kindString(Kind kind)
2662 {
2663     static const char *table[] = {
2664         js_var_str, js_const_str, js_let_str,
2665         js_function_str, js_argument_str, js_unknown_str
2666     };
2667
2668     JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2669     return table[kind];
2670 }
2671
2672 static JSFunctionBox *
2673 EnterFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2674               uintN lambda = JSFUN_LAMBDA)
2675 {
2676     JSTreeContext *tc = funtc->parent;
2677     JSFunction *fun = tc->parser->newFunction(tc, funAtom, lambda);
2678     if (!fun)
2679         return NULL;
2680
2681     /* Create box for fun->object early to protect against last-ditch GC. */
2682     JSFunctionBox *funbox = tc->parser->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2683     if (!funbox)
2684         return NULL;
2685
2686     /* Initialize non-default members of funtc. */
2687     funtc->flags |= funbox->tcflags;
2688     funtc->blockidGen = tc->blockidGen;
2689     if (!GenerateBlockId(funtc, funtc->bodyid))
2690         return NULL;
2691     funtc->setFunction(fun);
2692     funtc->funbox = funbox;
2693     if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2694         return NULL;
2695
2696     return funbox;
2697 }
2698
2699 static bool
2700 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2701               uintN lambda = JSFUN_LAMBDA)
2702 {
2703     JSTreeContext *tc = funtc->parent;
2704     tc->blockidGen = funtc->blockidGen;
2705
2706     JSFunctionBox *funbox = fn->pn_funbox;
2707     funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
2708
2709     fn->pn_dflags |= PND_INITIALIZED;
2710     if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2711         fn->pn_dflags |= PND_BLOCKCHILD;
2712
2713     /*
2714      * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2715      * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2716      * params and body. We do this only if there are lexical dependencies not
2717      * satisfied by the function's declarations, to avoid penalizing functions
2718      * that use only their arguments and other local bindings.
2719      */
2720     if (funtc->lexdeps.count != 0) {
2721         JSAtomListIterator iter(&funtc->lexdeps);
2722         JSAtomListElement *ale;
2723         int foundCallee = 0;
2724
2725         while ((ale = iter()) != NULL) {
2726             JSAtom *atom = ALE_ATOM(ale);
2727             JSDefinition *dn = ALE_DEFN(ale);
2728             JS_ASSERT(dn->isPlaceholder());
2729
2730             if (atom == funAtom && lambda != 0) {
2731                 dn->pn_op = JSOP_CALLEE;
2732                 dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
2733                 dn->pn_dflags |= PND_BOUND;
2734
2735                 /*
2736                  * If this named function expression uses its own name other
2737                  * than to call itself, flag this function specially.
2738                  */
2739                 if (dn->isFunArg())
2740                     funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2741                 foundCallee = 1;
2742                 continue;
2743             }
2744
2745             if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2746                 dn->isAssigned()) {
2747                 /*
2748                  * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2749                  * any use of dn in funtc assigns. See NoteLValue for the easy
2750                  * backward-reference case; this is the hard forward-reference
2751                  * case where we pay a higher price.
2752                  */
2753                 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2754                     if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2755                         funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2756                         break;
2757                     }
2758                 }
2759             }
2760
2761             JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2762
2763             /*
2764              * Make sure to deoptimize lexical dependencies that are polluted
2765              * by eval or with, to safely bind globals (see bug 561923).
2766              */
2767             if (funtc->callsEval() ||
2768                 (outer_ale && tc->innermostWith &&
2769                  ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) {
2770                 DeoptimizeUsesWithin(dn, fn->pn_pos);
2771             }
2772
2773             if (!outer_ale)
2774                 outer_ale = tc->lexdeps.lookup(atom);
2775             if (!outer_ale) {
2776                 /* 
2777                  * Create a new placeholder for our outer lexdep. We could simply re-use
2778                  * the inner placeholder, but that introduces subtleties in the case where
2779                  * we find a later definition that captures an existing lexdep. For
2780                  * example:
2781                  *
2782                  *   function f() { function g() { x; } let x; }
2783                  *
2784                  * Here, g's TOK_UPVARS node lists the placeholder for x, which must be
2785                  * captured by the 'let' declaration later, since 'let's are hoisted.
2786                  * Taking g's placeholder as our own would work fine. But consider:
2787                  *
2788                  *   function f() { x; { function g() { x; } let x; } }
2789                  *
2790                  * Here, the 'let' must not capture all the uses of f's lexdep entry for
2791                  * x, but it must capture the x node referred to from g's TOK_UPVARS node.
2792                  * Always turning inherited lexdeps into uses of a new outer definition
2793                  * allows us to handle both these cases in a natural way.
2794                  */
2795                 outer_ale = MakePlaceholder(dn, tc);
2796             }
2797
2798             JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2799
2800             /*
2801              * Insert dn's uses list at the front of outer_dn's list.
2802              *
2803              * Without loss of generality or correctness, we allow a dn to
2804              * be in inner and outer lexdeps, since the purpose of lexdeps
2805              * is one-pass coordination of name use and definition across
2806              * functions, and if different dn's are used we'll merge lists
2807              * when leaving the inner function.
2808              *
2809              * The dn == outer_dn case arises with generator expressions
2810              * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2811              * case), and nowhere else, currently.
2812              */
2813             if (dn != outer_dn) {
2814                 JSParseNode **pnup = &dn->dn_uses;
2815                 JSParseNode *pnu;
2816
2817                 while ((pnu = *pnup) != NULL) {
2818                     pnu->pn_lexdef = outer_dn;
2819                     pnup = &pnu->pn_link;
2820                 }
2821
2822                 /*
2823                  * Make dn be a use that redirects to outer_dn, because we
2824                  * can't replace dn with outer_dn in all the pn_namesets in
2825                  * the AST where it may be. Instead we make it forward to
2826                  * outer_dn. See JSDefinition::resolve.
2827                  */
2828                 *pnup = outer_dn->dn_uses;
2829                 outer_dn->dn_uses = dn;
2830                 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2831                 dn->pn_defn = false;
2832                 dn->pn_used = true;
2833                 dn->pn_lexdef = outer_dn;
2834             }
2835
2836             /* Mark the outer dn as escaping. */
2837             outer_dn->pn_dflags |= PND_CLOSED;
2838         }
2839
2840         if (funtc->lexdeps.count - foundCallee != 0) {
2841             JSParseNode *body = fn->pn_body;
2842
2843             fn->pn_body = NameSetNode::create(tc);
2844             if (!fn->pn_body)
2845                 return false;
2846
2847             fn->pn_body->pn_type = TOK_UPVARS;
2848             fn->pn_body->pn_pos = body->pn_pos;
2849             if (foundCallee)
2850                 funtc->lexdeps.remove(tc->parser, funAtom);
2851             fn->pn_body->pn_names = funtc->lexdeps;
2852             fn->pn_body->pn_tree = body;
2853         }
2854
2855         funtc->lexdeps.clear();
2856     }
2857
2858     /*
2859      * Check whether any parameters have been assigned within this function.
2860      * In strict mode parameters do not alias arguments[i], and to make the
2861      * arguments object reflect initial parameter values prior to any mutation
2862      * we create it eagerly whenever parameters are (or might, in the case of
2863      * calls to eval) be assigned.
2864      */
2865     if (funtc->inStrictMode() && funbox->object->getFunctionPrivate()->nargs > 0) {
2866         JSAtomListIterator iter(&funtc->decls);
2867         JSAtomListElement *ale;
2868
2869         while ((ale = iter()) != NULL) {
2870             JSDefinition *dn = ALE_DEFN(ale);
2871             if (dn->kind() == JSDefinition::ARG && dn->isAssigned()) {
2872                 funbox->tcflags |= TCF_FUN_MUTATES_PARAMETER;
2873                 break;
2874             }
2875         }
2876     }
2877
2878     funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
2879
2880     return true;
2881 }
2882
2883 static bool
2884 DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
2885
2886 /*
2887  * FIXME? this Parser method was factored from Parser::functionDef with minimal
2888  * change, hence the funtc ref param and funbox. It probably should match
2889  * functionBody, etc., and use tc and tc->funbox instead of taking explicit
2890  * parameters.
2891  */
2892 bool
2893 Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **listp)
2894 {
2895     if (tokenStream.getToken() != TOK_LP) {
2896         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
2897         return false;
2898     }
2899
2900     if (!tokenStream.matchToken(TOK_RP)) {
2901 #if JS_HAS_DESTRUCTURING
2902         JSAtom *duplicatedArg = NULL;
2903         bool destructuringArg = false;
2904         JSParseNode *list = NULL;
2905 #endif
2906
2907         do {
2908             switch (TokenKind tt = tokenStream.getToken()) {
2909 #if JS_HAS_DESTRUCTURING
2910               case TOK_LB:
2911               case TOK_LC:
2912               {
2913                 /* See comment below in the TOK_NAME case. */
2914                 if (duplicatedArg)
2915                     goto report_dup_and_destructuring;
2916                 destructuringArg = true;
2917
2918                 /*
2919                  * A destructuring formal parameter turns into one or more
2920                  * local variables initialized from properties of a single
2921                  * anonymous positional parameter, so here we must tweak our
2922                  * binder and its data.
2923                  */
2924                 BindData data;
2925                 data.pn = NULL;
2926                 data.op = JSOP_DEFVAR;
2927                 data.binder = BindDestructuringArg;
2928                 JSParseNode *lhs = destructuringExpr(&data, tt);
2929                 if (!lhs)
2930                     return false;
2931
2932                 /*
2933                  * Adjust fun->nargs to count the single anonymous positional
2934                  * parameter that is to be destructured.
2935                  */
2936                 uint16 slot;
2937                 if (!funtc.bindings.addDestructuring(context, &slot))
2938                     return false;
2939
2940                 /*
2941                  * Synthesize a destructuring assignment from the single
2942                  * anonymous positional parameter into the destructuring
2943                  * left-hand-side expression and accumulate it in list.
2944                  */
2945                 JSParseNode *rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
2946                 if (!rhs)
2947                     return false;
2948                 rhs->pn_type = TOK_NAME;
2949                 rhs->pn_op = JSOP_GETARG;
2950                 rhs->pn_cookie.set(funtc.staticLevel, slot);
2951                 rhs->pn_dflags |= PND_BOUND;
2952
2953                 JSParseNode *item =
2954                     JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2955                 if (!item)
2956                     return false;
2957                 if (!list) {
2958                     list = ListNode::create(&funtc);
2959                     if (!list)
2960                         return false;
2961                     list->pn_type = TOK_VAR;
2962                     list->makeEmpty();
2963                     *listp = list;
2964                 }
2965                 list->append(item);
2966                 break;
2967               }
2968 #endif /* JS_HAS_DESTRUCTURING */
2969
2970               case TOK_NAME:
2971               {
2972                 JSAtom *atom = tokenStream.currentToken().t_atom;
2973
2974 #ifdef JS_HAS_DESTRUCTURING
2975                 /*
2976                  * ECMA-262 requires us to support duplicate parameter names,
2977                  * but if the parameter list includes destructuring, we
2978                  * consider the code to have "opted in" to higher standards and
2979                  * forbid duplicates. We may see a destructuring parameter
2980                  * later, so always note duplicates now.
2981                  *
2982                  * Duplicates are warned about (strict option) or cause errors
2983                  * (strict mode code), but we do those tests in one place
2984                  * below, after having parsed the body in case it begins with a
2985                  * "use strict"; directive.
2986                  *
2987                  * NB: Check funtc.decls rather than funtc.bindings, because
2988                  *     destructuring bindings aren't added to funtc.bindings
2989                  *     until after all arguments have been parsed.
2990                  */
2991                 if (funtc.decls.lookup(atom)) {
2992                     duplicatedArg = atom;
2993                     if (destructuringArg)
2994                         goto report_dup_and_destructuring;
2995                 }
2996 #endif
2997
2998                 uint16 slot;
2999                 if (!funtc.bindings.addArgument(context, atom, &slot))
3000                     return false;
3001                 if (!DefineArg(funbox->node, atom, slot, &funtc))
3002                     return false;
3003                 break;
3004               }
3005
3006               default:
3007                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
3008                 /* FALL THROUGH */
3009               case TOK_ERROR:
3010                 return false;
3011
3012 #if JS_HAS_DESTRUCTURING
3013               report_dup_and_destructuring:
3014                 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
3015                 reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
3016                 return false;
3017 #endif
3018             }
3019         } while (tokenStream.matchToken(TOK_COMMA));
3020
3021         if (tokenStream.getToken() != TOK_RP) {
3022             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_FORMAL);
3023             return false;
3024         }
3025     }
3026
3027     return true;
3028 }
3029
3030 JSParseNode *
3031 Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
3032 {
3033     /* Make a TOK_FUNCTION node. */
3034     tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
3035     JSParseNode *pn = FunctionNode::create(tc);
3036     if (!pn)
3037         return NULL;
3038     pn->pn_body = NULL;
3039     pn->pn_cookie.makeFree();
3040
3041     /*
3042      * If a lambda, mark this function as escaping (as a "funarg") unless it is
3043      * immediately applied (we clear PND_FUNARG if so -- see memberExpr).
3044      *
3045      * Treat function sub-statements (non-lambda, non-body-level functions) as
3046      * escaping funargs, since we can't statically analyze their definitions
3047      * and uses.
3048      */
3049     bool bodyLevel = tc->atBodyLevel();
3050     pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
3051
3052     /*
3053      * Record names for function statements in tc->decls so we know when to
3054      * avoid optimizing variable references that might name a function.
3055      */
3056     if (lambda == 0 && funAtom) {
3057         if (JSAtomListElement *ale = tc->decls.lookup(funAtom)) {
3058             JSDefinition *dn = ALE_DEFN(ale);
3059             JSDefinition::Kind dn_kind = dn->kind();
3060
3061             JS_ASSERT(!dn->pn_used);
3062             JS_ASSERT(dn->pn_defn);
3063
3064             if (context->hasStrictOption() || dn_kind == JSDefinition::CONST) {
3065                 JSAutoByteString name;
3066                 if (!js_AtomToPrintableString(context, funAtom, &name) ||
3067                     !reportErrorNumber(NULL,
3068                                        (dn_kind != JSDefinition::CONST)
3069                                        ? JSREPORT_WARNING | JSREPORT_STRICT
3070                                        : JSREPORT_ERROR,
3071                                        JSMSG_REDECLARED_VAR,
3072                                        JSDefinition::kindString(dn_kind),
3073                                        name.ptr())) {
3074                     return NULL;
3075                 }
3076             }
3077
3078             if (bodyLevel) {
3079                 ALE_SET_DEFN(ale, pn);
3080                 pn->pn_defn = true;
3081                 pn->dn_uses = dn;               /* dn->dn_uses is now pn_link */
3082
3083                 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
3084                     return NULL;
3085             }
3086         } else if (bodyLevel) {
3087             /*
3088              * If this function was used before it was defined, claim the
3089              * pre-created definition node for this function that primaryExpr
3090              * put in tc->lexdeps on first forward reference, and recycle pn.
3091              */
3092             JSHashEntry **hep;
3093
3094             ale = tc->lexdeps.rawLookup(funAtom, hep);
3095             if (ale) {
3096                 JSDefinition *fn = ALE_DEFN(ale);
3097
3098                 JS_ASSERT(fn->pn_defn);
3099                 fn->pn_type = TOK_FUNCTION;
3100                 fn->pn_arity = PN_FUNC;
3101                 fn->pn_pos.begin = pn->pn_pos.begin;
3102                 fn->pn_body = NULL;
3103                 fn->pn_cookie.makeFree();
3104
3105                 tc->lexdeps.rawRemove(tc->parser, ale, hep);
3106                 RecycleTree(pn, tc);
3107                 pn = fn;
3108             }
3109
3110             if (!Define(pn, funAtom, tc))
3111                 return NULL;
3112         }
3113
3114         /*
3115          * A function directly inside another's body needs only a local
3116          * variable to bind its name to its value, and not an activation object
3117          * property (it might also need the activation property, if the outer
3118          * function contains with statements, e.g., but the stack slot wins
3119          * when jsemit.cpp's BindNameToSlot can optimize a JSOP_NAME into a
3120          * JSOP_GETLOCAL bytecode).
3121          */
3122         if (bodyLevel && tc->inFunction()) {
3123             /*
3124              * Define a local in the outer function so that BindNameToSlot
3125              * can properly optimize accesses. Note that we need a local
3126              * variable, not an argument, for the function statement. Thus
3127              * we add a variable even if a parameter with the given name
3128              * already exists.
3129              */
3130             uintN index;
3131             switch (tc->bindings.lookup(context, funAtom, &index)) {
3132               case NONE:
3133               case ARGUMENT:
3134                 index = tc->bindings.countVars();
3135                 if (!tc->bindings.addVariable(context, funAtom))
3136                     return NULL;
3137                 /* FALL THROUGH */
3138
3139               case VARIABLE:
3140                 pn->pn_cookie.set(tc->staticLevel, index);
3141                 pn->pn_dflags |= PND_BOUND;
3142                 break;
3143
3144               default:;
3145             }
3146         }
3147     }
3148
3149     JSTreeContext *outertc = tc;
3150
3151     /* Initialize early for possible flags mutation via destructuringExpr. */
3152     JSTreeContext funtc(tc->parser);
3153
3154     JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
3155     if (!funbox)
3156         return NULL;
3157
3158     JSFunction *fun = (JSFunction *) funbox->object;
3159
3160     /* Now parse formal argument list and compute fun->nargs. */
3161     JSParseNode *prelude = NULL;
3162     if (!functionArguments(funtc, funbox, &prelude))
3163         return NULL;
3164
3165     fun->setArgCount(funtc.bindings.countArgs());
3166
3167 #if JS_HAS_DESTRUCTURING
3168     /*
3169      * If there were destructuring formal parameters, bind the destructured-to
3170      * local variables now that we've parsed all the regular and destructuring
3171      * formal parameters. Because js::Bindings::add must be called first for
3172      * all ARGUMENTs, then all VARIABLEs and CONSTANTs, and finally all UPVARs,
3173      * we can't bind vars induced by formal parameter destructuring until after
3174      * Parser::functionArguments has returned.
3175      */
3176     if (prelude) {
3177         JSAtomListIterator iter(&funtc.decls);
3178
3179         while (JSAtomListElement *ale = iter()) {
3180             JSParseNode *apn = ALE_DEFN(ale);
3181
3182             /* Filter based on pn_op -- see BindDestructuringArg, above. */
3183             if (apn->pn_op != JSOP_SETLOCAL)
3184                 continue;
3185
3186             uint16 index = funtc.bindings.countVars();
3187             if (!BindLocalVariable(context, &funtc, apn->pn_atom, VARIABLE, true))
3188                 return NULL;
3189             apn->pn_cookie.set(funtc.staticLevel, index);
3190         }
3191     }
3192 #endif
3193
3194     if (type == GETTER && fun->nargs > 0) {
3195         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
3196                           "getter", "no", "s");
3197         return NULL;
3198     }
3199     if (type == SETTER && fun->nargs != 1) {
3200         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
3201                           "setter", "one", "");
3202         return NULL;
3203     }
3204
3205 #if JS_HAS_EXPR_CLOSURES
3206     TokenKind tt = tokenStream.getToken(TSF_OPERAND);
3207     if (tt != TOK_LC) {
3208         tokenStream.ungetToken();
3209         fun->flags |= JSFUN_EXPR_CLOSURE;
3210     }
3211 #else
3212     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
3213 #endif
3214
3215     JSParseNode *body = functionBody();
3216     if (!body)
3217         return NULL;
3218
3219     if (funAtom && !CheckStrictBinding(context, &funtc, funAtom, pn))
3220         return NULL;
3221
3222     if (!CheckStrictParameters(context, &funtc))
3223         return NULL;
3224
3225 #if JS_HAS_EXPR_CLOSURES
3226     if (tt == TOK_LC)
3227         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
3228     else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
3229         return NULL;
3230 #else
3231     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
3232 #endif
3233     pn->pn_pos.end = tokenStream.currentToken().pos.end;
3234
3235     /*
3236      * Fruit of the poisonous tree: if a closure calls eval, we consider the
3237      * parent to call eval. We need this for two reasons: (1) the Jaegermonkey
3238      * optimizations really need to know if eval is called transitively, and
3239      * (2) in strict mode, eval called transitively requires eager argument
3240      * creation in strict mode parent functions. 
3241      *
3242      * For the latter, we really only need to propagate callsEval if both 
3243      * functions are strict mode, but we don't lose much by always propagating. 
3244      * The only optimization we lose this way is in the case where a function 
3245      * is strict, does not mutate arguments, does not call eval directly, but
3246      * calls eval transitively.
3247      */
3248     if (funtc.callsEval())
3249         outertc->noteCallsEval();
3250
3251 #if JS_HAS_DESTRUCTURING
3252     /*
3253      * If there were destructuring formal parameters, prepend the initializing
3254      * comma expression that we synthesized to body. If the body is a return
3255      * node, we must make a special TOK_SEQ node, to prepend the destructuring
3256      * code without bracing the decompilation of the function body.
3257      */
3258     if (prelude) {
3259         if (body->pn_arity != PN_LIST) {
3260             JSParseNode *block;
3261
3262             block = ListNode::create(outertc);
3263             if (!block)
3264                 return NULL;
3265             block->pn_type = TOK_SEQ;
3266             block->pn_pos = body->pn_pos;
3267             block->initList(body);
3268
3269             body = block;
3270         }
3271
3272         JSParseNode *item = UnaryNode::create(outertc);
3273         if (!item)
3274             return NULL;
3275
3276         item->pn_type = TOK_SEMI;
3277         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
3278         item->pn_kid = prelude;
3279         item->pn_next = body->pn_head;
3280         body->pn_head = item;
3281         if (body->pn_tail == &body->pn_head)
3282             body->pn_tail = &item->pn_next;
3283         ++body->pn_count;
3284         body->pn_xflags |= PNX_DESTRUCT;
3285     }
3286 #endif
3287
3288     /*
3289      * If we collected flags that indicate nested heavyweight functions, or
3290      * this function contains heavyweight-making statements (with statement,
3291      * visible eval call, or assignment to 'arguments'), flag the function as
3292      * heavyweight (requiring a call object per invocation).
3293      */
3294     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
3295         fun->flags |= JSFUN_HEAVYWEIGHT;
3296         outertc->flags |= TCF_FUN_HEAVYWEIGHT;
3297     } else {
3298         /*
3299          * If this function is not at body level of a program or function (i.e.
3300          * it is a function statement that is not a direct child of a program
3301          * or function), then our enclosing function, if any, must be
3302          * heavyweight.
3303          */
3304         if (!bodyLevel && lambda == 0 && funAtom)
3305             outertc->flags |= TCF_FUN_HEAVYWEIGHT;
3306     }
3307
3308     JSParseNode *result = pn;
3309     JSOp op = JSOP_NOP;
3310     if (lambda != 0) {
3311         /*
3312          * ECMA ed. 3 standard: function expression, possibly anonymous.
3313          */
3314         op = JSOP_LAMBDA;
3315     } else if (!funAtom) {
3316         /*
3317          * If this anonymous function definition is *not* embedded within a
3318          * larger expression, we treat it as an expression statement, not as
3319          * a function declaration -- and not as a syntax error (as ECMA-262
3320          * Edition 3 would have it).  Backward compatibility must trump all,
3321          * unless JSOPTION_ANONFUNFIX is set.
3322          */
3323         result = UnaryNode::create(outertc);
3324         if (!result)
3325             return NULL;
3326         result->pn_type = TOK_SEMI;
3327         result->pn_pos = pn->pn_pos;
3328         result->pn_kid = pn;
3329         op = JSOP_LAMBDA;
3330     } else if (!bodyLevel) {
3331         /*
3332          * ECMA ed. 3 extension: a function expression statement not at the
3333          * top level, e.g., in a compound statement such as the "then" part
3334          * of an "if" statement, binds a closure only if control reaches that
3335          * sub-statement.
3336          */
3337         op = JSOP_DEFFUN;
3338         outertc->noteMightAliasLocals();
3339     }
3340
3341     funbox->kids = funtc.functionList;
3342
3343     pn->pn_funbox = funbox;
3344     pn->pn_op = op;
3345     if (pn->pn_body) {
3346         pn->pn_body->append(body);
3347         pn->pn_body->pn_pos = body->pn_pos;
3348     } else {
3349         pn->pn_body = body;
3350     }
3351
3352     if (!outertc->inFunction() && bodyLevel && funAtom && !lambda && outertc->compiling()) {
3353         JS_ASSERT(pn->pn_cookie.isFree());
3354         if (!DefineGlobal(pn, outertc->asCodeGenerator(), funAtom))
3355             return false;
3356     }
3357
3358     pn->pn_blockid = outertc->blockid();
3359
3360     if (!LeaveFunction(pn, &funtc, funAtom, lambda))
3361         return NULL;
3362
3363     /* If the surrounding function is not strict code, reset the lexer. */
3364     if (!outertc->inStrictMode())
3365         tokenStream.setStrictMode(false);
3366
3367     return result;
3368 }
3369
3370 JSParseNode *
3371 Parser::functionStmt()
3372 {
3373     JSAtom *name = NULL;
3374     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
3375         name = tokenStream.currentToken().t_atom;
3376     } else {
3377         if (hasAnonFunFix()) {
3378             /* Extension: accept unnamed function expressions as statements. */
3379             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
3380             return NULL;
3381         }
3382         tokenStream.ungetToken();
3383     }
3384
3385     /* We forbid function statements in strict mode code. */
3386     if (!tc->atBodyLevel() && tc->inStrictMode()) {
3387         reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
3388         return NULL;
3389     }
3390
3391     return functionDef(name, GENERAL, 0);
3392 }
3393
3394 JSParseNode *
3395 Parser::functionExpr()
3396 {
3397     JSAtom *name = NULL;
3398     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
3399         name = tokenStream.currentToken().t_atom;
3400     else
3401         tokenStream.ungetToken();
3402     return functionDef(name, GENERAL, JSFUN_LAMBDA);
3403 }
3404
3405 /*
3406  * Recognize Directive Prologue members and directives. Assuming |pn| is a
3407  * candidate for membership in a directive prologue, recognize directives and
3408  * set |tc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
3409  * |pn_prologue| flag.
3410  *
3411  * Note that the following is a strict mode function:
3412  *
3413  * function foo() {
3414  *   "blah" // inserted semi colon
3415  *        "blurgh"
3416  *   "use\x20loose"
3417  *   "use strict"
3418  * }
3419  *
3420  * That is, even though "use\x20loose" can never be a directive, now or in the
3421  * future (because of the hex escape), the Directive Prologue extends through it
3422  * to the "use strict" statement, which is indeed a directive.
3423  */
3424 bool
3425 Parser::recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember)
3426 {
3427     *isDirectivePrologueMember = pn->isStringExprStatement();
3428     if (!*isDirectivePrologueMember)
3429         return true;
3430
3431     JSParseNode *kid = pn->pn_kid;
3432     if (kid->isEscapeFreeStringLiteral()) {
3433         /*
3434          * Mark this statement as being a possibly legitimate part of a
3435          * directive prologue, so the byte code emitter won't warn about it
3436          * being useless code. (We mustn't just omit the statement entirely yet,
3437          * as it could be producing the value of an eval or JSScript execution.)
3438          *
3439          * Note that even if the string isn't one we recognize as a directive,
3440          * the emitter still shouldn't flag it as useless, as it could become a
3441          * directive in the future. We don't want to interfere with people
3442          * taking advantage of directive-prologue-enabled features that appear
3443          * in other browsers first.
3444          */
3445         pn->pn_prologue = true;
3446
3447         JSAtom *directive = kid->pn_atom;
3448         if (directive == context->runtime->atomState.useStrictAtom) {
3449             /*
3450              * Unfortunately, Directive Prologue members in general may contain
3451              * escapes, even while "use strict" directives may not.  Therefore
3452              * we must check whether an octal character escape has been seen in
3453              * any previous directives whenever we encounter a "use strict"
3454              * directive, so that the octal escape is properly treated as a
3455              * syntax error.  An example of this case:
3456              *
3457              *   function error()
3458              *   {
3459              *     "\145"; // octal escape
3460              *     "use strict"; // retroactively makes "\145" a syntax error
3461              *   }
3462              */
3463             if (tokenStream.hasOctalCharacterEscape()) {
3464                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
3465                 return false;
3466             }
3467
3468             tc->flags |= TCF_STRICT_MODE_CODE;
3469             tokenStream.setStrictMode();
3470         }
3471     }
3472     return true;
3473 }
3474
3475 /*
3476  * Parse the statements in a block, creating a TOK_LC node that lists the
3477  * statements' trees.  If called from block-parsing code, the caller must
3478  * match { before and } after.
3479  */
3480 JSParseNode *
3481 Parser::statements()
3482 {
3483     JSParseNode *pn, *pn2, *saveBlock;
3484     TokenKind tt;
3485
3486     JS_CHECK_RECURSION(context, return NULL);
3487
3488     pn = ListNode::create(tc);
3489     if (!pn)
3490         return NULL;
3491     pn->pn_type = TOK_LC;
3492     pn->makeEmpty();
3493     pn->pn_blockid = tc->blockid();
3494     saveBlock = tc->blockNode;
3495     tc->blockNode = pn;
3496
3497     bool inDirectivePrologue = tc->atBodyLevel();
3498     tokenStream.setOctalCharacterEscape(false);
3499     for (;;) {
3500         tt = tokenStream.peekToken(TSF_OPERAND);
3501         if (tt <= TOK_EOF || tt == TOK_RC) {
3502             if (tt == TOK_ERROR) {
3503                 if (tokenStream.isEOF())
3504                     tokenStream.setUnexpectedEOF();
3505                 return NULL;
3506             }
3507             break;
3508         }
3509         pn2 = statement();
3510         if (!pn2) {
3511             if (tokenStream.isEOF())
3512                 tokenStream.setUnexpectedEOF();
3513             return NULL;
3514         }
3515
3516         if (inDirectivePrologue && !recognizeDirectivePrologue(pn2, &inDirectivePrologue))
3517             return NULL;
3518
3519         if (pn2->pn_type == TOK_FUNCTION) {
3520             /*
3521              * PNX_FUNCDEFS notifies the emitter that the block contains body-
3522              * level function definitions that should be processed before the
3523              * rest of nodes.
3524              *
3525              * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3526              * is relevant only for function definitions not at body-level,
3527              * which we call function statements.
3528              */
3529             if (tc->atBodyLevel())
3530                 pn->pn_xflags |= PNX_FUNCDEFS;
3531             else
3532                 tc->flags |= TCF_HAS_FUNCTION_STMT;
3533         }
3534         pn->append(pn2);
3535     }
3536
3537     /*
3538      * Handle the case where there was a let declaration under this block.  If
3539      * it replaced tc->blockNode with a new block node then we must refresh pn
3540      * and then restore tc->blockNode.
3541      */
3542     if (tc->blockNode != pn)
3543         pn = tc->blockNode;
3544     tc->blockNode = saveBlock;
3545
3546     pn->pn_pos.end = tokenStream.currentToken().pos.end;
3547     return pn;
3548 }
3549
3550 JSParseNode *
3551 Parser::condition()
3552 {
3553     JSParseNode *pn;
3554
3555     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3556     pn = parenExpr();
3557     if (!pn)
3558         return NULL;
3559     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3560
3561     /* Check for (a = b) and warn about possible (a == b) mistype. */
3562     if (pn->pn_type == TOK_ASSIGN &&
3563         pn->pn_op == JSOP_NOP &&
3564         !pn->pn_parens &&
3565         !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, "")) {
3566         return NULL;
3567     }
3568     return pn;
3569 }
3570
3571 static JSBool
3572 MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
3573 {
3574     JSAtom *label;
3575     TokenKind tt;
3576
3577     tt = ts->peekTokenSameLine();
3578     if (tt == TOK_ERROR)
3579         return JS_FALSE;
3580     if (tt == TOK_NAME) {
3581         (void) ts->getToken();
3582         label = ts->currentToken().t_atom;
3583     } else {
3584         label = NULL;
3585     }
3586     pn->pn_atom = label;
3587     return JS_TRUE;
3588 }
3589
3590 static JSBool
3591 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3592 {
3593     JSParseNode *pn;
3594     JSObject *blockObj;
3595     JSAtomListElement *ale;
3596     jsint n;
3597
3598     /*
3599      * Body-level 'let' is the same as 'var' currently -- this may change in a
3600      * successor standard to ES5 that specifies 'let'.
3601      */
3602     JS_ASSERT(!tc->atBodyLevel());
3603
3604     pn = data->pn;
3605     if (!CheckStrictBinding(cx, tc, atom, pn))
3606         return false;
3607
3608     blockObj = tc->blockChain();
3609     ale = tc->decls.lookup(atom);
3610     if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3611         JSAutoByteString name;
3612         if (js_AtomToPrintableString(cx, atom, &name)) {
3613             ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3614                                      JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3615                                      (ale && ALE_DEFN(ale)->isConst())
3616                                      ? js_const_str
3617                                      : js_variable_str,
3618                                      name.ptr());
3619         }
3620         return false;
3621     }
3622
3623     n = OBJ_BLOCK_COUNT(cx, blockObj);
3624     if (n == JS_BIT(16)) {
3625         ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3626                                  JSREPORT_ERROR, data->let.overflow);
3627         return false;
3628     }
3629
3630     /*
3631      * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3632      * This is balanced by PopStatement, defined immediately below.
3633      */
3634     if (!Define(pn, atom, tc, true))
3635         return false;
3636
3637     /*
3638      * Assign block-local index to pn->pn_cookie right away, encoding it as an
3639      * upvar cookie whose skip tells the current static level. The emitter will
3640      * adjust the node's slot based on its stack depth model -- and, for global
3641      * and eval code, Compiler::compileScript will adjust the slot again to
3642      * include script->nfixed.
3643      */
3644     pn->pn_op = JSOP_GETLOCAL;
3645     pn->pn_cookie.set(tc->staticLevel, uint16(n));
3646     pn->pn_dflags |= PND_LET | PND_BOUND;
3647
3648     /*
3649      * Define the let binding's property before storing pn in the the binding's
3650      * slot indexed by n off the class-reserved slot base.
3651      */
3652     const Shape *shape = blockObj->defineBlockVariable(cx, ATOM_TO_JSID(atom), n);
3653     if (!shape)
3654         return false;
3655
3656     /*
3657      * Store pn temporarily in what would be shape-mapped slots in a cloned
3658      * block object (once the prototype's final population is known, after all
3659      * 'let' bindings for this block have been parsed). We free these slots in
3660      * jsemit.cpp:EmitEnterBlock so they don't tie up unused space in the so-
3661      * called "static" prototype Block.
3662      */
3663     blockObj->setSlot(shape->slot, PrivateValue(pn));
3664     return true;
3665 }
3666
3667 static void
3668 PopStatement(JSTreeContext *tc)
3669 {
3670     JSStmtInfo *stmt = tc->topStmt;
3671
3672     if (stmt->flags & SIF_SCOPE) {
3673         JSObject *obj = stmt->blockBox->object;
3674         JS_ASSERT(!obj->isClonedBlock());
3675
3676         for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
3677             JSAtom *atom = JSID_TO_ATOM(r.front().id);
3678
3679             /* Beware the empty destructuring dummy. */
3680             if (atom == tc->parser->context->runtime->atomState.emptyAtom)
3681                 continue;
3682             tc->decls.remove(tc->parser, atom);
3683         }
3684     }
3685     js_PopStatement(tc);
3686 }
3687
3688 static inline bool
3689 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3690 {
3691     while (stmt->downScope) {
3692         stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3693         if (!stmt)
3694             return false;
3695         if (stmt->type == STMT_BLOCK)
3696             return true;
3697     }
3698     return false;
3699 }
3700
3701 /*
3702  * If we are generating global or eval-called-from-global code, bind a "gvar"
3703  * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3704  * global variable access by memoizing name-to-slot mappings during execution
3705  * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3706  * can't be done due to a pre-existing property of the same name as the var or
3707  * const but incompatible attributes/getter/setter/etc, these ops devolve to
3708  * JSOP_NAME, etc.
3709  *
3710  * For now, don't try to lookup eval frame variables at compile time. This is
3711  * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3712  * gets its own script and frame. The eval-from-function-code case is harder,
3713  * since functions do not atomize gvars and then reserve their atom indexes as
3714  * stack frame slots.
3715  */
3716 static bool
3717 DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom)
3718 {
3719     GlobalScope *globalScope = cg->compiler()->globalScope;
3720     JSObject *globalObj = globalScope->globalObj;
3721
3722     if (!cg->compileAndGo() || !globalObj || cg->compilingForEval())
3723         return true;
3724
3725     JSAtomListElement *ale = globalScope->names.lookup(atom);
3726     if (!ale) {
3727         JSContext *cx = cg->parser->context;
3728
3729         JSObject *holder;
3730         JSProperty *prop;
3731         if (!globalObj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop))
3732             return false;
3733
3734         JSFunctionBox *funbox = (pn->pn_type == TOK_FUNCTION) ? pn->pn_funbox : NULL;
3735
3736         GlobalScope::GlobalDef def;
3737         if (prop) {
3738             /*
3739              * A few cases where we don't bother aggressively caching:
3740              *   1) Function value changes.
3741              *   2) Configurable properties.
3742              *   3) Properties without slots, or with getters/setters.
3743              */
3744             const Shape *shape = (const Shape *)prop;
3745             if (funbox ||
3746                 globalObj != holder ||
3747                 shape->configurable() ||
3748                 !shape->hasSlot() ||
3749                 !shape->hasDefaultGetterOrIsMethod() ||
3750                 !shape->hasDefaultSetter()) {
3751                 return true;
3752             }
3753             
3754             def = GlobalScope::GlobalDef(shape->slot);
3755         } else {
3756             def = GlobalScope::GlobalDef(atom, funbox);
3757         }
3758
3759         if (!globalScope->defs.append(def))
3760             return false;
3761
3762         ale = globalScope->names.add(cg->parser, atom);
3763         if (!ale)
3764             return false;
3765
3766         JS_ASSERT(ALE_INDEX(ale) == globalScope->defs.length() - 1);
3767     } else {
3768         /*
3769          * Functions can be redeclared, and the last one takes effect. Check
3770          * for this and make sure to rewrite the definition.
3771          *
3772          * Note: This could overwrite an existing variable declaration, for
3773          * example:
3774          *   var c = []
3775          *   function c() { }
3776          *
3777          * This rewrite is allowed because the function will be statically
3778          * hoisted to the top of the script, and the |c = []| will just
3779          * overwrite it at runtime.
3780          */
3781         if (pn->pn_type == TOK_FUNCTION) {
3782             JS_ASSERT(pn->pn_arity = PN_FUNC);
3783             uint32 index = ALE_INDEX(ale);
3784             globalScope->defs[index].funbox = pn->pn_funbox;
3785         }
3786     }
3787
3788     pn->pn_dflags |= PND_GVAR;
3789
3790     return true;
3791 }
3792
3793 static bool
3794 BindTopLevelVar(JSContext *cx, BindData *data, JSAtomListElement *ale, JSParseNode *pn,
3795                 JSAtom *varname, JSTreeContext *tc)
3796 {
3797     JS_ASSERT(pn->pn_op == JSOP_NAME);
3798     JS_ASSERT(!tc->inFunction());
3799
3800     /* There's no need to optimize bindings if we're not compiling code. */
3801     if (!tc->compiling())
3802         return true;
3803
3804     /*
3805      * Bindings at top level in eval code aren't like bindings at top level in
3806      * regular code, and we must handle them specially.
3807      */
3808     if (tc->parser->callerFrame) {
3809         /*
3810          * If the eval code is not strict mode code, such bindings are created
3811          * from scratch in the the caller's environment (if the eval is direct)
3812          * or in the global environment (if the eval is indirect) -- and they
3813          * can be deleted.  Therefore we can't bind early.
3814          */
3815         if (!tc->inStrictMode())
3816             return true;
3817
3818         /*
3819          * But if the eval code is strict mode code, bindings are added to a
3820          * new environment specifically for that eval code's compilation, and
3821          * they can't be deleted.  Thus strict mode eval code does not affect
3822          * the caller's environment, and we can bind such names early.  (But
3823          * note: strict mode eval code can still affect the global environment
3824          * by performing an indirect eval of non-strict mode code.)
3825          *
3826          * However, optimizing such bindings requires either precarious
3827          * type-punning or, ideally, a new kind of Call object specifically for
3828          * strict mode eval frames.  Further, strict mode eval is not (yet)
3829          * common.  So for now (until we rewrite the scope chain to not use
3830          * objects?) just disable optimizations for top-level names in eval
3831          * code.
3832          */
3833         return true;
3834     }
3835
3836     if (pn->pn_dflags & PND_CONST)
3837         return true;
3838
3839     /*
3840      * If this is a global variable, we're compile-and-go, and a global object
3841      * is present, try to bake in either an already available slot or a
3842      * predicted slot that will be defined after compiling is completed.
3843      */
3844     return DefineGlobal(pn, tc->asCodeGenerator(), pn->pn_atom);
3845 }
3846
3847 static bool
3848 BindFunctionLocal(JSContext *cx, BindData *data, JSAtomListElement *ale, JSParseNode *pn,
3849                   JSAtom *name, JSTreeContext *tc)
3850 {
3851     JS_ASSERT(tc->inFunction());
3852
3853     if (name == cx->runtime->atomState.argumentsAtom) {
3854         pn->pn_op = JSOP_ARGUMENTS;
3855         pn->pn_dflags |= PND_BOUND;
3856         return true;
3857     }
3858
3859     BindingKind kind = tc->bindings.lookup(cx, name, NULL);
3860     if (kind == NONE) {
3861         /*
3862          * Property not found in current variable scope: we have not seen this
3863          * variable before, so bind a new local variable for it. Any locals
3864          * declared in a with statement body are handled at runtime, by script
3865          * prolog JSOP_DEFVAR opcodes generated for global and heavyweight-
3866          * function-local vars.
3867          */
3868         kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE;
3869
3870         uintN index = tc->bindings.countVars();
3871         if (!BindLocalVariable(cx, tc, name, kind, false))
3872             return false;
3873         pn->pn_op = JSOP_GETLOCAL;
3874         pn->pn_cookie.set(tc->staticLevel, index);
3875         pn->pn_dflags |= PND_BOUND;
3876         return true;
3877     }
3878
3879     if (kind == ARGUMENT) {
3880         JS_ASSERT(tc->inFunction());
3881         JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3882     } else {
3883         JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
3884     }
3885
3886     return true;
3887 }
3888
3889 static JSBool
3890 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3891 {
3892     JSParseNode *pn = data->pn;
3893
3894     /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3895     pn->pn_op = JSOP_NAME;
3896
3897     if (!CheckStrictBinding(cx, tc, atom, pn))
3898         return false;
3899
3900     JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3901
3902     if (stmt && stmt->type == STMT_WITH) {
3903         data->fresh = false;
3904         pn->pn_dflags |= PND_DEOPTIMIZED;
3905         tc->noteMightAliasLocals();
3906         return true;
3907     }
3908
3909     JSAtomListElement *ale = tc->decls.lookup(atom);
3910     JSOp op = data->op;
3911
3912     if (stmt || ale) {
3913         JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3914         JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3915
3916         if (dn_kind == JSDefinition::ARG) {
3917             JSAutoByteString name;
3918             if (!js_AtomToPrintableString(cx, atom, &name))
3919                 return JS_FALSE;
3920
3921             if (op == JSOP_DEFCONST) {
3922                 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3923                                          JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3924                                          name.ptr());
3925                 return JS_FALSE;
3926             }
3927             if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3928                                           JSREPORT_WARNING | JSREPORT_STRICT,
3929                                           JSMSG_VAR_HIDES_ARG, name.ptr())) {
3930                 return JS_FALSE;
3931             }
3932         } else {
3933             bool error = (op == JSOP_DEFCONST ||
3934                           dn_kind == JSDefinition::CONST ||
3935                           (dn_kind == JSDefinition::LET &&
3936                            (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3937
3938             if (cx->hasStrictOption()
3939                 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3940                 : error) {
3941                 JSAutoByteString name;
3942                 if (!js_AtomToPrintableString(cx, atom, &name) ||
3943                     !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3944                                               !error
3945                                               ? JSREPORT_WARNING | JSREPORT_STRICT
3946                                               : JSREPORT_ERROR,
3947                                               JSMSG_REDECLARED_VAR,
3948                                               JSDefinition::kindString(dn_kind),
3949                                               name.ptr())) {
3950                     return JS_FALSE;
3951                 }
3952             }
3953         }
3954     }
3955
3956     if (!ale) {
3957         if (!Define(pn, atom, tc))
3958             return JS_FALSE;
3959     } else {
3960         /*
3961          * A var declaration never recreates an existing binding, it restates
3962          * it and possibly reinitializes its value. Beware that if pn becomes a
3963          * use of ALE_DEFN(ale), and if we have an initializer for this var or
3964          * const (typically a const would ;-), then pn must be rewritten into a
3965          * TOK_ASSIGN node. See Variables, further below.
3966          *
3967          * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3968          * There the x definition is hoisted but the x = 2 assignment mutates
3969          * the block-local binding of x.
3970          */
3971         JSDefinition *dn = ALE_DEFN(ale);
3972
3973         data->fresh = false;
3974
3975         if (!pn->pn_used) {
3976             /* Make pnu be a fresh name node that uses dn. */
3977             JSParseNode *pnu = pn;
3978
3979             if (pn->pn_defn) {
3980                 pnu = NameNode::create(atom, tc);
3981                 if (!pnu)
3982                     return JS_FALSE;
3983             }
3984
3985             LinkUseToDef(pnu, dn, tc);
3986             pnu->pn_op = JSOP_NAME;
3987         }
3988
3989         while (dn->kind() == JSDefinition::LET) {
3990             do {
3991                 ale = ALE_NEXT(ale);
3992             } while (ale && ALE_ATOM(ale) != atom);
3993             if (!ale)
3994                 break;
3995             dn = ALE_DEFN(ale);
3996         }
3997
3998         if (ale) {
3999             JS_ASSERT_IF(data->op == JSOP_DEFCONST,
4000                          dn->kind() == JSDefinition::CONST);
4001             return JS_TRUE;
4002         }
4003
4004         /*
4005          * A var or const that is shadowed by one or more let bindings of the
4006          * same name, but that has not been declared until this point, must be
4007          * hoisted above the let bindings.
4008          */
4009         if (!pn->pn_defn) {
4010             JSHashEntry **hep;
4011
4012             ale = tc->lexdeps.rawLookup(atom, hep);
4013             if (ale) {
4014                 pn = ALE_DEFN(ale);
4015                 tc->lexdeps.rawRemove(tc->parser, ale, hep);
4016             } else {
4017                 JSParseNode *pn2 = NameNode::create(atom, tc);
4018                 if (!pn2)
4019                     return JS_FALSE;
4020
4021                 /* The token stream may be past the location for pn. */
4022                 pn2->pn_type = TOK_NAME;
4023                 pn2->pn_pos = pn->pn_pos;
4024                 pn = pn2;
4025             }
4026             pn->pn_op = JSOP_NAME;
4027         }
4028
4029         ale = tc->decls.add(tc->parser, atom, JSAtomList::HOIST);
4030         if (!ale)
4031             return JS_FALSE;
4032         ALE_SET_DEFN(ale, pn);
4033         pn->pn_defn = true;
4034         pn->pn_dflags &= ~PND_PLACEHOLDER;
4035     }
4036
4037     if (data->op == JSOP_DEFCONST)
4038         pn->pn_dflags |= PND_CONST;
4039
4040     if (tc->inFunction())
4041         return BindFunctionLocal(cx, data, ale, pn, atom, tc);
4042
4043     return BindTopLevelVar(cx, data, ale, pn, atom, tc);
4044 }
4045
4046 static bool
4047 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
4048 {
4049     JS_ASSERT(pn->pn_arity == PN_LIST);
4050     JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL ||
4051               pn->pn_op == JSOP_FUNCALL || pn->pn_op == JSOP_FUNAPPLY);
4052     if (!ReportStrictModeError(cx, TS(tc->parser), tc, pn, msg))
4053         return false;
4054
4055     JSParseNode *pn2 = pn->pn_head;
4056     if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
4057         ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
4058         return false;
4059     }
4060     pn->pn_xflags |= PNX_SETCALL;
4061     return true;
4062 }
4063
4064 static void
4065 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
4066 {
4067     if (pn->pn_used) {
4068         JSDefinition *dn = pn->pn_lexdef;
4069
4070         /*
4071          * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
4072          * occur as direct kids of the same block with no forward refs to x.
4073          */
4074         if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
4075             dn->isBlockChild() &&
4076             pn->isBlockChild() &&
4077             dn->pn_blockid == pn->pn_blockid &&
4078             dn->pn_pos.end <= pn->pn_pos.begin &&
4079             dn->dn_uses == pn) {
4080             dflag = PND_INITIALIZED;
4081         }
4082
4083         dn->pn_dflags |= dflag;
4084
4085         if (dn->pn_cookie.isFree() || dn->frameLevel() < tc->staticLevel)
4086             tc->flags |= TCF_FUN_SETS_OUTER_NAME;
4087     }
4088
4089     pn->pn_dflags |= dflag;
4090
4091     /*
4092      * Both arguments and the enclosing function's name are immutable bindings
4093      * in ES5, so assignments to them must do nothing or throw a TypeError
4094      * depending on code strictness.  Assignment to arguments is a syntax error
4095      * in strict mode and thus cannot happen.  Outside strict mode, we optimize
4096      * away assignment to the function name.  For assignment to function name
4097      * to fail in strict mode, we must have a binding for it in the scope
4098      * chain; we ensure this happens by making such functions heavyweight.
4099      */
4100     JSAtom *lname = pn->pn_atom;
4101     if (lname == cx->runtime->atomState.argumentsAtom ||
4102         (tc->inFunction() && lname == tc->fun()->atom)) {
4103         tc->flags |= TCF_FUN_HEAVYWEIGHT;
4104     }
4105 }
4106
4107 #if JS_HAS_DESTRUCTURING
4108
4109 static JSBool
4110 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
4111                      JSTreeContext *tc)
4112 {
4113     JSAtom *atom;
4114
4115     /*
4116      * Destructuring is a form of assignment, so just as for an initialized
4117      * simple variable, we must check for assignment to 'arguments' and flag
4118      * the enclosing function (if any) as heavyweight.
4119      */
4120     JS_ASSERT(pn->pn_type == TOK_NAME);
4121     atom = pn->pn_atom;
4122     if (atom == cx->runtime->atomState.argumentsAtom)
4123         tc->flags |= TCF_FUN_HEAVYWEIGHT;
4124
4125     data->pn = pn;
4126     if (!data->binder(cx, data, atom, tc))
4127         return JS_FALSE;
4128
4129     /*
4130      * Select the appropriate name-setting opcode, respecting eager selection
4131      * done by the data->binder function.
4132      */
4133     if (pn->pn_dflags & PND_BOUND) {
4134         JS_ASSERT(!(pn->pn_dflags & PND_GVAR));
4135         pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
4136                     ? JSOP_SETNAME
4137                     : JSOP_SETLOCAL;
4138     } else {
4139         pn->pn_op = (data->op == JSOP_DEFCONST)
4140                     ? JSOP_SETCONST
4141                     : JSOP_SETNAME;
4142     }
4143
4144     if (data->op == JSOP_DEFCONST)
4145         pn->pn_dflags |= PND_CONST;
4146
4147     NoteLValue(cx, pn, tc, PND_INITIALIZED);
4148     return JS_TRUE;
4149 }
4150
4151 /*
4152  * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
4153  * LHS expression except a destructuring initialiser, and R is on the stack.
4154  * Because R is already evaluated, the usual LHS-specialized bytecodes won't
4155  * work.  After pushing R[P] we need to evaluate Q's "reference base" QB and
4156  * then push its property name QN.  At this point the stack looks like
4157  *
4158  *   [... R, R[P], QB, QN]
4159  *
4160  * We need to set QB[QN] = R[P].  This is a job for JSOP_ENUMELEM, which takes
4161  * its operands with left-hand side above right-hand side:
4162  *
4163  *   [rval, lval, xval]
4164  *
4165  * and pops all three values, setting lval[xval] = rval.  But we cannot select
4166  * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
4167  * which can be optimized further.  So we select JSOP_SETNAME.
4168  */
4169 static JSBool
4170 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4171 {
4172     switch (pn->pn_type) {
4173       case TOK_NAME:
4174         NoteLValue(cx, pn, tc);
4175         /* FALL THROUGH */
4176
4177       case TOK_DOT:
4178       case TOK_LB:
4179         /*
4180          * We may be called on a name node that has already been specialized,
4181          * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
4182          * case. See bug 558633.
4183          */
4184         if (!(js_CodeSpec[pn->pn_op].format & JOF_SET))
4185             pn->pn_op = JSOP_SETNAME;
4186         break;
4187
4188       case TOK_LP:
4189         if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
4190             return JS_FALSE;
4191         break;
4192
4193 #if JS_HAS_XML_SUPPORT
4194       case TOK_UNARYOP:
4195         if (pn->pn_op == JSOP_XMLNAME) {
4196             pn->pn_op = JSOP_BINDXMLNAME;
4197             break;
4198         }
4199         /* FALL THROUGH */
4200 #endif
4201
4202       default:
4203         ReportCompileErrorNumber(cx, TS(tc->parser), pn,
4204                                  JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
4205         return JS_FALSE;
4206     }
4207
4208     return JS_TRUE;
4209 }
4210
4211 typedef struct FindPropValData {
4212     uint32          numvars;    /* # of destructuring vars in left side */
4213     uint32          maxstep;    /* max # of steps searching right side */
4214     JSDHashTable    table;      /* hash table for O(1) right side search */
4215 } FindPropValData;
4216
4217 typedef struct FindPropValEntry {
4218     JSDHashEntryHdr hdr;
4219     JSParseNode     *pnkey;
4220     JSParseNode     *pnval;
4221 } FindPropValEntry;
4222
4223 #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \
4224     JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY &&                             \
4225                ((pnkey)->pn_type == TOK_NUMBER ||                             \
4226                 (pnkey)->pn_type == TOK_STRING ||                             \
4227                 (pnkey)->pn_type == TOK_NAME)) ||                             \
4228                ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
4229
4230 static JSDHashNumber
4231 HashFindPropValKey(JSDHashTable *table, const void *key)
4232 {
4233     const JSParseNode *pnkey = (const JSParseNode *)key;
4234
4235     ASSERT_VALID_PROPERTY_KEY(pnkey);
4236     return (pnkey->pn_type == TOK_NUMBER)
4237            ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
4238            : ATOM_HASH(pnkey->pn_atom);
4239 }
4240
4241 static JSBool
4242 MatchFindPropValEntry(JSDHashTable *table,
4243                       const JSDHashEntryHdr *entry,
4244                       const void *key)
4245 {
4246     const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
4247     const JSParseNode *pnkey = (const JSParseNode *)key;
4248
4249     ASSERT_VALID_PROPERTY_KEY(pnkey);
4250     return pnkey->pn_type == fpve->pnkey->pn_type &&
4251            ((pnkey->pn_type == TOK_NUMBER)
4252             ? pnkey->pn_dval == fpve->pnkey->pn_dval
4253             : pnkey->pn_atom == fpve->pnkey->pn_atom);
4254 }
4255
4256 static const JSDHashTableOps FindPropValOps = {
4257     JS_DHashAllocTable,
4258     JS_DHashFreeTable,
4259     HashFindPropValKey,
4260     MatchFindPropValEntry,
4261     JS_DHashMoveEntryStub,
4262     JS_DHashClearEntryStub,
4263     JS_DHashFinalizeStub,
4264     NULL
4265 };
4266
4267 #define STEP_HASH_THRESHOLD     10
4268 #define BIG_DESTRUCTURING        5
4269 #define BIG_OBJECT_INIT         20
4270
4271 static JSParseNode *
4272 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
4273 {
4274     FindPropValEntry *entry;
4275     JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
4276     uint32 step;
4277
4278     /* If we have a hash table, use it as the sole source of truth. */
4279     if (data->table.ops) {
4280         entry = (FindPropValEntry *)
4281                 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
4282         return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
4283     }
4284
4285     /* If pn is not an object initialiser node, we can't do anything here. */
4286     if (pn->pn_type != TOK_RC)
4287         return NULL;
4288
4289     /*
4290      * We must search all the way through pn's list, to handle the case of an
4291      * id duplicated for two or more property initialisers.
4292      */
4293     pnhit = NULL;
4294     step = 0;
4295     ASSERT_VALID_PROPERTY_KEY(pnid);
4296     pnhead = pn->pn_head;
4297     if (pnid->pn_type == TOK_NUMBER) {
4298         for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
4299             JS_ASSERT(pnprop->pn_type == TOK_COLON);
4300             if (pnprop->pn_op == JSOP_NOP) {
4301                 pnkey = pnprop->pn_left;
4302                 ASSERT_VALID_PROPERTY_KEY(pnkey);
4303                 if (pnkey->pn_type == TOK_NUMBER &&
4304                     pnkey->pn_dval == pnid->pn_dval) {
4305                     pnhit = pnprop;
4306                 }
4307                 ++step;
4308             }
4309         }
4310     } else {
4311         for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
4312             JS_ASSERT(pnprop->pn_type == TOK_COLON);
4313             if (pnprop->pn_op == JSOP_NOP) {
4314                 pnkey = pnprop->pn_left;
4315                 ASSERT_VALID_PROPERTY_KEY(pnkey);
4316                 if (pnkey->pn_type == pnid->pn_type &&
4317                     pnkey->pn_atom == pnid->pn_atom) {
4318                     pnhit = pnprop;
4319                 }
4320                 ++step;
4321             }
4322         }
4323     }
4324     if (!pnhit)
4325         return NULL;
4326
4327     /* Hit via full search -- see whether it's time to create the hash table. */
4328     JS_ASSERT(!data->table.ops);
4329     if (step > data->maxstep) {
4330         data->maxstep = step;
4331         if (step >= STEP_HASH_THRESHOLD &&
4332             data->numvars >= BIG_DESTRUCTURING &&
4333             pn->pn_count >= BIG_OBJECT_INIT &&
4334             JS_DHashTableInit(&data->table, &FindPropValOps, pn,
4335                               sizeof(FindPropValEntry),
4336                               JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
4337         {
4338             for (pn = pnhead; pn; pn = pn->pn_next) {
4339                 JS_ASSERT(pnprop->pn_type == TOK_COLON);
4340                 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
4341                 entry = (FindPropValEntry *)
4342                         JS_DHashTableOperate(&data->table, pn->pn_left,
4343                                              JS_DHASH_ADD);
4344                 entry->pnval = pn->pn_right;
4345             }
4346         }
4347     }
4348     return pnhit->pn_right;
4349 }
4350
4351 /*
4352  * Destructuring patterns can appear in two kinds of contexts:
4353  *
4354  * - assignment-like: assignment expressions and |for| loop heads.  In
4355  *   these cases, the patterns' property value positions can be
4356  *   arbitrary lvalue expressions; the destructuring is just a fancy
4357  *   assignment.
4358  *
4359  * - declaration-like: |var| and |let| declarations, functions' formal
4360  *   parameter lists, |catch| clauses, and comprehension tails.  In
4361  *   these cases, the patterns' property value positions must be
4362  *   simple names; the destructuring defines them as new variables.
4363  *
4364  * In both cases, other code parses the pattern as an arbitrary
4365  * primaryExpr, and then, here in CheckDestructuring, verify that the
4366  * tree is a valid destructuring expression.
4367  *
4368  * In assignment-like contexts, we parse the pattern with the
4369  * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
4370  * pattern are parsed normally.  primaryExpr links variable references
4371  * into the appropriate use chains; creates placeholder definitions;
4372  * and so on.  CheckDestructuring is called with |data| NULL (since we
4373  * won't be binding any new names), and we specialize lvalues as
4374  * appropriate.  If right is NULL, we just check for well-formed lvalues.
4375  *
4376  * In declaration-like contexts, the normal variable reference
4377  * processing would just be an obstruction, because we're going to
4378  * define the names that appear in the property value positions as new
4379  * variables anyway.  In this case, we parse the pattern with
4380  * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
4381  * whatever name nodes it creates unconnected.  Then, here in
4382  * CheckDestructuring, we require the pattern's property value
4383  * positions to be simple names, and define them as appropriate to the
4384  * context.  For these calls, |data| points to the right sort of
4385  * BindData.
4386  *
4387  * See also UndominateInitializers, immediately below. If you change
4388  * either of these functions, you might have to change the other to
4389  * match.
4390  */
4391 static JSBool
4392 CheckDestructuring(JSContext *cx, BindData *data,
4393                    JSParseNode *left, JSParseNode *right,
4394                    JSTreeContext *tc)
4395 {
4396     JSBool ok;
4397     FindPropValData fpvd;
4398     JSParseNode *lhs, *rhs, *pn, *pn2;
4399
4400     if (left->pn_type == TOK_ARRAYCOMP) {
4401         ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
4402                                  JSMSG_ARRAY_COMP_LEFTSIDE);
4403         return JS_FALSE;
4404     }
4405
4406 #if JS_HAS_DESTRUCTURING_SHORTHAND
4407     if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
4408         ReportCompileErrorNumber(cx, TS(tc->parser), right, JSREPORT_ERROR,
4409                                  JSMSG_BAD_OBJECT_INIT);
4410         return JS_FALSE;
4411     }
4412 #endif
4413
4414     fpvd.table.ops = NULL;
4415     lhs = left->pn_head;
4416     if (left->pn_type == TOK_RB) {
4417         rhs = (right && right->pn_type == left->pn_type)
4418               ? right->pn_head
4419               : NULL;
4420
4421         while (lhs) {
4422             pn = lhs, pn2 = rhs;
4423
4424             /* Nullary comma is an elision; binary comma is an expression.*/
4425             if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
4426                 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4427                     ok = CheckDestructuring(cx, data, pn, pn2, tc);
4428                 } else {
4429                     if (data) {
4430                         if (pn->pn_type != TOK_NAME)
4431                             goto no_var_name;
4432
4433                         ok = BindDestructuringVar(cx, data, pn, tc);
4434                     } else {
4435                         ok = BindDestructuringLHS(cx, pn, tc);
4436                     }
4437                 }
4438                 if (!ok)
4439                     goto out;
4440             }
4441
4442             lhs = lhs->pn_next;
4443             if (rhs)
4444                 rhs = rhs->pn_next;
4445         }
4446     } else {
4447         JS_ASSERT(left->pn_type == TOK_RC);
4448         fpvd.numvars = left->pn_count;
4449         fpvd.maxstep = 0;
4450         rhs = NULL;
4451
4452         while (lhs) {
4453             JS_ASSERT(lhs->pn_type == TOK_COLON);
4454             pn = lhs->pn_right;
4455
4456             if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4457                 if (right)
4458                     rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4459                 ok = CheckDestructuring(cx, data, pn, rhs, tc);
4460             } else if (data) {
4461                 if (pn->pn_type != TOK_NAME)
4462                     goto no_var_name;
4463
4464                 ok = BindDestructuringVar(cx, data, pn, tc);
4465             } else {
4466                 ok = BindDestructuringLHS(cx, pn, tc);
4467             }
4468             if (!ok)
4469                 goto out;
4470
4471             lhs = lhs->pn_next;
4472         }
4473     }
4474
4475     /*
4476      * The catch/finally handler implementation in the interpreter assumes
4477      * that any operation that introduces a new scope (like a "let" or "with"
4478      * block) increases the stack depth. This way, it is possible to restore
4479      * the scope chain based on stack depth of the handler alone. "let" with
4480      * an empty destructuring pattern like in
4481      *
4482      *   let [] = 1;
4483      *
4484      * would violate this assumption as the there would be no let locals to
4485      * store on the stack. To satisfy it we add an empty property to such
4486      * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
4487      * slots, would be always positive.
4488      *
4489      * Note that we add such a property even if the block has locals due to
4490      * later let declarations in it. We optimize for code simplicity here,
4491      * not the fastest runtime performance with empty [] or {}.
4492      */
4493     if (data &&
4494         data->binder == BindLet &&
4495         OBJ_BLOCK_COUNT(cx, tc->blockChain()) == 0) {
4496         ok = !!js_DefineNativeProperty(cx, tc->blockChain(),
4497                                        ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
4498                                        UndefinedValue(), NULL, NULL,
4499                                        JSPROP_ENUMERATE | JSPROP_PERMANENT,
4500                                        Shape::HAS_SHORTID, 0, NULL);
4501         if (!ok)
4502             goto out;
4503     }
4504
4505     ok = JS_TRUE;
4506
4507   out:
4508     if (fpvd.table.ops)
4509         JS_DHashTableFinish(&fpvd.table);
4510     return ok;
4511
4512   no_var_name:
4513     ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
4514                              JSMSG_NO_VARIABLE_NAME);
4515     ok = JS_FALSE;
4516     goto out;
4517 }
4518
4519 /*
4520  * This is a greatly pared down version of CheckDestructuring that extends the
4521  * pn_pos.end source coordinate of each name in a destructuring binding such as
4522  *
4523  *   var [x, y] = [function () y, 42];
4524  *
4525  * to cover its corresponding initializer, so that the initialized binding does
4526  * not appear to dominate any closures in its initializer. See bug 496134.
4527  *
4528  * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
4529  * very precise. With one-pass SSA construction from structured source code
4530  * (see "Single-Pass Generation of Static Single Assignment Form for Structured
4531  * Languages", Brandis and Mössenböck), we could do much better.
4532  *
4533  * See CheckDestructuring, immediately above. If you change either of these
4534  * functions, you might have to change the other to match.
4535  */
4536 static JSBool
4537 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
4538 {
4539     FindPropValData fpvd;
4540     JSParseNode *lhs, *rhs;
4541
4542     JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
4543     JS_ASSERT(right);
4544
4545 #if JS_HAS_DESTRUCTURING_SHORTHAND
4546     if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
4547         ReportCompileErrorNumber(tc->parser->context, TS(tc->parser), right, JSREPORT_ERROR,
4548                                  JSMSG_BAD_OBJECT_INIT);
4549         return JS_FALSE;
4550     }
4551 #endif
4552
4553     if (right->pn_type != left->pn_type)
4554         return JS_TRUE;
4555
4556     fpvd.table.ops = NULL;
4557     lhs = left->pn_head;
4558     if (left->pn_type == TOK_RB) {
4559         rhs = right->pn_head;
4560
4561         while (lhs && rhs) {
4562             /* Nullary comma is an elision; binary comma is an expression.*/
4563             if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
4564                 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
4565                     if (!UndominateInitializers(lhs, rhs, tc))
4566                         return JS_FALSE;
4567                 } else {
4568                     lhs->pn_pos.end = rhs->pn_pos.end;
4569                 }
4570             }
4571
4572             lhs = lhs->pn_next;
4573             rhs = rhs->pn_next;
4574         }
4575     } else {
4576         JS_ASSERT(left->pn_type == TOK_RC);
4577         fpvd.numvars = left->pn_count;
4578         fpvd.maxstep = 0;
4579
4580         while (lhs) {
4581             JS_ASSERT(lhs->pn_type == TOK_COLON);
4582             JSParseNode *pn = lhs->pn_right;
4583
4584             rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4585             if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4586                 if (rhs && !UndominateInitializers(pn, rhs, tc))
4587                     return JS_FALSE;
4588             } else {
4589                 if (rhs)
4590                     pn->pn_pos.end = rhs->pn_pos.end;
4591             }
4592
4593             lhs = lhs->pn_next;
4594         }
4595     }
4596     return JS_TRUE;
4597 }
4598
4599 JSParseNode *
4600 Parser::destructuringExpr(BindData *data, TokenKind tt)
4601 {
4602     JSParseNode *pn;
4603
4604     tc->flags |= TCF_DECL_DESTRUCTURING;
4605     pn = primaryExpr(tt, JS_FALSE);
4606     tc->flags &= ~TCF_DECL_DESTRUCTURING;
4607     if (!pn)
4608         return NULL;
4609     if (!CheckDestructuring(context, data, pn, NULL, tc))
4610         return NULL;
4611     return pn;
4612 }
4613
4614 /*
4615  * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4616  * This function assumes the cloned tree is for use in the same statement and
4617  * binding context as the original tree.
4618  */
4619 static JSParseNode *
4620 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4621 {
4622     JSParseNode *pn, *pn2, *opn2;
4623
4624     pn = NewOrRecycledNode(tc);
4625     if (!pn)
4626         return NULL;
4627     pn->pn_type = opn->pn_type;
4628     pn->pn_pos = opn->pn_pos;
4629     pn->pn_op = opn->pn_op;
4630     pn->pn_used = opn->pn_used;
4631     pn->pn_defn = opn->pn_defn;
4632     pn->pn_arity = opn->pn_arity;
4633     pn->pn_parens = opn->pn_parens;
4634
4635     switch (pn->pn_arity) {
4636 #define NULLCHECK(e)    JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4637
4638       case PN_FUNC:
4639         NULLCHECK(pn->pn_funbox =
4640                   tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
4641         NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4642         pn->pn_cookie = opn->pn_cookie;
4643         pn->pn_dflags = opn->pn_dflags;
4644         pn->pn_blockid = opn->pn_blockid;
4645         break;
4646
4647       case PN_LIST:
4648         pn->makeEmpty();
4649         for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4650             NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4651             pn->append(pn2);
4652         }
4653         pn->pn_xflags = opn->pn_xflags;
4654         break;
4655
4656       case PN_TERNARY:
4657         NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4658         NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4659         NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4660         break;
4661
4662       case PN_BINARY:
4663         NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4664         if (opn->pn_right != opn->pn_left)
4665             NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4666         else
4667             pn->pn_right = pn->pn_left;
4668         pn->pn_pval = opn->pn_pval;
4669         pn->pn_iflags = opn->pn_iflags;
4670         break;
4671
4672       case PN_UNARY:
4673         NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4674         pn->pn_num = opn->pn_num;
4675         pn->pn_hidden = opn->pn_hidden;
4676         break;
4677
4678       case PN_NAME:
4679         // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4680         pn->pn_u = opn->pn_u;
4681         if (opn->pn_used) {
4682             /*
4683              * The old name is a use of its pn_lexdef. Make the clone also be a
4684              * use of that definition.
4685              */
4686             JSDefinition *dn = pn->pn_lexdef;
4687
4688             pn->pn_link = dn->dn_uses;
4689             dn->dn_uses = pn;
4690         } else if (opn->pn_expr) {
4691             NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4692
4693             /*
4694              * If the old name is a definition, the new one has pn_defn set.
4695              * Make the old name a use of the new node.
4696              */
4697             if (opn->pn_defn) {
4698                 opn->pn_defn = false;
4699                 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4700             }
4701         }
4702         break;
4703
4704       case PN_NAMESET:
4705         pn->pn_names = opn->pn_names;
4706         NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4707         break;
4708
4709       case PN_NULLARY:
4710         // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4711         pn->pn_u = opn->pn_u;
4712         break;
4713
4714 #undef NULLCHECK
4715     }
4716     return pn;
4717 }
4718
4719 #endif /* JS_HAS_DESTRUCTURING */
4720
4721 extern const char js_with_statement_str[];
4722
4723 static JSParseNode *
4724 ContainsStmt(JSParseNode *pn, TokenKind tt)
4725 {
4726     JSParseNode *pn2, *pnt;
4727
4728     if (!pn)
4729         return NULL;
4730     if (PN_TYPE(pn) == tt)
4731         return pn;
4732     switch (pn->pn_arity) {
4733       case PN_LIST:
4734         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4735             pnt = ContainsStmt(pn2, tt);
4736             if (pnt)
4737                 return pnt;
4738         }
4739         break;
4740       case PN_TERNARY:
4741         pnt = ContainsStmt(pn->pn_kid1, tt);
4742         if (pnt)
4743             return pnt;
4744         pnt = ContainsStmt(pn->pn_kid2, tt);
4745         if (pnt)
4746             return pnt;
4747         return ContainsStmt(pn->pn_kid3, tt);
4748       case PN_BINARY:
4749         /*
4750          * Limit recursion if pn is a binary expression, which can't contain a
4751          * var statement.
4752          */
4753         if (pn->pn_op != JSOP_NOP)
4754             return NULL;
4755         pnt = ContainsStmt(pn->pn_left, tt);
4756         if (pnt)
4757             return pnt;
4758         return ContainsStmt(pn->pn_right, tt);
4759       case PN_UNARY:
4760         if (pn->pn_op != JSOP_NOP)
4761             return NULL;
4762         return ContainsStmt(pn->pn_kid, tt);
4763       case PN_NAME:
4764         return ContainsStmt(pn->maybeExpr(), tt);
4765       case PN_NAMESET:
4766         return ContainsStmt(pn->pn_tree, tt);
4767       default:;
4768     }
4769     return NULL;
4770 }
4771
4772 JSParseNode *
4773 Parser::returnOrYield(bool useAssignExpr)
4774 {
4775     TokenKind tt, tt2;
4776     JSParseNode *pn, *pn2;
4777
4778     tt = tokenStream.currentToken().type;
4779     if (tt == TOK_RETURN && !tc->inFunction()) {
4780         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4781         return NULL;
4782     }
4783
4784     pn = UnaryNode::create(tc);
4785     if (!pn)
4786         return NULL;
4787
4788 #if JS_HAS_GENERATORS
4789     if (tt == TOK_YIELD)
4790         tc->flags |= TCF_FUN_IS_GENERATOR;
4791 #endif
4792
4793     /* This is ugly, but we don't want to require a semicolon. */
4794     tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
4795     if (tt2 == TOK_ERROR)
4796         return NULL;
4797
4798     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4799 #if JS_HAS_GENERATORS
4800         && (tt != TOK_YIELD ||
4801             (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4802              tt2 != TOK_COLON && tt2 != TOK_COMMA))
4803 #endif
4804         ) {
4805         pn2 = useAssignExpr ? assignExpr() : expr();
4806         if (!pn2)
4807             return NULL;
4808 #if JS_HAS_GENERATORS
4809         if (tt == TOK_RETURN)
4810 #endif
4811             tc->flags |= TCF_RETURN_EXPR;
4812         pn->pn_pos.end = pn2->pn_pos.end;
4813         pn->pn_kid = pn2;
4814     } else {
4815 #if JS_HAS_GENERATORS
4816         if (tt == TOK_RETURN)
4817 #endif
4818             tc->flags |= TCF_RETURN_VOID;
4819     }
4820
4821     if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4822         /* As in Python (see PEP-255), disallow return v; in generators. */
4823         ReportBadReturn(context, tc, JSREPORT_ERROR,
4824                         JSMSG_BAD_GENERATOR_RETURN,
4825                         JSMSG_BAD_ANON_GENERATOR_RETURN);
4826         return NULL;
4827     }
4828
4829     if (context->hasStrictOption() &&
4830         (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4831         !ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4832                          JSMSG_NO_RETURN_VALUE,
4833                          JSMSG_ANON_NO_RETURN_VALUE)) {
4834         return NULL;
4835     }
4836
4837     return pn;
4838 }
4839
4840 static JSParseNode *
4841 PushLexicalScope(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
4842                  JSStmtInfo *stmt)
4843 {
4844     JSParseNode *pn;
4845     JSObject *obj;
4846     JSObjectBox *blockbox;
4847
4848     pn = LexicalScopeNode::create(tc);
4849     if (!pn)
4850         return NULL;
4851
4852     obj = js_NewBlockObject(cx);
4853     if (!obj)
4854         return NULL;
4855
4856     blockbox = tc->parser->newObjectBox(obj);
4857     if (!blockbox)
4858         return NULL;
4859
4860     js_PushBlockScope(tc, stmt, blockbox, -1);
4861     pn->pn_type = TOK_LEXICALSCOPE;
4862     pn->pn_op = JSOP_LEAVEBLOCK;
4863     pn->pn_objbox = blockbox;
4864     pn->pn_cookie.makeFree();
4865     pn->pn_dflags = 0;
4866     if (!GenerateBlockId(tc, stmt->blockid))
4867         return NULL;
4868     pn->pn_blockid = stmt->blockid;
4869     return pn;
4870 }
4871
4872 #if JS_HAS_BLOCK_SCOPE
4873
4874 JSParseNode *
4875 Parser::letBlock(JSBool statement)
4876 {
4877     JSParseNode *pn, *pnblock, *pnlet;
4878     JSStmtInfo stmtInfo;
4879
4880     JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
4881
4882     /* Create the let binary node. */
4883     pnlet = BinaryNode::create(tc);
4884     if (!pnlet)
4885         return NULL;
4886
4887     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4888
4889     /* This is a let block or expression of the form: let (a, b, c) .... */
4890     pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
4891     if (!pnblock)
4892         return NULL;
4893     pn = pnblock;
4894     pn->pn_expr = pnlet;
4895
4896     pnlet->pn_left = variables(true);
4897     if (!pnlet->pn_left)
4898         return NULL;
4899     pnlet->pn_left->pn_xflags = PNX_POPVAR;
4900
4901     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4902
4903     if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
4904         /*
4905          * Strict mode eliminates a grammar ambiguity with unparenthesized
4906          * LetExpressions in an ExpressionStatement. If followed immediately
4907          * by an arguments list, it's ambiguous whether the let expression
4908          * is the callee or the call is inside the let expression body.
4909          *
4910          * See bug 569464.
4911          */
4912         if (!ReportStrictModeError(context, &tokenStream, tc, pnlet,
4913                                    JSMSG_STRICT_CODE_LET_EXPR_STMT)) {
4914             return NULL;
4915         }
4916
4917         /*
4918          * If this is really an expression in let statement guise, then we
4919          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4920          * the return value of the expression.
4921          */
4922         pn = UnaryNode::create(tc);
4923         if (!pn)
4924             return NULL;
4925         pn->pn_type = TOK_SEMI;
4926         pn->pn_num = -1;
4927         pn->pn_kid = pnblock;
4928
4929         statement = JS_FALSE;
4930     }
4931
4932     if (statement) {
4933         pnlet->pn_right = statements();
4934         if (!pnlet->pn_right)
4935             return NULL;
4936         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4937     } else {
4938         /*
4939          * Change pnblock's opcode to the variant that propagates the last
4940          * result down after popping the block, and clear statement.
4941          */
4942         pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4943         pnlet->pn_right = assignExpr();
4944         if (!pnlet->pn_right)
4945             return NULL;
4946     }
4947
4948     PopStatement(tc);
4949     return pn;
4950 }
4951
4952 #endif /* JS_HAS_BLOCK_SCOPE */
4953
4954 static bool
4955 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4956 {
4957     js_PushStatement(tc, stmt, type, -1);
4958     return GenerateBlockId(tc, stmt->blockid);
4959 }
4960
4961 static JSParseNode *
4962 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4963 {
4964     JSParseNode *pn = NULL;
4965
4966     JSAtomListElement *ale = tc->decls.lookup(atom);
4967     if (ale) {
4968         pn = ALE_DEFN(ale);
4969         JS_ASSERT(!pn->isPlaceholder());
4970     } else {
4971         ale = tc->lexdeps.lookup(atom);
4972         if (ale) {
4973             pn = ALE_DEFN(ale);
4974             JS_ASSERT(pn->isPlaceholder());
4975         }
4976     }
4977
4978     if (pn) {
4979         JS_ASSERT(pn->pn_defn);
4980
4981         /*
4982          * A let binding at top level becomes a var before we get here, so if
4983          * pn and tc have the same blockid then that id must not be the bodyid.
4984          * If pn is a forward placeholder definition from the same or a higher
4985          * block then we claim it.
4986          */
4987         JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4988                      pn->pn_blockid != tc->bodyid);
4989
4990         if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4991             if (let)
4992                 pn->pn_blockid = tc->blockid();
4993
4994             tc->lexdeps.remove(tc->parser, atom);
4995             return pn;
4996         }
4997     }
4998
4999     /* Make a new node for this declarator name (or destructuring pattern). */
5000     pn = NameNode::create(atom, tc);
5001     if (!pn)
5002         return NULL;
5003     return pn;
5004 }
5005
5006 #if JS_HAS_BLOCK_SCOPE
5007 static bool
5008 RebindLets(JSParseNode *pn, JSTreeContext *tc)
5009 {
5010     if (!pn)
5011         return true;
5012
5013     switch (pn->pn_arity) {
5014       case PN_LIST:
5015         for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
5016             RebindLets(pn2, tc);
5017         break;
5018
5019       case PN_TERNARY:
5020         RebindLets(pn->pn_kid1, tc);
5021         RebindLets(pn->pn_kid2, tc);
5022         RebindLets(pn->pn_kid3, tc);
5023         break;
5024
5025       case PN_BINARY:
5026         RebindLets(pn->pn_left, tc);
5027         RebindLets(pn->pn_right, tc);
5028         break;
5029
5030       case PN_UNARY:
5031         RebindLets(pn->pn_kid, tc);
5032         break;
5033
5034       case PN_FUNC:
5035         RebindLets(pn->pn_body, tc);
5036         break;
5037
5038       case PN_NAME:
5039         RebindLets(pn->maybeExpr(), tc);
5040
5041         if (pn->pn_defn) {
5042             JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
5043         } else if (pn->pn_used) {
5044             if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
5045                 ForgetUse(pn);
5046
5047                 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
5048                 if (ale) {
5049                     while ((ale = ALE_NEXT(ale)) != NULL) {
5050                         if (ALE_ATOM(ale) == pn->pn_atom) {
5051                             LinkUseToDef(pn, ALE_DEFN(ale), tc);
5052                             return true;
5053                         }
5054                     }
5055                 }
5056
5057                 ale = tc->lexdeps.lookup(pn->pn_atom);
5058                 if (!ale) {
5059                     ale = MakePlaceholder(pn, tc);
5060                     if (!ale)
5061                         return NULL;
5062                 }
5063                 LinkUseToDef(pn, ALE_DEFN(ale), tc);
5064             }
5065         }
5066         break;
5067
5068       case PN_NAMESET:
5069         RebindLets(pn->pn_tree, tc);
5070         break;
5071     }
5072
5073     return true;
5074 }
5075 #endif /* JS_HAS_BLOCK_SCOPE */
5076
5077 JSParseNode *
5078 Parser::switchStatement()
5079 {
5080     JSParseNode *pn5, *saveBlock;
5081     JSBool seenDefault = JS_FALSE;
5082
5083     JSParseNode *pn = BinaryNode::create(tc);
5084     if (!pn)
5085         return NULL;
5086     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
5087
5088     /* pn1 points to the switch's discriminant. */
5089     JSParseNode *pn1 = parenExpr();
5090     if (!pn1)
5091         return NULL;
5092
5093     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
5094     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
5095
5096     /*
5097      * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
5098      * because that function states tc->topStmt->blockid.
5099      */
5100     JSStmtInfo stmtInfo;
5101     js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
5102
5103     /* pn2 is a list of case nodes. The default case has pn_left == NULL */
5104     JSParseNode *pn2 = ListNode::create(tc);
5105     if (!pn2)
5106         return NULL;
5107     pn2->makeEmpty();
5108     if (!GenerateBlockIdForStmtNode(pn2, tc))
5109         return NULL;
5110     saveBlock = tc->blockNode;
5111     tc->blockNode = pn2;
5112
5113     TokenKind tt;
5114     while ((tt = tokenStream.getToken()) != TOK_RC) {
5115         JSParseNode *pn3;
5116         switch (tt) {
5117           case TOK_DEFAULT:
5118             if (seenDefault) {
5119                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS);
5120                 return NULL;
5121             }
5122             seenDefault = JS_TRUE;
5123             /* FALL THROUGH */
5124
5125           case TOK_CASE:
5126           {
5127             pn3 = BinaryNode::create(tc);
5128             if (!pn3)
5129                 return NULL;
5130             if (tt == TOK_CASE) {
5131                 pn3->pn_left = expr();
5132                 if (!pn3->pn_left)
5133                     return NULL;
5134             }
5135             pn2->append(pn3);
5136             if (pn2->pn_count == JS_BIT(16)) {
5137                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_CASES);
5138                 return NULL;
5139             }
5140             break;
5141           }
5142
5143           case TOK_ERROR:
5144             return NULL;
5145
5146           default:
5147             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_SWITCH);
5148             return NULL;
5149         }
5150         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
5151
5152         JSParseNode *pn4 = ListNode::create(tc);
5153         if (!pn4)
5154             return NULL;
5155         pn4->pn_type = TOK_LC;
5156         pn4->makeEmpty();
5157         while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
5158                tt != TOK_CASE && tt != TOK_DEFAULT) {
5159             if (tt == TOK_ERROR)
5160                 return NULL;
5161             pn5 = statement();
5162             if (!pn5)
5163                 return NULL;
5164             pn4->pn_pos.end = pn5->pn_pos.end;
5165             pn4->append(pn5);
5166         }
5167
5168         /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
5169         if (pn4->pn_head)
5170             pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
5171         pn3->pn_pos.end = pn4->pn_pos.end;
5172         pn3->pn_right = pn4;
5173     }
5174
5175     /*
5176      * Handle the case where there was a let declaration in any case in
5177      * the switch body, but not within an inner block.  If it replaced
5178      * tc->blockNode with a new block node then we must refresh pn2 and
5179      * then restore tc->blockNode.
5180      */
5181     if (tc->blockNode != pn2)
5182         pn2 = tc->blockNode;
5183     tc->blockNode = saveBlock;
5184     PopStatement(tc);
5185
5186     pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
5187     pn->pn_left = pn1;
5188     pn->pn_right = pn2;
5189     return pn;
5190 }
5191
5192 JSParseNode *
5193 Parser::forStatement()
5194 {
5195     JSParseNode *pnseq = NULL;
5196 #if JS_HAS_BLOCK_SCOPE
5197     JSParseNode *pnlet = NULL;
5198     JSStmtInfo blockInfo;
5199 #endif
5200
5201     /* A FOR node is binary, left is loop control and right is the body. */
5202     JSParseNode *pn = BinaryNode::create(tc);
5203     if (!pn)
5204         return NULL;
5205     JSStmtInfo stmtInfo;
5206     js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
5207
5208     pn->pn_op = JSOP_ITER;
5209     pn->pn_iflags = 0;
5210     if (tokenStream.matchToken(TOK_NAME)) {
5211         if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
5212             pn->pn_iflags = JSITER_FOREACH;
5213         else
5214             tokenStream.ungetToken();
5215     }
5216
5217     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
5218     TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
5219
5220 #if JS_HAS_BLOCK_SCOPE
5221     bool let = false;
5222 #endif
5223
5224     JSParseNode *pn1;
5225     if (tt == TOK_SEMI) {
5226         if (pn->pn_iflags & JSITER_FOREACH)
5227             goto bad_for_each;
5228
5229         /* No initializer -- set first kid of left sub-node to null. */
5230         pn1 = NULL;
5231     } else {
5232         /*
5233          * Set pn1 to a var list or an initializing expression.
5234          *
5235          * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
5236          * of the for statement.  This flag will be used by the RelExpr
5237          * production; if it is set, then the 'in' keyword will not be
5238          * recognized as an operator, leaving it available to be parsed as
5239          * part of a for/in loop.
5240          *
5241          * A side effect of this restriction is that (unparenthesized)
5242          * expressions involving an 'in' operator are illegal in the init
5243          * clause of an ordinary for loop.
5244          */
5245         tc->flags |= TCF_IN_FOR_INIT;
5246         if (tt == TOK_VAR) {
5247             (void) tokenStream.getToken();
5248             pn1 = variables(false);
5249 #if JS_HAS_BLOCK_SCOPE
5250         } else if (tt == TOK_LET) {
5251             let = true;
5252             (void) tokenStream.getToken();
5253             if (tokenStream.peekToken() == TOK_LP) {
5254                 pn1 = letBlock(JS_FALSE);
5255                 tt = TOK_LEXICALSCOPE;
5256             } else {
5257                 pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
5258                 if (!pnlet)
5259                     return NULL;
5260                 blockInfo.flags |= SIF_FOR_BLOCK;
5261                 pn1 = variables(false);
5262             }
5263 #endif
5264         } else {
5265             pn1 = expr();
5266         }
5267         tc->flags &= ~TCF_IN_FOR_INIT;
5268         if (!pn1)
5269             return NULL;
5270     }
5271
5272     /*
5273      * We can be sure that it's a for/in loop if there's still an 'in'
5274      * keyword here, even if JavaScript recognizes 'in' as an operator,
5275      * as we've excluded 'in' from being parsed in RelExpr by setting
5276      * the TCF_IN_FOR_INIT flag in our JSTreeContext.
5277      */
5278     if (pn1 && tokenStream.matchToken(TOK_IN)) {
5279         pn->pn_iflags |= JSITER_ENUMERATE;
5280         stmtInfo.type = STMT_FOR_IN_LOOP;
5281
5282         /* Check that the left side of the 'in' is valid. */
5283         JS_ASSERT(!TokenKindIsDecl(tt) || PN_TYPE(pn1) == tt);
5284         if (TokenKindIsDecl(tt)
5285             ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
5286 #if JS_HAS_DESTRUCTURING
5287                || (versionNumber() == JSVERSION_1_7 &&
5288                    pn->pn_op == JSOP_ITER &&
5289                    !(pn->pn_iflags & JSITER_FOREACH) &&
5290                    (pn1->pn_head->pn_type == TOK_RC ||
5291                     (pn1->pn_head->pn_type == TOK_RB &&
5292                      pn1->pn_head->pn_count != 2) ||
5293                     (pn1->pn_head->pn_type == TOK_ASSIGN &&
5294                      (pn1->pn_head->pn_left->pn_type != TOK_RB ||
5295                       pn1->pn_head->pn_left->pn_count != 2))))
5296 #endif
5297               )
5298             : (pn1->pn_type != TOK_NAME &&
5299                pn1->pn_type != TOK_DOT &&
5300 #if JS_HAS_DESTRUCTURING
5301                ((versionNumber() == JSVERSION_1_7 &&
5302                  pn->pn_op == JSOP_ITER &&
5303                  !(pn->pn_iflags & JSITER_FOREACH))
5304                 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
5305                 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
5306 #endif
5307                pn1->pn_type != TOK_LP &&
5308 #if JS_HAS_XML_SUPPORT
5309                (pn1->pn_type != TOK_UNARYOP ||
5310                 pn1->pn_op != JSOP_XMLNAME) &&
5311 #endif
5312                pn1->pn_type != TOK_LB)) {
5313             reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
5314             return NULL;
5315         }
5316
5317         /* pn2 points to the name or destructuring pattern on in's left. */
5318         JSParseNode *pn2 = NULL;
5319         uintN dflag = PND_ASSIGNED;
5320
5321         if (TokenKindIsDecl(tt)) {
5322             /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
5323             pn1->pn_xflags |= PNX_FORINVAR;
5324
5325             /*
5326              * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
5327              * 'const' to hoist the initializer or the entire decl out of
5328              * the loop head. TOK_VAR is the type for both 'var' and 'const'.
5329              */
5330             pn2 = pn1->pn_head;
5331             if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
5332 #if JS_HAS_DESTRUCTURING
5333                 || pn2->pn_type == TOK_ASSIGN
5334 #endif
5335                 ) {
5336 #if JS_HAS_BLOCK_SCOPE
5337                 if (tt == TOK_LET) {
5338                     reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT);
5339                     return NULL;
5340                 }
5341 #endif /* JS_HAS_BLOCK_SCOPE */
5342
5343                 pnseq = ListNode::create(tc);
5344                 if (!pnseq)
5345                     return NULL;
5346                 pnseq->pn_type = TOK_SEQ;
5347                 pnseq->pn_pos.begin = pn->pn_pos.begin;
5348
5349                 dflag = PND_INITIALIZED;
5350
5351                 /*
5352                  * All of 'var x = i' is hoisted above 'for (x in o)',
5353                  * so clear PNX_FORINVAR.
5354                  *
5355                  * Request JSOP_POP here since the var is for a simple
5356                  * name (it is not a destructuring binding's left-hand
5357                  * side) and it has an initializer.
5358                  */
5359                 pn1->pn_xflags &= ~PNX_FORINVAR;
5360                 pn1->pn_xflags |= PNX_POPVAR;
5361                 pnseq->initList(pn1);
5362
5363 #if JS_HAS_DESTRUCTURING
5364                 if (pn2->pn_type == TOK_ASSIGN) {
5365                     pn1 = CloneParseTree(pn2->pn_left, tc);
5366                     if (!pn1)
5367                         return NULL;
5368                 } else
5369 #endif
5370                 {
5371                     JS_ASSERT(pn2->pn_type == TOK_NAME);
5372                     pn1 = NameNode::create(pn2->pn_atom, tc);
5373                     if (!pn1)
5374                         return NULL;
5375                     pn1->pn_type = TOK_NAME;
5376                     pn1->pn_op = JSOP_NAME;
5377                     pn1->pn_pos = pn2->pn_pos;
5378                     if (pn2->pn_defn)
5379                         LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
5380                 }
5381                 pn2 = pn1;
5382             }
5383         }
5384
5385         if (!pn2) {
5386             pn2 = pn1;
5387             if (pn2->pn_type == TOK_LP &&
5388                 !MakeSetCall(context, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
5389                 return NULL;
5390             }
5391 #if JS_HAS_XML_SUPPORT
5392             if (pn2->pn_type == TOK_UNARYOP)
5393                 pn2->pn_op = JSOP_BINDXMLNAME;
5394 #endif
5395         }
5396
5397         switch (pn2->pn_type) {
5398           case TOK_NAME:
5399             /* Beware 'for (arguments in ...)' with or without a 'var'. */
5400             NoteLValue(context, pn2, tc, dflag);
5401             break;
5402
5403 #if JS_HAS_DESTRUCTURING
5404           case TOK_ASSIGN:
5405             pn2 = pn2->pn_left;
5406             JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
5407             /* FALL THROUGH */
5408           case TOK_RB:
5409           case TOK_RC:
5410             /* Check for valid lvalues in var-less destructuring for-in. */
5411             if (pn1 == pn2 && !CheckDestructuring(context, NULL, pn2, NULL, tc))
5412                 return NULL;
5413
5414             if (versionNumber() == JSVERSION_1_7) {
5415                 /*
5416                  * Destructuring for-in requires [key, value] enumeration
5417                  * in JS1.7.
5418                  */
5419                 JS_ASSERT(pn->pn_op == JSOP_ITER);
5420                 if (!(pn->pn_iflags & JSITER_FOREACH))
5421                     pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
5422             }
5423             break;
5424 #endif
5425
5426           default:;
5427         }
5428
5429         /*
5430          * Parse the object expression as the right operand of 'in', first
5431          * removing the top statement from the statement-stack if this is a
5432          * 'for (let x in y)' loop.
5433          */
5434 #if JS_HAS_BLOCK_SCOPE
5435         JSStmtInfo *save = tc->topStmt;
5436         if (let)
5437             tc->topStmt = save->down;
5438 #endif
5439         pn2 = expr();
5440 #if JS_HAS_BLOCK_SCOPE
5441         if (let)
5442             tc->topStmt = save;
5443 #endif
5444
5445         pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
5446         if (!pn2)
5447             return NULL;
5448         pn->pn_left = pn2;
5449     } else {
5450         if (pn->pn_iflags & JSITER_FOREACH)
5451             goto bad_for_each;
5452         pn->pn_op = JSOP_NOP;
5453
5454         /* Parse the loop condition or null into pn2. */
5455         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
5456         tt = tokenStream.peekToken(TSF_OPERAND);
5457         JSParseNode *pn2;
5458         if (tt == TOK_SEMI) {
5459             pn2 = NULL;
5460         } else {
5461             pn2 = expr();
5462             if (!pn2)
5463                 return NULL;
5464         }
5465
5466         /* Parse the update expression or null into pn3. */
5467         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
5468         tt = tokenStream.peekToken(TSF_OPERAND);
5469         JSParseNode *pn3;
5470         if (tt == TOK_RP) {
5471             pn3 = NULL;
5472         } else {
5473             pn3 = expr();
5474             if (!pn3)
5475                 return NULL;
5476         }
5477
5478         /* Build the FORHEAD node to use as the left kid of pn. */
5479         JSParseNode *pn4 = TernaryNode::create(tc);
5480         if (!pn4)
5481             return NULL;
5482         pn4->pn_type = TOK_FORHEAD;
5483         pn4->pn_op = JSOP_NOP;
5484         pn4->pn_kid1 = pn1;
5485         pn4->pn_kid2 = pn2;
5486         pn4->pn_kid3 = pn3;
5487         pn->pn_left = pn4;
5488     }
5489
5490     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5491
5492     /* Parse the loop body into pn->pn_right. */
5493     JSParseNode *pn2;
5494     pn2 = statement();
5495     if (!pn2)
5496         return NULL;
5497     pn->pn_right = pn2;
5498
5499     /* Record the absolute line number for source note emission. */
5500     pn->pn_pos.end = pn2->pn_pos.end;
5501
5502 #if JS_HAS_BLOCK_SCOPE
5503     if (pnlet) {
5504         PopStatement(tc);
5505         pnlet->pn_expr = pn;
5506         pn = pnlet;
5507     }
5508 #endif
5509     if (pnseq) {
5510         pnseq->pn_pos.end = pn->pn_pos.end;
5511         pnseq->append(pn);
5512         pn = pnseq;
5513     }
5514     PopStatement(tc);
5515     return pn;
5516
5517   bad_for_each:
5518     reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
5519     return NULL;
5520 }
5521
5522 JSParseNode *
5523 Parser::tryStatement()
5524 {
5525     JSParseNode *catchList, *lastCatch;
5526
5527     /*
5528      * try nodes are ternary.
5529      * kid1 is the try statement
5530      * kid2 is the catch node list or null
5531      * kid3 is the finally statement
5532      *
5533      * catch nodes are ternary.
5534      * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5535      * kid2 is the catch guard or null if no guard
5536      * kid3 is the catch block
5537      *
5538      * catch lvalue nodes are either:
5539      *   TOK_NAME for a single identifier
5540      *   TOK_RB or TOK_RC for a destructuring left-hand side
5541      *
5542      * finally nodes are TOK_LC statement lists.
5543      */
5544     JSParseNode *pn = TernaryNode::create(tc);
5545     if (!pn)
5546         return NULL;
5547     pn->pn_op = JSOP_NOP;
5548
5549     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5550     JSStmtInfo stmtInfo;
5551     if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5552         return NULL;
5553     pn->pn_kid1 = statements();
5554     if (!pn->pn_kid1)
5555         return NULL;
5556     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5557     PopStatement(tc);
5558
5559     catchList = NULL;
5560     TokenKind tt = tokenStream.getToken();
5561     if (tt == TOK_CATCH) {
5562         catchList = ListNode::create(tc);
5563         if (!catchList)
5564             return NULL;
5565         catchList->pn_type = TOK_RESERVED;
5566         catchList->makeEmpty();
5567         lastCatch = NULL;
5568
5569         do {
5570             JSParseNode *pnblock;
5571             BindData data;
5572
5573             /* Check for another catch after unconditional catch. */
5574             if (lastCatch && !lastCatch->pn_kid2) {
5575                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
5576                 return NULL;
5577             }
5578
5579             /*
5580              * Create a lexical scope node around the whole catch clause,
5581              * including the head.
5582              */
5583             pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
5584             if (!pnblock)
5585                 return NULL;
5586             stmtInfo.type = STMT_CATCH;
5587
5588             /*
5589              * Legal catch forms are:
5590              *   catch (lhs)
5591              *   catch (lhs if <boolean_expression>)
5592              * where lhs is a name or a destructuring left-hand side.
5593              * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5594              */
5595             JSParseNode *pn2 = TernaryNode::create(tc);
5596             if (!pn2)
5597                 return NULL;
5598             pnblock->pn_expr = pn2;
5599             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5600
5601             /*
5602              * Contrary to ECMA Ed. 3, the catch variable is lexically
5603              * scoped, not a property of a new Object instance.  This is
5604              * an intentional change that anticipates ECMA Ed. 4.
5605              */
5606             data.pn = NULL;
5607             data.op = JSOP_NOP;
5608             data.binder = BindLet;
5609             data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5610
5611             tt = tokenStream.getToken();
5612             JSParseNode *pn3;
5613             switch (tt) {
5614 #if JS_HAS_DESTRUCTURING
5615               case TOK_LB:
5616               case TOK_LC:
5617                 pn3 = destructuringExpr(&data, tt);
5618                 if (!pn3)
5619                     return NULL;
5620                 break;
5621 #endif
5622
5623               case TOK_NAME:
5624               {
5625                 JSAtom *label = tokenStream.currentToken().t_atom;
5626                 pn3 = NewBindingNode(label, tc, true);
5627                 if (!pn3)
5628                     return NULL;
5629                 data.pn = pn3;
5630                 if (!data.binder(context, &data, label, tc))
5631                     return NULL;
5632                 break;
5633               }
5634
5635               default:
5636                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER);
5637                 return NULL;
5638             }
5639
5640             pn2->pn_kid1 = pn3;
5641 #if JS_HAS_CATCH_GUARD
5642             /*
5643              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5644              * to avoid conflicting with the JS2/ECMAv4 type annotation
5645              * catchguard syntax.
5646              */
5647             if (tokenStream.matchToken(TOK_IF)) {
5648                 pn2->pn_kid2 = expr();
5649                 if (!pn2->pn_kid2)
5650                     return NULL;
5651             }
5652 #endif
5653             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5654
5655             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5656             pn2->pn_kid3 = statements();
5657             if (!pn2->pn_kid3)
5658                 return NULL;
5659             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5660             PopStatement(tc);
5661
5662             catchList->append(pnblock);
5663             lastCatch = pn2;
5664             tt = tokenStream.getToken(TSF_OPERAND);
5665         } while (tt == TOK_CATCH);
5666     }
5667     pn->pn_kid2 = catchList;
5668
5669     if (tt == TOK_FINALLY) {
5670         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5671         if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5672             return NULL;
5673         pn->pn_kid3 = statements();
5674         if (!pn->pn_kid3)
5675             return NULL;
5676         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5677         PopStatement(tc);
5678     } else {
5679         tokenStream.ungetToken();
5680     }
5681     if (!catchList && !pn->pn_kid3) {
5682         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY);
5683         return NULL;
5684     }
5685     return pn;
5686 }
5687
5688 JSParseNode *
5689 Parser::withStatement()
5690 {
5691     /*
5692      * In most cases, we want the constructs forbidden in strict mode
5693      * code to be a subset of those that JSOPTION_STRICT warns about, and
5694      * we should use ReportStrictModeError.  However, 'with' is the sole
5695      * instance of a construct that is forbidden in strict mode code, but
5696      * doesn't even merit a warning under JSOPTION_STRICT.  See
5697      * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5698      */
5699     if (tc->flags & TCF_STRICT_MODE_CODE) {
5700         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
5701         return NULL;
5702     }
5703
5704     JSParseNode *pn = BinaryNode::create(tc);
5705     if (!pn)
5706         return NULL;
5707     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5708     JSParseNode *pn2 = parenExpr();
5709     if (!pn2)
5710         return NULL;
5711     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5712     pn->pn_left = pn2;
5713
5714     JSParseNode *oldWith = tc->innermostWith;
5715     tc->innermostWith = pn;
5716
5717     JSStmtInfo stmtInfo;
5718     js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5719     pn2 = statement();
5720     if (!pn2)
5721         return NULL;
5722     PopStatement(tc);
5723
5724     pn->pn_pos.end = pn2->pn_pos.end;
5725     pn->pn_right = pn2;
5726     tc->flags |= TCF_FUN_HEAVYWEIGHT;
5727     tc->innermostWith = oldWith;
5728
5729     /*
5730      * Make sure to deoptimize lexical dependencies inside the |with|
5731      * to safely optimize binding globals (see bug 561923).
5732      */
5733     JSAtomListIterator iter(&tc->lexdeps);
5734     while (JSAtomListElement *ale = iter()) {
5735         JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
5736         DeoptimizeUsesWithin(lexdep, pn->pn_pos);
5737     }
5738
5739     return pn;
5740 }
5741
5742 #if JS_HAS_BLOCK_SCOPE
5743 JSParseNode *
5744 Parser::letStatement()
5745 {
5746     JSObjectBox *blockbox;
5747
5748     JSParseNode *pn;
5749     do {
5750         /* Check for a let statement or let expression. */
5751         if (tokenStream.peekToken() == TOK_LP) {
5752             pn = letBlock(JS_TRUE);
5753             if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5754                 return pn;
5755
5756             /* Let expressions require automatic semicolon insertion. */
5757             JS_ASSERT(pn->pn_type == TOK_SEMI ||
5758                       pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5759             break;
5760         }
5761
5762         /*
5763          * This is a let declaration. We must be directly under a block per
5764          * the proposed ES4 specs, but not an implicit block created due to
5765          * 'for (let ...)'. If we pass this error test, make the enclosing
5766          * JSStmtInfo be our scope. Further let declarations in this block
5767          * will find this scope statement and use the same block object.
5768          *
5769          * If we are the first let declaration in this block (i.e., when the
5770          * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5771          * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5772          */
5773         JSStmtInfo *stmt = tc->topStmt;
5774         if (stmt &&
5775             (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5776             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LET_DECL_NOT_IN_BLOCK);
5777             return NULL;
5778         }
5779
5780         if (stmt && (stmt->flags & SIF_SCOPE)) {
5781             JS_ASSERT(tc->blockChainBox == stmt->blockBox);
5782         } else {
5783             if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5784                 /*
5785                  * ES4 specifies that let at top level and at body-block scope
5786                  * does not shadow var, so convert back to var.
5787                  */
5788                 tokenStream.mungeCurrentToken(TOK_VAR, JSOP_DEFVAR);
5789
5790                 pn = variables(false);
5791                 if (!pn)
5792                     return NULL;
5793                 pn->pn_xflags |= PNX_POPVAR;
5794                 break;
5795             }
5796
5797             /*
5798              * Some obvious assertions here, but they may help clarify the
5799              * situation. This stmt is not yet a scope, so it must not be a
5800              * catch block (catch is a lexical scope by definition).
5801              */
5802             JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5803             JS_ASSERT(stmt != tc->topScopeStmt);
5804             JS_ASSERT(stmt->type == STMT_BLOCK ||
5805                       stmt->type == STMT_SWITCH ||
5806                       stmt->type == STMT_TRY ||
5807                       stmt->type == STMT_FINALLY);
5808             JS_ASSERT(!stmt->downScope);
5809
5810             /* Convert the block statement into a scope statement. */
5811             JSObject *obj = js_NewBlockObject(tc->parser->context);
5812             if (!obj)
5813                 return NULL;
5814
5815             blockbox = tc->parser->newObjectBox(obj);
5816             if (!blockbox)
5817                 return NULL;
5818
5819             /*
5820              * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5821              * list stack, if it isn't already there.  If it is there, but it
5822              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5823              * block.
5824              */
5825             stmt->flags |= SIF_SCOPE;
5826             stmt->downScope = tc->topScopeStmt;
5827             tc->topScopeStmt = stmt;
5828             JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5829                                     (tc->maxScopeDepth = tc->scopeDepth));
5830
5831             obj->setParent(tc->blockChain());
5832             blockbox->parent = tc->blockChainBox;
5833             tc->blockChainBox = blockbox;
5834             stmt->blockBox = blockbox;
5835
5836 #ifdef DEBUG
5837             JSParseNode *tmp = tc->blockNode;
5838             JS_ASSERT(!tmp || tmp->pn_type != TOK_LEXICALSCOPE);
5839 #endif
5840
5841             /* Create a new lexical scope node for these statements. */
5842             JSParseNode *pn1 = LexicalScopeNode::create(tc);
5843             if (!pn1)
5844                 return NULL;
5845
5846             pn1->pn_type = TOK_LEXICALSCOPE;
5847             pn1->pn_op = JSOP_LEAVEBLOCK;
5848             pn1->pn_pos = tc->blockNode->pn_pos;
5849             pn1->pn_objbox = blockbox;
5850             pn1->pn_expr = tc->blockNode;
5851             pn1->pn_blockid = tc->blockNode->pn_blockid;
5852             tc->blockNode = pn1;
5853         }
5854
5855         pn = variables(false);
5856         if (!pn)
5857             return NULL;
5858         pn->pn_xflags = PNX_POPVAR;
5859     } while (0);
5860
5861     /* Check termination of this primitive statement. */
5862     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5863 }
5864 #endif
5865
5866 JSParseNode *
5867 Parser::expressionStatement()
5868 {
5869     tokenStream.ungetToken();
5870     JSParseNode *pn2 = expr();
5871     if (!pn2)
5872         return NULL;
5873
5874     if (tokenStream.peekToken() == TOK_COLON) {
5875         if (pn2->pn_type != TOK_NAME) {
5876             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LABEL);
5877             return NULL;
5878         }
5879         JSAtom *label = pn2->pn_atom;
5880         for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
5881             if (stmt->type == STMT_LABEL && stmt->label == label) {
5882                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL);
5883                 return NULL;
5884             }
5885         }
5886         ForgetUse(pn2);
5887
5888         (void) tokenStream.getToken();
5889
5890         /* Push a label struct and parse the statement. */
5891         JSStmtInfo stmtInfo;
5892         js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5893         stmtInfo.label = label;
5894         JSParseNode *pn = statement();
5895         if (!pn)
5896             return NULL;
5897
5898         /* Normalize empty statement to empty block for the decompiler. */
5899         if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5900             pn->pn_type = TOK_LC;
5901             pn->pn_arity = PN_LIST;
5902             pn->makeEmpty();
5903         }
5904
5905         /* Pop the label, set pn_expr, and return early. */
5906         PopStatement(tc);
5907         pn2->pn_type = TOK_COLON;
5908         pn2->pn_pos.end = pn->pn_pos.end;
5909         pn2->pn_expr = pn;
5910         return pn2;
5911     }
5912
5913     JSParseNode *pn = UnaryNode::create(tc);
5914     if (!pn)
5915         return NULL;
5916     pn->pn_type = TOK_SEMI;
5917     pn->pn_pos = pn2->pn_pos;
5918     pn->pn_kid = pn2;
5919
5920     switch (PN_TYPE(pn2)) {
5921       case TOK_LP:
5922         /*
5923          * Flag lambdas immediately applied as statements as instances of
5924          * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5925          */
5926         if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION &&
5927             !pn2->pn_head->pn_funbox->node->isFunArg()) {
5928             pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
5929         }
5930         break;
5931       case TOK_ASSIGN:
5932         /*
5933          * Keep track of all apparent methods created by assignments such
5934          * as this.foo = function (...) {...} in a function that could end
5935          * up a constructor function. See Parser::setFunctionKinds.
5936          */
5937         if (tc->funbox &&
5938             PN_OP(pn2) == JSOP_NOP &&
5939             PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5940             PN_OP(pn2->pn_left->pn_expr) == JSOP_THIS &&
5941             PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
5942             JS_ASSERT(!pn2->pn_defn);
5943             JS_ASSERT(!pn2->pn_used);
5944             pn2->pn_right->pn_link = tc->funbox->methods;
5945             tc->funbox->methods = pn2->pn_right;
5946         }
5947         break;
5948       default:;
5949     }
5950
5951     /* Check termination of this primitive statement. */
5952     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5953 }
5954
5955 JSParseNode *
5956 Parser::statement()
5957 {
5958     JSParseNode *pn;
5959
5960     JS_CHECK_RECURSION(context, return NULL);
5961
5962     switch (tokenStream.getToken(TSF_OPERAND)) {
5963       case TOK_FUNCTION:
5964       {
5965 #if JS_HAS_XML_SUPPORT
5966         TokenKind tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
5967         if (tt == TOK_DBLCOLON)
5968             goto expression;
5969 #endif
5970         return functionStmt();
5971       }
5972
5973       case TOK_IF:
5974       {
5975         /* An IF node has three kids: condition, then, and optional else. */
5976         pn = TernaryNode::create(tc);
5977         if (!pn)
5978             return NULL;
5979         JSParseNode *pn1 = condition();
5980         if (!pn1)
5981             return NULL;
5982         JSStmtInfo stmtInfo;
5983         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
5984         JSParseNode *pn2 = statement();
5985         if (!pn2)
5986             return NULL;
5987         JSParseNode *pn3;
5988         if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
5989             stmtInfo.type = STMT_ELSE;
5990             pn3 = statement();
5991             if (!pn3)
5992                 return NULL;
5993             pn->pn_pos.end = pn3->pn_pos.end;
5994         } else {
5995             pn3 = NULL;
5996             pn->pn_pos.end = pn2->pn_pos.end;
5997         }
5998         PopStatement(tc);
5999         pn->pn_kid1 = pn1;
6000         pn->pn_kid2 = pn2;
6001         pn->pn_kid3 = pn3;
6002         return pn;
6003       }
6004
6005       case TOK_SWITCH:
6006         return switchStatement();
6007
6008       case TOK_WHILE:
6009       {
6010         pn = BinaryNode::create(tc);
6011         if (!pn)
6012             return NULL;
6013         JSStmtInfo stmtInfo;
6014         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
6015         JSParseNode *pn2 = condition();
6016         if (!pn2)
6017             return NULL;
6018         pn->pn_left = pn2;
6019         JSParseNode *pn3 = statement();
6020         if (!pn3)
6021             return NULL;
6022         PopStatement(tc);
6023         pn->pn_pos.end = pn3->pn_pos.end;
6024         pn->pn_right = pn3;
6025         return pn;
6026       }
6027
6028       case TOK_DO:
6029       {
6030         pn = BinaryNode::create(tc);
6031         if (!pn)
6032             return NULL;
6033         JSStmtInfo stmtInfo;
6034         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
6035         JSParseNode *pn2 = statement();
6036         if (!pn2)
6037             return NULL;
6038         pn->pn_left = pn2;
6039         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
6040         JSParseNode *pn3 = condition();
6041         if (!pn3)
6042             return NULL;
6043         PopStatement(tc);
6044         pn->pn_pos.end = pn3->pn_pos.end;
6045         pn->pn_right = pn3;
6046         if (versionNumber() != JSVERSION_ECMA_3) {
6047             /*
6048              * All legacy and extended versions must do automatic semicolon
6049              * insertion after do-while.  See the testcase and discussion in
6050              * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
6051              */
6052             (void) tokenStream.matchToken(TOK_SEMI);
6053             return pn;
6054         }
6055         break;
6056       }
6057
6058       case TOK_FOR:
6059         return forStatement();
6060
6061       case TOK_TRY:
6062         return tryStatement();
6063
6064       case TOK_THROW:
6065       {
6066         pn = UnaryNode::create(tc);
6067         if (!pn)
6068             return NULL;
6069
6070         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
6071         TokenKind tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6072         if (tt == TOK_ERROR)
6073             return NULL;
6074         if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
6075             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
6076             return NULL;
6077         }
6078
6079         JSParseNode *pn2 = expr();
6080         if (!pn2)
6081             return NULL;
6082         pn->pn_pos.end = pn2->pn_pos.end;
6083         pn->pn_op = JSOP_THROW;
6084         pn->pn_kid = pn2;
6085         break;
6086       }
6087
6088       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
6089       case TOK_CATCH:
6090         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY);
6091         return NULL;
6092
6093       case TOK_FINALLY:
6094         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY);
6095         return NULL;
6096
6097       case TOK_BREAK:
6098       {
6099         pn = NullaryNode::create(tc);
6100         if (!pn)
6101             return NULL;
6102         if (!MatchLabel(context, &tokenStream, pn))
6103             return NULL;
6104         JSStmtInfo *stmt = tc->topStmt;
6105         JSAtom *label = pn->pn_atom;
6106         if (label) {
6107             for (; ; stmt = stmt->down) {
6108                 if (!stmt) {
6109                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
6110                     return NULL;
6111                 }
6112                 if (stmt->type == STMT_LABEL && stmt->label == label)
6113                     break;
6114             }
6115         } else {
6116             for (; ; stmt = stmt->down) {
6117                 if (!stmt) {
6118                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOUGH_BREAK);
6119                     return NULL;
6120                 }
6121                 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
6122                     break;
6123             }
6124         }
6125         if (label)
6126             pn->pn_pos.end = tokenStream.currentToken().pos.end;
6127         break;
6128       }
6129
6130       case TOK_CONTINUE:
6131       {
6132         pn = NullaryNode::create(tc);
6133         if (!pn)
6134             return NULL;
6135         if (!MatchLabel(context, &tokenStream, pn))
6136             return NULL;
6137         JSStmtInfo *stmt = tc->topStmt;
6138         JSAtom *label = pn->pn_atom;
6139         if (label) {
6140             for (JSStmtInfo *stmt2 = NULL; ; stmt = stmt->down) {
6141                 if (!stmt) {
6142                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
6143                     return NULL;
6144                 }
6145                 if (stmt->type == STMT_LABEL) {
6146                     if (stmt->label == label) {
6147                         if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
6148                             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
6149                             return NULL;
6150                         }
6151                         break;
6152                     }
6153                 } else {
6154                     stmt2 = stmt;
6155                 }
6156             }
6157         } else {
6158             for (; ; stmt = stmt->down) {
6159                 if (!stmt) {
6160                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
6161                     return NULL;
6162                 }
6163                 if (STMT_IS_LOOP(stmt))
6164                     break;
6165             }
6166         }
6167         if (label)
6168             pn->pn_pos.end = tokenStream.currentToken().pos.end;
6169         break;
6170       }
6171
6172       case TOK_WITH:
6173         return withStatement();
6174
6175       case TOK_VAR:
6176         pn = variables(false);
6177         if (!pn)
6178             return NULL;
6179
6180         /* Tell js_EmitTree to generate a final POP. */
6181         pn->pn_xflags |= PNX_POPVAR;
6182         break;
6183
6184 #if JS_HAS_BLOCK_SCOPE
6185       case TOK_LET:
6186         return letStatement();
6187 #endif /* JS_HAS_BLOCK_SCOPE */
6188
6189       case TOK_RETURN:
6190         pn = returnOrYield(false);
6191         if (!pn)
6192             return NULL;
6193         break;
6194
6195       case TOK_LC:
6196       {
6197         uintN oldflags;
6198
6199         oldflags = tc->flags;
6200         tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
6201         JSStmtInfo stmtInfo;
6202         if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
6203             return NULL;
6204         pn = statements();
6205         if (!pn)
6206             return NULL;
6207
6208         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
6209         PopStatement(tc);
6210
6211         /*
6212          * If we contain a function statement and our container is top-level
6213          * or another block, flag pn to preserve braces when decompiling.
6214          */
6215         if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
6216             (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
6217             pn->pn_xflags |= PNX_NEEDBRACES;
6218         }
6219         tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
6220         return pn;
6221       }
6222
6223       case TOK_EOL:
6224       case TOK_SEMI:
6225         pn = UnaryNode::create(tc);
6226         if (!pn)
6227             return NULL;
6228         pn->pn_type = TOK_SEMI;
6229         return pn;
6230
6231       case TOK_DEBUGGER:
6232         pn = NullaryNode::create(tc);
6233         if (!pn)
6234             return NULL;
6235         pn->pn_type = TOK_DEBUGGER;
6236         tc->flags |= TCF_FUN_HEAVYWEIGHT;
6237         break;
6238
6239 #if JS_HAS_XML_SUPPORT
6240       case TOK_DEFAULT:
6241       {
6242         pn = UnaryNode::create(tc);
6243         if (!pn)
6244             return NULL;
6245         if (!tokenStream.matchToken(TOK_NAME) ||
6246             tokenStream.currentToken().t_atom != context->runtime->atomState.xmlAtom ||
6247             !tokenStream.matchToken(TOK_NAME) ||
6248             tokenStream.currentToken().t_atom != context->runtime->atomState.namespaceAtom ||
6249             !tokenStream.matchToken(TOK_ASSIGN) ||
6250             tokenStream.currentToken().t_op != JSOP_NOP) {
6251             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE);
6252             return NULL;
6253         }
6254
6255         /* Is this an E4X dagger I see before me? */
6256         tc->flags |= TCF_FUN_HEAVYWEIGHT;
6257         JSParseNode *pn2 = expr();
6258         if (!pn2)
6259             return NULL;
6260         pn->pn_op = JSOP_DEFXMLNS;
6261         pn->pn_pos.end = pn2->pn_pos.end;
6262         pn->pn_kid = pn2;
6263         break;
6264       }
6265 #endif
6266
6267       case TOK_ERROR:
6268         return NULL;
6269
6270       default:
6271 #if JS_HAS_XML_SUPPORT
6272       expression:
6273 #endif
6274         return expressionStatement();
6275     }
6276
6277     /* Check termination of this primitive statement. */
6278     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
6279 }
6280
6281 JSParseNode *
6282 Parser::variables(bool inLetHead)
6283 {
6284     TokenKind tt;
6285     bool let;
6286     JSStmtInfo *scopeStmt;
6287     BindData data;
6288     JSParseNode *pn, *pn2;
6289     JSAtom *atom;
6290
6291     /*
6292      * The three options here are:
6293      * - TOK_LET: We are parsing a let declaration.
6294      * - TOK_LP: We are parsing the head of a let block.
6295      * - Otherwise, we're parsing var declarations.
6296      */
6297     tt = tokenStream.currentToken().type;
6298     let = (tt == TOK_LET || tt == TOK_LP);
6299     JS_ASSERT(let || tt == TOK_VAR);
6300
6301 #if JS_HAS_BLOCK_SCOPE
6302     bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
6303     JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
6304 #endif
6305
6306     /* Make sure that statement set up the tree context correctly. */
6307     scopeStmt = tc->topScopeStmt;
6308     if (let) {
6309         while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
6310             JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
6311             scopeStmt = scopeStmt->downScope;
6312         }
6313         JS_ASSERT(scopeStmt);
6314     }
6315
6316     data.op = let ? JSOP_NOP : tokenStream.currentToken().t_op;
6317     pn = ListNode::create(tc);
6318     if (!pn)
6319         return NULL;
6320     pn->pn_op = data.op;
6321     pn->makeEmpty();
6322
6323     /*
6324      * SpiderMonkey const is really "write once per initialization evaluation"
6325      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
6326      * this code will change soon.
6327      */
6328     if (let) {
6329         JS_ASSERT(tc->blockChainBox == scopeStmt->blockBox);
6330         data.binder = BindLet;
6331         data.let.overflow = JSMSG_TOO_MANY_LOCALS;
6332     } else {
6333         data.binder = BindVarOrConst;
6334     }
6335
6336     do {
6337         tt = tokenStream.getToken();
6338 #if JS_HAS_DESTRUCTURING
6339         if (tt == TOK_LB || tt == TOK_LC) {
6340             tc->flags |= TCF_DECL_DESTRUCTURING;
6341             pn2 = primaryExpr(tt, JS_FALSE);
6342             tc->flags &= ~TCF_DECL_DESTRUCTURING;
6343             if (!pn2)
6344                 return NULL;
6345
6346             if (!CheckDestructuring(context, &data, pn2, NULL, tc))
6347                 return NULL;
6348             if ((tc->flags & TCF_IN_FOR_INIT) &&
6349                 tokenStream.peekToken() == TOK_IN) {
6350                 pn->append(pn2);
6351                 continue;
6352             }
6353
6354             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
6355             if (tokenStream.currentToken().t_op != JSOP_NOP)
6356                 goto bad_var_init;
6357
6358 #if JS_HAS_BLOCK_SCOPE
6359             if (popScope) {
6360                 tc->topStmt = save->down;
6361                 tc->topScopeStmt = saveScope->downScope;
6362             }
6363 #endif
6364             JSParseNode *init = assignExpr();
6365 #if JS_HAS_BLOCK_SCOPE
6366             if (popScope) {
6367                 tc->topStmt = save;
6368                 tc->topScopeStmt = saveScope;
6369             }
6370 #endif
6371
6372             if (!init || !UndominateInitializers(pn2, init, tc))
6373                 return NULL;
6374
6375             pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
6376             if (!pn2)
6377                 return NULL;
6378             pn->append(pn2);
6379             continue;
6380         }
6381 #endif /* JS_HAS_DESTRUCTURING */
6382
6383         if (tt != TOK_NAME) {
6384             if (tt != TOK_ERROR) {
6385                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
6386             }
6387             return NULL;
6388         }
6389
6390         atom = tokenStream.currentToken().t_atom;
6391         pn2 = NewBindingNode(atom, tc, let);
6392         if (!pn2)
6393             return NULL;
6394         if (data.op == JSOP_DEFCONST)
6395             pn2->pn_dflags |= PND_CONST;
6396         data.pn = pn2;
6397         if (!data.binder(context, &data, atom, tc))
6398             return NULL;
6399         pn->append(pn2);
6400
6401         if (tokenStream.matchToken(TOK_ASSIGN)) {
6402             if (tokenStream.currentToken().t_op != JSOP_NOP)
6403                 goto bad_var_init;
6404
6405 #if JS_HAS_BLOCK_SCOPE
6406             if (popScope) {
6407                 tc->topStmt = save->down;
6408                 tc->topScopeStmt = saveScope->downScope;
6409             }
6410 #endif
6411             JSParseNode *init = assignExpr();
6412 #if JS_HAS_BLOCK_SCOPE
6413             if (popScope) {
6414                 tc->topStmt = save;
6415                 tc->topScopeStmt = saveScope;
6416             }
6417 #endif
6418             if (!init)
6419                 return NULL;
6420
6421             if (pn2->pn_used) {
6422                 pn2 = MakeAssignment(pn2, init, tc);
6423                 if (!pn2)
6424                     return NULL;
6425             } else {
6426                 pn2->pn_expr = init;
6427             }
6428
6429             JS_ASSERT_IF(pn2->pn_dflags & PND_GVAR, !(pn2->pn_dflags & PND_BOUND));
6430
6431             pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
6432                          ? JSOP_SETNAME
6433                          : (pn2->pn_dflags & PND_BOUND)
6434                          ? JSOP_SETLOCAL
6435                          : (data.op == JSOP_DEFCONST)
6436                          ? JSOP_SETCONST
6437                          : JSOP_SETNAME;
6438
6439             NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
6440
6441             /* The declarator's position must include the initializer. */
6442             pn2->pn_pos.end = init->pn_pos.end;
6443
6444             if (tc->inFunction() &&
6445                 atom == context->runtime->atomState.argumentsAtom) {
6446                 tc->noteArgumentsUse();
6447                 if (!let)
6448                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
6449             }
6450         }
6451     } while (tokenStream.matchToken(TOK_COMMA));
6452
6453     pn->pn_pos.end = pn->last()->pn_pos.end;
6454     return pn;
6455
6456 bad_var_init:
6457     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_VAR_INIT);
6458     return NULL;
6459 }
6460
6461 JSParseNode *
6462 Parser::expr()
6463 {
6464     JSParseNode *pn = assignExpr();
6465     if (pn && tokenStream.matchToken(TOK_COMMA)) {
6466         JSParseNode *pn2 = ListNode::create(tc);
6467         if (!pn2)
6468             return NULL;
6469         pn2->pn_pos.begin = pn->pn_pos.begin;
6470         pn2->initList(pn);
6471         pn = pn2;
6472         do {
6473 #if JS_HAS_GENERATORS
6474             pn2 = pn->last();
6475             if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
6476                 reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
6477                 return NULL;
6478             }
6479 #endif
6480             pn2 = assignExpr();
6481             if (!pn2)
6482                 return NULL;
6483             pn->append(pn2);
6484         } while (tokenStream.matchToken(TOK_COMMA));
6485         pn->pn_pos.end = pn->last()->pn_pos.end;
6486     }
6487     return pn;
6488 }
6489
6490 JSParseNode *
6491 Parser::assignExpr()
6492 {
6493     JS_CHECK_RECURSION(context, return NULL);
6494
6495 #if JS_HAS_GENERATORS
6496     if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
6497         return returnOrYield(true);
6498 #endif
6499
6500     JSParseNode *pn = condExpr();
6501     if (!pn)
6502         return NULL;
6503
6504     TokenKind tt = tokenStream.getToken();
6505     if (tt != TOK_ASSIGN) {
6506         tokenStream.ungetToken();
6507         return pn;
6508     }
6509
6510     JSOp op = tokenStream.currentToken().t_op;
6511     switch (pn->pn_type) {
6512       case TOK_NAME:
6513         if (!CheckStrictAssignment(context, tc, pn))
6514             return NULL;
6515         pn->pn_op = JSOP_SETNAME;
6516         NoteLValue(context, pn, tc);
6517         break;
6518       case TOK_DOT:
6519         pn->pn_op = JSOP_SETPROP;
6520         break;
6521       case TOK_LB:
6522         pn->pn_op = JSOP_SETELEM;
6523         break;
6524 #if JS_HAS_DESTRUCTURING
6525       case TOK_RB:
6526       case TOK_RC:
6527       {
6528         if (op != JSOP_NOP) {
6529             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS);
6530             return NULL;
6531         }
6532         JSParseNode *rhs = assignExpr();
6533         if (!rhs || !CheckDestructuring(context, NULL, pn, rhs, tc))
6534             return NULL;
6535         return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
6536       }
6537 #endif
6538       case TOK_LP:
6539         if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
6540             return NULL;
6541         break;
6542 #if JS_HAS_XML_SUPPORT
6543       case TOK_UNARYOP:
6544         if (pn->pn_op == JSOP_XMLNAME) {
6545             pn->pn_op = JSOP_SETXMLNAME;
6546             break;
6547         }
6548         /* FALL THROUGH */
6549 #endif
6550       default:
6551         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
6552         return NULL;
6553     }
6554
6555     JSParseNode *rhs = assignExpr();
6556     if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
6557         JSDefinition *dn = pn->pn_lexdef;
6558
6559         /*
6560          * If the definition is not flagged as assigned, we must have imputed
6561          * the initialized flag to it, to optimize for flat closures. But that
6562          * optimization uses source coordinates to check dominance relations,
6563          * so we must extend the end of the definition to cover the right-hand
6564          * side of this assignment, i.e., the initializer.
6565          */
6566         if (!dn->isAssigned()) {
6567             JS_ASSERT(dn->isInitialized());
6568             dn->pn_pos.end = rhs->pn_pos.end;
6569         }
6570     }
6571
6572     return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
6573 }
6574
6575 JSParseNode *
6576 Parser::condExpr()
6577 {
6578     JSParseNode *pn = orExpr();
6579     if (pn && tokenStream.matchToken(TOK_HOOK)) {
6580         JSParseNode *pn1 = pn;
6581         pn = TernaryNode::create(tc);
6582         if (!pn)
6583             return NULL;
6584
6585         /*
6586          * Always accept the 'in' operator in the middle clause of a ternary,
6587          * where it's unambiguous, even if we might be parsing the init of a
6588          * for statement.
6589          */
6590         uintN oldflags = tc->flags;
6591         tc->flags &= ~TCF_IN_FOR_INIT;
6592         JSParseNode *pn2 = assignExpr();
6593         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
6594
6595         if (!pn2)
6596             return NULL;
6597         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
6598         JSParseNode *pn3 = assignExpr();
6599         if (!pn3)
6600             return NULL;
6601         pn->pn_pos.begin = pn1->pn_pos.begin;
6602         pn->pn_pos.end = pn3->pn_pos.end;
6603         pn->pn_kid1 = pn1;
6604         pn->pn_kid2 = pn2;
6605         pn->pn_kid3 = pn3;
6606     }
6607     return pn;
6608 }
6609
6610 JSParseNode *
6611 Parser::orExpr()
6612 {
6613     JSParseNode *pn = andExpr();
6614     while (pn && tokenStream.matchToken(TOK_OR))
6615         pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, andExpr(), tc);
6616     return pn;
6617 }
6618
6619 JSParseNode *
6620 Parser::andExpr()
6621 {
6622     JSParseNode *pn = bitOrExpr();
6623     while (pn && tokenStream.matchToken(TOK_AND))
6624         pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, bitOrExpr(), tc);
6625     return pn;
6626 }
6627
6628 JSParseNode *
6629 Parser::bitOrExpr()
6630 {
6631     JSParseNode *pn = bitXorExpr();
6632     while (pn && tokenStream.matchToken(TOK_BITOR))
6633         pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, bitXorExpr(), tc);
6634     return pn;
6635 }
6636
6637 JSParseNode *
6638 Parser::bitXorExpr()
6639 {
6640     JSParseNode *pn = bitAndExpr();
6641     while (pn && tokenStream.matchToken(TOK_BITXOR))
6642         pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, bitAndExpr(), tc);
6643     return pn;
6644 }
6645
6646 JSParseNode *
6647 Parser::bitAndExpr()
6648 {
6649     JSParseNode *pn = eqExpr();
6650     while (pn && tokenStream.matchToken(TOK_BITAND))
6651         pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, eqExpr(), tc);
6652     return pn;
6653 }
6654
6655 JSParseNode *
6656 Parser::eqExpr()
6657 {
6658     JSParseNode *pn = relExpr();
6659     while (pn && tokenStream.matchToken(TOK_EQOP)) {
6660         JSOp op = tokenStream.currentToken().t_op;
6661         pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, relExpr(), tc);
6662     }
6663     return pn;
6664 }
6665
6666 JSParseNode *
6667 Parser::relExpr()
6668 {
6669     uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6670
6671     /*
6672      * Uses of the in operator in shiftExprs are always unambiguous,
6673      * so unset the flag that prohibits recognizing it.
6674      */
6675     tc->flags &= ~TCF_IN_FOR_INIT;
6676
6677     JSParseNode *pn = shiftExpr();
6678     while (pn &&
6679            (tokenStream.matchToken(TOK_RELOP) ||
6680             /*
6681              * Recognize the 'in' token as an operator only if we're not
6682              * currently in the init expr of a for loop.
6683              */
6684             (inForInitFlag == 0 && tokenStream.matchToken(TOK_IN)) ||
6685             tokenStream.matchToken(TOK_INSTANCEOF))) {
6686         TokenKind tt = tokenStream.currentToken().type;
6687         JSOp op = tokenStream.currentToken().t_op;
6688         pn = JSParseNode::newBinaryOrAppend(tt, op, pn, shiftExpr(), tc);
6689     }
6690     /* Restore previous state of inForInit flag. */
6691     tc->flags |= inForInitFlag;
6692
6693     return pn;
6694 }
6695
6696 JSParseNode *
6697 Parser::shiftExpr()
6698 {
6699     JSParseNode *pn = addExpr();
6700     while (pn && tokenStream.matchToken(TOK_SHOP)) {
6701         JSOp op = tokenStream.currentToken().t_op;
6702         pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, addExpr(), tc);
6703     }
6704     return pn;
6705 }
6706
6707 JSParseNode *
6708 Parser::addExpr()
6709 {
6710     JSParseNode *pn = mulExpr();
6711     while (pn &&
6712            (tokenStream.matchToken(TOK_PLUS) ||
6713             tokenStream.matchToken(TOK_MINUS))) {
6714         TokenKind tt = tokenStream.currentToken().type;
6715         JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6716         pn = JSParseNode::newBinaryOrAppend(tt, op, pn, mulExpr(), tc);
6717     }
6718     return pn;
6719 }
6720
6721 JSParseNode *
6722 Parser::mulExpr()
6723 {
6724     JSParseNode *pn = unaryExpr();
6725     while (pn && (tokenStream.matchToken(TOK_STAR) || tokenStream.matchToken(TOK_DIVOP))) {
6726         TokenKind tt = tokenStream.currentToken().type;
6727         JSOp op = tokenStream.currentToken().t_op;
6728         pn = JSParseNode::newBinaryOrAppend(tt, op, pn, unaryExpr(), tc);
6729     }
6730     return pn;
6731 }
6732
6733 static JSParseNode *
6734 SetLvalKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6735            JSParseNode *pn, JSParseNode *kid, const char *name)
6736 {
6737     if (kid->pn_type != TOK_NAME &&
6738         kid->pn_type != TOK_DOT &&
6739         (kid->pn_type != TOK_LP ||
6740          (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL &&
6741           kid->pn_op != JSOP_FUNCALL && kid->pn_op != JSOP_FUNAPPLY)) &&
6742 #if JS_HAS_XML_SUPPORT
6743         (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6744 #endif
6745         kid->pn_type != TOK_LB) {
6746         ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
6747         return NULL;
6748     }
6749     if (!CheckStrictAssignment(cx, tc, kid))
6750         return NULL;
6751     pn->pn_kid = kid;
6752     return kid;
6753 }
6754
6755 static const char incop_name_str[][10] = {"increment", "decrement"};
6756
6757 static JSBool
6758 SetIncOpKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6759             JSParseNode *pn, JSParseNode *kid,
6760             TokenKind tt, JSBool preorder)
6761 {
6762     JSOp op;
6763
6764     kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6765     if (!kid)
6766         return JS_FALSE;
6767     switch (kid->pn_type) {
6768       case TOK_NAME:
6769         op = (tt == TOK_INC)
6770              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6771              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6772         NoteLValue(cx, kid, tc);
6773         break;
6774
6775       case TOK_DOT:
6776         op = (tt == TOK_INC)
6777              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6778              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6779         break;
6780
6781       case TOK_LP:
6782         if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6783             return JS_FALSE;
6784         /* FALL THROUGH */
6785 #if JS_HAS_XML_SUPPORT
6786       case TOK_UNARYOP:
6787         if (kid->pn_op == JSOP_XMLNAME)
6788             kid->pn_op = JSOP_SETXMLNAME;
6789         /* FALL THROUGH */
6790 #endif
6791       case TOK_LB:
6792         op = (tt == TOK_INC)
6793              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6794              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6795         break;
6796
6797       default:
6798         JS_ASSERT(0);
6799         op = JSOP_NOP;
6800     }
6801     pn->pn_op = op;
6802     return JS_TRUE;
6803 }
6804
6805 JSParseNode *
6806 Parser::unaryExpr()
6807 {
6808     JSParseNode *pn, *pn2;
6809
6810     JS_CHECK_RECURSION(context, return NULL);
6811
6812     TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6813     switch (tt) {
6814       case TOK_UNARYOP:
6815       case TOK_PLUS:
6816       case TOK_MINUS:
6817         pn = UnaryNode::create(tc);
6818         if (!pn)
6819             return NULL;
6820         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
6821         pn->pn_op = tokenStream.currentToken().t_op;
6822         pn2 = unaryExpr();
6823         if (!pn2)
6824             return NULL;
6825         pn->pn_pos.end = pn2->pn_pos.end;
6826         pn->pn_kid = pn2;
6827         break;
6828
6829       case TOK_INC:
6830       case TOK_DEC:
6831         pn = UnaryNode::create(tc);
6832         if (!pn)
6833             return NULL;
6834         pn2 = memberExpr(JS_TRUE);
6835         if (!pn2)
6836             return NULL;
6837         if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, JS_TRUE))
6838             return NULL;
6839         pn->pn_pos.end = pn2->pn_pos.end;
6840         break;
6841
6842       case TOK_DELETE:
6843       {
6844         pn = UnaryNode::create(tc);
6845         if (!pn)
6846             return NULL;
6847         pn2 = unaryExpr();
6848         if (!pn2)
6849             return NULL;
6850         pn->pn_pos.end = pn2->pn_pos.end;
6851
6852         /*
6853          * Under ECMA3, deleting any unary expression is valid -- it simply
6854          * returns true. Here we fold constants before checking for a call
6855          * expression, in order to rule out delete of a generator expression.
6856          */
6857         if (!js_FoldConstants(context, pn2, tc))
6858             return NULL;
6859         switch (pn2->pn_type) {
6860           case TOK_LP:
6861             if (!(pn2->pn_xflags & PNX_SETCALL)) {
6862                 /*
6863                  * Call MakeSetCall to check for errors, but clear PNX_SETCALL
6864                  * because the optimizer will eliminate the useless delete.
6865                  */
6866                 if (!MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND))
6867                     return NULL;
6868                 pn2->pn_xflags &= ~PNX_SETCALL;
6869             }
6870             break;
6871           case TOK_NAME:
6872             if (!ReportStrictModeError(context, &tokenStream, tc, pn,
6873                                        JSMSG_DEPRECATED_DELETE_OPERAND)) {
6874                 return NULL;
6875             }
6876             pn2->pn_op = JSOP_DELNAME;
6877             if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
6878                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6879             break;
6880           default:;
6881         }
6882         pn->pn_kid = pn2;
6883         break;
6884       }
6885       case TOK_ERROR:
6886         return NULL;
6887
6888       default:
6889         tokenStream.ungetToken();
6890         pn = memberExpr(JS_TRUE);
6891         if (!pn)
6892             return NULL;
6893
6894         /* Don't look across a newline boundary for a postfix incop. */
6895         if (tokenStream.onCurrentLine(pn->pn_pos)) {
6896             tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6897             if (tt == TOK_INC || tt == TOK_DEC) {
6898                 (void) tokenStream.getToken();
6899                 pn2 = UnaryNode::create(tc);
6900                 if (!pn2)
6901                     return NULL;
6902                 if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, JS_FALSE))
6903                     return NULL;
6904                 pn2->pn_pos.begin = pn->pn_pos.begin;
6905                 pn = pn2;
6906             }
6907         }
6908         break;
6909     }
6910     return pn;
6911 }
6912
6913 #if JS_HAS_GENERATORS
6914
6915 /*
6916  * A dedicated helper for transplanting the comprehension expression E in
6917  *
6918  *   [E for (V in I)]   // array comprehension
6919  *   (E for (V in I))   // generator expression
6920  *
6921  * from its initial location in the AST, on the left of the 'for', to its final
6922  * position on the right. To avoid a separate pass we do this by adjusting the
6923  * blockids and name binding links that were established when E was parsed.
6924  *
6925  * A generator expression desugars like so:
6926  *
6927  *   (E for (V in I)) => (function () { for (var V in I) yield E; })()
6928  *
6929  * so the transplanter must adjust static level as well as blockid. E's source
6930  * coordinates in root->pn_pos are critical to deciding which binding links to
6931  * preserve and which to cut.
6932  *
6933  * NB: This is not a general tree transplanter -- it knows in particular that
6934  * the one or more bindings induced by V have not yet been created.
6935  */
6936 class CompExprTransplanter {
6937     JSParseNode     *root;
6938     JSTreeContext   *tc;
6939     bool            genexp;
6940     uintN           adjust;
6941     uintN           funcLevel;
6942
6943   public:
6944     CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6945       : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6946     {
6947     }
6948
6949     bool transplant(JSParseNode *pn);
6950 };
6951
6952 /*
6953  * Any definitions nested within the comprehension expression of a generator
6954  * expression must move "down" one static level, which of course increases the
6955  * upvar-frame-skip count.
6956  */
6957 static bool
6958 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6959 {
6960     if (!pn->pn_cookie.isFree()) {
6961         uintN level = pn->pn_cookie.level() + 1;
6962
6963         JS_ASSERT(level >= tc->staticLevel);
6964         if (level >= UpvarCookie::FREE_LEVEL) {
6965             JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
6966                                  JSMSG_TOO_DEEP, js_function_str);
6967             return false;
6968         }
6969
6970         pn->pn_cookie.set(level, pn->pn_cookie.slot());
6971     }
6972     return true;
6973 }
6974
6975 static void
6976 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6977 {
6978     JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6979     pn->pn_blockid += adjust;
6980     if (pn->pn_blockid >= tc->blockidGen)
6981         tc->blockidGen = pn->pn_blockid + 1;
6982 }
6983
6984 bool
6985 CompExprTransplanter::transplant(JSParseNode *pn)
6986 {
6987     if (!pn)
6988         return true;
6989
6990     switch (pn->pn_arity) {
6991       case PN_LIST:
6992         for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6993             if (!transplant(pn2))
6994                 return false;
6995         }
6996         if (pn->pn_pos >= root->pn_pos)
6997             AdjustBlockId(pn, adjust, tc);
6998         break;
6999
7000       case PN_TERNARY:
7001         if (!transplant(pn->pn_kid1) ||
7002             !transplant(pn->pn_kid2) ||
7003             !transplant(pn->pn_kid3))
7004             return false;
7005         break;
7006
7007       case PN_BINARY:
7008         if (!transplant(pn->pn_left))
7009             return false;
7010
7011         /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
7012         if (pn->pn_right != pn->pn_left) {
7013             if (!transplant(pn->pn_right))
7014                 return false;
7015         }
7016         break;
7017
7018       case PN_UNARY:
7019         if (!transplant(pn->pn_kid))
7020             return false;
7021         break;
7022
7023       case PN_FUNC:
7024       {
7025         /*
7026          * Only the first level of transplant recursion through functions needs
7027          * to reparent the funbox, since all descendant functions are correctly
7028          * linked under the top-most funbox. But every visit to this case needs
7029          * to update funbox->level.
7030          *
7031          * Recall that funbox->level is the static level of the code containing
7032          * the definition or expression of the function and not the static level
7033          * of the function's body.
7034          */
7035         JSFunctionBox *funbox = pn->pn_funbox;
7036
7037         funbox->level = tc->staticLevel + funcLevel;
7038         if (++funcLevel == 1 && genexp) {
7039             JSFunctionBox *parent = tc->funbox;
7040
7041             JSFunctionBox **funboxp = &tc->parent->functionList;
7042             while (*funboxp != funbox)
7043                 funboxp = &(*funboxp)->siblings;
7044             *funboxp = funbox->siblings;
7045
7046             funbox->parent = parent;
7047             funbox->siblings = parent->kids;
7048             parent->kids = funbox;
7049             funbox->level = tc->staticLevel;
7050         }
7051         /* FALL THROUGH */
7052       }
7053
7054       case PN_NAME:
7055         if (!transplant(pn->maybeExpr()))
7056             return false;
7057         if (pn->pn_arity == PN_FUNC)
7058             --funcLevel;
7059
7060         if (pn->pn_defn) {
7061             if (genexp && !BumpStaticLevel(pn, tc))
7062                 return false;
7063         } else if (pn->pn_used) {
7064             JS_ASSERT(pn->pn_op != JSOP_NOP);
7065             JS_ASSERT(pn->pn_cookie.isFree());
7066
7067             JSDefinition *dn = pn->pn_lexdef;
7068             JS_ASSERT(dn->pn_defn);
7069
7070             /*
7071              * Adjust the definition's block id only if it is a placeholder not
7072              * to the left of the root node, and if pn is the last use visited
7073              * in the comprehension expression (to avoid adjusting the blockid
7074              * multiple times).
7075              *
7076              * Non-placeholder definitions within the comprehension expression
7077              * will be visited further below.
7078              */
7079             if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
7080                 if (genexp && !BumpStaticLevel(dn, tc))
7081                     return false;
7082                 AdjustBlockId(dn, adjust, tc);
7083             }
7084
7085             JSAtom *atom = pn->pn_atom;
7086 #ifdef DEBUG
7087             JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
7088             JS_ASSERT(!stmt || stmt != tc->topStmt);
7089 #endif
7090             if (genexp && PN_OP(dn) != JSOP_CALLEE) {
7091                 JS_ASSERT(!tc->decls.lookup(atom));
7092
7093                 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
7094                     JSAtomListElement *ale = tc->lexdeps.add(tc->parser, atom);
7095                     if (!ale)
7096                         return false;
7097
7098                     if (dn->pn_pos >= root->pn_pos) {
7099                         tc->parent->lexdeps.remove(tc->parser, atom);
7100                     } else {
7101                         JSDefinition *dn2 = (JSDefinition *)NameNode::create(atom, tc);
7102                         if (!dn2)
7103                             return false;
7104
7105                         dn2->pn_type = TOK_NAME;
7106                         dn2->pn_op = JSOP_NOP;
7107                         dn2->pn_defn = true;
7108                         dn2->pn_dflags |= PND_PLACEHOLDER;
7109                         dn2->pn_pos = root->pn_pos;
7110
7111                         JSParseNode **pnup = &dn->dn_uses;
7112                         JSParseNode *pnu;
7113                         while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
7114                             pnu->pn_lexdef = dn2;
7115                             dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
7116                             pnup = &pnu->pn_link;
7117                         }
7118                         dn2->dn_uses = dn->dn_uses;
7119                         dn->dn_uses = *pnup;
7120                         *pnup = NULL;
7121
7122                         dn = dn2;
7123                     }
7124
7125                     ALE_SET_DEFN(ale, dn);
7126                 }
7127             }
7128         }
7129
7130         if (pn->pn_pos >= root->pn_pos)
7131             AdjustBlockId(pn, adjust, tc);
7132         break;
7133
7134       case PN_NAMESET:
7135         if (!transplant(pn->pn_tree))
7136             return false;
7137         break;
7138     }
7139     return true;
7140 }
7141
7142 /*
7143  * Starting from a |for| keyword after the first array initialiser element or
7144  * an expression in an open parenthesis, parse the tail of the comprehension
7145  * or generator expression signified by this |for| keyword in context.
7146  *
7147  * Return null on failure, else return the top-most parse node for the array
7148  * comprehension or generator expression, with a unary node as the body of the
7149  * (possibly nested) for-loop, initialized by |type, op, kid|.
7150  */
7151 JSParseNode *
7152 Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
7153                           TokenKind type, JSOp op)
7154 {
7155     uintN adjust;
7156     JSParseNode *pn, *pn2, *pn3, **pnp;
7157     JSStmtInfo stmtInfo;
7158     BindData data;
7159     TokenKind tt;
7160     JSAtom *atom;
7161
7162     JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
7163
7164     if (type == TOK_SEMI) {
7165         /*
7166          * Generator expression desugars to an immediately applied lambda that
7167          * yields the next value from a for-in loop (possibly nested, and with
7168          * optional if guard). Make pn be the TOK_LC body node.
7169          */
7170         pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
7171         if (!pn)
7172             return NULL;
7173         adjust = pn->pn_blockid - blockid;
7174     } else {
7175         JS_ASSERT(type == TOK_ARRAYPUSH);
7176
7177         /*
7178          * Make a parse-node and literal object representing the block scope of
7179          * this array comprehension. Our caller in primaryExpr, the TOK_LB case
7180          * aka the array initialiser case, has passed the blockid to claim for
7181          * the comprehension's block scope. We allocate that id or one above it
7182          * here, by calling js_PushLexicalScope.
7183          *
7184          * In the case of a comprehension expression that has nested blocks
7185          * (e.g., let expressions), we will allocate a higher blockid but then
7186          * slide all blocks "to the right" to make room for the comprehension's
7187          * block scope.
7188          */
7189         adjust = tc->blockid();
7190         pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
7191         if (!pn)
7192             return NULL;
7193
7194         JS_ASSERT(blockid <= pn->pn_blockid);
7195         JS_ASSERT(blockid < tc->blockidGen);
7196         JS_ASSERT(tc->bodyid < blockid);
7197         pn->pn_blockid = stmtInfo.blockid = blockid;
7198         JS_ASSERT(adjust < blockid);
7199         adjust = blockid - adjust;
7200     }
7201
7202     pnp = &pn->pn_expr;
7203
7204     CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
7205     transplanter.transplant(kid);
7206
7207     data.pn = NULL;
7208     data.op = JSOP_NOP;
7209     data.binder = BindLet;
7210     data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
7211
7212     do {
7213         /*
7214          * FOR node is binary, left is loop control and right is body.  Use
7215          * index to count each block-local let-variable on the left-hand side
7216          * of the IN.
7217          */
7218         pn2 = BinaryNode::create(tc);
7219         if (!pn2)
7220             return NULL;
7221
7222         pn2->pn_op = JSOP_ITER;
7223         pn2->pn_iflags = JSITER_ENUMERATE;
7224         if (tokenStream.matchToken(TOK_NAME)) {
7225             if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
7226                 pn2->pn_iflags |= JSITER_FOREACH;
7227             else
7228                 tokenStream.ungetToken();
7229         }
7230         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
7231
7232         atom = NULL;
7233         tt = tokenStream.getToken();
7234         switch (tt) {
7235 #if JS_HAS_DESTRUCTURING
7236           case TOK_LB:
7237           case TOK_LC:
7238             tc->flags |= TCF_DECL_DESTRUCTURING;
7239             pn3 = primaryExpr(tt, JS_FALSE);
7240             tc->flags &= ~TCF_DECL_DESTRUCTURING;
7241             if (!pn3)
7242                 return NULL;
7243             break;
7244 #endif
7245
7246           case TOK_NAME:
7247             atom = tokenStream.currentToken().t_atom;
7248
7249             /*
7250              * Create a name node with pn_op JSOP_NAME.  We can't set pn_op to
7251              * JSOP_GETLOCAL here, because we don't yet know the block's depth
7252              * in the operand stack frame.  The code generator computes that,
7253              * and it tries to bind all names to slots, so we must let it do
7254              * the deed.
7255              */
7256             pn3 = NewBindingNode(atom, tc, true);
7257             if (!pn3)
7258                 return NULL;
7259             break;
7260
7261           default:
7262             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
7263
7264           case TOK_ERROR:
7265             return NULL;
7266         }
7267
7268         MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
7269         JSParseNode *pn4 = expr();
7270         if (!pn4)
7271             return NULL;
7272         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
7273
7274         switch (tt) {
7275 #if JS_HAS_DESTRUCTURING
7276           case TOK_LB:
7277           case TOK_LC:
7278             if (!CheckDestructuring(context, &data, pn3, NULL, tc))
7279                 return NULL;
7280
7281             if (versionNumber() == JSVERSION_1_7) {
7282                 /* Destructuring requires [key, value] enumeration in JS1.7. */
7283                 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
7284                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
7285                     return NULL;
7286                 }
7287
7288                 JS_ASSERT(pn2->pn_op == JSOP_ITER);
7289                 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
7290                 if (!(pn2->pn_iflags & JSITER_FOREACH))
7291                     pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
7292             }
7293             break;
7294 #endif
7295
7296           case TOK_NAME:
7297             data.pn = pn3;
7298             if (!data.binder(context, &data, atom, tc))
7299                 return NULL;
7300             break;
7301
7302           default:;
7303         }
7304
7305         pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
7306         if (!pn2->pn_left)
7307             return NULL;
7308         *pnp = pn2;
7309         pnp = &pn2->pn_right;
7310     } while (tokenStream.matchToken(TOK_FOR));
7311
7312     if (tokenStream.matchToken(TOK_IF)) {
7313         pn2 = TernaryNode::create(tc);
7314         if (!pn2)
7315             return NULL;
7316         pn2->pn_kid1 = condition();
7317         if (!pn2->pn_kid1)
7318             return NULL;
7319         *pnp = pn2;
7320         pnp = &pn2->pn_kid2;
7321     }
7322
7323     pn2 = UnaryNode::create(tc);
7324     if (!pn2)
7325         return NULL;
7326     pn2->pn_type = type;
7327     pn2->pn_op = op;
7328     pn2->pn_kid = kid;
7329     *pnp = pn2;
7330
7331     PopStatement(tc);
7332     return pn;
7333 }
7334
7335 #if JS_HAS_GENERATOR_EXPRS
7336
7337 /*
7338  * Starting from a |for| keyword after an expression, parse the comprehension
7339  * tail completing this generator expression. Wrap the expression at kid in a
7340  * generator function that is immediately called to evaluate to the generator
7341  * iterator that is the value of this generator expression.
7342  *
7343  * |kid| must be the expression before the |for| keyword; we return an
7344  * application of a generator function that includes the |for| loops and
7345  * |if| guards, with |kid| as the operand of a |yield| expression as the
7346  * innermost loop body.
7347  *
7348  * Note how unlike Python, we do not evaluate the expression to the right of
7349  * the first |in| in the chain of |for| heads. Instead, a generator expression
7350  * is merely sugar for a generator function expression and its application.
7351  */
7352 JSParseNode *
7353 Parser::generatorExpr(JSParseNode *kid)
7354 {
7355     /* Create a |yield| node for |kid|. */
7356     JSParseNode *pn = UnaryNode::create(tc);
7357     if (!pn)
7358         return NULL;
7359     pn->pn_type = TOK_YIELD;
7360     pn->pn_op = JSOP_YIELD;
7361     pn->pn_parens = true;
7362     pn->pn_pos = kid->pn_pos;
7363     pn->pn_kid = kid;
7364     pn->pn_hidden = true;
7365
7366     /* Make a new node for the desugared generator function. */
7367     JSParseNode *genfn = FunctionNode::create(tc);
7368     if (!genfn)
7369         return NULL;
7370     genfn->pn_type = TOK_FUNCTION;
7371     genfn->pn_op = JSOP_LAMBDA;
7372     JS_ASSERT(!genfn->pn_body);
7373     genfn->pn_dflags = PND_FUNARG;
7374
7375     {
7376         JSTreeContext *outertc = tc;
7377         JSTreeContext gentc(tc->parser);
7378
7379         JSFunctionBox *funbox = EnterFunction(genfn, &gentc);
7380         if (!funbox)
7381             return NULL;
7382
7383         /*
7384          * We have to dance around a bit to propagate sharp variables from
7385          * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
7386          * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
7387          * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
7388          * do propagate to gentc.
7389          */
7390         if (outertc->flags & TCF_HAS_SHARPS) {
7391             gentc.flags |= TCF_IN_FUNCTION;
7392             if (!gentc.ensureSharpSlots())
7393                 return NULL;
7394         }
7395
7396         /*
7397          * We assume conservatively that any deoptimization flag in tc->flags
7398          * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
7399          * propagate these flags into genfn. For code simplicity we also do
7400          * not detect if the flags were only set in the kid and could be
7401          * removed from tc->flags.
7402          */
7403         gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
7404                        (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
7405         funbox->tcflags |= gentc.flags;
7406         genfn->pn_funbox = funbox;
7407         genfn->pn_blockid = gentc.bodyid;
7408
7409         JSParseNode *body = comprehensionTail(pn, outertc->blockid());
7410         if (!body)
7411             return NULL;
7412         JS_ASSERT(!genfn->pn_body);
7413         genfn->pn_body = body;
7414         genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
7415         genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
7416
7417         if (!LeaveFunction(genfn, &gentc))
7418             return NULL;
7419     }
7420
7421     /*
7422      * Our result is a call expression that invokes the anonymous generator
7423      * function object.
7424      */
7425     JSParseNode *result = ListNode::create(tc);
7426     if (!result)
7427         return NULL;
7428     result->pn_type = TOK_LP;
7429     result->pn_op = JSOP_CALL;
7430     result->pn_pos.begin = genfn->pn_pos.begin;
7431     result->initList(genfn);
7432     return result;
7433 }
7434
7435 static const char js_generator_str[] = "generator";
7436
7437 #endif /* JS_HAS_GENERATOR_EXPRS */
7438 #endif /* JS_HAS_GENERATORS */
7439
7440 JSBool
7441 Parser::argumentList(JSParseNode *listNode)
7442 {
7443     if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
7444         return JS_TRUE;
7445
7446     do {
7447         JSParseNode *argNode = assignExpr();
7448         if (!argNode)
7449             return JS_FALSE;
7450 #if JS_HAS_GENERATORS
7451         if (argNode->pn_type == TOK_YIELD &&
7452             !argNode->pn_parens &&
7453             tokenStream.peekToken() == TOK_COMMA) {
7454             reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
7455             return JS_FALSE;
7456         }
7457 #endif
7458 #if JS_HAS_GENERATOR_EXPRS
7459         if (tokenStream.matchToken(TOK_FOR)) {
7460             argNode = generatorExpr(argNode);
7461             if (!argNode)
7462                 return JS_FALSE;
7463             if (listNode->pn_count > 1 ||
7464                 tokenStream.peekToken() == TOK_COMMA) {
7465                 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
7466                                   js_generator_str);
7467                 return JS_FALSE;
7468             }
7469         }
7470 #endif
7471         listNode->append(argNode);
7472     } while (tokenStream.matchToken(TOK_COMMA));
7473
7474     if (tokenStream.getToken() != TOK_RP) {
7475         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
7476         return JS_FALSE;
7477     }
7478     return JS_TRUE;
7479 }
7480
7481 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
7482 static JSParseNode *
7483 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
7484 {
7485     if (pn->pn_type == TOK_FUNCTION) {
7486         JS_ASSERT(pn->pn_arity == PN_FUNC);
7487
7488         JSFunctionBox *funbox = pn->pn_funbox;
7489         JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
7490         if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
7491             pn->pn_dflags &= ~PND_FUNARG;
7492     }
7493     return pn;
7494 }
7495
7496 JSParseNode *
7497 Parser::memberExpr(JSBool allowCallSyntax)
7498 {
7499     JSParseNode *pn, *pn2, *pn3;
7500
7501     JS_CHECK_RECURSION(context, return NULL);
7502
7503     /* Check for new expression first. */
7504     TokenKind tt = tokenStream.getToken(TSF_OPERAND);
7505     if (tt == TOK_NEW) {
7506         pn = ListNode::create(tc);
7507         if (!pn)
7508             return NULL;
7509         pn2 = memberExpr(JS_FALSE);
7510         if (!pn2)
7511             return NULL;
7512         pn2 = CheckForImmediatelyAppliedLambda(pn2);
7513         pn->pn_op = JSOP_NEW;
7514         pn->initList(pn2);
7515         pn->pn_pos.begin = pn2->pn_pos.begin;
7516
7517         if (tokenStream.matchToken(TOK_LP) && !argumentList(pn))
7518             return NULL;
7519         if (pn->pn_count > ARGC_LIMIT) {
7520             JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7521                                  JSMSG_TOO_MANY_CON_ARGS);
7522             return NULL;
7523         }
7524         pn->pn_pos.end = pn->last()->pn_pos.end;
7525     } else {
7526         pn = primaryExpr(tt, JS_FALSE);
7527         if (!pn)
7528             return NULL;
7529
7530         if (pn->pn_type == TOK_ANYNAME ||
7531             pn->pn_type == TOK_AT ||
7532             pn->pn_type == TOK_DBLCOLON) {
7533             pn2 = NewOrRecycledNode(tc);
7534             if (!pn2)
7535                 return NULL;
7536             pn2->pn_type = TOK_UNARYOP;
7537             pn2->pn_pos = pn->pn_pos;
7538             pn2->pn_op = JSOP_XMLNAME;
7539             pn2->pn_arity = PN_UNARY;
7540             pn2->pn_parens = false;
7541             pn2->pn_kid = pn;
7542             pn = pn2;
7543         }
7544     }
7545
7546     while ((tt = tokenStream.getToken()) > TOK_EOF) {
7547         if (tt == TOK_DOT) {
7548             pn2 = NameNode::create(NULL, tc);
7549             if (!pn2)
7550                 return NULL;
7551 #if JS_HAS_XML_SUPPORT
7552             tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7553             pn3 = primaryExpr(tt, JS_TRUE);
7554             if (!pn3)
7555                 return NULL;
7556
7557             /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
7558             if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
7559                 pn2->pn_op = JSOP_GETPROP;
7560                 pn2->pn_expr = pn;
7561                 pn2->pn_atom = pn3->pn_atom;
7562                 RecycleTree(pn3, tc);
7563             } else {
7564                 if (tt == TOK_LP) {
7565                     pn2->pn_type = TOK_FILTER;
7566                     pn2->pn_op = JSOP_FILTER;
7567
7568                     /* A filtering predicate is like a with statement. */
7569                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
7570                 } else if (TokenKindIsXML(PN_TYPE(pn3))) {
7571                     pn2->pn_type = TOK_LB;
7572                     pn2->pn_op = JSOP_GETELEM;
7573                 } else {
7574                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
7575                     return NULL;
7576                 }
7577                 pn2->pn_arity = PN_BINARY;
7578                 pn2->pn_left = pn;
7579                 pn2->pn_right = pn3;
7580             }
7581 #else
7582             MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME, JSMSG_NAME_AFTER_DOT, TSF_KEYWORD_IS_NAME);
7583             pn2->pn_op = JSOP_GETPROP;
7584             pn2->pn_expr = pn;
7585             pn2->pn_atom = tokenStream.currentToken().t_atom;
7586 #endif
7587             pn2->pn_pos.begin = pn->pn_pos.begin;
7588             pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7589 #if JS_HAS_XML_SUPPORT
7590         } else if (tt == TOK_DBLDOT) {
7591             pn2 = BinaryNode::create(tc);
7592             if (!pn2)
7593                 return NULL;
7594             tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7595             pn3 = primaryExpr(tt, JS_TRUE);
7596             if (!pn3)
7597                 return NULL;
7598             tt = PN_TYPE(pn3);
7599             if (tt == TOK_NAME && !pn3->pn_parens) {
7600                 pn3->pn_type = TOK_STRING;
7601                 pn3->pn_arity = PN_NULLARY;
7602                 pn3->pn_op = JSOP_QNAMEPART;
7603             } else if (!TokenKindIsXML(tt)) {
7604                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
7605                 return NULL;
7606             }
7607             pn2->pn_op = JSOP_DESCENDANTS;
7608             pn2->pn_left = pn;
7609             pn2->pn_right = pn3;
7610             pn2->pn_pos.begin = pn->pn_pos.begin;
7611             pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7612 #endif
7613         } else if (tt == TOK_LB) {
7614             pn2 = BinaryNode::create(tc);
7615             if (!pn2)
7616                 return NULL;
7617             pn3 = expr();
7618             if (!pn3)
7619                 return NULL;
7620
7621             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
7622             pn2->pn_pos.begin = pn->pn_pos.begin;
7623             pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7624
7625             /*
7626              * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7627              * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7628              * the interpreter from fast property access. However, if the
7629              * bracketed string is a uint32, we rewrite pn3 to be a number
7630              * instead of a string.
7631              */
7632             do {
7633                 if (pn3->pn_type == TOK_STRING) {
7634                     jsuint index;
7635
7636                     if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
7637                         pn2->pn_type = TOK_DOT;
7638                         pn2->pn_op = JSOP_GETPROP;
7639                         pn2->pn_arity = PN_NAME;
7640                         pn2->pn_expr = pn;
7641                         pn2->pn_atom = pn3->pn_atom;
7642                         break;
7643                     }
7644                     pn3->pn_type = TOK_NUMBER;
7645                     pn3->pn_op = JSOP_DOUBLE;
7646                     pn3->pn_dval = index;
7647                 }
7648                 pn2->pn_op = JSOP_GETELEM;
7649                 pn2->pn_left = pn;
7650                 pn2->pn_right = pn3;
7651             } while (0);
7652         } else if (allowCallSyntax && tt == TOK_LP) {
7653             pn2 = ListNode::create(tc);
7654             if (!pn2)
7655                 return NULL;
7656             pn2->pn_op = JSOP_CALL;
7657
7658             pn = CheckForImmediatelyAppliedLambda(pn);
7659             if (pn->pn_op == JSOP_NAME) {
7660                 if (pn->pn_atom == context->runtime->atomState.evalAtom) {
7661                     /* Select JSOP_EVAL and flag tc as heavyweight. */
7662                     pn2->pn_op = JSOP_EVAL;
7663                     tc->noteCallsEval();
7664                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
7665                 }
7666             } else if (pn->pn_op == JSOP_GETPROP) {
7667                 /* Select JSOP_FUNAPPLY given foo.apply(...). */
7668                 if (pn->pn_atom == context->runtime->atomState.applyAtom)
7669                     pn2->pn_op = JSOP_FUNAPPLY;
7670                 else if (pn->pn_atom == context->runtime->atomState.callAtom)
7671                     pn2->pn_op = JSOP_FUNCALL;
7672             }
7673
7674             pn2->initList(pn);
7675             pn2->pn_pos.begin = pn->pn_pos.begin;
7676
7677             if (!argumentList(pn2))
7678                 return NULL;
7679             if (pn2->pn_count > ARGC_LIMIT) {
7680                 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7681                                      JSMSG_TOO_MANY_FUN_ARGS);
7682                 return NULL;
7683             }
7684             pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7685         } else {
7686             tokenStream.ungetToken();
7687             return pn;
7688         }
7689
7690         pn = pn2;
7691     }
7692     if (tt == TOK_ERROR)
7693         return NULL;
7694     return pn;
7695 }
7696
7697 JSParseNode *
7698 Parser::bracketedExpr()
7699 {
7700     uintN oldflags;
7701     JSParseNode *pn;
7702
7703     /*
7704      * Always accept the 'in' operator in a parenthesized expression,
7705      * where it's unambiguous, even if we might be parsing the init of a
7706      * for statement.
7707      */
7708     oldflags = tc->flags;
7709     tc->flags &= ~TCF_IN_FOR_INIT;
7710     pn = expr();
7711     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7712     return pn;
7713 }
7714
7715 #if JS_HAS_XML_SUPPORT
7716
7717 JSParseNode *
7718 Parser::endBracketedExpr()
7719 {
7720     JSParseNode *pn;
7721
7722     pn = bracketedExpr();
7723     if (!pn)
7724         return NULL;
7725
7726     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7727     return pn;
7728 }
7729
7730 /*
7731  * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7732  *
7733  *      AttributeIdentifier:
7734  *              @ PropertySelector
7735  *              @ QualifiedIdentifier
7736  *              @ [ Expression ]
7737  *
7738  *      PropertySelector:
7739  *              Identifier
7740  *              *
7741  *
7742  *      QualifiedIdentifier:
7743  *              PropertySelector :: PropertySelector
7744  *              PropertySelector :: [ Expression ]
7745  *
7746  * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7747  *
7748  *      AttributeIdentifier:
7749  *              @ QualifiedIdentifier
7750  *              @ [ Expression ]
7751  *
7752  *      PropertySelector:
7753  *              Identifier
7754  *              *
7755  *
7756  *      QualifiedIdentifier:
7757  *              PropertySelector :: PropertySelector
7758  *              PropertySelector :: [ Expression ]
7759  *              PropertySelector
7760  *
7761  * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7762  * for that rule to result in a name node, but ECMA-357 extends the grammar
7763  * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7764  *
7765  *      QualifiedIdentifier:
7766  *              PropertySelector QualifiedSuffix
7767  *
7768  *      QualifiedSuffix:
7769  *              :: PropertySelector
7770  *              :: [ Expression ]
7771  *              /nothing/
7772  *
7773  * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7774  *
7775  *      PrimaryExpression:
7776  *              Identifier QualifiedSuffix
7777  *
7778  * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7779  * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7780  */
7781 JSParseNode *
7782 Parser::propertySelector()
7783 {
7784     JSParseNode *pn;
7785
7786     pn = NullaryNode::create(tc);
7787     if (!pn)
7788         return NULL;
7789     if (pn->pn_type == TOK_STAR) {
7790         pn->pn_type = TOK_ANYNAME;
7791         pn->pn_op = JSOP_ANYNAME;
7792         pn->pn_atom = context->runtime->atomState.starAtom;
7793     } else {
7794         JS_ASSERT(pn->pn_type == TOK_NAME);
7795         pn->pn_op = JSOP_QNAMEPART;
7796         pn->pn_arity = PN_NAME;
7797         pn->pn_atom = tokenStream.currentToken().t_atom;
7798         pn->pn_cookie.makeFree();
7799     }
7800     return pn;
7801 }
7802
7803 JSParseNode *
7804 Parser::qualifiedSuffix(JSParseNode *pn)
7805 {
7806     JSParseNode *pn2, *pn3;
7807     TokenKind tt;
7808
7809     JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
7810     pn2 = NameNode::create(NULL, tc);
7811     if (!pn2)
7812         return NULL;
7813
7814     /* Left operand of :: must be evaluated if it is an identifier. */
7815     if (pn->pn_op == JSOP_QNAMEPART)
7816         pn->pn_op = JSOP_NAME;
7817
7818     tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7819     if (tt == TOK_STAR || tt == TOK_NAME) {
7820         /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7821         pn2->pn_op = JSOP_QNAMECONST;
7822         pn2->pn_pos.begin = pn->pn_pos.begin;
7823         pn2->pn_atom = (tt == TOK_STAR)
7824                        ? context->runtime->atomState.starAtom
7825                        : tokenStream.currentToken().t_atom;
7826         pn2->pn_expr = pn;
7827         pn2->pn_cookie.makeFree();
7828         return pn2;
7829     }
7830
7831     if (tt != TOK_LB) {
7832         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7833         return NULL;
7834     }
7835     pn3 = endBracketedExpr();
7836     if (!pn3)
7837         return NULL;
7838
7839     pn2->pn_op = JSOP_QNAME;
7840     pn2->pn_arity = PN_BINARY;
7841     pn2->pn_pos.begin = pn->pn_pos.begin;
7842     pn2->pn_pos.end = pn3->pn_pos.end;
7843     pn2->pn_left = pn;
7844     pn2->pn_right = pn3;
7845     return pn2;
7846 }
7847
7848 JSParseNode *
7849 Parser::qualifiedIdentifier()
7850 {
7851     JSParseNode *pn;
7852
7853     pn = propertySelector();
7854     if (!pn)
7855         return NULL;
7856     if (tokenStream.matchToken(TOK_DBLCOLON)) {
7857         /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7858         tc->flags |= TCF_FUN_HEAVYWEIGHT;
7859         pn = qualifiedSuffix(pn);
7860     }
7861     return pn;
7862 }
7863
7864 JSParseNode *
7865 Parser::attributeIdentifier()
7866 {
7867     JSParseNode *pn, *pn2;
7868     TokenKind tt;
7869
7870     JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
7871     pn = UnaryNode::create(tc);
7872     if (!pn)
7873         return NULL;
7874     pn->pn_op = JSOP_TOATTRNAME;
7875     tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7876     if (tt == TOK_STAR || tt == TOK_NAME) {
7877         pn2 = qualifiedIdentifier();
7878     } else if (tt == TOK_LB) {
7879         pn2 = endBracketedExpr();
7880     } else {
7881         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7882         return NULL;
7883     }
7884     if (!pn2)
7885         return NULL;
7886     pn->pn_kid = pn2;
7887     return pn;
7888 }
7889
7890 /*
7891  * Make a TOK_LC unary node whose pn_kid is an expression.
7892  */
7893 JSParseNode *
7894 Parser::xmlExpr(JSBool inTag)
7895 {
7896     JSParseNode *pn, *pn2;
7897
7898     JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
7899     pn = UnaryNode::create(tc);
7900     if (!pn)
7901         return NULL;
7902
7903     /*
7904      * Turn off XML tag mode. We save the old value of the flag because it may
7905      * already be off: XMLExpr is called both from within a tag, and from
7906      * within text contained in an element, but outside of any start, end, or
7907      * point tag.
7908      */
7909     bool oldflag = tokenStream.isXMLTagMode();
7910     tokenStream.setXMLTagMode(false);
7911     pn2 = expr();
7912     if (!pn2)
7913         return NULL;
7914
7915     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7916     tokenStream.setXMLTagMode(oldflag);
7917     pn->pn_kid = pn2;
7918     pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7919     return pn;
7920 }
7921
7922 /*
7923  * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7924  * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI.  When converting
7925  * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7926  * child of a container tag.
7927  */
7928 JSParseNode *
7929 Parser::xmlAtomNode()
7930 {
7931     JSParseNode *pn = NullaryNode::create(tc);
7932     if (!pn)
7933         return NULL;
7934     const Token &tok = tokenStream.currentToken();
7935     pn->pn_op = tok.t_op;
7936     pn->pn_atom = tok.t_atom;
7937     if (tok.type == TOK_XMLPI)
7938         pn->pn_atom2 = tok.t_atom2;
7939     return pn;
7940 }
7941
7942 /*
7943  * Parse the productions:
7944  *
7945  *      XMLNameExpr:
7946  *              XMLName XMLNameExpr?
7947  *              { Expr } XMLNameExpr?
7948  *
7949  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7950  * a list of names and/or expressions, a single expression, or a single name.
7951  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7952  * will be TOK_LC.
7953  */
7954 JSParseNode *
7955 Parser::xmlNameExpr()
7956 {
7957     JSParseNode *pn, *pn2, *list;
7958     TokenKind tt;
7959
7960     pn = list = NULL;
7961     do {
7962         tt = tokenStream.currentToken().type;
7963         if (tt == TOK_LC) {
7964             pn2 = xmlExpr(JS_TRUE);
7965             if (!pn2)
7966                 return NULL;
7967         } else {
7968             JS_ASSERT(tt == TOK_XMLNAME);
7969             pn2 = xmlAtomNode();
7970             if (!pn2)
7971                 return NULL;
7972         }
7973
7974         if (!pn) {
7975             pn = pn2;
7976         } else {
7977             if (!list) {
7978                 list = ListNode::create(tc);
7979                 if (!list)
7980                     return NULL;
7981                 list->pn_type = TOK_XMLNAME;
7982                 list->pn_pos.begin = pn->pn_pos.begin;
7983                 list->initList(pn);
7984                 list->pn_xflags = PNX_CANTFOLD;
7985                 pn = list;
7986             }
7987             pn->pn_pos.end = pn2->pn_pos.end;
7988             pn->append(pn2);
7989         }
7990     } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
7991
7992     tokenStream.ungetToken();
7993     return pn;
7994 }
7995
7996 /*
7997  * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7998  * at compile time into a JSXML tree.
7999  */
8000 #define XML_FOLDABLE(pn)        ((pn)->pn_arity == PN_LIST                    \
8001                                  ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0      \
8002                                  : (pn)->pn_type != TOK_LC)
8003
8004 /*
8005  * Parse the productions:
8006  *
8007  *      XMLTagContent:
8008  *              XMLNameExpr
8009  *              XMLTagContent S XMLNameExpr S? = S? XMLAttr
8010  *              XMLTagContent S XMLNameExpr S? = S? { Expr }
8011  *
8012  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
8013  * produces a list of name and attribute values and/or braced expressions, a
8014  * single expression, or a single name.
8015  *
8016  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
8017  * XMLTagContent: XMLNameExpr.  If pn_type is not TOK_XMLNAME but pn_arity is
8018  * PN_LIST, pn_type will be tagtype.  If PN_UNARY, pn_type will be TOK_LC and
8019  * we parsed exactly one expression.
8020  */
8021 JSParseNode *
8022 Parser::xmlTagContent(TokenKind tagtype, JSAtom **namep)
8023 {
8024     JSParseNode *pn, *pn2, *list;
8025     TokenKind tt;
8026
8027     pn = xmlNameExpr();
8028     if (!pn)
8029         return NULL;
8030     *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
8031     list = NULL;
8032
8033     while (tokenStream.matchToken(TOK_XMLSPACE)) {
8034         tt = tokenStream.getToken();
8035         if (tt != TOK_XMLNAME && tt != TOK_LC) {
8036             tokenStream.ungetToken();
8037             break;
8038         }
8039
8040         pn2 = xmlNameExpr();
8041         if (!pn2)
8042             return NULL;
8043         if (!list) {
8044             list = ListNode::create(tc);
8045             if (!list)
8046                 return NULL;
8047             list->pn_type = tagtype;
8048             list->pn_pos.begin = pn->pn_pos.begin;
8049             list->initList(pn);
8050             pn = list;
8051         }
8052         pn->append(pn2);
8053         if (!XML_FOLDABLE(pn2))
8054             pn->pn_xflags |= PNX_CANTFOLD;
8055
8056         tokenStream.matchToken(TOK_XMLSPACE);
8057         MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
8058         tokenStream.matchToken(TOK_XMLSPACE);
8059
8060         tt = tokenStream.getToken();
8061         if (tt == TOK_XMLATTR) {
8062             pn2 = xmlAtomNode();
8063         } else if (tt == TOK_LC) {
8064             pn2 = xmlExpr(JS_TRUE);
8065             pn->pn_xflags |= PNX_CANTFOLD;
8066         } else {
8067             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE);
8068             return NULL;
8069         }
8070         if (!pn2)
8071             return NULL;
8072         pn->pn_pos.end = pn2->pn_pos.end;
8073         pn->append(pn2);
8074     }
8075
8076     return pn;
8077 }
8078
8079 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result)                                              \
8080     JS_BEGIN_MACRO                                                                          \
8081         if ((tt) <= TOK_EOF) {                                                              \
8082             if ((tt) == TOK_EOF) {                                                          \
8083                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_END_OF_XML_SOURCE);           \
8084             }                                                                               \
8085             return result;                                                                  \
8086         }                                                                                   \
8087     JS_END_MACRO
8088
8089 /*
8090  * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
8091  * that opens the end tag for the container.
8092  */
8093 JSBool
8094 Parser::xmlElementContent(JSParseNode *pn)
8095 {
8096     tokenStream.setXMLTagMode(false);
8097     for (;;) {
8098         TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
8099         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
8100
8101         JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
8102         JSAtom *textAtom = tokenStream.currentToken().t_atom;
8103         if (textAtom) {
8104             /* Non-zero-length XML text scanned. */
8105             JSParseNode *pn2 = xmlAtomNode();
8106             if (!pn2)
8107                 return JS_FALSE;
8108             pn->pn_pos.end = pn2->pn_pos.end;
8109             pn->append(pn2);
8110         }
8111
8112         tt = tokenStream.getToken(TSF_OPERAND);
8113         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
8114         if (tt == TOK_XMLETAGO)
8115             break;
8116
8117         JSParseNode *pn2;
8118         if (tt == TOK_LC) {
8119             pn2 = xmlExpr(JS_FALSE);
8120             pn->pn_xflags |= PNX_CANTFOLD;
8121         } else if (tt == TOK_XMLSTAGO) {
8122             pn2 = xmlElementOrList(JS_FALSE);
8123             if (pn2) {
8124                 pn2->pn_xflags &= ~PNX_XMLROOT;
8125                 pn->pn_xflags |= pn2->pn_xflags;
8126             }
8127         } else {
8128             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
8129                       tt == TOK_XMLPI);
8130             pn2 = xmlAtomNode();
8131         }
8132         if (!pn2)
8133             return JS_FALSE;
8134         pn->pn_pos.end = pn2->pn_pos.end;
8135         pn->append(pn2);
8136     }
8137     tokenStream.setXMLTagMode(true);
8138
8139     JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
8140     return JS_TRUE;
8141 }
8142
8143 /*
8144  * Return a PN_LIST node containing an XML or XMLList Initialiser.
8145  */
8146 JSParseNode *
8147 Parser::xmlElementOrList(JSBool allowList)
8148 {
8149     JSParseNode *pn, *pn2, *list;
8150     TokenKind tt;
8151     JSAtom *startAtom, *endAtom;
8152
8153     JS_CHECK_RECURSION(context, return NULL);
8154
8155     JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
8156     pn = ListNode::create(tc);
8157     if (!pn)
8158         return NULL;
8159
8160     tokenStream.setXMLTagMode(true);
8161     tt = tokenStream.getToken();
8162     if (tt == TOK_ERROR)
8163         return NULL;
8164
8165     if (tt == TOK_XMLNAME || tt == TOK_LC) {
8166         /*
8167          * XMLElement.  Append the tag and its contents, if any, to pn.
8168          */
8169         pn2 = xmlTagContent(TOK_XMLSTAGO, &startAtom);
8170         if (!pn2)
8171             return NULL;
8172         tokenStream.matchToken(TOK_XMLSPACE);
8173
8174         tt = tokenStream.getToken();
8175         if (tt == TOK_XMLPTAGC) {
8176             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
8177             if (pn2->pn_type == TOK_XMLSTAGO) {
8178                 pn->makeEmpty();
8179                 RecycleTree(pn, tc);
8180                 pn = pn2;
8181             } else {
8182                 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
8183                           pn2->pn_type == TOK_LC);
8184                 pn->initList(pn2);
8185                 if (!XML_FOLDABLE(pn2))
8186                     pn->pn_xflags |= PNX_CANTFOLD;
8187             }
8188             pn->pn_type = TOK_XMLPTAGC;
8189             pn->pn_xflags |= PNX_XMLROOT;
8190         } else {
8191             /* We had better have a tag-close (>) at this point. */
8192             if (tt != TOK_XMLTAGC) {
8193                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8194                 return NULL;
8195             }
8196             pn2->pn_pos.end = tokenStream.currentToken().pos.end;
8197
8198             /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
8199             if (pn2->pn_type != TOK_XMLSTAGO) {
8200                 pn->initList(pn2);
8201                 if (!XML_FOLDABLE(pn2))
8202                     pn->pn_xflags |= PNX_CANTFOLD;
8203                 pn2 = pn;
8204                 pn = ListNode::create(tc);
8205                 if (!pn)
8206                     return NULL;
8207             }
8208
8209             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
8210             pn->pn_type = TOK_XMLELEM;
8211             pn->pn_pos.begin = pn2->pn_pos.begin;
8212             pn->initList(pn2);
8213             if (!XML_FOLDABLE(pn2))
8214                 pn->pn_xflags |= PNX_CANTFOLD;
8215             pn->pn_xflags |= PNX_XMLROOT;
8216
8217             /* Get element contents and delimiting end-tag-open sequence. */
8218             if (!xmlElementContent(pn))
8219                 return NULL;
8220
8221             tt = tokenStream.getToken();
8222             XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
8223             if (tt != TOK_XMLNAME && tt != TOK_LC) {
8224                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8225                 return NULL;
8226             }
8227
8228             /* Parse end tag; check mismatch at compile-time if we can. */
8229             pn2 = xmlTagContent(TOK_XMLETAGO, &endAtom);
8230             if (!pn2)
8231                 return NULL;
8232             if (pn2->pn_type == TOK_XMLETAGO) {
8233                 /* Oops, end tag has attributes! */
8234                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8235                 return NULL;
8236             }
8237             if (endAtom && startAtom && endAtom != startAtom) {
8238                 /* End vs. start tag name mismatch: point to the tag name. */
8239                 reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
8240                                   startAtom->chars());
8241                 return NULL;
8242             }
8243
8244             /* Make a TOK_XMLETAGO list with pn2 as its single child. */
8245             JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
8246             list = ListNode::create(tc);
8247             if (!list)
8248                 return NULL;
8249             list->pn_type = TOK_XMLETAGO;
8250             list->initList(pn2);
8251             pn->append(list);
8252             if (!XML_FOLDABLE(pn2)) {
8253                 list->pn_xflags |= PNX_CANTFOLD;
8254                 pn->pn_xflags |= PNX_CANTFOLD;
8255             }
8256
8257             tokenStream.matchToken(TOK_XMLSPACE);
8258             MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
8259         }
8260
8261         /* Set pn_op now that pn has been updated to its final value. */
8262         pn->pn_op = JSOP_TOXML;
8263     } else if (allowList && tt == TOK_XMLTAGC) {
8264         /* XMLList Initialiser. */
8265         pn->pn_type = TOK_XMLLIST;
8266         pn->pn_op = JSOP_TOXMLLIST;
8267         pn->makeEmpty();
8268         pn->pn_xflags |= PNX_XMLROOT;
8269         if (!xmlElementContent(pn))
8270             return NULL;
8271
8272         MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
8273     } else {
8274         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX);
8275         return NULL;
8276     }
8277     tokenStream.setXMLTagMode(false);
8278
8279     pn->pn_pos.end = tokenStream.currentToken().pos.end;
8280     return pn;
8281 }
8282
8283 JSParseNode *
8284 Parser::xmlElementOrListRoot(JSBool allowList)
8285 {
8286     /*
8287      * Force XML support to be enabled so that comments and CDATA literals
8288      * are recognized, instead of <! followed by -- starting an HTML comment
8289      * to end of line (used in script tags to hide content from old browsers
8290      * that don't recognize <script>).
8291      */
8292     bool hadXML = tokenStream.hasXML();
8293     tokenStream.setXML(true);
8294     JSParseNode *pn = xmlElementOrList(allowList);
8295     tokenStream.setXML(hadXML);
8296     return pn;
8297 }
8298
8299 JSParseNode *
8300 Parser::parseXMLText(JSObject *chain, bool allowList)
8301 {
8302     /*
8303      * Push a compiler frame if we have no frames, or if the top frame is a
8304      * lightweight function activation, or if its scope chain doesn't match
8305      * the one passed to us.
8306      */
8307     JSTreeContext xmltc(this);
8308     xmltc.setScopeChain(chain);
8309
8310     /* Set XML-only mode to turn off special treatment of {expr} in XML. */
8311     tokenStream.setXMLOnlyMode();
8312     TokenKind tt = tokenStream.getToken(TSF_OPERAND);
8313
8314     JSParseNode *pn;
8315     if (tt != TOK_XMLSTAGO) {
8316         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
8317         pn = NULL;
8318     } else {
8319         pn = xmlElementOrListRoot(allowList);
8320     }
8321     tokenStream.setXMLOnlyMode(false);
8322
8323     return pn;
8324 }
8325
8326 #endif /* JS_HAS_XMLSUPPORT */
8327
8328 #if JS_HAS_BLOCK_SCOPE
8329 /*
8330  * Check whether blockid is an active scoping statement in tc. This code is
8331  * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
8332  * (below) where the hits come from Scheme-ish let bindings in for loop heads
8333  * and let blocks and expressions (not let declarations).
8334  *
8335  * Unlike let declarations ("let as the new var"), which is a kind of letrec
8336  * due to hoisting, let in a for loop head, let block, or let expression acts
8337  * like Scheme's let: initializers are evaluated without the new let bindings
8338  * being in scope.
8339  *
8340  * Name binding analysis is eager with fixups, rather than multi-pass, and let
8341  * bindings push on the front of the tc->decls JSAtomList (either the singular
8342  * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
8343  * scope bindings of the same name.
8344  *
8345  * This simplifies binding lookup code at the price of a linear search here,
8346  * but only if code uses let (var predominates), and even then this function's
8347  * loop iterates more than once only in crazy cases.
8348  */
8349 static inline bool
8350 BlockIdInScope(uintN blockid, JSTreeContext *tc)
8351 {
8352     if (blockid > tc->blockid())
8353         return false;
8354     for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
8355         if (stmt->blockid == blockid)
8356             return true;
8357     }
8358     return false;
8359 }
8360 #endif
8361
8362 bool
8363 JSParseNode::isConstant()
8364 {
8365     switch (pn_type) {
8366       case TOK_NUMBER:
8367       case TOK_STRING:
8368         return true;
8369       case TOK_PRIMARY:
8370         switch (pn_op) {
8371           case JSOP_NULL:
8372           case JSOP_FALSE:
8373           case JSOP_TRUE:
8374             return true;
8375           default:
8376             return false;
8377         }
8378       case TOK_RB:
8379       case TOK_RC:
8380         return (pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST);
8381       default:
8382         return false;
8383     }
8384 }
8385
8386 JSParseNode *
8387 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
8388 {
8389     JSParseNode *pn, *pn2, *pn3;
8390     JSOp op;
8391
8392     JS_CHECK_RECURSION(context, return NULL);
8393
8394     switch (tt) {
8395       case TOK_FUNCTION:
8396 #if JS_HAS_XML_SUPPORT
8397         if (tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
8398             pn2 = NullaryNode::create(tc);
8399             if (!pn2)
8400                 return NULL;
8401             pn2->pn_type = TOK_FUNCTION;
8402             pn = qualifiedSuffix(pn2);
8403             if (!pn)
8404                 return NULL;
8405             break;
8406         }
8407 #endif
8408         pn = functionExpr();
8409         if (!pn)
8410             return NULL;
8411         break;
8412
8413       case TOK_LB:
8414       {
8415         JSBool matched;
8416         jsuint index;
8417
8418         pn = ListNode::create(tc);
8419         if (!pn)
8420             return NULL;
8421         pn->pn_type = TOK_RB;
8422         pn->pn_op = JSOP_NEWINIT;
8423         pn->makeEmpty();
8424
8425 #if JS_HAS_GENERATORS
8426         pn->pn_blockid = tc->blockidGen;
8427 #endif
8428
8429         matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
8430         if (!matched) {
8431             for (index = 0; ; index++) {
8432                 if (index == JS_ARGS_LENGTH_MAX) {
8433                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
8434                     return NULL;
8435                 }
8436
8437                 tt = tokenStream.peekToken(TSF_OPERAND);
8438                 if (tt == TOK_RB) {
8439                     pn->pn_xflags |= PNX_ENDCOMMA;
8440                     break;
8441                 }
8442
8443                 if (tt == TOK_COMMA) {
8444                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
8445                     tokenStream.matchToken(TOK_COMMA);
8446                     pn2 = NullaryNode::create(tc);
8447                     pn->pn_xflags |= PNX_HOLEY | PNX_NONCONST;
8448                 } else {
8449                     pn2 = assignExpr();
8450                     if (pn2 && !pn2->isConstant())
8451                         pn->pn_xflags |= PNX_NONCONST;
8452                 }
8453                 if (!pn2)
8454                     return NULL;
8455                 pn->append(pn2);
8456
8457                 if (tt != TOK_COMMA) {
8458                     /* If we didn't already match TOK_COMMA in above case. */
8459                     if (!tokenStream.matchToken(TOK_COMMA))
8460                         break;
8461                 }
8462             }
8463
8464 #if JS_HAS_GENERATORS
8465             /*
8466              * At this point, (index == 0 && pn->pn_count != 0) implies one
8467              * element initialiser was parsed.
8468              *
8469              * An array comprehension of the form:
8470              *
8471              *   [i * j for (i in o) for (j in p) if (i != j)]
8472              *
8473              * translates to roughly the following let expression:
8474              *
8475              *   let (array = new Array, i, j) {
8476              *     for (i in o) let {
8477              *       for (j in p)
8478              *         if (i != j)
8479              *           array.push(i * j)
8480              *     }
8481              *     array
8482              *   }
8483              *
8484              * where array is a nameless block-local variable.  The "roughly"
8485              * means that an implementation may optimize away the array.push.
8486              * An array comprehension opens exactly one block scope, no matter
8487              * how many for heads it contains.
8488              *
8489              * Each let () {...} or for (let ...) ... compiles to:
8490              *
8491              *   JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
8492              *
8493              * where <o> is a literal object representing the block scope,
8494              * with <n> properties, naming each var declared in the block.
8495              *
8496              * Each var declaration in a let-block binds a name in <o> at
8497              * compile time, and allocates a slot on the operand stack at
8498              * runtime via JSOP_ENTERBLOCK.  A block-local var is accessed
8499              * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
8500              * JSOP_FORLOCAL.  These ops all have an immediate operand, the
8501              * local slot's stack index from fp->spbase.
8502              *
8503              * The array comprehension iteration step, array.push(i * j) in
8504              * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
8505              * where <array> is the index of array's stack slot.
8506              */
8507             if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
8508                 JSParseNode *pnexp, *pntop;
8509
8510                 /* Relabel pn as an array comprehension node. */
8511                 pn->pn_type = TOK_ARRAYCOMP;
8512
8513                 /*
8514                  * Remove the comprehension expression from pn's linked list
8515                  * and save it via pnexp.  We'll re-install it underneath the
8516                  * ARRAYPUSH node after we parse the rest of the comprehension.
8517                  */
8518                 pnexp = pn->last();
8519                 JS_ASSERT(pn->pn_count == 1);
8520                 pn->pn_count = 0;
8521                 pn->pn_tail = &pn->pn_head;
8522                 *pn->pn_tail = NULL;
8523
8524                 pntop = comprehensionTail(pnexp, pn->pn_blockid,
8525                                           TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
8526                 if (!pntop)
8527                     return NULL;
8528                 pn->append(pntop);
8529             }
8530 #endif /* JS_HAS_GENERATORS */
8531
8532             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
8533         }
8534         pn->pn_pos.end = tokenStream.currentToken().pos.end;
8535         return pn;
8536       }
8537
8538       case TOK_LC:
8539       {
8540         JSBool afterComma;
8541         JSParseNode *pnval;
8542
8543         /*
8544          * A map from property names we've seen thus far to a mask of property
8545          * assignment types, stored and retrieved with ALE_SET_INDEX/ALE_INDEX.
8546          */
8547         JSAutoAtomList seen(tc->parser);
8548         enum AssignmentType {
8549             GET     = 0x1,
8550             SET     = 0x2,
8551             VALUE   = 0x4 | GET | SET
8552         };
8553
8554         pn = ListNode::create(tc);
8555         if (!pn)
8556             return NULL;
8557         pn->pn_type = TOK_RC;
8558         pn->pn_op = JSOP_NEWINIT;
8559         pn->makeEmpty();
8560
8561         afterComma = JS_FALSE;
8562         for (;;) {
8563             JSAtom *atom;
8564             tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
8565             switch (tt) {
8566               case TOK_NUMBER:
8567                 pn3 = NullaryNode::create(tc);
8568                 if (!pn3)
8569                     return NULL;
8570                 pn3->pn_dval = tokenStream.currentToken().t_dval;
8571                 if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
8572                     return NULL;
8573                 break;
8574               case TOK_NAME:
8575                 {
8576                     atom = tokenStream.currentToken().t_atom;
8577                     if (atom == context->runtime->atomState.getAtom)
8578                         op = JSOP_GETTER;
8579                     else if (atom == context->runtime->atomState.setAtom)
8580                         op = JSOP_SETTER;
8581                     else
8582                         goto property_name;
8583
8584                     tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
8585                     if (tt == TOK_NAME || tt == TOK_STRING) {
8586                         atom = tokenStream.currentToken().t_atom;
8587                         pn3 = NameNode::create(atom, tc);
8588                         if (!pn3)
8589                             return NULL;
8590                     } else if (tt == TOK_NUMBER) {
8591                         pn3 = NullaryNode::create(tc);
8592                         if (!pn3)
8593                             return NULL;
8594                         pn3->pn_dval = tokenStream.currentToken().t_dval;
8595                         if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
8596                             return NULL;
8597                     } else {
8598                         tokenStream.ungetToken();
8599                         goto property_name;
8600                     }
8601
8602                     pn->pn_xflags |= PNX_NONCONST;
8603
8604                     /* NB: Getter function in { get x(){} } is unnamed. */
8605                     pn2 = functionDef(NULL, op == JSOP_SETTER ? SETTER : GETTER, JSFUN_LAMBDA);
8606                     pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
8607                     goto skip;
8608                 }
8609               property_name:
8610               case TOK_STRING:
8611                 atom = tokenStream.currentToken().t_atom;
8612                 pn3 = NullaryNode::create(tc);
8613                 if (!pn3)
8614                     return NULL;
8615                 pn3->pn_atom = atom;
8616                 break;
8617               case TOK_RC:
8618                 goto end_obj_init;
8619               default:
8620                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_PROP_ID);
8621                 return NULL;
8622             }
8623
8624             op = JSOP_INITPROP;
8625             tt = tokenStream.getToken();
8626             if (tt == TOK_COLON) {
8627                 pnval = assignExpr();
8628                 if (pnval && !pnval->isConstant())
8629                     pn->pn_xflags |= PNX_NONCONST;
8630             } else {
8631 #if JS_HAS_DESTRUCTURING_SHORTHAND
8632                 if (tt != TOK_COMMA && tt != TOK_RC) {
8633 #endif
8634                     reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_COLON_AFTER_ID);
8635                     return NULL;
8636 #if JS_HAS_DESTRUCTURING_SHORTHAND
8637                 }
8638
8639                 /*
8640                  * Support, e.g., |var {x, y} = o| as destructuring shorthand
8641                  * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8642                  */
8643                 tokenStream.ungetToken();
8644                 pn->pn_xflags |= PNX_DESTRUCT | PNX_NONCONST;
8645                 pnval = pn3;
8646                 if (pnval->pn_type == TOK_NAME) {
8647                     pnval->pn_arity = PN_NAME;
8648                     ((NameNode *)pnval)->initCommon(tc);
8649                 }
8650 #endif
8651             }
8652
8653             pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
8654           skip:
8655             if (!pn2)
8656                 return NULL;
8657             pn->append(pn2);
8658
8659             /*
8660              * Check for duplicate property names.  Duplicate data properties
8661              * only conflict in strict mode.  Duplicate getter or duplicate
8662              * setter halves always conflict.  A data property conflicts with
8663              * any part of an accessor property.
8664              */
8665             AssignmentType assignType;
8666             if (op == JSOP_INITPROP) {
8667                 assignType = VALUE;
8668             } else if (op == JSOP_GETTER) {
8669                 assignType = GET;
8670             } else if (op == JSOP_SETTER) {
8671                 assignType = SET;
8672             } else {
8673                 JS_NOT_REACHED("bad opcode in object initializer");
8674                 assignType = VALUE; /* try to error early */
8675             }
8676
8677             if (JSAtomListElement *ale = seen.lookup(atom)) {
8678                 AssignmentType oldAssignType = AssignmentType(ALE_INDEX(ale));
8679                 if ((oldAssignType & assignType) &&
8680                     (oldAssignType != VALUE || assignType != VALUE || tc->needStrictChecks()))
8681                 {
8682                     JSAutoByteString name;
8683                     if (!js_AtomToPrintableString(context, atom, &name))
8684                         return NULL;
8685
8686                     uintN flags = (oldAssignType == VALUE &&
8687                                    assignType == VALUE &&
8688                                    !tc->inStrictMode())
8689                                   ? JSREPORT_WARNING
8690                                   : JSREPORT_ERROR;
8691                     if (!ReportCompileErrorNumber(context, &tokenStream, NULL, flags,
8692                                                   JSMSG_DUPLICATE_PROPERTY, name.ptr()))
8693                     {
8694                         return NULL;
8695                     }
8696                 }
8697                 ALE_SET_INDEX(ale, assignType | oldAssignType);
8698             } else {
8699                 ale = seen.add(tc->parser, atom);
8700                 if (!ale)
8701                     return NULL;
8702                 ALE_SET_INDEX(ale, assignType);
8703             }
8704
8705             tt = tokenStream.getToken();
8706             if (tt == TOK_RC)
8707                 goto end_obj_init;
8708             if (tt != TOK_COMMA) {
8709                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
8710                 return NULL;
8711             }
8712             afterComma = JS_TRUE;
8713         }
8714
8715       end_obj_init:
8716         pn->pn_pos.end = tokenStream.currentToken().pos.end;
8717         return pn;
8718       }
8719
8720 #if JS_HAS_BLOCK_SCOPE
8721       case TOK_LET:
8722         pn = letBlock(JS_FALSE);
8723         if (!pn)
8724             return NULL;
8725         break;
8726 #endif
8727
8728 #if JS_HAS_SHARP_VARS
8729       case TOK_DEFSHARP:
8730         pn = UnaryNode::create(tc);
8731         if (!pn)
8732             return NULL;
8733         pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8734         tt = tokenStream.getToken(TSF_OPERAND);
8735         pn->pn_kid = primaryExpr(tt, JS_FALSE);
8736         if (!pn->pn_kid)
8737             return NULL;
8738         if (PN_TYPE(pn->pn_kid) == TOK_USESHARP ||
8739             PN_TYPE(pn->pn_kid) == TOK_DEFSHARP ||
8740             PN_TYPE(pn->pn_kid) == TOK_STRING ||
8741             PN_TYPE(pn->pn_kid) == TOK_NUMBER ||
8742             PN_TYPE(pn->pn_kid) == TOK_PRIMARY) {
8743             reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
8744             return NULL;
8745         }
8746         if (!tc->ensureSharpSlots())
8747             return NULL;
8748         break;
8749
8750       case TOK_USESHARP:
8751         /* Check for forward/dangling references at runtime, to allow eval. */
8752         pn = NullaryNode::create(tc);
8753         if (!pn)
8754             return NULL;
8755         if (!tc->ensureSharpSlots())
8756             return NULL;
8757         pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8758         break;
8759 #endif /* JS_HAS_SHARP_VARS */
8760
8761       case TOK_LP:
8762       {
8763         JSBool genexp;
8764
8765         pn = parenExpr(&genexp);
8766         if (!pn)
8767             return NULL;
8768         pn->pn_parens = true;
8769         if (!genexp)
8770             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8771         break;
8772       }
8773
8774 #if JS_HAS_XML_SUPPORT
8775       case TOK_STAR:
8776         pn = qualifiedIdentifier();
8777         if (!pn)
8778             return NULL;
8779         break;
8780
8781       case TOK_AT:
8782         pn = attributeIdentifier();
8783         if (!pn)
8784             return NULL;
8785         break;
8786
8787       case TOK_XMLSTAGO:
8788         pn = xmlElementOrListRoot(JS_TRUE);
8789         if (!pn)
8790             return NULL;
8791         break;
8792 #endif /* JS_HAS_XML_SUPPORT */
8793
8794       case TOK_STRING:
8795 #if JS_HAS_SHARP_VARS
8796         /* FALL THROUGH */
8797 #endif
8798
8799 #if JS_HAS_XML_SUPPORT
8800       case TOK_XMLCDATA:
8801       case TOK_XMLCOMMENT:
8802       case TOK_XMLPI:
8803 #endif
8804         pn = NullaryNode::create(tc);
8805         if (!pn)
8806             return NULL;
8807         pn->pn_atom = tokenStream.currentToken().t_atom;
8808 #if JS_HAS_XML_SUPPORT
8809         if (tt == TOK_XMLPI)
8810             pn->pn_atom2 = tokenStream.currentToken().t_atom2;
8811         else
8812 #endif
8813             pn->pn_op = tokenStream.currentToken().t_op;
8814         break;
8815
8816       case TOK_NAME:
8817         pn = NameNode::create(tokenStream.currentToken().t_atom, tc);
8818         if (!pn)
8819             return NULL;
8820         JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
8821         pn->pn_op = JSOP_NAME;
8822
8823         if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8824             pn->pn_atom == context->runtime->atomState.argumentsAtom) {
8825             /*
8826              * Flag arguments usage so we can avoid unsafe optimizations such
8827              * as formal parameter assignment analysis (because of the hated
8828              * feature whereby arguments alias formals). We do this even for
8829              * a reference of the form foo.arguments, which ancient code may
8830              * still use instead of arguments (more hate).
8831              */
8832             tc->noteArgumentsUse();
8833
8834             /*
8835              * Bind early to JSOP_ARGUMENTS to relieve later code from having
8836              * to do this work (new rule for the emitter to count on).
8837              */
8838             if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8839                 pn->pn_op = JSOP_ARGUMENTS;
8840                 pn->pn_dflags |= PND_BOUND;
8841             }
8842         } else if ((!afterDot
8843 #if JS_HAS_XML_SUPPORT
8844                     || tokenStream.peekToken() == TOK_DBLCOLON
8845 #endif
8846                    ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8847             JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8848
8849             JSDefinition *dn;
8850
8851             JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8852             if (ale) {
8853                 dn = ALE_DEFN(ale);
8854 #if JS_HAS_BLOCK_SCOPE
8855                 /*
8856                  * Skip out-of-scope let bindings along an ALE list or hash
8857                  * chain. These can happen due to |let (x = x) x| block and
8858                  * expression bindings, where the x on the right of = comes
8859                  * from an outer scope. See bug 496532.
8860                  */
8861                 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8862                     do {
8863                         ale = ALE_NEXT(ale);
8864                     } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8865                     if (!ale)
8866                         break;
8867                     dn = ALE_DEFN(ale);
8868                 }
8869 #endif
8870             }
8871
8872             if (ale) {
8873                 dn = ALE_DEFN(ale);
8874             } else {
8875                 ale = tc->lexdeps.lookup(pn->pn_atom);
8876                 if (ale) {
8877                     dn = ALE_DEFN(ale);
8878                 } else {
8879                     /*
8880                      * No definition before this use in any lexical scope.
8881                      * Add a mapping in tc->lexdeps from pn->pn_atom to a
8882                      * new node for the forward-referenced definition. This
8883                      * placeholder definition node will be adopted when we
8884                      * parse the real defining declaration form, or left as
8885                      * a free variable definition if we never see the real
8886                      * definition.
8887                      */
8888                     ale = MakePlaceholder(pn, tc);
8889                     if (!ale)
8890                         return NULL;
8891                     dn = ALE_DEFN(ale);
8892
8893                     /*
8894                      * In case this is a forward reference to a function,
8895                      * we pessimistically set PND_FUNARG if the next token
8896                      * is not a left parenthesis.
8897                      *
8898                      * If the definition eventually parsed into dn is not a
8899                      * function, this flag won't hurt, and if we do parse a
8900                      * function with pn's name, then the PND_FUNARG flag is
8901                      * necessary for safe context->display-based optimiza-
8902                      * tion of the closure's static link.
8903                      */
8904                     if (tokenStream.peekToken() != TOK_LP)
8905                         dn->pn_dflags |= PND_FUNARG;
8906                 }
8907             }
8908
8909             JS_ASSERT(dn->pn_defn);
8910             LinkUseToDef(pn, dn, tc);
8911
8912             /* Here we handle the backward function reference case. */
8913             if (tokenStream.peekToken() != TOK_LP)
8914                 dn->pn_dflags |= PND_FUNARG;
8915
8916             pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8917             if (stmt && stmt->type == STMT_WITH)
8918                 pn->pn_dflags |= PND_DEOPTIMIZED;
8919         }
8920
8921 #if JS_HAS_XML_SUPPORT
8922         if (tokenStream.matchToken(TOK_DBLCOLON)) {
8923             if (afterDot) {
8924                 /*
8925                  * Here primaryExpr is called after . or .. followed by a name
8926                  * followed by ::. This is the only case where a keyword after
8927                  * . or .. is not treated as a property name.
8928                  */
8929                 const KeywordInfo *ki = FindKeyword(pn->pn_atom->charsZ(), pn->pn_atom->length());
8930                 if (ki) {
8931                     if (ki->tokentype != TOK_FUNCTION) {
8932                         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS);
8933                         return NULL;
8934                     }
8935
8936                     pn->pn_arity = PN_NULLARY;
8937                     pn->pn_type = TOK_FUNCTION;
8938                 }
8939             }
8940             pn = qualifiedSuffix(pn);
8941             if (!pn)
8942                 return NULL;
8943         }
8944 #endif
8945         break;
8946
8947       case TOK_REGEXP:
8948       {
8949         pn = NullaryNode::create(tc);
8950         if (!pn)
8951             return NULL;
8952
8953         JSObject *obj;
8954         if (context->hasfp()) {
8955             obj = RegExp::createObject(context, context->regExpStatics(),
8956                                        tokenStream.getTokenbuf().begin(),
8957                                        tokenStream.getTokenbuf().length(),
8958                                        tokenStream.currentToken().t_reflags);
8959         } else {
8960             obj = RegExp::createObjectNoStatics(context,
8961                                                 tokenStream.getTokenbuf().begin(),
8962                                                 tokenStream.getTokenbuf().length(),
8963                                                 tokenStream.currentToken().t_reflags);
8964         }
8965
8966         if (!obj)
8967             return NULL;
8968         if (!tc->compileAndGo()) {
8969             obj->clearParent();
8970             obj->clearProto();
8971         }
8972
8973         pn->pn_objbox = tc->parser->newObjectBox(obj);
8974         if (!pn->pn_objbox)
8975             return NULL;
8976
8977         pn->pn_op = JSOP_REGEXP;
8978         break;
8979       }
8980
8981       case TOK_NUMBER:
8982         pn = NullaryNode::create(tc);
8983         if (!pn)
8984             return NULL;
8985         pn->pn_op = JSOP_DOUBLE;
8986         pn->pn_dval = tokenStream.currentToken().t_dval;
8987         break;
8988
8989       case TOK_PRIMARY:
8990         pn = NullaryNode::create(tc);
8991         if (!pn)
8992             return NULL;
8993         pn->pn_op = tokenStream.currentToken().t_op;
8994         break;
8995
8996       case TOK_ERROR:
8997         /* The scanner or one of its subroutines reported the error. */
8998         return NULL;
8999
9000       default:
9001         reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
9002         return NULL;
9003     }
9004     return pn;
9005 }
9006
9007 JSParseNode *
9008 Parser::parenExpr(JSBool *genexp)
9009 {
9010     TokenPtr begin;
9011     JSParseNode *pn;
9012
9013     JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
9014     begin = tokenStream.currentToken().pos.begin;
9015
9016     if (genexp)
9017         *genexp = JS_FALSE;
9018     pn = bracketedExpr();
9019     if (!pn)
9020         return NULL;
9021
9022 #if JS_HAS_GENERATOR_EXPRS
9023     if (tokenStream.matchToken(TOK_FOR)) {
9024         if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
9025             reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
9026             return NULL;
9027         }
9028         if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
9029             reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
9030                               js_generator_str);
9031             return NULL;
9032         }
9033         pn = generatorExpr(pn);
9034         if (!pn)
9035             return NULL;
9036         pn->pn_pos.begin = begin;
9037         if (genexp) {
9038             if (tokenStream.getToken() != TOK_RP) {
9039                 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
9040                                   js_generator_str);
9041                 return NULL;
9042             }
9043             pn->pn_pos.end = tokenStream.currentToken().pos.end;
9044             *genexp = JS_TRUE;
9045         }
9046     }
9047 #endif /* JS_HAS_GENERATOR_EXPRS */
9048
9049     return pn;
9050 }
9051
9052 /*
9053  * Fold from one constant type to another.
9054  * XXX handles only strings and numbers for now
9055  */
9056 static JSBool
9057 FoldType(JSContext *cx, JSParseNode *pn, TokenKind type)
9058 {
9059     if (PN_TYPE(pn) != type) {
9060         switch (type) {
9061           case TOK_NUMBER:
9062             if (pn->pn_type == TOK_STRING) {
9063                 jsdouble d;
9064                 if (!ValueToNumber(cx, StringValue(ATOM_TO_STRING(pn->pn_atom)), &d))
9065                     return JS_FALSE;
9066                 pn->pn_dval = d;
9067                 pn->pn_type = TOK_NUMBER;
9068                 pn->pn_op = JSOP_DOUBLE;
9069             }
9070             break;
9071
9072           case TOK_STRING:
9073             if (pn->pn_type == TOK_NUMBER) {
9074                 JSString *str = js_NumberToString(cx, pn->pn_dval);
9075                 if (!str)
9076                     return JS_FALSE;
9077                 pn->pn_atom = js_AtomizeString(cx, str, 0);
9078                 if (!pn->pn_atom)
9079                     return JS_FALSE;
9080                 pn->pn_type = TOK_STRING;
9081                 pn->pn_op = JSOP_STRING;
9082             }
9083             break;
9084
9085           default:;
9086         }
9087     }
9088     return JS_TRUE;
9089 }
9090
9091 /*
9092  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
9093  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
9094  * a successful call to this function.
9095  */
9096 static JSBool
9097 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
9098                   JSParseNode *pn, JSTreeContext *tc)
9099 {
9100     jsdouble d, d2;
9101     int32 i, j;
9102
9103     JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
9104     d = pn1->pn_dval;
9105     d2 = pn2->pn_dval;
9106     switch (op) {
9107       case JSOP_LSH:
9108       case JSOP_RSH:
9109         i = js_DoubleToECMAInt32(d);
9110         j = js_DoubleToECMAInt32(d2);
9111         j &= 31;
9112         d = (op == JSOP_LSH) ? i << j : i >> j;
9113         break;
9114
9115       case JSOP_URSH:
9116         j = js_DoubleToECMAInt32(d2);
9117         j &= 31;
9118         d = js_DoubleToECMAUint32(d) >> j;
9119         break;
9120
9121       case JSOP_ADD:
9122         d += d2;
9123         break;
9124
9125       case JSOP_SUB:
9126         d -= d2;
9127         break;
9128
9129       case JSOP_MUL:
9130         d *= d2;
9131         break;
9132
9133       case JSOP_DIV:
9134         if (d2 == 0) {
9135 #if defined(XP_WIN)
9136             /* XXX MSVC miscompiles such that (NaN == 0) */
9137             if (JSDOUBLE_IS_NaN(d2))
9138                 d = js_NaN;
9139             else
9140 #endif
9141             if (d == 0 || JSDOUBLE_IS_NaN(d))
9142                 d = js_NaN;
9143             else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
9144                 d = js_NegativeInfinity;
9145             else
9146                 d = js_PositiveInfinity;
9147         } else {
9148             d /= d2;
9149         }
9150         break;
9151
9152       case JSOP_MOD:
9153         if (d2 == 0) {
9154             d = js_NaN;
9155         } else {
9156             d = js_fmod(d, d2);
9157         }
9158         break;
9159
9160       default:;
9161     }
9162
9163     /* Take care to allow pn1 or pn2 to alias pn. */
9164     if (pn1 != pn)
9165         RecycleTree(pn1, tc);
9166     if (pn2 != pn)
9167         RecycleTree(pn2, tc);
9168     pn->pn_type = TOK_NUMBER;
9169     pn->pn_op = JSOP_DOUBLE;
9170     pn->pn_arity = PN_NULLARY;
9171     pn->pn_dval = d;
9172     return JS_TRUE;
9173 }
9174
9175 #if JS_HAS_XML_SUPPORT
9176
9177 static JSBool
9178 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
9179 {
9180     TokenKind tt;
9181     JSParseNode **pnp, *pn1, *pn2;
9182     JSString *accum, *str;
9183     uint32 i, j;
9184
9185     JS_ASSERT(pn->pn_arity == PN_LIST);
9186     tt = PN_TYPE(pn);
9187     pnp = &pn->pn_head;
9188     pn1 = *pnp;
9189     accum = NULL;
9190     str = NULL;
9191     if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
9192         if (tt == TOK_XMLETAGO)
9193             accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
9194         else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
9195             accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
9196     }
9197
9198     /*
9199      * GC Rooting here is tricky: for most of the loop, |accum| is safe via
9200      * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
9201      * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
9202      * Therefore, we have to add additonal protection from GC nesting under
9203      * js_ConcatStrings.
9204      */
9205     for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
9206         /* The parser already rejected end-tags with attributes. */
9207         JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
9208         switch (pn2->pn_type) {
9209           case TOK_XMLATTR:
9210             if (!accum)
9211                 goto cantfold;
9212             /* FALL THROUGH */
9213           case TOK_XMLNAME:
9214           case TOK_XMLSPACE:
9215           case TOK_XMLTEXT:
9216           case TOK_STRING:
9217             if (pn2->pn_arity == PN_LIST)
9218                 goto cantfold;
9219             str = ATOM_TO_STRING(pn2->pn_atom);
9220             break;
9221
9222           case TOK_XMLCDATA:
9223             str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
9224             if (!str)
9225                 return JS_FALSE;
9226             break;
9227
9228           case TOK_XMLCOMMENT:
9229             str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
9230             if (!str)
9231                 return JS_FALSE;
9232             break;
9233
9234           case TOK_XMLPI:
9235             str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
9236                                          ATOM_TO_STRING(pn2->pn_atom2));
9237             if (!str)
9238                 return JS_FALSE;
9239             break;
9240
9241           cantfold:
9242           default:
9243             JS_ASSERT(*pnp == pn1);
9244             if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
9245                 (i & 1) ^ (j & 1)) {
9246 #ifdef DEBUG_brendanXXX
9247                 printf("1: %d, %d => ", i, j);
9248                 if (accum)
9249                     FileEscapedString(stdout, accum, 0);
9250                 else
9251                     fputs("NULL", stdout);
9252                 fputc('\n', stdout);
9253 #endif
9254             } else if (accum && pn1 != pn2) {
9255                 while (pn1->pn_next != pn2) {
9256                     pn1 = RecycleTree(pn1, tc);
9257                     --pn->pn_count;
9258                 }
9259                 pn1->pn_type = TOK_XMLTEXT;
9260                 pn1->pn_op = JSOP_STRING;
9261                 pn1->pn_arity = PN_NULLARY;
9262                 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
9263                 if (!pn1->pn_atom)
9264                     return JS_FALSE;
9265                 JS_ASSERT(pnp != &pn1->pn_next);
9266                 *pnp = pn1;
9267             }
9268             pnp = &pn2->pn_next;
9269             pn1 = *pnp;
9270             accum = NULL;
9271             continue;
9272         }
9273
9274         if (accum) {
9275             {
9276                 AutoStringRooter tvr(cx, accum);
9277                 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
9278                       ? js_AddAttributePart(cx, i & 1, accum, str)
9279                       : js_ConcatStrings(cx, accum, str);
9280             }
9281             if (!str)
9282                 return JS_FALSE;
9283 #ifdef DEBUG_brendanXXX
9284             printf("2: %d, %d => ", i, j);
9285             FileEscapedString(stdout, str, 0);
9286             printf(" (%u)\n", str->length());
9287 #endif
9288             ++j;
9289         }
9290         accum = str;
9291     }
9292
9293     if (accum) {
9294         str = NULL;
9295         if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
9296             if (tt == TOK_XMLPTAGC)
9297                 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
9298             else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
9299                 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
9300         }
9301         if (str) {
9302             accum = js_ConcatStrings(cx, accum, str);
9303             if (!accum)
9304                 return JS_FALSE;
9305         }
9306
9307         JS_ASSERT(*pnp == pn1);
9308         while (pn1->pn_next) {
9309             pn1 = RecycleTree(pn1, tc);
9310             --pn->pn_count;
9311         }
9312         pn1->pn_type = TOK_XMLTEXT;
9313         pn1->pn_op = JSOP_STRING;
9314         pn1->pn_arity = PN_NULLARY;
9315         pn1->pn_atom = js_AtomizeString(cx, accum, 0);
9316         if (!pn1->pn_atom)
9317             return JS_FALSE;
9318         JS_ASSERT(pnp != &pn1->pn_next);
9319         *pnp = pn1;
9320     }
9321
9322     if (pn1 && pn->pn_count == 1) {
9323         /*
9324          * Only one node under pn, and it has been folded: move pn1 onto pn
9325          * unless pn is an XML root (in which case we need it to tell the code
9326          * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op).  If pn is an
9327          * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
9328          * extra "<" and "/>" bracketing at runtime.
9329          */
9330         if (!(pn->pn_xflags & PNX_XMLROOT)) {
9331             pn->become(pn1);
9332         } else if (tt == TOK_XMLPTAGC) {
9333             pn->pn_type = TOK_XMLELEM;
9334             pn->pn_op = JSOP_TOXML;
9335         }
9336     }
9337     return JS_TRUE;
9338 }
9339
9340 #endif /* JS_HAS_XML_SUPPORT */
9341
9342 static int
9343 Boolish(JSParseNode *pn)
9344 {
9345     switch (pn->pn_op) {
9346       case JSOP_DOUBLE:
9347         return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
9348
9349       case JSOP_STRING:
9350         return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
9351
9352 #if JS_HAS_GENERATOR_EXPRS
9353       case JSOP_CALL:
9354       {
9355         /*
9356          * A generator expression as an if or loop condition has no effects, it
9357          * simply results in a truthy object reference. This condition folding
9358          * is needed for the decompiler. See bug 442342 and bug 443074.
9359          */
9360         if (pn->pn_count != 1)
9361             break;
9362         JSParseNode *pn2 = pn->pn_head;
9363         if (pn2->pn_type != TOK_FUNCTION)
9364             break;
9365         if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
9366             break;
9367         /* FALL THROUGH */
9368       }
9369 #endif
9370
9371       case JSOP_DEFFUN:
9372       case JSOP_LAMBDA:
9373       case JSOP_TRUE:
9374         return 1;
9375
9376       case JSOP_NULL:
9377       case JSOP_FALSE:
9378         return 0;
9379
9380       default:;
9381     }
9382     return -1;
9383 }
9384
9385 JSBool
9386 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
9387 {
9388     JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
9389
9390     JS_CHECK_RECURSION(cx, return JS_FALSE);
9391
9392     switch (pn->pn_arity) {
9393       case PN_FUNC:
9394       {
9395         uint32 oldflags = tc->flags;
9396         JSFunctionBox *oldlist = tc->functionList;
9397
9398         tc->flags = pn->pn_funbox->tcflags;
9399         tc->functionList = pn->pn_funbox->kids;
9400         if (!js_FoldConstants(cx, pn->pn_body, tc))
9401             return JS_FALSE;
9402         pn->pn_funbox->kids = tc->functionList;
9403         tc->flags = oldflags;
9404         tc->functionList = oldlist;
9405         break;
9406       }
9407
9408       case PN_LIST:
9409       {
9410         /* Propagate inCond through logical connectives. */
9411         bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
9412
9413         /* Don't fold a parenthesized call expression. See bug 537673. */
9414         pn1 = pn2 = pn->pn_head;
9415         if ((pn->pn_type == TOK_LP || pn->pn_type == TOK_NEW) && pn2->pn_parens)
9416             pn2 = pn2->pn_next;
9417
9418         /* Save the list head in pn1 for later use. */
9419         for (; pn2; pn2 = pn2->pn_next) {
9420             if (!js_FoldConstants(cx, pn2, tc, cond))
9421                 return JS_FALSE;
9422         }
9423         break;
9424       }
9425
9426       case PN_TERNARY:
9427         /* Any kid may be null (e.g. for (;;)). */
9428         pn1 = pn->pn_kid1;
9429         pn2 = pn->pn_kid2;
9430         pn3 = pn->pn_kid3;
9431         if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
9432             return JS_FALSE;
9433         if (pn2) {
9434             if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
9435                 return JS_FALSE;
9436             if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
9437                 RecycleTree(pn2, tc);
9438                 pn->pn_kid2 = NULL;
9439             }
9440         }
9441         if (pn3 && !js_FoldConstants(cx, pn3, tc))
9442             return JS_FALSE;
9443         break;
9444
9445       case PN_BINARY:
9446         pn1 = pn->pn_left;
9447         pn2 = pn->pn_right;
9448
9449         /* Propagate inCond through logical connectives. */
9450         if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
9451             if (!js_FoldConstants(cx, pn1, tc, inCond))
9452                 return JS_FALSE;
9453             if (!js_FoldConstants(cx, pn2, tc, inCond))
9454                 return JS_FALSE;
9455             break;
9456         }
9457
9458         /* First kid may be null (for default case in switch). */
9459         if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
9460             return JS_FALSE;
9461         if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
9462             return JS_FALSE;
9463         break;
9464
9465       case PN_UNARY:
9466         pn1 = pn->pn_kid;
9467
9468         /*
9469          * Kludge to deal with typeof expressions: because constant folding
9470          * can turn an expression into a name node, we have to check here,
9471          * before folding, to see if we should throw undefined name errors.
9472          *
9473          * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
9474          * null. This assumption does not hold true for other unary
9475          * expressions.
9476          */
9477         if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
9478             pn->pn_op = JSOP_TYPEOFEXPR;
9479
9480         if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
9481             return JS_FALSE;
9482         break;
9483
9484       case PN_NAME:
9485         /*
9486          * Skip pn1 down along a chain of dotted member expressions to avoid
9487          * excessive recursion.  Our only goal here is to fold constants (if
9488          * any) in the primary expression operand to the left of the first
9489          * dot in the chain.
9490          */
9491         if (!pn->pn_used) {
9492             pn1 = pn->pn_expr;
9493             while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
9494                 pn1 = pn1->pn_expr;
9495             if (pn1 && !js_FoldConstants(cx, pn1, tc))
9496                 return JS_FALSE;
9497         }
9498         break;
9499
9500       case PN_NAMESET:
9501         pn1 = pn->pn_tree;
9502         if (!js_FoldConstants(cx, pn1, tc))
9503             return JS_FALSE;
9504         break;
9505
9506       case PN_NULLARY:
9507         break;
9508     }
9509
9510     switch (pn->pn_type) {
9511       case TOK_IF:
9512         if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
9513             break;
9514         /* FALL THROUGH */
9515
9516       case TOK_HOOK:
9517         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
9518         switch (pn1->pn_type) {
9519           case TOK_NUMBER:
9520             if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
9521                 pn2 = pn3;
9522             break;
9523           case TOK_STRING:
9524             if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
9525                 pn2 = pn3;
9526             break;
9527           case TOK_PRIMARY:
9528             if (pn1->pn_op == JSOP_TRUE)
9529                 break;
9530             if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
9531                 pn2 = pn3;
9532                 break;
9533             }
9534             /* FALL THROUGH */
9535           default:
9536             /* Early return to dodge common code that copies pn2 to pn. */
9537             return JS_TRUE;
9538         }
9539
9540 #if JS_HAS_GENERATOR_EXPRS
9541         /* Don't fold a trailing |if (0)| in a generator expression. */
9542         if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
9543             break;
9544 #endif
9545
9546         if (pn2 && !pn2->pn_defn)
9547             pn->become(pn2);
9548         if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
9549             /*
9550              * False condition and no else, or an empty then-statement was
9551              * moved up over pn.  Either way, make pn an empty block (not an
9552              * empty statement, which does not decompile, even when labeled).
9553              * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
9554              * or an empty statement for a child.
9555              */
9556             pn->pn_type = TOK_LC;
9557             pn->pn_arity = PN_LIST;
9558             pn->makeEmpty();
9559         }
9560         RecycleTree(pn2, tc);
9561         if (pn3 && pn3 != pn2)
9562             RecycleTree(pn3, tc);
9563         break;
9564
9565       case TOK_OR:
9566       case TOK_AND:
9567         if (inCond) {
9568             if (pn->pn_arity == PN_LIST) {
9569                 JSParseNode **pnp = &pn->pn_head;
9570                 JS_ASSERT(*pnp == pn1);
9571                 do {
9572                     int cond = Boolish(pn1);
9573                     if (cond == (pn->pn_type == TOK_OR)) {
9574                         for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
9575                             pn3 = pn2->pn_next;
9576                             RecycleTree(pn2, tc);
9577                             --pn->pn_count;
9578                         }
9579                         pn1->pn_next = NULL;
9580                         break;
9581                     }
9582                     if (cond != -1) {
9583                         JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9584                         if (pn->pn_count == 1)
9585                             break;
9586                         *pnp = pn1->pn_next;
9587                         RecycleTree(pn1, tc);
9588                         --pn->pn_count;
9589                     } else {
9590                         pnp = &pn1->pn_next;
9591                     }
9592                 } while ((pn1 = *pnp) != NULL);
9593
9594                 // We may have to change arity from LIST to BINARY.
9595                 pn1 = pn->pn_head;
9596                 if (pn->pn_count == 2) {
9597                     pn2 = pn1->pn_next;
9598                     pn1->pn_next = NULL;
9599                     JS_ASSERT(!pn2->pn_next);
9600                     pn->pn_arity = PN_BINARY;
9601                     pn->pn_left = pn1;
9602                     pn->pn_right = pn2;
9603                 } else if (pn->pn_count == 1) {
9604                     pn->become(pn1);
9605                     RecycleTree(pn1, tc);
9606                 }
9607             } else {
9608                 int cond = Boolish(pn1);
9609                 if (cond == (pn->pn_type == TOK_OR)) {
9610                     RecycleTree(pn2, tc);
9611                     pn->become(pn1);
9612                 } else if (cond != -1) {
9613                     JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9614                     RecycleTree(pn1, tc);
9615                     pn->become(pn2);
9616                 }
9617             }
9618         }
9619         break;
9620
9621       case TOK_ASSIGN:
9622         /*
9623          * Compound operators such as *= should be subject to folding, in case
9624          * the left-hand side is constant, and so that the decompiler produces
9625          * the same string that you get from decompiling a script or function
9626          * compiled from that same string.  As with +, += is special.
9627          */
9628         if (pn->pn_op == JSOP_NOP)
9629             break;
9630         if (pn->pn_op != JSOP_ADD)
9631             goto do_binary_op;
9632         /* FALL THROUGH */
9633
9634       case TOK_PLUS:
9635         if (pn->pn_arity == PN_LIST) {
9636             size_t length, length2;
9637             jschar *chars;
9638             JSString *str, *str2;
9639
9640             /*
9641              * Any string literal term with all others number or string means
9642              * this is a concatenation.  If any term is not a string or number
9643              * literal, we can't fold.
9644              */
9645             JS_ASSERT(pn->pn_count > 2);
9646             if (pn->pn_xflags & PNX_CANTFOLD)
9647                 return JS_TRUE;
9648             if (pn->pn_xflags != PNX_STRCAT)
9649                 goto do_binary_op;
9650
9651             /* Ok, we're concatenating: convert non-string constant operands. */
9652             length = 0;
9653             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9654                 if (!FoldType(cx, pn2, TOK_STRING))
9655                     return JS_FALSE;
9656                 /* XXX fold only if all operands convert to string */
9657                 if (pn2->pn_type != TOK_STRING)
9658                     return JS_TRUE;
9659                 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9660             }
9661
9662             /* Allocate a new buffer and string descriptor for the result. */
9663             chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9664             if (!chars)
9665                 return JS_FALSE;
9666             chars[length] = 0;
9667             str = js_NewString(cx, chars, length);
9668             if (!str) {
9669                 cx->free(chars);
9670                 return JS_FALSE;
9671             }
9672
9673             /* Fill the buffer, advancing chars and recycling kids as we go. */
9674             for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9675                 str2 = ATOM_TO_STRING(pn2->pn_atom);
9676                 length2 = str2->flatLength();
9677                 js_strncpy(chars, str2->flatChars(), length2);
9678                 chars += length2;
9679             }
9680             JS_ASSERT(*chars == 0);
9681
9682             /* Atomize the result string and mutate pn to refer to it. */
9683             pn->pn_atom = js_AtomizeString(cx, str, 0);
9684             if (!pn->pn_atom)
9685                 return JS_FALSE;
9686             pn->pn_type = TOK_STRING;
9687             pn->pn_op = JSOP_STRING;
9688             pn->pn_arity = PN_NULLARY;
9689             break;
9690         }
9691
9692         /* Handle a binary string concatenation. */
9693         JS_ASSERT(pn->pn_arity == PN_BINARY);
9694         if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9695             JSString *left, *right, *str;
9696
9697             if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9698                           TOK_STRING)) {
9699                 return JS_FALSE;
9700             }
9701             if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9702                 return JS_TRUE;
9703             left = ATOM_TO_STRING(pn1->pn_atom);
9704             right = ATOM_TO_STRING(pn2->pn_atom);
9705             str = js_ConcatStrings(cx, left, right);
9706             if (!str)
9707                 return JS_FALSE;
9708             pn->pn_atom = js_AtomizeString(cx, str, 0);
9709             if (!pn->pn_atom)
9710                 return JS_FALSE;
9711             pn->pn_type = TOK_STRING;
9712             pn->pn_op = JSOP_STRING;
9713             pn->pn_arity = PN_NULLARY;
9714             RecycleTree(pn1, tc);
9715             RecycleTree(pn2, tc);
9716             break;
9717         }
9718
9719         /* Can't concatenate string literals, let's try numbers. */
9720         goto do_binary_op;
9721
9722       case TOK_STAR:
9723       case TOK_SHOP:
9724       case TOK_MINUS:
9725       case TOK_DIVOP:
9726       do_binary_op:
9727         if (pn->pn_arity == PN_LIST) {
9728             JS_ASSERT(pn->pn_count > 2);
9729             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9730                 if (!FoldType(cx, pn2, TOK_NUMBER))
9731                     return JS_FALSE;
9732             }
9733             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9734                 /* XXX fold only if all operands convert to number */
9735                 if (pn2->pn_type != TOK_NUMBER)
9736                     break;
9737             }
9738             if (!pn2) {
9739                 JSOp op = PN_OP(pn);
9740
9741                 pn2 = pn1->pn_next;
9742                 pn3 = pn2->pn_next;
9743                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9744                     return JS_FALSE;
9745                 while ((pn2 = pn3) != NULL) {
9746                     pn3 = pn2->pn_next;
9747                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9748                         return JS_FALSE;
9749                 }
9750             }
9751         } else {
9752             JS_ASSERT(pn->pn_arity == PN_BINARY);
9753             if (!FoldType(cx, pn1, TOK_NUMBER) ||
9754                 !FoldType(cx, pn2, TOK_NUMBER)) {
9755                 return JS_FALSE;
9756             }
9757             if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9758                 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9759                     return JS_FALSE;
9760             }
9761         }
9762         break;
9763
9764       case TOK_UNARYOP:
9765         if (pn1->pn_type == TOK_NUMBER) {
9766             jsdouble d;
9767
9768             /* Operate on one numeric constant. */
9769             d = pn1->pn_dval;
9770             switch (pn->pn_op) {
9771               case JSOP_BITNOT:
9772                 d = ~js_DoubleToECMAInt32(d);
9773                 break;
9774
9775               case JSOP_NEG:
9776                 d = -d;
9777                 break;
9778
9779               case JSOP_POS:
9780                 break;
9781
9782               case JSOP_NOT:
9783                 pn->pn_type = TOK_PRIMARY;
9784                 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9785                 pn->pn_arity = PN_NULLARY;
9786                 /* FALL THROUGH */
9787
9788               default:
9789                 /* Return early to dodge the common TOK_NUMBER code. */
9790                 return JS_TRUE;
9791             }
9792             pn->pn_type = TOK_NUMBER;
9793             pn->pn_op = JSOP_DOUBLE;
9794             pn->pn_arity = PN_NULLARY;
9795             pn->pn_dval = d;
9796             RecycleTree(pn1, tc);
9797         } else if (pn1->pn_type == TOK_PRIMARY) {
9798             if (pn->pn_op == JSOP_NOT &&
9799                 (pn1->pn_op == JSOP_TRUE ||
9800                  pn1->pn_op == JSOP_FALSE)) {
9801                 pn->become(pn1);
9802                 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9803                 RecycleTree(pn1, tc);
9804             }
9805         }
9806         break;
9807
9808 #if JS_HAS_XML_SUPPORT
9809       case TOK_XMLELEM:
9810       case TOK_XMLLIST:
9811       case TOK_XMLPTAGC:
9812       case TOK_XMLSTAGO:
9813       case TOK_XMLETAGO:
9814       case TOK_XMLNAME:
9815         if (pn->pn_arity == PN_LIST) {
9816             JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9817             if (!FoldXMLConstants(cx, pn, tc))
9818                 return JS_FALSE;
9819         }
9820         break;
9821
9822       case TOK_AT:
9823         if (pn1->pn_type == TOK_XMLNAME) {
9824             JSObjectBox *xmlbox;
9825
9826             Value v = StringValue(ATOM_TO_STRING(pn1->pn_atom));
9827             if (!js_ToAttributeName(cx, &v))
9828                 return JS_FALSE;
9829             JS_ASSERT(v.isObject());
9830
9831             xmlbox = tc->parser->newObjectBox(&v.toObject());
9832             if (!xmlbox)
9833                 return JS_FALSE;
9834
9835             pn->pn_type = TOK_XMLNAME;
9836             pn->pn_op = JSOP_OBJECT;
9837             pn->pn_arity = PN_NULLARY;
9838             pn->pn_objbox = xmlbox;
9839             RecycleTree(pn1, tc);
9840         }
9841         break;
9842 #endif /* JS_HAS_XML_SUPPORT */
9843
9844       default:;
9845     }
9846
9847     if (inCond) {
9848         int cond = Boolish(pn);
9849         if (cond >= 0) {
9850             /*
9851              * We can turn function nodes into constant nodes here, but mutating function
9852              * nodes is tricky --- in particular, mutating a function node that appears on
9853              * a method list corrupts the method list. However, methods are M's in
9854              * statements of the form 'this.foo = M;', which we never fold, so we're okay.
9855              */
9856             PrepareNodeForMutation(pn, tc);
9857             pn->pn_type = TOK_PRIMARY;
9858             pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9859             pn->pn_arity = PN_NULLARY;
9860         }
9861     }
9862
9863     return JS_TRUE;
9864 }