1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * JS parser.
42  *
43  * This is a recursive-descent parser for the JavaScript language specified by
44  * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic
45  * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
46  * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
47  * After tree construction, it rewrites trees to fold constants and evaluate
48  * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to
49  * generate bytecode.
50  *
51  * This parser attempts no error recovery.
52  */
53 #include "jsstddef.h"
54 #include <stdlib.h>
55 #include <string.h>
56 #include <math.h>
57 #include "jstypes.h"
58 #include "jsarena.h" /* Added by JSIFY */
59 #include "jsutil.h" /* Added by JSIFY */
60 #include "jsapi.h"
61 #include "jsatom.h"
62 #include "jscntxt.h"
63 #include "jsconfig.h"
64 #include "jsemit.h"
65 #include "jsfun.h"
66 #include "jsinterp.h"
67 #include "jslock.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsopcode.h"
71 #include "jsparse.h"
72 #include "jsscan.h"
73 #include "jsscope.h"
74 #include "jsscript.h"
75 #include "jsstr.h"
76
77 #if JS_HAS_XML_SUPPORT
78 #include "jsxml.h"
79 #endif
80
81 /*
82  * JS parsers, from lowest to highest precedence.
83  *
84  * Each parser takes a context, a token stream, and a tree context struct.
85  * Each returns a parse node tree or null on error.
86  */
87
88 typedef JSParseNode *
89 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
90
91 typedef JSParseNode *
92 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
93                JSBool allowCallSyntax);
94
95 static JSParser FunctionStmt;
96 #if JS_HAS_LEXICAL_CLOSURE
97 static JSParser FunctionExpr;
98 #endif
99 static JSParser Statements;
100 static JSParser Statement;
101 static JSParser Variables;
102 static JSParser Expr;
103 static JSParser AssignExpr;
104 static JSParser CondExpr;
105 static JSParser OrExpr;
106 static JSParser AndExpr;
107 static JSParser BitOrExpr;
108 static JSParser BitXorExpr;
109 static JSParser BitAndExpr;
110 static JSParser EqExpr;
111 static JSParser RelExpr;
112 static JSParser ShiftExpr;
113 static JSParser AddExpr;
114 static JSParser MulExpr;
115 static JSParser UnaryExpr;
116 static JSMemberParser MemberExpr;
117 static JSParser PrimaryExpr;
118
119 /*
120  * Insist that the next token be of type tt, or report errno and return null.
121  * NB: this macro uses cx and ts from its lexical environment.
122  */
123 #define MUST_MATCH_TOKEN(tt, errno)                                           \
124     JS_BEGIN_MACRO                                                            \
125         if (js_GetToken(cx, ts) != tt) {                                      \
126             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
127                             errno);                                           \
128             return NULL;                                                      \
129         }                                                                     \
130     JS_END_MACRO
131
132 #define CHECK_RECURSION()                                                     \
133     JS_BEGIN_MACRO                                                            \
134         int stackDummy;                                                       \
135         if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {                           \
136             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
137                                         JSMSG_OVER_RECURSED);                 \
138             return NULL;                                                      \
139         }                                                                     \
140     JS_END_MACRO
141
142 #ifdef METER_PARSENODES
143 static uint32 parsenodes = 0;
144 static uint32 maxparsenodes = 0;
145 static uint32 recyclednodes = 0;
146 #endif
147
148 static JSParseNode *
149 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
150 848094 {
151 848094     JSParseNode *next;
152
153 848094     if (!pn)
154 369381         return NULL;
155 478713     JS_ASSERT(pn != tc->nodeList);      /* catch back-to-back dup recycles */
156 478713     next = pn->pn_next;
157 478713     pn->pn_next = tc->nodeList;
158 478713     tc->nodeList = pn;
159 #ifdef METER_PARSENODES
160     recyclednodes++;
161 #endif
162 478713     return next;
163 }
164
165 static JSParseNode *
166 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
167 1345686 {
168 1345686     JSParseNode *pn;
169
170 1345686     pn = tc->nodeList;
171 1345686     if (!pn) {
172 417627         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
173 417627         if (!pn)
174 0             JS_ReportOutOfMemory(cx);
175     } else {
176 928059         tc->nodeList = pn->pn_next;
177
178         /* Recycle immediate descendents only, to save work and working set. */
179 928059         switch (pn->pn_arity) {
180           case PN_FUNC:
181 3552             RecycleTree(pn->pn_body, tc);
182 3552             break;
183           case PN_LIST:
184 217143             if (pn->pn_head) {
185                 /* XXX check for dup recycles in the list */
186 217047                 *pn->pn_tail = tc->nodeList;
187 217047                 tc->nodeList = pn->pn_head;
188 #ifdef METER_PARSENODES
189                 recyclednodes += pn->pn_count;
190 #endif
191             }
192 217047             break;
193           case PN_TERNARY:
194 3941             RecycleTree(pn->pn_kid1, tc);
195 3941             RecycleTree(pn->pn_kid2, tc);
196 3941             RecycleTree(pn->pn_kid3, tc);
197 3941             break;
198           case PN_BINARY:
199 14587             RecycleTree(pn->pn_left, tc);
200 14587             RecycleTree(pn->pn_right, tc);
201 14587             break;
202           case PN_UNARY:
203 200991             RecycleTree(pn->pn_kid, tc);
204 200991             break;
205           case PN_NAME:
206 378584             RecycleTree(pn->pn_expr, tc);
207             break;
208           case PN_NULLARY:
209 1345686             break;
210         }
211     }
212 #ifdef METER_PARSENODES
213     if (pn) {
214         parsenodes++;
215         if (parsenodes - recyclednodes > maxparsenodes)
216             maxparsenodes = parsenodes - recyclednodes;
217     }
218 #endif
219 1345686     return pn;
220 }
221
222 /*
223  * Allocate a JSParseNode from cx's temporary arena.
224  */
225 static JSParseNode *
226 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
227              JSTreeContext *tc)
228 1309964 {
229 1309964     JSParseNode *pn;
230 1309964     JSToken *tp;
231
232 1309964     pn = NewOrRecycledNode(cx, tc);
233 1309964     if (!pn)
234 0         return NULL;
235 1309964     tp = &CURRENT_TOKEN(ts);
236 1309964     pn->pn_type = tp->type;
237 1309964     pn->pn_pos = tp->pos;
238 1309964     pn->pn_op = JSOP_NOP;
239 1309964     pn->pn_arity = arity;
240 1309964     pn->pn_next = NULL;
241 #if JS_HAS_XML_SUPPORT
242 1309964     pn->pn_ts = ts;
243 #endif
244 1309964     return pn;
245 }
246
247 static JSParseNode *
248 NewBinary(JSContext *cx, JSTokenType tt,
249           JSOp op, JSParseNode *left, JSParseNode *right,
250           JSTreeContext *tc)
251 36927 {
252 36927     JSParseNode *pn, *pn1, *pn2;
253
254 36927     if (!left || !right)
255 0         return NULL;
256
257     /*
258      * Flatten a left-associative (left-heavy) tree of a given operator into
259      * a list, to reduce js_FoldConstants and js_EmitTree recursion.
260      */
261 36927     if (left->pn_type == tt &&
262         left->pn_op == op &&
263         (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
264 1205         if (left->pn_arity != PN_LIST) {
265 1013             pn1 = left->pn_left, pn2 = left->pn_right;
266 1013             left->pn_arity = PN_LIST;
267 1013             PN_INIT_LIST_1(left, pn1);
268 1013             PN_APPEND(left, pn2);
269 1013             if (tt == TOK_PLUS) {
270 1013                 if (pn1->pn_type == TOK_STRING)
271 825                     left->pn_extra |= PNX_STRCAT;
272 188                 else if (pn1->pn_type != TOK_NUMBER)
273 188                     left->pn_extra |= PNX_CANTFOLD;
274 1013                 if (pn2->pn_type == TOK_STRING)
275 156                     left->pn_extra |= PNX_STRCAT;
276 857                 else if (pn2->pn_type != TOK_NUMBER)
277 857                     left->pn_extra |= PNX_CANTFOLD;
278             }
279         }
280 1205         PN_APPEND(left, right);
281 1205         left->pn_pos.end = right->pn_pos.end;
282 1205         if (tt == TOK_PLUS) {
283 1205             if (right->pn_type == TOK_STRING)
284 921                 left->pn_extra |= PNX_STRCAT;
285 284             else if (right->pn_type != TOK_NUMBER)
286 284                 left->pn_extra |= PNX_CANTFOLD;
287         }
288 1205         return left;
289     }
290
291     /*
292      * Fold constant addition immediately, to conserve node space and, what's
293      * more, so js_FoldConstants never sees mixed addition and concatenation
294      * operations with more than one leading non-string operand in a PN_LIST
295      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
296      * to "3pt", not "12pt").
297      */
298 35722     if (tt == TOK_PLUS &&
299         left->pn_type == TOK_NUMBER &&
300         right->pn_type == TOK_NUMBER) {
301 0         left->pn_dval += right->pn_dval;
302 0         left->pn_pos.end = right->pn_pos.end;
303 0         RecycleTree(right, tc);
304 0         return left;
305     }
306
307 35722     pn = NewOrRecycledNode(cx, tc);
308 35722     if (!pn)
309 0         return NULL;
310 35722     pn->pn_type = tt;
311 35722     pn->pn_pos.begin = left->pn_pos.begin;
312 35722     pn->pn_pos.end = right->pn_pos.end;
313 35722     pn->pn_op = op;
314 35722     pn->pn_arity = PN_BINARY;
315 35722     pn->pn_left = left;
316 35722     pn->pn_right = right;
317 35722     pn->pn_next = NULL;
318 #if JS_HAS_XML_SUPPORT
319 35722     pn->pn_ts = NULL;
320 #endif
321 35722     return pn;
322 }
323
324 #if JS_HAS_GETTER_SETTER
325 static JSTokenType
326 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
327 764838 {
328 764838     JSAtom *atom;
329 764838     JSRuntime *rt;
330 764838     JSOp op;
331 764838     const char *name;
332
333 764838     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
334 764838     atom = CURRENT_TOKEN(ts).t_atom;
335 764838     rt = cx->runtime;
336 764838     if (atom == rt->atomState.getterAtom)
337 0         op = JSOP_GETTER;
338 764838     else if (atom == rt->atomState.setterAtom)
339 0         op = JSOP_SETTER;
340     else
341 764838         return TOK_NAME;
342 0     if (js_PeekTokenSameLine(cx, ts) != tt)
343 0         return TOK_NAME;
344 0     (void) js_GetToken(cx, ts);
345 0     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
346 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
347                                     JSMSG_BAD_GETTER_OR_SETTER,
348                                     (op == JSOP_GETTER)
349                                     ? js_getter_str
350                                     : js_setter_str);
351 0         return TOK_ERROR;
352     }
353 0     CURRENT_TOKEN(ts).t_op = op;
354 0     name = js_AtomToPrintableString(cx, atom);
355 0     if (!name ||
356         !js_ReportCompileErrorNumber(cx, ts,
357                                      JSREPORT_TS |
358                                      JSREPORT_WARNING |
359                                      JSREPORT_STRICT,
360                                      JSMSG_DEPRECATED_USAGE,
361                                      name)) {
362 0         return TOK_ERROR;
363     }
364 0     return tt;
365 }
366 #endif
367
368 /*
369  * Parse a top-level JS script.
370  */
371 JS_FRIEND_API(JSParseNode *)
372 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
373 0 {
374 0     JSStackFrame *fp, frame;
375 0     JSTreeContext tc;
376 0     JSParseNode *pn;
377
378     /*
379      * Push a compiler frame if we have no frames, or if the top frame is a
380      * lightweight function activation, or if its scope chain doesn't match
381      * the one passed to us.
382      */
383 0     fp = cx->fp;
384 0     if (!fp || !fp->varobj || fp->scopeChain != chain) {
385 0         memset(&frame, 0, sizeof frame);
386 0         frame.varobj = frame.scopeChain = chain;
387 0         if (cx->options & JSOPTION_VAROBJFIX) {
388 0             while ((chain = JS_GetParent(cx, chain)) != NULL)
389 0                 frame.varobj = chain;
390         }
391 0         frame.down = fp;
392 0         if (fp) {
393 0             frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
394                                        JSFRAME_SCRIPT_OBJECT);
395         }
396 0         cx->fp = &frame;
397     }
398
399     /*
400      * Protect atoms from being collected by a GC activation, which might
401      * - nest on this thread due to out of memory (the so-called "last ditch"
402      *   GC attempted within js_NewGCThing), or
403      * - run for any reason on another thread if this thread is suspended on
404      *   an object lock before it finishes generating bytecode into a script
405      *   protected from the GC by a root or a stack frame reference.
406      */
407 0     JS_KEEP_ATOMS(cx->runtime);
408 0     TREE_CONTEXT_INIT(&tc);
409 0     pn = Statements(cx, ts, &tc);
410 0     if (pn) {
411 0         if (!js_MatchToken(cx, ts, TOK_EOF)) {
412 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
413                                         JSMSG_SYNTAX_ERROR);
414 0             pn = NULL;
415         } else {
416 0             pn->pn_type = TOK_LC;
417 0             if (!js_FoldConstants(cx, pn, &tc))
418 0                 pn = NULL;
419         }
420     }
421
422 0     TREE_CONTEXT_FINISH(&tc);
423 0     JS_UNKEEP_ATOMS(cx->runtime);
424 0     cx->fp = fp;
425 0     return pn;
426 }
427
428 /*
429  * Compile a top-level script.
430  */
431 JS_FRIEND_API(JSBool)
432 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
433                       JSCodeGenerator *cg)
434 1207 {
435 1207     JSStackFrame *fp, frame;
436 1207     uint32 flags;
437 1207     JSParseNode *pn;
438 1207     JSBool ok;
439 #ifdef METER_PARSENODES
440     void *sbrk(ptrdiff_t), *before = sbrk(0);
441 #endif
442
443     /*
444      * Push a compiler frame if we have no frames, or if the top frame is a
445      * lightweight function activation, or if its scope chain doesn't match
446      * the one passed to us.
447      */
448 1207     fp = cx->fp;
449 1207     if (!fp || !fp->varobj || fp->scopeChain != chain) {
450 16         memset(&frame, 0, sizeof frame);
451 16         frame.varobj = frame.scopeChain = chain;
452 16         if (cx->options & JSOPTION_VAROBJFIX) {
453 0             while ((chain = JS_GetParent(cx, chain)) != NULL)
454 0                 frame.varobj = chain;
455         }
456 16         frame.down = fp;
457 16         if (fp) {
458 16             frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
459                                        JSFRAME_SCRIPT_OBJECT);
460         }
461 16         cx->fp = &frame;
462     }
463 1207     flags = cx->fp->flags;
464 1207     cx->fp->flags = flags |
465                     (JS_HAS_COMPILE_N_GO_OPTION(cx)
466                      ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
467                      : JSFRAME_COMPILING);
468
469     /* Prevent GC activation while compiling. */
470 1207     JS_KEEP_ATOMS(cx->runtime);
471
472 1207     pn = Statements(cx, ts, &cg->treeContext);
473 1207     if (!pn) {
474 0         ok = JS_FALSE;
475 1207     } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
476 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
477                                     JSMSG_SYNTAX_ERROR);
478 0         ok = JS_FALSE;
479     } else {
480 #ifdef METER_PARSENODES
481         printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
482                (char *)sbrk(0) - (char *)before,
483                parsenodes,
484                maxparsenodes,
485                parsenodes - recyclednodes);
486         before = sbrk(0);
487 #endif
488
489         /*
490          * No need to emit code here -- Statements already has, for each
491          * statement in turn.  Search for TCF_COMPILING in Statements, below.
492          * That flag is set for every tc == &cg->treeContext, and it implies
493          * that the tc can be downcast to a cg and used to emit code during
494          * parsing, rather than at the end of the parse phase.
495          */
496 1207         JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
497 1207         ok = JS_TRUE;
498     }
499
500 #ifdef METER_PARSENODES
501     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
502            (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
503 #endif
504 #ifdef JS_ARENAMETER
505     JS_DumpArenaStats(stdout);
506 #endif
507 1207     JS_UNKEEP_ATOMS(cx->runtime);
508 1207     cx->fp->flags = flags;
509 1207     cx->fp = fp;
510 1207     return ok;
511 }
512
513 /*
514  * Insist on a final return before control flows out of pn, but don't be too
515  * smart about loops (do {...; return e2;} while(0) at the end of a function
516  * that contains an early return e1 will get a strict-option-only warning).
517  */
518 #define ENDS_IN_OTHER   0
519 #define ENDS_IN_RETURN  1
520 #define ENDS_IN_BREAK   2
521
522 static int
523 HasFinalReturn(JSParseNode *pn)
524 0 {
525 0     uintN rv, rv2, hasDefault;
526 0     JSParseNode *pn2, *pn3;
527
528 0     switch (pn->pn_type) {
529       case TOK_LC:
530 0         if (!pn->pn_head)
531 0             return ENDS_IN_OTHER;
532 0         return HasFinalReturn(PN_LAST(pn));
533
534       case TOK_IF:
535 0         rv = HasFinalReturn(pn->pn_kid2);
536 0         if (pn->pn_kid3)
537 0             rv &= HasFinalReturn(pn->pn_kid3);
538 0         return rv;
539
540 #if JS_HAS_SWITCH_STATEMENT
541       case TOK_SWITCH:
542 0         rv = ENDS_IN_RETURN;
543 0         hasDefault = ENDS_IN_OTHER;
544 0         for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
545 0             if (pn2->pn_type == TOK_DEFAULT)
546 0                 hasDefault = ENDS_IN_RETURN;
547 0             pn3 = pn2->pn_right;
548 0             JS_ASSERT(pn3->pn_type == TOK_LC);
549 0             if (pn3->pn_head) {
550 0                 rv2 = HasFinalReturn(PN_LAST(pn3));
551 0                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
552                     /* Falling through to next case or default. */;
553                 else
554 0                     rv &= rv2;
555             }
556         }
557         /* If a final switch has no default case, we judge it harshly. */
558 0         rv &= hasDefault;
559 0         return rv;
560 #endif /* JS_HAS_SWITCH_STATEMENT */
561
562       case TOK_BREAK:
563 0         return ENDS_IN_BREAK;
564
565       case TOK_WITH:
566 0         return HasFinalReturn(pn->pn_right);
567
568       case TOK_RETURN:
569 0         return ENDS_IN_RETURN;
570
571       case TOK_COLON:
572 0         return HasFinalReturn(pn->pn_expr);
573
574 #if JS_HAS_EXCEPTIONS
575       case TOK_THROW:
576 0         return ENDS_IN_RETURN;
577
578       case TOK_TRY:
579         /* If we have a finally block that returns, we are done. */
580 0         if (pn->pn_kid3) {
581 0             rv = HasFinalReturn(pn->pn_kid3);
582 0             if (rv == ENDS_IN_RETURN)
583 0                 return rv;
584         }
585
586         /* Else check the try block and any and all catch statements. */
587 0         rv = HasFinalReturn(pn->pn_kid1);
588 0         if (pn->pn_kid2)
589 0             rv &= HasFinalReturn(pn->pn_kid2);
590 0         return rv;
591
592       case TOK_CATCH:
593         /* Check this block's code and iterate over further catch blocks. */
594 0         rv = HasFinalReturn(pn->pn_kid3);
595 0         for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
596 0             rv &= HasFinalReturn(pn2->pn_kid3);
597 0         return rv;
598 #endif
599
600       default:
601 0         return ENDS_IN_OTHER;
602     }
603 }
604
605 static JSBool
606 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
607 0 {
608 0     JSFunction *fun;
609 0     JSBool ok;
610
611 0     fun = cx->fp->fun;
612 0     if (fun->atom) {
613 0         char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
614 0         ok = js_ReportCompileErrorNumber(cx, ts,
615                                          JSREPORT_TS |
616                                          JSREPORT_WARNING |
617                                          JSREPORT_STRICT,
618                                          JSMSG_NO_RETURN_VALUE, name);
619     } else {
620 0         ok = js_ReportCompileErrorNumber(cx, ts,
621                                          JSREPORT_TS |
622                                          JSREPORT_WARNING |
623                                          JSREPORT_STRICT,
624                                          JSMSG_ANON_NO_RETURN_VALUE);
625     }
626 0     return ok;
627 }
628
629 static JSBool
630 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
631 0 {
632 0     return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
633 }
634
635 static JSParseNode *
636 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
637              JSTreeContext *tc)
638 3552 {
639 3552     JSStackFrame *fp, frame;
640 3552     JSObject *funobj;
641 3552     uintN oldflags;
642 3552     JSParseNode *pn;
643
644 3552     fp = cx->fp;
645 3552     funobj = fun->object;
646 3552     if (!fp || fp->fun != fun || fp->varobj != funobj ||
647         fp->scopeChain != funobj) {
648 3552         memset(&frame, 0, sizeof frame);
649 3552         frame.fun = fun;
650 3552         frame.varobj = frame.scopeChain = funobj;
651 3552         frame.down = fp;
652 3552         if (fp)
653 3552             frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
654 3552         cx->fp = &frame;
655     }
656
657 3552     oldflags = tc->flags;
658 3552     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
659 3552     tc->flags |= TCF_IN_FUNCTION;
660 3552     pn = Statements(cx, ts, tc);
661
662     /* Check for falling off the end of a function that returns a value. */
663 3552     if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
664 0         if (!CheckFinalReturn(cx, ts, pn))
665 0             pn = NULL;
666     }
667
668 3552     cx->fp = fp;
669 3552     tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
670 3552     return pn;
671 }
672
673 /*
674  * Compile a JS function body, which might appear as the value of an event
675  * handler attribute in an HTML <INPUT> tag.
676  */
677 JSBool
678 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
679 0 {
680 0     JSArenaPool codePool, notePool;
681 0     JSCodeGenerator funcg;
682 0     JSStackFrame *fp, frame;
683 0     JSObject *funobj;
684 0     JSParseNode *pn;
685 0     JSBool ok;
686
687 0     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
688 0     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
689 0     if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
690                               ts->filename, ts->lineno,
691                               ts->principals)) {
692 0         return JS_FALSE;
693     }
694
695     /* Prevent GC activation while compiling. */
696 0     JS_KEEP_ATOMS(cx->runtime);
697
698     /* Push a JSStackFrame for use by FunctionBody. */
699 0     fp = cx->fp;
700 0     funobj = fun->object;
701 0     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
702                       fp->scopeChain != funobj));
703 0     memset(&frame, 0, sizeof frame);
704 0     frame.fun = fun;
705 0     frame.varobj = frame.scopeChain = funobj;
706 0     frame.down = fp;
707 0     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
708                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
709                   : JSFRAME_COMPILING;
710 0     cx->fp = &frame;
711
712     /* Ensure that the body looks like a block statement to js_EmitTree. */
713 0     CURRENT_TOKEN(ts).type = TOK_LC;
714 0     pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
715 0     if (!pn) {
716 0         ok = JS_FALSE;
717     } else {
718         /*
719          * No need to emit code here -- Statements (via FunctionBody) already
720          * has.  See similar comment in js_CompileTokenStream, and bug 108257.
721          */
722 0         fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
723 0         if (!fun->u.script) {
724 0             ok = JS_FALSE;
725         } else {
726 0             fun->interpreted = JS_TRUE;
727 0             if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
728 0                 fun->flags |= JSFUN_HEAVYWEIGHT;
729 0             ok = JS_TRUE;
730         }
731     }
732
733     /* Restore saved state and release code generation arenas. */
734 0     cx->fp = fp;
735 0     JS_UNKEEP_ATOMS(cx->runtime);
736 0     js_FinishCodeGenerator(cx, &funcg);
737 0     JS_FinishArenaPool(&codePool);
738 0     JS_FinishArenaPool(&notePool);
739 0     return ok;
740 }
741
742 static JSParseNode *
743 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
744             JSBool lambda)
745 3552 {
746 3552     JSOp op, prevop;
747 3552     JSParseNode *pn, *body, *result;
748 3552     JSAtom *funAtom, *objAtom, *argAtom;
749 3552     JSStackFrame *fp;
750 3552     JSObject *varobj, *pobj;
751 3552     JSAtomListElement *ale;
752 3552     JSProperty *prop;
753 3552     JSFunction *fun;
754 3552     uintN dupflag;
755 3552     JSBool ok;
756 3552     JSTreeContext funtc;
757
758     /* Make a TOK_FUNCTION node. */
759 #if JS_HAS_GETTER_SETTER
760 3552     op = CURRENT_TOKEN(ts).t_op;
761 #endif
762 3552     pn = NewParseNode(cx, ts, PN_FUNC, tc);
763 3552     if (!pn)
764 0         return NULL;
765
766     /* Scan the optional function name into funAtom. */
767 3552     funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL;
768 #if !JS_HAS_LEXICAL_CLOSURE
769     if (!funAtom && !lambda) {
770         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
771                                     JSMSG_SYNTAX_ERROR);
772         return NULL;
773     }
774 #endif
775
776     /* Find the nearest variable-declaring scope and use it as our parent. */
777 3552     fp = cx->fp;
778 3552     varobj = fp->varobj;
779
780     /*
781      * Record names for function statements in tc->decls so we know when to
782      * avoid optimizing variable references that might name a function.
783      */
784 3552     if (!lambda && funAtom) {
785 3552         ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
786 3552         if (ale) {
787 64             prevop = ALE_JSOP(ale);
788 64             if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
789 0                 const char *name = js_AtomToPrintableString(cx, funAtom);
790 0                 if (!name ||
791                     !js_ReportCompileErrorNumber(cx, ts,
792                                                  (prevop != JSOP_DEFCONST)
793                                                  ? JSREPORT_TS |
794                                                    JSREPORT_WARNING |
795                                                    JSREPORT_STRICT
796                                                  : JSREPORT_TS | JSREPORT_ERROR,
797                                                  JSMSG_REDECLARED_VAR,
798                                                  (prevop == JSOP_DEFFUN ||
799                                                   prevop == JSOP_CLOSURE)
800                                                  ? js_function_str
801                                                  : (prevop == JSOP_DEFCONST)
802                                                  ? js_const_str
803                                                  : js_var_str,
804                                                  name)) {
805 0                     return NULL;
806                 }
807             }
808 64             if (tc->topStmt && prevop == JSOP_DEFVAR)
809 0                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
810         } else {
811 3488             ale = js_IndexAtom(cx, funAtom, &tc->decls);
812 3488             if (!ale)
813 0                 return NULL;
814         }
815 3552         ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
816
817 #if JS_HAS_LEXICAL_CLOSURE
818         /*
819          * A function nested at top level inside another's body needs only a
820          * local variable to bind its name to its value, and not an activation
821          * object property (it might also need the activation property, if the
822          * outer function contains with statements, e.g., but the stack slot
823          * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
824          * JSOP_GETVAR bytecode).
825          */
826 3552         if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
827 0             JSScopeProperty *sprop;
828
829             /*
830              * Define a property on the outer function so that BindNameToSlot
831              * can properly optimize accesses.
832              */
833 0             JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
834 0             JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
835 0             if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
836                                          &pobj, &prop)) {
837 0                 return NULL;
838             }
839 0             if (prop)
840 0                 OBJ_DROP_PROPERTY(cx, pobj, prop);
841 0             sprop = NULL;
842 0             if (!prop ||
843                 pobj != varobj ||
844                 (sprop = (JSScopeProperty *)prop,
845                  sprop->getter != js_GetLocalVariable)) {
846 0                 uintN sflags;
847
848                 /*
849                  * Use SPROP_IS_DUPLICATE if there is a formal argument of the
850                  * same name, so the decompiler can find the parameter name.
851                  */
852 0                 sflags = (sprop && sprop->getter == js_GetArgument)
853                          ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
854                          : SPROP_HAS_SHORTID;
855 0                 if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
856                                           js_GetLocalVariable,
857                                           js_SetLocalVariable,
858                                           SPROP_INVALID_SLOT,
859                                           JSPROP_PERMANENT | JSPROP_SHARED,
860                                           sflags, fp->fun->nvars)) {
861 0                     return NULL;
862                 }
863 0                 if (fp->fun->nvars == JS_BITMASK(16)) {
864 0                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
865                                          JSMSG_TOO_MANY_FUN_VARS);
866 0                     return NULL;
867                 }
868 0                 fp->fun->nvars++;
869             }
870         }
871 #endif
872     }
873
874 3552     fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
875                          funAtom);
876 3552     if (!fun)
877 0         return NULL;
878 #if JS_HAS_GETTER_SETTER
879 3552     if (op != JSOP_NOP)
880 0         fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
881 #endif
882
883
884     /*
885      * Set interpreted early so js_EmitTree can test it to decide whether to
886      * eliminate useless expressions.
887      */
888 3552     fun->interpreted = JS_TRUE;
889
890     /*
891      * Atomize fun->object early to protect against a last-ditch GC under
892      * js_LookupHiddenProperty.
893      *
894      * Absent use of the new scoped local GC roots API around compiler calls,
895      * we need to atomize here to protect against a GC activation.  Atoms are
896      * protected from GC during compilation by the JS_FRIEND_API entry points
897      * in this file.  There doesn't seem to be any gain in switching from the
898      * atom-keeping method to the bulkier, slower scoped local roots method.
899      */
900 3552     objAtom = js_AtomizeObject(cx, fun->object, 0);
901 3552     if (!objAtom)
902 0         return NULL;
903
904     /* Now parse formal argument list and compute fun->nargs. */
905 3552     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
906 3552     if (!js_MatchToken(cx, ts, TOK_RP)) {
907 17344         do {
908 17344             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
909 17344             argAtom = CURRENT_TOKEN(ts).t_atom;
910 17344             pobj = NULL;
911 17344             if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom),
912                                          &pobj, &prop)) {
913 0                 return NULL;
914             }
915 17344             dupflag = 0;
916 17344             if (prop) {
917 32                 ok = JS_TRUE;
918 32                 if (pobj == fun->object &&
919                     ((JSScopeProperty *) prop)->getter == js_GetArgument) {
920 32                     const char *name = js_AtomToPrintableString(cx, argAtom);
921
922                     /*
923                      * A duplicate parameter name. We force a duplicate node
924                      * on the SCOPE_LAST_PROP(scope) list with the same id,
925                      * distinguished by the SPROP_IS_DUPLICATE flag, and not
926                      * mapped by an entry in scope.
927                      */
928 32                     ok = name &&
929                          js_ReportCompileErrorNumber(cx, ts,
930                                                      JSREPORT_TS |
931                                                      JSREPORT_WARNING |
932                                                      JSREPORT_STRICT,
933                                                      JSMSG_DUPLICATE_FORMAL,
934                                                      name);
935
936 32                     dupflag = SPROP_IS_DUPLICATE;
937                 }
938 32                 OBJ_DROP_PROPERTY(cx, pobj, prop);
939 32                 if (!ok)
940 0                     return NULL;
941 32                 prop = NULL;
942             }
943 17344             if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom),
944                                       js_GetArgument, js_SetArgument,
945                                       SPROP_INVALID_SLOT,
946                                       JSPROP_PERMANENT | JSPROP_SHARED,
947                                       dupflag | SPROP_HAS_SHORTID,
948                                       fun->nargs)) {
949 0                 return NULL;
950             }
951 17344             if (fun->nargs == JS_BITMASK(16)) {
952 0                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
953                                      JSMSG_TOO_MANY_FUN_ARGS);
954 0                 return NULL;
955             }
956 17344             fun->nargs++;
957 17344         } while (js_MatchToken(cx, ts, TOK_COMMA));
958
959 3552         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
960     }
961
962 3552     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
963 3552     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
964
965 3552     TREE_CONTEXT_INIT(&funtc);
966 3552     body = FunctionBody(cx, ts, fun, &funtc);
967 3552     if (!body)
968 0         return NULL;
969
970 3552     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
971 3552     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
972
973 #if JS_HAS_LEXICAL_CLOSURE
974     /*
975      * If we collected flags that indicate nested heavyweight functions, or
976      * this function contains heavyweight-making statements (references to
977      * __parent__ or __proto__; use of with, eval, import, or export; and
978      * assignment to arguments), flag the function as heavyweight (requiring
979      * a call object per invocation).
980      */
981 3552     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
982 0         fun->flags |= JSFUN_HEAVYWEIGHT;
983 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
984     } else {
985         /*
986          * If this function is a named statement function not at top-level
987          * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
988          * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
989          * enclosing function, if any, must be heavyweight.
990          */
991 3552         if ((!lambda && funAtom && tc->topStmt) ||
992             (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
993 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
994         }
995     }
996 #endif
997
998 3552     result = pn;
999 #if JS_HAS_LEXICAL_CLOSURE
1000 3552     if (lambda) {
1001         /*
1002          * ECMA ed. 3 standard: function expression, possibly anonymous.
1003          */
1004 0         op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1005 3552     } else if (!funAtom) {
1006         /*
1007          * If this anonymous function definition is *not* embedded within a
1008          * larger expression, we treat it as an expression statement, not as
1009          * a function declaration -- and not as a syntax error (as ECMA-262
1010          * Edition 3 would have it).  Backward compatibility trumps all.
1011          */
1012 0         result = NewParseNode(cx, ts, PN_UNARY, tc);
1013 0         if (!result)
1014 0             return NULL;
1015 0         result->pn_type = TOK_SEMI;
1016 0         result->pn_pos = pn->pn_pos;
1017 0         result->pn_kid = pn;
1018 0         op = JSOP_ANONFUNOBJ;
1019 3552     } else if (tc->topStmt) {
1020         /*
1021          * ECMA ed. 3 extension: a function expression statement not at the
1022          * top level, e.g., in a compound statement such as the "then" part
1023          * of an "if" statement, binds a closure only if control reaches that
1024          * sub-statement.
1025          */
1026 0         op = JSOP_CLOSURE;
1027     } else
1028 #endif
1029 3552         op = JSOP_NOP;
1030
1031 3552     pn->pn_funAtom = objAtom;
1032 3552     pn->pn_op = op;
1033 3552     pn->pn_body = body;
1034 3552     pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
1035 3552     pn->pn_tryCount = funtc.tryCount;
1036 3552     TREE_CONTEXT_FINISH(&funtc);
1037 3552     return result;
1038 }
1039
1040 static JSParseNode *
1041 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1042 3552 {
1043 3552     return FunctionDef(cx, ts, tc, JS_FALSE);
1044 }
1045
1046 #if JS_HAS_LEXICAL_CLOSURE
1047 static JSParseNode *
1048 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1049 0 {
1050 0     return FunctionDef(cx, ts, tc, JS_TRUE);
1051 }
1052 #endif
1053
1054 /*
1055  * Parse the statements in a block, creating a TOK_LC node that lists the
1056  * statements' trees.  If called from block-parsing code, the caller must
1057  * match { before and } after.
1058  */
1059 static JSParseNode *
1060 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1061 22929 {
1062 22929     JSParseNode *pn, *pn2;
1063 22929     JSTokenType tt;
1064
1065 22929     CHECK_RECURSION();
1066
1067 22929     pn = NewParseNode(cx, ts, PN_LIST, tc);
1068 22929     if (!pn)
1069 0         return NULL;
1070 22929     PN_INIT_LIST(pn);
1071
1072 22929     ts->flags |= TSF_OPERAND;
1073 308093     while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1074 285164         ts->flags &= ~TSF_OPERAND;
1075 285164         pn2 = Statement(cx, ts, tc);
1076 285164         if (!pn2) {
1077 0             if (ts->flags & TSF_EOF)
1078 0                 ts->flags |= TSF_UNEXPECTED_EOF;
1079 0             return NULL;
1080         }
1081 285164         ts->flags |= TSF_OPERAND;
1082
1083         /* If compiling top-level statements, emit as we go to save space. */
1084 285164         if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1085 189986             if (cx->fp->fun &&
1086                 JS_HAS_STRICT_OPTION(cx) &&
1087                 (tc->flags & TCF_RETURN_EXPR)) {
1088                 /*
1089                  * Check pn2 for lack of a final return statement if it is the
1090                  * last statement in the block.
1091                  */
1092 0                 tt = js_PeekToken(cx, ts);
1093 0                 if ((tt == TOK_EOF || tt == TOK_RC) &&
1094                     !CheckFinalReturn(cx, ts, pn2)) {
1095 0                     tt = TOK_ERROR;
1096 0                     break;
1097                 }
1098
1099                 /*
1100                  * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1101                  * CheckFinalReturn again.
1102                  */
1103 0                 tc->flags &= ~TCF_RETURN_EXPR;
1104             }
1105 189986             if (!js_FoldConstants(cx, pn2, tc) ||
1106                 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1107                 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1108 0                 tt = TOK_ERROR;
1109 0                 break;
1110             }
1111 189986             RecycleTree(pn2, tc);
1112         } else {
1113 95178             PN_APPEND(pn, pn2);
1114         }
1115     }
1116 22929     ts->flags &= ~TSF_OPERAND;
1117 22929     if (tt == TOK_ERROR)
1118 0         return NULL;
1119
1120 22929     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1121 22929     return pn;
1122 }
1123
1124 static JSParseNode *
1125 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1126 13589 {
1127 13589     JSParseNode *pn, *pn2;
1128
1129 13589     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1130 13589     pn = Expr(cx, ts, tc);
1131 13589     if (!pn)
1132 0         return NULL;
1133 13589     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1134
1135     /*
1136      * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1137      * greater precedence than ==.
1138      * XXX not ECMA, but documented in several books -- now a strict warning.
1139      */
1140 13589     if (pn->pn_type == TOK_ASSIGN &&
1141         pn->pn_op == JSOP_NOP &&
1142         pn->pn_right->pn_type > TOK_EQOP)
1143     {
1144 0         JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
1145 0         if (!js_ReportCompileErrorNumber(cx, ts,
1146                                          JSREPORT_TS |
1147                                          JSREPORT_WARNING |
1148                                          JSREPORT_STRICT,
1149                                          JSMSG_EQUAL_AS_ASSIGN,
1150                                          rewrite
1151                                          ? "\nAssuming equality test"
1152                                          : "")) {
1153 0             return NULL;
1154         }
1155 0         if (rewrite) {
1156 0             pn->pn_type = TOK_EQOP;
1157 0             pn->pn_op = (JSOp)cx->jsop_eq;
1158 0             pn2 = pn->pn_left;
1159 0             switch (pn2->pn_op) {
1160               case JSOP_SETNAME:
1161 0                 pn2->pn_op = JSOP_NAME;
1162 0                 break;
1163               case JSOP_SETPROP:
1164 0                 pn2->pn_op = JSOP_GETPROP;
1165 0                 break;
1166               case JSOP_SETELEM:
1167 0                 pn2->pn_op = JSOP_GETELEM;
1168                 break;
1169               default:
1170 13589                 JS_ASSERT(0);
1171             }
1172         }
1173     }
1174 13589     return pn;
1175 }
1176
1177 static JSBool
1178 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1179 496 {
1180 496     JSAtom *label;
1181 #if JS_HAS_LABEL_STATEMENT
1182 496     JSTokenType tt;
1183
1184 496     tt = js_PeekTokenSameLine(cx, ts);
1185 496     if (tt == TOK_ERROR)
1186 0         return JS_FALSE;
1187 496     if (tt == TOK_NAME) {
1188 0         (void) js_GetToken(cx, ts);
1189 0         label = CURRENT_TOKEN(ts).t_atom;
1190     } else {
1191 496         label = NULL;
1192     }
1193 #else
1194     label = NULL;
1195 #endif
1196 496     pn->pn_atom = label;
1197 496     return JS_TRUE;
1198 }
1199
1200 #if JS_HAS_EXPORT_IMPORT
1201 static JSParseNode *
1202 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1203 0 {
1204 0     JSParseNode *pn, *pn2;
1205 0     JSTokenType tt;
1206
1207 0     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1208 0     pn = NewParseNode(cx, ts, PN_NAME, tc);
1209 0     if (!pn)
1210 0         return NULL;
1211 0     pn->pn_op = JSOP_NAME;
1212 0     pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1213 0     pn->pn_expr = NULL;
1214 0     pn->pn_slot = -1;
1215 0     pn->pn_attrs = 0;
1216
1217 0     ts->flags |= TSF_OPERAND;
1218 0     while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1219 0         ts->flags &= ~TSF_OPERAND;
1220 0         if (pn->pn_op == JSOP_IMPORTALL)
1221 0             goto bad_import;
1222
1223 0         if (tt == TOK_DOT) {
1224 0             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1225 0             if (!pn2)
1226 0                 return NULL;
1227 0             if (js_MatchToken(cx, ts, TOK_STAR)) {
1228 0                 pn2->pn_op = JSOP_IMPORTALL;
1229 0                 pn2->pn_atom = NULL;
1230             } else {
1231 0                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1232 0                 pn2->pn_op = JSOP_GETPROP;
1233 0                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1234 0                 pn2->pn_slot = -1;
1235 0                 pn2->pn_attrs = 0;
1236             }
1237 0             pn2->pn_expr = pn;
1238 0             pn2->pn_pos.begin = pn->pn_pos.begin;
1239 0             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1240         } else {
1241             /* Make a TOK_LB binary node. */
1242 0             pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
1243 0             if (!pn2)
1244 0                 return NULL;
1245
1246 0             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1247         }
1248
1249 0         pn = pn2;
1250 0         ts->flags |= TSF_OPERAND;
1251     }
1252 0     ts->flags &= ~TSF_OPERAND;
1253 0     if (tt == TOK_ERROR)
1254 0         return NULL;
1255 0     js_UngetToken(ts);
1256
1257 0     switch (pn->pn_op) {
1258       case JSOP_GETPROP:
1259 0         pn->pn_op = JSOP_IMPORTPROP;
1260 0         break;
1261       case JSOP_GETELEM:
1262 0         pn->pn_op = JSOP_IMPORTELEM;
1263         break;
1264       case JSOP_IMPORTALL:
1265 0         break;
1266       default:
1267 0         goto bad_import;
1268     }
1269 0     return pn;
1270
1271   bad_import:
1272 0     js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1273                                 JSMSG_BAD_IMPORT);
1274 0     return NULL;
1275 }
1276 #endif /* JS_HAS_EXPORT_IMPORT */
1277
1278 extern const char js_with_statement_str[];
1279
1280 static JSParseNode *
1281 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1282 305766 {
1283 305766     JSTokenType tt;
1284 305766     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1285 305766     JSStmtInfo stmtInfo, *stmt, *stmt2;
1286 305766     JSAtom *label;
1287
1288 305766     CHECK_RECURSION();
1289
1290 305766     ts->flags |= TSF_OPERAND;
1291 305766     tt = js_GetToken(cx, ts);
1292 305766     ts->flags &= ~TSF_OPERAND;
1293
1294 #if JS_HAS_GETTER_SETTER
1295 305766     if (tt == TOK_NAME) {
1296 257845         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1297 257845         if (tt == TOK_ERROR)
1298 0             return NULL;
1299     }
1300 #endif
1301
1302 305766     switch (tt) {
1303 #if JS_HAS_EXPORT_IMPORT
1304       case TOK_EXPORT:
1305 0         pn = NewParseNode(cx, ts, PN_LIST, tc);
1306 0         if (!pn)
1307 0             return NULL;
1308 0         PN_INIT_LIST(pn);
1309 0         if (js_MatchToken(cx, ts, TOK_STAR)) {
1310 0             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
1311 0             if (!pn2)
1312 0                 return NULL;
1313 0             PN_APPEND(pn, pn2);
1314         } else {
1315 0             do {
1316 0                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1317 0                 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1318 0                 if (!pn2)
1319 0                     return NULL;
1320 0                 pn2->pn_op = JSOP_NAME;
1321 0                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1322 0                 pn2->pn_expr = NULL;
1323 0                 pn2->pn_slot = -1;
1324 0                 pn2->pn_attrs = 0;
1325 0                 PN_APPEND(pn, pn2);
1326 0             } while (js_MatchToken(cx, ts, TOK_COMMA));
1327         }
1328 0         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1329 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1330 0         break;
1331
1332       case TOK_IMPORT:
1333 0         pn = NewParseNode(cx, ts, PN_LIST, tc);
1334 0         if (!pn)
1335 0             return NULL;
1336 0         PN_INIT_LIST(pn);
1337 0         do {
1338 0             pn2 = ImportExpr(cx, ts, tc);
1339 0             if (!pn2)
1340 0                 return NULL;
1341 0             PN_APPEND(pn, pn2);
1342 0         } while (js_MatchToken(cx, ts, TOK_COMMA));
1343 0         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1344 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1345 0         break;
1346 #endif /* JS_HAS_EXPORT_IMPORT */
1347
1348       case TOK_FUNCTION:
1349 #if JS_HAS_XML_SUPPORT
1350 3552         if (js_PeekToken(cx, ts) == TOK_DBLCOLON)
1351 0             goto expression;
1352 #endif
1353 3552         return FunctionStmt(cx, ts, tc);
1354
1355       case TOK_IF:
1356         /* An IF node has three kids: condition, then, and optional else. */
1357 13365         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1358 13365         if (!pn)
1359 0             return NULL;
1360 13365         pn1 = Condition(cx, ts, tc);
1361 13365         if (!pn1)
1362 0             return NULL;
1363 13365         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1364 13365         pn2 = Statement(cx, ts, tc);
1365 13365         if (!pn2)
1366 0             return NULL;
1367 13365         if (js_MatchToken(cx, ts, TOK_ELSE)) {
1368 5596             stmtInfo.type = STMT_ELSE;
1369 5596             pn3 = Statement(cx, ts, tc);
1370 5596             if (!pn3)
1371 0                 return NULL;
1372 5596             pn->pn_pos.end = pn3->pn_pos.end;
1373         } else {
1374 7769             pn3 = NULL;
1375 7769             pn->pn_pos.end = pn2->pn_pos.end;
1376         }
1377 13365         js_PopStatement(tc);
1378 13365         pn->pn_kid1 = pn1;
1379 13365         pn->pn_kid2 = pn2;
1380 13365         pn->pn_kid3 = pn3;
1381 13365         return pn;
1382
1383 #if JS_HAS_SWITCH_STATEMENT
1384       case TOK_SWITCH:
1385       {
1386 0         JSParseNode *pn5;
1387 0         JSBool seenDefault = JS_FALSE;
1388
1389 0         pn = NewParseNode(cx, ts, PN_BINARY, tc);
1390 0         if (!pn)
1391 0             return NULL;
1392 0         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1393
1394         /* pn1 points to the switch's discriminant. */
1395 0         pn1 = Expr(cx, ts, tc);
1396 0         if (!pn1)
1397 0             return NULL;
1398
1399 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1400 0         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1401
1402         /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1403 0         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
1404 0         if (!pn2)
1405 0             return NULL;
1406 0         PN_INIT_LIST(pn2);
1407
1408 0         js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1409
1410 0         while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1411 0             switch (tt) {
1412               case TOK_DEFAULT:
1413 0                 if (seenDefault) {
1414 0                     js_ReportCompileErrorNumber(cx, ts,
1415                                                 JSREPORT_TS | JSREPORT_ERROR,
1416                                                 JSMSG_TOO_MANY_DEFAULTS);
1417 0                     return NULL;
1418                 }
1419 0                 seenDefault = JS_TRUE;
1420                 /* fall through */
1421
1422               case TOK_CASE:
1423 0                 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
1424 0                 if (!pn3)
1425 0                     return NULL;
1426 0                 if (tt == TOK_DEFAULT) {
1427 0                     pn3->pn_left = NULL;
1428                 } else {
1429 0                     pn3->pn_left = Expr(cx, ts, tc);
1430 0                     if (!pn3->pn_left)
1431 0                         return NULL;
1432                 }
1433 0                 PN_APPEND(pn2, pn3);
1434 0                 if (pn2->pn_count == JS_BIT(16)) {
1435 0                     js_ReportCompileErrorNumber(cx, ts,
1436                                                 JSREPORT_TS | JSREPORT_ERROR,
1437                                                 JSMSG_TOO_MANY_CASES);
1438 0                     return NULL;
1439                 }
1440 0                 break;
1441
1442               case TOK_ERROR:
1443 0                 return NULL;
1444
1445               default:
1446 0                 js_ReportCompileErrorNumber(cx, ts,
1447                                             JSREPORT_TS | JSREPORT_ERROR,
1448                                             JSMSG_BAD_SWITCH);
1449 0                 return NULL;
1450             }
1451 0             MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1452
1453 0             pn4 = NewParseNode(cx, ts, PN_LIST, tc);
1454 0             if (!pn4)
1455 0                 return NULL;
1456 0             pn4->pn_type = TOK_LC;
1457 0             PN_INIT_LIST(pn4);
1458 0             ts->flags |= TSF_OPERAND;
1459 0             while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1460                    tt != TOK_CASE && tt != TOK_DEFAULT) {
1461 0                 ts->flags &= ~TSF_OPERAND;
1462 0                 if (tt == TOK_ERROR)
1463 0                     return NULL;
1464 0                 pn5 = Statement(cx, ts, tc);
1465 0                 if (!pn5)
1466 0                     return NULL;
1467 0                 pn4->pn_pos.end = pn5->pn_pos.end;
1468 0                 PN_APPEND(pn4, pn5);
1469 0                 ts->flags |= TSF_OPERAND;
1470             }
1471 0             ts->flags &= ~TSF_OPERAND;
1472
1473             /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1474 0             if (pn4->pn_head)
1475 0                 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1476 0             pn3->pn_pos.end = pn4->pn_pos.end;
1477 0             pn3->pn_right = pn4;
1478         }
1479
1480 0         js_PopStatement(tc);
1481
1482 0         pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1483 0         pn->pn_kid1 = pn1;
1484 0         pn->pn_kid2 = pn2;
1485 0         return pn;
1486       }
1487 #endif /* JS_HAS_SWITCH_STATEMENT */
1488
1489       case TOK_WHILE:
1490 224         pn = NewParseNode(cx, ts, PN_BINARY, tc);
1491 224         if (!pn)
1492 0             return NULL;
1493 224         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1494 224         pn2 = Condition(cx, ts, tc);
1495 224         if (!pn2)
1496 0             return NULL;
1497 224         pn->pn_left = pn2;
1498 224         pn2 = Statement(cx, ts, tc);
1499 224         if (!pn2)
1500 0             return NULL;
1501 224         js_PopStatement(tc);
1502 224         pn->pn_pos.end = pn2->pn_pos.end;
1503 224         pn->pn_right = pn2;
1504 224         return pn;
1505
1506 #if JS_HAS_DO_WHILE_LOOP
1507       case TOK_DO:
1508 0         pn = NewParseNode(cx, ts, PN_BINARY, tc);
1509 0         if (!pn)
1510 0             return NULL;
1511 0         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1512 0         pn2 = Statement(cx, ts, tc);
1513 0         if (!pn2)
1514 0             return NULL;
1515 0         pn->pn_left = pn2;
1516 0         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1517 0         pn2 = Condition(cx, ts, tc);
1518 0         if (!pn2)
1519 0             return NULL;
1520 0         js_PopStatement(tc);
1521 0         pn->pn_pos.end = pn2->pn_pos.end;
1522 0         pn->pn_right = pn2;
1523 0         if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) {
1524             /*
1525              * All legacy and extended versions must do automatic semicolon
1526              * insertion after do-while.  See the testcase and discussion in
1527              * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
1528              */
1529 0             (void) js_MatchToken(cx, ts, TOK_SEMI);
1530 0             return pn;
1531         }
1532 1417         break;
1533 #endif /* JS_HAS_DO_WHILE_LOOP */
1534
1535       case TOK_FOR:
1536         /* A FOR node is binary, left is loop control and right is the body. */
1537 1417         pn = NewParseNode(cx, ts, PN_BINARY, tc);
1538 1417         if (!pn)
1539 0             return NULL;
1540 1417         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1541
1542 #if JS_HAS_XML_SUPPORT
1543 1417         pn->pn_op = JSOP_NOP;
1544 1417         if (js_MatchToken(cx, ts, TOK_NAME)) {
1545 0             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
1546 0                 pn->pn_op = JSOP_FOREACH;
1547             else
1548 0                 js_UngetToken(ts);
1549         }
1550 #endif
1551
1552 1417         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1553 1417         ts->flags |= TSF_OPERAND;
1554 1417         tt = js_PeekToken(cx, ts);
1555 1417         ts->flags &= ~TSF_OPERAND;
1556 1417         if (tt == TOK_SEMI) {
1557 #if JS_HAS_XML_SUPPORT
1558 0             if (pn->pn_op == JSOP_FOREACH)
1559 0                 goto bad_for_each;
1560 #endif
1561
1562             /* No initializer -- set first kid of left sub-node to null. */
1563 0             pn1 = NULL;
1564         } else {
1565             /* Set pn1 to a var list or an initializing expression. */
1566 #if JS_HAS_IN_OPERATOR
1567             /*
1568              * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1569              * of the for statement.  This flag will be used by the RelExpr
1570              * production; if it is set, then the 'in' keyword will not be
1571              * recognized as an operator, leaving it available to be parsed as
1572              * part of a for/in loop.  A side effect of this restriction is
1573              * that (unparenthesized) expressions involving an 'in' operator
1574              * are illegal in the init clause of an ordinary for loop.
1575              */
1576 1417             tc->flags |= TCF_IN_FOR_INIT;
1577 #endif /* JS_HAS_IN_OPERATOR */
1578 1417             if (tt == TOK_VAR) {
1579 1417                 (void) js_GetToken(cx, ts);
1580 1417                 pn1 = Variables(cx, ts, tc);
1581             } else {
1582 0                 pn1 = Expr(cx, ts, tc);
1583             }
1584 #if JS_HAS_IN_OPERATOR
1585 1417             tc->flags &= ~TCF_IN_FOR_INIT;
1586 #endif /* JS_HAS_IN_OPERATOR */
1587 1417             if (!pn1)
1588 0                 return NULL;
1589         }
1590
1591         /*
1592          * We can be sure that it's a for/in loop if there's still an 'in'
1593          * keyword here, even if JavaScript recognizes 'in' as an operator,
1594          * as we've excluded 'in' from being parsed in RelExpr by setting
1595          * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1596          */
1597 1417         if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1598 1353             stmtInfo.type = STMT_FOR_IN_LOOP;
1599
1600             /* Check that the left side of the 'in' is valid. */
1601 1353             while (pn1->pn_type == TOK_RP)
1602 0                 pn1 = pn1->pn_kid;
1603 1353             if ((pn1->pn_type == TOK_VAR)
1604                 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1605                 : (pn1->pn_type != TOK_NAME &&
1606                    pn1->pn_type != TOK_DOT &&
1607 #if JS_HAS_LVALUE_RETURN
1608                    pn1->pn_type != TOK_LP &&
1609 #endif
1610 #if JS_HAS_XML_SUPPORT
1611                    (pn1->pn_type != TOK_UNARYOP ||
1612                     pn1->pn_op != JSOP_XMLNAME) &&
1613 #endif
1614                    pn1->pn_type != TOK_LB)) {
1615 0                 js_ReportCompileErrorNumber(cx, ts,
1616                                             JSREPORT_TS | JSREPORT_ERROR,
1617                                             JSMSG_BAD_FOR_LEFTSIDE);
1618 0                 return NULL;
1619             }
1620
1621 1353             if (pn1->pn_type == TOK_VAR) {
1622                 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1623 1353                 pn1->pn_extra |= PNX_FORINVAR;
1624
1625                 /* Generate a final POP only if the var has an initializer. */
1626 1353                 pn2 = pn1->pn_head;
1627 1353                 if (pn2->pn_expr)
1628 0                     pn1->pn_extra |= PNX_POPVAR;
1629             } else {
1630 0                 pn2 = pn1;
1631 #if JS_HAS_LVALUE_RETURN
1632 0                 if (pn2->pn_type == TOK_LP)
1633 0                     pn2->pn_op = JSOP_SETCALL;
1634 #endif
1635 #if JS_HAS_XML_SUPPORT
1636 0                 if (pn2->pn_type == TOK_UNARYOP)
1637 0                     pn2->pn_op = JSOP_BINDXMLNAME;
1638 #endif
1639             }
1640
1641             /* Beware 'for (arguments in ...)' with or without a 'var'. */
1642 1353             if (pn2->pn_type == TOK_NAME &&
1643                 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1644 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1645             }
1646
1647             /* Parse the object expression as the right operand of 'in'. */
1648 1353             pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1649 1353             if (!pn2)
1650 0                 return NULL;
1651 1353             pn->pn_left = pn2;
1652         } else {
1653 #if JS_HAS_XML_SUPPORT
1654 64             if (pn->pn_op == JSOP_FOREACH)
1655 0                 goto bad_for_each;
1656 #endif
1657
1658             /* Parse the loop condition or null into pn2. */
1659 64             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1660 64             ts->flags |= TSF_OPERAND;
1661 64             tt = js_PeekToken(cx, ts);
1662 64             ts->flags &= ~TSF_OPERAND;
1663 64             if (tt == TOK_SEMI) {
1664 0                 pn2 = NULL;
1665             } else {
1666 64                 pn2 = Expr(cx, ts, tc);
1667 64                 if (!pn2)
1668 0                     return NULL;
1669             }
1670
1671             /* Parse the update expression or null into pn3. */
1672 64             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1673 64             ts->flags |= TSF_OPERAND;
1674 64             tt = js_PeekToken(cx, ts);
1675 64             ts->flags &= ~TSF_OPERAND;
1676 64             if (tt == TOK_RP) {
1677 0                 pn3 = NULL;
1678             } else {
1679 64                 pn3 = Expr(cx, ts, tc);
1680 64                 if (!pn3)
1681 0                     return NULL;
1682             }
1683
1684             /* Build the RESERVED node to use as the left kid of pn. */
1685 64             pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
1686 64             if (!pn4)
1687 0                 return NULL;
1688 64             pn4->pn_type = TOK_RESERVED;
1689 64             pn4->pn_op = JSOP_NOP;
1690 64             pn4->pn_kid1 = pn1;
1691 64             pn4->pn_kid2 = pn2;
1692 64             pn4->pn_kid3 = pn3;
1693 64             pn->pn_left = pn4;
1694         }
1695
1696 1417         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1697
1698         /* Parse the loop body into pn->pn_right. */
1699 1417         pn2 = Statement(cx, ts, tc);
1700 1417         if (!pn2)
1701 0             return NULL;
1702 1417         pn->pn_right = pn2;
1703 1417         js_PopStatement(tc);
1704
1705         /* Record the absolute line number for source note emission. */
1706 1417         pn->pn_pos.end = pn2->pn_pos.end;
1707 1417         return pn;
1708
1709 #if JS_HAS_XML_SUPPORT
1710       bad_for_each:
1711 0         js_ReportCompileErrorNumber(cx, pn,
1712                                     JSREPORT_PN | JSREPORT_ERROR,
1713                                     JSMSG_BAD_FOR_EACH_LOOP);
1714 0         return NULL;
1715 #endif
1716
1717 #if JS_HAS_EXCEPTIONS
1718       case TOK_TRY: {
1719 32         JSParseNode *catchtail = NULL;
1720         /*
1721          * try nodes are ternary.
1722          * kid1 is the try Statement
1723          * kid2 is the catch node
1724          * kid3 is the finally Statement
1725          *
1726          * catch nodes are ternary.
1727          * kid1 is the discriminant
1728          * kid2 is the next catch node, or NULL
1729          * kid3 is the catch block (on kid3 so that we can always append a
1730          *                          new catch pn on catchtail->kid2)
1731          *
1732          * catch discriminant nodes are binary
1733          * atom is the receptacle
1734          * expr is the discriminant code
1735          *
1736          * finally nodes are unary (just the finally expression)
1737          */
1738 32         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1739 32         pn->pn_op = JSOP_NOP;
1740
1741 32         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1742 32         js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1743 32         pn->pn_kid1 = Statements(cx, ts, tc);
1744 32         if (!pn->pn_kid1)
1745 0             return NULL;
1746 32         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1747 32         js_PopStatement(tc);
1748
1749 32         catchtail = pn;
1750 64         while (js_PeekToken(cx, ts) == TOK_CATCH) {
1751             /* check for another catch after unconditional catch */
1752 32             if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1753 0                 js_ReportCompileErrorNumber(cx, ts,
1754                                             JSREPORT_TS | JSREPORT_ERROR,
1755                                             JSMSG_CATCH_AFTER_GENERAL);
1756 0                 return NULL;
1757             }
1758
1759             /*
1760              * legal catch forms are:
1761              * catch (v)
1762              * catch (v if <boolean_expression>)
1763              * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1764              */
1765 32             (void) js_GetToken(cx, ts); /* eat `catch' */
1766 32             pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
1767 32             if (!pn2)
1768 0                 return NULL;
1769
1770             /*
1771              * We use a PN_NAME for the discriminant (catchguard) node
1772              * with the actual discriminant code in the initializer spot
1773              */
1774 32             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1775 32             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1776 32             pn3 = NewParseNode(cx, ts, PN_NAME, tc);
1777 32             if (!pn3)
1778 0                 return NULL;
1779
1780 32             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1781 32             pn3->pn_expr = NULL;
1782 #if JS_HAS_CATCH_GUARD
1783             /*
1784              * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1785              * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1786              */
1787 32             if (js_PeekToken(cx, ts) == TOK_IF) {
1788 0                 (void)js_GetToken(cx, ts); /* eat `if' */
1789 0                 pn3->pn_expr = Expr(cx, ts, tc);
1790 0                 if (!pn3->pn_expr)
1791 0                     return NULL;
1792             }
1793 #endif
1794 32             pn2->pn_kid1 = pn3;
1795
1796 32             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1797
1798 32             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1799 32             js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1800 32             stmtInfo.label = pn3->pn_atom;
1801 32             pn2->pn_kid3 = Statements(cx, ts, tc);
1802 32             if (!pn2->pn_kid3)
1803 0                 return NULL;
1804 32             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1805 32             js_PopStatement(tc);
1806
1807 32             catchtail = catchtail->pn_kid2 = pn2;
1808         }
1809 32         catchtail->pn_kid2 = NULL;
1810
1811 32         if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1812 0             tc->tryCount++;
1813 0             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1814 0             js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1815 0             pn->pn_kid3 = Statements(cx, ts, tc);
1816 0             if (!pn->pn_kid3)
1817 0                 return NULL;
1818 0             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1819 0             js_PopStatement(tc);
1820         } else {
1821 32             pn->pn_kid3 = NULL;
1822         }
1823 32         if (!pn->pn_kid2 && !pn->pn_kid3) {
1824 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1825                                         JSMSG_CATCH_OR_FINALLY);
1826 0             return NULL;
1827         }
1828 32         tc->tryCount++;
1829 32         return pn;
1830       }
1831
1832       case TOK_THROW:
1833 71         pn = NewParseNode(cx, ts, PN_UNARY, tc);
1834 71         if (!pn)
1835 0             return NULL;
1836
1837         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
1838 71         ts->flags |= TSF_OPERAND;
1839 71         tt = js_PeekTokenSameLine(cx, ts);
1840 71         ts->flags &= ~TSF_OPERAND;
1841 71         if (tt == TOK_ERROR)
1842 0             return NULL;
1843 71         if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
1844 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1845                                         JSMSG_SYNTAX_ERROR);
1846 0             return NULL;
1847         }
1848
1849 71         pn2 = Expr(cx, ts, tc);
1850 71         if (!pn2)
1851 0             return NULL;
1852 71         pn->pn_pos.end = pn2->pn_pos.end;
1853 71         pn->pn_op = JSOP_THROW;
1854 71         pn->pn_kid = pn2;
1855 71         break;
1856
1857       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1858       case TOK_CATCH:
1859 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1860                                     JSMSG_CATCH_WITHOUT_TRY);
1861 0         return NULL;
1862
1863       case TOK_FINALLY:
1864 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1865                                     JSMSG_FINALLY_WITHOUT_TRY);
1866 0         return NULL;
1867
1868 #endif /* JS_HAS_EXCEPTIONS */
1869
1870       case TOK_BREAK:
1871 32         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1872 32         if (!pn)
1873 0             return NULL;
1874 32         if (!MatchLabel(cx, ts, pn))
1875 0             return NULL;
1876 32         stmt = tc->topStmt;
1877 32         label = pn->pn_atom;
1878 32         if (label) {
1879 0             for (; ; stmt = stmt->down) {
1880 0                 if (!stmt) {
1881 0                     js_ReportCompileErrorNumber(cx, ts,
1882                                                 JSREPORT_TS | JSREPORT_ERROR,
1883                                                 JSMSG_LABEL_NOT_FOUND);
1884 0                     return NULL;
1885                 }
1886 0                 if (stmt->type == STMT_LABEL && stmt->label == label)
1887 0                     break;
1888             }
1889         } else {
1890 96             for (; ; stmt = stmt->down) {
1891 64                 if (!stmt) {
1892 0                     js_ReportCompileErrorNumber(cx, ts,
1893                                                 JSREPORT_TS | JSREPORT_ERROR,
1894                                                 JSMSG_TOUGH_BREAK);
1895 0                     return NULL;
1896                 }
1897 64                 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1898 32                     break;
1899             }
1900         }
1901 32         if (label)
1902 0             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1903 0         break;
1904
1905       case TOK_CONTINUE:
1906 464         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1907 464         if (!pn)
1908 0             return NULL;
1909 464         if (!MatchLabel(cx, ts, pn))
1910 0             return NULL;
1911 464         stmt = tc->topStmt;
1912 464         label = pn->pn_atom;
1913 464         if (label) {
1914 0             for (stmt2 = NULL; ; stmt = stmt->down) {
1915 0                 if (!stmt) {
1916 0                     js_ReportCompileErrorNumber(cx, ts,
1917                                                 JSREPORT_TS | JSREPORT_ERROR,
1918                                                 JSMSG_LABEL_NOT_FOUND);
1919 0                     return NULL;
1920                 }
1921 0                 if (stmt->type == STMT_LABEL) {
1922 0                     if (stmt->label == label) {
1923 0                         if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1924 0                             js_ReportCompileErrorNumber(cx, ts,
1925                                                         JSREPORT_TS |
1926                                                         JSREPORT_ERROR,
1927                                                         JSMSG_BAD_CONTINUE);
1928 0                             return NULL;
1929                         }
1930 0                         break;
1931                     }
1932                 } else {
1933 0                     stmt2 = stmt;
1934                 }
1935             }
1936         } else {
1937 3568             for (; ; stmt = stmt->down) {
1938 2016                 if (!stmt) {
1939 0                     js_ReportCompileErrorNumber(cx, ts,
1940                                                 JSREPORT_TS | JSREPORT_ERROR,
1941                                                 JSMSG_BAD_CONTINUE);
1942 0                     return NULL;
1943                 }
1944 2016                 if (STMT_IS_LOOP(stmt))
1945 464                     break;
1946             }
1947         }
1948 464         if (label)
1949 0             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1950 0         break;
1951
1952       case TOK_WITH:
1953 0         pn = NewParseNode(cx, ts, PN_BINARY, tc);
1954 0         if (!pn)
1955 0             return NULL;
1956 0         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1957 0         pn2 = Expr(cx, ts, tc);
1958 0         if (!pn2)
1959 0             return NULL;
1960 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1961 0         pn->pn_left = pn2;
1962
1963 0         js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1964 0         pn2 = Statement(cx, ts, tc);
1965 0         if (!pn2)
1966 0             return NULL;
1967 0         js_PopStatement(tc);
1968
1969 0         pn->pn_pos.end = pn2->pn_pos.end;
1970 0         pn->pn_right = pn2;
1971 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1972 0         return pn;
1973
1974       case TOK_VAR:
1975 8162         pn = Variables(cx, ts, tc);
1976 8162         if (!pn)
1977 0             return NULL;
1978
1979         /* Tell js_EmitTree to generate a final POP. */
1980 8162         pn->pn_extra |= PNX_POPVAR;
1981 8162         break;
1982
1983       case TOK_RETURN:
1984 2464         if (!(tc->flags & TCF_IN_FUNCTION)) {
1985 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1986                                         JSMSG_BAD_RETURN);
1987 0             return NULL;
1988         }
1989 2464         pn = NewParseNode(cx, ts, PN_UNARY, tc);
1990 2464         if (!pn)
1991 0             return NULL;
1992
1993         /* This is ugly, but we don't want to require a semicolon. */
1994 2464         ts->flags |= TSF_OPERAND;
1995 2464         tt = js_PeekTokenSameLine(cx, ts);
1996 2464         ts->flags &= ~TSF_OPERAND;
1997 2464         if (tt == TOK_ERROR)
1998 0             return NULL;
1999
2000 2464         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2001 1536             pn2 = Expr(cx, ts, tc);
2002 1536             if (!pn2)
2003 0                 return NULL;
2004 1536             tc->flags |= TCF_RETURN_EXPR;
2005 1536             pn->pn_pos.end = pn2->pn_pos.end;
2006 1536             pn->pn_kid = pn2;
2007         } else {
2008 928             tc->flags |= TCF_RETURN_VOID;
2009 928             pn->pn_kid = NULL;
2010         }
2011
2012 2464         if (JS_HAS_STRICT_OPTION(cx) &&
2013             (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
2014             /*
2015              * We must be in a frame with a non-native function, because
2016              * we're compiling one.
2017              */
2018 0             if (!ReportNoReturnValue(cx, ts))
2019 0                 return NULL;
2020         }
2021 18106         break;
2022
2023       case TOK_LC:
2024 18106         js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
2025 18106         pn = Statements(cx, ts, tc);
2026 18106         if (!pn)
2027 0             return NULL;
2028
2029 18106         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
2030 18106         js_PopStatement(tc);
2031 18106         return pn;
2032
2033       case TOK_EOL:
2034       case TOK_SEMI:
2035 0         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2036 0         if (!pn)
2037 0             return NULL;
2038 0         pn->pn_type = TOK_SEMI;
2039 0         pn->pn_kid = NULL;
2040 0         return pn;
2041
2042 #if JS_HAS_DEBUGGER_KEYWORD
2043       case TOK_DEBUGGER:
2044 0         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
2045 0         if (!pn)
2046 0             return NULL;
2047 0         pn->pn_type = TOK_DEBUGGER;
2048 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
2049 0         break;
2050 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2051
2052 #if JS_HAS_XML_SUPPORT
2053       case TOK_DEFAULT:
2054 0         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2055 0         if (!pn)
2056 0             return NULL;
2057 0         if (!js_MatchToken(cx, ts, TOK_NAME) ||
2058             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
2059             !js_MatchToken(cx, ts, TOK_NAME) ||
2060             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
2061             !js_MatchToken(cx, ts, TOK_ASSIGN) ||
2062             CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2063 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2064                                         JSMSG_BAD_DEFAULT_XML_NAMESPACE);
2065 0             return NULL;
2066         }
2067 0         pn2 = Expr(cx, ts, tc);
2068 0         if (!pn2)
2069 0             return NULL;
2070 0         pn->pn_op = JSOP_DEFXMLNS;
2071 0         pn->pn_pos.end = pn2->pn_pos.end;
2072 0         pn->pn_kid = pn2;
2073 0         tc->flags |= TCF_HAS_DEFXMLNS;
2074 0         break;
2075 #endif
2076
2077       case TOK_ERROR:
2078 0         return NULL;
2079
2080       default:
2081 #if JS_HAS_XML_SUPPORT
2082       expression:
2083 #endif
2084 257877         js_UngetToken(ts);
2085 257877         pn2 = Expr(cx, ts, tc);
2086 257877         if (!pn2)
2087 0             return NULL;
2088
2089 257877         if (js_PeekToken(cx, ts) == TOK_COLON) {
2090 0             if (pn2->pn_type != TOK_NAME) {
2091 0                 js_ReportCompileErrorNumber(cx, ts,
2092                                             JSREPORT_TS | JSREPORT_ERROR,
2093                                             JSMSG_BAD_LABEL);
2094 0                 return NULL;
2095             }
2096 0             label = pn2->pn_atom;
2097 0             for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
2098 0                 if (stmt->type == STMT_LABEL && stmt->label == label) {
2099 0                     js_ReportCompileErrorNumber(cx, ts,
2100                                                 JSREPORT_TS | JSREPORT_ERROR,
2101                                                 JSMSG_DUPLICATE_LABEL);
2102 0                     return NULL;
2103                 }
2104             }
2105 0             (void) js_GetToken(cx, ts);
2106
2107             /* Push a label struct and parse the statement. */
2108 0             js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
2109 0             stmtInfo.label = label;
2110 0             pn = Statement(cx, ts, tc);
2111 0             if (!pn)
2112 0                 return NULL;
2113
2114             /* Pop the label, set pn_expr, and return early. */
2115 0             js_PopStatement(tc);
2116 0             pn2->pn_type = TOK_COLON;
2117 0             pn2->pn_pos.end = pn->pn_pos.end;
2118 0             pn2->pn_expr = pn;
2119 0             return pn2;
2120         }
2121
2122 257877         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2123 257877         if (!pn)
2124 0             return NULL;
2125 257877         pn->pn_type = TOK_SEMI;
2126 257877         pn->pn_pos = pn2->pn_pos;
2127 257877         pn->pn_kid = pn2;
2128 269070         break;
2129     }
2130
2131     /* Check termination of this primitive statement. */
2132 269070     if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2133 268844         tt = js_PeekTokenSameLine(cx, ts);
2134 268844         if (tt == TOK_ERROR)
2135 0             return NULL;
2136 268844         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2137 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2138                                         JSMSG_SEMI_BEFORE_STMNT);
2139 0             return NULL;
2140         }
2141     }
2142
2143 269070     (void) js_MatchToken(cx, ts, TOK_SEMI);
2144 269070     return pn;
2145 }
2146
2147 static JSParseNode *
2148 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2149 9579 {
2150 9579     JSParseNode *pn, *pn2;
2151 9579     JSObject *obj, *pobj;
2152 9579     JSStackFrame *fp;
2153 9579     JSFunction *fun;
2154 9579     JSClass *clasp;
2155 9579     JSPropertyOp getter, setter, currentGetter, currentSetter;
2156 9579     JSAtom *atom;
2157 9579     JSAtomListElement *ale;
2158 9579     JSOp prevop;
2159 9579     JSProperty *prop;
2160 9579     JSScopeProperty *sprop;
2161 9579     JSBool ok;
2162
2163     /*
2164      * The tricky part of this code is to create special parsenode opcodes for
2165      * getting and setting variables (which will be stored as special slots in
2166      * the frame).  The complex special case is an eval() inside a function.
2167      * If the evaluated string references variables in the enclosing function,
2168      * then we need to generate the special variable opcodes.  We determine
2169      * this by looking up the variable id in the current variable scope.
2170      */
2171 9579     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
2172 9579     pn = NewParseNode(cx, ts, PN_LIST, tc);
2173 9579     if (!pn)
2174 0         return NULL;
2175 9579     pn->pn_op = CURRENT_TOKEN(ts).t_op;
2176 9579     PN_INIT_LIST(pn);
2177
2178     /*
2179      * Skip eval and debugger frames when looking for the function whose code
2180      * is being compiled.  If we are called from FunctionBody, TCF_IN_FUNCTION
2181      * will be set in tc->flags, and we can be sure fp->fun is the function to
2182      * use.  But if a function calls eval, the string argument is treated as a
2183      * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2184      *
2185      * What's more, when the following code is reached from eval, cx->fp->fun
2186      * is eval's JSFunction (a native function), so we need to skip its frame.
2187      * We should find the scripted caller's function frame just below it, but
2188      * we code a loop out of paranoia.
2189      */
2190 9579     for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2191 0         continue;
2192 9579     obj = fp->varobj;
2193 9579     fun = fp->fun;
2194 9579     clasp = OBJ_GET_CLASS(cx, obj);
2195 9579     if (fun && clasp == &js_FunctionClass) {
2196         /* We are compiling code inside a function */
2197 5536         getter = js_GetLocalVariable;
2198 5536         setter = js_SetLocalVariable;
2199 4043     } else if (fun && clasp == &js_CallClass) {
2200         /* We are compiling code from an eval inside a function */
2201 0         getter = js_GetCallVariable;
2202 0         setter = js_SetCallVariable;
2203     } else {
2204 4043         getter = clasp->getProperty;
2205 4043         setter = clasp->setProperty;
2206     }
2207
2208 9579     ok = JS_TRUE;
2209 9623     do {
2210 9623         currentGetter = getter;
2211 9623         currentSetter = setter;
2212 9623         MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2213 9623         atom = CURRENT_TOKEN(ts).t_atom;
2214
2215 9623         ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2216 9623         if (ale) {
2217 3239             prevop = ALE_JSOP(ale);
2218 3239             if (JS_HAS_STRICT_OPTION(cx)
2219                 ? pn->pn_op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
2220                 : pn->pn_op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
2221 0                 const char *name = js_AtomToPrintableString(cx, atom);
2222 0                 if (!name ||
2223                     !js_ReportCompileErrorNumber(cx, ts,
2224                                                  (pn->pn_op != JSOP_DEFCONST &&
2225                                                   prevop != JSOP_DEFCONST)
2226                                                  ? JSREPORT_TS |
2227                                                    JSREPORT_WARNING |
2228                                                    JSREPORT_STRICT
2229                                                  : JSREPORT_TS | JSREPORT_ERROR,
2230                                                  JSMSG_REDECLARED_VAR,
2231                                                  (prevop == JSOP_DEFFUN ||
2232                                                   prevop == JSOP_CLOSURE)
2233                                                  ? js_function_str
2234                                                  : (prevop == JSOP_DEFCONST)
2235                                                  ? js_const_str
2236                                                  : js_var_str,
2237                                                  name)) {
2238 0                     return NULL;
2239                 }
2240             }
2241 3239             if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2242 0                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2243         } else {
2244 6384             ale = js_IndexAtom(cx, atom, &tc->decls);
2245 6384             if (!ale)
2246 0                 return NULL;
2247         }
2248 9623         ALE_SET_JSOP(ale, pn->pn_op);
2249
2250 9623         pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2251 9623         if (!pn2)
2252 0             return NULL;
2253 9623         pn2->pn_op = JSOP_NAME;
2254 9623         pn2->pn_atom = atom;
2255 9623         pn2->pn_expr = NULL;
2256 9623         pn2->pn_slot = -1;
2257 9623         pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2258                         ? JSPROP_PERMANENT | JSPROP_READONLY
2259                         : JSPROP_PERMANENT;
2260 9623         PN_APPEND(pn, pn2);
2261
2262 9623         if (!fun) {
2263             /* Don't lookup global variables at compile time. */
2264 160             prop = NULL;
2265 9463         } else if (OBJ_IS_NATIVE(obj)) {
2266 9463             if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
2267                                          &pobj, &prop)) {
2268 0                 return NULL;
2269             }
2270         } else {
2271 0             if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
2272 0                 return NULL;
2273         }
2274 9623         if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2275 1184             sprop = (JSScopeProperty *)prop;
2276 1184             if (sprop->getter == js_GetArgument) {
2277 0                 const char *name = js_AtomToPrintableString(cx, atom);
2278 0                 if (!name) {
2279 0                     ok = JS_FALSE;
2280 0                 } else if (pn->pn_op == JSOP_DEFCONST) {
2281 0                     js_ReportCompileErrorNumber(cx, ts,
2282                                                 JSREPORT_TS | JSREPORT_ERROR,
2283                                                 JSMSG_REDECLARED_PARAM,
2284                                                 name);
2285 0                     ok = JS_FALSE;
2286                 } else {
2287 0                     currentGetter = js_GetArgument;
2288 0                     currentSetter = js_SetArgument;
2289 0                     ok = js_ReportCompileErrorNumber(cx, ts,
2290                                                      JSREPORT_TS |
2291                                                      JSREPORT_WARNING |
2292                                                      JSREPORT_STRICT,
2293                                                      JSMSG_VAR_HIDES_ARG,
2294                                                      name);
2295                 }
2296             } else {
2297 1184                 if (fun) {
2298                     /* Not an argument, must be a redeclared local var. */
2299 1184                     if (clasp == &js_FunctionClass) {
2300 1184                         JS_ASSERT(sprop->getter == js_GetLocalVariable);
2301 1184                         JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2302                                   (uint16) sprop->shortid < fun->nvars);
2303 0                     } else if (clasp == &js_CallClass) {
2304 0                         if (sprop->getter == js_GetCallVariable) {
2305                             /*
2306                              * Referencing a variable introduced by a var
2307                              * statement in the enclosing function. Check
2308                              * that the slot number we have is in range.
2309                              */
2310 0                             JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2311                                       (uint16) sprop->shortid < fun->nvars);
2312                         } else {
2313                             /*
2314                              * A variable introduced through another eval:
2315                              * don't use the special getters and setters
2316                              * since we can't allocate a slot in the frame.
2317                              */
2318 0                             currentGetter = sprop->getter;
2319 0                             currentSetter = sprop->setter;
2320                         }
2321                     }
2322
2323                     /* Override the old getter and setter, to handle eval. */
2324 1184                     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2325                                                          0, sprop->attrs,
2326                                                          currentGetter,
2327                                                          currentSetter);
2328 1184                     if (!sprop)
2329 0                         ok = JS_FALSE;
2330                 }
2331             }
2332         } else {
2333             /*
2334              * Property not found in current variable scope: we have not seen
2335              * this variable before.  Define a new local variable by adding a
2336              * property to the function's scope, allocating one slot in the
2337              * function's frame.  Global variables and any locals declared in
2338              * with statement bodies are handled at runtime, by script prolog
2339              * JSOP_DEFVAR bytecodes generated for slot-less vars.
2340              */
2341 8439             sprop = NULL;
2342 8439             if (prop) {
2343 0                 OBJ_DROP_PROPERTY(cx, pobj, prop);
2344 0                 prop = NULL;
2345             }
2346 8439             if (currentGetter == js_GetCallVariable) {
2347                 /* Can't increase fun->nvars in an active frame! */
2348 0                 currentGetter = clasp->getProperty;
2349 0                 currentSetter = clasp->setProperty;
2350             }
2351 8439             if (currentGetter == js_GetLocalVariable &&
2352                 atom != cx->runtime->atomState.argumentsAtom &&
2353                 fp->scopeChain == obj &&
2354                 !js_InWithStatement(tc)) {
2355 4352                 if (!js_AddHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
2356                                           currentGetter, currentSetter,
2357                                           SPROP_INVALID_SLOT,
2358                                           pn2->pn_attrs | JSPROP_SHARED,
2359                                           SPROP_HAS_SHORTID, fun->nvars)) {
2360 0                     return NULL;
2361                 }
2362 4352                 if (fun->nvars == JS_BITMASK(16)) {
2363 0                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2364                                          JSMSG_TOO_MANY_FUN_VARS);
2365 0                     return NULL;
2366                 }
2367 4352                 fun->nvars++;
2368             }
2369         }
2370
2371 9623         if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2372 7828             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2373 0                 js_ReportCompileErrorNumber(cx, ts,
2374                                             JSREPORT_TS | JSREPORT_ERROR,
2375                                             JSMSG_BAD_VAR_INIT);
2376 0                 ok = JS_FALSE;
2377             } else {
2378 7828                 pn2->pn_expr = AssignExpr(cx, ts, tc);
2379 7828                 if (!pn2->pn_expr) {
2380 0                     ok = JS_FALSE;
2381                 } else {
2382 7828                     pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2383                                  ? JSOP_SETCONST
2384                                  : JSOP_SETNAME;
2385 7828                     if (atom == cx->runtime->atomState.argumentsAtom)
2386 0                         tc->flags |= TCF_FUN_HEAVYWEIGHT;
2387                 }
2388             }
2389         }
2390
2391 9623         if (prop)
2392 1184             OBJ_DROP_PROPERTY(cx, pobj, prop);
2393 9623         if (!ok)
2394 0             return NULL;
2395 9623     } while (js_MatchToken(cx, ts, TOK_COMMA));
2396
2397 9579     pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2398 9579     return pn;
2399 }
2400
2401 static JSParseNode *
2402 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2403 296339 {
2404 296339     JSParseNode *pn, *pn2;
2405
2406 296339     pn = AssignExpr(cx, ts, tc);
2407 296339     if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2408 0         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2409 0         if (!pn2)
2410 0             return NULL;
2411 0         pn2->pn_pos.begin = pn->pn_pos.begin;
2412 0         PN_INIT_LIST_1(pn2, pn);
2413 0         pn = pn2;
2414 0         do {
2415 0             pn2 = AssignExpr(cx, ts, tc);
2416 0             if (!pn2)
2417 0                 return NULL;
2418 0             PN_APPEND(pn, pn2);
2419 0         } while (js_MatchToken(cx, ts, TOK_COMMA));
2420 0         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2421     }
2422 296339     return pn;
2423 }
2424
2425 static JSParseNode *
2426 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2427 610754 {
2428 610754     JSParseNode *pn, *pn2;
2429 610754     JSTokenType tt;
2430 610754     JSOp op;
2431
2432 610754     CHECK_RECURSION();
2433
2434 610754     pn = CondExpr(cx, ts, tc);
2435 610754     if (!pn)
2436 0         return NULL;
2437
2438 610754     tt = js_GetToken(cx, ts);
2439 #if JS_HAS_GETTER_SETTER
2440 610754     if (tt == TOK_NAME) {
2441 0         tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2442 0         if (tt == TOK_ERROR)
2443 0             return NULL;
2444     }
2445 #endif
2446 610754     if (tt != TOK_ASSIGN) {
2447 601766         js_UngetToken(ts);
2448 601766         return pn;
2449     }
2450
2451 8988     op = CURRENT_TOKEN(ts).t_op;
2452 8988     for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2453 0         continue;
2454 8988     switch (pn2->pn_type) {
2455       case TOK_NAME:
2456 6144         pn2->pn_op = JSOP_SETNAME;
2457 6144         if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2458 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
2459 0         break;
2460       case TOK_DOT:
2461 320         pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
2462                      ? JSOP_SETMETHOD
2463                      : JSOP_SETPROP;
2464 320         break;
2465       case TOK_LB:
2466 2524         pn2->pn_op = JSOP_SETELEM;
2467 2524         break;
2468 #if JS_HAS_LVALUE_RETURN
2469       case TOK_LP:
2470 0         pn2->pn_op = JSOP_SETCALL;
2471 0         break;
2472 #endif
2473 #if JS_HAS_XML_SUPPORT
2474       case TOK_UNARYOP:
2475 0         if (pn2->pn_op == JSOP_XMLNAME) {
2476 0             pn2->pn_op = JSOP_SETXMLNAME;
2477 0             break;
2478         }
2479         /* FALL THROUGH */
2480 #endif
2481       default:
2482 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2483                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
2484 0         return NULL;
2485     }
2486 8988     pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2487 8988     return pn;
2488 }
2489
2490 static JSParseNode *
2491 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2492 610754 {
2493 610754     JSParseNode *pn, *pn1, *pn2, *pn3;
2494 #if JS_HAS_IN_OPERATOR
2495 610754     uintN oldflags;
2496 #endif /* JS_HAS_IN_OPERATOR */
2497
2498 610754     pn = OrExpr(cx, ts, tc);
2499 610754     if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2500 0         pn1 = pn;
2501 0         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2502 0         if (!pn)
2503 0             return NULL;
2504 #if JS_HAS_IN_OPERATOR
2505         /*
2506          * Always accept the 'in' operator in the middle clause of a ternary,
2507          * where it's unambiguous, even if we might be parsing the init of a
2508          * for statement.
2509          */
2510 0         oldflags = tc->flags;
2511 0         tc->flags &= ~TCF_IN_FOR_INIT;
2512 #endif /* JS_HAS_IN_OPERATOR */
2513 0         pn2 = AssignExpr(cx, ts, tc);
2514 #if JS_HAS_IN_OPERATOR
2515 0         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2516 #endif /* JS_HAS_IN_OPERATOR */
2517
2518 0         if (!pn2)
2519 0             return NULL;
2520 0         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2521 0         pn3 = AssignExpr(cx, ts, tc);
2522 0         if (!pn3)
2523 0             return NULL;
2524 0         pn->pn_pos.begin = pn1->pn_pos.begin;
2525 0         pn->pn_pos.end = pn3->pn_pos.end;
2526 0         pn->pn_kid1 = pn1;
2527 0         pn->pn_kid2 = pn2;
2528 0         pn->pn_kid3 = pn3;
2529     }
2530 610754     return pn;
2531 }
2532
2533 static JSParseNode *
2534 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2535 612261 {
2536 612261     JSParseNode *pn;
2537
2538 612261     pn = AndExpr(cx, ts, tc);
2539 612261     if (pn && js_MatchToken(cx, ts, TOK_OR))
2540 1507         pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2541 612261     return pn;
2542 }
2543
2544 static JSParseNode *
2545 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2546 614713 {
2547 614713     JSParseNode *pn;
2548
2549 614713     pn = BitOrExpr(cx, ts, tc);
2550 614713     if (pn && js_MatchToken(cx, ts, TOK_AND))
2551 2452         pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2552 614713     return pn;
2553 }
2554
2555 static JSParseNode *
2556 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2557 614713 {
2558 614713     JSParseNode *pn;
2559
2560 614713     pn = BitXorExpr(cx, ts, tc);
2561 614713     while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2562 0         pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2563                        tc);
2564     }
2565 614713     return pn;
2566 }
2567
2568 static JSParseNode *
2569 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2570 614713 {
2571 614713     JSParseNode *pn;
2572
2573 614713     pn = BitAndExpr(cx, ts, tc);
2574 614713     while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2575 0         pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2576                        tc);
2577     }
2578 614713     return pn;
2579 }
2580
2581 static JSParseNode *
2582 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2583 614713 {
2584 614713     JSParseNode *pn;
2585
2586 614713     pn = EqExpr(cx, ts, tc);
2587 614713     while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2588 0         pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2589 614713     return pn;
2590 }
2591
2592 static JSParseNode *
2593 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2594 614713 {
2595 614713     JSParseNode *pn;
2596 614713     JSOp op;
2597
2598 614713     pn = RelExpr(cx, ts, tc);
2599 629206     while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2600 14493         op = CURRENT_TOKEN(ts).t_op;
2601 14493         pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2602     }
2603 614713     return pn;
2604 }
2605
2606 static JSParseNode *
2607 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2608 629206 {
2609 629206     JSParseNode *pn;
2610 629206     JSTokenType tt;
2611 629206     JSOp op;
2612 #if JS_HAS_IN_OPERATOR
2613 629206     uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2614
2615     /*
2616      * Uses of the in operator in ShiftExprs are always unambiguous,
2617      * so unset the flag that prohibits recognizing it.
2618      */
2619 629206     tc->flags &= ~TCF_IN_FOR_INIT;
2620 #endif /* JS_HAS_IN_OPERATOR */
2621
2622 629206     pn = ShiftExpr(cx, ts, tc);
2623 629590     while (pn &&
2624            (js_MatchToken(cx, ts, TOK_RELOP)
2625 #if JS_HAS_IN_OPERATOR
2626             /*
2627              * Recognize the 'in' token as an operator only if we're not
2628              * currently in the init expr of a for loop.
2629              */
2630             || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2631 #endif /* JS_HAS_IN_OPERATOR */
2632 #if JS_HAS_INSTANCEOF
2633             || js_MatchToken(cx, ts, TOK_INSTANCEOF)
2634 #endif /* JS_HAS_INSTANCEOF */
2635             )) {
2636 384         tt = CURRENT_TOKEN(ts).type;
2637 384         op = CURRENT_TOKEN(ts).t_op;
2638 384         pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2639     }
2640 #if JS_HAS_IN_OPERATOR
2641     /* Restore previous state of inForInit flag. */
2642 629206     tc->flags |= inForInitFlag;
2643 #endif /* JS_HAS_IN_OPERATOR */
2644
2645 629206     return pn;
2646 }
2647
2648 static JSParseNode *
2649 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2650 629590 {
2651 629590     JSParseNode *pn;
2652 629590     JSOp op;
2653
2654 629590     pn = AddExpr(cx, ts, tc);
2655 629590     while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2656 0         op = CURRENT_TOKEN(ts).t_op;
2657 0         pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2658     }
2659 629590     return pn;
2660 }
2661
2662 static JSParseNode *
2663 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2664 629590 {
2665 629590     JSParseNode *pn;
2666 629590     JSTokenType tt;
2667 629590     JSOp op;
2668
2669 629590     pn = MulExpr(cx, ts, tc);
2670 637340     while (pn &&
2671            (js_MatchToken(cx, ts, TOK_PLUS) ||
2672             js_MatchToken(cx, ts, TOK_MINUS))) {
2673 7750         tt = CURRENT_TOKEN(ts).type;
2674 7750         op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2675 7750         pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2676     }
2677 629590     return pn;
2678 }
2679
2680 static JSParseNode *
2681 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2682 637340 {
2683 637340     JSParseNode *pn;
2684 637340     JSTokenType tt;
2685 637340     JSOp op;
2686
2687 637340     pn = UnaryExpr(cx, ts, tc);
2688 637340     while (pn &&
2689            (js_MatchToken(cx, ts, TOK_STAR) ||
2690             js_MatchToken(cx, ts, TOK_DIVOP))) {
2691 0         tt = CURRENT_TOKEN(ts).type;
2692 0         op = CURRENT_TOKEN(ts).t_op;
2693 0         pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2694     }
2695 637340     return pn;
2696 }
2697
2698 static JSParseNode *
2699 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2700            const char *name)
2701 416 {
2702 416     while (kid->pn_type == TOK_RP)
2703 0         kid = kid->pn_kid;
2704 416     if (kid->pn_type != TOK_NAME &&
2705         kid->pn_type != TOK_DOT &&
2706 #if JS_HAS_LVALUE_RETURN
2707         (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2708 #endif
2709 #if JS_HAS_XML_SUPPORT
2710         (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
2711 #endif
2712         kid->pn_type != TOK_LB) {
2713 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2714                                     JSMSG_BAD_OPERAND, name);
2715 0         return NULL;
2716     }
2717 416     pn->pn_kid = kid;
2718 416     return kid;
2719 }
2720
2721 static const char *incop_name_str[] = {"increment", "decrement"};
2722
2723 static JSBool
2724 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2725             JSParseNode *pn, JSParseNode *kid,
2726             JSTokenType tt, JSBool preorder)
2727 416 {
2728 416     JSOp op;
2729
2730 416     kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2731 416     if (!kid)
2732 0         return JS_FALSE;
2733 416     switch (kid->pn_type) {
2734       case TOK_NAME:
2735 128         op = (tt == TOK_INC)
2736              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2737              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2738 128         if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2739 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
2740 0         break;
2741
2742       case TOK_DOT:
2743 0         op = (tt == TOK_INC)
2744              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2745              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2746 0         break;
2747
2748 #if JS_HAS_LVALUE_RETURN
2749       case TOK_LP:
2750 0         JS_ASSERT(kid->pn_op == JSOP_CALL);
2751 0         kid->pn_op = JSOP_SETCALL;
2752         /* FALL THROUGH */
2753 #endif
2754 #if JS_HAS_XML_SUPPORT
2755       case TOK_UNARYOP:
2756 0         if (kid->pn_op == JSOP_XMLNAME)
2757 0             kid->pn_op = JSOP_SETXMLNAME;
2758         /* FALL THROUGH */
2759 #endif
2760       case TOK_LB:
2761 288         op = (tt == TOK_INC)
2762              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2763              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2764 288         break;
2765
2766       default:
2767 0         JS_ASSERT(0);
2768 0         op = JSOP_NOP;
2769     }
2770 416     pn->pn_op = op;
2771 416     return JS_TRUE;
2772 }
2773
2774 static JSParseNode *
2775 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2776 638273 {
2777 638273     JSTokenType tt;
2778 638273     JSParseNode *pn, *pn2;
2779
2780 638273     CHECK_RECURSION();
2781
2782 638273     ts->flags |= TSF_OPERAND;
2783 638273     tt = js_GetToken(cx, ts);
2784 638273     ts->flags &= ~TSF_OPERAND;
2785
2786 638273     switch (tt) {
2787       case TOK_UNARYOP:
2788       case TOK_PLUS:
2789       case TOK_MINUS:
2790 933         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2791 933         if (!pn)
2792 0             return NULL;
2793 933         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
2794 933         pn->pn_op = CURRENT_TOKEN(ts).t_op;
2795 933         pn2 = UnaryExpr(cx, ts, tc);
2796 933         if (!pn2)
2797 0             return NULL;
2798 933         pn->pn_pos.end = pn2->pn_pos.end;
2799 933         pn->pn_kid = pn2;
2800 933         break;
2801
2802       case TOK_INC:
2803       case TOK_DEC:
2804 96         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2805 96         if (!pn)
2806 0             return NULL;
2807 96         pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2808 96         if (!pn2)
2809 0             return NULL;
2810 96         if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2811 0             return NULL;
2812 96         pn->pn_pos.end = pn2->pn_pos.end;
2813 96         break;
2814
2815       case TOK_DELETE:
2816 0         pn = NewParseNode(cx, ts, PN_UNARY, tc);
2817 0         if (!pn)
2818 0             return NULL;
2819 0         pn2 = UnaryExpr(cx, ts, tc);
2820 0         if (!pn2)
2821 0             return NULL;
2822 0         pn->pn_pos.end = pn2->pn_pos.end;
2823
2824         /*
2825          * Under ECMA3, deleting any unary expression is valid -- it simply
2826          * returns true. Here we strip off any parentheses.
2827          */
2828 0         while (pn2->pn_type == TOK_RP)
2829 0             pn2 = pn2->pn_kid;
2830 0         pn->pn_kid = pn2;
2831 0         break;
2832
2833       case TOK_ERROR:
2834 0         return NULL;
2835
2836       default:
2837 637244         js_UngetToken(ts);
2838 637244         pn = MemberExpr(cx, ts, tc, JS_TRUE);
2839 637244         if (!pn)
2840 0             return NULL;
2841
2842         /* Don't look across a newline boundary for a postfix incop. */
2843 637244         if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2844 637063             tt = js_PeekTokenSameLine(cx, ts);
2845 637063             if (tt == TOK_INC || tt == TOK_DEC) {
2846 320                 (void) js_GetToken(cx, ts);
2847 320                 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
2848 320                 if (!pn2)
2849 0                     return NULL;
2850 320                 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2851 0                     return NULL;
2852 320                 pn2->pn_pos.begin = pn->pn_pos.begin;
2853 320                 pn = pn2;
2854             }
2855         }
2856 638273         break;
2857     }
2858 638273     return pn;
2859 }
2860
2861 static JSBool
2862 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2863              JSParseNode *listNode)
2864 261276 {
2865 261276     JSBool matched;
2866
2867 261276     ts->flags |= TSF_OPERAND;
2868 261276     matched = js_MatchToken(cx, ts, TOK_RP);
2869 261276     ts->flags &= ~TSF_OPERAND;
2870 261276     if (!matched) {
2871 274816         do {
2872 274816             JSParseNode *argNode = AssignExpr(cx, ts, tc);
2873 274816             if (!argNode)
2874 0                 return JS_FALSE;
2875 274816             PN_APPEND(listNode, argNode);
2876 274816         } while (js_MatchToken(cx, ts, TOK_COMMA));
2877
2878 258194         if (js_GetToken(cx, ts) != TOK_RP) {
2879 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2880                                         JSMSG_PAREN_AFTER_ARGS);
2881 0             return JS_FALSE;
2882         }
2883     }
2884 261276     return JS_TRUE;
2885 }
2886
2887 static JSParseNode *
2888 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2889            JSBool allowCallSyntax)
2890 638170 {
2891 638170     JSParseNode *pn, *pn2, *pn3;
2892 638170     JSTokenType tt;
2893
2894 638170     CHECK_RECURSION();
2895
2896     /* Check for new expression first. */
2897 638170     ts->flags |= TSF_OPERAND;
2898 638170     tt = js_PeekToken(cx, ts);
2899 638170     ts->flags &= ~TSF_OPERAND;
2900 638170     if (tt == TOK_NEW) {
2901 830         (void) js_GetToken(cx, ts);
2902
2903 830         pn = NewParseNode(cx, ts, PN_LIST, tc);
2904 830         if (!pn)
2905 0             return NULL;
2906 830         pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2907 830         if (!pn2)
2908 0             return NULL;
2909 830         pn->pn_op = JSOP_NEW;
2910 830         PN_INIT_LIST_1(pn, pn2);
2911 830         pn->pn_pos.begin = pn2->pn_pos.begin;
2912
2913 830         if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2914 0             return NULL;
2915 830         if (pn->pn_count > ARGC_LIMIT) {
2916 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2917                                  JSMSG_TOO_MANY_CON_ARGS);
2918 0             return NULL;
2919         }
2920 830         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2921     } else {
2922 637340         pn = PrimaryExpr(cx, ts, tc);
2923 637340         if (!pn)
2924 0             return NULL;
2925
2926 637340         if (pn->pn_type == TOK_ANYNAME ||
2927             pn->pn_type == TOK_AT ||
2928             pn->pn_type == TOK_DBLCOLON) {
2929 0             pn2 = NewOrRecycledNode(cx, tc);
2930 0             if (!pn2)
2931 0                 return NULL;
2932 0             pn2->pn_type = TOK_UNARYOP;
2933 0             pn2->pn_pos = pn->pn_pos;
2934 0             pn2->pn_op = JSOP_XMLNAME;
2935 0             pn2->pn_arity = PN_UNARY;
2936 0             pn2->pn_kid = pn;
2937 0             pn2->pn_next = NULL;
2938 #if JS_HAS_XML_SUPPORT
2939 0             pn2->pn_ts = ts;
2940 #endif
2941 0             pn = pn2;
2942         }
2943     }
2944
2945 953354     while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2946 953354         if (tt == TOK_DOT) {
2947 33504             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2948 33504             if (!pn2)
2949 0                 return NULL;
2950 #if JS_HAS_XML_SUPPORT
2951 33504             pn3 = PrimaryExpr(cx, ts, tc);
2952 33504             if (!pn3)
2953 0                 return NULL;
2954 33504             tt = pn3->pn_type;
2955 33504             if (tt == TOK_NAME ||
2956                 (tt == TOK_DBLCOLON &&
2957                  pn3->pn_arity == PN_NAME &&
2958                  pn3->pn_expr->pn_type == TOK_FUNCTION)) {
2959 33504                 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
2960 33504                 pn2->pn_expr = pn;
2961 33504                 pn2->pn_atom = pn3->pn_atom;
2962 33504                 RecycleTree(pn3, tc);
2963             } else {
2964 0                 if (TOKEN_TYPE_IS_XML(tt)) {
2965 0                     pn2->pn_type = TOK_LB;
2966 0                     pn2->pn_op = JSOP_GETELEM;
2967 0                 } else if (tt == TOK_RP) {
2968 0                     JSParseNode *group = pn3;
2969
2970                     /* Recycle the useless TOK_RP/JSOP_GROUP node. */
2971 0                     pn3 = group->pn_kid;
2972 0                     group->pn_kid = NULL;
2973 0                     RecycleTree(group, tc);
2974 0                     pn2->pn_type = TOK_FILTER;
2975 0                     pn2->pn_op = JSOP_FILTER;
2976                 } else {
2977 0                     js_ReportCompileErrorNumber(cx, ts,
2978                                                 JSREPORT_TS | JSREPORT_ERROR,
2979                                                 JSMSG_NAME_AFTER_DOT);
2980 0                     return NULL;
2981                 }
2982 0                 pn2->pn_arity = PN_BINARY;
2983 0                 pn2->pn_left = pn;
2984 0                 pn2->pn_right = pn3;
2985             }
2986 #else
2987             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2988             pn2->pn_op = JSOP_GETPROP;
2989             pn2->pn_expr = pn;
2990             pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2991 #endif
2992 33504             pn2->pn_pos.begin = pn->pn_pos.begin;
2993 33504             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2994 #if JS_HAS_XML_SUPPORT
2995 919850         } else if (tt == TOK_DBLDOT) {
2996 0             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
2997 0             if (!pn2)
2998 0                 return NULL;
2999 0             pn3 = PrimaryExpr(cx, ts, tc);
3000 0             if (!pn3)
3001 0                 return NULL;
3002 0             tt = pn3->pn_type;
3003 0             if (tt == TOK_NAME) {
3004 0                 pn3->pn_type = TOK_STRING;
3005 0                 pn3->pn_arity = PN_NULLARY;
3006 0                 pn3->pn_op = JSOP_STRING;
3007 0             } else if (!TOKEN_TYPE_IS_XML(tt)) {
3008 0                 js_ReportCompileErrorNumber(cx, ts,
3009                                             JSREPORT_TS | JSREPORT_ERROR,
3010                                             JSMSG_NAME_AFTER_DOT);
3011 0                 return NULL;
3012             }
3013 0             pn2->pn_op = JSOP_DESCENDANTS;
3014 0             pn2->pn_left = pn;
3015 0             pn2->pn_right = pn3;
3016 0             pn2->pn_pos.begin = pn->pn_pos.begin;
3017 0             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3018 #endif
3019 919850         } else if (tt == TOK_LB) {
3020 21234             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
3021 21234             if (!pn2)
3022 0                 return NULL;
3023 21234             pn3 = Expr(cx, ts, tc);
3024 21234             if (!pn3)
3025 0                 return NULL;
3026
3027 21234             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
3028 21234             pn2->pn_pos.begin = pn->pn_pos.begin;
3029 21234             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3030
3031             /* Optimize o['p'] to o.p by rewriting pn2. */
3032 21234             if (pn3->pn_type == TOK_STRING) {
3033 576                 pn2->pn_type = TOK_DOT;
3034 576                 pn2->pn_op = JSOP_GETPROP;
3035 576                 pn2->pn_arity = PN_NAME;
3036 576                 pn2->pn_expr = pn;
3037 576                 pn2->pn_atom = pn3->pn_atom;
3038             } else {
3039 20658                 pn2->pn_op = JSOP_GETELEM;
3040 20658                 pn2->pn_left = pn;
3041 20658                 pn2->pn_right = pn3;
3042             }
3043 898616         } else if (allowCallSyntax && tt == TOK_LP) {
3044 260446             pn2 = NewParseNode(cx, ts, PN_LIST, tc);
3045 260446             if (!pn2)
3046 0                 return NULL;
3047
3048             /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
3049 260446             pn2->pn_op = JSOP_CALL;
3050 260446             if (pn->pn_op == JSOP_NAME &&
3051                 pn->pn_atom == cx->runtime->atomState.evalAtom) {
3052 0                 pn2->pn_op = JSOP_EVAL;
3053 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3054             }
3055
3056 260446             PN_INIT_LIST_1(pn2, pn);
3057 260446             pn2->pn_pos.begin = pn->pn_pos.begin;
3058
3059 260446             if (!ArgumentList(cx, ts, tc, pn2))
3060 0                 return NULL;
3061 260446             if (pn2->pn_count > ARGC_LIMIT) {
3062 0                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3063                                      JSMSG_TOO_MANY_FUN_ARGS);
3064 0                 return NULL;
3065             }
3066 260446             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3067         } else {
3068 638170             js_UngetToken(ts);
3069 638170             return pn;
3070         }
3071
3072 315184         pn = pn2;
3073     }
3074 0     if (tt == TOK_ERROR)
3075 0         return NULL;
3076 0     return pn;
3077 }
3078
3079 static JSParseNode *
3080 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3081 551 {
3082 551     uintN oldflags;
3083 551     JSParseNode *pn;
3084
3085 #if JS_HAS_IN_OPERATOR
3086     /*
3087      * Always accept the 'in' operator in a parenthesized expression,
3088      * where it's unambiguous, even if we might be parsing the init of a
3089      * for statement.
3090      */
3091 551     oldflags = tc->flags;
3092 551     tc->flags &= ~TCF_IN_FOR_INIT;
3093 #endif
3094 551     pn = Expr(cx, ts, tc);
3095 #if JS_HAS_IN_OPERATOR
3096 551     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3097 #endif
3098 551     return pn;
3099 }
3100
3101 #if JS_HAS_XML_SUPPORT
3102
3103 static JSParseNode *
3104 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3105 0 {
3106 0     JSParseNode *pn;
3107
3108 0     pn = BracketedExpr(cx, ts, tc);
3109 0     if (!pn)
3110 0         return NULL;
3111
3112 0     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
3113 0     return pn;
3114 }
3115
3116 /*
3117  * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
3118  *
3119  *      AttributeIdentifier:
3120  *              @ PropertySelector
3121  *              @ QualifiedIdentifier
3122  *              @ [ Expression ]
3123  *
3124  *      PropertySelector:
3125  *              Identifier
3126  *              *
3127  *
3128  *      QualifiedIdentifier:
3129  *              PropertySelector :: PropertySelector
3130  *              PropertySelector :: [ Expression ]
3131  *
3132  * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
3133  *
3134  *      AttributeIdentifier:
3135  *              @ QualifiedIdentifier
3136  *              @ [ Expression ]
3137  *
3138  *      PropertySelector:
3139  *              Identifier
3140  *              *
3141  *
3142  *      QualifiedIdentifier:
3143  *              PropertySelector :: PropertySelector
3144  *              PropertySelector :: [ Expression ]
3145  *              PropertySelector
3146  *
3147  * Since PrimaryExpression: Identifier in ECMA-262 and we want the semantics
3148  * for that rule to result in a name node, but extend the grammar to include
3149  * PrimaryExpression: QualifiedIdentifier, we factor further:
3150  *
3151  *      QualifiedIdentifier:
3152  *              PropertySelector QualifiedSuffix
3153  *
3154  *      QualifiedSuffix:
3155  *              :: PropertySelector
3156  *              :: [ Expression ]
3157  *              /nothing/
3158  *
3159  * And use this production instead of PrimaryExpression: QualifiedIdentifier:
3160  *
3161  *      PrimaryExpression:
3162  *              Identifier QualifiedSuffix
3163  *
3164  * We hoists the :: match into callers of QualifiedSuffix, in order to tweak
3165  * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
3166  */
3167 static JSParseNode *
3168 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3169 0 {
3170 0     JSParseNode *pn;
3171
3172 0     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3173 0     if (!pn)
3174 0         return NULL;
3175 0     if (pn->pn_type == TOK_STAR) {
3176 0         pn->pn_type = TOK_ANYNAME;
3177 0         pn->pn_op = JSOP_ANYNAME;
3178 0         pn->pn_atom = cx->runtime->atomState.starAtom;
3179     } else {
3180 0         JS_ASSERT(pn->pn_type == TOK_NAME);
3181 0         pn->pn_op = JSOP_QNAMEPART;
3182 0         pn->pn_arity = PN_NAME;
3183 0         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3184 0         pn->pn_expr = NULL;
3185 0         pn->pn_slot = -1;
3186 0         pn->pn_attrs = 0;
3187     }
3188 0     return pn;
3189 }
3190
3191 static JSParseNode *
3192 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
3193                 JSTreeContext *tc)
3194 0 {
3195 0     JSParseNode *pn2, *pn3;
3196 0     JSTokenType tt;
3197
3198 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
3199 0     pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3200 0     if (!pn2)
3201 0         return NULL;
3202
3203     /* Left operand of :: must be evaluated if it is an identifier. */
3204 0     if (pn->pn_op == JSOP_QNAMEPART)
3205 0         pn->pn_op = JSOP_NAME;
3206
3207 0     tt = js_GetToken(cx, ts);
3208 0     if (tt == TOK_STAR || tt == TOK_NAME) {
3209         /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
3210 0         pn2->pn_op = JSOP_QNAMECONST;
3211 0         pn2->pn_atom = (tt == TOK_STAR)
3212                        ? cx->runtime->atomState.starAtom
3213                        : CURRENT_TOKEN(ts).t_atom;
3214 0         pn2->pn_expr = pn;
3215 0         pn2->pn_slot = -1;
3216 0         pn2->pn_attrs = 0;
3217 0         return pn2;
3218     }
3219
3220 0     if (tt != TOK_LB) {
3221 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3222                                     JSMSG_SYNTAX_ERROR);
3223 0         return NULL;
3224     }
3225 0     pn3 = EndBracketedExpr(cx, ts, tc);
3226 0     if (!pn3)
3227 0         return NULL;
3228
3229 0     pn2->pn_op = JSOP_QNAME;
3230 0     pn2->pn_arity = PN_BINARY;
3231 0     pn2->pn_left = pn;
3232 0     pn2->pn_right = pn3;
3233 0     return pn2;
3234 }
3235
3236 static JSParseNode *
3237 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3238 0 {
3239 0     JSParseNode *pn;
3240
3241 0     pn = PropertySelector(cx, ts, tc);
3242 0     if (!pn)
3243 0         return NULL;
3244 0     if (js_MatchToken(cx, ts, TOK_DBLCOLON))
3245 0         pn = QualifiedSuffix(cx, ts, pn, tc);
3246 0     return pn;
3247 }
3248
3249 static JSParseNode *
3250 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3251 0 {
3252 0     JSParseNode *pn, *pn2;
3253 0     JSTokenType tt;
3254
3255 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
3256 0     pn = NewParseNode(cx, ts, PN_UNARY, tc);
3257 0     if (!pn)
3258 0         return NULL;
3259 0     pn->pn_op = JSOP_TOATTRNAME;
3260 0     tt = js_GetToken(cx, ts);
3261 0     if (tt == TOK_STAR || tt == TOK_NAME) {
3262 0         pn2 = QualifiedIdentifier(cx, ts, tc);
3263 0     } else if (tt == TOK_LB) {
3264 0         pn2 = EndBracketedExpr(cx, ts, tc);
3265     } else {
3266 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3267                                     JSMSG_SYNTAX_ERROR);
3268 0         return NULL;
3269     }
3270 0     if (!pn2)
3271 0         return NULL;
3272 0     pn->pn_kid = pn2;
3273 0     return pn;
3274 }
3275
3276 /*
3277  * Make a TOK_LC unary node whose pn_kid is an expression.
3278  */
3279 static JSParseNode *
3280 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
3281 0 {
3282 0     JSParseNode *pn, *pn2;
3283 0     uintN oldflags;
3284
3285 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
3286 0     pn = NewParseNode(cx, ts, PN_UNARY, tc);
3287 0     if (!pn)
3288 0         return NULL;
3289
3290     /*
3291      * Turn off XML tag mode, but don't restore it after parsing this braced
3292      * expression.  Instead, simply restore ts's old flags.  This is required
3293      * because XMLExpr is called both from within a tag, and from within text
3294      * contained in an element, but outside of any start, end, or point tag.
3295      */
3296 0     oldflags = ts->flags;
3297 0     ts->flags = oldflags & ~TSF_XMLTAGMODE;
3298 0     pn2 = Expr(cx, ts, tc);
3299 0     if (!pn2)
3300 0         return NULL;
3301
3302 0     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
3303 0     ts->flags = oldflags;
3304 0     pn->pn_kid = pn2;
3305 0     pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
3306 0     return pn;
3307 }
3308
3309 /*
3310  * Make a terminal node for oneof TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
3311  * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI.  When converting
3312  * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
3313  * child of a container tag.
3314  */
3315 static JSParseNode *
3316 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3317 0 {
3318 0     JSParseNode *pn;
3319 0     JSToken *tp;
3320
3321 0     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3322 0     if (!pn)
3323 0         return NULL;
3324 0     tp = &CURRENT_TOKEN(ts);
3325 0     pn->pn_op = tp->t_op;
3326 0     pn->pn_atom = tp->t_atom;
3327 0     if (tp->type == TOK_XMLPI)
3328 0         pn->pn_atom2 = tp->t_atom2;
3329 0     return pn;
3330 }
3331
3332 /*
3333  * Parse the productions:
3334  *
3335  *      XMLNameExpr:
3336  *              XMLName XMLNameExpr?
3337  *              { Expr } XMLNameExpr?
3338  *
3339  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
3340  * a list of names and/or expressions, a single expression, or a single name.
3341  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
3342  * will be TOK_LC.
3343  */
3344 static JSParseNode *
3345 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3346 0 {
3347 0     JSParseNode *pn, *pn2, *list;
3348 0     JSTokenType tt;
3349
3350 0     pn = list = NULL;
3351 0     do {
3352 0         tt = CURRENT_TOKEN(ts).type;
3353 0         if (tt == TOK_LC) {
3354 0             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3355 0             if (!pn2)
3356 0                 return NULL;
3357         } else {
3358 0             JS_ASSERT(tt == TOK_XMLNAME);
3359 0             pn2 = XMLAtomNode(cx, ts, tc);
3360 0             if (!pn2)
3361 0                 return NULL;
3362         }
3363
3364 0         if (!pn) {
3365 0             pn = pn2;
3366         } else {
3367 0             if (!list) {
3368 0                 list = NewParseNode(cx, ts, PN_LIST, tc);
3369 0                 if (!list)
3370 0                     return NULL;
3371 0                 list->pn_type = TOK_XMLNAME;
3372 0                 list->pn_pos.begin = pn->pn_pos.begin;
3373 0                 PN_INIT_LIST_1(list, pn);
3374 0                 list->pn_extra = PNX_CANTFOLD;
3375 0                 pn = list;
3376             }
3377 0             pn->pn_pos.end = pn2->pn_pos.end;
3378 0             PN_APPEND(pn, pn2);
3379         }
3380 0     } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
3381
3382 0     js_UngetToken(ts);
3383 0     return pn;
3384 }
3385
3386 /*
3387  * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
3388  * at compile time into a JSXML tree.
3389  */
3390 #define XML_FOLDABLE(pn)        ((pn)->pn_arity == PN_LIST                    \
3391                                  ? ((pn)->pn_extra & PNX_CANTFOLD) == 0       \
3392                                  : (pn)->pn_type != TOK_LC)
3393
3394 /*
3395  * Parse the productions:
3396  *
3397  *      XMLTagContent:
3398  *              XMLNameExpr
3399  *              XMLTagContent S XMLNameExpr S? = S? XMLAttr
3400  *              XMLTagContent S XMLNameExpr S? = S? { Expr }
3401  *
3402  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
3403  * produces a list of name and attribute values and/or braced expressions, a
3404  * single expression, or a single name.
3405  *
3406  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
3407  * XMLTagContent: XMLNameExpr.  If pn_type is not TOK_XMLNAME but pn_arity is
3408  * PN_LIST, pn_type will be tagtype.  If PN_UNARY, pn_type will be TOK_LC and
3409  * we parsed exactly one expression.
3410  */
3411 static JSParseNode *
3412 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3413               JSTokenType tagtype, JSAtom **namep)
3414 0 {
3415 0     JSParseNode *pn, *pn2, *list;
3416 0     JSTokenType tt;
3417
3418 0     pn = XMLNameExpr(cx, ts, tc);
3419 0     if (!pn)
3420 0         return NULL;
3421 0     *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
3422 0     list = NULL;
3423
3424 0     while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
3425 0         tt = js_GetToken(cx, ts);
3426 0         if (tt != TOK_XMLNAME && tt != TOK_LC) {
3427 0             js_UngetToken(ts);
3428 0             break;
3429         }
3430
3431 0         pn2 = XMLNameExpr(cx, ts, tc);
3432 0         if (!pn2)
3433 0             return NULL;
3434 0         if (!list) {
3435 0             list = NewParseNode(cx, ts, PN_LIST, tc);
3436 0             if (!list)
3437 0                 return NULL;
3438 0             list->pn_type = tagtype;
3439 0             list->pn_pos.begin = pn->pn_pos.begin;
3440 0             PN_INIT_LIST_1(list, pn);
3441 0             pn = list;
3442         }
3443 0         PN_APPEND(pn, pn2);
3444 0         if (!XML_FOLDABLE(pn2))
3445 0             pn->pn_extra |= PNX_CANTFOLD;
3446
3447 0         js_MatchToken(cx, ts, TOK_XMLSPACE);
3448 0         MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
3449 0         js_MatchToken(cx, ts, TOK_XMLSPACE);
3450
3451 0         tt = js_GetToken(cx, ts);
3452 0         if (tt == TOK_XMLATTR) {
3453 0             pn2 = XMLAtomNode(cx, ts, tc);
3454 0         } else if (tt == TOK_LC) {
3455 0             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3456 0             pn->pn_extra |= PNX_CANTFOLD;
3457         } else {
3458 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3459                                         JSMSG_BAD_XML_ATTR_VALUE);
3460 0             return NULL;
3461         }
3462 0         if (!pn2)
3463 0             return NULL;
3464 0         pn->pn_pos.end = pn2->pn_pos.end;
3465 0         PN_APPEND(pn, pn2);
3466     }
3467
3468 0     return pn;
3469 }
3470
3471 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result)                                \
3472     JS_BEGIN_MACRO                                                            \
3473         if ((tt) <= TOK_EOF) {                                                \
3474             if ((tt) == TOK_EOF) {                                            \
3475                 js_ReportCompileErrorNumber(cx, ts,                           \
3476                                             JSREPORT_TS | JSREPORT_ERROR,     \
3477                                             JSMSG_END_OF_XML_SOURCE);         \
3478             }                                                                 \
3479             return result;                                                    \
3480         }                                                                     \
3481     JS_END_MACRO
3482
3483 static JSParseNode *
3484 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3485                  JSBool allowList);
3486
3487 /*
3488  * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
3489  * that opens the end tag for the container.
3490  */
3491 static JSBool
3492 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
3493                   JSTreeContext *tc)
3494 0 {
3495 0     JSTokenType tt;
3496 0     JSParseNode *pn2;
3497 0     JSAtom *textAtom;
3498
3499 0     ts->flags &= ~TSF_XMLTAGMODE;
3500 0     for (;;) {
3501 0         ts->flags |= TSF_XMLTEXTMODE;
3502 0         tt = js_GetToken(cx, ts);
3503 0         ts->flags &= ~TSF_XMLTEXTMODE;
3504 0         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3505
3506 0         JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
3507 0         textAtom = CURRENT_TOKEN(ts).t_atom;
3508 0         if (textAtom) {
3509             /* Non-zero-length XML text scanned. */
3510 0             pn2 = XMLAtomNode(cx, ts, tc);
3511 0             if (!pn2)
3512 0                 return JS_FALSE;
3513 0             pn->pn_pos.end = pn2->pn_pos.end;
3514 0             PN_APPEND(pn, pn2);
3515         }
3516
3517 0         ts->flags |= TSF_OPERAND;
3518 0         tt = js_GetToken(cx, ts);
3519 0         ts->flags &= ~TSF_OPERAND;
3520 0         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3521 0         if (tt == TOK_XMLETAGO)
3522 0             break;
3523
3524 0         if (tt == TOK_LC) {
3525 0             pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
3526 0             pn->pn_extra |= PNX_CANTFOLD;
3527 0         } else if (tt == TOK_XMLSTAGO) {
3528 0             pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
3529 0             if (pn2) {
3530 0                 pn2->pn_extra &= ~PNX_XMLROOT;
3531 0                 pn->pn_extra |= pn2->pn_extra;
3532             }
3533         } else {
3534 0             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
3535                       tt == TOK_XMLPI);
3536 0             pn2 = XMLAtomNode(cx, ts, tc);
3537         }
3538 0         if (!pn2)
3539 0             return JS_FALSE;
3540 0         pn->pn_pos.end = pn2->pn_pos.end;
3541 0         PN_APPEND(pn, pn2);
3542     }
3543
3544 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
3545 0     ts->flags |= TSF_XMLTAGMODE;
3546 0     return JS_TRUE;
3547 }
3548
3549 /*
3550  * Return a PN_LIST node containing an XML or XMLList Initialiser.
3551  */
3552 static JSParseNode *
3553 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3554                  JSBool allowList)
3555 0 {
3556 0     JSParseNode *pn, *pn2, *list;
3557 0     JSBool hadSpace;
3558 0     JSTokenType tt;
3559 0     JSAtom *startAtom, *endAtom;
3560
3561 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
3562 0     pn = NewParseNode(cx, ts, PN_LIST, tc);
3563 0     if (!pn)
3564 0         return NULL;
3565
3566 0     ts->flags |= TSF_XMLTAGMODE;
3567 0     hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
3568 0     tt = js_GetToken(cx, ts);
3569 0     if (tt == TOK_ERROR)
3570 0         return NULL;
3571
3572 0     if (tt == TOK_XMLNAME || tt == TOK_LC) {
3573         /*
3574          * XMLElement.  Append the tag and its contents, if any, to pn.
3575          */
3576 0         pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
3577 0         if (!pn2)
3578 0             return NULL;
3579 0         js_MatchToken(cx, ts, TOK_XMLSPACE);
3580
3581 0         tt = js_GetToken(cx, ts);
3582 0         if (tt == TOK_XMLPTAGC) {
3583             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
3584 0             if (pn2->pn_type == TOK_XMLSTAGO) {
3585 0                 PN_INIT_LIST(pn);
3586 0                 RecycleTree(pn, tc);
3587 0                 pn = pn2;
3588             } else {
3589 0                 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
3590                           pn2->pn_type == TOK_LC);
3591 0                 PN_INIT_LIST_1(pn, pn2);
3592 0                 if (!XML_FOLDABLE(pn2))
3593 0                     pn->pn_extra |= PNX_CANTFOLD;
3594             }
3595 0             pn->pn_type = TOK_XMLPTAGC;
3596 0             pn->pn_extra |= PNX_XMLROOT;
3597         } else {
3598             /* We had better have a tag-close (>) at this point. */
3599 0             if (tt != TOK_XMLTAGC) {
3600 0                 js_ReportCompileErrorNumber(cx, ts,
3601                                             JSREPORT_TS | JSREPORT_ERROR,
3602                                             JSMSG_BAD_XML_TAG_SYNTAX);
3603 0                 return NULL;
3604             }
3605 0             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3606
3607             /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
3608 0             if (pn2->pn_type != TOK_XMLSTAGO) {
3609 0                 PN_INIT_LIST_1(pn, pn2);
3610 0                 if (!XML_FOLDABLE(pn2))
3611 0                     pn->pn_extra |= PNX_CANTFOLD;
3612 0                 pn2 = pn;
3613 0                 pn = NewParseNode(cx, ts, PN_LIST, tc);
3614 0                 if (!pn)
3615 0                     return NULL;
3616             }
3617
3618             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
3619 0             pn->pn_type = TOK_XMLELEM;
3620 0             PN_INIT_LIST_1(pn, pn2);
3621 0             if (!XML_FOLDABLE(pn2))
3622 0                 pn->pn_extra |= PNX_CANTFOLD;
3623 0             pn->pn_extra |= PNX_XMLROOT;
3624
3625             /* Get element contents and delimiting end-tag-open sequence. */
3626 0             if (!XMLElementContent(cx, ts, pn, tc))
3627 0                 return NULL;
3628
3629 0             js_MatchToken(cx, ts, TOK_XMLSPACE);
3630 0             tt = js_GetToken(cx, ts);
3631 0             XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
3632 0             if (tt != TOK_XMLNAME && tt != TOK_LC) {
3633 0                 js_ReportCompileErrorNumber(cx, ts,
3634                                             JSREPORT_TS | JSREPORT_ERROR,
3635                                             JSMSG_BAD_XML_TAG_SYNTAX);
3636 0                 return NULL;
3637             }
3638
3639             /* Parse end tag; check mismatch at compile-time if we can. */
3640 0             pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
3641 0             if (!pn2)
3642 0                 return NULL;
3643 0             if (pn2->pn_type == TOK_XMLETAGO) {
3644                 /* Oops, end tag has attributes! */
3645 0                 js_ReportCompileErrorNumber(cx, ts,
3646                                             JSREPORT_TS | JSREPORT_ERROR,
3647                                             JSMSG_BAD_XML_TAG_SYNTAX);
3648 0                 return NULL;
3649             }
3650 0             if (endAtom && startAtom && endAtom != startAtom) {
3651                 /* End vs. start tag name mismatch: point to the tag name. */
3652 0                 ++pn2->pn_pos.begin.index;
3653 0                 js_ReportCompileErrorNumber(cx, pn2,
3654                                             JSREPORT_PN | JSREPORT_ERROR,
3655                                             JSMSG_XML_TAG_NAME_MISMATCH);
3656 0                 return NULL;
3657             }
3658
3659             /* Make a TOK_XMLETAGO list with pn2 as its single child. */
3660 0             JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
3661 0             list = NewParseNode(cx, ts, PN_LIST, tc);
3662 0             if (!list)
3663 0                 return NULL;
3664 0             list->pn_type = TOK_XMLETAGO;
3665 0             PN_INIT_LIST_1(list, pn2);
3666 0             PN_APPEND(pn, list);
3667 0             if (!XML_FOLDABLE(pn2)) {
3668 0                 list->pn_extra |= PNX_CANTFOLD;
3669 0                 pn->pn_extra |= PNX_CANTFOLD;
3670             }
3671
3672 0             js_MatchToken(cx, ts, TOK_XMLSPACE);
3673 0             MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
3674         }
3675
3676         /* Set pn_op now that pn has been updated to its final value. */
3677 0         pn->pn_op = JSOP_TOXML;
3678 0     } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
3679         /* XMLList Initialiser. */
3680 0         pn->pn_type = TOK_XMLLIST;
3681 0         pn->pn_op = JSOP_TOXMLLIST;
3682 0         PN_INIT_LIST(pn);
3683 0         pn->pn_extra |= PNX_XMLROOT;
3684 0         if (!XMLElementContent(cx, ts, pn, tc))
3685 0             return NULL;
3686
3687 0         MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
3688     } else {
3689 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3690                                     JSMSG_BAD_XML_NAME_SYNTAX);
3691 0         return NULL;
3692     }
3693
3694 0     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3695 0     ts->flags &= ~TSF_XMLTAGMODE;
3696 0     return pn;
3697 }
3698
3699 static JSParseNode *
3700 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3701                      JSBool allowList)
3702 0 {
3703 0     uint32 oldopts;
3704 0     JSParseNode *pn;
3705
3706     /*
3707      * Force XML support to be enabled so that comments and CDATA literals
3708      * are recognized, instead of <! followed by -- starting an HTML comment
3709      * to end of line (used in script tags to hide content from old browsers
3710      * that don't recognize <script>).
3711      */
3712 0     oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
3713 0     pn = XMLElementOrList(cx, ts, tc, allowList);
3714 0     JS_SetOptions(cx, oldopts);
3715 0     return pn;
3716 }
3717
3718 JS_FRIEND_API(JSParseNode *)
3719 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
3720                        JSBool allowList)
3721 0 {
3722 0     JSStackFrame *fp, frame;
3723 0     JSParseNode *pn;
3724 0     JSTreeContext tc;
3725 0     JSTokenType tt;
3726
3727     /*
3728      * Push a compiler frame if we have no frames, or if the top frame is a
3729      * lightweight function activation, or if its scope chain doesn't match
3730      * the one passed to us.
3731      */
3732 0     fp = cx->fp;
3733 0     if (!fp || !fp->varobj || fp->scopeChain != chain) {
3734 0         memset(&frame, 0, sizeof frame);
3735 0         frame.varobj = frame.scopeChain = chain;
3736 0         if (cx->options & JSOPTION_VAROBJFIX) {
3737 0             while ((chain = JS_GetParent(cx, chain)) != NULL)
3738 0                 frame.varobj = chain;
3739         }
3740 0         frame.down = fp;
3741 0         if (fp) {
3742 0             frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
3743                                        JSFRAME_SCRIPT_OBJECT);
3744         }
3745 0         cx->fp = &frame;
3746     }
3747
3748 0     JS_KEEP_ATOMS(cx->runtime);
3749 0     TREE_CONTEXT_INIT(&tc);
3750
3751     /* Set XML-only mode to turn off special treatment of {expr} in XML. */
3752 0     ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
3753 0     tt = js_GetToken(cx, ts);
3754 0     ts->flags &= ~TSF_OPERAND;
3755
3756 0     if (tt != TOK_XMLSTAGO) {
3757 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3758                                     JSMSG_BAD_XML_MARKUP);
3759 0         pn = NULL;
3760     } else {
3761 0         pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
3762     }
3763
3764 0     ts->flags &= ~TSF_XMLONLYMODE;
3765 0     TREE_CONTEXT_FINISH(&tc);
3766 0     JS_UNKEEP_ATOMS(cx->runtime);
3767 0     cx->fp = fp;
3768 0     return pn;
3769 }
3770
3771 #endif /* JS_HAS_XMLSUPPORT */
3772
3773 static JSParseNode *
3774 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3775 670844 {
3776 670844     JSTokenType tt;
3777 670844     JSParseNode *pn, *pn2, *pn3;
3778 #if JS_HAS_GETTER_SETTER
3779 670844     JSAtom *atom;
3780 670844     JSRuntime *rt;
3781 #endif
3782
3783 #if JS_HAS_SHARP_VARS
3784 670844     JSParseNode *defsharp;
3785 670844     JSBool notsharp;
3786
3787 670844     defsharp = NULL;
3788 670844     notsharp = JS_FALSE;
3789   again:
3790     /*
3791      * Control flows here after #n= is scanned.  If the following primary is
3792      * not valid after such a "sharp variable" definition, the tt switch case
3793      * should set notsharp.
3794      */
3795 #endif
3796
3797 670844     CHECK_RECURSION();
3798
3799 670844     ts->flags |= TSF_OPERAND;
3800 670844     tt = js_GetToken(cx, ts);
3801 670844     ts->flags &= ~TSF_OPERAND;
3802
3803 #if JS_HAS_GETTER_SETTER
3804 670844     if (tt == TOK_NAME) {
3805 506993         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
3806 506993         if (tt == TOK_ERROR)
3807 0             return NULL;
3808     }
3809 #endif
3810
3811 670844     switch (tt) {
3812 #if JS_HAS_LEXICAL_CLOSURE || JS_HAS_XML_SUPPORT
3813       case TOK_FUNCTION:
3814 #if JS_HAS_XML_SUPPORT
3815 0         if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
3816 0             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3817 0             if (!pn2)
3818 0                 return NULL;
3819 0             pn2->pn_type = TOK_FUNCTION;
3820 0             pn = QualifiedSuffix(cx, ts, pn2, tc);
3821 0             if (!pn)
3822 0                 return NULL;
3823 0             break;
3824         }
3825 #endif
3826 0         pn = FunctionExpr(cx, ts, tc);
3827 0         if (!pn)
3828 0             return NULL;
3829 5147         break;
3830 #endif
3831
3832 #if JS_HAS_INITIALIZERS
3833       case TOK_LB:
3834       {
3835 5147         JSBool matched;
3836 5147         jsuint atomIndex;
3837
3838 5147         pn = NewParseNode(cx, ts, PN_LIST, tc);
3839 5147         if (!pn)
3840 0             return NULL;
3841 5147         pn->pn_type = TOK_RB;
3842
3843 #if JS_HAS_SHARP_VARS
3844 5147         if (defsharp) {
3845 0             PN_INIT_LIST_1(pn, defsharp);
3846 0             defsharp = NULL;
3847         } else
3848 #endif
3849 5147             PN_INIT_LIST(pn);
3850
3851 5147         ts->flags |= TSF_OPERAND;
3852 5147         matched = js_MatchToken(cx, ts, TOK_RB);
3853 5147         ts->flags &= ~TSF_OPERAND;
3854 5147         if (!matched) {
3855 22783             for (atomIndex = 0; ; atomIndex++) {
3856 22783                 if (atomIndex == ATOM_INDEX_LIMIT) {
3857 0                     js_ReportCompileErrorNumber(cx, ts,
3858                                                 JSREPORT_TS | JSREPORT_ERROR,
3859                                                 JSMSG_ARRAY_INIT_TOO_BIG);
3860 0                     return NULL;
3861                 }
3862
3863 22783                 ts->flags |= TSF_OPERAND;
3864 22783                 tt = js_PeekToken(cx, ts);
3865 22783                 ts->flags &= ~TSF_OPERAND;
3866 22783                 if (tt == TOK_RB) {
3867 0                     pn->pn_extra |= PNX_ENDCOMMA;
3868 0                     break;
3869                 }
3870
3871 22783                 if (tt == TOK_COMMA) {
3872                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
3873 0                     js_MatchToken(cx, ts, TOK_COMMA);
3874 0                     pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3875                 } else {
3876 22783                     pn2 = AssignExpr(cx, ts, tc);
3877                 }
3878 22783                 if (!pn2)
3879 0                     return NULL;
3880 22783                 PN_APPEND(pn, pn2);
3881
3882 22783                 if (tt != TOK_COMMA) {
3883                     /* If we didn't already match TOK_COMMA in above case. */
3884 22783                     if (!js_MatchToken(cx, ts, TOK_COMMA))
3885 5147                         break;
3886                 }
3887             }
3888
3889 5147             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
3890         }
3891 5147         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3892 5147         return pn;
3893       }
3894
3895       case TOK_LC:
3896 0         pn = NewParseNode(cx, ts, PN_LIST, tc);
3897 0         if (!pn)
3898 0             return NULL;
3899 0         pn->pn_type = TOK_RC;
3900
3901 #if JS_HAS_SHARP_VARS
3902 0         if (defsharp) {
3903 0             PN_INIT_LIST_1(pn, defsharp);
3904 0             defsharp = NULL;
3905         } else
3906 #endif
3907 0             PN_INIT_LIST(pn);
3908
3909 0         if (!js_MatchToken(cx, ts, TOK_RC)) {
3910 0             do {
3911 0                 JSOp op;
3912
3913 0                 tt = js_GetToken(cx, ts);
3914 0                 switch (tt) {
3915                   case TOK_NUMBER:
3916 0                     pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3917 0                     if (pn3)
3918 0                         pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
3919 0                     break;
3920                   case TOK_NAME:
3921 #if JS_HAS_GETTER_SETTER
3922 0                     atom = CURRENT_TOKEN(ts).t_atom;
3923 0                     rt = cx->runtime;
3924 0                     if (atom == rt->atomState.getAtom ||
3925                         atom == rt->atomState.setAtom) {
3926 0                         op = (atom == rt->atomState.getAtom)
3927                              ? JSOP_GETTER
3928                              : JSOP_SETTER;
3929 0                         if (js_MatchToken(cx, ts, TOK_NAME)) {
3930 0                             pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3931 0                             if (!pn3)
3932 0                                 return NULL;
3933 0                             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3934 0                             pn3->pn_expr = NULL;
3935
3936                             /* We have to fake a 'function' token here. */
3937 0                             CURRENT_TOKEN(ts).t_op = JSOP_NOP;
3938 0                             CURRENT_TOKEN(ts).type = TOK_FUNCTION;
3939 0                             pn2 = FunctionExpr(cx, ts, tc);
3940 0                             pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
3941 0                             goto skip;
3942                         }
3943                     }
3944                     /* else fall thru ... */
3945 #endif
3946                   case TOK_STRING:
3947 0                     pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3948 0                     if (pn3)
3949 0                         pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3950 0                     break;
3951                   case TOK_RC:
3952 0                     if (!js_ReportCompileErrorNumber(cx, ts,
3953                                                      JSREPORT_TS |
3954                                                      JSREPORT_WARNING |
3955                                                      JSREPORT_STRICT,
3956                                                      JSMSG_TRAILING_COMMA)) {
3957 0                         return NULL;
3958                     }
3959 0                     goto end_obj_init;
3960                   default:
3961 0                     js_ReportCompileErrorNumber(cx, ts,
3962                                                 JSREPORT_TS | JSREPORT_ERROR,
3963                                                 JSMSG_BAD_PROP_ID);
3964 0                     return NULL;
3965                 }
3966
3967 0                 tt = js_GetToken(cx, ts);
3968 #if JS_HAS_GETTER_SETTER
3969 0                 if (tt == TOK_NAME) {
3970 0                     tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
3971 0                     if (tt == TOK_ERROR)
3972 0                         return NULL;
3973                 }
3974 #endif
3975 0                 if (tt != TOK_COLON) {
3976 0                     js_ReportCompileErrorNumber(cx, ts,
3977                                                 JSREPORT_TS | JSREPORT_ERROR,
3978                                                 JSMSG_COLON_AFTER_ID);
3979 0                     return NULL;
3980                 }
3981 0                 op = CURRENT_TOKEN(ts).t_op;
3982 0                 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
3983                                 tc);
3984 #if JS_HAS_GETTER_SETTER
3985               skip:
3986 #endif
3987 0                 if (!pn2)
3988 0                     return NULL;
3989 0                 PN_APPEND(pn, pn2);
3990 0             } while (js_MatchToken(cx, ts, TOK_COMMA));
3991
3992 0             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
3993         }
3994       end_obj_init:
3995 0         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3996 0         return pn;
3997
3998 #if JS_HAS_SHARP_VARS
3999       case TOK_DEFSHARP:
4000 0         if (defsharp)
4001 0             goto badsharp;
4002 0         defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
4003 0         if (!defsharp)
4004 0             return NULL;
4005 0         defsharp->pn_kid = NULL;
4006 0         defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4007 0         goto again;
4008
4009       case TOK_USESHARP:
4010         /* Check for forward/dangling references at runtime, to allow eval. */
4011 0         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4012 0         if (!pn)
4013 0             return NULL;
4014 0         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4015 0         notsharp = JS_TRUE;
4016 0         break;
4017 #endif /* JS_HAS_SHARP_VARS */
4018 #endif /* JS_HAS_INITIALIZERS */
4019
4020       case TOK_LP:
4021 551         pn = NewParseNode(cx, ts, PN_UNARY, tc);
4022 551         if (!pn)
4023 0             return NULL;
4024 551         pn2 = BracketedExpr(cx, ts, tc);
4025 551         if (!pn2)
4026 0             return NULL;
4027
4028 551         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
4029 551         pn->pn_type = TOK_RP;
4030 551         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4031 551         pn->pn_kid = pn2;
4032 551         break;
4033
4034 #if JS_HAS_XML_SUPPORT
4035       case TOK_STAR:
4036 0         pn = QualifiedIdentifier(cx, ts, tc);
4037 0         if (!pn)
4038 0             return NULL;
4039 0         notsharp = JS_TRUE;
4040 0         break;
4041
4042       case TOK_AT:
4043 0         pn = AttributeIdentifier(cx, ts, tc);
4044 0         if (!pn)
4045 0             return NULL;
4046 0         notsharp = JS_TRUE;
4047 0         break;
4048
4049       case TOK_XMLSTAGO:
4050 0         pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
4051 0         if (!pn)
4052 0             return NULL;
4053 0         notsharp = JS_TRUE;     /* XXXbe could be sharp? */
4054 0         break;
4055 #endif /* JS_HAS_XML_SUPPORT */
4056
4057       case TOK_STRING:
4058 #if JS_HAS_SHARP_VARS
4059 139291         notsharp = JS_TRUE;
4060         /* FALL THROUGH */
4061 #endif
4062
4063 #if JS_HAS_XML_SUPPORT
4064       case TOK_XMLCDATA:
4065       case TOK_XMLCOMMENT:
4066       case TOK_XMLPI:
4067 #endif
4068       case TOK_NAME:
4069       case TOK_OBJECT:
4070 646668         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4071 646668         if (!pn)
4072 0             return NULL;
4073 646668         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
4074 #if JS_HAS_XML_SUPPORT
4075 646668         if (tt == TOK_XMLPI)
4076 0             pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
4077         else
4078 #endif
4079 646668             pn->pn_op = CURRENT_TOKEN(ts).t_op;
4080 646668         if (tt == TOK_NAME) {
4081 506993             pn->pn_arity = PN_NAME;
4082 506993             pn->pn_expr = NULL;
4083 506993             pn->pn_slot = -1;
4084 506993             pn->pn_attrs = 0;
4085
4086 #if JS_HAS_XML_SUPPORT
4087 506993             if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
4088 0                 pn = QualifiedSuffix(cx, ts, pn, tc);
4089 0                 if (!pn)
4090 0                     return NULL;
4091 506993                 break;
4092             }
4093 #endif
4094
4095             /* Unqualified __parent__ and __proto__ uses require activations. */
4096 506993             if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
4097                 pn->pn_atom == cx->runtime->atomState.protoAtom) {
4098 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4099             } else {
4100 506993                 JSAtomListElement *ale;
4101 506993                 JSStackFrame *fp;
4102 506993                 JSStmtInfo *stmt;
4103
4104                 /* Measure optimizable global variable uses. */
4105 506993                 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
4106 506993                 if (ale &&
4107                     !(fp = cx->fp)->fun &&
4108                     fp->scopeChain == fp->varobj &&
4109                     !js_InWithStatement(tc) &&
4110                     !js_InCatchBlock(tc, pn->pn_atom)) {
4111 1168                     tc->globalUses++;
4112 4496                     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
4113 4480                         if (STMT_IS_LOOP(stmt)) {
4114 1152                             tc->loopyGlobalUses++;
4115 1152                             break;
4116                         }
4117                     }
4118                 }
4119             }
4120         }
4121 16316         break;
4122
4123       case TOK_NUMBER:
4124 16316         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4125 16316         if (!pn)
4126 0             return NULL;
4127 16316         pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
4128 #if JS_HAS_SHARP_VARS
4129 16316         notsharp = JS_TRUE;
4130 #endif
4131 16316         break;
4132
4133       case TOK_PRIMARY:
4134 2162         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4135 2162         if (!pn)
4136 0             return NULL;
4137 2162         pn->pn_op = CURRENT_TOKEN(ts).t_op;
4138 #if JS_HAS_SHARP_VARS
4139 2162         notsharp = JS_TRUE;
4140 #endif
4141 2162         break;
4142
4143 #if !JS_HAS_EXPORT_IMPORT
4144       case TOK_EXPORT:
4145       case TOK_IMPORT:
4146 #endif
4147       case TOK_ERROR:
4148         /* The scanner or one of its subroutines reported the error. */
4149 0         return NULL;
4150
4151       default:
4152 0         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4153                                     JSMSG_SYNTAX_ERROR);
4154 0         return NULL;
4155     }
4156
4157 #if JS_HAS_SHARP_VARS
4158 665697     if (defsharp) {
4159 0         if (notsharp) {
4160   badsharp:
4161 0             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4162                                         JSMSG_BAD_SHARP_VAR_DEF);
4163 0             return NULL;
4164         }
4165 0         defsharp->pn_kid = pn;
4166 0         return defsharp;
4167     }
4168 #endif
4169 665697     return pn;
4170 }
4171
4172 static JSBool
4173 ContainsVarStmt(JSParseNode *pn)
4174 453950 {
4175 453950     JSParseNode *pn2;
4176
4177 453950     if (!pn)
4178 10033         return JS_FALSE;
4179 443917     switch (pn->pn_arity) {
4180       case PN_LIST:
4181 110405         if (pn->pn_type == TOK_VAR)
4182 1723             return JS_TRUE;
4183 401730         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4184 295023             if (ContainsVarStmt(pn2))
4185 1975                 return JS_TRUE;
4186         }
4187 9700         break;
4188       case PN_TERNARY:
4189 9700         return ContainsVarStmt(pn->pn_kid1) ||
4190                ContainsVarStmt(pn->pn_kid2) ||
4191                ContainsVarStmt(pn->pn_kid3);
4192       case PN_BINARY:
4193         /*
4194          * Limit recursion if pn is a binary expression, which can't contain a
4195          * var statement.
4196          */
4197 30676         if (pn->pn_op != JSOP_NOP)
4198 20060             return JS_FALSE;
4199 10616         return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
4200       case PN_UNARY:
4201 84465         if (pn->pn_op != JSOP_NOP)
4202 416             return JS_FALSE;
4203 84049         return ContainsVarStmt(pn->pn_kid);
4204       default:;
4205     }
4206 315378     return JS_FALSE;
4207 }
4208
4209 /*
4210  * Fold from one constant type to another.
4211  * XXX handles only strings and numbers for now
4212  */
4213 static JSBool
4214 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
4215 8654 {
4216 8654     if (pn->pn_type != type) {
4217 8475         switch (type) {
4218           case TOK_NUMBER:
4219 6065             if (pn->pn_type == TOK_STRING) {
4220 0                 jsdouble d;
4221 0                 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
4222 0                     return JS_FALSE;
4223 0                 pn->pn_dval = d;
4224 0                 pn->pn_type = TOK_NUMBER;
4225 0                 pn->pn_op = JSOP_NUMBER;
4226             }
4227 0             break;
4228
4229           case TOK_STRING:
4230 2410             if (pn->pn_type == TOK_NUMBER) {
4231 0                 JSString *str = js_NumberToString(cx, pn->pn_dval);
4232 0                 if (!str)
4233 0                     return JS_FALSE;
4234 0                 pn->pn_atom = js_AtomizeString(cx, str, 0);
4235 0                 if (!pn->pn_atom)
4236 0                     return JS_FALSE;
4237 0                 pn->pn_type = TOK_STRING;
4238 0                 pn->pn_op = JSOP_STRING;
4239             }
4240             break;
4241
4242           default:;
4243         }
4244     }
4245 8654     return JS_TRUE;
4246 }
4247
4248 /*
4249  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
4250  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
4251  * a successful call to this function.
4252  */
4253 static JSBool
4254 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
4255                   JSParseNode *pn, JSTreeContext *tc)
4256 0 {
4257 0     jsdouble d, d2;
4258 0     int32 i, j;
4259 0     uint32 u;
4260
4261 0     JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
4262 0     d = pn1->pn_dval;
4263 0     d2 = pn2->pn_dval;
4264 0     switch (op) {
4265       case JSOP_LSH:
4266       case JSOP_RSH:
4267 0         if (!js_DoubleToECMAInt32(cx, d, &i))
4268 0             return JS_FALSE;
4269 0         if (!js_DoubleToECMAInt32(cx, d2, &j))
4270 0             return JS_FALSE;
4271 0         j &= 31;
4272 0         d = (op == JSOP_LSH) ? i << j : i >> j;
4273 0         break;
4274
4275       case JSOP_URSH:
4276 0         if (!js_DoubleToECMAUint32(cx, d, &u))
4277 0             return JS_FALSE;
4278 0         if (!js_DoubleToECMAInt32(cx, d2, &j))
4279 0             return JS_FALSE;
4280 0         j &= 31;
4281 0         d = u >> j;
4282 0         break;
4283
4284       case JSOP_ADD:
4285 0         d += d2;
4286 0         break;
4287
4288       case JSOP_SUB:
4289 0         d -= d2;
4290 0         break;
4291
4292       case JSOP_MUL:
4293 0         d *= d2;
4294 0         break;
4295
4296       case JSOP_DIV:
4297 0         if (d2 == 0) {
4298 #if defined(XP_WIN)
4299             /* XXX MSVC miscompiles such that (NaN == 0) */
4300             if (JSDOUBLE_IS_NaN(d2))
4301                 d = *cx->runtime->jsNaN;
4302             else
4303 #endif
4304 0             if (d == 0 || JSDOUBLE_IS_NaN(d))
4305 0                 d = *cx->runtime->jsNaN;
4306 0             else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
4307 0                 d = *cx->runtime->jsNegativeInfinity;
4308             else
4309 0                 d = *cx->runtime->jsPositiveInfinity;
4310         } else {
4311 0             d /= d2;
4312         }
4313 0         break;
4314
4315       case JSOP_MOD:
4316 0         if (d2 == 0) {
4317 0             d = *cx->runtime->jsNaN;
4318         } else {
4319 #if defined(XP_WIN)
4320           /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
4321           if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
4322 #endif
4323 0             d = fmod(d, d2);
4324         }
4325         break;
4326
4327       default:;
4328     }
4329
4330     /* Take care to allow pn1 or pn2 to alias pn. */
4331 0     if (pn1 != pn)
4332 0         RecycleTree(pn1, tc);
4333 0     if (pn2 != pn)
4334 0         RecycleTree(pn2, tc);
4335 0     pn->pn_type = TOK_NUMBER;
4336 0     pn->pn_op = JSOP_NUMBER;
4337 0     pn->pn_arity = PN_NULLARY;
4338 0     pn->pn_dval = d;
4339 0     return JS_TRUE;
4340 }
4341
4342 #if JS_HAS_XML_SUPPORT
4343
4344 static JSBool
4345 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4346 0 {
4347 0     JSTokenType tt;
4348 0     JSParseNode **pnp, *pn1, *pn2;
4349 0     JSString *accum, *str;
4350 0     uint32 i, j;
4351
4352 0     JS_ASSERT(pn->pn_arity == PN_LIST);
4353 0     tt = pn->pn_type;
4354 0     pnp = &pn->pn_head;
4355 0     pn1 = *pnp;
4356 0     accum = NULL;
4357 0     if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4358 0         if (tt == TOK_XMLETAGO)
4359 0             accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
4360 0         else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
4361 0             accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
4362     }
4363
4364 0     for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
4365         /* The parser already rejected end-tags with attributes. */
4366 0         JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
4367 0         switch (pn2->pn_type) {
4368           case TOK_XMLATTR:
4369 0             if (!accum)
4370 0                 goto cantfold;
4371             /* FALL THROUGH */
4372           case TOK_XMLNAME:
4373           case TOK_XMLSPACE:
4374           case TOK_XMLTEXT:
4375           case TOK_STRING:
4376 0             if (pn2->pn_arity == PN_LIST)
4377 0                 goto cantfold;
4378 0             str = ATOM_TO_STRING(pn2->pn_atom);
4379 0             break;
4380
4381           case TOK_XMLCDATA:
4382 0             str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
4383 0             if (!str)
4384 0                 return JS_FALSE;
4385 0             break;
4386
4387           case TOK_XMLCOMMENT:
4388 0             str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
4389 0             if (!str)
4390 0                 return JS_FALSE;
4391 0             break;
4392
4393           case TOK_XMLPI:
4394 0             str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
4395                                          ATOM_TO_STRING(pn2->pn_atom2));
4396 0             if (!str)
4397 0                 return JS_FALSE;
4398 0             break;
4399
4400           cantfold:
4401           default:
4402 0             JS_ASSERT(*pnp == pn1);
4403 0             if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
4404                 (i & 1) ^ (j & 1)) {
4405 #ifdef DEBUG_brendanXXX
4406                 printf("1: %d, %d => %s\n",
4407                        i, j, accum ? JS_GetStringBytes(accum) : "NULL");
4408 #endif
4409 0             } else if (accum && pn1 != pn2) {
4410 0                 while (pn1->pn_next != pn2) {
4411 0                     pn1 = RecycleTree(pn1, tc);
4412 0                     --pn->pn_count;
4413                 }
4414 0                 pn1->pn_type = TOK_XMLTEXT;
4415 0                 pn1->pn_op = JSOP_STRING;
4416 0                 pn1->pn_arity = PN_NULLARY;
4417 0                 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4418 0                 if (!pn1->pn_atom)
4419 0                     return JS_FALSE;
4420 0                 JS_ASSERT(pnp != &pn1->pn_next);
4421 0                 *pnp = pn1;
4422             }
4423 0             pnp = &pn2->pn_next;
4424 0             pn1 = *pnp;
4425 0             accum = NULL;
4426 0             continue;
4427         }
4428
4429 0         if (accum) {
4430 0             str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
4431                   ? js_AddAttributePart(cx, i & 1, accum, str)
4432                   : js_ConcatStrings(cx, accum, str);
4433 0             if (!str)
4434 0                 return JS_FALSE;
4435 #ifdef DEBUG_brendanXXX
4436             printf("2: %d, %d => %s (%u)\n",
4437                    i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str));
4438 #endif
4439 0             ++j;
4440         }
4441 0         accum = str;
4442     }
4443
4444 0     if (accum) {
4445 0         str = NULL;
4446 0         if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4447 0             if (tt == TOK_XMLPTAGC)
4448 0                 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
4449 0             else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
4450 0                 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
4451         }
4452 0         if (str) {
4453 0             accum = js_ConcatStrings(cx, accum, str);
4454 0             if (!accum)
4455 0                 return JS_FALSE;
4456         }
4457
4458 0         JS_ASSERT(*pnp == pn1);
4459 0         while (pn1->pn_next) {
4460 0             pn1 = RecycleTree(pn1, tc);
4461 0             --pn->pn_count;
4462         }
4463 0         pn1->pn_type = TOK_XMLTEXT;
4464 0         pn1->pn_op = JSOP_STRING;
4465 0         pn1->pn_arity = PN_NULLARY;
4466 0         pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4467 0         if (!pn1->pn_atom)
4468 0             return JS_FALSE;
4469 0         JS_ASSERT(pnp != &pn1->pn_next);
4470 0         *pnp = pn1;
4471     }
4472
4473 0     if (pn1 && pn->pn_count == 1) {
4474         /*
4475          * Only one node under pn, and it has been folded: move pn1 onto pn
4476          * unless pn is an XML root (in which case we need it to tell the code
4477          * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op).  If pn is an
4478          * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
4479          * extra "<" and "/>" bracketing at runtime.
4480          */
4481 0         if (!(pn->pn_extra & PNX_XMLROOT)) {
4482 0             PN_MOVE_NODE(pn, pn1);
4483 0         } else if (tt == TOK_XMLPTAGC) {
4484 0             pn->pn_type = TOK_XMLELEM;
4485 0             pn->pn_op = JSOP_TOXML;
4486         }
4487     }
4488 0     return JS_TRUE;
4489 }
4490
4491 #endif /* JS_HAS_XML_SUPPORT */
4492
4493 JSBool
4494 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4495 1279442 {
4496 1279442     JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
4497 1279442     int stackDummy;
4498
4499 1279442     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4500 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4501 0         return JS_FALSE;
4502     }
4503
4504 1279442     switch (pn->pn_arity) {
4505       case PN_FUNC:
4506       {
4507 3552         uint16 oldflags = tc->flags;
4508
4509 3552         tc->flags = (uint16) pn->pn_flags;
4510 3552         if (!js_FoldConstants(cx, pn->pn_body, tc))
4511 0             return JS_FALSE;
4512 3552         tc->flags = oldflags;
4513 3552         break;
4514       }
4515
4516       case PN_LIST:
4517 #if 0 /* JS_HAS_XML_SUPPORT */
4518         switch (pn->pn_type) {
4519           case TOK_XMLELEM:
4520           case TOK_XMLLIST:
4521           case TOK_XMLPTAGC:
4522             /*
4523              * Try to fold this XML parse tree once, from the top down, into
4524              * a JSXML tree with just one object wrapping the tree root.
4525              *
4526              * Certain subtrees could be folded similarly, but we'd have to
4527              * ensure that none used namespace prefixes declared elsewhere in
4528              * its super-tree, and we would have to convert each XML object
4529              * created at runtime for such sub-trees back into a string, and
4530              * concatenate and re-parse anyway.
4531              */
4532             if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT &&
4533                 !(tc->flags & TCF_HAS_DEFXMLNS)) {
4534                 JSObject *obj;
4535                 JSAtom *atom;
4536
4537                 obj = js_ParseNodeToXMLObject(cx, pn);
4538                 if (!obj)
4539                     return JS_FALSE;
4540                 atom = js_AtomizeObject(cx, obj, 0);
4541                 if (!atom)
4542                     return JS_FALSE;
4543                 pn->pn_op = JSOP_XMLOBJECT;
4544                 pn->pn_arity = PN_NULLARY;
4545                 pn->pn_atom = atom;
4546                 return JS_TRUE;
4547             }
4548
4549             /*
4550              * Can't fold from parse node to XML tree -- try folding strings
4551              * as much as possible, and folding XML sub-trees bottom up to
4552              * minimize string concatenation and ToXML/ToXMLList operations
4553              * at runtime.
4554              */
4555             break;
4556
4557           default:;
4558         }
4559 #endif
4560
4561         /* Save the list head in pn1 for later use. */
4562 965644         for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4563 666907             if (!js_FoldConstants(cx, pn2, tc))
4564 0                 return JS_FALSE;
4565         }
4566 13493         break;
4567
4568       case PN_TERNARY:
4569         /* Any kid may be null (e.g. for (;;)). */
4570 13493         pn1 = pn->pn_kid1;
4571 13493         pn2 = pn->pn_kid2;
4572 13493         pn3 = pn->pn_kid3;
4573 13493         if (pn1 && !js_FoldConstants(cx, pn1, tc))
4574 0             return JS_FALSE;
4575 13493         if (pn2 && !js_FoldConstants(cx, pn2, tc))
4576 0             return JS_FALSE;
4577 13493         if (pn3 && !js_FoldConstants(cx, pn3, tc))
4578 0             return JS_FALSE;
4579 57008         break;
4580
4581       case PN_BINARY:
4582         /* First kid may be null (for default case in switch). */
4583 57008         pn1 = pn->pn_left;
4584 57008         pn2 = pn->pn_right;
4585 57008         if (pn1 && !js_FoldConstants(cx, pn1, tc))
4586 0             return JS_FALSE;
4587 57008         if (!js_FoldConstants(cx, pn2, tc))
4588 0             return JS_FALSE;
4589 262312         break;
4590
4591       case PN_UNARY:
4592         /* Our kid may be null (e.g. return; vs. return e;). */
4593 262312         pn1 = pn->pn_kid;
4594 262312         if (pn1 && !js_FoldConstants(cx, pn1, tc))
4595 0             return JS_FALSE;
4596 486267         break;
4597
4598       case PN_NAME:
4599         /*
4600          * Skip pn1 down along a chain of dotted member expressions to avoid
4601          * excessive recursion.  Our only goal here is to fold constants (if
4602          * any) in the primary expression operand to the left of the first
4603          * dot in the chain.
4604          */
4605 486267         pn1 = pn->pn_expr;
4606 517224         while (pn1 && pn1->pn_arity == PN_NAME)
4607 30957             pn1 = pn1->pn_expr;
4608 486267         if (pn1 && !js_FoldConstants(cx, pn1, tc))
4609 0             return JS_FALSE;
4610 1279442         break;
4611
4612       case PN_NULLARY:
4613 1279442         break;
4614     }
4615
4616 1279442     switch (pn->pn_type) {
4617       case TOK_IF:
4618 13365         if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
4619 11642             break;
4620         /* FALL THROUGH */
4621
4622       case TOK_HOOK:
4623         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
4624 11642         switch (pn1->pn_type) {
4625           case TOK_NUMBER:
4626 0             if (pn1->pn_dval == 0)
4627 0                 pn2 = pn3;
4628 0             break;
4629           case TOK_STRING:
4630 0             if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
4631 0                 pn2 = pn3;
4632 0             break;
4633           case TOK_PRIMARY:
4634 0             if (pn1->pn_op == JSOP_TRUE)
4635 0                 break;
4636 0             if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
4637 0                 pn2 = pn3;
4638 0                 break;
4639             }
4640             /* FALL THROUGH */
4641           default:
4642             /* Early return to dodge common code that copies pn2 to pn. */
4643 11642             return JS_TRUE;
4644         }
4645
4646 0         if (pn2) {
4647             /* pn2 is the then- or else-statement subtree to compile. */
4648 0             PN_MOVE_NODE(pn, pn2);
4649         } else {
4650             /* False condition and no else: make pn an empty statement. */
4651 0             pn->pn_type = TOK_SEMI;
4652 0             pn->pn_arity = PN_UNARY;
4653 0             pn->pn_kid = NULL;
4654         }
4655 0         RecycleTree(pn2, tc);
4656 0         if (pn3 && pn3 != pn2)
4657 0             RecycleTree(pn3, tc);
4658 0         break;
4659
4660       case TOK_PLUS:
4661 6430         if (pn->pn_arity == PN_LIST) {
4662 1013             size_t length, length2;
4663 1013             jschar *chars;
4664 1013             JSString *str, *str2;
4665
4666             /*
4667              * Any string literal term with all others number or string means
4668              * this is a concatenation.  If any term is not a string or number
4669              * literal, we can't fold.
4670              */
4671 1013             JS_ASSERT(pn->pn_count > 2);
4672 1013             if (pn->pn_extra & PNX_CANTFOLD)
4673 1013                 return JS_TRUE;
4674 0             if (pn->pn_extra != PNX_STRCAT)
4675 0                 goto do_binary_op;
4676
4677             /* Ok, we're concatenating: convert non-string constant operands. */
4678 0             length = 0;
4679 0             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4680 0                 if (!FoldType(cx, pn2, TOK_STRING))
4681 0                     return JS_FALSE;
4682                 /* XXX fold only if all operands convert to string */
4683 0                 if (pn2->pn_type != TOK_STRING)
4684 0                     return JS_TRUE;
4685 0                 length += ATOM_TO_STRING(pn2->pn_atom)->length;
4686             }
4687
4688             /* Allocate a new buffer and string descriptor for the result. */
4689 0             chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
4690 0             if (!chars)
4691 0                 return JS_FALSE;
4692 0             str = js_NewString(cx, chars, length, 0);
4693 0             if (!str) {
4694 0                 JS_free(cx, chars);
4695 0                 return JS_FALSE;
4696             }
4697
4698             /* Fill the buffer, advancing chars and recycling kids as we go. */
4699 0             for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
4700 0                 str2 = ATOM_TO_STRING(pn2->pn_atom);
4701 0                 length2 = str2->length;
4702 0                 js_strncpy(chars, str2->chars, length2);
4703 0                 chars += length2;
4704             }
4705 0             *chars = 0;
4706
4707             /* Atomize the result string and mutate pn to refer to it. */
4708 0             pn->pn_atom = js_AtomizeString(cx, str, 0);
4709 0             if (!pn->pn_atom)
4710 0                 return JS_FALSE;
4711 0             pn->pn_type = TOK_STRING;
4712 0             pn->pn_op = JSOP_STRING;
4713 0             pn->pn_arity = PN_NULLARY;
4714 0             break;
4715         }
4716
4717         /* Handle a binary string concatenation. */
4718 5417         JS_ASSERT(pn->pn_arity == PN_BINARY);
4719 5417         if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
4720 2410             JSString *left, *right, *str;
4721
4722 2410             if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
4723                           TOK_STRING)) {
4724 0                 return JS_FALSE;
4725             }
4726 2410             if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
4727 2410                 return JS_TRUE;
4728 0             left = ATOM_TO_STRING(pn1->pn_atom);
4729 0             right = ATOM_TO_STRING(pn2->pn_atom);
4730 0             str = js_ConcatStrings(cx, left, right);
4731 0             if (!str)
4732 0                 return JS_FALSE;
4733 0             pn->pn_atom = js_AtomizeString(cx, str, 0);
4734 0             if (!pn->pn_atom)
4735 0                 return JS_FALSE;
4736 0             pn->pn_type = TOK_STRING;
4737 0             pn->pn_op = JSOP_STRING;
4738 0             pn->pn_arity = PN_NULLARY;
4739 0             RecycleTree(pn1, tc);
4740 0             RecycleTree(pn2, tc);
4741 0             break;
4742         }
4743
4744         /* Can't concatenate string literals, let's try numbers. */
4745 0         goto do_binary_op;
4746
4747       case TOK_STAR:
4748         /* The * in 'import *;' parses as a nullary star node. */
4749 0         if (pn->pn_arity == PN_NULLARY)
4750 0             break;
4751         /* FALL THROUGH */
4752
4753       case TOK_SHOP:
4754       case TOK_MINUS:
4755       case TOK_DIVOP:
4756       do_binary_op:
4757 3122         if (pn->pn_arity == PN_LIST) {
4758 0             JS_ASSERT(pn->pn_count > 2);
4759 0             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4760 0                 if (!FoldType(cx, pn2, TOK_NUMBER))
4761 0                     return JS_FALSE;
4762                 /* XXX fold only if all operands convert to number */
4763 0                 if (pn2->pn_type != TOK_NUMBER)
4764 0                     break;
4765             }
4766 0             if (!pn2) {
4767 0                 JSOp op = pn->pn_op;
4768
4769 0                 pn2 = pn1->pn_next;
4770 0                 pn3 = pn2->pn_next;
4771 0                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
4772 0                     return JS_FALSE;
4773 0                 while ((pn2 = pn3) != NULL) {
4774 0                     pn3 = pn2->pn_next;
4775 0                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
4776 0                         return JS_FALSE;
4777                 }
4778             }
4779         } else {
4780 3122             JS_ASSERT(pn->pn_arity == PN_BINARY);
4781 3122             if (!FoldType(cx, pn1, TOK_NUMBER) ||
4782                 !FoldType(cx, pn2, TOK_NUMBER)) {
4783 0                 return JS_FALSE;
4784             }
4785 3122             if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
4786 0                 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
4787 0                     return JS_FALSE;
4788             }
4789         }
4790 933         break;
4791
4792       case TOK_UNARYOP:
4793 933         if (pn1->pn_type == TOK_NUMBER) {
4794 480             jsdouble d;
4795 480             int32 i;
4796
4797             /* Operate on one numeric constant. */
4798 480             d = pn1->pn_dval;
4799 480             switch (pn->pn_op) {
4800               case JSOP_BITNOT:
4801 0                 if (!js_DoubleToECMAInt32(cx, d, &i))
4802 0                     return JS_FALSE;
4803 0                 d = ~i;
4804 0                 break;
4805
4806               case JSOP_NEG:
4807 #ifdef HPUX
4808                 /*
4809                  * Negation of a zero doesn't produce a negative
4810                  * zero on HPUX. Perform the operation by bit
4811                  * twiddling.
4812                  */
4813                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
4814 #else
4815 480                 d = -d;
4816 #endif
4817 480                 break;
4818
4819               case JSOP_POS:
4820 0                 break;
4821
4822               case JSOP_NOT:
4823 0                 pn->pn_type = TOK_PRIMARY;
4824 0                 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
4825 0                 pn->pn_arity = PN_NULLARY;
4826                 /* FALL THROUGH */
4827
4828               default:
4829                 /* Return early to dodge the common TOK_NUMBER code. */
4830 0                 return JS_TRUE;
4831             }
4832 480             pn->pn_type = TOK_NUMBER;
4833 480             pn->pn_op = JSOP_NUMBER;
4834 480             pn->pn_arity = PN_NULLARY;
4835 480             pn->pn_dval = d;
4836 480             RecycleTree(pn1, tc);
4837         }
4838 480         break;
4839
4840 #if JS_HAS_XML_SUPPORT
4841       case TOK_XMLELEM:
4842       case TOK_XMLLIST:
4843       case TOK_XMLPTAGC:
4844       case TOK_XMLSTAGO:
4845       case TOK_XMLETAGO:
4846       case TOK_XMLNAME:
4847 0         if (pn->pn_arity == PN_LIST) {
4848 0             JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
4849 0             if (!FoldXMLConstants(cx, pn, tc))
4850 0                 return JS_FALSE;
4851         }
4852 0         break;
4853
4854       case TOK_AT:
4855 0         if (pn1->pn_type == TOK_XMLNAME) {
4856 0             jsval v;
4857 0             JSAtom *atom;
4858
4859 0             v = ATOM_KEY(pn1->pn_atom);
4860 0             if (!js_ToAttributeName(cx, &v))
4861 0                 return JS_FALSE;
4862 0             JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
4863 0             atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
4864 0             if (!atom)
4865 0                 return JS_FALSE;
4866
4867 0             pn->pn_type = TOK_XMLNAME;
4868 0             pn->pn_op = JSOP_OBJECT;
4869 0             pn->pn_arity = PN_NULLARY;
4870 0             pn->pn_atom = atom;
4871 0             RecycleTree(pn1, tc);
4872         }
4873         break;
4874 #endif /* JS_HAS_XML_SUPPORT */
4875
4876       default:;
4877     }
4878
4879 1264377     return JS_TRUE;