1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=78:
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 "jsstddef.h"
55 #include <stdlib.h>
56 #include <string.h>
57 #include <math.h>
58 #include "jstypes.h"
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
61 #include "jsapi.h"
62 #include "jsarray.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsconfig.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jslock.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsopcode.h"
73 #include "jsparse.h"
74 #include "jsscan.h"
75 #include "jsscope.h"
76 #include "jsscript.h"
77 #include "jsstr.h"
78 
79 #if JS_HAS_XML_SUPPORT
80 #include "jsxml.h"
81 #endif
82 
83 #if JS_HAS_DESTRUCTURING
84 #include "jsdhash.h"
85 #endif
86 
87 /*
88  * JS parsers, from lowest to highest precedence.
89  *
90  * Each parser takes a context, a token stream, and a tree context struct.
91  * Each returns a parse node tree or null on error.
92  */
93 
94 typedef JSParseNode *
95 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
96 
97 typedef JSParseNode *
98 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
99                JSBool allowCallSyntax);
100 
101 typedef JSParseNode *
102 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
103                 JSTokenType tt, JSBool afterDot);
104 
105 static JSParser FunctionStmt;
106 static JSParser FunctionExpr;
107 static JSParser Statements;
108 static JSParser Statement;
109 static JSParser Variables;
110 static JSParser Expr;
111 static JSParser AssignExpr;
112 static JSParser CondExpr;
113 static JSParser OrExpr;
114 static JSParser AndExpr;
115 static JSParser BitOrExpr;
116 static JSParser BitXorExpr;
117 static JSParser BitAndExpr;
118 static JSParser EqExpr;
119 static JSParser RelExpr;
120 static JSParser ShiftExpr;
121 static JSParser AddExpr;
122 static JSParser MulExpr;
123 static JSParser UnaryExpr;
124 static JSMemberParser MemberExpr;
125 static JSPrimaryParser PrimaryExpr;
126 
127 /*
128  * Insist that the next token be of type tt, or report errno and return null.
129  * NB: this macro uses cx and ts from its lexical environment.
130  */
131 #define MUST_MATCH_TOKEN(tt, errno)                                           \
132     JS_BEGIN_MACRO                                                            \
133         if (js_GetToken(cx, ts) != tt) {                                      \
134             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
135                             errno);                                           \
136             return NULL;                                                      \
137         }                                                                     \
138     JS_END_MACRO
139 
140 #define CHECK_RECURSION()                                                     \
141     JS_BEGIN_MACRO                                                            \
142         int stackDummy;                                                       \
143         if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {                           \
144             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
145                                         JSMSG_OVER_RECURSED);                 \
146             return NULL;                                                      \
147         }                                                                     \
148     JS_END_MACRO
149 
150 #ifdef METER_PARSENODES
151 static uint32 parsenodes = 0;
152 static uint32 maxparsenodes = 0;
153 static uint32 recyclednodes = 0;
154 #endif
155 
156 static JSParseNode *
RecycleTree(JSParseNode * pn,JSTreeContext * tc)157 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
158 {
159     JSParseNode *next;
160 
161     if (!pn)
162         return NULL;
163     JS_ASSERT(pn != tc->nodeList);      /* catch back-to-back dup recycles */
164     next = pn->pn_next;
165     pn->pn_next = tc->nodeList;
166     tc->nodeList = pn;
167 #ifdef METER_PARSENODES
168     recyclednodes++;
169 #endif
170     return next;
171 }
172 
173 static JSParseNode *
NewOrRecycledNode(JSContext * cx,JSTreeContext * tc)174 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
175 {
176     JSParseNode *pn;
177 
178     pn = tc->nodeList;
179     if (!pn) {
180         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
181         if (!pn)
182             JS_ReportOutOfMemory(cx);
183     } else {
184         tc->nodeList = pn->pn_next;
185 
186         /* Recycle immediate descendents only, to save work and working set. */
187         switch (pn->pn_arity) {
188           case PN_FUNC:
189             RecycleTree(pn->pn_body, tc);
190             break;
191           case PN_LIST:
192             if (pn->pn_head) {
193                 /* XXX check for dup recycles in the list */
194                 *pn->pn_tail = tc->nodeList;
195                 tc->nodeList = pn->pn_head;
196 #ifdef METER_PARSENODES
197                 recyclednodes += pn->pn_count;
198 #endif
199             }
200             break;
201           case PN_TERNARY:
202             RecycleTree(pn->pn_kid1, tc);
203             RecycleTree(pn->pn_kid2, tc);
204             RecycleTree(pn->pn_kid3, tc);
205             break;
206           case PN_BINARY:
207             RecycleTree(pn->pn_left, tc);
208             RecycleTree(pn->pn_right, tc);
209             break;
210           case PN_UNARY:
211             RecycleTree(pn->pn_kid, tc);
212             break;
213           case PN_NAME:
214             RecycleTree(pn->pn_expr, tc);
215             break;
216           case PN_NULLARY:
217             break;
218         }
219     }
220 #ifdef METER_PARSENODES
221     if (pn) {
222         parsenodes++;
223         if (parsenodes - recyclednodes > maxparsenodes)
224             maxparsenodes = parsenodes - recyclednodes;
225     }
226 #endif
227     return pn;
228 }
229 
230 /*
231  * Allocate a JSParseNode from cx's temporary arena.
232  */
233 static JSParseNode *
NewParseNode(JSContext * cx,JSTokenStream * ts,JSParseNodeArity arity,JSTreeContext * tc)234 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
235              JSTreeContext *tc)
236 {
237     JSParseNode *pn;
238     JSToken *tp;
239 
240     pn = NewOrRecycledNode(cx, tc);
241     if (!pn)
242         return NULL;
243     tp = &CURRENT_TOKEN(ts);
244     pn->pn_type = tp->type;
245     pn->pn_pos = tp->pos;
246     pn->pn_op = JSOP_NOP;
247     pn->pn_arity = arity;
248     pn->pn_next = NULL;
249     pn->pn_ts = ts;
250     pn->pn_source = NULL;
251     return pn;
252 }
253 
254 static JSParseNode *
NewBinary(JSContext * cx,JSTokenType tt,JSOp op,JSParseNode * left,JSParseNode * right,JSTreeContext * tc)255 NewBinary(JSContext *cx, JSTokenType tt,
256           JSOp op, JSParseNode *left, JSParseNode *right,
257           JSTreeContext *tc)
258 {
259     JSParseNode *pn, *pn1, *pn2;
260 
261     if (!left || !right)
262         return NULL;
263 
264     /*
265      * Flatten a left-associative (left-heavy) tree of a given operator into
266      * a list, to reduce js_FoldConstants and js_EmitTree recursion.
267      */
268     if (left->pn_type == tt &&
269         left->pn_op == op &&
270         (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
271         if (left->pn_arity != PN_LIST) {
272             pn1 = left->pn_left, pn2 = left->pn_right;
273             left->pn_arity = PN_LIST;
274             PN_INIT_LIST_1(left, pn1);
275             PN_APPEND(left, pn2);
276             if (tt == TOK_PLUS) {
277                 if (pn1->pn_type == TOK_STRING)
278                     left->pn_extra |= PNX_STRCAT;
279                 else if (pn1->pn_type != TOK_NUMBER)
280                     left->pn_extra |= PNX_CANTFOLD;
281                 if (pn2->pn_type == TOK_STRING)
282                     left->pn_extra |= PNX_STRCAT;
283                 else if (pn2->pn_type != TOK_NUMBER)
284                     left->pn_extra |= PNX_CANTFOLD;
285             }
286         }
287         PN_APPEND(left, right);
288         left->pn_pos.end = right->pn_pos.end;
289         if (tt == TOK_PLUS) {
290             if (right->pn_type == TOK_STRING)
291                 left->pn_extra |= PNX_STRCAT;
292             else if (right->pn_type != TOK_NUMBER)
293                 left->pn_extra |= PNX_CANTFOLD;
294         }
295         return left;
296     }
297 
298     /*
299      * Fold constant addition immediately, to conserve node space and, what's
300      * more, so js_FoldConstants never sees mixed addition and concatenation
301      * operations with more than one leading non-string operand in a PN_LIST
302      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
303      * to "3pt", not "12pt").
304      */
305     if (tt == TOK_PLUS &&
306         left->pn_type == TOK_NUMBER &&
307         right->pn_type == TOK_NUMBER) {
308         left->pn_dval += right->pn_dval;
309         left->pn_pos.end = right->pn_pos.end;
310         RecycleTree(right, tc);
311         return left;
312     }
313 
314     pn = NewOrRecycledNode(cx, tc);
315     if (!pn)
316         return NULL;
317     pn->pn_type = tt;
318     pn->pn_pos.begin = left->pn_pos.begin;
319     pn->pn_pos.end = right->pn_pos.end;
320     pn->pn_op = op;
321     pn->pn_arity = PN_BINARY;
322     pn->pn_left = left;
323     pn->pn_right = right;
324     pn->pn_next = NULL;
325     pn->pn_ts = NULL;
326     pn->pn_source = NULL;
327     return pn;
328 }
329 
330 #if JS_HAS_GETTER_SETTER
331 static JSTokenType
CheckGetterOrSetter(JSContext * cx,JSTokenStream * ts,JSTokenType tt)332 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
333 {
334     JSAtom *atom;
335     JSRuntime *rt;
336     JSOp op;
337     const char *name;
338 
339     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
340     atom = CURRENT_TOKEN(ts).t_atom;
341     rt = cx->runtime;
342     if (atom == rt->atomState.getterAtom)
343         op = JSOP_GETTER;
344     else if (atom == rt->atomState.setterAtom)
345         op = JSOP_SETTER;
346     else
347         return TOK_NAME;
348     if (js_PeekTokenSameLine(cx, ts) != tt)
349         return TOK_NAME;
350     (void) js_GetToken(cx, ts);
351     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
352         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
353                                     JSMSG_BAD_GETTER_OR_SETTER,
354                                     (op == JSOP_GETTER)
355                                     ? js_getter_str
356                                     : js_setter_str);
357         return TOK_ERROR;
358     }
359     CURRENT_TOKEN(ts).t_op = op;
360     if (JS_HAS_STRICT_OPTION(cx)) {
361         name = js_AtomToPrintableString(cx, atom);
362         if (!name ||
363             !js_ReportCompileErrorNumber(cx, ts,
364                                          JSREPORT_TS |
365                                          JSREPORT_WARNING |
366                                          JSREPORT_STRICT,
367                                          JSMSG_DEPRECATED_USAGE,
368                                          name)) {
369             return TOK_ERROR;
370         }
371     }
372     return tt;
373 }
374 #endif
375 
376 static void
MaybeSetupFrame(JSContext * cx,JSObject * chain,JSStackFrame * oldfp,JSStackFrame * newfp)377 MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp,
378                 JSStackFrame *newfp)
379 {
380     /*
381      * Always push a new frame if the current frame is special, so that
382      * Variables gets the correct variables object: the one from the special
383      * frame's caller.
384      */
385     if (oldfp &&
386         oldfp->varobj &&
387         oldfp->scopeChain == chain &&
388         !(oldfp->flags & JSFRAME_SPECIAL)) {
389         return;
390     }
391 
392     memset(newfp, 0, sizeof *newfp);
393 
394     /* Default to sharing the same variables object and scope chain. */
395     newfp->varobj = newfp->scopeChain = chain;
396     if (cx->options & JSOPTION_VAROBJFIX) {
397         while ((chain = JS_GetParent(cx, chain)) != NULL)
398             newfp->varobj = chain;
399     }
400     newfp->down = oldfp;
401     if (oldfp) {
402         /*
403          * In the case of eval and debugger frames, we need to dig down and find
404          * the real variables objects and function that our new stack frame is
405          * going to use.
406          */
407         newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
408                                        JSFRAME_SCRIPT_OBJECT);
409         while (oldfp->flags & JSFRAME_SPECIAL) {
410             oldfp = oldfp->down;
411             if (!oldfp)
412                 break;
413         }
414         if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) {
415             newfp->varobj = oldfp->varobj;
416             newfp->vars = oldfp->vars;
417             newfp->fun = oldfp->fun;
418         }
419     }
420     cx->fp = newfp;
421 }
422 
423 /*
424  * Parse a top-level JS script.
425  */
426 JS_FRIEND_API(JSParseNode *)
js_ParseTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts)427 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
428 {
429     JSStackFrame *fp, frame;
430     JSTreeContext tc;
431     JSParseNode *pn;
432 
433     /*
434      * Push a compiler frame if we have no frames, or if the top frame is a
435      * lightweight function activation, or if its scope chain doesn't match
436      * the one passed to us.
437      */
438     fp = cx->fp;
439     MaybeSetupFrame(cx, chain, fp, &frame);
440 
441     /*
442      * Protect atoms from being collected by a GC activation, which might
443      * - nest on this thread due to out of memory (the so-called "last ditch"
444      *   GC attempted within js_NewGCThing), or
445      * - run for any reason on another thread if this thread is suspended on
446      *   an object lock before it finishes generating bytecode into a script
447      *   protected from the GC by a root or a stack frame reference.
448      */
449     JS_KEEP_ATOMS(cx->runtime);
450     TREE_CONTEXT_INIT(&tc);
451     pn = Statements(cx, ts, &tc);
452     if (pn) {
453         if (!js_MatchToken(cx, ts, TOK_EOF)) {
454             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
455                                         JSMSG_SYNTAX_ERROR);
456             pn = NULL;
457         } else {
458             pn->pn_type = TOK_LC;
459             if (!js_FoldConstants(cx, pn, &tc))
460                 pn = NULL;
461         }
462     }
463 
464     TREE_CONTEXT_FINISH(&tc);
465     JS_UNKEEP_ATOMS(cx->runtime);
466     cx->fp = fp;
467     return pn;
468 }
469 
470 /*
471  * Compile a top-level script.
472  */
473 JS_FRIEND_API(JSBool)
js_CompileTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts,JSCodeGenerator * cg)474 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
475                       JSCodeGenerator *cg)
476 {
477     JSStackFrame *fp, frame;
478     uint32 flags;
479     JSParseNode *pn;
480     JSBool ok;
481 #ifdef METER_PARSENODES
482     void *sbrk(ptrdiff_t), *before = sbrk(0);
483 #endif
484 
485     /*
486      * Push a compiler frame if we have no frames, or if the top frame is a
487      * lightweight function activation, or if its scope chain doesn't match
488      * the one passed to us.
489      */
490     fp = cx->fp;
491     MaybeSetupFrame(cx, chain, fp, &frame);
492     flags = cx->fp->flags;
493     cx->fp->flags = flags |
494                     (JS_HAS_COMPILE_N_GO_OPTION(cx)
495                      ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
496                      : JSFRAME_COMPILING);
497 
498     /* Prevent GC activation while compiling. */
499     JS_KEEP_ATOMS(cx->runtime);
500 
501     pn = Statements(cx, ts, &cg->treeContext);
502     if (!pn) {
503         ok = JS_FALSE;
504     } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
505         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
506                                     JSMSG_SYNTAX_ERROR);
507         ok = JS_FALSE;
508     } else {
509 #ifdef METER_PARSENODES
510         printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
511                (char *)sbrk(0) - (char *)before,
512                parsenodes,
513                maxparsenodes,
514                parsenodes - recyclednodes);
515         before = sbrk(0);
516 #endif
517 
518         /*
519          * No need to emit bytecode here -- Statements already has, for each
520          * statement in turn.  Search for TCF_COMPILING in Statements, below.
521          * That flag is set for every tc == &cg->treeContext, and it implies
522          * that the tc can be downcast to a cg and used to emit code during
523          * parsing, rather than at the end of the parse phase.
524          *
525          * Nowadays the threaded interpreter needs a stop instruction, so we
526          * do have to emit that here.
527          */
528         JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
529         ok = js_Emit1(cx, cg, JSOP_STOP) >= 0;
530     }
531 
532 #ifdef METER_PARSENODES
533     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
534            (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
535 #endif
536 #ifdef JS_ARENAMETER
537     JS_DumpArenaStats(stdout);
538 #endif
539     JS_UNKEEP_ATOMS(cx->runtime);
540     cx->fp->flags = flags;
541     cx->fp = fp;
542     return ok;
543 }
544 
545 /*
546  * Insist on a final return before control flows out of pn.  Try to be a bit
547  * smart about loops: do {...; return e2;} while(0) at the end of a function
548  * that contains an early return e1 will get a strict warning.  Similarly for
549  * iloops: while (true){...} is treated as though ... returns.
550  */
551 #define ENDS_IN_OTHER   0
552 #define ENDS_IN_RETURN  1
553 #define ENDS_IN_BREAK   2
554 
555 static int
HasFinalReturn(JSParseNode * pn)556 HasFinalReturn(JSParseNode *pn)
557 {
558     JSParseNode *pn2, *pn3;
559     uintN rv, rv2, hasDefault;
560 
561     switch (pn->pn_type) {
562       case TOK_LC:
563         if (!pn->pn_head)
564             return ENDS_IN_OTHER;
565         return HasFinalReturn(PN_LAST(pn));
566 
567       case TOK_IF:
568         if (!pn->pn_kid3)
569             return ENDS_IN_OTHER;
570         return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
571 
572       case TOK_WHILE:
573         pn2 = pn->pn_left;
574         if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
575             return ENDS_IN_RETURN;
576         if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
577             return ENDS_IN_RETURN;
578         return ENDS_IN_OTHER;
579 
580       case TOK_DO:
581         pn2 = pn->pn_right;
582         if (pn2->pn_type == TOK_PRIMARY) {
583             if (pn2->pn_op == JSOP_FALSE)
584                 return HasFinalReturn(pn->pn_left);
585             if (pn2->pn_op == JSOP_TRUE)
586                 return ENDS_IN_RETURN;
587         }
588         if (pn2->pn_type == TOK_NUMBER) {
589             if (pn2->pn_dval == 0)
590                 return HasFinalReturn(pn->pn_left);
591             return ENDS_IN_RETURN;
592         }
593         return ENDS_IN_OTHER;
594 
595       case TOK_FOR:
596         pn2 = pn->pn_left;
597         if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
598             return ENDS_IN_RETURN;
599         return ENDS_IN_OTHER;
600 
601       case TOK_SWITCH:
602         rv = ENDS_IN_RETURN;
603         hasDefault = ENDS_IN_OTHER;
604         pn2 = pn->pn_right;
605         if (pn2->pn_type == TOK_LEXICALSCOPE)
606             pn2 = pn2->pn_expr;
607         for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
608             if (pn2->pn_type == TOK_DEFAULT)
609                 hasDefault = ENDS_IN_RETURN;
610             pn3 = pn2->pn_right;
611             JS_ASSERT(pn3->pn_type == TOK_LC);
612             if (pn3->pn_head) {
613                 rv2 = HasFinalReturn(PN_LAST(pn3));
614                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
615                     /* Falling through to next case or default. */;
616                 else
617                     rv &= rv2;
618             }
619         }
620         /* If a final switch has no default case, we judge it harshly. */
621         rv &= hasDefault;
622         return rv;
623 
624       case TOK_BREAK:
625         return ENDS_IN_BREAK;
626 
627       case TOK_WITH:
628         return HasFinalReturn(pn->pn_right);
629 
630       case TOK_RETURN:
631         return ENDS_IN_RETURN;
632 
633       case TOK_COLON:
634       case TOK_LEXICALSCOPE:
635         return HasFinalReturn(pn->pn_expr);
636 
637       case TOK_THROW:
638         return ENDS_IN_RETURN;
639 
640       case TOK_TRY:
641         /* If we have a finally block that returns, we are done. */
642         if (pn->pn_kid3) {
643             rv = HasFinalReturn(pn->pn_kid3);
644             if (rv == ENDS_IN_RETURN)
645                 return rv;
646         }
647 
648         /* Else check the try block and any and all catch statements. */
649         rv = HasFinalReturn(pn->pn_kid1);
650         if (pn->pn_kid2) {
651             JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
652             for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
653                 rv &= HasFinalReturn(pn2);
654         }
655         return rv;
656 
657       case TOK_CATCH:
658         /* Check this catch block's body. */
659         return HasFinalReturn(pn->pn_kid3);
660 
661       case TOK_LET:
662         /* Non-binary let statements are let declarations. */
663         if (pn->pn_arity != PN_BINARY)
664             return ENDS_IN_OTHER;
665         return HasFinalReturn(pn->pn_right);
666 
667       default:
668         return ENDS_IN_OTHER;
669     }
670 }
671 
672 static JSBool
ReportBadReturn(JSContext * cx,JSTokenStream * ts,uintN flags,uintN errnum,uintN anonerrnum)673 ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum,
674                 uintN anonerrnum)
675 {
676     JSFunction *fun;
677     const char *name;
678 
679     fun = cx->fp->fun;
680     if (fun->atom) {
681         name = js_AtomToPrintableString(cx, fun->atom);
682     } else {
683         errnum = anonerrnum;
684         name = NULL;
685     }
686     return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum,
687                                        name);
688 }
689 
690 static JSBool
CheckFinalReturn(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)691 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
692 {
693     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
694            ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
695                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
696 }
697 
698 static JSParseNode *
FunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun,JSTreeContext * tc)699 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
700              JSTreeContext *tc)
701 {
702     JSStackFrame *fp, frame;
703     JSObject *funobj;
704     JSStmtInfo stmtInfo;
705     uintN oldflags, firstLine;
706     JSParseNode *pn;
707 
708     fp = cx->fp;
709     funobj = fun->object;
710     if (!fp || fp->fun != fun || fp->varobj != funobj ||
711         fp->scopeChain != funobj) {
712         memset(&frame, 0, sizeof frame);
713         frame.fun = fun;
714         frame.varobj = frame.scopeChain = funobj;
715         frame.down = fp;
716         if (fp)
717             frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
718         cx->fp = &frame;
719     }
720 
721     /*
722      * Set interpreted early so js_EmitTree can test it to decide whether to
723      * eliminate useless expressions.
724      */
725     fun->flags |= JSFUN_INTERPRETED;
726 
727     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
728     stmtInfo.flags = SIF_BODY_BLOCK;
729 
730     oldflags = tc->flags;
731     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
732     tc->flags |= TCF_IN_FUNCTION;
733 
734     /*
735      * Save the body's first line, and store it in pn->pn_pos.begin.lineno
736      * later, because we may have not peeked in ts yet, so Statements won't
737      * acquire a valid pn->pn_pos.begin from the current token.
738      */
739     firstLine = ts->lineno;
740     pn = Statements(cx, ts, tc);
741 
742     js_PopStatement(tc);
743 
744     /* Check for falling off the end of a function that returns a value. */
745     if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
746         if (!CheckFinalReturn(cx, ts, pn))
747             pn = NULL;
748     }
749 
750     /*
751      * If we have a parse tree in pn and a code generator in tc, emit this
752      * function's code.  We must do this here, not in js_CompileFunctionBody,
753      * in order to detect TCF_IN_FUNCTION among tc->flags.
754      */
755     if (pn) {
756         pn->pn_pos.begin.lineno = firstLine;
757         if ((tc->flags & TCF_COMPILING)) {
758             JSCodeGenerator *cg = (JSCodeGenerator *) tc;
759 
760             if (!js_FoldConstants(cx, pn, tc) ||
761                 !js_EmitFunctionBytecode(cx, cg, pn)) {
762                 pn = NULL;
763             }
764         }
765     }
766 
767     cx->fp = fp;
768     tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
769     return pn;
770 }
771 
772 /*
773  * Compile a JS function body, which might appear as the value of an event
774  * handler attribute in an HTML <INPUT> tag.
775  */
776 JSBool
js_CompileFunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun)777 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
778 {
779     JSArenaPool codePool, notePool;
780     JSCodeGenerator funcg;
781     JSStackFrame *fp, frame;
782     JSObject *funobj;
783     JSParseNode *pn;
784 
785     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
786     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
787     if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
788                               ts->filename, ts->lineno,
789                               ts->principals)) {
790         return JS_FALSE;
791     }
792 
793     /* Prevent GC activation while compiling. */
794     JS_KEEP_ATOMS(cx->runtime);
795 
796     /* Push a JSStackFrame for use by FunctionBody. */
797     fp = cx->fp;
798     funobj = fun->object;
799     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
800                       fp->scopeChain != funobj));
801     memset(&frame, 0, sizeof frame);
802     frame.fun = fun;
803     frame.varobj = frame.scopeChain = funobj;
804     frame.down = fp;
805     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
806                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
807                   : JSFRAME_COMPILING;
808     cx->fp = &frame;
809 
810     /*
811      * Farble the body so that it looks like a block statement to js_EmitTree,
812      * which is called beneath FunctionBody; see Statements, further below in
813      * this file.  FunctionBody pushes a STMT_BLOCK record around its call to
814      * Statements, so Statements will not compile each statement as it loops
815      * to save JSParseNode space -- it will not compile at all, only build a
816      * JSParseNode tree.
817      *
818      * Therefore we must fold constants, allocate try notes, and generate code
819      * for this function, including a stop opcode at the end.
820      */
821     CURRENT_TOKEN(ts).type = TOK_LC;
822     pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
823     if (pn && !js_NewScriptFromCG(cx, &funcg, fun))
824         pn = NULL;
825 
826     /* Restore saved state and release code generation arenas. */
827     cx->fp = fp;
828     JS_UNKEEP_ATOMS(cx->runtime);
829     js_FinishCodeGenerator(cx, &funcg);
830     JS_FinishArenaPool(&codePool);
831     JS_FinishArenaPool(&notePool);
832     return pn != NULL;
833 }
834 
835 /*
836  * Parameter block types for the several Binder functions.  We use a common
837  * helper function signature in order to share code among destructuring and
838  * simple variable declaration parsers.  In the destructuring case, the binder
839  * function is called indirectly from the variable declaration parser by way
840  * of CheckDestructuring and its friends.
841  */
842 typedef struct BindData BindData;
843 
844 typedef JSBool
845 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
846 
847 struct BindData {
848     JSParseNode             *pn;                /* error source coordinate */
849     JSTokenStream           *ts;                /* fallback if pn is null */
850     JSObject                *obj;               /* the variable object */
851     JSOp                    op;                 /* prolog bytecode or nop */
852     Binder                  binder;             /* binder, discriminates u */
853     union {
854         struct {
855             JSFunction      *fun;               /* must come first! see next */
856         } arg;
857         struct {
858             JSFunction      *fun;               /* this overlays u.arg.fun */
859             JSClass         *clasp;
860             JSPropertyOp    getter;
861             JSPropertyOp    setter;
862             uintN           attrs;
863         } var;
864         struct {
865             jsuint          index;
866             uintN           overflow;
867         } let;
868     } u;
869 };
870 
871 /*
872  * Given BindData *data and JSREPORT_* flags, expand to the second and third
873  * actual parameters to js_ReportCompileErrorNumber.  Prefer reporting via pn
874  * to reporting via ts, for better destructuring error pointers.
875  */
876 #define BIND_DATA_REPORT_ARGS(data, flags)                                    \
877     (data)->pn ? (void *)(data)->pn : (void *)(data)->ts,                     \
878     ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags)
879 
880 static JSBool
BumpFormalCount(JSContext * cx,JSFunction * fun)881 BumpFormalCount(JSContext *cx, JSFunction *fun)
882 {
883     if (fun->nargs == JS_BITMASK(16)) {
884         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
885                              JSMSG_TOO_MANY_FUN_ARGS);
886         return JS_FALSE;
887     }
888     fun->nargs++;
889     return JS_TRUE;
890 }
891 
892 static JSBool
BindArg(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)893 BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
894 {
895     JSObject *obj, *pobj;
896     JSProperty *prop;
897     JSBool ok;
898     uintN dupflag;
899     JSFunction *fun;
900     const char *name;
901 
902     obj = data->obj;
903     ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
904     if (!ok)
905         return JS_FALSE;
906 
907     dupflag = 0;
908     if (prop) {
909         JS_ASSERT(pobj == obj);
910         name = js_AtomToPrintableString(cx, atom);
911 
912         /*
913          * A duplicate parameter name, a "feature" required by ECMA-262.
914          * We force a duplicate node on the SCOPE_LAST_PROP(scope) list
915          * with the same id, distinguished by the SPROP_IS_DUPLICATE flag,
916          * and not mapped by an entry in scope.
917          */
918         ok = name &&
919              js_ReportCompileErrorNumber(cx,
920                                          BIND_DATA_REPORT_ARGS(data,
921                                              JSREPORT_WARNING |
922                                              JSREPORT_STRICT),
923                                          JSMSG_DUPLICATE_FORMAL,
924                                          name);
925 
926         OBJ_DROP_PROPERTY(cx, pobj, prop);
927         if (!ok)
928             return JS_FALSE;
929 
930         dupflag = SPROP_IS_DUPLICATE;
931     }
932 
933     fun = data->u.arg.fun;
934     if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
935                               js_GetArgument, js_SetArgument,
936                               SPROP_INVALID_SLOT,
937                               JSPROP_PERMANENT | JSPROP_SHARED,
938                               dupflag | SPROP_HAS_SHORTID,
939                               fun->nargs)) {
940         return JS_FALSE;
941     }
942 
943     return BumpFormalCount(cx, fun);
944 }
945 
946 static JSBool
BindLocalVariable(JSContext * cx,BindData * data,JSAtom * atom)947 BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom)
948 {
949     JSFunction *fun;
950 
951     /*
952      * Can't increase fun->nvars in an active frame, so insist that getter is
953      * js_GetLocalVariable, not js_GetCallVariable or anything else.
954      */
955     if (data->u.var.getter != js_GetLocalVariable)
956         return JS_TRUE;
957 
958     /*
959      * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
960      * Instead 'var arguments' always restates the predefined property of the
961      * activation objects with unhidden name 'arguments'.  Assignment to such
962      * a variable must be handled specially.
963      */
964     if (atom == cx->runtime->atomState.argumentsAtom)
965         return JS_TRUE;
966 
967     fun = data->u.var.fun;
968     if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
969                               data->u.var.getter, data->u.var.setter,
970                               SPROP_INVALID_SLOT,
971                               data->u.var.attrs | JSPROP_SHARED,
972                               SPROP_HAS_SHORTID, fun->u.i.nvars)) {
973         return JS_FALSE;
974     }
975     if (fun->u.i.nvars == JS_BITMASK(16)) {
976         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
977                              JSMSG_TOO_MANY_FUN_VARS);
978         return JS_FALSE;
979     }
980     fun->u.i.nvars++;
981     return JS_TRUE;
982 }
983 
984 #if JS_HAS_DESTRUCTURING
985 /*
986  * Forward declaration to maintain top-down presentation.
987  */
988 static JSParseNode *
989 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
990                   JSTokenType tt);
991 
992 static JSBool
BindDestructuringArg(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)993 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
994                      JSTreeContext *tc)
995 {
996     JSAtomListElement *ale;
997     JSFunction *fun;
998     JSObject *obj, *pobj;
999     JSProperty *prop;
1000     const char *name;
1001 
1002     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1003     if (!ale) {
1004         ale = js_IndexAtom(cx, atom, &tc->decls);
1005         if (!ale)
1006             return JS_FALSE;
1007         ALE_SET_JSOP(ale, data->op);
1008     }
1009 
1010     fun = data->u.var.fun;
1011     obj = data->obj;
1012     if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
1013         return JS_FALSE;
1014 
1015     if (prop) {
1016         JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj));
1017         name = js_AtomToPrintableString(cx, atom);
1018         if (!name ||
1019             !js_ReportCompileErrorNumber(cx,
1020                                          BIND_DATA_REPORT_ARGS(data,
1021                                              JSREPORT_WARNING |
1022                                              JSREPORT_STRICT),
1023                                          JSMSG_DUPLICATE_FORMAL,
1024                                          name)) {
1025             return JS_FALSE;
1026         }
1027         OBJ_DROP_PROPERTY(cx, pobj, prop);
1028     } else {
1029         if (!BindLocalVariable(cx, data, atom))
1030             return JS_FALSE;
1031     }
1032     return JS_TRUE;
1033 }
1034 #endif /* JS_HAS_DESTRUCTURING */
1035 
1036 static JSParseNode *
FunctionDef(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool lambda)1037 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1038             JSBool lambda)
1039 {
1040     JSOp op, prevop;
1041     JSParseNode *pn, *body, *result;
1042     JSTokenType tt;
1043     JSAtom *funAtom, *objAtom;
1044     JSStackFrame *fp;
1045     JSObject *varobj, *pobj;
1046     JSAtomListElement *ale;
1047     JSProperty *prop;
1048     JSFunction *fun;
1049     JSTreeContext funtc;
1050 #if JS_HAS_DESTRUCTURING
1051     JSParseNode *item, *list = NULL;
1052 #endif
1053 
1054     /* Make a TOK_FUNCTION node. */
1055 #if JS_HAS_GETTER_SETTER
1056     op = CURRENT_TOKEN(ts).t_op;
1057 #endif
1058     pn = NewParseNode(cx, ts, PN_FUNC, tc);
1059     if (!pn)
1060         return NULL;
1061 
1062     /* Scan the optional function name into funAtom. */
1063     ts->flags |= TSF_KEYWORD_IS_NAME;
1064     tt = js_GetToken(cx, ts);
1065     ts->flags &= ~TSF_KEYWORD_IS_NAME;
1066     if (tt == TOK_NAME) {
1067         funAtom = CURRENT_TOKEN(ts).t_atom;
1068     } else {
1069         funAtom = NULL;
1070         js_UngetToken(ts);
1071     }
1072 
1073     /* Find the nearest variable-declaring scope and use it as our parent. */
1074     fp = cx->fp;
1075     varobj = fp->varobj;
1076 
1077     /*
1078      * Record names for function statements in tc->decls so we know when to
1079      * avoid optimizing variable references that might name a function.
1080      */
1081     if (!lambda && funAtom) {
1082         ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1083         if (ale) {
1084             prevop = ALE_JSOP(ale);
1085             if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1086                 const char *name = js_AtomToPrintableString(cx, funAtom);
1087                 if (!name ||
1088                     !js_ReportCompileErrorNumber(cx, ts,
1089                                                  (prevop != JSOP_DEFCONST)
1090                                                  ? JSREPORT_TS |
1091                                                    JSREPORT_WARNING |
1092                                                    JSREPORT_STRICT
1093                                                  : JSREPORT_TS | JSREPORT_ERROR,
1094                                                  JSMSG_REDECLARED_VAR,
1095                                                  (prevop == JSOP_DEFFUN ||
1096                                                   prevop == JSOP_CLOSURE)
1097                                                  ? js_function_str
1098                                                  : (prevop == JSOP_DEFCONST)
1099                                                  ? js_const_str
1100                                                  : js_var_str,
1101                                                  name)) {
1102                     return NULL;
1103                 }
1104             }
1105             if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1106                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1107         } else {
1108             ale = js_IndexAtom(cx, funAtom, &tc->decls);
1109             if (!ale)
1110                 return NULL;
1111         }
1112         ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE);
1113 
1114         /*
1115          * A function nested at top level inside another's body needs only a
1116          * local variable to bind its name to its value, and not an activation
1117          * object property (it might also need the activation property, if the
1118          * outer function contains with statements, e.g., but the stack slot
1119          * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1120          * JSOP_GETVAR bytecode).
1121          */
1122         if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
1123             JSScopeProperty *sprop;
1124 
1125             /*
1126              * Define a property on the outer function so that BindNameToSlot
1127              * can properly optimize accesses.
1128              */
1129             JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
1130             JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
1131             if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
1132                                          &pobj, &prop)) {
1133                 return NULL;
1134             }
1135             if (prop)
1136                 OBJ_DROP_PROPERTY(cx, pobj, prop);
1137             sprop = NULL;
1138             if (!prop ||
1139                 pobj != varobj ||
1140                 (sprop = (JSScopeProperty *)prop,
1141                  sprop->getter != js_GetLocalVariable)) {
1142                 uintN sflags;
1143 
1144                 /*
1145                  * Use SPROP_IS_DUPLICATE if there is a formal argument of the
1146                  * same name, so the decompiler can find the parameter name.
1147                  */
1148                 sflags = (sprop && sprop->getter == js_GetArgument)
1149                          ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
1150                          : SPROP_HAS_SHORTID;
1151                 if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
1152                                           js_GetLocalVariable,
1153                                           js_SetLocalVariable,
1154                                           SPROP_INVALID_SLOT,
1155                                           JSPROP_PERMANENT | JSPROP_SHARED,
1156                                           sflags, fp->fun->u.i.nvars)) {
1157                     return NULL;
1158                 }
1159                 if (fp->fun->u.i.nvars == JS_BITMASK(16)) {
1160                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1161                                          JSMSG_TOO_MANY_FUN_VARS);
1162                     return NULL;
1163                 }
1164                 fp->fun->u.i.nvars++;
1165             }
1166         }
1167     }
1168 
1169     fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
1170                          funAtom);
1171     if (!fun)
1172         return NULL;
1173 #if JS_HAS_GETTER_SETTER
1174     if (op != JSOP_NOP)
1175         fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1176 #endif
1177 
1178     /*
1179      * Atomize fun->object early to protect against a last-ditch GC under
1180      * js_LookupHiddenProperty.
1181      *
1182      * Absent use of the new scoped local GC roots API around compiler calls,
1183      * we need to atomize here to protect against a GC activation.  Atoms are
1184      * protected from GC during compilation by the JS_FRIEND_API entry points
1185      * in this file.  There doesn't seem to be any gain in switching from the
1186      * atom-keeping method to the bulkier, slower scoped local roots method.
1187      */
1188     objAtom = js_AtomizeObject(cx, fun->object, 0);
1189     if (!objAtom)
1190         return NULL;
1191 
1192     /* Initialize early for possible flags mutation via DestructuringExpr. */
1193     TREE_CONTEXT_INIT(&funtc);
1194 
1195     /* Now parse formal argument list and compute fun->nargs. */
1196     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1197     if (!js_MatchToken(cx, ts, TOK_RP)) {
1198         BindData data;
1199 
1200         data.pn = NULL;
1201         data.ts = ts;
1202         data.obj = fun->object;
1203         data.op = JSOP_NOP;
1204         data.binder = BindArg;
1205         data.u.arg.fun = fun;
1206 
1207         do {
1208             tt = js_GetToken(cx, ts);
1209             switch (tt) {
1210 #if JS_HAS_DESTRUCTURING
1211               case TOK_LB:
1212               case TOK_LC:
1213               {
1214                 JSParseNode *lhs, *rhs;
1215                 jsint slot;
1216 
1217                 /*
1218                  * A destructuring formal parameter turns into one or more
1219                  * local variables initialized from properties of a single
1220                  * anonymous positional parameter, so here we must tweak our
1221                  * binder and its data.
1222                  */
1223                 data.op = JSOP_DEFVAR;
1224                 data.binder = BindDestructuringArg;
1225                 data.u.var.clasp = &js_FunctionClass;
1226                 data.u.var.getter = js_GetLocalVariable;
1227                 data.u.var.setter = js_SetLocalVariable;
1228                 data.u.var.attrs = JSPROP_PERMANENT;
1229 
1230                 /*
1231                  * Temporarily transfer the owneship of the recycle list to
1232                  * funtc. See bug 313967.
1233                  */
1234                 funtc.nodeList = tc->nodeList;
1235                 tc->nodeList = NULL;
1236                 lhs = DestructuringExpr(cx, &data, &funtc, tt);
1237                 tc->nodeList = funtc.nodeList;
1238                 funtc.nodeList = NULL;
1239                 if (!lhs)
1240                     return NULL;
1241 
1242                 /*
1243                  * Restore the formal parameter binder in case there are more
1244                  * non-destructuring formals in the parameter list.
1245                  */
1246                 data.binder = BindArg;
1247 
1248                 /*
1249                  * Adjust fun->nargs to count the single anonymous positional
1250                  * parameter that is to be destructured.
1251                  */
1252                 slot = fun->nargs;
1253                 if (!BumpFormalCount(cx, fun))
1254                     return NULL;
1255 
1256                 /*
1257                  * Synthesize a destructuring assignment from the single
1258                  * anonymous positional parameter into the destructuring
1259                  * left-hand-side expression and accumulate it in list.
1260                  */
1261                 rhs = NewParseNode(cx, ts, PN_NAME, tc);
1262                 if (!rhs)
1263                     return NULL;
1264                 rhs->pn_type = TOK_NAME;
1265                 rhs->pn_op = JSOP_GETARG;
1266                 rhs->pn_atom = cx->runtime->atomState.emptyAtom;
1267                 rhs->pn_expr = NULL;
1268                 rhs->pn_slot = slot;
1269                 rhs->pn_attrs = 0;
1270 
1271                 item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
1272                 if (!item)
1273                     return NULL;
1274                 if (!list) {
1275                     list = NewParseNode(cx, ts, PN_LIST, tc);
1276                     if (!list)
1277                         return NULL;
1278                     list->pn_type = TOK_COMMA;
1279                     PN_INIT_LIST(list);
1280                 }
1281                 PN_APPEND(list, item);
1282                 break;
1283               }
1284 #endif /* JS_HAS_DESTRUCTURING */
1285 
1286               case TOK_NAME:
1287                 if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc))
1288                     return NULL;
1289                 break;
1290 
1291               default:
1292                 js_ReportCompileErrorNumber(cx, ts,
1293                                             JSREPORT_TS | JSREPORT_ERROR,
1294                                             JSMSG_MISSING_FORMAL);
1295                 return NULL;
1296             }
1297         } while (js_MatchToken(cx, ts, TOK_COMMA));
1298 
1299         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1300     }
1301 
1302     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1303     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1304 
1305     /*
1306      * Temporarily transfer the owneship of the recycle list to funtc.
1307      * See bug 313967.
1308      */
1309     funtc.nodeList = tc->nodeList;
1310     tc->nodeList = NULL;
1311     body = FunctionBody(cx, ts, fun, &funtc);
1312     tc->nodeList = funtc.nodeList;
1313     funtc.nodeList = NULL;
1314 
1315     if (!body)
1316         return NULL;
1317 
1318     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1319     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1320 
1321 #if JS_HAS_DESTRUCTURING
1322     /*
1323      * If there were destructuring formal parameters, prepend the initializing
1324      * comma expression that we synthesized to body.  If the body is a lexical
1325      * scope node, we must make a special TOK_BODY node, to prepend the formal
1326      * parameter destructuring code without bracing the decompilation of the
1327      * function body's lexical scope.
1328      */
1329     if (list) {
1330         if (body->pn_arity != PN_LIST) {
1331             JSParseNode *block;
1332 
1333             JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE);
1334             JS_ASSERT(body->pn_arity == PN_NAME);
1335 
1336             block = NewParseNode(cx, ts, PN_LIST, tc);
1337             if (!block)
1338                 return NULL;
1339             block->pn_type = TOK_BODY;
1340             block->pn_pos = body->pn_pos;
1341             PN_INIT_LIST_1(block, body);
1342 
1343             body = block;
1344         }
1345 
1346         item = NewParseNode(cx, ts, PN_UNARY, tc);
1347         if (!item)
1348             return NULL;
1349 
1350         item->pn_type = TOK_SEMI;
1351         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1352         item->pn_kid = list;
1353         item->pn_next = body->pn_head;
1354         body->pn_head = item;
1355         if (body->pn_tail == &body->pn_head)
1356             body->pn_tail = &item->pn_next;
1357         ++body->pn_count;
1358     }
1359 #endif
1360 
1361     /*
1362      * If we collected flags that indicate nested heavyweight functions, or
1363      * this function contains heavyweight-making statements (references to
1364      * __parent__ or __proto__; use of with, eval, import, or export; and
1365      * assignment to arguments), flag the function as heavyweight (requiring
1366      * a call object per invocation).
1367      */
1368     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1369         fun->flags |= JSFUN_HEAVYWEIGHT;
1370         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1371     } else {
1372         /*
1373          * If this function is a named statement function not at top-level
1374          * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then
1375          * our enclosing function, if any, must be heavyweight.
1376          *
1377          * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1378          * so it won't be set here.  Assert that it's not.  We have to check
1379          * it later, in js_EmitTree, after js_EmitFunctionBody has traversed
1380          * the function's body
1381          */
1382         JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
1383         if (!lambda && funAtom && !AT_TOP_LEVEL(tc))
1384             tc->flags |= TCF_FUN_HEAVYWEIGHT;
1385     }
1386 
1387     result = pn;
1388     if (lambda) {
1389         /*
1390          * ECMA ed. 3 standard: function expression, possibly anonymous.
1391          */
1392         op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1393     } else if (!funAtom) {
1394         /*
1395          * If this anonymous function definition is *not* embedded within a
1396          * larger expression, we treat it as an expression statement, not as
1397          * a function declaration -- and not as a syntax error (as ECMA-262
1398          * Edition 3 would have it).  Backward compatibility trumps all.
1399          */
1400         result = NewParseNode(cx, ts, PN_UNARY, tc);
1401         if (!result)
1402             return NULL;
1403         result->pn_type = TOK_SEMI;
1404         result->pn_pos = pn->pn_pos;
1405         result->pn_kid = pn;
1406         op = JSOP_ANONFUNOBJ;
1407     } else if (!AT_TOP_LEVEL(tc)) {
1408         /*
1409          * ECMA ed. 3 extension: a function expression statement not at the
1410          * top level, e.g., in a compound statement such as the "then" part
1411          * of an "if" statement, binds a closure only if control reaches that
1412          * sub-statement.
1413          */
1414         op = JSOP_CLOSURE;
1415     } else {
1416         op = JSOP_NOP;
1417     }
1418 
1419     pn->pn_funAtom = objAtom;
1420     pn->pn_op = op;
1421     pn->pn_body = body;
1422     pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
1423     pn->pn_tryCount = funtc.tryCount;
1424     TREE_CONTEXT_FINISH(&funtc);
1425     return result;
1426 }
1427 
1428 static JSParseNode *
FunctionStmt(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1429 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1430 {
1431     return FunctionDef(cx, ts, tc, JS_FALSE);
1432 }
1433 
1434 static JSParseNode *
FunctionExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1435 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1436 {
1437     return FunctionDef(cx, ts, tc, JS_TRUE);
1438 }
1439 
1440 /*
1441  * Parse the statements in a block, creating a TOK_LC node that lists the
1442  * statements' trees.  If called from block-parsing code, the caller must
1443  * match { before and } after.
1444  */
1445 static JSParseNode *
Statements(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1446 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1447 {
1448     JSParseNode *pn, *pn2, *saveBlock;
1449     JSTokenType tt;
1450 
1451     CHECK_RECURSION();
1452 
1453     pn = NewParseNode(cx, ts, PN_LIST, tc);
1454     if (!pn)
1455         return NULL;
1456     saveBlock = tc->blockNode;
1457     tc->blockNode = pn;
1458     PN_INIT_LIST(pn);
1459 
1460     ts->flags |= TSF_OPERAND;
1461     while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1462         ts->flags &= ~TSF_OPERAND;
1463         pn2 = Statement(cx, ts, tc);
1464         if (!pn2) {
1465             if (ts->flags & TSF_EOF)
1466                 ts->flags |= TSF_UNEXPECTED_EOF;
1467             return NULL;
1468         }
1469         ts->flags |= TSF_OPERAND;
1470 
1471         /* Detect a function statement for the TOK_LC case in Statement. */
1472         if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc))
1473             tc->flags |= TCF_HAS_FUNCTION_STMT;
1474 
1475         /* If compiling top-level statements, emit as we go to save space. */
1476         if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1477             if (cx->fp->fun &&
1478                 JS_HAS_STRICT_OPTION(cx) &&
1479                 (tc->flags & TCF_RETURN_EXPR)) {
1480                 /*
1481                  * Check pn2 for lack of a final return statement if it is the
1482                  * last statement in the block.
1483                  */
1484                 tt = js_PeekToken(cx, ts);
1485                 if ((tt == TOK_EOF || tt == TOK_RC) &&
1486                     !CheckFinalReturn(cx, ts, pn2)) {
1487                     tt = TOK_ERROR;
1488                     break;
1489                 }
1490 
1491                 /*
1492                  * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1493                  * CheckFinalReturn again.
1494                  */
1495                 tc->flags &= ~TCF_RETURN_EXPR;
1496             }
1497             if (!js_FoldConstants(cx, pn2, tc) ||
1498                 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1499                 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1500                 tt = TOK_ERROR;
1501                 break;
1502             }
1503             RecycleTree(pn2, tc);
1504         } else {
1505             PN_APPEND(pn, pn2);
1506         }
1507     }
1508 
1509     /*
1510      * Handle the case where there was a let declaration under this block.  If
1511      * it replaced tc->blockNode with a new block node then we must refresh pn
1512      * and then restore tc->blockNode.
1513      */
1514     if (tc->blockNode != pn)
1515         pn = tc->blockNode;
1516     tc->blockNode = saveBlock;
1517 
1518     ts->flags &= ~TSF_OPERAND;
1519     if (tt == TOK_ERROR)
1520         return NULL;
1521 
1522     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1523     return pn;
1524 }
1525 
1526 static JSParseNode *
Condition(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1527 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1528 {
1529     JSParseNode *pn, *pn2;
1530 
1531     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1532     pn = Expr(cx, ts, tc);
1533     if (!pn)
1534         return NULL;
1535     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1536 
1537     /*
1538      * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1539      * greater precedence than ==.
1540      * XXX not ECMA, but documented in several books -- now a strict warning.
1541      */
1542     if (pn->pn_type == TOK_ASSIGN &&
1543         pn->pn_op == JSOP_NOP &&
1544         pn->pn_right->pn_type > TOK_EQOP)
1545     {
1546         JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
1547         if (!js_ReportCompileErrorNumber(cx, ts,
1548                                          JSREPORT_TS |
1549                                          JSREPORT_WARNING |
1550                                          JSREPORT_STRICT,
1551                                          JSMSG_EQUAL_AS_ASSIGN,
1552                                          rewrite
1553                                          ? "\nAssuming equality test"
1554                                          : "")) {
1555             return NULL;
1556         }
1557         if (rewrite) {
1558             pn->pn_type = TOK_EQOP;
1559             pn->pn_op = (JSOp)cx->jsop_eq;
1560             pn2 = pn->pn_left;
1561             switch (pn2->pn_op) {
1562               case JSOP_SETNAME:
1563                 pn2->pn_op = JSOP_NAME;
1564                 break;
1565               case JSOP_SETPROP:
1566                 pn2->pn_op = JSOP_GETPROP;
1567                 break;
1568               case JSOP_SETELEM:
1569                 pn2->pn_op = JSOP_GETELEM;
1570                 break;
1571               default:
1572                 JS_ASSERT(0);
1573             }
1574         }
1575     }
1576     return pn;
1577 }
1578 
1579 static JSBool
MatchLabel(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)1580 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1581 {
1582     JSAtom *label;
1583     JSTokenType tt;
1584 
1585     tt = js_PeekTokenSameLine(cx, ts);
1586     if (tt == TOK_ERROR)
1587         return JS_FALSE;
1588     if (tt == TOK_NAME) {
1589         (void) js_GetToken(cx, ts);
1590         label = CURRENT_TOKEN(ts).t_atom;
1591     } else {
1592         label = NULL;
1593     }
1594     pn->pn_atom = label;
1595     return JS_TRUE;
1596 }
1597 
1598 #if JS_HAS_EXPORT_IMPORT
1599 static JSParseNode *
ImportExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1600 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1601 {
1602     JSParseNode *pn, *pn2;
1603     JSTokenType tt;
1604 
1605     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1606     pn = NewParseNode(cx, ts, PN_NAME, tc);
1607     if (!pn)
1608         return NULL;
1609     pn->pn_op = JSOP_NAME;
1610     pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1611     pn->pn_expr = NULL;
1612     pn->pn_slot = -1;
1613     pn->pn_attrs = 0;
1614 
1615     ts->flags |= TSF_OPERAND;
1616     while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1617         ts->flags &= ~TSF_OPERAND;
1618         if (pn->pn_op == JSOP_IMPORTALL)
1619             goto bad_import;
1620 
1621         if (tt == TOK_DOT) {
1622             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1623             if (!pn2)
1624                 return NULL;
1625             ts->flags |= TSF_KEYWORD_IS_NAME;
1626             if (js_MatchToken(cx, ts, TOK_STAR)) {
1627                 pn2->pn_op = JSOP_IMPORTALL;
1628                 pn2->pn_atom = NULL;
1629                 pn2->pn_slot = -1;
1630                 pn2->pn_attrs = 0;
1631             } else {
1632                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1633                 pn2->pn_op = JSOP_GETPROP;
1634                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1635                 pn2->pn_slot = -1;
1636                 pn2->pn_attrs = 0;
1637             }
1638             ts->flags &= ~TSF_KEYWORD_IS_NAME;
1639             pn2->pn_expr = pn;
1640             pn2->pn_pos.begin = pn->pn_pos.begin;
1641             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1642         } else {
1643             /* Make a TOK_LB binary node. */
1644             pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
1645             if (!pn2)
1646                 return NULL;
1647 
1648             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1649         }
1650 
1651         pn = pn2;
1652         ts->flags |= TSF_OPERAND;
1653     }
1654     ts->flags &= ~TSF_OPERAND;
1655     if (tt == TOK_ERROR)
1656         return NULL;
1657     js_UngetToken(ts);
1658 
1659     switch (pn->pn_op) {
1660       case JSOP_GETPROP:
1661         pn->pn_op = JSOP_IMPORTPROP;
1662         break;
1663       case JSOP_GETELEM:
1664         pn->pn_op = JSOP_IMPORTELEM;
1665         break;
1666       case JSOP_IMPORTALL:
1667         break;
1668       default:
1669         goto bad_import;
1670     }
1671     return pn;
1672 
1673   bad_import:
1674     js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1675                                 JSMSG_BAD_IMPORT);
1676     return NULL;
1677 }
1678 #endif /* JS_HAS_EXPORT_IMPORT */
1679 
1680 static JSBool
BindLet(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)1681 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1682 {
1683     JSObject *blockObj;
1684     JSScopeProperty *sprop;
1685     JSAtomListElement *ale;
1686 
1687     blockObj = data->obj;
1688     sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
1689     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1690     if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
1691         const char *name;
1692 
1693         if (sprop) {
1694             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1695             JS_ASSERT((uint16)sprop->shortid < data->u.let.index);
1696         }
1697 
1698         name = js_AtomToPrintableString(cx, atom);
1699         if (name) {
1700             js_ReportCompileErrorNumber(cx,
1701                                         BIND_DATA_REPORT_ARGS(data,
1702                                                               JSREPORT_ERROR),
1703                                         JSMSG_REDECLARED_VAR,
1704                                         (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
1705                                         ? js_const_str
1706                                         : "variable",
1707                                         name);
1708         }
1709         return JS_FALSE;
1710     }
1711 
1712     if (data->u.let.index == JS_BIT(16)) {
1713         js_ReportCompileErrorNumber(cx,
1714                                     BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR),
1715                                     data->u.let.overflow);
1716         return JS_FALSE;
1717     }
1718 
1719     /* Use JSPROP_ENUMERATE to aid the disassembler. */
1720     return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
1721                                    JSVAL_VOID, NULL, NULL,
1722                                    JSPROP_ENUMERATE | JSPROP_PERMANENT,
1723                                    SPROP_HAS_SHORTID,
1724                                    (intN)data->u.let.index++,
1725                                    NULL);
1726 }
1727 
1728 static JSBool
BindVarOrConst(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)1729 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1730 {
1731     JSStmtInfo *stmt;
1732     JSAtomListElement *ale;
1733     JSOp op, prevop;
1734     const char *name;
1735     JSFunction *fun;
1736     JSObject *obj, *pobj;
1737     JSProperty *prop;
1738     JSBool ok;
1739     JSPropertyOp getter, setter;
1740     JSScopeProperty *sprop;
1741 
1742     stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE);
1743     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1744     op = data->op;
1745     if ((stmt && stmt->type != STMT_WITH) || ale) {
1746         prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
1747         if (JS_HAS_STRICT_OPTION(cx)
1748             ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
1749             : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
1750             name = js_AtomToPrintableString(cx, atom);
1751             if (!name ||
1752                 !js_ReportCompileErrorNumber(cx,
1753                                              BIND_DATA_REPORT_ARGS(data,
1754                                                  (op != JSOP_DEFCONST &&
1755                                                   prevop != JSOP_DEFCONST)
1756                                                  ? JSREPORT_WARNING |
1757                                                    JSREPORT_STRICT
1758                                                  : JSREPORT_ERROR),
1759                                              JSMSG_REDECLARED_VAR,
1760                                              (prevop == JSOP_DEFFUN ||
1761                                               prevop == JSOP_CLOSURE)
1762                                              ? js_function_str
1763                                              : (prevop == JSOP_DEFCONST)
1764                                              ? js_const_str
1765                                              : js_var_str,
1766                                              name)) {
1767                 return JS_FALSE;
1768             }
1769         }
1770         if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
1771             tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1772     }
1773     if (!ale) {
1774         ale = js_IndexAtom(cx, atom, &tc->decls);
1775         if (!ale)
1776             return JS_FALSE;
1777     }
1778     ALE_SET_JSOP(ale, op);
1779 
1780     fun = data->u.var.fun;
1781     obj = data->obj;
1782     if (!fun) {
1783         /* Don't lookup global variables at compile time. */
1784         prop = NULL;
1785     } else {
1786         JS_ASSERT(OBJ_IS_NATIVE(obj));
1787         if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
1788                                      &pobj, &prop)) {
1789             return JS_FALSE;
1790         }
1791     }
1792 
1793     ok = JS_TRUE;
1794     getter = data->u.var.getter;
1795     setter = data->u.var.setter;
1796 
1797     if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
1798         sprop = (JSScopeProperty *)prop;
1799         if (sprop->getter == js_GetArgument) {
1800             name  = js_AtomToPrintableString(cx, atom);
1801             if (!name) {
1802                 ok = JS_FALSE;
1803             } else if (op == JSOP_DEFCONST) {
1804                 js_ReportCompileErrorNumber(cx,
1805                                             BIND_DATA_REPORT_ARGS(data,
1806                                                 JSREPORT_ERROR),
1807                                             JSMSG_REDECLARED_PARAM,
1808                                             name);
1809                 ok = JS_FALSE;
1810             } else {
1811                 getter = js_GetArgument;
1812                 setter = js_SetArgument;
1813                 ok = js_ReportCompileErrorNumber(cx,
1814                                                  BIND_DATA_REPORT_ARGS(data,
1815                                                      JSREPORT_WARNING |
1816                                                      JSREPORT_STRICT),
1817                                                  JSMSG_VAR_HIDES_ARG,
1818                                                  name);
1819             }
1820         } else {
1821             JS_ASSERT(getter == js_GetLocalVariable);
1822 
1823             if (fun) {
1824                 /* Not an argument, must be a redeclared local var. */
1825                 if (data->u.var.clasp == &js_FunctionClass) {
1826                     JS_ASSERT(sprop->getter == js_GetLocalVariable);
1827                     JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
1828                               (uint16) sprop->shortid < fun->u.i.nvars);
1829                 } else if (data->u.var.clasp == &js_CallClass) {
1830                     if (sprop->getter == js_GetCallVariable) {
1831                         /*
1832                          * Referencing a name introduced by a var statement in
1833                          * the enclosing function.  Check that the slot number
1834                          * we have is in range.
1835                          */
1836                         JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
1837                                   (uint16) sprop->shortid < fun->u.i.nvars);
1838                     } else {
1839                         /*
1840                          * A variable introduced through another eval: don't
1841                          * use the special getters and setters since we can't
1842                          * allocate a slot in the frame.
1843                          */
1844                         getter = sprop->getter;
1845                         setter = sprop->setter;
1846                     }
1847                 }
1848 
1849                 /* Override the old getter and setter, to handle eval. */
1850                 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
1851                                                      0, sprop->attrs,
1852                                                      getter, setter);
1853                 if (!sprop)
1854                     ok = JS_FALSE;
1855             }
1856         }
1857         if (prop)
1858             OBJ_DROP_PROPERTY(cx, pobj, prop);
1859     } else {
1860         /*
1861          * Property not found in current variable scope: we have not seen this
1862          * variable before.  Define a new local variable by adding a property
1863          * to the function's scope, allocating one slot in the function's vars
1864          * frame.  Global variables and any locals declared in with statement
1865          * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes
1866          * generated for slot-less vars.
1867          */
1868         sprop = NULL;
1869         if (prop) {
1870             OBJ_DROP_PROPERTY(cx, pobj, prop);
1871             prop = NULL;
1872         }
1873 
1874         if (cx->fp->scopeChain == obj &&
1875             !js_InWithStatement(tc) &&
1876             !BindLocalVariable(cx, data, atom)) {
1877             return JS_FALSE;
1878         }
1879     }
1880     return ok;
1881 }
1882 
1883 #if JS_HAS_DESTRUCTURING
1884 
1885 static JSBool
BindDestructuringVar(JSContext * cx,BindData * data,JSParseNode * pn,JSTreeContext * tc)1886 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
1887                      JSTreeContext *tc)
1888 {
1889     JSAtom *atom;
1890 
1891     /*
1892      * Destructuring is a form of assignment, so just as for an initialized
1893      * simple variable, we must check for assignment to 'arguments' and flag
1894      * the enclosing function (if any) as heavyweight.
1895      */
1896     JS_ASSERT(pn->pn_type == TOK_NAME);
1897     atom = pn->pn_atom;
1898     if (atom == cx->runtime->atomState.argumentsAtom)
1899         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1900 
1901     data->pn = pn;
1902     if (!data->binder(cx, data, atom, tc))
1903         return JS_FALSE;
1904     data->pn = NULL;
1905 
1906     /*
1907      * Select the appropriate name-setting opcode, which may be specialized
1908      * further for local variable and argument slot optimizations.  At this
1909      * point, we can't select the optimal final opcode, yet we must preserve
1910      * the CONST bit and convey "set", not "get".
1911      */
1912     pn->pn_op = (data->op == JSOP_DEFCONST)
1913                 ? JSOP_SETCONST
1914                 : JSOP_SETNAME;
1915     pn->pn_attrs = data->u.var.attrs;
1916     return JS_TRUE;
1917 }
1918 
1919 /*
1920  * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
1921  * LHS expression except a destructuring initialiser, and R is on the stack.
1922  * Because R is already evaluated, the usual LHS-specialized bytecodes won't
1923  * work.  After pushing R[P] we need to evaluate Q's "reference base" QB and
1924  * then push its property name QN.  At this point the stack looks like
1925  *
1926  *   [... R, R[P], QB, QN]
1927  *
1928  * We need to set QB[QN] = R[P].  This is a job for JSOP_ENUMELEM, which takes
1929  * its operands with left-hand side above right-hand side:
1930  *
1931  *   [rval, lval, xval]
1932  *
1933  * and pops all three values, setting lval[xval] = rval.  But we cannot select
1934  * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
1935  * which can be optimized further.  So we select JSOP_SETNAME.
1936  */
1937 static JSBool
BindDestructuringLHS(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)1938 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
1939 {
1940     while (pn->pn_type == TOK_RP)
1941         pn = pn->pn_kid;
1942 
1943     switch (pn->pn_type) {
1944       case TOK_NAME:
1945         if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
1946             tc->flags |= TCF_FUN_HEAVYWEIGHT;
1947         /* FALL THROUGH */
1948       case TOK_DOT:
1949       case TOK_LB:
1950         pn->pn_op = JSOP_SETNAME;
1951         break;
1952 
1953 #if JS_HAS_LVALUE_RETURN
1954       case TOK_LP:
1955         JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
1956         pn->pn_op = JSOP_SETCALL;
1957         break;
1958 #endif
1959 
1960 #if JS_HAS_XML_SUPPORT
1961       case TOK_UNARYOP:
1962         if (pn->pn_op == JSOP_XMLNAME) {
1963             pn->pn_op = JSOP_BINDXMLNAME;
1964             break;
1965         }
1966         /* FALL THROUGH */
1967 #endif
1968 
1969       default:
1970         js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
1971                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
1972         return JS_FALSE;
1973     }
1974 
1975     return JS_TRUE;
1976 }
1977 
1978 typedef struct FindPropValData {
1979     uint32          numvars;    /* # of destructuring vars in left side */
1980     uint32          maxstep;    /* max # of steps searching right side */
1981     JSDHashTable    table;      /* hash table for O(1) right side search */
1982 } FindPropValData;
1983 
1984 typedef struct FindPropValEntry {
1985     JSDHashEntryHdr hdr;
1986     JSParseNode     *pnkey;
1987     JSParseNode     *pnval;
1988 } FindPropValEntry;
1989 
1990 #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \
1991     JS_ASSERT((pnkey)->pn_arity == PN_NULLARY &&                              \
1992               ((pnkey)->pn_type == TOK_NUMBER ||                              \
1993                (pnkey)->pn_type == TOK_STRING ||                              \
1994                (pnkey)->pn_type == TOK_NAME))
1995 
1996 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
HashFindPropValKey(JSDHashTable * table,const void * key)1997 HashFindPropValKey(JSDHashTable *table, const void *key)
1998 {
1999     const JSParseNode *pnkey = (const JSParseNode *)key;
2000 
2001     ASSERT_VALID_PROPERTY_KEY(pnkey);
2002     return (pnkey->pn_type == TOK_NUMBER)
2003            ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
2004                               JSDOUBLE_LO32(pnkey->pn_dval))
2005            : (JSDHashNumber) pnkey->pn_atom->number;
2006 }
2007 
2008 JS_STATIC_DLL_CALLBACK(JSBool)
MatchFindPropValEntry(JSDHashTable * table,const JSDHashEntryHdr * entry,const void * key)2009 MatchFindPropValEntry(JSDHashTable *table,
2010                       const JSDHashEntryHdr *entry,
2011                       const void *key)
2012 {
2013     const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
2014     const JSParseNode *pnkey = (const JSParseNode *)key;
2015 
2016     ASSERT_VALID_PROPERTY_KEY(pnkey);
2017     return pnkey->pn_type == fpve->pnkey->pn_type &&
2018            ((pnkey->pn_type == TOK_NUMBER)
2019             ? pnkey->pn_dval == fpve->pnkey->pn_dval
2020             : pnkey->pn_atom == fpve->pnkey->pn_atom);
2021 }
2022 
2023 static const JSDHashTableOps FindPropValOps = {
2024     JS_DHashAllocTable,
2025     JS_DHashFreeTable,
2026     JS_DHashGetKeyStub,
2027     HashFindPropValKey,
2028     MatchFindPropValEntry,
2029     JS_DHashMoveEntryStub,
2030     JS_DHashClearEntryStub,
2031     JS_DHashFinalizeStub,
2032     NULL
2033 };
2034 
2035 #define STEP_HASH_THRESHOLD     10
2036 #define BIG_DESTRUCTURING        5
2037 #define BIG_OBJECT_INIT         20
2038 
2039 static JSParseNode *
FindPropertyValue(JSParseNode * pn,JSParseNode * pnid,FindPropValData * data)2040 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
2041 {
2042     FindPropValEntry *entry;
2043     JSParseNode *pnhit, *pnprop, *pnkey;
2044     uint32 step;
2045 
2046     /* If we have a hash table, use it as the sole source of truth. */
2047     if (data->table.ops) {
2048         entry = (FindPropValEntry *)
2049                 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
2050         return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
2051     }
2052 
2053     /* If pn is not an object initialiser node, we can't do anything here. */
2054     if (pn->pn_type != TOK_RC)
2055         return NULL;
2056 
2057     /*
2058      * We must search all the way through pn's list, to handle the case of an
2059      * id duplicated for two or more property initialisers.
2060      */
2061     pnhit = NULL;
2062     step = 0;
2063     ASSERT_VALID_PROPERTY_KEY(pnid);
2064     if (pnid->pn_type == TOK_NUMBER) {
2065         for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
2066             JS_ASSERT(pnprop->pn_type == TOK_COLON);
2067             if (pnprop->pn_op == JSOP_NOP) {
2068                 pnkey = pnprop->pn_left;
2069                 ASSERT_VALID_PROPERTY_KEY(pnkey);
2070                 if (pnkey->pn_type == TOK_NUMBER &&
2071                     pnkey->pn_dval == pnid->pn_dval) {
2072                     pnhit = pnprop;
2073                 }
2074                 ++step;
2075             }
2076         }
2077     } else {
2078         for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
2079             JS_ASSERT(pnprop->pn_type == TOK_COLON);
2080             if (pnprop->pn_op == JSOP_NOP) {
2081                 pnkey = pnprop->pn_left;
2082                 ASSERT_VALID_PROPERTY_KEY(pnkey);
2083                 if (pnkey->pn_type == pnid->pn_type &&
2084                     pnkey->pn_atom == pnid->pn_atom) {
2085                     pnhit = pnprop;
2086                 }
2087                 ++step;
2088             }
2089         }
2090     }
2091     if (!pnhit)
2092         return NULL;
2093 
2094     /* Hit via full search -- see whether it's time to create the hash table. */
2095     JS_ASSERT(!data->table.ops);
2096     if (step > data->maxstep) {
2097         data->maxstep = step;
2098         if (step >= STEP_HASH_THRESHOLD &&
2099             data->numvars >= BIG_DESTRUCTURING &&
2100             pn->pn_count >= BIG_OBJECT_INIT &&
2101             JS_DHashTableInit(&data->table, &FindPropValOps, pn,
2102                               sizeof(FindPropValEntry), pn->pn_count)) {
2103 
2104             for (pn = pn->pn_head; pn; pn = pn->pn_next) {
2105                 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
2106                 entry = (FindPropValEntry *)
2107                         JS_DHashTableOperate(&data->table, pn->pn_left,
2108                                              JS_DHASH_ADD);
2109                 entry->pnval = pn->pn_right;
2110             }
2111         }
2112     }
2113     return pnhit->pn_right;
2114 }
2115 
2116 /*
2117  * If data is null, the caller is AssignExpr and instead of binding variables,
2118  * we specialize lvalues in the propery value positions of the left-hand side.
2119  * If right is null, just check for well-formed lvalues.
2120  */
2121 static JSBool
CheckDestructuring(JSContext * cx,BindData * data,JSParseNode * left,JSParseNode * right,JSTreeContext * tc)2122 CheckDestructuring(JSContext *cx, BindData *data,
2123                    JSParseNode *left, JSParseNode *right,
2124                    JSTreeContext *tc)
2125 {
2126     JSBool ok;
2127     FindPropValData fpvd;
2128     JSParseNode *lhs, *rhs, *pn, *pn2;
2129 
2130     if (left->pn_type == TOK_ARRAYCOMP) {
2131         js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR,
2132                                     JSMSG_ARRAY_COMP_LEFTSIDE);
2133         return JS_FALSE;
2134     }
2135 
2136     ok = JS_TRUE;
2137     fpvd.table.ops = NULL;
2138     lhs = left->pn_head;
2139     if (lhs && lhs->pn_type == TOK_DEFSHARP) {
2140         pn = lhs;
2141         goto no_var_name;
2142     }
2143 
2144     if (left->pn_type == TOK_RB) {
2145         rhs = (right && right->pn_type == left->pn_type)
2146               ? right->pn_head
2147               : NULL;
2148 
2149         while (lhs) {
2150             pn = lhs, pn2 = rhs;
2151             if (!data) {
2152                 /* Skip parenthesization if not in a variable declaration. */
2153                 while (pn->pn_type == TOK_RP)
2154                     pn = pn->pn_kid;
2155                 if (pn2) {
2156                     while (pn2->pn_type == TOK_RP)
2157                         pn2 = pn2->pn_kid;
2158                 }
2159             }
2160 
2161             /* Nullary comma is an elision; binary comma is an expression.*/
2162             if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
2163                 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2164                     ok = CheckDestructuring(cx, data, pn, pn2, tc);
2165                 } else {
2166                     if (data) {
2167                         if (pn->pn_type != TOK_NAME)
2168                             goto no_var_name;
2169 
2170                         ok = BindDestructuringVar(cx, data, pn, tc);
2171                     } else {
2172                         ok = BindDestructuringLHS(cx, pn, tc);
2173                     }
2174                 }
2175                 if (!ok)
2176                     goto out;
2177             }
2178 
2179             lhs = lhs->pn_next;
2180             if (rhs)
2181                 rhs = rhs->pn_next;
2182         }
2183     } else {
2184         JS_ASSERT(left->pn_type == TOK_RC);
2185         fpvd.numvars = left->pn_count;
2186         fpvd.maxstep = 0;
2187         rhs = NULL;
2188 
2189         while (lhs) {
2190             JS_ASSERT(lhs->pn_type == TOK_COLON);
2191             pn = lhs->pn_right;
2192             if (!data) {
2193                 /* Skip parenthesization if not in a variable declaration. */
2194                 while (pn->pn_type == TOK_RP)
2195                     pn = pn->pn_kid;
2196             }
2197 
2198             if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2199                 if (right) {
2200                     rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
2201                     if (rhs && !data) {
2202                         while (rhs->pn_type == TOK_RP)
2203                             rhs = rhs->pn_kid;
2204                     }
2205                 }
2206 
2207                 ok = CheckDestructuring(cx, data, pn, rhs, tc);
2208             } else if (data) {
2209                 if (pn->pn_type != TOK_NAME)
2210                     goto no_var_name;
2211 
2212                 ok = BindDestructuringVar(cx, data, pn, tc);
2213             } else {
2214                 ok = BindDestructuringLHS(cx, pn, tc);
2215             }
2216             if (!ok)
2217                 goto out;
2218 
2219             lhs = lhs->pn_next;
2220         }
2221     }
2222 
2223 out:
2224     if (fpvd.table.ops)
2225         JS_DHashTableFinish(&fpvd.table);
2226     return ok;
2227 
2228 no_var_name:
2229     js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
2230                                 JSMSG_NO_VARIABLE_NAME);
2231     ok = JS_FALSE;
2232     goto out;
2233 }
2234 
2235 static JSParseNode *
DestructuringExpr(JSContext * cx,BindData * data,JSTreeContext * tc,JSTokenType tt)2236 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
2237                   JSTokenType tt)
2238 {
2239     JSParseNode *pn;
2240 
2241     pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE);
2242     if (!pn)
2243         return NULL;
2244     if (!CheckDestructuring(cx, data, pn, NULL, tc))
2245         return NULL;
2246     return pn;
2247 }
2248 
2249 #endif /* JS_HAS_DESTRUCTURING */
2250 
2251 extern const char js_with_statement_str[];
2252 
2253 static JSParseNode *
ContainsStmt(JSParseNode * pn,JSTokenType tt)2254 ContainsStmt(JSParseNode *pn, JSTokenType tt)
2255 {
2256     JSParseNode *pn2, *pnt;
2257 
2258     if (!pn)
2259         return NULL;
2260     if (pn->pn_type == tt)
2261         return pn;
2262     switch (pn->pn_arity) {
2263       case PN_LIST:
2264         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2265             pnt = ContainsStmt(pn2, tt);
2266             if (pnt)
2267                 return pnt;
2268         }
2269         break;
2270       case PN_TERNARY:
2271         pnt = ContainsStmt(pn->pn_kid1, tt);
2272         if (pnt)
2273             return pnt;
2274         pnt = ContainsStmt(pn->pn_kid2, tt);
2275         if (pnt)
2276             return pnt;
2277         return ContainsStmt(pn->pn_kid3, tt);
2278       case PN_BINARY:
2279         /*
2280          * Limit recursion if pn is a binary expression, which can't contain a
2281          * var statement.
2282          */
2283         if (pn->pn_op != JSOP_NOP)
2284             return NULL;
2285         pnt = ContainsStmt(pn->pn_left, tt);
2286         if (pnt)
2287             return pnt;
2288         return ContainsStmt(pn->pn_right, tt);
2289       case PN_UNARY:
2290         if (pn->pn_op != JSOP_NOP)
2291             return NULL;
2292         return ContainsStmt(pn->pn_kid, tt);
2293       case PN_NAME:
2294         return ContainsStmt(pn->pn_expr, tt);
2295       default:;
2296     }
2297     return NULL;
2298 }
2299 
2300 static JSParseNode *
ReturnOrYield(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParser operandParser)2301 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2302               JSParser operandParser)
2303 {
2304     JSTokenType tt, tt2;
2305     JSParseNode *pn, *pn2;
2306 
2307     tt = CURRENT_TOKEN(ts).type;
2308     if (!(tc->flags & TCF_IN_FUNCTION)) {
2309         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2310                                     JSMSG_BAD_RETURN_OR_YIELD,
2311 #if JS_HAS_GENERATORS
2312                                     (tt == TOK_YIELD) ? js_yield_str :
2313 #endif
2314                                     js_return_str);
2315         return NULL;
2316     }
2317 
2318     pn = NewParseNode(cx, ts, PN_UNARY, tc);
2319     if (!pn)
2320         return NULL;
2321 
2322 #if JS_HAS_GENERATORS
2323     if (tt == TOK_YIELD)
2324         tc->flags |= TCF_FUN_IS_GENERATOR;
2325 #endif
2326 
2327     /* This is ugly, but we don't want to require a semicolon. */
2328     ts->flags |= TSF_OPERAND;
2329     tt2 = js_PeekTokenSameLine(cx, ts);
2330     ts->flags &= ~TSF_OPERAND;
2331     if (tt2 == TOK_ERROR)
2332         return NULL;
2333 
2334     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
2335 #if JS_HAS_GENERATORS
2336         && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP))
2337 #endif
2338         ) {
2339         pn2 = operandParser(cx, ts, tc);
2340         if (!pn2)
2341             return NULL;
2342 #if JS_HAS_GENERATORS
2343         if (tt == TOK_RETURN)
2344 #endif
2345             tc->flags |= TCF_RETURN_EXPR;
2346         pn->pn_pos.end = pn2->pn_pos.end;
2347         pn->pn_kid = pn2;
2348     } else {
2349 #if JS_HAS_GENERATORS
2350         if (tt == TOK_RETURN)
2351 #endif
2352             tc->flags |= TCF_RETURN_VOID;
2353         pn->pn_kid = NULL;
2354     }
2355 
2356     if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
2357         /* As in Python (see PEP-255), disallow return v; in generators. */
2358         ReportBadReturn(cx, ts, JSREPORT_ERROR,
2359                         JSMSG_BAD_GENERATOR_RETURN,
2360                         JSMSG_BAD_ANON_GENERATOR_RETURN);
2361         return NULL;
2362     }
2363 
2364     if (JS_HAS_STRICT_OPTION(cx) &&
2365         (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
2366         !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
2367                          JSMSG_NO_RETURN_VALUE,
2368                          JSMSG_ANON_NO_RETURN_VALUE)) {
2369         return NULL;
2370     }
2371 
2372     return pn;
2373 }
2374 
2375 static JSParseNode *
PushLexicalScope(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSStmtInfo * stmtInfo)2376 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2377                  JSStmtInfo *stmtInfo)
2378 {
2379     JSParseNode *pn;
2380     JSObject *obj;
2381     JSAtom *atom;
2382 
2383     pn = NewParseNode(cx, ts, PN_NAME, tc);
2384     if (!pn)
2385         return NULL;
2386 
2387     obj = js_NewBlockObject(cx);
2388     if (!obj)
2389         return NULL;
2390 
2391     atom = js_AtomizeObject(cx, obj, 0);
2392     if (!atom)
2393         return NULL;
2394 
2395     js_PushBlockScope(tc, stmtInfo, atom, -1);
2396     pn->pn_type = TOK_LEXICALSCOPE;
2397     pn->pn_op = JSOP_LEAVEBLOCK;
2398     pn->pn_atom = atom;
2399     pn->pn_expr = NULL;
2400     pn->pn_slot = -1;
2401     pn->pn_attrs = 0;
2402     return pn;
2403 }
2404 
2405 #if JS_HAS_BLOCK_SCOPE
2406 
2407 static JSParseNode *
LetBlock(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool statement)2408 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
2409 {
2410     JSParseNode *pn, *pnblock, *pnlet;
2411     JSStmtInfo stmtInfo;
2412 
2413     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
2414 
2415     /* Create the let binary node. */
2416     pnlet = NewParseNode(cx, ts, PN_BINARY, tc);
2417     if (!pnlet)
2418         return NULL;
2419 
2420     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
2421 
2422     /* This is a let block or expression of the form: let (a, b, c) .... */
2423     pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
2424     if (!pnblock)
2425         return NULL;
2426     pn = pnblock;
2427     pn->pn_expr = pnlet;
2428 
2429     pnlet->pn_left = Variables(cx, ts, tc);
2430     if (!pnlet->pn_left)
2431         return NULL;
2432     pnlet->pn_left->pn_extra = PNX_POPVAR;
2433 
2434     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
2435 
2436     ts->flags |= TSF_OPERAND;
2437     if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
2438         /*
2439          * If this is really an expression in let statement guise, then we
2440          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2441          * the return value of the expression.
2442          */
2443         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2444         if (!pn)
2445             return NULL;
2446         pn->pn_type = TOK_SEMI;
2447         pn->pn_num = -1;
2448         pn->pn_kid = pnblock;
2449 
2450         statement = JS_FALSE;
2451     }
2452     ts->flags &= ~TSF_OPERAND;
2453 
2454     if (statement) {
2455         pnlet->pn_right = Statements(cx, ts, tc);
2456         if (!pnlet->pn_right)
2457             return NULL;
2458         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2459     } else {
2460         /*
2461          * Change pnblock's opcode to the variant that propagates the last
2462          * result down after popping the block, and clear statement.
2463          */
2464         pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
2465         pnlet->pn_right = Expr(cx, ts, tc);
2466         if (!pnlet->pn_right)
2467             return NULL;
2468     }
2469 
2470     js_PopStatement(tc);
2471     return pn;
2472 }
2473 
2474 #endif /* JS_HAS_BLOCK_SCOPE */
2475 
2476 static JSParseNode *
Statement(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)2477 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2478 {
2479     JSTokenType tt;
2480     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
2481     JSStmtInfo stmtInfo, *stmt, *stmt2;
2482     JSAtom *label;
2483 
2484     CHECK_RECURSION();
2485 
2486     ts->flags |= TSF_OPERAND;
2487     tt = js_GetToken(cx, ts);
2488     ts->flags &= ~TSF_OPERAND;
2489 
2490 #if JS_HAS_GETTER_SETTER
2491     if (tt == TOK_NAME) {
2492         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2493         if (tt == TOK_ERROR)
2494             return NULL;
2495     }
2496 #endif
2497 
2498     switch (tt) {
2499 #if JS_HAS_EXPORT_IMPORT
2500       case TOK_EXPORT:
2501         pn = NewParseNode(cx, ts, PN_LIST, tc);
2502         if (!pn)
2503             return NULL;
2504         PN_INIT_LIST(pn);
2505         if (js_MatchToken(cx, ts, TOK_STAR)) {
2506             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
2507             if (!pn2)
2508                 return NULL;
2509             PN_APPEND(pn, pn2);
2510         } else {
2511             do {
2512                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
2513                 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2514                 if (!pn2)
2515                     return NULL;
2516                 pn2->pn_op = JSOP_NAME;
2517                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2518                 pn2->pn_expr = NULL;
2519                 pn2->pn_slot = -1;
2520                 pn2->pn_attrs = 0;
2521                 PN_APPEND(pn, pn2);
2522             } while (js_MatchToken(cx, ts, TOK_COMMA));
2523         }
2524         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2525         tc->flags |= TCF_FUN_HEAVYWEIGHT;
2526         break;
2527 
2528       case TOK_IMPORT:
2529         pn = NewParseNode(cx, ts, PN_LIST, tc);
2530         if (!pn)
2531             return NULL;
2532         PN_INIT_LIST(pn);
2533         do {
2534             pn2 = ImportExpr(cx, ts, tc);
2535             if (!pn2)
2536                 return NULL;
2537             PN_APPEND(pn, pn2);
2538         } while (js_MatchToken(cx, ts, TOK_COMMA));
2539         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2540         tc->flags |= TCF_FUN_HEAVYWEIGHT;
2541         break;
2542 #endif /* JS_HAS_EXPORT_IMPORT */
2543 
2544       case TOK_FUNCTION:
2545 #if JS_HAS_XML_SUPPORT
2546         ts->flags |= TSF_KEYWORD_IS_NAME;
2547         tt = js_PeekToken(cx, ts);
2548         ts->flags &= ~TSF_KEYWORD_IS_NAME;
2549         if (tt == TOK_DBLCOLON)
2550             goto expression;
2551 #endif
2552         return FunctionStmt(cx, ts, tc);
2553 
2554       case TOK_IF:
2555         /* An IF node has three kids: condition, then, and optional else. */
2556         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2557         if (!pn)
2558             return NULL;
2559         pn1 = Condition(cx, ts, tc);
2560         if (!pn1)
2561             return NULL;
2562         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
2563         pn2 = Statement(cx, ts, tc);
2564         if (!pn2)
2565             return NULL;
2566         ts->flags |= TSF_OPERAND;
2567         if (js_MatchToken(cx, ts, TOK_ELSE)) {
2568             ts->flags &= ~TSF_OPERAND;
2569             stmtInfo.type = STMT_ELSE;
2570             pn3 = Statement(cx, ts, tc);
2571             if (!pn3)
2572                 return NULL;
2573             pn->pn_pos.end = pn3->pn_pos.end;
2574         } else {
2575             ts->flags &= ~TSF_OPERAND;
2576             pn3 = NULL;
2577             pn->pn_pos.end = pn2->pn_pos.end;
2578         }
2579         js_PopStatement(tc);
2580         pn->pn_kid1 = pn1;
2581         pn->pn_kid2 = pn2;
2582         pn->pn_kid3 = pn3;
2583         return pn;
2584 
2585       case TOK_SWITCH:
2586       {
2587         JSParseNode *pn5, *saveBlock;
2588         JSBool seenDefault = JS_FALSE;
2589 
2590         pn = NewParseNode(cx, ts, PN_BINARY, tc);
2591         if (!pn)
2592             return NULL;
2593         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
2594 
2595         /* pn1 points to the switch's discriminant. */
2596         pn1 = Expr(cx, ts, tc);
2597         if (!pn1)
2598             return NULL;
2599 
2600         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
2601         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
2602 
2603         /* pn2 is a list of case nodes. The default case has pn_left == NULL */
2604         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2605         if (!pn2)
2606             return NULL;
2607         saveBlock = tc->blockNode;
2608         tc->blockNode = pn2;
2609         PN_INIT_LIST(pn2);
2610 
2611         js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
2612 
2613         while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
2614             switch (tt) {
2615               case TOK_DEFAULT:
2616                 if (seenDefault) {
2617                     js_ReportCompileErrorNumber(cx, ts,
2618                                                 JSREPORT_TS | JSREPORT_ERROR,
2619                                                 JSMSG_TOO_MANY_DEFAULTS);
2620                     return NULL;
2621                 }
2622                 seenDefault = JS_TRUE;
2623                 /* fall through */
2624 
2625               case TOK_CASE:
2626                 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
2627                 if (!pn3)
2628                     return NULL;
2629                 if (tt == TOK_DEFAULT) {
2630                     pn3->pn_left = NULL;
2631                 } else {
2632                     pn3->pn_left = Expr(cx, ts, tc);
2633                     if (!pn3->pn_left)
2634                         return NULL;
2635                 }
2636                 PN_APPEND(pn2, pn3);
2637                 if (pn2->pn_count == JS_BIT(16)) {
2638                     js_ReportCompileErrorNumber(cx, ts,
2639                                                 JSREPORT_TS | JSREPORT_ERROR,
2640                                                 JSMSG_TOO_MANY_CASES);
2641                     return NULL;
2642                 }
2643                 break;
2644 
2645               case TOK_ERROR:
2646                 return NULL;
2647 
2648               default:
2649                 js_ReportCompileErrorNumber(cx, ts,
2650                                             JSREPORT_TS | JSREPORT_ERROR,
2651                                             JSMSG_BAD_SWITCH);
2652                 return NULL;
2653             }
2654             MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
2655 
2656             pn4 = NewParseNode(cx, ts, PN_LIST, tc);
2657             if (!pn4)
2658                 return NULL;
2659             pn4->pn_type = TOK_LC;
2660             PN_INIT_LIST(pn4);
2661             ts->flags |= TSF_OPERAND;
2662             while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
2663                    tt != TOK_CASE && tt != TOK_DEFAULT) {
2664                 ts->flags &= ~TSF_OPERAND;
2665                 if (tt == TOK_ERROR)
2666                     return NULL;
2667                 pn5 = Statement(cx, ts, tc);
2668                 if (!pn5)
2669                     return NULL;
2670                 pn4->pn_pos.end = pn5->pn_pos.end;
2671                 PN_APPEND(pn4, pn5);
2672                 ts->flags |= TSF_OPERAND;
2673             }
2674             ts->flags &= ~TSF_OPERAND;
2675 
2676             /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
2677             if (pn4->pn_head)
2678                 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
2679             pn3->pn_pos.end = pn4->pn_pos.end;
2680             pn3->pn_right = pn4;
2681         }
2682 
2683         /*
2684          * Handle the case where there was a let declaration in any case in
2685          * the switch body, but not within an inner block.  If it replaced
2686          * tc->blockNode with a new block node then we must refresh pn2 and
2687          * then restore tc->blockNode.
2688          */
2689         if (tc->blockNode != pn2)
2690             pn2 = tc->blockNode;
2691         tc->blockNode = saveBlock;
2692         js_PopStatement(tc);
2693 
2694         pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2695         pn->pn_left = pn1;
2696         pn->pn_right = pn2;
2697         return pn;
2698       }
2699 
2700       case TOK_WHILE:
2701         pn = NewParseNode(cx, ts, PN_BINARY, tc);
2702         if (!pn)
2703             return NULL;
2704         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2705         pn2 = Condition(cx, ts, tc);
2706         if (!pn2)
2707             return NULL;
2708         pn->pn_left = pn2;
2709         pn2 = Statement(cx, ts, tc);
2710         if (!pn2)
2711             return NULL;
2712         js_PopStatement(tc);
2713         pn->pn_pos.end = pn2->pn_pos.end;
2714         pn->pn_right = pn2;
2715         return pn;
2716 
2717       case TOK_DO:
2718         pn = NewParseNode(cx, ts, PN_BINARY, tc);
2719         if (!pn)
2720             return NULL;
2721         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2722         pn2 = Statement(cx, ts, tc);
2723         if (!pn2)
2724             return NULL;
2725         pn->pn_left = pn2;
2726         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2727         pn2 = Condition(cx, ts, tc);
2728         if (!pn2)
2729             return NULL;
2730         js_PopStatement(tc);
2731         pn->pn_pos.end = pn2->pn_pos.end;
2732         pn->pn_right = pn2;
2733         if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) {
2734             /*
2735              * All legacy and extended versions must do automatic semicolon
2736              * insertion after do-while.  See the testcase and discussion in
2737              * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2738              */
2739             (void) js_MatchToken(cx, ts, TOK_SEMI);
2740             return pn;
2741         }
2742         break;
2743 
2744       case TOK_FOR:
2745       {
2746 #if JS_HAS_BLOCK_SCOPE
2747         JSParseNode *pnlet;
2748         JSStmtInfo blockInfo;
2749 
2750         pnlet = NULL;
2751 #endif
2752 
2753         /* A FOR node is binary, left is loop control and right is the body. */
2754         pn = NewParseNode(cx, ts, PN_BINARY, tc);
2755         if (!pn)
2756             return NULL;
2757         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2758 
2759         pn->pn_op = JSOP_FORIN;
2760         if (js_MatchToken(cx, ts, TOK_NAME)) {
2761             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
2762                 pn->pn_op = JSOP_FOREACH;
2763             else
2764                 js_UngetToken(ts);
2765         }
2766 
2767         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
2768         ts->flags |= TSF_OPERAND;
2769         tt = js_PeekToken(cx, ts);
2770         ts->flags &= ~TSF_OPERAND;
2771         if (tt == TOK_SEMI) {
2772             if (pn->pn_op == JSOP_FOREACH)
2773                 goto bad_for_each;
2774 
2775             /* No initializer -- set first kid of left sub-node to null. */
2776             pn1 = NULL;
2777         } else {
2778             /*
2779              * Set pn1 to a var list or an initializing expression.
2780              *
2781              * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2782              * of the for statement.  This flag will be used by the RelExpr
2783              * production; if it is set, then the 'in' keyword will not be
2784              * recognized as an operator, leaving it available to be parsed as
2785              * part of a for/in loop.
2786              *
2787              * A side effect of this restriction is that (unparenthesized)
2788              * expressions involving an 'in' operator are illegal in the init
2789              * clause of an ordinary for loop.
2790              */
2791             tc->flags |= TCF_IN_FOR_INIT;
2792             if (tt == TOK_VAR) {
2793                 (void) js_GetToken(cx, ts);
2794                 pn1 = Variables(cx, ts, tc);
2795 #if JS_HAS_BLOCK_SCOPE
2796             } else if (tt == TOK_LET) {
2797                 (void) js_GetToken(cx, ts);
2798                 if (js_PeekToken(cx, ts) == TOK_LP) {
2799                     pn1 = LetBlock(cx, ts, tc, JS_FALSE);
2800                     tt = TOK_LEXICALSCOPE;
2801                 } else {
2802                     pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
2803                     if (!pnlet)
2804                         return NULL;
2805                     pn1 = Variables(cx, ts, tc);
2806                 }
2807 #endif
2808             } else {
2809                 pn1 = Expr(cx, ts, tc);
2810                 if (pn1) {
2811                     while (pn1->pn_type == TOK_RP)
2812                         pn1 = pn1->pn_kid;
2813                 }
2814             }
2815             tc->flags &= ~TCF_IN_FOR_INIT;
2816             if (!pn1)
2817                 return NULL;
2818         }
2819 
2820         /*
2821          * We can be sure that it's a for/in loop if there's still an 'in'
2822          * keyword here, even if JavaScript recognizes 'in' as an operator,
2823          * as we've excluded 'in' from being parsed in RelExpr by setting
2824          * the TCF_IN_FOR_INIT flag in our JSTreeContext.
2825          */
2826         if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
2827             stmtInfo.type = STMT_FOR_IN_LOOP;
2828 
2829             /* Check that the left side of the 'in' is valid. */
2830             JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
2831             if (TOKEN_TYPE_IS_DECL(tt)
2832                 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
2833 #if JS_HAS_DESTRUCTURING
2834                    || (pn->pn_op == JSOP_FORIN &&
2835                        (pn1->pn_head->pn_type == TOK_RC ||
2836                         (pn1->pn_head->pn_type == TOK_RB &&
2837                          pn1->pn_head->pn_count != 2) ||
2838                         (pn1->pn_head->pn_type == TOK_ASSIGN &&
2839                          (pn1->pn_head->pn_left->pn_type != TOK_RB ||
2840                           pn1->pn_head->pn_left->pn_count != 2))))
2841 #endif
2842                   )
2843                 : (pn1->pn_type != TOK_NAME &&
2844                    pn1->pn_type != TOK_DOT &&
2845 #if JS_HAS_DESTRUCTURING
2846                    ((pn->pn_op == JSOP_FORIN)
2847                     ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
2848                     : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
2849 #endif
2850 #if JS_HAS_LVALUE_RETURN
2851                    pn1->pn_type != TOK_LP &&
2852 #endif
2853 #if JS_HAS_XML_SUPPORT
2854                    (pn1->pn_type != TOK_UNARYOP ||
2855                     pn1->pn_op != JSOP_XMLNAME) &&
2856 #endif
2857                    pn1->pn_type != TOK_LB)) {
2858                 js_ReportCompileErrorNumber(cx, ts,
2859                                             JSREPORT_TS | JSREPORT_ERROR,
2860                                             JSMSG_BAD_FOR_LEFTSIDE);
2861                 return NULL;
2862             }
2863 
2864             if (TOKEN_TYPE_IS_DECL(tt)) {
2865                 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
2866                 pn1->pn_extra |= PNX_FORINVAR;
2867 
2868                 /*
2869                  * Generate a final POP only if the variable is a simple name
2870                  * (which means it is not a destructuring left-hand side) and
2871                  * it has an initializer.
2872                  */
2873                 pn2 = pn1->pn_head;
2874                 if (pn2->pn_type == TOK_NAME && pn2->pn_expr)
2875                     pn1->pn_extra |= PNX_POPVAR;
2876             } else {
2877                 pn2 = pn1;
2878 #if JS_HAS_LVALUE_RETURN
2879                 if (pn2->pn_type == TOK_LP)
2880                     pn2->pn_op = JSOP_SETCALL;
2881 #endif
2882 #if JS_HAS_XML_SUPPORT
2883                 if (pn2->pn_type == TOK_UNARYOP)
2884                     pn2->pn_op = JSOP_BINDXMLNAME;
2885 #endif
2886             }
2887 
2888             switch (pn2->pn_type) {
2889               case TOK_NAME:
2890                 /* Beware 'for (arguments in ...)' with or without a 'var'. */
2891                 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2892                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
2893                 break;
2894 
2895 #if JS_HAS_DESTRUCTURING
2896               case TOK_ASSIGN:
2897                 pn2 = pn2->pn_left;
2898                 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
2899                 /* FALL THROUGH */
2900               case TOK_RB:
2901               case TOK_RC:
2902                 /* Check for valid lvalues in var-less destructuring for-in. */
2903                 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
2904                     return NULL;
2905 
2906                 /* Destructuring for-in requires [key, value] enumeration. */
2907                 if (pn->pn_op != JSOP_FOREACH)
2908                     pn->pn_op = JSOP_FOREACHKEYVAL;
2909                 break;
2910 #endif
2911 
2912               default:;
2913             }
2914 
2915             /* Parse the object expression as the right operand of 'in'. */
2916             pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
2917             if (!pn2)
2918                 return NULL;
2919             pn->pn_left = pn2;
2920         } else {
2921             if (pn->pn_op == JSOP_FOREACH)
2922                 goto bad_for_each;
2923             pn->pn_op = JSOP_NOP;
2924 
2925             /* Parse the loop condition or null into pn2. */
2926             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
2927             ts->flags |= TSF_OPERAND;
2928             tt = js_PeekToken(cx, ts);
2929             ts->flags &= ~TSF_OPERAND;
2930             if (tt == TOK_SEMI) {
2931                 pn2 = NULL;
2932             } else {
2933                 pn2 = Expr(cx, ts, tc);
2934                 if (!pn2)
2935                     return NULL;
2936             }
2937 
2938             /* Parse the update expression or null into pn3. */
2939             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
2940             ts->flags |= TSF_OPERAND;
2941             tt = js_PeekToken(cx, ts);
2942             ts->flags &= ~TSF_OPERAND;
2943             if (tt == TOK_RP) {
2944                 pn3 = NULL;
2945             } else {
2946                 pn3 = Expr(cx, ts, tc);
2947                 if (!pn3)
2948                     return NULL;
2949             }
2950 
2951             /* Build the RESERVED node to use as the left kid of pn. */
2952             pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
2953             if (!pn4)
2954                 return NULL;
2955             pn4->pn_type = TOK_RESERVED;
2956             pn4->pn_op = JSOP_NOP;
2957             pn4->pn_kid1 = pn1;
2958             pn4->pn_kid2 = pn2;
2959             pn4->pn_kid3 = pn3;
2960             pn->pn_left = pn4;
2961         }
2962 
2963         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
2964 
2965         /* Parse the loop body into pn->pn_right. */
2966         pn2 = Statement(cx, ts, tc);
2967         if (!pn2)
2968             return NULL;
2969         pn->pn_right = pn2;
2970 
2971         /* Record the absolute line number for source note emission. */
2972         pn->pn_pos.end = pn2->pn_pos.end;
2973 
2974 #if JS_HAS_BLOCK_SCOPE
2975         if (pnlet) {
2976             js_PopStatement(tc);
2977             pnlet->pn_expr = pn;
2978             pn = pnlet;
2979         }
2980 #endif
2981         js_PopStatement(tc);
2982         return pn;
2983 
2984       bad_for_each:
2985         js_ReportCompileErrorNumber(cx, pn,
2986                                     JSREPORT_PN | JSREPORT_ERROR,
2987                                     JSMSG_BAD_FOR_EACH_LOOP);
2988         return NULL;
2989       }
2990 
2991       case TOK_TRY: {
2992         JSParseNode *catchList, *lastCatch;
2993 
2994         /*
2995          * try nodes are ternary.
2996          * kid1 is the try Statement
2997          * kid2 is the catch node list or null
2998          * kid3 is the finally Statement
2999          *
3000          * catch nodes are ternary.
3001          * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
3002          * kid2 is the catch guard or null if no guard
3003          * kid3 is the catch block
3004          *
3005          * catch lvalue nodes are either:
3006          *   TOK_NAME for a single identifier
3007          *   TOK_RB or TOK_RC for a destructuring left-hand side
3008          *
3009          * finally nodes are TOK_LC Statement lists.
3010          */
3011         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
3012         if (!pn)
3013             return NULL;
3014         pn->pn_op = JSOP_NOP;
3015 
3016         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
3017         js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
3018         pn->pn_kid1 = Statements(cx, ts, tc);
3019         if (!pn->pn_kid1)
3020             return NULL;
3021         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
3022         js_PopStatement(tc);
3023 
3024         catchList = NULL;
3025         tt = js_GetToken(cx, ts);
3026         if (tt == TOK_CATCH) {
3027             catchList = NewParseNode(cx, ts, PN_LIST, tc);
3028             if (!catchList)
3029                 return NULL;
3030             catchList->pn_type = TOK_RESERVED;
3031             PN_INIT_LIST(catchList);
3032             lastCatch = NULL;
3033 
3034             do {
3035                 JSParseNode *pnblock;
3036                 BindData data;
3037 
3038                 /* Check for another catch after unconditional catch. */
3039                 if (lastCatch && !lastCatch->pn_kid2) {
3040                     js_ReportCompileErrorNumber(cx, ts,
3041                                                 JSREPORT_TS | JSREPORT_ERROR,
3042                                                 JSMSG_CATCH_AFTER_GENERAL);
3043                     return NULL;
3044                 }
3045 
3046                 /*
3047                  * Create a lexical scope node around the whole catch clause,
3048                  * including the head.
3049                  */
3050                 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
3051                 if (!pnblock)
3052                     return NULL;
3053                 stmtInfo.type = STMT_CATCH;
3054 
3055                 /*
3056                  * Legal catch forms are:
3057                  *   catch (lhs)
3058                  *   catch (lhs if <boolean_expression>)
3059                  * where lhs is a name or a destructuring left-hand side.
3060                  * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
3061                  */
3062                 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
3063                 if (!pn2)
3064                     return NULL;
3065                 pnblock->pn_expr = pn2;
3066                 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
3067 
3068                 /*
3069                  * Contrary to ECMA Ed. 3, the catch variable is lexically
3070                  * scoped, not a property of a new Object instance.  This is
3071                  * an intentional change that anticipates ECMA Ed. 4.
3072                  */
3073                 data.pn = NULL;
3074                 data.ts = ts;
3075                 data.obj = tc->blockChain;
3076                 data.op = JSOP_NOP;
3077                 data.binder = BindLet;
3078                 data.u.let.index = 0;
3079                 data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
3080 
3081                 tt = js_GetToken(cx, ts);
3082                 switch (tt) {
3083 #if JS_HAS_DESTRUCTURING
3084                   case TOK_LB:
3085                   case TOK_LC:
3086                     pn3 = DestructuringExpr(cx, &data, tc, tt);
3087                     if (!pn3)
3088                         return NULL;
3089                     break;
3090 #endif
3091 
3092                   case TOK_NAME:
3093                     label = CURRENT_TOKEN(ts).t_atom;
3094                     if (!data.binder(cx, &data, label, tc))
3095                         return NULL;
3096 
3097                     pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3098                     if (!pn3)
3099                         return NULL;
3100                     pn3->pn_atom = label;
3101                     pn3->pn_expr = NULL;
3102                     pn3->pn_slot = 0;
3103                     pn3->pn_attrs = 0;
3104                     break;
3105 
3106                   default:
3107                     js_ReportCompileErrorNumber(cx, ts,
3108                                                 JSREPORT_TS | JSREPORT_ERROR,
3109                                                 JSMSG_CATCH_IDENTIFIER);
3110                     return NULL;
3111                 }
3112 
3113                 pn2->pn_kid1 = pn3;
3114                 pn2->pn_kid2 = NULL;
3115 #if JS_HAS_CATCH_GUARD
3116                 /*
3117                  * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
3118                  * to avoid conflicting with the JS2/ECMAv4 type annotation
3119                  * catchguard syntax.
3120                  */
3121                 if (js_MatchToken(cx, ts, TOK_IF)) {
3122                     pn2->pn_kid2 = Expr(cx, ts, tc);
3123                     if (!pn2->pn_kid2)
3124                         return NULL;
3125                 }
3126 #endif
3127                 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
3128 
3129                 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
3130                 pn2->pn_kid3 = Statements(cx, ts, tc);
3131                 if (!pn2->pn_kid3)
3132                     return NULL;
3133                 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
3134                 js_PopStatement(tc);
3135 
3136                 PN_APPEND(catchList, pnblock);
3137                 lastCatch = pn2;
3138                 ts->flags |= TSF_OPERAND;
3139                 tt = js_GetToken(cx, ts);
3140                 ts->flags &= ~TSF_OPERAND;
3141             } while (tt == TOK_CATCH);
3142         }
3143         pn->pn_kid2 = catchList;
3144 
3145         if (tt == TOK_FINALLY) {
3146             tc->tryCount++;
3147             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
3148             js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
3149             pn->pn_kid3 = Statements(cx, ts, tc);
3150             if (!pn->pn_kid3)
3151                 return NULL;
3152             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
3153             js_PopStatement(tc);
3154         } else {
3155             js_UngetToken(ts);
3156             pn->pn_kid3 = NULL;
3157         }
3158         if (!catchList && !pn->pn_kid3) {
3159             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3160                                         JSMSG_CATCH_OR_FINALLY);
3161             return NULL;
3162         }
3163         tc->tryCount++;
3164         return pn;
3165       }
3166 
3167       case TOK_THROW:
3168         pn = NewParseNode(cx, ts, PN_UNARY, tc);
3169         if (!pn)
3170             return NULL;
3171 
3172         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
3173         ts->flags |= TSF_OPERAND;
3174         tt = js_PeekTokenSameLine(cx, ts);
3175         ts->flags &= ~TSF_OPERAND;
3176         if (tt == TOK_ERROR)
3177             return NULL;
3178         if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
3179             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3180                                         JSMSG_SYNTAX_ERROR);
3181             return NULL;
3182         }
3183 
3184         pn2 = Expr(cx, ts, tc);
3185         if (!pn2)
3186             return NULL;
3187         pn->pn_pos.end = pn2->pn_pos.end;
3188         pn->pn_op = JSOP_THROW;
3189         pn->pn_kid = pn2;
3190         break;
3191 
3192       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
3193       case TOK_CATCH:
3194         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3195                                     JSMSG_CATCH_WITHOUT_TRY);
3196         return NULL;
3197 
3198       case TOK_FINALLY:
3199         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3200                                     JSMSG_FINALLY_WITHOUT_TRY);
3201         return NULL;
3202 
3203       case TOK_BREAK:
3204         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3205         if (!pn)
3206             return NULL;
3207         if (!MatchLabel(cx, ts, pn))
3208             return NULL;
3209         stmt = tc->topStmt;
3210         label = pn->pn_atom;
3211         if (label) {
3212             for (; ; stmt = stmt->down) {
3213                 if (!stmt) {
3214                     js_ReportCompileErrorNumber(cx, ts,
3215                                                 JSREPORT_TS | JSREPORT_ERROR,
3216                                                 JSMSG_LABEL_NOT_FOUND);
3217                     return NULL;
3218                 }
3219                 if (stmt->type == STMT_LABEL && stmt->atom == label)
3220                     break;
3221             }
3222         } else {
3223             for (; ; stmt = stmt->down) {
3224                 if (!stmt) {
3225                     js_ReportCompileErrorNumber(cx, ts,
3226                                                 JSREPORT_TS | JSREPORT_ERROR,
3227                                                 JSMSG_TOUGH_BREAK);
3228                     return NULL;
3229                 }
3230                 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
3231                     break;
3232             }
3233         }
3234         if (label)
3235             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3236         break;
3237 
3238       case TOK_CONTINUE:
3239         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3240         if (!pn)
3241             return NULL;
3242         if (!MatchLabel(cx, ts, pn))
3243             return NULL;
3244         stmt = tc->topStmt;
3245         label = pn->pn_atom;
3246         if (label) {
3247             for (stmt2 = NULL; ; stmt = stmt->down) {
3248                 if (!stmt) {
3249                     js_ReportCompileErrorNumber(cx, ts,
3250                                                 JSREPORT_TS | JSREPORT_ERROR,
3251                                                 JSMSG_LABEL_NOT_FOUND);
3252                     return NULL;
3253                 }
3254                 if (stmt->type == STMT_LABEL) {
3255                     if (stmt->atom == label) {
3256                         if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
3257                             js_ReportCompileErrorNumber(cx, ts,
3258                                                         JSREPORT_TS |
3259                                                         JSREPORT_ERROR,
3260                                                         JSMSG_BAD_CONTINUE);
3261                             return NULL;
3262                         }
3263                         break;
3264                     }
3265                 } else {
3266                     stmt2 = stmt;
3267                 }
3268             }
3269         } else {
3270             for (; ; stmt = stmt->down) {
3271                 if (!stmt) {
3272                     js_ReportCompileErrorNumber(cx, ts,
3273                                                 JSREPORT_TS | JSREPORT_ERROR,
3274                                                 JSMSG_BAD_CONTINUE);
3275                     return NULL;
3276                 }
3277                 if (STMT_IS_LOOP(stmt))
3278                     break;
3279             }
3280         }
3281         if (label)
3282             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3283         break;
3284 
3285       case TOK_WITH:
3286         pn = NewParseNode(cx, ts, PN_BINARY, tc);
3287         if (!pn)
3288             return NULL;
3289         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
3290         pn2 = Expr(cx, ts, tc);
3291         if (!pn2)
3292             return NULL;
3293         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
3294         pn->pn_left = pn2;
3295 
3296         js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
3297         pn2 = Statement(cx, ts, tc);
3298         if (!pn2)
3299             return NULL;
3300         js_PopStatement(tc);
3301 
3302         pn->pn_pos.end = pn2->pn_pos.end;
3303         pn->pn_right = pn2;
3304         tc->flags |= TCF_FUN_HEAVYWEIGHT;
3305         return pn;
3306 
3307       case TOK_VAR:
3308         pn = Variables(cx, ts, tc);
3309         if (!pn)
3310             return NULL;
3311 
3312         /* Tell js_EmitTree to generate a final POP. */
3313         pn->pn_extra |= PNX_POPVAR;
3314         break;
3315 
3316 #if JS_HAS_BLOCK_SCOPE
3317       case TOK_LET:
3318       {
3319         JSStmtInfo **sip;
3320         JSObject *obj;
3321         JSAtom *atom;
3322 
3323         /* Check for a let statement or let expression. */
3324         if (js_PeekToken(cx, ts) == TOK_LP) {
3325             pn = LetBlock(cx, ts, tc, JS_TRUE);
3326             if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
3327                 return pn;
3328 
3329             /* Let expressions require automatic semicolon insertion. */
3330             JS_ASSERT(pn->pn_type == TOK_SEMI ||
3331                       pn->pn_op == JSOP_LEAVEBLOCKEXPR);
3332             break;
3333         }
3334 
3335         /*
3336          * This is a let declaration. We must convert the nearest JSStmtInfo
3337          * that is a block or a switch body to be our scope statement. Further
3338          * let declarations in this block will find this scope statement and
3339          * use the same block object. If we are the first let declaration in
3340          * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a
3341          * scope statement) then we also need to set tc->blockNode to be our
3342          * TOK_LEXICALSCOPE.
3343          */
3344         sip = &tc->topScopeStmt;
3345         for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3346             if (STMT_MAYBE_SCOPE(stmt))
3347                 break;
3348             if (stmt == *sip)
3349                 sip = &stmt->downScope;
3350         }
3351 
3352         if (stmt && (stmt->flags & SIF_SCOPE)) {
3353             JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom));
3354             obj = tc->blockChain;
3355         } else {
3356             if (!stmt) {
3357                 /*
3358                  * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749
3359                  *
3360                  * This is a hard case that requires more work. In particular,
3361                  * in many cases, we're trying to emit code as we go. However,
3362                  * this means that we haven't necessarily finished processing
3363                  * all let declarations in the implicit top-level block when
3364                  * we emit a reference to one of them.  For now, punt on this
3365                  * and pretend this is a var declaration.
3366                  */
3367                 CURRENT_TOKEN(ts).type = TOK_VAR;
3368                 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
3369 
3370                 pn = Variables(cx, ts, tc);
3371                 if (!pn)
3372                     return NULL;
3373                 pn->pn_extra |= PNX_POPVAR;
3374                 break;
3375             }
3376 
3377             /* Convert the block statement into a scope statement. */
3378             obj = js_NewBlockObject(cx);
3379             if (!obj)
3380                 return NULL;
3381             atom = js_AtomizeObject(cx, obj, 0);
3382             if (!atom)
3383                 return NULL;
3384 
3385             /*
3386              * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
3387              * list stack, if it isn't already there.  If it is there, but it
3388              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
3389              * block.
3390              */
3391             JS_ASSERT(!(stmt->flags & SIF_SCOPE));
3392             stmt->flags |= SIF_SCOPE;
3393             if (stmt != *sip) {
3394                 JS_ASSERT(!stmt->downScope);
3395                 JS_ASSERT(stmt->type == STMT_BLOCK ||
3396                           stmt->type == STMT_SWITCH ||
3397                           stmt->type == STMT_TRY ||
3398                           stmt->type == STMT_FINALLY);
3399                 stmt->downScope = *sip;
3400                 *sip = stmt;
3401             } else {
3402                 JS_ASSERT(stmt->type == STMT_CATCH);
3403                 JS_ASSERT(stmt->downScope);
3404             }
3405 
3406             obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
3407             tc->blockChain = obj;
3408             stmt->atom = atom;
3409 
3410 #ifdef DEBUG
3411             pn1 = tc->blockNode;
3412             JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
3413 #endif
3414 
3415             /* Create a new lexical scope node for these statements. */
3416             pn1 = NewParseNode(cx, ts, PN_NAME, tc);
3417             if (!pn1)
3418                 return NULL;
3419 
3420             pn1->pn_type = TOK_LEXICALSCOPE;
3421             pn1->pn_op = JSOP_LEAVEBLOCK;
3422             pn1->pn_pos = tc->blockNode->pn_pos;
3423             pn1->pn_atom = atom;
3424             pn1->pn_expr = tc->blockNode;
3425             pn1->pn_slot = -1;
3426             pn1->pn_attrs = 0;
3427             tc->blockNode = pn1;
3428         }
3429 
3430         pn = Variables(cx, ts, tc);
3431         if (!pn)
3432             return NULL;
3433         pn->pn_extra = PNX_POPVAR;
3434         break;
3435       }
3436 #endif /* JS_HAS_BLOCK_SCOPE */
3437 
3438       case TOK_RETURN:
3439         pn = ReturnOrYield(cx, ts, tc, Expr);
3440         if (!pn)
3441             return NULL;
3442         break;
3443 
3444       case TOK_LC:
3445       {
3446         uintN oldflags;
3447 
3448         oldflags = tc->flags;
3449         tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
3450         js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
3451         pn = Statements(cx, ts, tc);
3452         if (!pn)
3453             return NULL;
3454 
3455         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
3456         js_PopStatement(tc);
3457 
3458         /*
3459          * If we contain a function statement and our container is top-level
3460          * or another block, flag pn to preserve braces when decompiling.
3461          */
3462         if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
3463             (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
3464             pn->pn_extra |= PNX_NEEDBRACES;
3465         }
3466         tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
3467         return pn;
3468       }
3469 
3470       case TOK_EOL:
3471       case TOK_SEMI:
3472         pn = NewParseNode(cx, ts, PN_UNARY, tc);
3473         if (!pn)
3474             return NULL;
3475         pn->pn_type = TOK_SEMI;
3476         pn->pn_kid = NULL;
3477         return pn;
3478 
3479 #if JS_HAS_DEBUGGER_KEYWORD
3480       case TOK_DEBUGGER:
3481         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3482         if (!pn)
3483             return NULL;
3484         pn->pn_type = TOK_DEBUGGER;
3485         tc->flags |= TCF_FUN_HEAVYWEIGHT;
3486         break;
3487 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3488 
3489 #if JS_HAS_XML_SUPPORT
3490       case TOK_DEFAULT:
3491         pn = NewParseNode(cx, ts, PN_UNARY, tc);
3492         if (!pn)
3493             return NULL;
3494         if (!js_MatchToken(cx, ts, TOK_NAME) ||
3495             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
3496             !js_MatchToken(cx, ts, TOK_NAME) ||
3497             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
3498             !js_MatchToken(cx, ts, TOK_ASSIGN) ||
3499             CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
3500             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3501                                         JSMSG_BAD_DEFAULT_XML_NAMESPACE);
3502             return NULL;
3503         }
3504         pn2 = Expr(cx, ts, tc);
3505         if (!pn2)
3506             return NULL;
3507         pn->pn_op = JSOP_DEFXMLNS;
3508         pn->pn_pos.end = pn2->pn_pos.end;
3509         pn->pn_kid = pn2;
3510         tc->flags |= TCF_HAS_DEFXMLNS;
3511         break;
3512 #endif
3513 
3514       case TOK_ERROR:
3515         return NULL;
3516 
3517       default:
3518 #if JS_HAS_XML_SUPPORT
3519       expression:
3520 #endif
3521         js_UngetToken(ts);
3522         pn2 = Expr(cx, ts, tc);
3523         if (!pn2)
3524             return NULL;
3525 
3526         if (js_PeekToken(cx, ts) == TOK_COLON) {
3527             if (pn2->pn_type != TOK_NAME) {
3528                 js_ReportCompileErrorNumber(cx, ts,
3529                                             JSREPORT_TS | JSREPORT_ERROR,
3530                                             JSMSG_BAD_LABEL);
3531                 return NULL;
3532             }
3533             label = pn2->pn_atom;
3534             for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3535                 if (stmt->type == STMT_LABEL && stmt->atom == label) {
3536                     js_ReportCompileErrorNumber(cx, ts,
3537                                                 JSREPORT_TS | JSREPORT_ERROR,
3538                                                 JSMSG_DUPLICATE_LABEL);
3539                     return NULL;
3540                 }
3541             }
3542             (void) js_GetToken(cx, ts);
3543 
3544             /* Push a label struct and parse the statement. */
3545             js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
3546             stmtInfo.atom = label;
3547             pn = Statement(cx, ts, tc);
3548             if (!pn)
3549                 return NULL;
3550 
3551             /* Normalize empty statement to empty block for the decompiler. */
3552             if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
3553                 pn->pn_type = TOK_LC;
3554                 pn->pn_arity = PN_LIST;
3555                 PN_INIT_LIST(pn);
3556             }
3557 
3558             /* Pop the label, set pn_expr, and return early. */
3559             js_PopStatement(tc);
3560             pn2->pn_type = TOK_COLON;
3561             pn2->pn_pos.end = pn->pn_pos.end;
3562             pn2->pn_expr = pn;
3563             return pn2;
3564         }
3565 
3566         pn = NewParseNode(cx, ts, PN_UNARY, tc);
3567         if (!pn)
3568             return NULL;
3569         pn->pn_type = TOK_SEMI;
3570         pn->pn_pos = pn2->pn_pos;
3571         pn->pn_kid = pn2;
3572         break;
3573     }
3574 
3575     /* Check termination of this primitive statement. */
3576     if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
3577         ts->flags |= TSF_OPERAND;
3578         tt = js_PeekTokenSameLine(cx, ts);
3579         ts->flags &= ~TSF_OPERAND;
3580         if (tt == TOK_ERROR)
3581             return NULL;
3582         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
3583             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3584                                         JSMSG_SEMI_BEFORE_STMNT);
3585             return NULL;
3586         }
3587     }
3588 
3589     (void) js_MatchToken(cx, ts, TOK_SEMI);
3590     return pn;
3591 }
3592 
3593 static JSParseNode *
Variables(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3594 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3595 {
3596     JSTokenType tt;
3597     JSBool let;
3598     JSStmtInfo *scopeStmt;
3599     BindData data;
3600     JSParseNode *pn, *pn2;
3601     JSStackFrame *fp;
3602     JSAtom *atom;
3603 
3604     /*
3605      * The three options here are:
3606      * - TOK_LET: We are parsing a let declaration.
3607      * - TOK_LP: We are parsing the head of a let block.
3608      * - Otherwise, we're parsing var declarations.
3609      */
3610     tt = CURRENT_TOKEN(ts).type;
3611     let = (tt == TOK_LET || tt == TOK_LP);
3612     JS_ASSERT(let || tt == TOK_VAR);
3613 
3614     /* Make sure that Statement set the tree context up correctly. */
3615     scopeStmt = tc->topScopeStmt;
3616     if (let) {
3617         while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
3618             JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
3619             scopeStmt = scopeStmt->downScope;
3620         }
3621         JS_ASSERT(scopeStmt);
3622     }
3623 
3624     data.pn = NULL;
3625     data.ts = ts;
3626     data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
3627     data.binder = let ? BindLet : BindVarOrConst;
3628     pn = NewParseNode(cx, ts, PN_LIST, tc);
3629     if (!pn)
3630         return NULL;
3631     pn->pn_op = data.op;
3632     PN_INIT_LIST(pn);
3633 
3634     /*
3635      * The tricky part of this code is to create special parsenode opcodes for
3636      * getting and setting variables (which will be stored as special slots in
3637      * the frame).  The most complicated case is an eval() inside a function.
3638      * If the evaluated string references variables in the enclosing function,
3639      * then we need to generate the special variable opcodes.  We determine
3640      * this by looking up the variable's id in the current variable object.
3641      * Fortunately, we can avoid doing this for let declared variables.
3642      */
3643     fp = cx->fp;
3644     if (let) {
3645         JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom));
3646         data.obj = tc->blockChain;
3647         data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj);
3648         data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS;
3649     } else {
3650         data.obj = fp->varobj;
3651         data.u.var.fun = fp->fun;
3652         data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj);
3653         if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) {
3654             /* We are compiling code inside a function */
3655             data.u.var.getter = js_GetLocalVariable;
3656             data.u.var.setter = js_SetLocalVariable;
3657         } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) {
3658             /* We are compiling code from an eval inside a function */
3659             data.u.var.getter = js_GetCallVariable;
3660             data.u.var.setter = js_SetCallVariable;
3661         } else {
3662             data.u.var.getter = data.u.var.clasp->getProperty;
3663             data.u.var.setter = data.u.var.clasp->setProperty;
3664         }
3665 
3666         data.u.var.attrs = (data.op == JSOP_DEFCONST)
3667                            ? JSPROP_PERMANENT | JSPROP_READONLY
3668                            : JSPROP_PERMANENT;
3669     }
3670 
3671     do {
3672         tt = js_GetToken(cx, ts);
3673 #if JS_HAS_DESTRUCTURING
3674         if (tt == TOK_LB || tt == TOK_LC) {
3675             pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3676             if (!pn2)
3677                 return NULL;
3678 
3679             if ((tc->flags & TCF_IN_FOR_INIT) &&
3680                 js_PeekToken(cx, ts) == TOK_IN) {
3681                 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
3682                     return NULL;
3683                 PN_APPEND(pn, pn2);
3684                 continue;
3685             }
3686 
3687             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
3688             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3689                 goto bad_var_init;
3690 
3691             pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP,
3692                             pn2, AssignExpr(cx, ts, tc),
3693                             tc);
3694             if (!pn2 ||
3695                 !CheckDestructuring(cx, &data,
3696                                     pn2->pn_left, pn2->pn_right,
3697                                     tc)) {
3698                 return NULL;
3699             }
3700             PN_APPEND(pn, pn2);
3701             continue;
3702         }
3703 #endif
3704 
3705         if (tt != TOK_NAME) {
3706             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3707                                         JSMSG_NO_VARIABLE_NAME);
3708             return NULL;
3709         }
3710         atom = CURRENT_TOKEN(ts).t_atom;
3711         if (!data.binder(cx, &data, atom, tc))
3712             return NULL;
3713 
3714         pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3715         if (!pn2)
3716             return NULL;
3717         pn2->pn_op = JSOP_NAME;
3718         pn2->pn_atom = atom;
3719         pn2->pn_expr = NULL;
3720         pn2->pn_slot = -1;
3721         pn2->pn_attrs = let ? 0 : data.u.var.attrs;
3722         PN_APPEND(pn, pn2);
3723 
3724         if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
3725             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3726                 goto bad_var_init;
3727 
3728             pn2->pn_expr = AssignExpr(cx, ts, tc);
3729             if (!pn2->pn_expr)
3730                 return NULL;
3731             pn2->pn_op = (!let && data.op == JSOP_DEFCONST)
3732                          ? JSOP_SETCONST
3733                          : JSOP_SETNAME;
3734             if (!let && atom == cx->runtime->atomState.argumentsAtom)
3735                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3736         }
3737     } while (js_MatchToken(cx, ts, TOK_COMMA));
3738 
3739     pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3740     return pn;
3741 
3742 bad_var_init:
3743     js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3744                                 JSMSG_BAD_VAR_INIT);
3745     return NULL;
3746 }
3747 
3748 static JSParseNode *
Expr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3749 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3750 {
3751     JSParseNode *pn, *pn2;
3752 
3753     pn = AssignExpr(cx, ts, tc);
3754     if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
3755         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
3756         if (!pn2)
3757             return NULL;
3758         pn2->pn_pos.begin = pn->pn_pos.begin;
3759         PN_INIT_LIST_1(pn2, pn);
3760         pn = pn2;
3761         do {
3762 #if JS_HAS_GENERATORS
3763             pn2 = PN_LAST(pn);
3764             if (pn2->pn_type == TOK_YIELD) {
3765                 js_ReportCompileErrorNumber(cx, pn2,
3766                                             JSREPORT_PN | JSREPORT_ERROR,
3767                                             JSMSG_BAD_YIELD_SYNTAX);
3768                 return NULL;
3769             }
3770 #endif
3771             pn2 = AssignExpr(cx, ts, tc);
3772             if (!pn2)
3773                 return NULL;
3774             PN_APPEND(pn, pn2);
3775         } while (js_MatchToken(cx, ts, TOK_COMMA));
3776         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3777     }
3778     return pn;
3779 }
3780 
3781 static JSParseNode *
AssignExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3782 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3783 {
3784     JSParseNode *pn, *pn2;
3785     JSTokenType tt;
3786     JSOp op;
3787 
3788     CHECK_RECURSION();
3789 
3790 #if JS_HAS_GENERATORS
3791     ts->flags |= TSF_OPERAND;
3792     if (js_MatchToken(cx, ts, TOK_YIELD)) {
3793         ts->flags &= ~TSF_OPERAND;
3794         return ReturnOrYield(cx, ts, tc, AssignExpr);
3795     }
3796     ts->flags &= ~TSF_OPERAND;
3797 #endif
3798 
3799     pn = CondExpr(cx, ts, tc);
3800     if (!pn)
3801         return NULL;
3802 
3803     tt = js_GetToken(cx, ts);
3804 #if JS_HAS_GETTER_SETTER
3805     if (tt == TOK_NAME) {
3806         tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
3807         if (tt == TOK_ERROR)
3808             return NULL;
3809     }
3810 #endif
3811     if (tt != TOK_ASSIGN) {
3812         js_UngetToken(ts);
3813         return pn;
3814     }
3815 
3816     op = CURRENT_TOKEN(ts).t_op;
3817     for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
3818         continue;
3819     switch (pn2->pn_type) {
3820       case TOK_NAME:
3821         pn2->pn_op = JSOP_SETNAME;
3822         if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
3823             tc->flags |= TCF_FUN_HEAVYWEIGHT;
3824         break;
3825       case TOK_DOT:
3826         pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
3827                      ? JSOP_SETMETHOD
3828                      : JSOP_SETPROP;
3829         break;
3830       case TOK_LB:
3831         pn2->pn_op = JSOP_SETELEM;
3832         break;
3833 #if JS_HAS_DESTRUCTURING
3834       case TOK_RB:
3835       case TOK_RC:
3836         if (op != JSOP_NOP) {
3837             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3838                                         JSMSG_BAD_DESTRUCT_ASS);
3839             return NULL;
3840         }
3841         pn = AssignExpr(cx, ts, tc);
3842         if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
3843             return NULL;
3844         return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc);
3845 #endif
3846 #if JS_HAS_LVALUE_RETURN
3847       case TOK_LP:
3848         JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
3849         pn2->pn_op = JSOP_SETCALL;
3850         break;
3851 #endif
3852 #if JS_HAS_XML_SUPPORT
3853       case TOK_UNARYOP:
3854         if (pn2->pn_op == JSOP_XMLNAME) {
3855             pn2->pn_op = JSOP_SETXMLNAME;
3856             break;
3857         }
3858         /* FALL THROUGH */
3859 #endif
3860       default:
3861         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3862                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
3863         return NULL;
3864     }
3865 
3866     return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
3867 }
3868 
3869 static JSParseNode *
CondExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3870 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3871 {
3872     JSParseNode *pn, *pn1, *pn2, *pn3;
3873     uintN oldflags;
3874 
3875     pn = OrExpr(cx, ts, tc);
3876     if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
3877         pn1 = pn;
3878         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
3879         if (!pn)
3880             return NULL;
3881         /*
3882          * Always accept the 'in' operator in the middle clause of a ternary,
3883          * where it's unambiguous, even if we might be parsing the init of a
3884          * for statement.
3885          */
3886         oldflags = tc->flags;
3887         tc->flags &= ~TCF_IN_FOR_INIT;
3888         pn2 = AssignExpr(cx, ts, tc);
3889         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3890 
3891         if (!pn2)
3892             return NULL;
3893         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
3894         pn3 = AssignExpr(cx, ts, tc);
3895         if (!pn3)
3896             return NULL;
3897         pn->pn_pos.begin = pn1->pn_pos.begin;
3898         pn->pn_pos.end = pn3->pn_pos.end;
3899         pn->pn_kid1 = pn1;
3900         pn->pn_kid2 = pn2;
3901         pn->pn_kid3 = pn3;
3902     }
3903     return pn;
3904 }
3905 
3906 static JSParseNode *
OrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3907 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3908 {
3909     JSParseNode *pn;
3910 
3911     pn = AndExpr(cx, ts, tc);
3912     if (pn && js_MatchToken(cx, ts, TOK_OR))
3913         pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
3914     return pn;
3915 }
3916 
3917 static JSParseNode *
AndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3918 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3919 {
3920     JSParseNode *pn;
3921 
3922     pn = BitOrExpr(cx, ts, tc);
3923     if (pn && js_MatchToken(cx, ts, TOK_AND))
3924         pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
3925     return pn;
3926 }
3927 
3928 static JSParseNode *
BitOrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3929 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3930 {
3931     JSParseNode *pn;
3932 
3933     pn = BitXorExpr(cx, ts, tc);
3934     while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
3935         pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
3936                        tc);
3937     }
3938     return pn;
3939 }
3940 
3941 static JSParseNode *
BitXorExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3942 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3943 {
3944     JSParseNode *pn;
3945 
3946     pn = BitAndExpr(cx, ts, tc);
3947     while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
3948         pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
3949                        tc);
3950     }
3951     return pn;
3952 }
3953 
3954 static JSParseNode *
BitAndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3955 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3956 {
3957     JSParseNode *pn;
3958 
3959     pn = EqExpr(cx, ts, tc);
3960     while (pn && js_MatchToken(cx, ts, TOK_BITAND))
3961         pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
3962     return pn;
3963 }
3964 
3965 static JSParseNode *
EqExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3966 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3967 {
3968     JSParseNode *pn;
3969     JSOp op;
3970 
3971     pn = RelExpr(cx, ts, tc);
3972     while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
3973         op = CURRENT_TOKEN(ts).t_op;
3974         pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
3975     }
3976     return pn;
3977 }
3978 
3979 static JSParseNode *
RelExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3980 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3981 {
3982     JSParseNode *pn;
3983     JSTokenType tt;
3984     JSOp op;
3985     uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
3986 
3987     /*
3988      * Uses of the in operator in ShiftExprs are always unambiguous,
3989      * so unset the flag that prohibits recognizing it.
3990      */
3991     tc->flags &= ~TCF_IN_FOR_INIT;
3992 
3993     pn = ShiftExpr(cx, ts, tc);
3994     while (pn &&
3995            (js_MatchToken(cx, ts, TOK_RELOP) ||
3996             /*
3997              * Recognize the 'in' token as an operator only if we're not
3998              * currently in the init expr of a for loop.
3999              */
4000             (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
4001             js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
4002         tt = CURRENT_TOKEN(ts).type;
4003         op = CURRENT_TOKEN(ts).t_op;
4004         pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
4005     }
4006     /* Restore previous state of inForInit flag. */
4007     tc->flags |= inForInitFlag;
4008 
4009     return pn;
4010 }
4011 
4012 static JSParseNode *
ShiftExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4013 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4014 {
4015     JSParseNode *pn;
4016     JSOp op;
4017 
4018     pn = AddExpr(cx, ts, tc);
4019     while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
4020         op = CURRENT_TOKEN(ts).t_op;
4021         pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
4022     }
4023     return pn;
4024 }
4025 
4026 static JSParseNode *
AddExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4027 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4028 {
4029     JSParseNode *pn;
4030     JSTokenType tt;
4031     JSOp op;
4032 
4033     pn = MulExpr(cx, ts, tc);
4034     while (pn &&
4035            (js_MatchToken(cx, ts, TOK_PLUS) ||
4036             js_MatchToken(cx, ts, TOK_MINUS))) {
4037         tt = CURRENT_TOKEN(ts).type;
4038         op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
4039         pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
4040     }
4041     return pn;
4042 }
4043 
4044 static JSParseNode *
MulExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4045 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4046 {
4047     JSParseNode *pn;
4048     JSTokenType tt;
4049     JSOp op;
4050 
4051     pn = UnaryExpr(cx, ts, tc);
4052     while (pn &&
4053            (js_MatchToken(cx, ts, TOK_STAR) ||
4054             js_MatchToken(cx, ts, TOK_DIVOP))) {
4055         tt = CURRENT_TOKEN(ts).type;
4056         op = CURRENT_TOKEN(ts).t_op;
4057         pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
4058     }
4059     return pn;
4060 }
4061 
4062 static JSParseNode *
SetLvalKid(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSParseNode * kid,const char * name)4063 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
4064            const char *name)
4065 {
4066     while (kid->pn_type == TOK_RP)
4067         kid = kid->pn_kid;
4068     if (kid->pn_type != TOK_NAME &&
4069         kid->pn_type != TOK_DOT &&
4070 #if JS_HAS_LVALUE_RETURN
4071         (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
4072 #endif
4073 #if JS_HAS_XML_SUPPORT
4074         (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
4075 #endif
4076         kid->pn_type != TOK_LB) {
4077         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4078                                     JSMSG_BAD_OPERAND, name);
4079         return NULL;
4080     }
4081     pn->pn_kid = kid;
4082     return kid;
4083 }
4084 
4085 static const char incop_name_str[][10] = {"increment", "decrement"};
4086 
4087 static JSBool
SetIncOpKid(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn,JSParseNode * kid,JSTokenType tt,JSBool preorder)4088 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4089             JSParseNode *pn, JSParseNode *kid,
4090             JSTokenType tt, JSBool preorder)
4091 {
4092     JSOp op;
4093 
4094     kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
4095     if (!kid)
4096         return JS_FALSE;
4097     switch (kid->pn_type) {
4098       case TOK_NAME:
4099         op = (tt == TOK_INC)
4100              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
4101              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
4102         if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
4103             tc->flags |= TCF_FUN_HEAVYWEIGHT;
4104         break;
4105 
4106       case TOK_DOT:
4107         op = (tt == TOK_INC)
4108              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
4109              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
4110         break;
4111 
4112 #if JS_HAS_LVALUE_RETURN
4113       case TOK_LP:
4114         JS_ASSERT(kid->pn_op == JSOP_CALL);
4115         kid->pn_op = JSOP_SETCALL;
4116         /* FALL THROUGH */
4117 #endif
4118 #if JS_HAS_XML_SUPPORT
4119       case TOK_UNARYOP:
4120         if (kid->pn_op == JSOP_XMLNAME)
4121             kid->pn_op = JSOP_SETXMLNAME;
4122         /* FALL THROUGH */
4123 #endif
4124       case TOK_LB:
4125         op = (tt == TOK_INC)
4126              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
4127              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
4128         break;
4129 
4130       default:
4131         JS_ASSERT(0);
4132         op = JSOP_NOP;
4133     }
4134     pn->pn_op = op;
4135     return JS_TRUE;
4136 }
4137 
4138 static JSParseNode *
UnaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4139 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4140 {
4141     JSTokenType tt;
4142     JSParseNode *pn, *pn2;
4143 
4144     CHECK_RECURSION();
4145 
4146     ts->flags |= TSF_OPERAND;
4147     tt = js_GetToken(cx, ts);
4148     ts->flags &= ~TSF_OPERAND;
4149 
4150     switch (tt) {
4151       case TOK_UNARYOP:
4152       case TOK_PLUS:
4153       case TOK_MINUS:
4154         pn = NewParseNode(cx, ts, PN_UNARY, tc);
4155         if (!pn)
4156             return NULL;
4157         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
4158         pn->pn_op = CURRENT_TOKEN(ts).t_op;
4159         pn2 = UnaryExpr(cx, ts, tc);
4160         if (!pn2)
4161             return NULL;
4162         pn->pn_pos.end = pn2->pn_pos.end;
4163         pn->pn_kid = pn2;
4164         break;
4165 
4166       case TOK_INC:
4167       case TOK_DEC:
4168         pn = NewParseNode(cx, ts, PN_UNARY, tc);
4169         if (!pn)
4170             return NULL;
4171         pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
4172         if (!pn2)
4173             return NULL;
4174         if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
4175             return NULL;
4176         pn->pn_pos.end = pn2->pn_pos.end;
4177         break;
4178 
4179       case TOK_DELETE:
4180         pn = NewParseNode(cx, ts, PN_UNARY, tc);
4181         if (!pn)
4182             return NULL;
4183         pn2 = UnaryExpr(cx, ts, tc);
4184         if (!pn2)
4185             return NULL;
4186         pn->pn_pos.end = pn2->pn_pos.end;
4187 
4188         /*
4189          * Under ECMA3, deleting any unary expression is valid -- it simply
4190          * returns true. Here we strip off any parentheses.
4191          */
4192         while (pn2->pn_type == TOK_RP)
4193             pn2 = pn2->pn_kid;
4194         pn->pn_kid = pn2;
4195         break;
4196 
4197       case TOK_ERROR:
4198         return NULL;
4199 
4200       default:
4201         js_UngetToken(ts);
4202         pn = MemberExpr(cx, ts, tc, JS_TRUE);
4203         if (!pn)
4204             return NULL;
4205 
4206         /* Don't look across a newline boundary for a postfix incop. */
4207         if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
4208             ts->flags |= TSF_OPERAND;
4209             tt = js_PeekTokenSameLine(cx, ts);
4210             ts->flags &= ~TSF_OPERAND;
4211             if (tt == TOK_INC || tt == TOK_DEC) {
4212                 (void) js_GetToken(cx, ts);
4213                 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
4214                 if (!pn2)
4215                     return NULL;
4216                 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
4217                     return NULL;
4218                 pn2->pn_pos.begin = pn->pn_pos.begin;
4219                 pn = pn2;
4220             }
4221         }
4222         break;
4223     }
4224     return pn;
4225 }
4226 
4227 static JSBool
ArgumentList(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * listNode)4228 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4229              JSParseNode *listNode)
4230 {
4231     JSBool matched;
4232 
4233     ts->flags |= TSF_OPERAND;
4234     matched = js_MatchToken(cx, ts, TOK_RP);
4235     ts->flags &= ~TSF_OPERAND;
4236     if (!matched) {
4237         do {
4238             JSParseNode *argNode = AssignExpr(cx, ts, tc);
4239             if (!argNode)
4240                 return JS_FALSE;
4241 #if JS_HAS_GENERATORS
4242             if (argNode->pn_type == TOK_YIELD) {
4243                 js_ReportCompileErrorNumber(cx, argNode,
4244                                             JSREPORT_PN | JSREPORT_ERROR,
4245                                             JSMSG_BAD_YIELD_SYNTAX);
4246                 return JS_FALSE;
4247             }
4248 #endif
4249             PN_APPEND(listNode, argNode);
4250         } while (js_MatchToken(cx, ts, TOK_COMMA));
4251 
4252         if (js_GetToken(cx, ts) != TOK_RP) {
4253             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4254                                         JSMSG_PAREN_AFTER_ARGS);
4255             return JS_FALSE;
4256         }
4257     }
4258     return JS_TRUE;
4259 }
4260 
4261 static JSParseNode *
MemberExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowCallSyntax)4262 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4263            JSBool allowCallSyntax)
4264 {
4265     JSParseNode *pn, *pn2, *pn3;
4266     JSTokenType tt;
4267 
4268     CHECK_RECURSION();
4269 
4270     /* Check for new expression first. */
4271     ts->flags |= TSF_OPERAND;
4272     tt = js_GetToken(cx, ts);
4273     ts->flags &= ~TSF_OPERAND;
4274     if (tt == TOK_NEW) {
4275         pn = NewParseNode(cx, ts, PN_LIST, tc);
4276         if (!pn)
4277             return NULL;
4278         pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
4279         if (!pn2)
4280             return NULL;
4281         pn->pn_op = JSOP_NEW;
4282         PN_INIT_LIST_1(pn, pn2);
4283         pn->pn_pos.begin = pn2->pn_pos.begin;
4284 
4285         if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
4286             return NULL;
4287         if (pn->pn_count > ARGC_LIMIT) {
4288             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4289                                  JSMSG_TOO_MANY_CON_ARGS);
4290             return NULL;
4291         }
4292         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
4293     } else {
4294         pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
4295         if (!pn)
4296             return NULL;
4297 
4298         if (pn->pn_type == TOK_ANYNAME ||
4299             pn->pn_type == TOK_AT ||
4300             pn->pn_type == TOK_DBLCOLON) {
4301             pn2 = NewOrRecycledNode(cx, tc);
4302             if (!pn2)
4303                 return NULL;
4304             pn2->pn_type = TOK_UNARYOP;
4305             pn2->pn_pos = pn->pn_pos;
4306             pn2->pn_op = JSOP_XMLNAME;
4307             pn2->pn_arity = PN_UNARY;
4308             pn2->pn_kid = pn;
4309             pn2->pn_next = NULL;
4310             pn2->pn_ts = ts;
4311             pn2->pn_source = NULL;
4312             pn = pn2;
4313         }
4314     }
4315 
4316     while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
4317         if (tt == TOK_DOT) {
4318             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4319             if (!pn2)
4320                 return NULL;
4321             pn2->pn_slot = -1;
4322             pn2->pn_attrs = 0;
4323 #if JS_HAS_XML_SUPPORT
4324             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
4325             tt = js_GetToken(cx, ts);
4326             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
4327             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
4328             if (!pn3)
4329                 return NULL;
4330             tt = pn3->pn_type;
4331             if (tt == TOK_NAME ||
4332                 (tt == TOK_DBLCOLON &&
4333                  pn3->pn_arity == PN_NAME &&
4334                  pn3->pn_expr->pn_type == TOK_FUNCTION)) {
4335                 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
4336                 pn2->pn_expr = pn;
4337                 pn2->pn_atom = pn3->pn_atom;
4338                 RecycleTree(pn3, tc);
4339             } else {
4340                 if (TOKEN_TYPE_IS_XML(tt)) {
4341                     pn2->pn_type = TOK_LB;
4342                     pn2->pn_op = JSOP_GETELEM;
4343                 } else if (tt == TOK_RP) {
4344                     JSParseNode *group = pn3;
4345 
4346                     /* Recycle the useless TOK_RP/JSOP_GROUP node. */
4347                     pn3 = group->pn_kid;
4348                     group->pn_kid = NULL;
4349                     RecycleTree(group, tc);
4350                     pn2->pn_type = TOK_FILTER;
4351                     pn2->pn_op = JSOP_FILTER;
4352 
4353                     /* A filtering predicate is like a with statement. */
4354                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
4355                 } else {
4356                     js_ReportCompileErrorNumber(cx, ts,
4357                                                 JSREPORT_TS | JSREPORT_ERROR,
4358                                                 JSMSG_NAME_AFTER_DOT);
4359                     return NULL;
4360                 }
4361                 pn2->pn_arity = PN_BINARY;
4362                 pn2->pn_left = pn;
4363                 pn2->pn_right = pn3;
4364             }
4365 #else
4366             ts->flags |= TSF_KEYWORD_IS_NAME;
4367             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
4368             ts->flags &= ~TSF_KEYWORD_IS_NAME;
4369             pn2->pn_op = JSOP_GETPROP;
4370             pn2->pn_expr = pn;
4371             pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
4372 #endif
4373             pn2->pn_pos.begin = pn->pn_pos.begin;
4374             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4375 #if JS_HAS_XML_SUPPORT
4376         } else if (tt == TOK_DBLDOT) {
4377             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4378             if (!pn2)
4379                 return NULL;
4380             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
4381             tt = js_GetToken(cx, ts);
4382             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
4383             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
4384             if (!pn3)
4385                 return NULL;
4386             tt = pn3->pn_type;
4387             if (tt == TOK_NAME) {
4388                 pn3->pn_type = TOK_STRING;
4389                 pn3->pn_arity = PN_NULLARY;
4390                 pn3->pn_op = JSOP_QNAMEPART;
4391             } else if (!TOKEN_TYPE_IS_XML(tt)) {
4392                 js_ReportCompileErrorNumber(cx, ts,
4393                                             JSREPORT_TS | JSREPORT_ERROR,
4394                                             JSMSG_NAME_AFTER_DOT);
4395                 return NULL;
4396             }
4397             pn2->pn_op = JSOP_DESCENDANTS;
4398             pn2->pn_left = pn;
4399             pn2->pn_right = pn3;
4400             pn2->pn_pos.begin = pn->pn_pos.begin;
4401             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4402 #endif
4403         } else if (tt == TOK_LB) {
4404             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4405             if (!pn2)
4406                 return NULL;
4407             pn3 = Expr(cx, ts, tc);
4408             if (!pn3)
4409                 return NULL;
4410 
4411             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
4412             pn2->pn_pos.begin = pn->pn_pos.begin;
4413             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4414 
4415             /* Optimize o['p'] to o.p by rewriting pn2. */
4416             if (pn3->pn_type == TOK_STRING) {
4417                 pn2->pn_type = TOK_DOT;
4418                 pn2->pn_op = JSOP_GETPROP;
4419                 pn2->pn_arity = PN_NAME;
4420                 pn2->pn_expr = pn;
4421                 pn2->pn_atom = pn3->pn_atom;
4422             } else {
4423                 pn2->pn_op = JSOP_GETELEM;
4424                 pn2->pn_left = pn;
4425                 pn2->pn_right = pn3;
4426             }
4427         } else if (allowCallSyntax && tt == TOK_LP) {
4428             pn2 = NewParseNode(cx, ts, PN_LIST, tc);
4429             if (!pn2)
4430                 return NULL;
4431 
4432             /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
4433             pn2->pn_op = JSOP_CALL;
4434             if (pn->pn_op == JSOP_NAME &&
4435                 pn->pn_atom == cx->runtime->atomState.evalAtom) {
4436                 pn2->pn_op = JSOP_EVAL;
4437                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4438             }
4439 
4440             PN_INIT_LIST_1(pn2, pn);
4441             pn2->pn_pos.begin = pn->pn_pos.begin;
4442 
4443             if (!ArgumentList(cx, ts, tc, pn2))
4444                 return NULL;
4445             if (pn2->pn_count > ARGC_LIMIT) {
4446                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4447                                      JSMSG_TOO_MANY_FUN_ARGS);
4448                 return NULL;
4449             }
4450             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4451         } else {
4452             js_UngetToken(ts);
4453             return pn;
4454         }
4455 
4456         pn = pn2;
4457     }
4458     if (tt == TOK_ERROR)
4459         return NULL;
4460     return pn;
4461 }
4462 
4463 static JSParseNode *
BracketedExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4464 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4465 {
4466     uintN oldflags;
4467     JSParseNode *pn;
4468 
4469     /*
4470      * Always accept the 'in' operator in a parenthesized expression,
4471      * where it's unambiguous, even if we might be parsing the init of a
4472      * for statement.
4473      */
4474     oldflags = tc->flags;
4475     tc->flags &= ~TCF_IN_FOR_INIT;
4476     pn = Expr(cx, ts, tc);
4477     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
4478     return pn;
4479 }
4480 
4481 #if JS_HAS_XML_SUPPORT
4482 
4483 static JSParseNode *
EndBracketedExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4484 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4485 {
4486     JSParseNode *pn;
4487 
4488     pn = BracketedExpr(cx, ts, tc);
4489     if (!pn)
4490         return NULL;
4491 
4492     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
4493     return pn;
4494 }
4495 
4496 /*
4497  * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
4498  *
4499  *      AttributeIdentifier:
4500  *              @ PropertySelector
4501  *              @ QualifiedIdentifier
4502  *              @ [ Expression ]
4503  *
4504  *      PropertySelector:
4505  *              Identifier
4506  *              *
4507  *
4508  *      QualifiedIdentifier:
4509  *              PropertySelector :: PropertySelector
4510  *              PropertySelector :: [ Expression ]
4511  *
4512  * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
4513  *
4514  *      AttributeIdentifier:
4515  *              @ QualifiedIdentifier
4516  *              @ [ Expression ]
4517  *
4518  *      PropertySelector:
4519  *              Identifier
4520  *              *
4521  *
4522  *      QualifiedIdentifier:
4523  *              PropertySelector :: PropertySelector
4524  *              PropertySelector :: [ Expression ]
4525  *              PropertySelector
4526  *
4527  * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
4528  * for that rule to result in a name node, but ECMA-357 extends the grammar
4529  * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
4530  *
4531  *      QualifiedIdentifier:
4532  *              PropertySelector QualifiedSuffix
4533  *
4534  *      QualifiedSuffix:
4535  *              :: PropertySelector
4536  *              :: [ Expression ]
4537  *              /nothing/
4538  *
4539  * And use this production instead of PrimaryExpression: QualifiedIdentifier:
4540  *
4541  *      PrimaryExpression:
4542  *              Identifier QualifiedSuffix
4543  *
4544  * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
4545  * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
4546  */
4547 static JSParseNode *
PropertySelector(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4548 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4549 {
4550     JSParseNode *pn;
4551 
4552     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4553     if (!pn)
4554         return NULL;
4555     if (pn->pn_type == TOK_STAR) {
4556         pn->pn_type = TOK_ANYNAME;
4557         pn->pn_op = JSOP_ANYNAME;
4558         pn->pn_atom = cx->runtime->atomState.starAtom;
4559     } else {
4560         JS_ASSERT(pn->pn_type == TOK_NAME);
4561         pn->pn_op = JSOP_QNAMEPART;
4562         pn->pn_arity = PN_NAME;
4563         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
4564         pn->pn_expr = NULL;
4565         pn->pn_slot = -1;
4566         pn->pn_attrs = 0;
4567     }
4568     return pn;
4569 }
4570 
4571 static JSParseNode *
QualifiedSuffix(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSTreeContext * tc)4572 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
4573                 JSTreeContext *tc)
4574 {
4575     JSParseNode *pn2, *pn3;
4576     JSTokenType tt;
4577 
4578     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
4579     pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4580     if (!pn2)
4581         return NULL;
4582 
4583     /* Left operand of :: must be evaluated if it is an identifier. */
4584     if (pn->pn_op == JSOP_QNAMEPART)
4585         pn->pn_op = JSOP_NAME;
4586 
4587     ts->flags |= TSF_KEYWORD_IS_NAME;
4588     tt = js_GetToken(cx, ts);
4589     ts->flags &= ~TSF_KEYWORD_IS_NAME;
4590     if (tt == TOK_STAR || tt == TOK_NAME) {
4591         /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
4592         pn2->pn_op = JSOP_QNAMECONST;
4593         pn2->pn_atom = (tt == TOK_STAR)
4594                        ? cx->runtime->atomState.starAtom
4595                        : CURRENT_TOKEN(ts).t_atom;
4596         pn2->pn_expr = pn;
4597         pn2->pn_slot = -1;
4598         pn2->pn_attrs = 0;
4599         return pn2;
4600     }
4601 
4602     if (tt != TOK_LB) {
4603         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4604                                     JSMSG_SYNTAX_ERROR);
4605         return NULL;
4606     }
4607     pn3 = EndBracketedExpr(cx, ts, tc);
4608     if (!pn3)
4609         return NULL;
4610 
4611     pn2->pn_op = JSOP_QNAME;
4612     pn2->pn_arity = PN_BINARY;
4613     pn2->pn_left = pn;
4614     pn2->pn_right = pn3;
4615     return pn2;
4616 }
4617 
4618 static JSParseNode *
QualifiedIdentifier(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4619 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4620 {
4621     JSParseNode *pn;
4622 
4623     pn = PropertySelector(cx, ts, tc);
4624     if (!pn)
4625         return NULL;
4626     if (js_MatchToken(cx, ts, TOK_DBLCOLON))
4627         pn = QualifiedSuffix(cx, ts, pn, tc);
4628     return pn;
4629 }
4630 
4631 static JSParseNode *
AttributeIdentifier(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4632 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4633 {
4634     JSParseNode *pn, *pn2;
4635     JSTokenType tt;
4636 
4637     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
4638     pn = NewParseNode(cx, ts, PN_UNARY, tc);
4639     if (!pn)
4640         return NULL;
4641     pn->pn_op = JSOP_TOATTRNAME;
4642     ts->flags |= TSF_KEYWORD_IS_NAME;
4643     tt = js_GetToken(cx, ts);
4644     ts->flags &= ~TSF_KEYWORD_IS_NAME;
4645     if (tt == TOK_STAR || tt == TOK_NAME) {
4646         pn2 = QualifiedIdentifier(cx, ts, tc);
4647     } else if (tt == TOK_LB) {
4648         pn2 = EndBracketedExpr(cx, ts, tc);
4649     } else {
4650         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4651                                     JSMSG_SYNTAX_ERROR);
4652         return NULL;
4653     }
4654     if (!pn2)
4655         return NULL;
4656     pn->pn_kid = pn2;
4657     return pn;
4658 }
4659 
4660 /*
4661  * Make a TOK_LC unary node whose pn_kid is an expression.
4662  */
4663 static JSParseNode *
XMLExpr(JSContext * cx,JSTokenStream * ts,JSBool inTag,JSTreeContext * tc)4664 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
4665 {
4666     JSParseNode *pn, *pn2;
4667     uintN oldflags;
4668 
4669     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
4670     pn = NewParseNode(cx, ts, PN_UNARY, tc);
4671     if (!pn)
4672         return NULL;
4673 
4674     /*
4675      * Turn off XML tag mode, but don't restore it after parsing this braced
4676      * expression.  Instead, simply restore ts's old flags.  This is required
4677      * because XMLExpr is called both from within a tag, and from within text
4678      * contained in an element, but outside of any start, end, or point tag.
4679      */
4680     oldflags = ts->flags;
4681     ts->flags = oldflags & ~TSF_XMLTAGMODE;
4682     pn2 = Expr(cx, ts, tc);
4683     if (!pn2)
4684         return NULL;
4685 
4686     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
4687     ts->flags = oldflags;
4688     pn->pn_kid = pn2;
4689     pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
4690     return pn;
4691 }
4692 
4693 /*
4694  * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
4695  * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI.  When converting
4696  * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
4697  * child of a container tag.
4698  */
4699 static JSParseNode *
XMLAtomNode(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4700 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4701 {
4702     JSParseNode *pn;
4703     JSToken *tp;
4704 
4705     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4706     if (!pn)
4707         return NULL;
4708     tp = &CURRENT_TOKEN(ts);
4709     pn->pn_op = tp->t_op;
4710     pn->pn_atom = tp->t_atom;
4711     if (tp->type == TOK_XMLPI)
4712         pn->pn_atom2 = tp->t_atom2;
4713     return pn;
4714 }
4715 
4716 /*
4717  * Parse the productions:
4718  *
4719  *      XMLNameExpr:
4720  *              XMLName XMLNameExpr?
4721  *              { Expr } XMLNameExpr?
4722  *
4723  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
4724  * a list of names and/or expressions, a single expression, or a single name.
4725  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
4726  * will be TOK_LC.
4727  */
4728 static JSParseNode *
XMLNameExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4729 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4730 {
4731     JSParseNode *pn, *pn2, *list;
4732     JSTokenType tt;
4733 
4734     pn = list = NULL;
4735     do {
4736         tt = CURRENT_TOKEN(ts).type;
4737         if (tt == TOK_LC) {
4738             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
4739             if (!pn2)
4740                 return NULL;
4741         } else {
4742             JS_ASSERT(tt == TOK_XMLNAME);
4743             pn2 = XMLAtomNode(cx, ts, tc);
4744             if (!pn2)
4745                 return NULL;
4746         }
4747 
4748         if (!pn) {
4749             pn = pn2;
4750         } else {
4751             if (!list) {
4752                 list = NewParseNode(cx, ts, PN_LIST, tc);
4753                 if (!list)
4754                     return NULL;
4755                 list->pn_type = TOK_XMLNAME;
4756                 list->pn_pos.begin = pn->pn_pos.begin;
4757                 PN_INIT_LIST_1(list, pn);
4758                 list->pn_extra = PNX_CANTFOLD;
4759                 pn = list;
4760             }
4761             pn->pn_pos.end = pn2->pn_pos.end;
4762             PN_APPEND(pn, pn2);
4763         }
4764     } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
4765 
4766     js_UngetToken(ts);
4767     return pn;
4768 }
4769 
4770 /*
4771  * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
4772  * at compile time into a JSXML tree.
4773  */
4774 #define XML_FOLDABLE(pn)        ((pn)->pn_arity == PN_LIST                    \
4775                                  ? ((pn)->pn_extra & PNX_CANTFOLD) == 0       \
4776                                  : (pn)->pn_type != TOK_LC)
4777 
4778 /*
4779  * Parse the productions:
4780  *
4781  *      XMLTagContent:
4782  *              XMLNameExpr
4783  *              XMLTagContent S XMLNameExpr S? = S? XMLAttr
4784  *              XMLTagContent S XMLNameExpr S? = S? { Expr }
4785  *
4786  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
4787  * produces a list of name and attribute values and/or braced expressions, a
4788  * single expression, or a single name.
4789  *
4790  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
4791  * XMLTagContent: XMLNameExpr.  If pn_type is not TOK_XMLNAME but pn_arity is
4792  * PN_LIST, pn_type will be tagtype.  If PN_UNARY, pn_type will be TOK_LC and
4793  * we parsed exactly one expression.
4794  */
4795 static JSParseNode *
XMLTagContent(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSTokenType tagtype,JSAtom ** namep)4796 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4797               JSTokenType tagtype, JSAtom **namep)
4798 {
4799     JSParseNode *pn, *pn2, *list;
4800     JSTokenType tt;
4801 
4802     pn = XMLNameExpr(cx, ts, tc);
4803     if (!pn)
4804         return NULL;
4805     *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
4806     list = NULL;
4807 
4808     while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
4809         tt = js_GetToken(cx, ts);
4810         if (tt != TOK_XMLNAME && tt != TOK_LC) {
4811             js_UngetToken(ts);
4812             break;
4813         }
4814 
4815         pn2 = XMLNameExpr(cx, ts, tc);
4816         if (!pn2)
4817             return NULL;
4818         if (!list) {
4819             list = NewParseNode(cx, ts, PN_LIST, tc);
4820             if (!list)
4821                 return NULL;
4822             list->pn_type = tagtype;
4823             list->pn_pos.begin = pn->pn_pos.begin;
4824             PN_INIT_LIST_1(list, pn);
4825             pn = list;
4826         }
4827         PN_APPEND(pn, pn2);
4828         if (!XML_FOLDABLE(pn2))
4829             pn->pn_extra |= PNX_CANTFOLD;
4830 
4831         js_MatchToken(cx, ts, TOK_XMLSPACE);
4832         MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
4833         js_MatchToken(cx, ts, TOK_XMLSPACE);
4834 
4835         tt = js_GetToken(cx, ts);
4836         if (tt == TOK_XMLATTR) {
4837             pn2 = XMLAtomNode(cx, ts, tc);
4838         } else if (tt == TOK_LC) {
4839             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
4840             pn->pn_extra |= PNX_CANTFOLD;
4841         } else {
4842             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4843                                         JSMSG_BAD_XML_ATTR_VALUE);
4844             return NULL;
4845         }
4846         if (!pn2)
4847             return NULL;
4848         pn->pn_pos.end = pn2->pn_pos.end;
4849         PN_APPEND(pn, pn2);
4850     }
4851 
4852     return pn;
4853 }
4854 
4855 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result)                                \
4856     JS_BEGIN_MACRO                                                            \
4857         if ((tt) <= TOK_EOF) {                                                \
4858             if ((tt) == TOK_EOF) {                                            \
4859                 js_ReportCompileErrorNumber(cx, ts,                           \
4860                                             JSREPORT_TS | JSREPORT_ERROR,     \
4861                                             JSMSG_END_OF_XML_SOURCE);         \
4862             }                                                                 \
4863             return result;                                                    \
4864         }                                                                     \
4865     JS_END_MACRO
4866 
4867 static JSParseNode *
4868 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4869                  JSBool allowList);
4870 
4871 /*
4872  * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
4873  * that opens the end tag for the container.
4874  */
4875 static JSBool
XMLElementContent(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSTreeContext * tc)4876 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
4877                   JSTreeContext *tc)
4878 {
4879     JSTokenType tt;
4880     JSParseNode *pn2;
4881     JSAtom *textAtom;
4882 
4883     ts->flags &= ~TSF_XMLTAGMODE;
4884     for (;;) {
4885         ts->flags |= TSF_XMLTEXTMODE;
4886         tt = js_GetToken(cx, ts);
4887         ts->flags &= ~TSF_XMLTEXTMODE;
4888         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
4889 
4890         JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
4891         textAtom = CURRENT_TOKEN(ts).t_atom;
4892         if (textAtom) {
4893             /* Non-zero-length XML text scanned. */
4894             pn2 = XMLAtomNode(cx, ts, tc);
4895             if (!pn2)
4896                 return JS_FALSE;
4897             pn->pn_pos.end = pn2->pn_pos.end;
4898             PN_APPEND(pn, pn2);
4899         }
4900 
4901         ts->flags |= TSF_OPERAND;
4902         tt = js_GetToken(cx, ts);
4903         ts->flags &= ~TSF_OPERAND;
4904         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
4905         if (tt == TOK_XMLETAGO)
4906             break;
4907 
4908         if (tt == TOK_LC) {
4909             pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
4910             pn->pn_extra |= PNX_CANTFOLD;
4911         } else if (tt == TOK_XMLSTAGO) {
4912             pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
4913             if (pn2) {
4914                 pn2->pn_extra &= ~PNX_XMLROOT;
4915                 pn->pn_extra |= pn2->pn_extra;
4916             }
4917         } else {
4918             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
4919                       tt == TOK_XMLPI);
4920             pn2 = XMLAtomNode(cx, ts, tc);
4921         }
4922         if (!pn2)
4923             return JS_FALSE;
4924         pn->pn_pos.end = pn2->pn_pos.end;
4925         PN_APPEND(pn, pn2);
4926     }
4927 
4928     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
4929     ts->flags |= TSF_XMLTAGMODE;
4930     return JS_TRUE;
4931 }
4932 
4933 /*
4934  * Return a PN_LIST node containing an XML or XMLList Initialiser.
4935  */
4936 static JSParseNode *
XMLElementOrList(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowList)4937 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4938                  JSBool allowList)
4939 {
4940     JSParseNode *pn, *pn2, *list;
4941     JSBool hadSpace;
4942     JSTokenType tt;
4943     JSAtom *startAtom, *endAtom;
4944 
4945     CHECK_RECURSION();
4946 
4947     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
4948     pn = NewParseNode(cx, ts, PN_LIST, tc);
4949     if (!pn)
4950         return NULL;
4951 
4952     ts->flags |= TSF_XMLTAGMODE;
4953     hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
4954     tt = js_GetToken(cx, ts);
4955     if (tt == TOK_ERROR)
4956         return NULL;
4957 
4958     if (tt == TOK_XMLNAME || tt == TOK_LC) {
4959         /*
4960          * XMLElement.  Append the tag and its contents, if any, to pn.
4961          */
4962         pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
4963         if (!pn2)
4964             return NULL;
4965         js_MatchToken(cx, ts, TOK_XMLSPACE);
4966 
4967         tt = js_GetToken(cx, ts);
4968         if (tt == TOK_XMLPTAGC) {
4969             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
4970             if (pn2->pn_type == TOK_XMLSTAGO) {
4971                 PN_INIT_LIST(pn);
4972                 RecycleTree(pn, tc);
4973                 pn = pn2;
4974             } else {
4975                 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
4976                           pn2->pn_type == TOK_LC);
4977                 PN_INIT_LIST_1(pn, pn2);
4978                 if (!XML_FOLDABLE(pn2))
4979                     pn->pn_extra |= PNX_CANTFOLD;
4980             }
4981             pn->pn_type = TOK_XMLPTAGC;
4982             pn->pn_extra |= PNX_XMLROOT;
4983         } else {
4984             /* We had better have a tag-close (>) at this point. */
4985             if (tt != TOK_XMLTAGC) {
4986                 js_ReportCompileErrorNumber(cx, ts,
4987                                             JSREPORT_TS | JSREPORT_ERROR,
4988                                             JSMSG_BAD_XML_TAG_SYNTAX);
4989                 return NULL;
4990             }
4991             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4992 
4993             /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
4994             if (pn2->pn_type != TOK_XMLSTAGO) {
4995                 PN_INIT_LIST_1(pn, pn2);
4996                 if (!XML_FOLDABLE(pn2))
4997                     pn->pn_extra |= PNX_CANTFOLD;
4998                 pn2 = pn;
4999                 pn = NewParseNode(cx, ts, PN_LIST, tc);
5000                 if (!pn)
5001                     return NULL;
5002             }
5003 
5004             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
5005             pn->pn_type = TOK_XMLELEM;
5006             PN_INIT_LIST_1(pn, pn2);
5007             if (!XML_FOLDABLE(pn2))
5008                 pn->pn_extra |= PNX_CANTFOLD;
5009             pn->pn_extra |= PNX_XMLROOT;
5010 
5011             /* Get element contents and delimiting end-tag-open sequence. */
5012             if (!XMLElementContent(cx, ts, pn, tc))
5013                 return NULL;
5014 
5015             js_MatchToken(cx, ts, TOK_XMLSPACE);
5016             tt = js_GetToken(cx, ts);
5017             XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
5018             if (tt != TOK_XMLNAME && tt != TOK_LC) {
5019                 js_ReportCompileErrorNumber(cx, ts,
5020                                             JSREPORT_TS | JSREPORT_ERROR,
5021                                             JSMSG_BAD_XML_TAG_SYNTAX);
5022                 return NULL;
5023             }
5024 
5025             /* Parse end tag; check mismatch at compile-time if we can. */
5026             pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
5027             if (!pn2)
5028                 return NULL;
5029             if (pn2->pn_type == TOK_XMLETAGO) {
5030                 /* Oops, end tag has attributes! */
5031                 js_ReportCompileErrorNumber(cx, ts,
5032                                             JSREPORT_TS | JSREPORT_ERROR,
5033                                             JSMSG_BAD_XML_TAG_SYNTAX);
5034                 return NULL;
5035             }
5036             if (endAtom && startAtom && endAtom != startAtom) {
5037                 JSString *str = ATOM_TO_STRING(startAtom);
5038 
5039                 /* End vs. start tag name mismatch: point to the tag name. */
5040                 js_ReportCompileErrorNumberUC(cx, pn2,
5041                                               JSREPORT_PN | JSREPORT_ERROR,
5042                                               JSMSG_XML_TAG_NAME_MISMATCH,
5043                                               JSSTRING_CHARS(str));
5044                 return NULL;
5045             }
5046 
5047             /* Make a TOK_XMLETAGO list with pn2 as its single child. */
5048             JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
5049             list = NewParseNode(cx, ts, PN_LIST, tc);
5050             if (!list)
5051                 return NULL;
5052             list->pn_type = TOK_XMLETAGO;
5053             PN_INIT_LIST_1(list, pn2);
5054             PN_APPEND(pn, list);
5055             if (!XML_FOLDABLE(pn2)) {
5056                 list->pn_extra |= PNX_CANTFOLD;
5057                 pn->pn_extra |= PNX_CANTFOLD;
5058             }
5059 
5060             js_MatchToken(cx, ts, TOK_XMLSPACE);
5061             MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
5062         }
5063 
5064         /* Set pn_op now that pn has been updated to its final value. */
5065         pn->pn_op = JSOP_TOXML;
5066     } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
5067         /* XMLList Initialiser. */
5068         pn->pn_type = TOK_XMLLIST;
5069         pn->pn_op = JSOP_TOXMLLIST;
5070         PN_INIT_LIST(pn);
5071         pn->pn_extra |= PNX_XMLROOT;
5072         if (!XMLElementContent(cx, ts, pn, tc))
5073             return NULL;
5074 
5075         MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
5076     } else {
5077         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5078                                     JSMSG_BAD_XML_NAME_SYNTAX);
5079         return NULL;
5080     }
5081 
5082     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5083     ts->flags &= ~TSF_XMLTAGMODE;
5084     return pn;
5085 }
5086 
5087 static JSParseNode *
XMLElementOrListRoot(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowList)5088 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5089                      JSBool allowList)
5090 {
5091     uint32 oldopts;
5092     JSParseNode *pn;
5093 
5094     /*
5095      * Force XML support to be enabled so that comments and CDATA literals
5096      * are recognized, instead of <! followed by -- starting an HTML comment
5097      * to end of line (used in script tags to hide content from old browsers
5098      * that don't recognize <script>).
5099      */
5100     oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
5101     pn = XMLElementOrList(cx, ts, tc, allowList);
5102     JS_SetOptions(cx, oldopts);
5103     return pn;
5104 }
5105 
5106 JS_FRIEND_API(JSParseNode *)
js_ParseXMLTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts,JSBool allowList)5107 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
5108                        JSBool allowList)
5109 {
5110     JSStackFrame *fp, frame;
5111     JSParseNode *pn;
5112     JSTreeContext tc;
5113     JSTokenType tt;
5114 
5115     /*
5116      * Push a compiler frame if we have no frames, or if the top frame is a
5117      * lightweight function activation, or if its scope chain doesn't match
5118      * the one passed to us.
5119      */
5120     fp = cx->fp;
5121     MaybeSetupFrame(cx, chain, fp, &frame);
5122     JS_KEEP_ATOMS(cx->runtime);
5123     TREE_CONTEXT_INIT(&tc);
5124 
5125     /* Set XML-only mode to turn off special treatment of {expr} in XML. */
5126     ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
5127     tt = js_GetToken(cx, ts);
5128     ts->flags &= ~TSF_OPERAND;
5129 
5130     if (tt != TOK_XMLSTAGO) {
5131         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5132                                     JSMSG_BAD_XML_MARKUP);
5133         pn = NULL;
5134     } else {
5135         pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
5136     }
5137 
5138     ts->flags &= ~TSF_XMLONLYMODE;
5139     TREE_CONTEXT_FINISH(&tc);
5140     JS_UNKEEP_ATOMS(cx->runtime);
5141     cx->fp = fp;
5142     return pn;
5143 }
5144 
5145 #endif /* JS_HAS_XMLSUPPORT */
5146 
5147 static JSParseNode *
PrimaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSTokenType tt,JSBool afterDot)5148 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5149             JSTokenType tt, JSBool afterDot)
5150 {
5151     JSParseNode *pn, *pn2, *pn3;
5152     JSOp op;
5153 
5154 #if JS_HAS_SHARP_VARS
5155     JSParseNode *defsharp;
5156     JSBool notsharp;
5157 
5158     defsharp = NULL;
5159     notsharp = JS_FALSE;
5160   again:
5161     /*
5162      * Control flows here after #n= is scanned.  If the following primary is
5163      * not valid after such a "sharp variable" definition, the tt switch case
5164      * should set notsharp.
5165      */
5166 #endif
5167 
5168     CHECK_RECURSION();
5169 
5170 #if JS_HAS_GETTER_SETTER
5171     if (tt == TOK_NAME) {
5172         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
5173         if (tt == TOK_ERROR)
5174             return NULL;
5175     }
5176 #endif
5177 
5178     switch (tt) {
5179       case TOK_FUNCTION:
5180 #if JS_HAS_XML_SUPPORT
5181         ts->flags |= TSF_KEYWORD_IS_NAME;
5182         if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
5183             ts->flags &= ~TSF_KEYWORD_IS_NAME;
5184             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
5185             if (!pn2)
5186                 return NULL;
5187             pn2->pn_type = TOK_FUNCTION;
5188             pn = QualifiedSuffix(cx, ts, pn2, tc);
5189             if (!pn)
5190                 return NULL;
5191             break;
5192         }
5193         ts->flags &= ~TSF_KEYWORD_IS_NAME;
5194 #endif
5195         pn = FunctionExpr(cx, ts, tc);
5196         if (!pn)
5197             return NULL;
5198         break;
5199 
5200       case TOK_LB:
5201       {
5202         JSBool matched;
5203         jsuint index;
5204 
5205         pn = NewParseNode(cx, ts, PN_LIST, tc);
5206         if (!pn)
5207             return NULL;
5208         pn->pn_type = TOK_RB;
5209 
5210 #if JS_HAS_SHARP_VARS
5211         if (defsharp) {
5212             PN_INIT_LIST_1(pn, defsharp);
5213             defsharp = NULL;
5214         } else
5215 #endif
5216             PN_INIT_LIST(pn);
5217 
5218         ts->flags |= TSF_OPERAND;
5219         matched = js_MatchToken(cx, ts, TOK_RB);
5220         ts->flags &= ~TSF_OPERAND;
5221         if (!matched) {
5222             for (index = 0; ; index++) {
5223                 if (index == ARRAY_INIT_LIMIT) {
5224                     js_ReportCompileErrorNumber(cx, ts,
5225                                                 JSREPORT_TS | JSREPORT_ERROR,
5226                                                 JSMSG_ARRAY_INIT_TOO_BIG);
5227                     return NULL;
5228                 }
5229 
5230                 ts->flags |= TSF_OPERAND;
5231                 tt = js_PeekToken(cx, ts);
5232                 ts->flags &= ~TSF_OPERAND;
5233                 if (tt == TOK_RB) {
5234                     pn->pn_extra |= PNX_ENDCOMMA;
5235                     break;
5236                 }
5237 
5238                 if (tt == TOK_COMMA) {
5239                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
5240                     js_MatchToken(cx, ts, TOK_COMMA);
5241                     pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
5242                 } else {
5243                     pn2 = AssignExpr(cx, ts, tc);
5244                 }
5245                 if (!pn2)
5246                     return NULL;
5247                 PN_APPEND(pn, pn2);
5248 
5249                 if (tt != TOK_COMMA) {
5250                     /* If we didn't already match TOK_COMMA in above case. */
5251                     if (!js_MatchToken(cx, ts, TOK_COMMA))
5252                         break;
5253                 }
5254             }
5255 
5256 #if JS_HAS_GENERATORS
5257             /*
5258              * At this point, (index == 0 && pn->pn_count != 0) implies one
5259              * element initialiser was parsed (possibly with a defsharp before
5260              * the left bracket).
5261              *
5262              * An array comprehension of the form:
5263              *
5264              *   [i * j for (i in o) for (j in p) if (i != j)]
5265              *
5266              * translates to roughly the following let expression:
5267              *
5268              *   let (array = new Array, i, j) {
5269              *     for (i in o) let {
5270              *       for (j in p)
5271              *         if (i != j)
5272              *           array.push(i * j)
5273              *     }
5274              *     array
5275              *   }
5276              *
5277              * where array is a nameless block-local variable.  The "roughly"
5278              * means that an implementation may optimize away the array.push.
5279              * An array comprehension opens exactly one block scope, no matter
5280              * how many for heads it contains.
5281              *
5282              * Each let () {...} or for (let ...) ... compiles to:
5283              *
5284              *   JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
5285              *
5286              * where <o> is a literal object representing the block scope,
5287              * with <n> properties, naming each var declared in the block.
5288              *
5289              * Each var declaration in a let-block binds a name in <o> at
5290              * compile time, and allocates a slot on the operand stack at
5291              * runtime via JSOP_ENTERBLOCK.  A block-local var is accessed
5292              * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
5293              * JSOP_FORLOCAL.  These ops all have an immediate operand, the
5294              * local slot's stack index from fp->spbase.
5295              *
5296              * The array comprehension iteration step, array.push(i * j) in
5297              * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
5298              * where <array> is the index of array's stack slot.
5299              */
5300             if (index == 0 &&
5301                 pn->pn_count != 0 &&
5302                 js_MatchToken(cx, ts, TOK_FOR)) {
5303                 JSParseNode **pnp, *pnexp, *pntop, *pnlet;
5304                 BindData data;
5305                 JSRuntime *rt;
5306                 JSStmtInfo stmtInfo;
5307                 JSAtom *atom;
5308 
5309                 /* Relabel pn as an array comprehension node. */
5310                 pn->pn_type = TOK_ARRAYCOMP;
5311 
5312                 /*
5313                  * Remove the comprehension expression from pn's linked list
5314                  * and save it via pnexp.  We'll re-install it underneath the
5315                  * ARRAYPUSH node after we parse the rest of the comprehension.
5316                  */
5317                 pnexp = PN_LAST(pn);
5318                 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
5319                 pn->pn_tail = (--pn->pn_count == 1)
5320                               ? &pn->pn_head->pn_next
5321                               : &pn->pn_head;
5322                 *pn->pn_tail = NULL;
5323 
5324                 /*
5325                  * Make a parse-node and literal object representing the array
5326                  * comprehension's block scope.
5327                  */
5328                 pntop = PushLexicalScope(cx, ts, tc, &stmtInfo);
5329                 if (!pntop)
5330                     return NULL;
5331                 pnp = &pntop->pn_expr;
5332 
5333                 data.pn = NULL;
5334                 data.ts = ts;
5335                 data.obj = tc->blockChain;
5336                 data.op = JSOP_NOP;
5337                 data.binder = BindLet;
5338                 data.u.let.index = 0;
5339                 data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
5340 
5341                 rt = cx->runtime;
5342                 do {
5343                     /*
5344                      * FOR node is binary, left is control and right is body.
5345                      * Use index to count each block-local let-variable on the
5346                      * left-hand side of IN.
5347                      */
5348                     pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
5349                     if (!pn2)
5350                         return NULL;
5351 
5352                     pn2->pn_op = JSOP_FORIN;
5353                     if (js_MatchToken(cx, ts, TOK_NAME)) {
5354                         if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
5355                             pn2->pn_op = JSOP_FOREACH;
5356                         else
5357                             js_UngetToken(ts);
5358                     }
5359                     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
5360 
5361                     tt = js_GetToken(cx, ts);
5362                     switch (tt) {
5363 #if JS_HAS_DESTRUCTURING
5364                       case TOK_LB:
5365                       case TOK_LC:
5366                         pnlet = DestructuringExpr(cx, &data, tc, tt);
5367                         if (!pnlet)
5368                             return NULL;
5369 
5370                         if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) {
5371                             js_ReportCompileErrorNumber(cx, ts,
5372                                                         JSREPORT_TS |
5373                                                         JSREPORT_ERROR,
5374                                                         JSMSG_BAD_FOR_LEFTSIDE);
5375                             return NULL;
5376                         }
5377 
5378                         /* Destructuring requires [key, value] enumeration. */
5379                         if (pn2->pn_op != JSOP_FOREACH)
5380                             pn2->pn_op = JSOP_FOREACHKEYVAL;
5381                         break;
5382 #endif
5383 
5384                       case TOK_NAME:
5385                         atom = CURRENT_TOKEN(ts).t_atom;
5386                         if (!data.binder(cx, &data, atom, tc))
5387                             return NULL;
5388 
5389                         /*
5390                          * Create a name node with op JSOP_NAME.  We can't set
5391                          * op to JSOP_GETLOCAL here, because we don't yet know
5392                          * the block's depth in the operand stack frame.  The
5393                          * code generator computes that, and it tries to bind
5394                          * all names to slots, so we must let it do the deed.
5395                          */
5396                         pnlet = NewParseNode(cx, ts, PN_NAME, tc);
5397                         if (!pnlet)
5398                             return NULL;
5399                         pnlet->pn_op = JSOP_NAME;
5400                         pnlet->pn_atom = atom;
5401                         pnlet->pn_expr = NULL;
5402                         pnlet->pn_slot = -1;
5403                         pnlet->pn_attrs = 0;
5404                         break;
5405 
5406                       default:
5407                         js_ReportCompileErrorNumber(cx, ts,
5408                                                     JSREPORT_TS|JSREPORT_ERROR,
5409                                                     JSMSG_NO_VARIABLE_NAME);
5410                         return NULL;
5411                     }
5412 
5413                     MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
5414                     pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet,
5415                                     Expr(cx, ts, tc), tc);
5416                     if (!pn3)
5417                         return NULL;
5418 
5419                     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5420                     pn2->pn_left = pn3;
5421                     *pnp = pn2;
5422                     pnp = &pn2->pn_right;
5423                 } while (js_MatchToken(cx, ts, TOK_FOR));
5424 
5425                 if (js_MatchToken(cx, ts, TOK_IF)) {
5426                     pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
5427                     if (!pn2)
5428                         return NULL;
5429                     pn2->pn_kid1 = Condition(cx, ts, tc);
5430                     if (!pn2->pn_kid1)
5431                         return NULL;
5432                     pn2->pn_kid2 = NULL;
5433                     pn2->pn_kid3 = NULL;
5434                     *pnp = pn2;
5435                     pnp = &pn2->pn_kid2;
5436                 }
5437 
5438                 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
5439                 if (!pn2)
5440                     return NULL;
5441                 pn2->pn_type = TOK_ARRAYPUSH;
5442                 pn2->pn_op = JSOP_ARRAYPUSH;
5443                 pn2->pn_kid = pnexp;
5444                 *pnp = pn2;
5445                 PN_APPEND(pn, pntop);
5446 
5447                 js_PopStatement(tc);
5448             }
5449 #endif /* JS_HAS_GENERATORS */
5450 
5451             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
5452         }
5453         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5454         return pn;
5455       }
5456 
5457 #if JS_HAS_BLOCK_SCOPE
5458       case TOK_LET:
5459         pn = LetBlock(cx, ts, tc, JS_FALSE);
5460         if (!pn)
5461             return NULL;
5462         break;
5463 #endif
5464 
5465       case TOK_LC:
5466       {
5467         JSBool afterComma;
5468 
5469         pn = NewParseNode(cx, ts, PN_LIST, tc);
5470         if (!pn)
5471             return NULL;
5472         pn->pn_type = TOK_RC;
5473 
5474 #if JS_HAS_SHARP_VARS
5475         if (defsharp) {
5476             PN_INIT_LIST_1(pn, defsharp);
5477             defsharp = NULL;
5478         } else
5479 #endif
5480             PN_INIT_LIST(pn);
5481 
5482         afterComma = JS_FALSE;
5483         for (;;) {
5484             ts->flags |= TSF_KEYWORD_IS_NAME;
5485             tt = js_GetToken(cx, ts);
5486             ts->flags &= ~TSF_KEYWORD_IS_NAME;
5487             switch (tt) {
5488               case TOK_NUMBER:
5489                 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5490                 if (pn3)
5491                     pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
5492                 break;
5493               case TOK_NAME:
5494 #if JS_HAS_GETTER_SETTER
5495                 {
5496                     JSAtom *atom;
5497                     JSRuntime *rt;
5498 
5499                     atom = CURRENT_TOKEN(ts).t_atom;
5500                     rt = cx->runtime;
5501                     if (atom == rt->atomState.getAtom ||
5502                         atom == rt->atomState.setAtom) {
5503                         op = (atom == rt->atomState.getAtom)
5504                             ? JSOP_GETTER
5505                             : JSOP_SETTER;
5506                         if (js_MatchToken(cx, ts, TOK_NAME)) {
5507                             pn3 = NewParseNode(cx, ts, PN_NAME, tc);
5508                             if (!pn3)
5509                                 return NULL;
5510                             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5511                             pn3->pn_expr = NULL;
5512                             pn3->pn_slot = -1;
5513                             pn3->pn_attrs = 0;
5514 
5515                             /* We have to fake a 'function' token here. */
5516                             CURRENT_TOKEN(ts).t_op = JSOP_NOP;
5517                             CURRENT_TOKEN(ts).type = TOK_FUNCTION;
5518                             pn2 = FunctionExpr(cx, ts, tc);
5519                             pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
5520                             goto skip;
5521                         }
5522                     }
5523                     /* else fall thru ... */
5524                 }
5525 #endif
5526               case TOK_STRING:
5527                 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5528                 if (pn3)
5529                     pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5530                 break;
5531               case TOK_RC:
5532                 if (afterComma &&
5533                     !js_ReportCompileErrorNumber(cx, ts,
5534                                                  JSREPORT_TS |
5535                                                  JSREPORT_WARNING |
5536                                                  JSREPORT_STRICT,
5537                                                  JSMSG_TRAILING_COMMA)) {
5538                         return NULL;
5539                 }
5540                 goto end_obj_init;
5541               default:
5542                 js_ReportCompileErrorNumber(cx, ts,
5543                                             JSREPORT_TS | JSREPORT_ERROR,
5544                                             JSMSG_BAD_PROP_ID);
5545                 return NULL;
5546             }
5547 
5548             tt = js_GetToken(cx, ts);
5549 #if JS_HAS_GETTER_SETTER
5550             if (tt == TOK_NAME) {
5551                 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
5552                 if (tt == TOK_ERROR)
5553                     return NULL;
5554             }
5555 #endif
5556             if (tt != TOK_COLON) {
5557                 js_ReportCompileErrorNumber(cx, ts,
5558                                             JSREPORT_TS | JSREPORT_ERROR,
5559                                             JSMSG_COLON_AFTER_ID);
5560                 return NULL;
5561             }
5562             op = CURRENT_TOKEN(ts).t_op;
5563             pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc);
5564 #if JS_HAS_GETTER_SETTER
5565           skip:
5566 #endif
5567             if (!pn2)
5568                 return NULL;
5569             PN_APPEND(pn, pn2);
5570 
5571             tt = js_GetToken(cx, ts);
5572             if (tt == TOK_RC)
5573                 goto end_obj_init;
5574             if (tt != TOK_COMMA) {
5575                 js_ReportCompileErrorNumber(cx, ts,
5576                                             JSREPORT_TS | JSREPORT_ERROR,
5577                                             JSMSG_CURLY_AFTER_LIST);
5578                 return NULL;
5579             }
5580             afterComma = JS_TRUE;
5581         }
5582      end_obj_init:
5583         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5584         return pn;
5585       }
5586 
5587 #if JS_HAS_SHARP_VARS
5588       case TOK_DEFSHARP:
5589         if (defsharp)
5590             goto badsharp;
5591         defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
5592         if (!defsharp)
5593             return NULL;
5594         defsharp->pn_kid = NULL;
5595         defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
5596         ts->flags |= TSF_OPERAND;
5597         tt = js_GetToken(cx, ts);
5598         ts->flags &= ~TSF_OPERAND;
5599         goto again;
5600 
5601       case TOK_USESHARP:
5602         /* Check for forward/dangling references at runtime, to allow eval. */
5603         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5604         if (!pn)
5605             return NULL;
5606         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
5607         notsharp = JS_TRUE;
5608         break;
5609 #endif /* JS_HAS_SHARP_VARS */
5610 
5611       case TOK_LP:
5612         pn = NewParseNode(cx, ts, PN_UNARY, tc);
5613         if (!pn)
5614             return NULL;
5615         pn2 = BracketedExpr(cx, ts, tc);
5616         if (!pn2)
5617             return NULL;
5618 
5619         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
5620         if (pn2->pn_type == TOK_RP ||
5621             (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
5622              !afterDot)) {
5623             /*
5624              * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly
5625              * to help the decompiler look ahead from a JSOP_ENDINIT to see a
5626              * JSOP_GROUP followed by a POP or POPV.  That sequence means the
5627              * parentheses are mandatory, to disambiguate object initialisers
5628              * as expression statements from block statements.
5629              *
5630              * Also drop pn if pn2 is a member or a primary expression of any
5631              * kind.  This is required to avoid generating a JSOP_GROUP that
5632              * will null the |obj| interpreter register, causing |this| in any
5633              * call of that member expression to bind to the global object.
5634              */
5635             pn->pn_kid = NULL;
5636             RecycleTree(pn, tc);
5637             pn = pn2;
5638         } else {
5639             pn->pn_type = TOK_RP;
5640             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5641             pn->pn_kid = pn2;
5642         }
5643         break;
5644 
5645 #if JS_HAS_XML_SUPPORT
5646       case TOK_STAR:
5647         pn = QualifiedIdentifier(cx, ts, tc);
5648         if (!pn)
5649             return NULL;
5650         notsharp = JS_TRUE;
5651         break;
5652 
5653       case TOK_AT:
5654         pn = AttributeIdentifier(cx, ts, tc);
5655         if (!pn)
5656             return NULL;
5657         notsharp = JS_TRUE;
5658         break;
5659 
5660       case TOK_XMLSTAGO:
5661         pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
5662         if (!pn)
5663             return NULL;
5664         notsharp = JS_TRUE;     /* XXXbe could be sharp? */
5665         break;
5666 #endif /* JS_HAS_XML_SUPPORT */
5667 
5668       case TOK_STRING:
5669 #if JS_HAS_SHARP_VARS
5670         notsharp = JS_TRUE;
5671         /* FALL THROUGH */
5672 #endif
5673 
5674 #if JS_HAS_XML_SUPPORT
5675       case TOK_XMLCDATA:
5676       case TOK_XMLCOMMENT:
5677       case TOK_XMLPI:
5678 #endif
5679       case TOK_NAME:
5680       case TOK_OBJECT:
5681         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5682         if (!pn)
5683             return NULL;
5684         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
5685 #if JS_HAS_XML_SUPPORT
5686         if (tt == TOK_XMLPI)
5687             pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
5688         else
5689 #endif
5690             pn->pn_op = CURRENT_TOKEN(ts).t_op;
5691         if (tt == TOK_NAME) {
5692             pn->pn_arity = PN_NAME;
5693             pn->pn_expr = NULL;
5694             pn->pn_slot = -1;
5695             pn->pn_attrs = 0;
5696 
5697 #if JS_HAS_XML_SUPPORT
5698             if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
5699                 if (afterDot) {
5700                     JSString *str;
5701 
5702                     /*
5703                      * Here PrimaryExpr is called after '.' or '..' and we
5704                      * just scanned .name:: or ..name:: . This is the only
5705                      * case where a keyword after '.' or '..' is not
5706                      * treated as a property name.
5707                      */
5708                     str = ATOM_TO_STRING(pn->pn_atom);
5709                     tt = js_CheckKeyword(JSSTRING_CHARS(str),
5710                                          JSSTRING_LENGTH(str));
5711                     if (tt == TOK_FUNCTION) {
5712                         pn->pn_arity = PN_NULLARY;
5713                         pn->pn_type = TOK_FUNCTION;
5714                     } else if (tt != TOK_EOF) {
5715                         js_ReportCompileErrorNumber(
5716                             cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5717                             JSMSG_KEYWORD_NOT_NS);
5718                         return NULL;
5719                     }
5720                 }
5721                 pn = QualifiedSuffix(cx, ts, pn, tc);
5722                 if (!pn)
5723                     return NULL;
5724                 break;
5725             }
5726 #endif
5727 
5728             /* Unqualified __parent__ and __proto__ uses require activations. */
5729             if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
5730                 pn->pn_atom == cx->runtime->atomState.protoAtom) {
5731                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5732             } else {
5733                 JSAtomListElement *ale;
5734                 JSStackFrame *fp;
5735                 JSBool loopy;
5736 
5737                 /* Measure optimizable global variable uses. */
5738                 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
5739                 if (ale &&
5740                     !(fp = cx->fp)->fun &&
5741                     fp->scopeChain == fp->varobj &&
5742                     js_IsGlobalReference(tc, pn->pn_atom, &loopy)) {
5743                     tc->globalUses++;
5744                     if (loopy)
5745                         tc->loopyGlobalUses++;
5746                 }
5747             }
5748         }
5749         break;
5750 
5751       case TOK_NUMBER:
5752         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5753         if (!pn)
5754             return NULL;
5755         pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
5756 #if JS_HAS_SHARP_VARS
5757         notsharp = JS_TRUE;
5758 #endif
5759         break;
5760 
5761       case TOK_PRIMARY:
5762         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5763         if (!pn)
5764             return NULL;
5765         pn->pn_op = CURRENT_TOKEN(ts).t_op;
5766 #if JS_HAS_SHARP_VARS
5767         notsharp = JS_TRUE;
5768 #endif
5769         break;
5770 
5771 #if !JS_HAS_EXPORT_IMPORT
5772       case TOK_EXPORT:
5773       case TOK_IMPORT:
5774 #endif
5775       case TOK_ERROR:
5776         /* The scanner or one of its subroutines reported the error. */
5777         return NULL;
5778 
5779       default:
5780         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5781                                     JSMSG_SYNTAX_ERROR);
5782         return NULL;
5783     }
5784 
5785 #if JS_HAS_SHARP_VARS
5786     if (defsharp) {
5787         if (notsharp) {
5788   badsharp:
5789             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5790                                         JSMSG_BAD_SHARP_VAR_DEF);
5791             return NULL;
5792         }
5793         defsharp->pn_kid = pn;
5794         return defsharp;
5795     }
5796 #endif
5797     return pn;
5798 }
5799 
5800 /*
5801  * Fold from one constant type to another.
5802  * XXX handles only strings and numbers for now
5803  */
5804 static JSBool
FoldType(JSContext * cx,JSParseNode * pn,JSTokenType type)5805 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
5806 {
5807     if (pn->pn_type != type) {
5808         switch (type) {
5809           case TOK_NUMBER:
5810             if (pn->pn_type == TOK_STRING) {
5811                 jsdouble d;
5812                 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
5813                     return JS_FALSE;
5814                 pn->pn_dval = d;
5815                 pn->pn_type = TOK_NUMBER;
5816                 pn->pn_op = JSOP_NUMBER;
5817             }
5818             break;
5819 
5820           case TOK_STRING:
5821             if (pn->pn_type == TOK_NUMBER) {
5822                 JSString *str = js_NumberToString(cx, pn->pn_dval);
5823                 if (!str)
5824                     return JS_FALSE;
5825                 pn->pn_atom = js_AtomizeString(cx, str, 0);
5826                 if (!pn->pn_atom)
5827                     return JS_FALSE;
5828                 pn->pn_type = TOK_STRING;
5829                 pn->pn_op = JSOP_STRING;
5830             }
5831             break;
5832 
5833           default:;
5834         }
5835     }
5836     return JS_TRUE;
5837 }
5838 
5839 /*
5840  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
5841  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
5842  * a successful call to this function.
5843  */
5844 static JSBool
FoldBinaryNumeric(JSContext * cx,JSOp op,JSParseNode * pn1,JSParseNode * pn2,JSParseNode * pn,JSTreeContext * tc)5845 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
5846                   JSParseNode *pn, JSTreeContext *tc)
5847 {
5848     jsdouble d, d2;
5849     int32 i, j;
5850     uint32 u;
5851 
5852     JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
5853     d = pn1->pn_dval;
5854     d2 = pn2->pn_dval;
5855     switch (op) {
5856       case JSOP_LSH:
5857       case JSOP_RSH:
5858         if (!js_DoubleToECMAInt32(cx, d, &i))
5859             return JS_FALSE;
5860         if (!js_DoubleToECMAInt32(cx, d2, &j))
5861             return JS_FALSE;
5862         j &= 31;
5863         d = (op == JSOP_LSH) ? i << j : i >> j;
5864         break;
5865 
5866       case JSOP_URSH:
5867         if (!js_DoubleToECMAUint32(cx, d, &u))
5868             return JS_FALSE;
5869         if (!js_DoubleToECMAInt32(cx, d2, &j))
5870             return JS_FALSE;
5871         j &= 31;
5872         d = u >> j;
5873         break;
5874 
5875       case JSOP_ADD:
5876         d += d2;
5877         break;
5878 
5879       case JSOP_SUB:
5880         d -= d2;
5881         break;
5882 
5883       case JSOP_MUL:
5884         d *= d2;
5885         break;
5886 
5887       case JSOP_DIV:
5888         if (d2 == 0) {
5889 #if defined(XP_WIN)
5890             /* XXX MSVC miscompiles such that (NaN == 0) */
5891             if (JSDOUBLE_IS_NaN(d2))
5892                 d = *cx->runtime->jsNaN;
5893             else
5894 #endif
5895             if (d == 0 || JSDOUBLE_IS_NaN(d))
5896                 d = *cx->runtime->jsNaN;
5897             else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
5898                 d = *cx->runtime->jsNegativeInfinity;
5899             else
5900                 d = *cx->runtime->jsPositiveInfinity;
5901         } else {
5902             d /= d2;
5903         }
5904         break;
5905 
5906       case JSOP_MOD:
5907         if (d2 == 0) {
5908             d = *cx->runtime->jsNaN;
5909         } else {
5910 #if defined(XP_WIN)
5911           /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
5912           if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
5913 #endif
5914             d = fmod(d, d2);
5915         }
5916         break;
5917 
5918       default:;
5919     }
5920 
5921     /* Take care to allow pn1 or pn2 to alias pn. */
5922     if (pn1 != pn)
5923         RecycleTree(pn1, tc);
5924     if (pn2 != pn)
5925         RecycleTree(pn2, tc);
5926     pn->pn_type = TOK_NUMBER;
5927     pn->pn_op = JSOP_NUMBER;
5928     pn->pn_arity = PN_NULLARY;
5929     pn->pn_dval = d;
5930     return JS_TRUE;
5931 }
5932 
5933 #if JS_HAS_XML_SUPPORT
5934 
5935 static JSBool
FoldXMLConstants(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)5936 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
5937 {
5938     JSTokenType tt;
5939     JSParseNode **pnp, *pn1, *pn2;
5940     JSString *accum, *str;
5941     uint32 i, j;
5942 
5943     JS_ASSERT(pn->pn_arity == PN_LIST);
5944     tt = pn->pn_type;
5945     pnp = &pn->pn_head;
5946     pn1 = *pnp;
5947     accum = NULL;
5948     if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
5949         if (tt == TOK_XMLETAGO)
5950             accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
5951         else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
5952             accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
5953     }
5954 
5955     for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
5956         /* The parser already rejected end-tags with attributes. */
5957         JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
5958         switch (pn2->pn_type) {
5959           case TOK_XMLATTR:
5960             if (!accum)
5961                 goto cantfold;
5962             /* FALL THROUGH */
5963           case TOK_XMLNAME:
5964           case TOK_XMLSPACE:
5965           case TOK_XMLTEXT:
5966           case TOK_STRING:
5967             if (pn2->pn_arity == PN_LIST)
5968                 goto cantfold;
5969             str = ATOM_TO_STRING(pn2->pn_atom);
5970             break;
5971 
5972           case TOK_XMLCDATA:
5973             str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
5974             if (!str)
5975                 return JS_FALSE;
5976             break;
5977 
5978           case TOK_XMLCOMMENT:
5979             str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
5980             if (!str)
5981                 return JS_FALSE;
5982             break;
5983 
5984           case TOK_XMLPI:
5985             str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
5986                                          ATOM_TO_STRING(pn2->pn_atom2));
5987             if (!str)
5988                 return JS_FALSE;
5989             break;
5990 
5991           cantfold:
5992           default:
5993             JS_ASSERT(*pnp == pn1);
5994             if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
5995                 (i & 1) ^ (j & 1)) {
5996 #ifdef DEBUG_brendanXXX
5997                 printf("1: %d, %d => %s\n",
5998                        i, j, accum ? JS_GetStringBytes(accum) : "NULL");
5999 #endif
6000             } else if (accum && pn1 != pn2) {
6001                 while (pn1->pn_next != pn2) {
6002                     pn1 = RecycleTree(pn1, tc);
6003                     --pn->pn_count;
6004                 }
6005                 pn1->pn_type = TOK_XMLTEXT;
6006                 pn1->pn_op = JSOP_STRING;
6007                 pn1->pn_arity = PN_NULLARY;
6008                 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
6009                 if (!pn1->pn_atom)
6010                     return JS_FALSE;
6011                 JS_ASSERT(pnp != &pn1->pn_next);
6012                 *pnp = pn1;
6013             }
6014             pnp = &pn2->pn_next;
6015             pn1 = *pnp;
6016             accum = NULL;
6017             continue;
6018         }
6019 
6020         if (accum) {
6021             str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
6022                   ? js_AddAttributePart(cx, i & 1, accum, str)
6023                   : js_ConcatStrings(cx, accum, str);
6024             if (!str)
6025                 return JS_FALSE;
6026 #ifdef DEBUG_brendanXXX
6027             printf("2: %d, %d => %s (%u)\n",
6028                    i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str));
6029 #endif
6030             ++j;
6031         }
6032         accum = str;
6033     }
6034 
6035     if (accum) {
6036         str = NULL;
6037         if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
6038             if (tt == TOK_XMLPTAGC)
6039                 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
6040             else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
6041                 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
6042         }
6043         if (str) {
6044             accum = js_ConcatStrings(cx, accum, str);
6045             if (!accum)
6046                 return JS_FALSE;
6047         }
6048 
6049         JS_ASSERT(*pnp == pn1);
6050         while (pn1->pn_next) {
6051             pn1 = RecycleTree(pn1, tc);
6052             --pn->pn_count;
6053         }
6054         pn1->pn_type = TOK_XMLTEXT;
6055         pn1->pn_op = JSOP_STRING;
6056         pn1->pn_arity = PN_NULLARY;
6057         pn1->pn_atom = js_AtomizeString(cx, accum, 0);
6058         if (!pn1->pn_atom)
6059             return JS_FALSE;
6060         JS_ASSERT(pnp != &pn1->pn_next);
6061         *pnp = pn1;
6062     }
6063 
6064     if (pn1 && pn->pn_count == 1) {
6065         /*
6066          * Only one node under pn, and it has been folded: move pn1 onto pn
6067          * unless pn is an XML root (in which case we need it to tell the code
6068          * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op).  If pn is an
6069          * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
6070          * extra "<" and "/>" bracketing at runtime.
6071          */
6072         if (!(pn->pn_extra & PNX_XMLROOT)) {
6073             PN_MOVE_NODE(pn, pn1);
6074         } else if (tt == TOK_XMLPTAGC) {
6075             pn->pn_type = TOK_XMLELEM;
6076             pn->pn_op = JSOP_TOXML;
6077         }
6078     }
6079     return JS_TRUE;
6080 }
6081 
6082 #endif /* JS_HAS_XML_SUPPORT */
6083 
6084 static JSBool
StartsWith(JSParseNode * pn,JSTokenType tt)6085 StartsWith(JSParseNode *pn, JSTokenType tt)
6086 {
6087 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
6088 
6089 recur:
6090     if (pn->pn_type == tt)
6091         return JS_TRUE;
6092     switch (pn->pn_arity) {
6093       case PN_FUNC:
6094         return  tt == TOK_FUNCTION;
6095       case PN_LIST:
6096         if (pn->pn_head)
6097             TAIL_RECURSE(pn->pn_head);
6098         break;
6099       case PN_TERNARY:
6100         if (pn->pn_kid1)
6101             TAIL_RECURSE(pn->pn_kid1);
6102         break;
6103       case PN_BINARY:
6104         if (pn->pn_left)
6105             TAIL_RECURSE(pn->pn_left);
6106         break;
6107       case PN_UNARY:
6108         /* A parenthesized expression starts with a left parenthesis. */
6109         if (pn->pn_type == TOK_RP)
6110             return tt == TOK_LP;
6111         if (pn->pn_kid)
6112             TAIL_RECURSE(pn->pn_kid);
6113         break;
6114       case PN_NAME:
6115         if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
6116             TAIL_RECURSE(pn->pn_expr);
6117         /* FALL THROUGH */
6118     }
6119     return JS_FALSE;
6120 #undef TAIL_RECURSE
6121 }
6122 
6123 JSBool
js_FoldConstants(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)6124 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
6125 {
6126     JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
6127     int stackDummy;
6128 
6129     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
6130         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
6131         return JS_FALSE;
6132     }
6133 
6134     switch (pn->pn_arity) {
6135       case PN_FUNC:
6136       {
6137         uint16 oldflags = tc->flags;
6138 
6139         tc->flags = (uint16) pn->pn_flags;
6140         if (!js_FoldConstants(cx, pn->pn_body, tc))
6141             return JS_FALSE;
6142         tc->flags = oldflags;
6143         break;
6144       }
6145 
6146       case PN_LIST:
6147 #if 0 /* JS_HAS_XML_SUPPORT */
6148         switch (pn->pn_type) {
6149           case TOK_XMLELEM:
6150           case TOK_XMLLIST:
6151           case TOK_XMLPTAGC:
6152             /*
6153              * Try to fold this XML parse tree once, from the top down, into
6154              * a JSXML tree with just one object wrapping the tree root.
6155              *
6156              * Certain subtrees could be folded similarly, but we'd have to
6157              * ensure that none used namespace prefixes declared elsewhere in
6158              * its super-tree, and we would have to convert each XML object
6159              * created at runtime for such sub-trees back into a string, and
6160              * concatenate and re-parse anyway.
6161              */
6162             if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT &&
6163                 !(tc->flags & TCF_HAS_DEFXMLNS)) {
6164                 JSObject *obj;
6165                 JSAtom *atom;
6166 
6167                 obj = js_ParseNodeToXMLObject(cx, pn);
6168                 if (!obj)
6169                     return JS_FALSE;
6170                 atom = js_AtomizeObject(cx, obj, 0);
6171                 if (!atom)
6172                     return JS_FALSE;
6173                 pn->pn_op = JSOP_XMLOBJECT;
6174                 pn->pn_arity = PN_NULLARY;
6175                 pn->pn_atom = atom;
6176                 return JS_TRUE;
6177             }
6178 
6179             /*
6180              * Can't fold from parse node to XML tree -- try folding strings
6181              * as much as possible, and folding XML sub-trees bottom up to
6182              * minimize string concatenation and ToXML/ToXMLList operations
6183              * at runtime.
6184              */
6185             break;
6186 
6187           default:;
6188         }
6189 #endif
6190 
6191         /* Save the list head in pn1 for later use. */
6192         for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6193             if (!js_FoldConstants(cx, pn2, tc))
6194                 return JS_FALSE;
6195         }
6196         break;
6197 
6198       case PN_TERNARY:
6199         /* Any kid may be null (e.g. for (;;)). */
6200         pn1 = pn->pn_kid1;
6201         pn2 = pn->pn_kid2;
6202         pn3 = pn->pn_kid3;
6203         if (pn1 && !js_FoldConstants(cx, pn1, tc))
6204             return JS_FALSE;
6205         if (pn2 && !js_FoldConstants(cx, pn2, tc))
6206             return JS_FALSE;
6207         if (pn3 && !js_FoldConstants(cx, pn3, tc))
6208             return JS_FALSE;
6209         break;
6210 
6211       case PN_BINARY:
6212         /* First kid may be null (for default case in switch). */
6213         pn1 = pn->pn_left;
6214         pn2 = pn->pn_right;
6215         if (pn1 && !js_FoldConstants(cx, pn1, tc))
6216             return JS_FALSE;
6217         if (!js_FoldConstants(cx, pn2, tc))
6218             return JS_FALSE;
6219         break;
6220 
6221       case PN_UNARY:
6222         /* Our kid may be null (e.g. return; vs. return e;). */
6223         pn1 = pn->pn_kid;
6224         if (pn1 && !js_FoldConstants(cx, pn1, tc))
6225             return JS_FALSE;
6226         break;
6227 
6228       case PN_NAME:
6229         /*
6230          * Skip pn1 down along a chain of dotted member expressions to avoid
6231          * excessive recursion.  Our only goal here is to fold constants (if
6232          * any) in the primary expression operand to the left of the first
6233          * dot in the chain.
6234          */
6235         pn1 = pn->pn_expr;
6236         while (pn1 && pn1->pn_arity == PN_NAME)
6237             pn1 = pn1->pn_expr;
6238         if (pn1 && !js_FoldConstants(cx, pn1, tc))
6239             return JS_FALSE;
6240         break;
6241 
6242       case PN_NULLARY:
6243         break;
6244     }
6245 
6246     switch (pn->pn_type) {
6247       case TOK_IF:
6248         if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
6249             break;
6250         /* FALL THROUGH */
6251 
6252       case TOK_HOOK:
6253         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
6254         while (pn1->pn_type == TOK_RP)
6255             pn1 = pn1->pn_kid;
6256         switch (pn1->pn_type) {
6257           case TOK_NUMBER:
6258             if (pn1->pn_dval == 0)
6259                 pn2 = pn3;
6260             break;
6261           case TOK_STRING:
6262             if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
6263                 pn2 = pn3;
6264             break;
6265           case TOK_PRIMARY:
6266             if (pn1->pn_op == JSOP_TRUE)
6267                 break;
6268             if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
6269                 pn2 = pn3;
6270                 break;
6271             }
6272             /* FALL THROUGH */
6273           default:
6274             /* Early return to dodge common code that copies pn2 to pn. */
6275             return JS_TRUE;
6276         }
6277 
6278         if (pn2) {
6279             /*
6280              * pn2 is the then- or else-statement subtree to compile.  Take
6281              * care not to expose an object initialiser, which would be parsed
6282              * as a block, to the Statement parser via eval(uneval(e)) where e
6283              * is '1 ? {p:2, q:3}[i] : r;' or the like.
6284              */
6285             if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) {
6286                 pn->pn_type = TOK_RP;
6287                 pn->pn_arity = PN_UNARY;
6288                 pn->pn_kid = pn2;
6289             } else {
6290                 PN_MOVE_NODE(pn, pn2);
6291             }
6292         }
6293         if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
6294             /*
6295              * False condition and no else, or an empty then-statement was
6296              * moved up over pn.  Either way, make pn an empty block (not an
6297              * empty statement, which does not decompile, even when labeled).
6298              * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
6299              * or an empty statement for a child.
6300              */
6301             pn->pn_type = TOK_LC;
6302             pn->pn_arity = PN_LIST;
6303             PN_INIT_LIST(pn);
6304         }
6305         RecycleTree(pn2, tc);
6306         if (pn3 && pn3 != pn2)
6307             RecycleTree(pn3, tc);
6308         break;
6309 
6310       case TOK_ASSIGN:
6311         /*
6312          * Compound operators such as *= should be subject to folding, in case
6313          * the left-hand side is constant, and so that the decompiler produces
6314          * the same string that you get from decompiling a script or function
6315          * compiled from that same string.  As with +, += is special.
6316          */
6317         if (pn->pn_op == JSOP_NOP)
6318             break;
6319         if (pn->pn_op != JSOP_ADD)
6320             goto do_binary_op;
6321         /* FALL THROUGH */
6322 
6323       case TOK_PLUS:
6324         if (pn->pn_arity == PN_LIST) {
6325             size_t length, length2;
6326             jschar *chars;
6327             JSString *str, *str2;
6328 
6329             /*
6330              * Any string literal term with all others number or string means
6331              * this is a concatenation.  If any term is not a string or number
6332              * literal, we can't fold.
6333              */
6334             JS_ASSERT(pn->pn_count > 2);
6335             if (pn->pn_extra & PNX_CANTFOLD)
6336                 return JS_TRUE;
6337             if (pn->pn_extra != PNX_STRCAT)
6338                 goto do_binary_op;
6339 
6340             /* Ok, we're concatenating: convert non-string constant operands. */
6341             length = 0;
6342             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6343                 if (!FoldType(cx, pn2, TOK_STRING))
6344                     return JS_FALSE;
6345                 /* XXX fold only if all operands convert to string */
6346                 if (pn2->pn_type != TOK_STRING)
6347                     return JS_TRUE;
6348                 length += ATOM_TO_STRING(pn2->pn_atom)->length;
6349             }
6350 
6351             /* Allocate a new buffer and string descriptor for the result. */
6352             chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
6353             if (!chars)
6354                 return JS_FALSE;
6355             str = js_NewString(cx, chars, length, 0);
6356             if (!str) {
6357                 JS_free(cx, chars);
6358                 return JS_FALSE;
6359             }
6360 
6361             /* Fill the buffer, advancing chars and recycling kids as we go. */
6362             for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
6363                 str2 = ATOM_TO_STRING(pn2->pn_atom);
6364                 length2 = str2->length;
6365                 js_strncpy(chars, str2->chars, length2);
6366                 chars += length2;
6367             }
6368             *chars = 0;
6369 
6370             /* Atomize the result string and mutate pn to refer to it. */
6371             pn->pn_atom = js_AtomizeString(cx, str, 0);
6372             if (!pn->pn_atom)
6373                 return JS_FALSE;
6374             pn->pn_type = TOK_STRING;
6375             pn->pn_op = JSOP_STRING;
6376             pn->pn_arity = PN_NULLARY;
6377             break;
6378         }
6379 
6380         /* Handle a binary string concatenation. */
6381         JS_ASSERT(pn->pn_arity == PN_BINARY);
6382         if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
6383             JSString *left, *right, *str;
6384 
6385             if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
6386                           TOK_STRING)) {
6387                 return JS_FALSE;
6388             }
6389             if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
6390                 return JS_TRUE;
6391             left = ATOM_TO_STRING(pn1->pn_atom);
6392             right = ATOM_TO_STRING(pn2->pn_atom);
6393             str = js_ConcatStrings(cx, left, right);
6394             if (!str)
6395                 return JS_FALSE;
6396             pn->pn_atom = js_AtomizeString(cx, str, 0);
6397             if (!pn->pn_atom)
6398                 return JS_FALSE;
6399             pn->pn_type = TOK_STRING;
6400             pn->pn_op = JSOP_STRING;
6401             pn->pn_arity = PN_NULLARY;
6402             RecycleTree(pn1, tc);
6403             RecycleTree(pn2, tc);
6404             break;
6405         }
6406 
6407         /* Can't concatenate string literals, let's try numbers. */
6408         goto do_binary_op;
6409 
6410       case TOK_STAR:
6411         /* The * in 'import *;' parses as a nullary star node. */
6412         if (pn->pn_arity == PN_NULLARY)
6413             break;
6414         /* FALL THROUGH */
6415 
6416       case TOK_SHOP:
6417       case TOK_MINUS:
6418       case TOK_DIVOP:
6419       do_binary_op:
6420         if (pn->pn_arity == PN_LIST) {
6421             JS_ASSERT(pn->pn_count > 2);
6422             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6423                 if (!FoldType(cx, pn2, TOK_NUMBER))
6424                     return JS_FALSE;
6425             }
6426             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6427                 /* XXX fold only if all operands convert to number */
6428                 if (pn2->pn_type != TOK_NUMBER)
6429                     break;
6430             }
6431             if (!pn2) {
6432                 JSOp op = pn->pn_op;
6433 
6434                 pn2 = pn1->pn_next;
6435                 pn3 = pn2->pn_next;
6436                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
6437                     return JS_FALSE;
6438                 while ((pn2 = pn3) != NULL) {
6439                     pn3 = pn2->pn_next;
6440                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
6441                         return JS_FALSE;
6442                 }
6443             }
6444         } else {
6445             JS_ASSERT(pn->pn_arity == PN_BINARY);
6446             if (!FoldType(cx, pn1, TOK_NUMBER) ||
6447                 !FoldType(cx, pn2, TOK_NUMBER)) {
6448                 return JS_FALSE;
6449             }
6450             if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
6451                 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
6452                     return JS_FALSE;
6453             }
6454         }
6455         break;
6456 
6457       case TOK_UNARYOP:
6458         while (pn1->pn_type == TOK_RP)
6459             pn1 = pn1->pn_kid;
6460         if (pn1->pn_type == TOK_NUMBER) {
6461             jsdouble d;
6462             int32 i;
6463 
6464             /* Operate on one numeric constant. */
6465             d = pn1->pn_dval;
6466             switch (pn->pn_op) {
6467               case JSOP_BITNOT:
6468                 if (!js_DoubleToECMAInt32(cx, d, &i))
6469                     return JS_FALSE;
6470                 d = ~i;
6471                 break;
6472 
6473               case JSOP_NEG:
6474 #ifdef HPUX
6475                 /*
6476                  * Negation of a zero doesn't produce a negative
6477                  * zero on HPUX. Perform the operation by bit
6478                  * twiddling.
6479                  */
6480                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
6481 #else
6482                 d = -d;
6483 #endif
6484                 break;
6485 
6486               case JSOP_POS:
6487                 break;
6488 
6489               case JSOP_NOT:
6490                 pn->pn_type = TOK_PRIMARY;
6491                 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
6492                 pn->pn_arity = PN_NULLARY;
6493                 /* FALL THROUGH */
6494 
6495               default:
6496                 /* Return early to dodge the common TOK_NUMBER code. */
6497                 return JS_TRUE;
6498             }
6499             pn->pn_type = TOK_NUMBER;
6500             pn->pn_op = JSOP_NUMBER;
6501             pn->pn_arity = PN_NULLARY;
6502             pn->pn_dval = d;
6503             RecycleTree(pn1, tc);
6504         }
6505         break;
6506 
6507 #if JS_HAS_XML_SUPPORT
6508       case TOK_XMLELEM:
6509       case TOK_XMLLIST:
6510       case TOK_XMLPTAGC:
6511       case TOK_XMLSTAGO:
6512       case TOK_XMLETAGO:
6513       case TOK_XMLNAME:
6514         if (pn->pn_arity == PN_LIST) {
6515             JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
6516             if (!FoldXMLConstants(cx, pn, tc))
6517                 return JS_FALSE;
6518         }
6519         break;
6520 
6521       case TOK_AT:
6522         if (pn1->pn_type == TOK_XMLNAME) {
6523             jsval v;
6524             JSAtom *atom;
6525 
6526             v = ATOM_KEY(pn1->pn_atom);
6527             if (!js_ToAttributeName(cx, &v))
6528                 return JS_FALSE;
6529             JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
6530             atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
6531             if (!atom)
6532                 return JS_FALSE;
6533 
6534             pn->pn_type = TOK_XMLNAME;
6535             pn->pn_op = JSOP_OBJECT;
6536             pn->pn_arity = PN_NULLARY;
6537             pn->pn_atom = atom;
6538             RecycleTree(pn1, tc);
6539         }
6540         break;
6541 #endif /* JS_HAS_XML_SUPPORT */
6542 
6543       default:;
6544     }
6545 
6546     return JS_TRUE;
6547 }
6548