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.  The dense JSTokenType enumeration
52  * was designed with error recovery built on 64-bit first and follow bitsets
53  * in mind, however.
54  */
55 #include "jsstddef.h"
56 #include <stdlib.h>
57 #include <string.h>
58 #include <math.h>
59 #include "jstypes.h"
60 #include "jsarena.h" /* Added by JSIFY */
61 #include "jsutil.h" /* Added by JSIFY */
62 #include "jsapi.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsconfig.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jslock.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsopcode.h"
73 #include "jsparse.h"
74 #include "jsscan.h"
75 #include "jsscope.h"
76 #include "jsscript.h"
77 #include "jsstr.h"
78
79 /*
80  * JS parsers, from lowest to highest precedence.
81  *
82  * Each parser takes a context and a token stream, and emits bytecode using
83  * a code generator.
84  */
85
86 typedef JSParseNode *
87 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
88
89 typedef JSParseNode *
90 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
91                JSBool allowCallSyntax);
92
93 static JSParser FunctionStmt;
94 #if JS_HAS_LEXICAL_CLOSURE
95 static JSParser FunctionExpr;
96 #endif
97 static JSParser Statements;
98 static JSParser Statement;
99 static JSParser Variables;
100 static JSParser Expr;
101 static JSParser AssignExpr;
102 static JSParser CondExpr;
103 static JSParser OrExpr;
104 static JSParser AndExpr;
105 static JSParser BitOrExpr;
106 static JSParser BitXorExpr;
107 static JSParser BitAndExpr;
108 static JSParser EqExpr;
109 static JSParser RelExpr;
110 static JSParser ShiftExpr;
111 static JSParser AddExpr;
112 static JSParser MulExpr;
113 static JSParser UnaryExpr;
114 static JSMemberParser MemberExpr;
115 static JSParser PrimaryExpr;
116
117 /*
118  * Insist that the next token be of type tt, or report errno and return null.
119  * NB: this macro uses cx and ts from its lexical environment.
120  */
121 #define MUST_MATCH_TOKEN(tt, errno)                                           \
122     JS_BEGIN_MACRO                                                            \
123         if (js_GetToken(cx, ts) != tt) {                                      \
124             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
125             return NULL;                                                      \
126         }                                                                     \
127     JS_END_MACRO
128
129 #define CHECK_RECURSION()                                                     \
130     JS_BEGIN_MACRO                                                            \
131         int stackDummy;                                                       \
132         if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {                           \
133             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,         \
134                                         JSMSG_OVER_RECURSED);                 \
135             return NULL;                                                      \
136         }                                                                     \
137     JS_END_MACRO
138
139 #ifdef METER_PARSENODES
140 static uint32 parsenodes = 0;
141 static uint32 maxparsenodes = 0;
142 static uint32 recyclednodes = 0;
143 #endif
144
145 static void
146 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
147 0 {
148 0     if (!pn)
149 0         return;
150 0     JS_ASSERT(pn != tc->nodeList);      /* catch back-to-back dup recycles */
151 0     pn->pn_next = tc->nodeList;
152 0     tc->nodeList = pn;
153 #ifdef METER_PARSENODES
154     recyclednodes++;
155 #endif
156 }
157
158 static JSParseNode *
159 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
160 0 {
161 0     JSParseNode *pn;
162
163 0     pn = tc->nodeList;
164 0     if (!pn) {
165 0         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
166 0         if (!pn)
167 0             JS_ReportOutOfMemory(cx);
168     } else {
169 0         tc->nodeList = pn->pn_next;
170
171         /* Recycle immediate descendents only, to save work and working set. */
172 0         switch (pn->pn_arity) {
173           case PN_FUNC:
174 0             RecycleTree(pn->pn_body, tc);
175 0             break;
176           case PN_LIST:
177 0             if (pn->pn_head) {
178                 /* XXX check for dup recycles in the list */
179 0                 *pn->pn_tail = tc->nodeList;
180 0                 tc->nodeList = pn->pn_head;
181 #ifdef METER_PARSENODES
182                 recyclednodes += pn->pn_count;
183 #endif
184             }
185 0             break;
186           case PN_TERNARY:
187 0             RecycleTree(pn->pn_kid1, tc);
188 0             RecycleTree(pn->pn_kid2, tc);
189 0             RecycleTree(pn->pn_kid3, tc);
190 0             break;
191           case PN_BINARY:
192 0             RecycleTree(pn->pn_left, tc);
193 0             RecycleTree(pn->pn_right, tc);
194 0             break;
195           case PN_UNARY:
196 0             RecycleTree(pn->pn_kid, tc);
197 0             break;
198           case PN_NAME:
199 0             RecycleTree(pn->pn_expr, tc);
200             break;
201           case PN_NULLARY:
202 0             break;
203         }
204     }
205 0     return pn;
206 }
207
208 /*
209  * Allocate a JSParseNode from cx's temporary arena.
210  */
211 static JSParseNode *
212 NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,
213              JSTreeContext *tc)
214 0 {
215 0     JSParseNode *pn;
216
217 0     pn = NewOrRecycledNode(cx, tc);
218 0     if (!pn)
219 0         return NULL;
220 0     pn->pn_type = tok->type;
221 0     pn->pn_pos = tok->pos;
222 0     pn->pn_op = JSOP_NOP;
223 0     pn->pn_arity = arity;
224 0     pn->pn_next = NULL;
225 #ifdef METER_PARSENODES
226     parsenodes++;
227     if (parsenodes - recyclednodes > maxparsenodes)
228         maxparsenodes = parsenodes - recyclednodes;
229 #endif
230 0     return pn;
231 }
232
233 static JSParseNode *
234 NewBinary(JSContext *cx, JSTokenType tt,
235           JSOp op, JSParseNode *left, JSParseNode *right,
236           JSTreeContext *tc)
237 0 {
238 0     JSParseNode *pn, *pn1, *pn2;
239
240 0     if (!left || !right)
241 0         return NULL;
242
243     /*
244      * Flatten a left-associative (left-heavy) tree of a given operator into
245      * a list, to reduce js_FoldConstants and js_EmitTree recursion.
246      */
247 0     if (left->pn_type == tt &&
248         left->pn_op == op &&
249         (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
250 0         if (left->pn_arity != PN_LIST) {
251 0             pn1 = left->pn_left, pn2 = left->pn_right;
252 0             left->pn_arity = PN_LIST;
253 0             PN_INIT_LIST_1(left, pn1);
254 0             PN_APPEND(left, pn2);
255 0             left->pn_extra = 0;
256 0             if (tt == TOK_PLUS) {
257 0                 if (pn1->pn_type == TOK_STRING)
258 0                     left->pn_extra |= PNX_STRCAT;
259 0                 else if (pn1->pn_type != TOK_NUMBER)
260 0                     left->pn_extra |= PNX_CANTFOLD;
261 0                 if (pn2->pn_type == TOK_STRING)
262 0                     left->pn_extra |= PNX_STRCAT;
263 0                 else if (pn2->pn_type != TOK_NUMBER)
264 0                     left->pn_extra |= PNX_CANTFOLD;
265             }
266         }
267 0         PN_APPEND(left, right);
268 0         left->pn_pos.end = right->pn_pos.end;
269 0         if (tt == TOK_PLUS) {
270 0             if (right->pn_type == TOK_STRING)
271 0                 left->pn_extra |= PNX_STRCAT;
272 0             else if (right->pn_type != TOK_NUMBER)
273 0                 left->pn_extra |= PNX_CANTFOLD;
274         }
275 0         return left;
276     }
277
278     /*
279      * Fold constant addition immediately, to conserve node space and, what's
280      * more, so js_FoldConstants never sees mixed addition and concatenation
281      * operations with more than one leading non-string operand in a PN_LIST
282      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
283      * to "3pt", not "12pt").
284      */
285 0     if (tt == TOK_PLUS &&
286         left->pn_type == TOK_NUMBER &&
287         right->pn_type == TOK_NUMBER) {
288 0         left->pn_dval += right->pn_dval;
289 0         RecycleTree(right, tc);
290 0         return left;
291     }
292
293 0     pn = NewOrRecycledNode(cx, tc);
294 0     if (!pn)
295 0         return NULL;
296 0     pn->pn_type = tt;
297 0     pn->pn_pos.begin = left->pn_pos.begin;
298 0     pn->pn_pos.end = right->pn_pos.end;
299 0     pn->pn_op = op;
300 0     pn->pn_arity = PN_BINARY;
301 0     pn->pn_left = left;
302 0     pn->pn_right = right;
303 0     pn->pn_next = NULL;
304 #ifdef METER_PARSENODES
305     parsenodes++;
306     if (parsenodes - recyclednodes > maxparsenodes)
307         maxparsenodes = parsenodes - recyclednodes;
308 #endif
309 0     return pn;
310 }
311
312 #if JS_HAS_GETTER_SETTER
313 static JSTokenType
314 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
315 0 {
316 0     JSAtom *atom;
317 0     JSRuntime *rt;
318 0     JSOp op;
319 0     const char *name;
320
321 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
322 0     atom = CURRENT_TOKEN(ts).t_atom;
323 0     rt = cx->runtime;
324 0     if (atom == rt->atomState.getterAtom)
325 0         op = JSOP_GETTER;
326 0     else if (atom == rt->atomState.setterAtom)
327 0         op = JSOP_SETTER;
328     else
329 0         return TOK_NAME;
330 0     if (js_PeekTokenSameLine(cx, ts) != tt)
331 0         return TOK_NAME;
332 0     (void) js_GetToken(cx, ts);
333 0     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
334 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
335                                     JSMSG_BAD_GETTER_OR_SETTER,
336                                     (op == JSOP_GETTER)
337                                     ? js_getter_str
338                                     : js_setter_str);
339 0         return TOK_ERROR;
340     }
341 0     CURRENT_TOKEN(ts).t_op = op;
342 0     name = js_AtomToPrintableString(cx, atom);
343 0     if (!name ||
344         !js_ReportCompileErrorNumber(cx, ts, NULL,
345                                      JSREPORT_WARNING |
346                                      JSREPORT_STRICT,
347                                      JSMSG_DEPRECATED_USAGE,
348                                      name)) {
349 0         return TOK_ERROR;
350     }
351 0     return tt;
352 }
353 #endif
354
355 /*
356  * Parse a top-level JS script.
357  */
358 JS_FRIEND_API(JSParseNode *)
359 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
360 0 {
361 0     JSStackFrame *fp, frame;
362 0     JSTreeContext tc;
363 0     JSParseNode *pn;
364
365     /*
366      * Push a compiler frame if we have no frames, or if the top frame is a
367      * lightweight function activation, or if its scope chain doesn't match
368      * the one passed to us.
369      */
370 0     fp = cx->fp;
371 0     if (!fp || !fp->varobj || fp->scopeChain != chain) {
372 0         memset(&frame, 0, sizeof frame);
373 0         frame.varobj = frame.scopeChain = chain;
374 0         if (cx->options & JSOPTION_VAROBJFIX) {
375 0             while ((chain = JS_GetParent(cx, chain)) != NULL)
376 0                 frame.varobj = chain;
377         }
378 0         frame.down = fp;
379 0         cx->fp = &frame;
380     }
381
382     /*
383      * Protect atoms from being collected by a GC activation, which might
384      * - nest on this thread due to out of memory (the so-called "last ditch"
385      *   GC attempted within js_AllocGCThing), or
386      * - run for any reason on another thread if this thread is suspended on
387      *   an object lock before it finishes generating bytecode into a script
388      *   protected from the GC by a root or a stack frame reference.
389      */
390 0     JS_KEEP_ATOMS(cx->runtime);
391 0     TREE_CONTEXT_INIT(&tc);
392 0     pn = Statements(cx, ts, &tc);
393 0     if (pn) {
394 0         if (!js_MatchToken(cx, ts, TOK_EOF)) {
395 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
396                                         JSMSG_SYNTAX_ERROR);
397 0             pn = NULL;
398         } else {
399 0             pn->pn_type = TOK_LC;
400 0             if (!js_FoldConstants(cx, pn, &tc))
401 0                 pn = NULL;
402         }
403     }
404
405 0     TREE_CONTEXT_FINISH(&tc);
406 0     JS_UNKEEP_ATOMS(cx->runtime);
407 0     cx->fp = fp;
408 0     return pn;
409 }
410
411 /*
412  * Compile a top-level script.
413  */
414 JS_FRIEND_API(JSBool)
415 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
416                       JSCodeGenerator *cg)
417 0 {
418 0     JSStackFrame *fp, frame;
419 0     uint32 flags;
420 0     JSParseNode *pn;
421 0     JSBool ok;
422 #ifdef METER_PARSENODES
423     void *sbrk(ptrdiff_t), *before = sbrk(0);
424 #endif
425
426     /*
427      * Push a compiler frame if we have no frames, or if the top frame is a
428      * lightweight function activation, or if its scope chain doesn't match
429      * the one passed to us.
430      */
431 0     fp = cx->fp;
432 0     if (!fp || !fp->varobj || fp->scopeChain != chain) {
433 0         memset(&frame, 0, sizeof frame);
434 0         frame.varobj = frame.scopeChain = chain;
435 0         if (cx->options & JSOPTION_VAROBJFIX) {
436 0             while ((chain = JS_GetParent(cx, chain)) != NULL)
437 0                 frame.varobj = chain;
438         }
439 0         frame.down = fp;
440 0         cx->fp = &frame;
441     }
442 0     flags = cx->fp->flags;
443 0     cx->fp->flags = flags |
444                     (JS_HAS_COMPILE_N_GO_OPTION(cx)
445                      ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
446                      : JSFRAME_COMPILING);
447
448     /* Prevent GC activation while compiling. */
449 0     JS_KEEP_ATOMS(cx->runtime);
450
451 0     pn = Statements(cx, ts, &cg->treeContext);
452 0     if (!pn) {
453 0         ok = JS_FALSE;
454 0     } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
455 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
456                                     JSMSG_SYNTAX_ERROR);
457 0         ok = JS_FALSE;
458     } else {
459 #ifdef METER_PARSENODES
460         printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
461                (char *)sbrk(0) - (char *)before,
462                parsenodes,
463                maxparsenodes,
464                parsenodes - recyclednodes);
465         before = sbrk(0);
466 #endif
467
468         /*
469          * No need to emit code here -- Statements already has, for each
470          * statement in turn.  Search for TCF_COMPILING in Statements, below.
471          * That flag is set for every tc == &cg->treeContext, and it implies
472          * that the tc can be downcast to a cg and used to emit code during
473          * parsing, rather than at the end of the parse phase.
474          */
475 0         JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
476 0         ok = JS_TRUE;
477     }
478
479 #ifdef METER_PARSENODES
480     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
481            (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
482 #endif
483 #ifdef JS_ARENAMETER
484     JS_DumpArenaStats(stdout);
485 #endif
486 0     JS_UNKEEP_ATOMS(cx->runtime);
487 0     cx->fp->flags = flags;
488 0     cx->fp = fp;
489 0     return ok;
490 }
491
492 /*
493  * Insist on a final return before control flows out of pn, but don't be too
494  * smart about loops (do {...; return e2;} while(0) at the end of a function
495  * that contains an early return e1 will get a strict-option-only warning).
496  */
497 #define ENDS_IN_OTHER   0
498 #define ENDS_IN_RETURN  1
499 #define ENDS_IN_BREAK   2
500
501 static int
502 HasFinalReturn(JSParseNode *pn)
503 0 {
504 0     uintN rv, rv2, hasDefault;
505 0     JSParseNode *pn2, *pn3;
506
507 0     switch (pn->pn_type) {
508       case TOK_LC:
509 0         if (!pn->pn_head)
510 0             return ENDS_IN_OTHER;
511 0         return HasFinalReturn(PN_LAST(pn));
512
513       case TOK_IF:
514 0         rv = HasFinalReturn(pn->pn_kid2);
515 0         if (pn->pn_kid3)
516 0             rv &= HasFinalReturn(pn->pn_kid3);
517 0         return rv;
518
519 #if JS_HAS_SWITCH_STATEMENT
520       case TOK_SWITCH:
521 0         rv = ENDS_IN_RETURN;
522 0         hasDefault = ENDS_IN_OTHER;
523 0         for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
524 0             if (pn2->pn_type == TOK_DEFAULT)
525 0                 hasDefault = ENDS_IN_RETURN;
526 0             pn3 = pn2->pn_right;
527 0             JS_ASSERT(pn3->pn_type == TOK_LC);
528 0             if (pn3->pn_head) {
529 0                 rv2 = HasFinalReturn(PN_LAST(pn3));
530 0                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
531                     /* Falling through to next case or default. */;
532                 else
533 0                     rv &= rv2;
534             }
535         }
536         /* If a final switch has no default case, we judge it harshly. */
537 0         rv &= hasDefault;
538 0         return rv;
539 #endif /* JS_HAS_SWITCH_STATEMENT */
540
541       case TOK_BREAK:
542 0         return ENDS_IN_BREAK;
543
544       case TOK_WITH:
545 0         return HasFinalReturn(pn->pn_right);
546
547       case TOK_RETURN:
548 0         return ENDS_IN_RETURN;
549
550 #if JS_HAS_EXCEPTIONS
551       case TOK_THROW:
552 0         return ENDS_IN_RETURN;
553
554       case TOK_TRY:
555         /* If we have a finally block that returns, we are done. */
556 0         if (pn->pn_kid3) {
557 0             rv = HasFinalReturn(pn->pn_kid3);
558 0             if (rv == ENDS_IN_RETURN)
559 0                 return rv;
560         }
561
562         /* Else check the try block and any and all catch statements. */
563 0         rv = HasFinalReturn(pn->pn_kid1);
564 0         if (pn->pn_kid2)
565 0             rv &= HasFinalReturn(pn->pn_kid2);
566 0         return rv;
567
568       case TOK_CATCH:
569         /* Check this block's code and iterate over further catch blocks. */
570 0         rv = HasFinalReturn(pn->pn_kid3);
571 0         for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
572 0             rv &= HasFinalReturn(pn2->pn_kid3);
573 0         return rv;
574 #endif
575
576       default:
577 0         return ENDS_IN_OTHER;
578     }
579 }
580
581 static JSBool
582 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
583 0 {
584 0     JSFunction *fun;
585 0     JSBool ok;
586
587 0     fun = cx->fp->fun;
588 0     if (fun->atom) {
589 0         char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
590 0         ok = js_ReportCompileErrorNumber(cx, ts, NULL,
591                                          JSREPORT_WARNING |
592                                          JSREPORT_STRICT,
593                                          JSMSG_NO_RETURN_VALUE, name);
594     } else {
595 0         ok = js_ReportCompileErrorNumber(cx, ts, NULL,
596                                          JSREPORT_WARNING |
597                                          JSREPORT_STRICT,
598                                          JSMSG_ANON_NO_RETURN_VALUE);
599     }
600 0     return ok;
601 }
602
603 static JSBool
604 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
605 0 {
606 0     return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
607 }
608
609 static JSParseNode *
610 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
611              JSTreeContext *tc)
612 0 {
613 0     JSStackFrame *fp, frame;
614 0     JSObject *funobj;
615 0     uintN oldflags;
616 0     JSParseNode *pn;
617
618 0     fp = cx->fp;
619 0     funobj = fun->object;
620 0     if (!fp || fp->fun != fun || fp->varobj != funobj ||
621         fp->scopeChain != funobj) {
622 0         memset(&frame, 0, sizeof frame);
623 0         frame.fun = fun;
624 0         frame.varobj = frame.scopeChain = funobj;
625 0         frame.down = fp;
626 0         cx->fp = &frame;
627     }
628
629 0     oldflags = tc->flags;
630 0     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
631 0     tc->flags |= TCF_IN_FUNCTION;
632 0     pn = Statements(cx, ts, tc);
633
634     /* Check for falling off the end of a function that returns a value. */
635 0     if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
636 0         if (!CheckFinalReturn(cx, ts, pn))
637 0             pn = NULL;
638     }
639
640 0     cx->fp = fp;
641 0     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
642 0     return pn;
643 }
644
645 /*
646  * Compile a JS function body, which might appear as the value of an event
647  * handler attribute in an HTML <INPUT> tag.
648  */
649 JSBool
650 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
651 0 {
652 0     JSArenaPool codePool, notePool;
653 0     JSCodeGenerator funcg;
654 0     JSStackFrame *fp, frame;
655 0     JSObject *funobj;
656 0     JSParseNode *pn;
657 0     JSBool ok;
658
659 0     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
660 0     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
661 0     if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
662                               ts->filename, ts->lineno,
663                               ts->principals)) {
664 0         return JS_FALSE;
665     }
666
667     /* Prevent GC activation while compiling. */
668 0     JS_KEEP_ATOMS(cx->runtime);
669
670     /* Push a JSStackFrame for use by FunctionBody. */
671 0     fp = cx->fp;
672 0     funobj = fun->object;
673 0     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
674                       fp->scopeChain != funobj));
675 0     memset(&frame, 0, sizeof frame);
676 0     frame.fun = fun;
677 0     frame.varobj = frame.scopeChain = funobj;
678 0     frame.down = fp;
679 0     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
680                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
681                   : JSFRAME_COMPILING;
682 0     cx->fp = &frame;
683
684     /* Ensure that the body looks like a block statement to js_EmitTree. */
685 0     CURRENT_TOKEN(ts).type = TOK_LC;
686 0     pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
687 0     if (!pn) {
688 0         ok = JS_FALSE;
689     } else {
690         /*
691          * No need to emit code here -- Statements (via FunctionBody) already
692          * has.  See similar comment in js_CompileTokenStream, and bug 108257.
693          */
694 0         fun->script = js_NewScriptFromCG(cx, &funcg, fun);
695 0         if (!fun->script) {
696 0             ok = JS_FALSE;
697         } else {
698 0             if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
699 0                 fun->flags |= JSFUN_HEAVYWEIGHT;
700 0             ok = JS_TRUE;
701         }
702     }
703
704     /* Restore saved state and release code generation arenas. */
705 0     cx->fp = fp;
706 0     JS_UNKEEP_ATOMS(cx->runtime);
707 0     js_FinishCodeGenerator(cx, &funcg);
708 0     JS_FinishArenaPool(&codePool);
709 0     JS_FinishArenaPool(&notePool);
710 0     return ok;
711 }
712
713 static JSParseNode *
714 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
715             JSBool lambda)
716 0 {
717 0     JSParseNode *pn, *body;
718 0     JSOp op, prevop;
719 0     JSAtom *funAtom, *argAtom;
720 0     JSFunction *fun;
721 0     JSObject *parent;
722 0     JSObject *pobj;
723 0     JSScopeProperty *sprop;
724 0     uintN dupflag;
725 0     JSBool ok;
726 0     JSTreeContext funtc;
727 0     JSAtomListElement *ale;
728
729     /* Make a TOK_FUNCTION node. */
730 0     pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);
731 0     if (!pn)
732 0         return NULL;
733 #if JS_HAS_GETTER_SETTER
734 0     op = CURRENT_TOKEN(ts).t_op;
735 #endif
736
737     /* Scan the optional function name into funAtom. */
738 0     if (js_MatchToken(cx, ts, TOK_NAME))
739 0         funAtom = CURRENT_TOKEN(ts).t_atom;
740     else
741 0         funAtom = NULL;
742
743     /* Find the nearest variable-declaring scope and use it as our parent. */
744 0     parent = cx->fp->varobj;
745 0     fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, parent,
746                          funAtom);
747 0     if (!fun)
748 0         return NULL;
749
750 #if JS_HAS_GETTER_SETTER
751 0     if (op != JSOP_NOP)
752 0         fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
753 #endif
754
755     /* Now parse formal argument list and compute fun->nargs. */
756 0     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
757 0     if (!js_MatchToken(cx, ts, TOK_RP)) {
758 0         do {
759 0             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
760 0             argAtom = CURRENT_TOKEN(ts).t_atom;
761 0             pobj = NULL;
762 0             if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
763                                    (JSProperty **)&sprop)) {
764 0                 return NULL;
765             }
766 0             dupflag = 0;
767 0             if (sprop) {
768 0                 ok = JS_TRUE;
769 0                 if (pobj == fun->object &&
770                     sprop->getter == js_GetArgument) {
771 0                     const char *name = js_AtomToPrintableString(cx, argAtom);
772
773                     /*
774                      * A duplicate parameter name. We force a duplicate node
775                      * on the SCOPE_LAST_PROP(scope) list with the same id,
776                      * distinguished by the SPROP_IS_DUPLICATE flag, and not
777                      * mapped by an entry in scope.
778                      */
779 0                     ok = name &&
780                          js_ReportCompileErrorNumber(cx, ts, NULL,
781                                                      JSREPORT_WARNING |
782                                                      JSREPORT_STRICT,
783                                                      JSMSG_DUPLICATE_FORMAL,
784                                                      name);
785
786 0                     dupflag = SPROP_IS_DUPLICATE;
787                 }
788 0                 OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
789 0                 if (!ok)
790 0                     return NULL;
791 0                 sprop = NULL;
792             }
793 0             if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
794                                       js_GetArgument, js_SetArgument,
795                                       SPROP_INVALID_SLOT,
796                                       JSPROP_ENUMERATE | JSPROP_PERMANENT |
797                                       JSPROP_SHARED,
798                                       SPROP_HAS_SHORTID | dupflag,
799                                       fun->nargs)) {
800 0                 return NULL;
801             }
802 0             fun->nargs++;
803 0         } while (js_MatchToken(cx, ts, TOK_COMMA));
804
805 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
806     }
807
808 0     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
809 0     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
810
811 0     TREE_CONTEXT_INIT(&funtc);
812 0     body = FunctionBody(cx, ts, fun, &funtc);
813 0     if (!body)
814 0         return NULL;
815
816 0     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
817 0     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
818
819 #if JS_HAS_LEXICAL_CLOSURE
820     /*
821      * If we collected flags that indicate nested heavyweight functions, or
822      * this function contains heavyweight-making statements (references to
823      * __parent__ or __proto__; use of with, eval, import, or export; and
824      * assignment to arguments), flag the function as heavyweight (requiring
825      * a call object per invocation).
826      */
827 0     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
828 0         fun->flags |= JSFUN_HEAVYWEIGHT;
829 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
830     } else {
831         /*
832          * If this function is a named statement function not at top-level
833          * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
834          * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
835          * enclosing function, if any, must be heavyweight.
836          */
837 0         if ((!lambda && funAtom && tc->topStmt) ||
838             (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
839 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
840         }
841     }
842 #endif
843
844     /*
845      * Record names for function statements in tc->decls so we know when to
846      * avoid optimizing variable references that might name a function.
847      */
848 0     if (!lambda && funAtom) {
849 0         ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
850 0         if (ale) {
851 0             prevop = ALE_JSOP(ale);
852 0             if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
853 0                 const char *name = js_AtomToPrintableString(cx, funAtom);
854 0                 if (!name ||
855                     !js_ReportCompileErrorNumber(cx, ts, NULL,
856                                                  (prevop != JSOP_DEFCONST)
857                                                  ? JSREPORT_WARNING |
858                                                    JSREPORT_STRICT
859                                                  : JSREPORT_ERROR,
860                                                  JSMSG_REDECLARED_VAR,
861                                                  (prevop == JSOP_DEFFUN ||
862                                                   prevop == JSOP_CLOSURE)
863                                                  ? js_function_str
864                                                  : (prevop == JSOP_DEFCONST)
865                                                  ? js_const_str
866                                                  : js_var_str,
867                                                  name)) {
868 0                     return NULL;
869                 }
870             }
871 0             if (tc->topStmt && prevop == JSOP_DEFVAR)
872 0                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
873         } else {
874 0             ale = js_IndexAtom(cx, funAtom, &tc->decls);
875 0             if (!ale)
876 0                 return NULL;
877         }
878 0         ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
879
880 #if JS_HAS_LEXICAL_CLOSURE
881         /*
882          * A function nested at top level inside another's body needs only a
883          * local variable to bind its name to its value, and not an activation
884          * object property (it might also need the activation property, if the
885          * outer function contains with statements, e.g., but the stack slot
886          * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
887          * JSOP_GETVAR bytecode).
888          */
889 0         if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
890 0             JSStackFrame *fp;
891 0             JSObject *varobj;
892
893             /*
894              * Define a property on the outer function so that LookupArgOrVar
895              * can properly optimize accesses.
896              *
897              * XXX Here and in Variables, we use the function object's scope,
898              * XXX arguably polluting it, when we could use a compiler-private
899              * XXX scope structure.  Tradition!
900              */
901 0             fp = cx->fp;
902 0             varobj = fp->varobj;
903 0             JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
904 0             JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
905 0             if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
906                                          OBJECT_TO_JSVAL(fun->object),
907                                          js_GetLocalVariable,
908                                          js_SetLocalVariable,
909                                          JSPROP_ENUMERATE,
910                                          SPROP_HAS_SHORTID, fp->fun->nvars,
911                                          NULL)) {
912 0                 return NULL;
913             }
914 0             fp->fun->nvars++;
915         }
916 #endif
917     }
918
919 #if JS_HAS_LEXICAL_CLOSURE
920 0     if (lambda || !funAtom) {
921         /*
922          * ECMA ed. 3 standard: function expression, possibly anonymous (even
923          * if at top-level, an unnamed function is an expression statement, not
924          * a function declaration).
925          */
926 0         op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
927 0     } else if (tc->topStmt) {
928         /*
929          * ECMA ed. 3 extension: a function expression statement not at the
930          * top level, e.g., in a compound statement such as the "then" part
931          * of an "if" statement, binds a closure only if control reaches that
932          * sub-statement.
933          */
934 0         op = JSOP_CLOSURE;
935     } else
936 #endif
937 0         op = JSOP_NOP;
938
939     /*
940      * Pending a better automatic GC root management scheme (see Mozilla bug
941      * 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
942      * atomize here to protect against a GC activation.
943      */
944 0     pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
945 0     if (!pn->pn_funAtom)
946 0         return NULL;
947
948 0     pn->pn_op = op;
949 0     pn->pn_body = body;
950 0     pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
951 0     pn->pn_tryCount = funtc.tryCount;
952 0     TREE_CONTEXT_FINISH(&funtc);
953 0     return pn;
954 }
955
956 static JSParseNode *
957 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
958 0 {
959 0     return FunctionDef(cx, ts, tc, JS_FALSE);
960 }
961
962 #if JS_HAS_LEXICAL_CLOSURE
963 static JSParseNode *
964 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
965 0 {
966 0     return FunctionDef(cx, ts, tc, JS_TRUE);
967 }
968 #endif
969
970 /*
971  * Parse the statements in a block, creating a TOK_LC node that lists the
972  * statements' trees.  If called from block-parsing code, the caller must
973  * match { before and } after.
974  */
975 static JSParseNode *
976 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
977 0 {
978 0     JSParseNode *pn, *pn2;
979 0     JSTokenType tt;
980
981 0     CHECK_RECURSION();
982
983 0     pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
984 0     if (!pn)
985 0         return NULL;
986 0     PN_INIT_LIST(pn);
987
988 0     ts->flags |= TSF_REGEXP;
989 0     while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
990 0         ts->flags &= ~TSF_REGEXP;
991 0         pn2 = Statement(cx, ts, tc);
992 0         if (!pn2)
993 0             return NULL;
994 0         ts->flags |= TSF_REGEXP;
995
996         /* If compiling top-level statements, emit as we go to save space. */
997 0         if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
998 0             if (cx->fp->fun &&
999                 JS_HAS_STRICT_OPTION(cx) &&
1000                 (tc->flags & TCF_RETURN_EXPR)) {
1001                 /*
1002                  * Check pn2 for lack of a final return statement if it is the
1003                  * last statement in the block.
1004                  */
1005 0                 tt = js_PeekToken(cx, ts);
1006 0                 if ((tt == TOK_EOF || tt == TOK_RC) &&
1007                     !CheckFinalReturn(cx, ts, pn2)) {
1008 0                     tt = TOK_ERROR;
1009 0                     break;
1010                 }
1011
1012                 /*
1013                  * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1014                  * CheckFinalReturn again.
1015                  */
1016 0                 tc->flags &= ~TCF_RETURN_EXPR;
1017             }
1018 0             if (!js_FoldConstants(cx, pn2, tc) ||
1019                 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1020                 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1021 0                 tt = TOK_ERROR;
1022 0                 break;
1023             }
1024 0             RecycleTree(pn2, tc);
1025         } else {
1026 0             PN_APPEND(pn, pn2);
1027         }
1028     }
1029 0     ts->flags &= ~TSF_REGEXP;
1030 0     if (tt == TOK_ERROR)
1031 0         return NULL;
1032
1033 0     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1034 0     return pn;
1035 }
1036
1037 static JSParseNode *
1038 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1039 0 {
1040 0     JSParseNode *pn, *pn2;
1041
1042 0     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1043 0     pn = Expr(cx, ts, tc);
1044 0     if (!pn)
1045 0         return NULL;
1046 0     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1047
1048     /*
1049      * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1050      * greater precedence than ==.
1051      * XXX not ECMA, but documented in several books -- now a strict warning.
1052      */
1053 0     if (pn->pn_type == TOK_ASSIGN &&
1054         pn->pn_op == JSOP_NOP &&
1055         pn->pn_right->pn_type > TOK_EQOP)
1056     {
1057 0         JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
1058 0         if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1059                                          JSREPORT_WARNING | JSREPORT_STRICT,
1060                                          JSMSG_EQUAL_AS_ASSIGN,
1061                                          rewrite
1062                                          ? "\nAssuming equality test"
1063                                          : "")) {
1064 0             return NULL;
1065         }
1066 0         if (rewrite) {
1067 0             pn->pn_type = TOK_EQOP;
1068 0             pn->pn_op = (JSOp)cx->jsop_eq;
1069 0             pn2 = pn->pn_left;
1070 0             switch (pn2->pn_op) {
1071               case JSOP_SETNAME:
1072 0                 pn2->pn_op = JSOP_NAME;
1073 0                 break;
1074               case JSOP_SETPROP:
1075 0                 pn2->pn_op = JSOP_GETPROP;
1076 0                 break;
1077               case JSOP_SETELEM:
1078 0                 pn2->pn_op = JSOP_GETELEM;
1079                 break;
1080               default:
1081 0                 JS_ASSERT(0);
1082             }
1083         }
1084     }
1085 0     return pn;
1086 }
1087
1088 static JSBool
1089 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1090 0 {
1091 0     JSAtom *label;
1092 #if JS_HAS_LABEL_STATEMENT
1093 0     JSTokenType tt;
1094
1095 0     tt = js_PeekTokenSameLine(cx, ts);
1096 0     if (tt == TOK_ERROR)
1097 0         return JS_FALSE;
1098 0     if (tt == TOK_NAME) {
1099 0         (void) js_GetToken(cx, ts);
1100 0         label = CURRENT_TOKEN(ts).t_atom;
1101     } else {
1102 0         label = NULL;
1103     }
1104 #else
1105     label = NULL;
1106 #endif
1107 0     pn->pn_atom = label;
1108 0     return JS_TRUE;
1109 }
1110
1111 #if JS_HAS_EXPORT_IMPORT
1112 static JSParseNode *
1113 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1114 {
1115     JSParseNode *pn, *pn2, *pn3;
1116     JSTokenType tt;
1117
1118     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1119     pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1120     if (!pn)
1121         return NULL;
1122     pn->pn_op = JSOP_NAME;
1123     pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1124     pn->pn_expr = NULL;
1125     pn->pn_slot = -1;
1126     pn->pn_attrs = 0;
1127
1128     ts->flags |= TSF_REGEXP;
1129     while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1130         ts->flags &= ~TSF_REGEXP;
1131         if (pn->pn_op == JSOP_IMPORTALL)
1132             goto bad_import;
1133
1134         if (tt == TOK_DOT) {
1135             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1136             if (!pn2)
1137                 return NULL;
1138             if (js_MatchToken(cx, ts, TOK_STAR)) {
1139                 pn2->pn_op = JSOP_IMPORTALL;
1140                 pn2->pn_atom = NULL;
1141             } else {
1142                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1143                 pn2->pn_op = JSOP_GETPROP;
1144                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1145                 pn2->pn_slot = -1;
1146                 pn2->pn_attrs = 0;
1147             }
1148             pn2->pn_expr = pn;
1149             pn2->pn_pos.begin = pn->pn_pos.begin;
1150             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1151         } else {
1152             /* Make a TOK_LB node. */
1153             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1154             if (!pn2)
1155                 return NULL;
1156             pn3 = Expr(cx, ts, tc);
1157             if (!pn3)
1158                 return NULL;
1159
1160             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1161             pn2->pn_pos.begin = pn->pn_pos.begin;
1162             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1163
1164             pn2->pn_op = JSOP_GETELEM;
1165             pn2->pn_left = pn;
1166             pn2->pn_right = pn3;
1167         }
1168
1169         pn = pn2;
1170         ts->flags |= TSF_REGEXP;
1171     }
1172     ts->flags &= ~TSF_REGEXP;
1173     if (tt == TOK_ERROR)
1174         return NULL;
1175     js_UngetToken(ts);
1176
1177     switch (pn->pn_op) {
1178       case JSOP_GETPROP:
1179         pn->pn_op = JSOP_IMPORTPROP;
1180         break;
1181       case JSOP_GETELEM:
1182         pn->pn_op = JSOP_IMPORTELEM;
1183         break;
1184       case JSOP_IMPORTALL:
1185         break;
1186       default:
1187         goto bad_import;
1188     }
1189     return pn;
1190
1191   bad_import:
1192     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
1193     return NULL;
1194 }
1195 #endif /* JS_HAS_EXPORT_IMPORT */
1196
1197 extern const char js_with_statement_str[];
1198
1199 static JSParseNode *
1200 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1201 0 {
1202 0     JSTokenType tt;
1203 0     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1204 0     JSStmtInfo stmtInfo, *stmt, *stmt2;
1205 0     JSAtom *label;
1206
1207 0     ts->flags |= TSF_REGEXP;
1208 0     tt = js_GetToken(cx, ts);
1209 0     ts->flags &= ~TSF_REGEXP;
1210
1211 #if JS_HAS_GETTER_SETTER
1212 0     if (tt == TOK_NAME) {
1213 0         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1214 0         if (tt == TOK_ERROR)
1215 0             return NULL;
1216     }
1217 #endif
1218
1219 0     switch (tt) {
1220 #if JS_HAS_EXPORT_IMPORT
1221       case TOK_EXPORT:
1222         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1223         if (!pn)
1224             return NULL;
1225         PN_INIT_LIST(pn);
1226         if (js_MatchToken(cx, ts, TOK_STAR)) {
1227             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1228             if (!pn2)
1229                 return NULL;
1230             PN_APPEND(pn, pn2);
1231         } else {
1232             do {
1233                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1234                 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1235                 if (!pn2)
1236                     return NULL;
1237                 pn2->pn_op = JSOP_NAME;
1238                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1239                 pn2->pn_expr = NULL;
1240                 pn2->pn_slot = -1;
1241                 pn2->pn_attrs = 0;
1242                 PN_APPEND(pn, pn2);
1243             } while (js_MatchToken(cx, ts, TOK_COMMA));
1244         }
1245         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1246         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1247         break;
1248
1249       case TOK_IMPORT:
1250         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1251         if (!pn)
1252             return NULL;
1253         PN_INIT_LIST(pn);
1254         do {
1255             pn2 = ImportExpr(cx, ts, tc);
1256             if (!pn2)
1257                 return NULL;
1258             PN_APPEND(pn, pn2);
1259         } while (js_MatchToken(cx, ts, TOK_COMMA));
1260         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1261         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1262         break;
1263 #endif /* JS_HAS_EXPORT_IMPORT */
1264
1265       case TOK_FUNCTION:
1266 0         return FunctionStmt(cx, ts, tc);
1267
1268       case TOK_IF:
1269         /* An IF node has three kids: condition, then, and optional else. */
1270 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1271 0         if (!pn)
1272 0             return NULL;
1273 0         pn1 = Condition(cx, ts, tc);
1274 0         if (!pn1)
1275 0             return NULL;
1276 0         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1277 0         pn2 = Statement(cx, ts, tc);
1278 0         if (!pn2)
1279 0             return NULL;
1280 0         if (js_MatchToken(cx, ts, TOK_ELSE)) {
1281 0             stmtInfo.type = STMT_ELSE;
1282 0             pn3 = Statement(cx, ts, tc);
1283 0             if (!pn3)
1284 0                 return NULL;
1285 0             pn->pn_pos.end = pn3->pn_pos.end;
1286         } else {
1287 0             pn3 = NULL;
1288 0             pn->pn_pos.end = pn2->pn_pos.end;
1289         }
1290 0         js_PopStatement(tc);
1291 0         pn->pn_kid1 = pn1;
1292 0         pn->pn_kid2 = pn2;
1293 0         pn->pn_kid3 = pn3;
1294 0         return pn;
1295
1296 #if JS_HAS_SWITCH_STATEMENT
1297       case TOK_SWITCH:
1298       {
1299 0         JSParseNode *pn5;
1300 0         JSBool seenDefault = JS_FALSE;
1301
1302 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1303 0         if (!pn)
1304 0             return NULL;
1305 0         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1306
1307         /* pn1 points to the switch's discriminant. */
1308 0         pn1 = Expr(cx, ts, tc);
1309 0         if (!pn1)
1310 0             return NULL;
1311
1312 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1313 0         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1314
1315         /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1316 0         pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1317 0         if (!pn2)
1318 0             return NULL;
1319 0         PN_INIT_LIST(pn2);
1320
1321 0         js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1322
1323 0         while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1324 0             switch (tt) {
1325               case TOK_DEFAULT:
1326 0                 if (seenDefault) {
1327 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1328                                                 JSMSG_TOO_MANY_DEFAULTS);
1329 0                     return NULL;
1330                 }
1331 0                 seenDefault = JS_TRUE;
1332                 /* fall through */
1333
1334               case TOK_CASE:
1335 0                 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1336 0                 if (!pn3)
1337 0                     return NULL;
1338 0                 if (tt == TOK_DEFAULT) {
1339 0                     pn3->pn_left = NULL;
1340                 } else {
1341 0                     pn3->pn_left = Expr(cx, ts, tc);
1342 0                     if (!pn3->pn_left)
1343 0                         return NULL;
1344                 }
1345 0                 PN_APPEND(pn2, pn3);
1346 0                 if (pn2->pn_count == JS_BIT(16)) {
1347 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1348                                                 JSMSG_TOO_MANY_CASES);
1349 0                     return NULL;
1350                 }
1351 0                 break;
1352
1353               case TOK_ERROR:
1354 0                 return NULL;
1355
1356               default:
1357 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1358                                             JSMSG_BAD_SWITCH);
1359 0                 return NULL;
1360             }
1361 0             MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1362
1363 0             pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1364 0             if (!pn4)
1365 0                 return NULL;
1366 0             pn4->pn_type = TOK_LC;
1367 0             PN_INIT_LIST(pn4);
1368 0             while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1369                    tt != TOK_CASE && tt != TOK_DEFAULT) {
1370 0                 if (tt == TOK_ERROR)
1371 0                     return NULL;
1372 0                 pn5 = Statement(cx, ts, tc);
1373 0                 if (!pn5)
1374 0                     return NULL;
1375 0                 pn4->pn_pos.end = pn5->pn_pos.end;
1376 0                 PN_APPEND(pn4, pn5);
1377             }
1378
1379             /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1380 0             if (pn4->pn_head)
1381 0                 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1382 0             pn3->pn_pos.end = pn4->pn_pos.end;
1383 0             pn3->pn_right = pn4;
1384         }
1385
1386 0         js_PopStatement(tc);
1387
1388 0         pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1389 0         pn->pn_kid1 = pn1;
1390 0         pn->pn_kid2 = pn2;
1391 0         return pn;
1392       }
1393 #endif /* JS_HAS_SWITCH_STATEMENT */
1394
1395       case TOK_WHILE:
1396 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1397 0         if (!pn)
1398 0             return NULL;
1399 0         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1400 0         pn2 = Condition(cx, ts, tc);
1401 0         if (!pn2)
1402 0             return NULL;
1403 0         pn->pn_left = pn2;
1404 0         pn2 = Statement(cx, ts, tc);
1405 0         if (!pn2)
1406 0             return NULL;
1407 0         js_PopStatement(tc);
1408 0         pn->pn_pos.end = pn2->pn_pos.end;
1409 0         pn->pn_right = pn2;
1410 0         return pn;
1411
1412 #if JS_HAS_DO_WHILE_LOOP
1413       case TOK_DO:
1414 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1415 0         if (!pn)
1416 0             return NULL;
1417 0         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1418 0         pn2 = Statement(cx, ts, tc);
1419 0         if (!pn2)
1420 0             return NULL;
1421 0         pn->pn_left = pn2;
1422 0         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1423 0         pn2 = Condition(cx, ts, tc);
1424 0         if (!pn2)
1425 0             return NULL;
1426 0         js_PopStatement(tc);
1427 0         pn->pn_pos.end = pn2->pn_pos.end;
1428 0         pn->pn_right = pn2;
1429 0         break;
1430 #endif /* JS_HAS_DO_WHILE_LOOP */
1431
1432       case TOK_FOR:
1433         /* A FOR node is binary, left is loop control and right is the body. */
1434 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1435 0         if (!pn)
1436 0             return NULL;
1437 0         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1438
1439 0         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1440 0         ts->flags |= TSF_REGEXP;
1441 0         tt = js_PeekToken(cx, ts);
1442 0         ts->flags &= ~TSF_REGEXP;
1443 0         if (tt == TOK_SEMI) {
1444             /* No initializer -- set first kid of left sub-node to null. */
1445 0             pn1 = NULL;
1446         } else {
1447             /* Set pn1 to a var list or an initializing expression. */
1448 #if JS_HAS_IN_OPERATOR
1449             /*
1450              * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1451              * of the for statement.  This flag will be used by the RelExpr
1452              * production; if it is set, then the 'in' keyword will not be
1453              * recognized as an operator, leaving it available to be parsed as
1454              * part of a for/in loop.  A side effect of this restriction is
1455              * that (unparenthesized) expressions involving an 'in' operator
1456              * are illegal in the init clause of an ordinary for loop.
1457              */
1458 0             tc->flags |= TCF_IN_FOR_INIT;
1459 #endif /* JS_HAS_IN_OPERATOR */
1460 0             if (tt == TOK_VAR) {
1461 0                 (void) js_GetToken(cx, ts);
1462 0                 pn1 = Variables(cx, ts, tc);
1463             } else {
1464 0                 pn1 = Expr(cx, ts, tc);
1465             }
1466 #if JS_HAS_IN_OPERATOR
1467 0             tc->flags &= ~TCF_IN_FOR_INIT;
1468 #endif /* JS_HAS_IN_OPERATOR */
1469 0             if (!pn1)
1470 0                 return NULL;
1471         }
1472
1473         /*
1474          * We can be sure that it's a for/in loop if there's still an 'in'
1475          * keyword here, even if JavaScript recognizes 'in' as an operator,
1476          * as we've excluded 'in' from being parsed in RelExpr by setting
1477          * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1478          */
1479 0         if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1480 0             stmtInfo.type = STMT_FOR_IN_LOOP;
1481
1482             /* Check that the left side of the 'in' is valid. */
1483 0             if ((pn1->pn_type == TOK_VAR)
1484                 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1485                 : (pn1->pn_type != TOK_NAME &&
1486                    pn1->pn_type != TOK_DOT &&
1487                    pn1->pn_type != TOK_LB)) {
1488 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1489                                             JSMSG_BAD_FOR_LEFTSIDE);
1490 0                 return NULL;
1491             }
1492
1493 0             if (pn1->pn_type == TOK_VAR) {
1494                 /* Tell js_EmitTree(TOK_VAR) to generate a final POP. */
1495 0                 pn1->pn_extra = JS_TRUE;
1496 0                 pn2 = pn1->pn_head;
1497             } else {
1498 0                 pn2 = pn1;
1499             }
1500
1501             /* Beware 'for (arguments in ...)' with or without a 'var'. */
1502 0             if (pn2->pn_type == TOK_NAME &&
1503                 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1504 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1505             }
1506
1507             /* Parse the object expression as the right operand of 'in'. */
1508 0             pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1509 0             if (!pn2)
1510 0                 return NULL;
1511 0             pn->pn_left = pn2;
1512         } else {
1513             /* Parse the loop condition or null into pn2. */
1514 0             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1515 0             ts->flags |= TSF_REGEXP;
1516 0             tt = js_PeekToken(cx, ts);
1517 0             ts->flags &= ~TSF_REGEXP;
1518 0             if (tt == TOK_SEMI) {
1519 0                 pn2 = NULL;
1520             } else {
1521 0                 pn2 = Expr(cx, ts, tc);
1522 0                 if (!pn2)
1523 0                     return NULL;
1524             }
1525
1526             /* Parse the update expression or null into pn3. */
1527 0             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1528 0             ts->flags |= TSF_REGEXP;
1529 0             tt = js_PeekToken(cx, ts);
1530 0             ts->flags &= ~TSF_REGEXP;
1531 0             if (tt == TOK_RP) {
1532 0                 pn3 = NULL;
1533             } else {
1534 0                 pn3 = Expr(cx, ts, tc);
1535 0                 if (!pn3)
1536 0                     return NULL;
1537             }
1538
1539             /* Build the RESERVED node to use as the left kid of pn. */
1540 0             pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1541 0             if (!pn4)
1542 0                 return NULL;
1543 0             pn4->pn_type = TOK_RESERVED;
1544 0             pn4->pn_op = JSOP_NOP;
1545 0             pn4->pn_kid1 = pn1;
1546 0             pn4->pn_kid2 = pn2;
1547 0             pn4->pn_kid3 = pn3;
1548 0             pn->pn_left = pn4;
1549         }
1550
1551 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1552
1553         /* Parse the loop body into pn->pn_right. */
1554 0         pn2 = Statement(cx, ts, tc);
1555 0         if (!pn2)
1556 0             return NULL;
1557 0         pn->pn_right = pn2;
1558 0         js_PopStatement(tc);
1559
1560         /* Record the absolute line number for source note emission. */
1561 0         pn->pn_pos.end = pn2->pn_pos.end;
1562 0         return pn;
1563
1564 #if JS_HAS_EXCEPTIONS
1565       case TOK_TRY: {
1566 0         JSParseNode *catchtail = NULL;
1567         /*
1568          * try nodes are ternary.
1569          * kid1 is the try Statement
1570          * kid2 is the catch node
1571          * kid3 is the finally Statement
1572          *
1573          * catch nodes are ternary.
1574          * kid1 is the discriminant
1575          * kid2 is the next catch node, or NULL
1576          * kid3 is the catch block (on kid3 so that we can always append a
1577          *                          new catch pn on catchtail->kid2)
1578          *
1579          * catch discriminant nodes are binary
1580          * atom is the receptacle
1581          * expr is the discriminant code
1582          *
1583          * finally nodes are unary (just the finally expression)
1584          */
1585 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1586 0         pn->pn_op = JSOP_NOP;
1587
1588 0         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1589 0         js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1590 0         pn->pn_kid1 = Statements(cx, ts, tc);
1591 0         if (!pn->pn_kid1)
1592 0             return NULL;
1593 0         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1594 0         js_PopStatement(tc);
1595
1596 0         catchtail = pn;
1597 0         while (js_PeekToken(cx, ts) == TOK_CATCH) {
1598             /* check for another catch after unconditional catch */
1599 0             if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1600 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1601                                             JSMSG_CATCH_AFTER_GENERAL);
1602 0                 return NULL;
1603             }
1604
1605             /*
1606              * legal catch forms are:
1607              * catch (v)
1608              * catch (v if <boolean_expression>)
1609              * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1610              */
1611 0             (void) js_GetToken(cx, ts); /* eat `catch' */
1612 0             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1613 0             if (!pn2)
1614 0                 return NULL;
1615
1616             /*
1617              * We use a PN_NAME for the discriminant (catchguard) node
1618              * with the actual discriminant code in the initializer spot
1619              */
1620 0             MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1621 0             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1622 0             pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1623 0             if (!pn3)
1624 0                 return NULL;
1625
1626 0             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1627 0             pn3->pn_expr = NULL;
1628 #if JS_HAS_CATCH_GUARD
1629             /*
1630              * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1631              * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1632              */
1633 0             if (js_PeekToken(cx, ts) == TOK_IF) {
1634 0                 (void)js_GetToken(cx, ts); /* eat `if' */
1635 0                 pn3->pn_expr = Expr(cx, ts, tc);
1636 0                 if (!pn3->pn_expr)
1637 0                     return NULL;
1638             }
1639 #endif
1640 0             pn2->pn_kid1 = pn3;
1641
1642 0             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1643
1644 0             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1645 0             js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1646 0             stmtInfo.label = pn3->pn_atom;
1647 0             pn2->pn_kid3 = Statements(cx, ts, tc);
1648 0             if (!pn2->pn_kid3)
1649 0                 return NULL;
1650 0             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1651 0             js_PopStatement(tc);
1652
1653 0             catchtail = catchtail->pn_kid2 = pn2;
1654         }
1655 0         catchtail->pn_kid2 = NULL;
1656
1657 0         if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1658 0             tc->tryCount++;
1659 0             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1660 0             js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1661 0             pn->pn_kid3 = Statements(cx, ts, tc);
1662 0             if (!pn->pn_kid3)
1663 0                 return NULL;
1664 0             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1665 0             js_PopStatement(tc);
1666         } else {
1667 0             pn->pn_kid3 = NULL;
1668         }
1669 0         if (!pn->pn_kid2 && !pn->pn_kid3) {
1670 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1671                                         JSMSG_CATCH_OR_FINALLY);
1672 0             return NULL;
1673         }
1674 0         tc->tryCount++;
1675 0         return pn;
1676       }
1677
1678       case TOK_THROW:
1679 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1680 0         if (!pn)
1681 0             return NULL;
1682 0         pn2 = Expr(cx, ts, tc);
1683 0         if (!pn2)
1684 0             return NULL;
1685 0         pn->pn_pos.end = pn2->pn_pos.end;
1686 0         pn->pn_op = JSOP_THROW;
1687 0         pn->pn_kid = pn2;
1688 0         break;
1689
1690       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1691       case TOK_CATCH:
1692 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1693                                     JSMSG_CATCH_WITHOUT_TRY);
1694 0         return NULL;
1695
1696       case TOK_FINALLY:
1697 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1698                                     JSMSG_FINALLY_WITHOUT_TRY);
1699 0         return NULL;
1700
1701 #endif /* JS_HAS_EXCEPTIONS */
1702
1703       case TOK_BREAK:
1704 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1705 0         if (!pn)
1706 0             return NULL;
1707 0         if (!MatchLabel(cx, ts, pn))
1708 0             return NULL;
1709 0         stmt = tc->topStmt;
1710 0         label = pn->pn_atom;
1711 0         if (label) {
1712 0             for (; ; stmt = stmt->down) {
1713 0                 if (!stmt) {
1714 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1715                                                 JSMSG_LABEL_NOT_FOUND);
1716 0                     return NULL;
1717                 }
1718 0                 if (stmt->type == STMT_LABEL && stmt->label == label)
1719 0                     break;
1720             }
1721         } else {
1722 0             for (; ; stmt = stmt->down) {
1723 0                 if (!stmt) {
1724 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1725                                                 JSMSG_TOUGH_BREAK);
1726 0                     return NULL;
1727                 }
1728 0                 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1729 0                     break;
1730             }
1731         }
1732 0         if (label)
1733 0             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1734 0         break;
1735
1736       case TOK_CONTINUE:
1737 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1738 0         if (!pn)
1739 0             return NULL;
1740 0         if (!MatchLabel(cx, ts, pn))
1741 0             return NULL;
1742 0         stmt = tc->topStmt;
1743 0         label = pn->pn_atom;
1744 0         if (label) {
1745 0             for (stmt2 = NULL; ; stmt = stmt->down) {
1746 0                 if (!stmt) {
1747 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1748                                                 JSMSG_LABEL_NOT_FOUND);
1749 0                     return NULL;
1750                 }
1751 0                 if (stmt->type == STMT_LABEL) {
1752 0                     if (stmt->label == label) {
1753 0                         if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1754 0                             js_ReportCompileErrorNumber(cx, ts, NULL,
1755                                                         JSREPORT_ERROR,
1756                                                         JSMSG_BAD_CONTINUE);
1757 0                             return NULL;
1758                         }
1759 0                         break;
1760                     }
1761                 } else {
1762 0                     stmt2 = stmt;
1763                 }
1764             }
1765         } else {
1766 0             for (; ; stmt = stmt->down) {
1767 0                 if (!stmt) {
1768 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1769                                                 JSMSG_BAD_CONTINUE);
1770 0                     return NULL;
1771                 }
1772 0                 if (STMT_IS_LOOP(stmt))
1773 0                     break;
1774             }
1775         }
1776 0         if (label)
1777 0             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1778 0         break;
1779
1780       case TOK_WITH:
1781 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1782 0         if (!pn)
1783 0             return NULL;
1784 0         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1785 0         pn2 = Expr(cx, ts, tc);
1786 0         if (!pn2)
1787 0             return NULL;
1788 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1789 0         pn->pn_left = pn2;
1790
1791 0         js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1792 0         pn2 = Statement(cx, ts, tc);
1793 0         if (!pn2)
1794 0             return NULL;
1795 0         js_PopStatement(tc);
1796
1797         /* Deprecate after parsing, in case of WERROR option. */
1798 0         if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1799                                          JSREPORT_WARNING | JSREPORT_STRICT,
1800                                          JSMSG_DEPRECATED_USAGE,
1801                                          js_with_statement_str)) {
1802 0             return NULL;
1803         }
1804
1805 0         pn->pn_pos.end = pn2->pn_pos.end;
1806 0         pn->pn_right = pn2;
1807 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1808 0         return pn;
1809
1810       case TOK_VAR:
1811 0         pn = Variables(cx, ts, tc);
1812 0         if (!pn)
1813 0             return NULL;
1814
1815         /* Tell js_EmitTree to generate a final POP. */
1816 0         pn->pn_extra = JS_TRUE;
1817 0         break;
1818
1819       case TOK_RETURN:
1820 0         if (!(tc->flags & TCF_IN_FUNCTION)) {
1821 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1822                                         JSMSG_BAD_RETURN);
1823 0             return NULL;
1824         }
1825 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1826 0         if (!pn)
1827 0             return NULL;
1828
1829         /* This is ugly, but we don't want to require a semicolon. */
1830 0         ts->flags |= TSF_REGEXP;
1831 0         tt = js_PeekTokenSameLine(cx, ts);
1832 0         ts->flags &= ~TSF_REGEXP;
1833 0         if (tt == TOK_ERROR)
1834 0             return NULL;
1835
1836 0         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1837 0             pn2 = Expr(cx, ts, tc);
1838 0             if (!pn2)
1839 0                 return NULL;
1840 0             tc->flags |= TCF_RETURN_EXPR;
1841 0             pn->pn_pos.end = pn2->pn_pos.end;
1842 0             pn->pn_kid = pn2;
1843         } else {
1844 0             tc->flags |= TCF_RETURN_VOID;
1845 0             pn->pn_kid = NULL;
1846         }
1847
1848 0         if (JS_HAS_STRICT_OPTION(cx) &&
1849             (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
1850             /*
1851              * We must be in a frame with a non-native function, because
1852              * we're compiling one.
1853              */
1854 0             if (!ReportNoReturnValue(cx, ts))
1855 0                 return NULL;
1856         }
1857 0         break;
1858
1859       case TOK_LC:
1860 0         js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1861 0         pn = Statements(cx, ts, tc);
1862 0         if (!pn)
1863 0             return NULL;
1864
1865 0         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
1866 0         js_PopStatement(tc);
1867 0         return pn;
1868
1869       case TOK_EOL:
1870       case TOK_SEMI:
1871 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1872 0         if (!pn)
1873 0             return NULL;
1874 0         pn->pn_type = TOK_SEMI;
1875 0         pn->pn_kid = NULL;
1876 0         return pn;
1877
1878 #if JS_HAS_DEBUGGER_KEYWORD
1879       case TOK_DEBUGGER:
1880 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1881 0         if (!pn)
1882 0             return NULL;
1883 0         pn->pn_type = TOK_DEBUGGER;
1884 0         tc->flags |= TCF_FUN_HEAVYWEIGHT;
1885 0         break;
1886 #endif /* JS_HAS_DEBUGGER_KEYWORD */
1887
1888       case TOK_ERROR:
1889 0         return NULL;
1890
1891       default:
1892 0         js_UngetToken(ts);
1893 0         pn2 = Expr(cx, ts, tc);
1894 0         if (!pn2)
1895 0             return NULL;
1896
1897 0         if (js_PeekToken(cx, ts) == TOK_COLON) {
1898 0             if (pn2->pn_type != TOK_NAME) {
1899 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1900                                             JSMSG_BAD_LABEL);
1901 0                 return NULL;
1902             }
1903 0             label = pn2->pn_atom;
1904 0             for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1905 0                 if (stmt->type == STMT_LABEL && stmt->label == label) {
1906 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1907                                                 JSMSG_DUPLICATE_LABEL);
1908 0                     return NULL;
1909                 }
1910             }
1911 0             (void) js_GetToken(cx, ts);
1912
1913             /* Push a label struct and parse the statement. */
1914 0             js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
1915 0             stmtInfo.label = label;
1916 0             pn = Statement(cx, ts, tc);
1917 0             if (!pn)
1918 0                 return NULL;
1919
1920             /* Pop the label, set pn_expr, and return early. */
1921 0             js_PopStatement(tc);
1922 0             pn2->pn_type = TOK_COLON;
1923 0             pn2->pn_pos.end = pn->pn_pos.end;
1924 0             pn2->pn_expr = pn;
1925 0             return pn2;
1926         }
1927
1928 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1929 0         if (!pn)
1930 0             return NULL;
1931 0         pn->pn_type = TOK_SEMI;
1932 0         pn->pn_pos = pn2->pn_pos;
1933 0         pn->pn_kid = pn2;
1934 0         break;
1935     }
1936
1937     /* Check termination of this primitive statement. */
1938 0     if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
1939 0         tt = js_PeekTokenSameLine(cx, ts);
1940 0         if (tt == TOK_ERROR)
1941 0             return NULL;
1942 0         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1943 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1944                                         JSMSG_SEMI_BEFORE_STMNT);
1945 0             return NULL;
1946         }
1947     }
1948
1949 0     (void) js_MatchToken(cx, ts, TOK_SEMI);
1950 0     return pn;
1951 }
1952
1953 static JSParseNode *
1954 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1955 0 {
1956 0     JSParseNode *pn, *pn2;
1957 0     JSObject *obj, *pobj;
1958 0     JSStackFrame *fp;
1959 0     JSFunction *fun;
1960 0     JSClass *clasp;
1961 0     JSPropertyOp getter, setter, currentGetter, currentSetter;
1962 0     JSAtom *atom;
1963 0     JSAtomListElement *ale;
1964 0     JSOp prevop;
1965 0     JSProperty *prop;
1966 0     JSScopeProperty *sprop;
1967 0     JSBool ok;
1968
1969     /*
1970      * The tricky part of this code is to create special parsenode opcodes for
1971      * getting and setting variables (which will be stored as special slots in
1972      * the frame).  The complex special case is an eval() inside a function.
1973      * If the evaluated string references variables in the enclosing function,
1974      * then we need to generate the special variable opcodes.  We determine
1975      * this by looking up the variable id in the current variable scope.
1976      */
1977 0     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
1978 0     pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1979 0     if (!pn)
1980 0         return NULL;
1981 0     pn->pn_op = CURRENT_TOKEN(ts).t_op;
1982 0     pn->pn_extra = JS_FALSE;            /* assume no JSOP_POP needed */
1983 0     PN_INIT_LIST(pn);
1984
1985     /*
1986      * Skip eval and debugger frames when looking for the function whose code
1987      * is being compiled.  If we are called from FunctionBody, TCF_IN_FUNCTION
1988      * will be set in tc->flags, and we can be sure fp->fun is the function to
1989      * use.  But if a function calls eval, the string argument is treated as a
1990      * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
1991      *
1992      * What's more, when the following code is reached from eval, cx->fp->fun
1993      * is eval's JSFunction (a native function), so we need to skip its frame.
1994      * We should find the scripted caller's function frame just below it, but
1995      * we code a loop out of paranoia.
1996      */
1997 0     for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
1998 0         continue;
1999 0     obj = fp->varobj;
2000 0     fun = fp->fun;
2001 0     clasp = OBJ_GET_CLASS(cx, obj);
2002 0     if (fun && clasp == &js_FunctionClass) {
2003         /* We are compiling code inside a function */
2004 0         getter = js_GetLocalVariable;
2005 0         setter = js_SetLocalVariable;
2006 0     } else if (fun && clasp == &js_CallClass) {
2007         /* We are compiling code from an eval inside a function */
2008 0         getter = js_GetCallVariable;
2009 0         setter = js_SetCallVariable;
2010     } else {
2011 0         getter = clasp->getProperty;
2012 0         setter = clasp->setProperty;
2013     }
2014
2015 0     ok = JS_TRUE;
2016 0     do {
2017 0         currentGetter = getter;
2018 0         currentSetter = setter;
2019 0         MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2020 0         atom = CURRENT_TOKEN(ts).t_atom;
2021
2022 0         ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2023 0         if (ale) {
2024 0             prevop = ALE_JSOP(ale);
2025 0             if (JS_HAS_STRICT_OPTION(cx) ||
2026                 pn->pn_op == JSOP_DEFCONST ||
2027                 prevop == JSOP_DEFCONST) {
2028 0                 const char *name = js_AtomToPrintableString(cx, atom);
2029 0                 if (!name ||
2030                     !js_ReportCompileErrorNumber(cx, ts, NULL,
2031                                                  (pn->pn_op != JSOP_DEFCONST &&
2032                                                   prevop != JSOP_DEFCONST)
2033                                                  ? JSREPORT_WARNING |
2034                                                    JSREPORT_STRICT
2035                                                  : JSREPORT_ERROR,
2036                                                  JSMSG_REDECLARED_VAR,
2037                                                  (prevop == JSOP_DEFFUN ||
2038                                                   prevop == JSOP_CLOSURE)
2039                                                  ? js_function_str
2040                                                  : (prevop == JSOP_DEFCONST)
2041                                                  ? js_const_str
2042                                                  : js_var_str,
2043                                                  name)) {
2044 0                     return NULL;
2045                 }
2046             }
2047 0             if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2048 0                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2049         } else {
2050 0             ale = js_IndexAtom(cx, atom, &tc->decls);
2051 0             if (!ale)
2052 0                 return NULL;
2053         }
2054 0         ALE_SET_JSOP(ale, pn->pn_op);
2055
2056 0         pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2057 0         if (!pn2)
2058 0             return NULL;
2059 0         pn2->pn_op = JSOP_NAME;
2060 0         pn2->pn_atom = atom;
2061 0         pn2->pn_expr = NULL;
2062 0         pn2->pn_slot = -1;
2063 0         pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2064                         ? JSPROP_ENUMERATE | JSPROP_PERMANENT |
2065                           JSPROP_READONLY
2066                         : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2067 0         PN_APPEND(pn, pn2);
2068
2069 0         if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
2070 0             return NULL;
2071 0         if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2072 0             sprop = (JSScopeProperty *)prop;
2073 0             if (sprop->getter == js_GetArgument) {
2074 0                 const char *name = js_AtomToPrintableString(cx, atom);
2075 0                 if (!name) {
2076 0                     ok = JS_FALSE;
2077 0                 } else if (pn->pn_op == JSOP_DEFCONST) {
2078 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2079                                                 JSMSG_REDECLARED_PARAM,
2080                                                 name);
2081 0                     ok = JS_FALSE;
2082                 } else {
2083 0                     currentGetter = js_GetArgument;
2084 0                     currentSetter = js_SetArgument;
2085 0                     ok = js_ReportCompileErrorNumber(cx, ts, NULL,
2086                                                      JSREPORT_WARNING |
2087                                                      JSREPORT_STRICT,
2088                                                      JSMSG_VAR_HIDES_ARG,
2089                                                      name);
2090                 }
2091             } else {
2092 0                 if (fun) {
2093                     /* Not an argument, must be a redeclared local var. */
2094 0                     if (clasp == &js_FunctionClass) {
2095 0                         JS_ASSERT(sprop->getter == js_GetLocalVariable);
2096 0                         JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2097                                   sprop->shortid < fun->nvars);
2098 0                     } else if (clasp == &js_CallClass) {
2099 0                         if (sprop->getter == js_GetCallVariable) {
2100                             /*
2101                              * Referencing a variable introduced by a var
2102                              * statement in the enclosing function. Check
2103                              * that the slot number we have is in range.
2104                              */
2105 0                             JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2106                                       sprop->shortid < fun->nvars);
2107                         } else {
2108                             /*
2109                              * A variable introduced through another eval:
2110                              * don't use the special getters and setters
2111                              * since we can't allocate a slot in the frame.
2112                              */
2113 0                             currentGetter = sprop->getter;
2114 0                             currentSetter = sprop->setter;
2115                         }
2116                     }
2117
2118                     /* Override the old getter and setter, to handle eval. */
2119 0                     sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2120                                                          0, sprop->attrs,
2121                                                          currentGetter,
2122                                                          currentSetter);
2123 0                     if (!sprop)
2124 0                         ok = JS_FALSE;
2125                 }
2126             }
2127         } else {
2128             /*
2129              * Property not found in current variable scope: we have not seen
2130              * this variable before.  Define a new local variable by adding a
2131              * property to the function's scope, allocating one slot in the
2132              * function's frame.  Global variables and any locals declared in
2133              * with statement bodies are handled at runtime, by script prolog
2134              * JSOP_DEFVAR bytecodes generated for slot-less vars.
2135              */
2136 0             sprop = NULL;
2137 0             if (prop) {
2138 0                 OBJ_DROP_PROPERTY(cx, pobj, prop);
2139 0                 prop = NULL;
2140             }
2141 0             if (currentGetter == js_GetCallVariable) {
2142                 /* Can't increase fun->nvars in an active frame! */
2143 0                 currentGetter = clasp->getProperty;
2144 0                 currentSetter = clasp->setProperty;
2145             }
2146 0             if (currentGetter == js_GetLocalVariable &&
2147                 atom != cx->runtime->atomState.argumentsAtom &&
2148                 fp->scopeChain == obj &&
2149                 !js_InWithStatement(tc)) {
2150 0                 if (!js_AddNativeProperty(cx, obj, (jsid)atom,
2151                                           currentGetter, currentSetter,
2152                                           SPROP_INVALID_SLOT,
2153                                           pn2->pn_attrs | JSPROP_SHARED,
2154                                           SPROP_HAS_SHORTID, fun->nvars)) {
2155 0                     ok = JS_FALSE;
2156                 }
2157 0                 fun->nvars++;
2158             }
2159         }
2160
2161 0         if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2162 0             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2163 0                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2164                                             JSMSG_BAD_VAR_INIT);
2165 0                 ok = JS_FALSE;
2166             } else {
2167 0                 pn2->pn_expr = AssignExpr(cx, ts, tc);
2168 0                 if (!pn2->pn_expr) {
2169 0                     ok = JS_FALSE;
2170                 } else {
2171 0                     pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2172                                  ? JSOP_SETCONST
2173                                  : JSOP_SETNAME;
2174 0                     if (atom == cx->runtime->atomState.argumentsAtom)
2175 0                         tc->flags |= TCF_FUN_HEAVYWEIGHT;
2176                 }
2177             }
2178         }
2179
2180 0         if (prop)
2181 0             OBJ_DROP_PROPERTY(cx, pobj, prop);
2182 0         if (!ok)
2183 0             return NULL;
2184 0     } while (js_MatchToken(cx, ts, TOK_COMMA));
2185
2186 0     pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2187 0     return pn;
2188 }
2189
2190 static JSParseNode *
2191 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2192 0 {
2193 0     JSParseNode *pn, *pn2;
2194
2195 0     pn = AssignExpr(cx, ts, tc);
2196 0     if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2197 0         pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2198 0         if (!pn2)
2199 0             return NULL;
2200 0         pn2->pn_pos.begin = pn->pn_pos.begin;
2201 0         PN_INIT_LIST_1(pn2, pn);
2202 0         pn = pn2;
2203 0         do {
2204 0             pn2 = AssignExpr(cx, ts, tc);
2205 0             if (!pn2)
2206 0                 return NULL;
2207 0             PN_APPEND(pn, pn2);
2208 0         } while (js_MatchToken(cx, ts, TOK_COMMA));
2209 0         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2210     }
2211 0     return pn;
2212 }
2213
2214 static JSParseNode *
2215 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2216 0 {
2217 0     JSParseNode *pn, *pn2;
2218 0     JSTokenType tt;
2219 0     JSOp op;
2220
2221 0     CHECK_RECURSION();
2222
2223 0     pn = CondExpr(cx, ts, tc);
2224 0     if (!pn)
2225 0         return NULL;
2226
2227 0     tt = js_GetToken(cx, ts);
2228 #if JS_HAS_GETTER_SETTER
2229 0     if (tt == TOK_NAME) {
2230 0         tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2231 0         if (tt == TOK_ERROR)
2232 0             return NULL;
2233     }
2234 #endif
2235 0     if (tt != TOK_ASSIGN) {
2236 0         js_UngetToken(ts);
2237 0         return pn;
2238     }
2239
2240 0     op = CURRENT_TOKEN(ts).t_op;
2241 0     for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2242 0         continue;
2243 0     switch (pn2->pn_type) {
2244       case TOK_NAME:
2245 0         pn2->pn_op = JSOP_SETNAME;
2246 0         if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2247 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
2248 0         break;
2249       case TOK_DOT:
2250 0         pn2->pn_op = JSOP_SETPROP;
2251 0         break;
2252       case TOK_LB:
2253 0         pn2->pn_op = JSOP_SETELEM;
2254 0         break;
2255 #if JS_HAS_LVALUE_RETURN
2256       case TOK_LP:
2257 0         pn2->pn_op = JSOP_SETCALL;
2258 0         break;
2259 #endif
2260       default:
2261 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2262                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
2263 0         return NULL;
2264     }
2265 0     pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2266 0     return pn;
2267 }
2268
2269 static JSParseNode *
2270 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2271 0 {
2272 0     JSParseNode *pn, *pn1, *pn2, *pn3;
2273 #if JS_HAS_IN_OPERATOR
2274 0     uintN oldflags;
2275 #endif /* JS_HAS_IN_OPERATOR */
2276
2277 0     pn = OrExpr(cx, ts, tc);
2278 0     if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2279 0         pn1 = pn;
2280 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2281 0         if (!pn)
2282 0             return NULL;
2283 #if JS_HAS_IN_OPERATOR
2284         /*
2285          * Always accept the 'in' operator in the middle clause of a ternary,
2286          * where it's unambiguous, even if we might be parsing the init of a
2287          * for statement.
2288          */
2289 0         oldflags = tc->flags;
2290 0         tc->flags &= ~TCF_IN_FOR_INIT;
2291 #endif /* JS_HAS_IN_OPERATOR */
2292 0         pn2 = AssignExpr(cx, ts, tc);
2293 #if JS_HAS_IN_OPERATOR
2294 0         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2295 #endif /* JS_HAS_IN_OPERATOR */
2296
2297 0         if (!pn2)
2298 0             return NULL;
2299 0         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2300 0         pn3 = AssignExpr(cx, ts, tc);
2301 0         if (!pn3)
2302 0             return NULL;
2303 0         pn->pn_pos.begin = pn1->pn_pos.begin;
2304 0         pn->pn_pos.end = pn3->pn_pos.end;
2305 0         pn->pn_kid1 = pn1;
2306 0         pn->pn_kid2 = pn2;
2307 0         pn->pn_kid3 = pn3;
2308     }
2309 0     return pn;
2310 }
2311
2312 static JSParseNode *
2313 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2314 0 {
2315 0     JSParseNode *pn;
2316
2317 0     pn = AndExpr(cx, ts, tc);
2318 0     if (pn && js_MatchToken(cx, ts, TOK_OR))
2319 0         pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2320 0     return pn;
2321 }
2322
2323 static JSParseNode *
2324 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2325 0 {
2326 0     JSParseNode *pn;
2327
2328 0     pn = BitOrExpr(cx, ts, tc);
2329 0     if (pn && js_MatchToken(cx, ts, TOK_AND))
2330 0         pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2331 0     return pn;
2332 }
2333
2334 static JSParseNode *
2335 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2336 0 {
2337 0     JSParseNode *pn;
2338
2339 0     pn = BitXorExpr(cx, ts, tc);
2340 0     while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2341 0         pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2342                        tc);
2343     }
2344 0     return pn;
2345 }
2346
2347 static JSParseNode *
2348 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2349 0 {
2350 0     JSParseNode *pn;
2351
2352 0     pn = BitAndExpr(cx, ts, tc);
2353 0     while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2354 0         pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2355                        tc);
2356     }
2357 0     return pn;
2358 }
2359
2360 static JSParseNode *
2361 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2362 0 {
2363 0     JSParseNode *pn;
2364
2365 0     pn = EqExpr(cx, ts, tc);
2366 0     while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2367 0         pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2368 0     return pn;
2369 }
2370
2371 static JSParseNode *
2372 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2373 0 {
2374 0     JSParseNode *pn;
2375 0     JSOp op;
2376
2377 0     pn = RelExpr(cx, ts, tc);
2378 0     while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2379 0         op = CURRENT_TOKEN(ts).t_op;
2380 0         pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2381     }
2382 0     return pn;
2383 }
2384
2385 static JSParseNode *
2386 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2387 0 {
2388 0     JSParseNode *pn;
2389 0     JSTokenType tt;
2390 0     JSOp op;
2391 #if JS_HAS_IN_OPERATOR
2392 0     uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2393
2394     /*
2395      * Uses of the in operator in ShiftExprs are always unambiguous,
2396      * so unset the flag that prohibits recognizing it.
2397      */
2398 0     tc->flags &= ~TCF_IN_FOR_INIT;
2399 #endif /* JS_HAS_IN_OPERATOR */
2400
2401 0     pn = ShiftExpr(cx, ts, tc);
2402 0     while (pn &&
2403            (js_MatchToken(cx, ts, TOK_RELOP)
2404 #if JS_HAS_IN_OPERATOR
2405             /*
2406              * Recognize the 'in' token as an operator only if we're not
2407              * currently in the init expr of a for loop.
2408              */
2409             || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2410 #endif /* JS_HAS_IN_OPERATOR */
2411 #if JS_HAS_INSTANCEOF
2412             || js_MatchToken(cx, ts, TOK_INSTANCEOF)
2413 #endif /* JS_HAS_INSTANCEOF */
2414             )) {
2415 0         tt = CURRENT_TOKEN(ts).type;
2416 0         op = CURRENT_TOKEN(ts).t_op;
2417 0         pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2418     }
2419 #if JS_HAS_IN_OPERATOR
2420     /* Restore previous state of inForInit flag. */
2421 0     tc->flags |= inForInitFlag;
2422 #endif /* JS_HAS_IN_OPERATOR */
2423
2424 0     return pn;
2425 }
2426
2427 static JSParseNode *
2428 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2429 0 {
2430 0     JSParseNode *pn;
2431 0     JSOp op;
2432
2433 0     pn = AddExpr(cx, ts, tc);
2434 0     while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2435 0         op = CURRENT_TOKEN(ts).t_op;
2436 0         pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2437     }
2438 0     return pn;
2439 }
2440
2441 static JSParseNode *
2442 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2443 0 {
2444 0     JSParseNode *pn;
2445 0     JSTokenType tt;
2446 0     JSOp op;
2447
2448 0     pn = MulExpr(cx, ts, tc);
2449 0     while (pn &&
2450            (js_MatchToken(cx, ts, TOK_PLUS) ||
2451             js_MatchToken(cx, ts, TOK_MINUS))) {
2452 0         tt = CURRENT_TOKEN(ts).type;
2453 0         op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2454 0         pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2455     }
2456 0     return pn;
2457 }
2458
2459 static JSParseNode *
2460 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2461 0 {
2462 0     JSParseNode *pn;
2463 0     JSTokenType tt;
2464 0     JSOp op;
2465
2466 0     pn = UnaryExpr(cx, ts, tc);
2467 0     while (pn &&
2468            (js_MatchToken(cx, ts, TOK_STAR) ||
2469             js_MatchToken(cx, ts, TOK_DIVOP))) {
2470 0         tt = CURRENT_TOKEN(ts).type;
2471 0         op = CURRENT_TOKEN(ts).t_op;
2472 0         pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2473     }
2474 0     return pn;
2475 }
2476
2477 static JSParseNode *
2478 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2479            const char *name)
2480 0 {
2481 0     while (kid->pn_type == TOK_RP)
2482 0         kid = kid->pn_kid;
2483 0     if (kid->pn_type != TOK_NAME &&
2484         kid->pn_type != TOK_DOT &&
2485 #if JS_HAS_LVALUE_RETURN
2486         (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2487 #endif
2488         kid->pn_type != TOK_LB) {
2489 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2490                                     JSMSG_BAD_OPERAND, name);
2491 0         return NULL;
2492     }
2493 0     pn->pn_kid = kid;
2494 0     return kid;
2495 }
2496
2497 static const char *incop_name_str[] = {"increment", "decrement"};
2498
2499 static JSBool
2500 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2501             JSParseNode *pn, JSParseNode *kid,
2502             JSTokenType tt, JSBool preorder)
2503 0 {
2504 0     JSOp op;
2505
2506 0     kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2507 0     if (!kid)
2508 0         return JS_FALSE;
2509 0     switch (kid->pn_type) {
2510       case TOK_NAME:
2511 0         op = (tt == TOK_INC)
2512              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2513              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2514 0         if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2515 0             tc->flags |= TCF_FUN_HEAVYWEIGHT;
2516 0         break;
2517
2518       case TOK_DOT:
2519 0         op = (tt == TOK_INC)
2520              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2521              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2522 0         break;
2523
2524 #if JS_HAS_LVALUE_RETURN
2525       case TOK_LP:
2526 0         kid->pn_op = JSOP_SETCALL;
2527 #endif
2528       case TOK_LB:
2529 0         op = (tt == TOK_INC)
2530              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2531              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2532 0         break;
2533
2534       default:
2535 0         JS_ASSERT(0);
2536 0         op = JSOP_NOP;
2537     }
2538 0     pn->pn_op = op;
2539 0     return JS_TRUE;
2540 }
2541
2542 static JSParseNode *
2543 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2544 0 {
2545 0     JSTokenType tt;
2546 0     JSParseNode *pn, *pn2;
2547
2548 0     ts->flags |= TSF_REGEXP;
2549 0     tt = js_GetToken(cx, ts);
2550 0     ts->flags &= ~TSF_REGEXP;
2551
2552 0     switch (tt) {
2553       case TOK_UNARYOP:
2554       case TOK_PLUS:
2555       case TOK_MINUS:
2556 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2557 0         if (!pn)
2558 0             return NULL;
2559 0         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
2560 0         pn->pn_op = CURRENT_TOKEN(ts).t_op;
2561 0         pn2 = UnaryExpr(cx, ts, tc);
2562 0         if (!pn2)
2563 0             return NULL;
2564 0         pn->pn_pos.end = pn2->pn_pos.end;
2565 0         pn->pn_kid = pn2;
2566 0         break;
2567
2568       case TOK_INC:
2569       case TOK_DEC:
2570 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2571 0         if (!pn)
2572 0             return NULL;
2573 0         pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2574 0         if (!pn2)
2575 0             return NULL;
2576 0         if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2577 0             return NULL;
2578 0         pn->pn_pos.end = pn2->pn_pos.end;
2579 0         break;
2580
2581       case TOK_DELETE:
2582 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2583 0         if (!pn)
2584 0             return NULL;
2585 0         pn2 = UnaryExpr(cx, ts, tc);
2586 0         if (!pn2)
2587 0             return NULL;
2588 0         pn->pn_pos.end = pn2->pn_pos.end;
2589
2590         /*
2591          * Under ECMA3, deleting any unary expression is valid -- it simply
2592          * returns true. Here we strip off any parentheses.
2593          */
2594 0         while (pn2->pn_type == TOK_RP)
2595 0             pn2 = pn2->pn_kid;
2596 0         pn->pn_kid = pn2;
2597 0         break;
2598
2599       case TOK_ERROR:
2600 0         return NULL;
2601
2602       default:
2603 0         js_UngetToken(ts);
2604 0         pn = MemberExpr(cx, ts, tc, JS_TRUE);
2605 0         if (!pn)
2606 0             return NULL;
2607
2608         /* Don't look across a newline boundary for a postfix incop. */
2609 0         if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2610 0             tt = js_PeekTokenSameLine(cx, ts);
2611 0             if (tt == TOK_INC || tt == TOK_DEC) {
2612 0                 (void) js_GetToken(cx, ts);
2613 0                 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2614 0                 if (!pn2)
2615 0                     return NULL;
2616 0                 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2617 0                     return NULL;
2618 0                 pn2->pn_pos.begin = pn->pn_pos.begin;
2619 0                 pn = pn2;
2620             }
2621         }
2622 0         break;
2623     }
2624 0     return pn;
2625 }
2626
2627 static JSBool
2628 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2629              JSParseNode *listNode)
2630 0 {
2631 0     JSBool matched;
2632
2633 0     ts->flags |= TSF_REGEXP;
2634 0     matched = js_MatchToken(cx, ts, TOK_RP);
2635 0     ts->flags &= ~TSF_REGEXP;
2636 0     if (!matched) {
2637 0         do {
2638 0             JSParseNode *argNode = AssignExpr(cx, ts, tc);
2639 0             if (!argNode)
2640 0                 return JS_FALSE;
2641 0             PN_APPEND(listNode, argNode);
2642 0         } while (js_MatchToken(cx, ts, TOK_COMMA));
2643
2644 0         if (js_GetToken(cx, ts) != TOK_RP) {
2645 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2646                                         JSMSG_PAREN_AFTER_ARGS);
2647 0             return JS_FALSE;
2648         }
2649     }
2650 0     return JS_TRUE;
2651 }
2652
2653 static JSParseNode *
2654 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2655            JSBool allowCallSyntax)
2656 0 {
2657 0     JSParseNode *pn, *pn2, *pn3;
2658 0     JSTokenType tt;
2659
2660 0     CHECK_RECURSION();
2661
2662     /* Check for new expression first. */
2663 0     ts->flags |= TSF_REGEXP;
2664 0     tt = js_PeekToken(cx, ts);
2665 0     ts->flags &= ~TSF_REGEXP;
2666 0     if (tt == TOK_NEW) {
2667 0         (void) js_GetToken(cx, ts);
2668
2669 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2670 0         if (!pn)
2671 0             return NULL;
2672 0         pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2673 0         if (!pn2)
2674 0             return NULL;
2675 0         pn->pn_op = JSOP_NEW;
2676 0         PN_INIT_LIST_1(pn, pn2);
2677 0         pn->pn_pos.begin = pn2->pn_pos.begin;
2678
2679 0         if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2680 0             return NULL;
2681 0         if (pn->pn_count > ARGC_LIMIT) {
2682 0             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2683                                  JSMSG_TOO_MANY_CON_ARGS);
2684 0             return NULL;
2685         }
2686 0         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2687     } else {
2688 0         pn = PrimaryExpr(cx, ts, tc);
2689 0         if (!pn)
2690 0             return NULL;
2691     }
2692
2693 0     while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2694 0         if (tt == TOK_DOT) {
2695 0             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2696 0             if (!pn2)
2697 0                 return NULL;
2698 0             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2699 0             pn2->pn_pos.begin = pn->pn_pos.begin;
2700 0             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2701 0             pn2->pn_op = JSOP_GETPROP;
2702 0             pn2->pn_expr = pn;
2703 0             pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2704 0         } else if (tt == TOK_LB) {
2705 0             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2706 0             if (!pn2)
2707 0                 return NULL;
2708 0             pn3 = Expr(cx, ts, tc);
2709 0             if (!pn3)
2710 0                 return NULL;
2711
2712 0             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
2713 0             pn2->pn_pos.begin = pn->pn_pos.begin;
2714 0             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2715
2716             /* Optimize o['p'] to o.p by rewriting pn2. */
2717 0             if (pn3->pn_type == TOK_STRING) {
2718 0                 pn2->pn_type = TOK_DOT;
2719 0                 pn2->pn_op = JSOP_GETPROP;
2720 0                 pn2->pn_arity = PN_NAME;
2721 0                 pn2->pn_expr = pn;
2722 0                 pn2->pn_atom = pn3->pn_atom;
2723             } else {
2724 0                 pn2->pn_op = JSOP_GETELEM;
2725 0                 pn2->pn_left = pn;
2726 0                 pn2->pn_right = pn3;
2727             }
2728 0         } else if (allowCallSyntax && tt == TOK_LP) {
2729 0             pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2730 0             if (!pn2)
2731 0                 return NULL;
2732
2733             /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
2734 0             pn2->pn_op = JSOP_CALL;
2735 0             if (pn->pn_op == JSOP_NAME &&
2736                 pn->pn_atom == cx->runtime->atomState.evalAtom) {
2737 0                 pn2->pn_op = JSOP_EVAL;
2738 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2739             }
2740
2741 0             PN_INIT_LIST_1(pn2, pn);
2742 0             pn2->pn_pos.begin = pn->pn_pos.begin;
2743
2744 0             if (!ArgumentList(cx, ts, tc, pn2))
2745 0                 return NULL;
2746 0             if (pn2->pn_count > ARGC_LIMIT) {
2747 0                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2748                                      JSMSG_TOO_MANY_FUN_ARGS);
2749 0                 return NULL;
2750             }
2751 0             pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end;
2752         } else {
2753 0             js_UngetToken(ts);
2754 0             return pn;
2755         }
2756
2757 0         pn = pn2;
2758     }
2759 0     if (tt == TOK_ERROR)
2760 0         return NULL;
2761 0     return pn;
2762 }
2763
2764 static JSParseNode *
2765 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2766 0 {
2767 0     JSTokenType tt;
2768 0     JSParseNode *pn, *pn2, *pn3;
2769 0     char *badWord;
2770 #if JS_HAS_GETTER_SETTER
2771 0     JSAtom *atom;
2772 0     JSRuntime *rt;
2773 #endif
2774
2775 #if JS_HAS_SHARP_VARS
2776 0     JSParseNode *defsharp;
2777 0     JSBool notsharp;
2778
2779 0     defsharp = NULL;
2780 0     notsharp = JS_FALSE;
2781   again:
2782     /*
2783      * Control flows here after #n= is scanned.  If the following primary is
2784      * not valid after such a "sharp variable" definition, the token type case
2785      * should set notsharp.
2786      */
2787 #endif
2788
2789 0     CHECK_RECURSION();
2790
2791 0     ts->flags |= TSF_REGEXP;
2792 0     tt = js_GetToken(cx, ts);
2793 0     ts->flags &= ~TSF_REGEXP;
2794
2795 #if JS_HAS_GETTER_SETTER
2796 0     if (tt == TOK_NAME) {
2797 0         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2798 0         if (tt == TOK_ERROR)
2799 0             return NULL;
2800     }
2801 #endif
2802
2803 0     switch (tt) {
2804 #if JS_HAS_LEXICAL_CLOSURE
2805       case TOK_FUNCTION:
2806 0         pn = FunctionExpr(cx, ts, tc);
2807 0         if (!pn)
2808 0             return NULL;
2809 0         break;
2810 #endif
2811
2812 #if JS_HAS_INITIALIZERS
2813       case TOK_LB:
2814       {
2815 0         JSBool matched;
2816 0         jsuint atomIndex;
2817
2818 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2819 0         if (!pn)
2820 0             return NULL;
2821 0         pn->pn_type = TOK_RB;
2822 0         pn->pn_extra = JS_FALSE;
2823
2824 #if JS_HAS_SHARP_VARS
2825 0         if (defsharp) {
2826 0             PN_INIT_LIST_1(pn, defsharp);
2827 0             defsharp = NULL;
2828         } else
2829 #endif
2830 0             PN_INIT_LIST(pn);
2831
2832 0         ts->flags |= TSF_REGEXP;
2833 0         matched = js_MatchToken(cx, ts, TOK_RB);
2834 0         ts->flags &= ~TSF_REGEXP;
2835 0         if (!matched) {
2836 0             for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
2837 0                 ts->flags |= TSF_REGEXP;
2838 0                 tt = js_PeekToken(cx, ts);
2839 0                 ts->flags &= ~TSF_REGEXP;
2840 0                 if (tt == TOK_RB) {
2841 0                     pn->pn_extra = JS_TRUE;
2842 0                     break;
2843                 }
2844
2845 0                 if (tt == TOK_COMMA) {
2846                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
2847 0                     js_MatchToken(cx, ts, TOK_COMMA);
2848 0                     pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2849                 } else {
2850 0                     pn2 = AssignExpr(cx, ts, tc);
2851                 }
2852 0                 if (!pn2)
2853 0                     return NULL;
2854 0                 PN_APPEND(pn, pn2);
2855
2856 0                 if (tt != TOK_COMMA) {
2857                     /* If we didn't already match TOK_COMMA in above case. */
2858 0                     if (!js_MatchToken(cx, ts, TOK_COMMA))
2859 0                         break;
2860                 }
2861             }
2862
2863 0             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
2864         }
2865 0         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2866 0         return pn;
2867       }
2868
2869       case TOK_LC:
2870 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2871 0         if (!pn)
2872 0             return NULL;
2873 0         pn->pn_type = TOK_RC;
2874
2875 #if JS_HAS_SHARP_VARS
2876 0         if (defsharp) {
2877 0             PN_INIT_LIST_1(pn, defsharp);
2878 0             defsharp = NULL;
2879         } else
2880 #endif
2881 0             PN_INIT_LIST(pn);
2882
2883 0         if (!js_MatchToken(cx, ts, TOK_RC)) {
2884 0             do {
2885 0                 JSOp op;
2886
2887 0                 tt = js_GetToken(cx, ts);
2888 0                 switch (tt) {
2889                   case TOK_NUMBER:
2890 0                     pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2891 0                     if (pn3)
2892 0                         pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
2893 0                     break;
2894                   case TOK_NAME:
2895 #if JS_HAS_GETTER_SETTER
2896 0                     atom = CURRENT_TOKEN(ts).t_atom;
2897 0                     rt = cx->runtime;
2898 0                     if (atom == rt->atomState.getAtom ||
2899                         atom == rt->atomState.setAtom) {
2900 0                         op = (atom == rt->atomState.getAtom)
2901                              ? JSOP_GETTER
2902                              : JSOP_SETTER;
2903 0                         if (js_MatchToken(cx, ts, TOK_NAME)) {
2904 0                             pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,
2905                                                tc);
2906 0                             if (!pn3)
2907 0                                 return NULL;
2908 0                             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2909 0                             pn3->pn_expr = NULL;
2910
2911                             /* We have to fake a 'function' token here. */
2912 0                             CURRENT_TOKEN(ts).t_op = JSOP_NOP;
2913 0                             CURRENT_TOKEN(ts).type = TOK_FUNCTION;
2914 0                             pn2 = FunctionExpr(cx, ts, tc);
2915 0                             pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
2916 0                             goto skip;
2917                         }
2918                     }
2919                     /* else fall thru ... */
2920 #endif
2921                   case TOK_STRING:
2922 0                     pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2923 0                     if (pn3)
2924 0                         pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2925 0                     break;
2926                   case TOK_RC:
2927 0                     if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2928                                                      JSREPORT_WARNING |
2929                                                      JSREPORT_STRICT,
2930                                                      JSMSG_TRAILING_COMMA)) {
2931 0                         return NULL;
2932                     }
2933 0                     goto end_obj_init;
2934                   default:
2935 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2936                                                 JSMSG_BAD_PROP_ID);
2937 0                     return NULL;
2938                 }
2939
2940 0                 tt = js_GetToken(cx, ts);
2941 #if JS_HAS_GETTER_SETTER
2942 0                 if (tt == TOK_NAME) {
2943 0                     tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
2944 0                     if (tt == TOK_ERROR)
2945 0                         return NULL;
2946                 }
2947 #endif
2948 0                 if (tt != TOK_COLON) {
2949 0                     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2950                                                 JSMSG_COLON_AFTER_ID);
2951 0                     return NULL;
2952                 }
2953 0                 op = CURRENT_TOKEN(ts).t_op;
2954 0                 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
2955                                 tc);
2956 #if JS_HAS_GETTER_SETTER
2957               skip:
2958 #endif
2959 0                 if (!pn2)
2960 0                     return NULL;
2961 0                 PN_APPEND(pn, pn2);
2962 0             } while (js_MatchToken(cx, ts, TOK_COMMA));
2963
2964 0             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
2965         }
2966       end_obj_init:
2967 0         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2968 0         return pn;
2969
2970 #if JS_HAS_SHARP_VARS
2971       case TOK_DEFSHARP:
2972 0         if (defsharp)
2973 0             goto badsharp;
2974 0         defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2975 0         if (!defsharp)
2976 0             return NULL;
2977 0         defsharp->pn_kid = NULL;
2978 0         defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
2979 0         goto again;
2980
2981       case TOK_USESHARP:
2982         /* Check for forward/dangling references at runtime, to allow eval. */
2983 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2984 0         if (!pn)
2985 0             return NULL;
2986 0         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
2987 0         notsharp = JS_TRUE;
2988 0         break;
2989 #endif /* JS_HAS_SHARP_VARS */
2990 #endif /* JS_HAS_INITIALIZERS */
2991
2992       case TOK_LP:
2993       {
2994 #if JS_HAS_IN_OPERATOR
2995 0         uintN oldflags;
2996 #endif
2997 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2998 0         if (!pn)
2999 0             return NULL;
3000 #if JS_HAS_IN_OPERATOR
3001         /*
3002          * Always accept the 'in' operator in a parenthesized expression,
3003          * where it's unambiguous, even if we might be parsing the init of a
3004          * for statement.
3005          */
3006 0         oldflags = tc->flags;
3007 0         tc->flags &= ~TCF_IN_FOR_INIT;
3008 #endif
3009 0         pn2 = Expr(cx, ts, tc);
3010 #if JS_HAS_IN_OPERATOR
3011 0         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3012 #endif
3013 0         if (!pn2)
3014 0             return NULL;
3015
3016 0         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
3017 0         pn->pn_type = TOK_RP;
3018 0         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3019 0         pn->pn_kid = pn2;
3020 0         break;
3021       }
3022
3023       case TOK_STRING:
3024 #if JS_HAS_SHARP_VARS
3025 0         notsharp = JS_TRUE;
3026 #endif
3027         /* FALL THROUGH */
3028       case TOK_NAME:
3029       case TOK_OBJECT:
3030 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3031 0         if (!pn)
3032 0             return NULL;
3033 0         pn->pn_op = CURRENT_TOKEN(ts).t_op;
3034 0         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3035 0         if (tt == TOK_NAME) {
3036 0             pn->pn_arity = PN_NAME;
3037 0             pn->pn_expr = NULL;
3038 0             pn->pn_slot = -1;
3039 0             pn->pn_attrs = 0;
3040
3041             /* Unqualified __parent__ and __proto__ uses require activations. */
3042 0             if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
3043                 pn->pn_atom == cx->runtime->atomState.protoAtom) {
3044 0                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3045             }
3046         }
3047 0         break;
3048
3049       case TOK_NUMBER:
3050 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3051 0         if (!pn)
3052 0             return NULL;
3053 0         pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
3054 #if JS_HAS_SHARP_VARS
3055 0         notsharp = JS_TRUE;
3056 #endif
3057 0         break;
3058
3059       case TOK_PRIMARY:
3060 0         pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3061 0         if (!pn)
3062 0             return NULL;
3063 0         pn->pn_op = CURRENT_TOKEN(ts).t_op;
3064 #if JS_HAS_SHARP_VARS
3065 0         notsharp = JS_TRUE;
3066 #endif
3067 0         break;
3068
3069 #if !JS_HAS_EXPORT_IMPORT
3070       case TOK_EXPORT:
3071       case TOK_IMPORT:
3072 #endif
3073       case TOK_RESERVED:
3074 0         badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,
3075                                    (size_t) CURRENT_TOKEN(ts).pos.end.index
3076                                           - CURRENT_TOKEN(ts).pos.begin.index);
3077 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3078                                     JSMSG_RESERVED_ID, badWord);
3079 0         JS_free(cx, badWord);
3080 0         return NULL;
3081
3082       case TOK_ERROR:
3083         /* The scanner or one of its subroutines reported the error. */
3084 0         return NULL;
3085
3086       default:
3087 0         js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3088                                     JSMSG_SYNTAX_ERROR);
3089 0         return NULL;
3090     }
3091
3092 #if JS_HAS_SHARP_VARS
3093 0     if (defsharp) {
3094 0         if (notsharp) {
3095   badsharp:
3096 0             js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3097                                         JSMSG_BAD_SHARP_VAR_DEF);
3098 0             return NULL;
3099         }
3100 0         defsharp->pn_kid = pn;
3101 0         return defsharp;
3102     }
3103 #endif
3104 0     return pn;
3105 }
3106
3107 static JSBool
3108 ContainsVarStmt(JSParseNode *pn)
3109 0 {
3110 0     JSParseNode *pn2;
3111
3112 0     if (!pn)
3113 0         return JS_FALSE;
3114 0     switch (pn->pn_arity) {
3115       case PN_LIST:
3116 0         if (pn->pn_type == TOK_VAR)
3117 0             return JS_TRUE;
3118 0         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3119 0             if (ContainsVarStmt(pn2))
3120 0                 return JS_TRUE;
3121         }
3122 0         break;
3123       case PN_TERNARY:
3124 0         return ContainsVarStmt(pn->pn_kid1) ||
3125                ContainsVarStmt(pn->pn_kid2) ||
3126                ContainsVarStmt(pn->pn_kid3);
3127       case PN_BINARY:
3128         /*
3129          * Limit recursion if pn is a binary expression, which can't contain a
3130          * var statement.
3131          */
3132 0         if (pn->pn_op != JSOP_NOP)
3133 0             return JS_FALSE;
3134 0         return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
3135       case PN_UNARY:
3136 0         if (pn->pn_op != JSOP_NOP)
3137 0             return JS_FALSE;
3138 0         return ContainsVarStmt(pn->pn_kid);
3139       default:;
3140     }
3141 0     return JS_FALSE;
3142 }
3143
3144 /*
3145  * Fold from one constant type to another.
3146  * XXX handles only strings and numbers for now
3147  */
3148 static JSBool
3149 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
3150 0 {
3151 0     if (pn->pn_type != type) {
3152 0         switch (type) {
3153           case TOK_NUMBER:
3154 0             if (pn->pn_type == TOK_STRING) {
3155 0                 jsdouble d;
3156 0                 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
3157 0                     return JS_FALSE;
3158 0                 pn->pn_dval = d;
3159 0                 pn->pn_type = TOK_NUMBER;
3160 0                 pn->pn_op = JSOP_NUMBER;
3161             }
3162 0             break;
3163
3164           case TOK_STRING:
3165 0             if (pn->pn_type == TOK_NUMBER) {
3166 0                 JSString *str = js_NumberToString(cx, pn->pn_dval);
3167 0                 if (!str)
3168 0                     return JS_FALSE;
3169 0                 pn->pn_atom = js_AtomizeString(cx, str, 0);
3170 0                 if (!pn->pn_atom)
3171 0                     return JS_FALSE;
3172 0                 pn->pn_type = TOK_STRING;
3173 0                 pn->pn_op = JSOP_STRING;
3174             }
3175             break;
3176
3177           default:;
3178         }
3179     }
3180 0     return JS_TRUE;
3181 }
3182
3183 /*
3184  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
3185  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
3186  * a successful call to this function.
3187  */
3188 static JSBool
3189 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
3190                   JSParseNode *pn, JSTreeContext *tc)
3191 0 {
3192 0     jsdouble d, d2;
3193 0     int32 i, j;
3194 0     uint32 u;
3195
3196 0     JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
3197 0     d = pn1->pn_dval;
3198 0     d2 = pn2->pn_dval;
3199 0     switch (op) {
3200       case JSOP_LSH:
3201       case JSOP_RSH:
3202 0         if (!js_DoubleToECMAInt32(cx, d, &i))
3203 0             return JS_FALSE;
3204 0         if (!js_DoubleToECMAInt32(cx, d2, &j))
3205 0             return JS_FALSE;
3206 0         j &= 31;
3207 0         d = (op == JSOP_LSH) ? i << j : i >> j;
3208 0         break;
3209
3210       case JSOP_URSH:
3211 0         if (!js_DoubleToECMAUint32(cx, d, &u))
3212 0             return JS_FALSE;
3213 0         if (!js_DoubleToECMAInt32(cx, d2, &j))
3214 0             return JS_FALSE;
3215 0         j &= 31;
3216 0         d = u >> j;
3217 0         break;
3218
3219       case JSOP_ADD:
3220 0         d += d2;
3221 0         break;
3222
3223       case JSOP_SUB:
3224 0         d -= d2;
3225 0         break;
3226
3227       case JSOP_MUL:
3228 0         d *= d2;
3229 0         break;
3230
3231       case JSOP_DIV:
3232 0         if (d2 == 0) {
3233 #if defined(XP_WIN)
3234             /* XXX MSVC miscompiles such that (NaN == 0) */
3235             if (JSDOUBLE_IS_NaN(d2))
3236                 d = *cx->runtime->jsNaN;
3237             else
3238 #endif
3239 0             if (d == 0 || JSDOUBLE_IS_NaN(d))
3240 0                 d = *cx->runtime->jsNaN;
3241 0             else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3242 0                 d = *cx->runtime->jsNegativeInfinity;
3243             else
3244 0                 d = *cx->runtime->jsPositiveInfinity;
3245         } else {
3246 0             d /= d2;
3247         }
3248 0         break;
3249
3250       case JSOP_MOD:
3251 0         if (d2 == 0) {
3252 0             d = *cx->runtime->jsNaN;
3253         } else {
3254 #if defined(XP_WIN)
3255           /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3256           if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3257 #endif
3258 0             d = fmod(d, d2);
3259         }
3260         break;
3261
3262       default:;
3263     }
3264
3265     /* Take care to allow pn1 or pn2 to alias pn. */
3266 0     if (pn1 != pn)
3267 0         RecycleTree(pn1, tc);
3268 0     if (pn2 != pn)
3269 0         RecycleTree(pn2, tc);
3270 0     pn->pn_type = TOK_NUMBER;
3271 0     pn->pn_op = JSOP_NUMBER;
3272 0     pn->pn_arity = PN_NULLARY;
3273 0     pn->pn_dval = d;
3274 0     return JS_TRUE;
3275 }
3276
3277 JSBool
3278 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3279 0 {
3280 0     JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
3281 0     int stackDummy;
3282
3283 0     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
3284 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
3285 0         return JS_FALSE;
3286     }
3287
3288 0     switch (pn->pn_arity) {
3289       case PN_FUNC:
3290 0         if (!js_FoldConstants(cx, pn->pn_body, tc))
3291 0             return JS_FALSE;
3292 0         break;
3293
3294       case PN_LIST:
3295         /* Save the list head in pn1 for later use. */
3296 0         for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3297 0             if (!js_FoldConstants(cx, pn2, tc))
3298 0                 return JS_FALSE;
3299         }
3300 0         break;
3301
3302       case PN_TERNARY:
3303         /* Any kid may be null (e.g. for (;;)). */
3304 0         pn1 = pn->pn_kid1;
3305 0         pn2 = pn->pn_kid2;
3306 0         pn3 = pn->pn_kid3;
3307 0         if (pn1 && !js_FoldConstants(cx, pn1, tc))
3308 0             return JS_FALSE;
3309 0         if (pn2 && !js_FoldConstants(cx, pn2, tc))
3310 0             return JS_FALSE;
3311 0         if (pn3 && !js_FoldConstants(cx, pn3, tc))
3312 0             return JS_FALSE;
3313 0         break;
3314
3315       case PN_BINARY:
3316         /* First kid may be null (for default case in switch). */
3317 0         pn1 = pn->pn_left;
3318 0         pn2 = pn->pn_right;
3319 0         if (pn1 && !js_FoldConstants(cx, pn1, tc))
3320 0             return JS_FALSE;
3321 0         if (!js_FoldConstants(cx, pn2, tc))
3322 0             return JS_FALSE;
3323 0         break;
3324
3325       case PN_UNARY:
3326         /* Our kid may be null (e.g. return; vs. return e;). */
3327 0         pn1 = pn->pn_kid;
3328 0         if (pn1 && !js_FoldConstants(cx, pn1, tc))
3329 0             return JS_FALSE;
3330 0         break;
3331
3332       case PN_NAME:
3333         /*
3334          * Skip pn1 down along a chain of dotted member expressions to avoid
3335          * excessive recursion.  Our only goal here is to fold constants (if
3336          * any) in the primary expression operand to the left of the first
3337          * dot in the chain.
3338          */
3339 0         pn1 = pn->pn_expr;
3340 0         while (pn1 && pn1->pn_arity == PN_NAME)
3341 0             pn1 = pn1->pn_expr;
3342 0         if (pn1 && !js_FoldConstants(cx, pn1, tc))
3343 0             return JS_FALSE;
3344 0         break;
3345
3346       case PN_NULLARY:
3347 0         break;
3348     }
3349
3350 0     switch (pn->pn_type) {
3351       case TOK_IF:
3352 0         if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
3353 0             break;
3354         /* FALL THROUGH */
3355
3356       case TOK_HOOK:
3357         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
3358 0         switch (pn1->pn_type) {
3359           case TOK_NUMBER:
3360 0             if (pn1->pn_dval == 0)
3361 0                 pn2 = pn3;
3362 0             break;
3363           case TOK_STRING:
3364 0             if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
3365 0                 pn2 = pn3;
3366 0             break;
3367           case TOK_PRIMARY:
3368 0             if (pn1->pn_op == JSOP_TRUE)
3369 0                 break;
3370 0             if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
3371 0                 pn2 = pn3;
3372 0                 break;
3373             }
3374             /* FALL THROUGH */
3375           default:
3376             /* Early return to dodge common code that copies pn2 to pn. */
3377 0             return JS_TRUE;
3378         }
3379
3380 0         if (pn2) {
3381             /* pn2 is the then- or else-statement subtree to compile. */
3382 0             PN_MOVE_NODE(pn, pn2);
3383         } else {
3384             /* False condition and no else: make pn an empty statement. */
3385 0             pn->pn_type = TOK_SEMI;
3386 0             pn->pn_arity = PN_UNARY;
3387 0             pn->pn_kid = NULL;
3388         }
3389 0         RecycleTree(pn2, tc);
3390 0         if (pn3 && pn3 != pn2)
3391 0             RecycleTree(pn3, tc);
3392 0         break;
3393
3394       case TOK_PLUS:
3395 0         if (pn->pn_arity == PN_LIST) {
3396 0             size_t length, length2;
3397 0             jschar *chars;
3398 0             JSString *str, *str2;
3399
3400             /*
3401              * Any string literal term with all others number or string means
3402              * this is a concatenation.  If any term is not a string or number
3403              * literal, we can't fold.
3404              */
3405 0             JS_ASSERT(pn->pn_count > 2);
3406 0             if (pn->pn_extra & PNX_CANTFOLD)
3407 0                 return JS_TRUE;
3408 0             if (pn->pn_extra != PNX_STRCAT)
3409 0                 goto do_binary_op;
3410
3411             /* Ok, we're concatenating: convert non-string constant operands. */
3412 0             length = 0;
3413 0             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3414 0                 if (!FoldType(cx, pn2, TOK_STRING))
3415 0                     return JS_FALSE;
3416                 /* XXX fold only if all operands convert to string */
3417 0                 if (pn2->pn_type != TOK_STRING)
3418 0                     return JS_TRUE;
3419 0                 length += ATOM_TO_STRING(pn2->pn_atom)->length;
3420             }
3421
3422             /* Allocate a new buffer and string descriptor for the result. */
3423 0             chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
3424 0             if (!chars)
3425 0                 return JS_FALSE;
3426 0             str = js_NewString(cx, chars, length, 0);
3427 0             if (!str) {
3428 0                 JS_free(cx, chars);
3429 0                 return JS_FALSE;
3430             }
3431
3432             /* Fill the buffer, advancing chars and recycling kids as we go. */
3433 0             for (pn2 = pn1; pn2; pn2 = pn3) {
3434 0                 str2 = ATOM_TO_STRING(pn2->pn_atom);
3435 0                 length2 = str2->length;
3436 0                 js_strncpy(chars, str2->chars, length2);
3437 0                 chars += length2;
3438 0                 pn3 = pn2->pn_next;
3439 0                 RecycleTree(pn2, tc);
3440             }
3441 0             *chars = 0;
3442
3443             /* Atomize the result string and mutate pn to refer to it. */
3444 0             pn->pn_atom = js_AtomizeString(cx, str, 0);
3445 0             if (!pn->pn_atom)
3446 0                 return JS_FALSE;
3447 0             pn->pn_type = TOK_STRING;
3448 0             pn->pn_op = JSOP_STRING;
3449 0             pn->pn_arity = PN_NULLARY;
3450 0             break;
3451         }
3452
3453         /* Handle a binary string concatenation. */
3454 0         JS_ASSERT(pn->pn_arity == PN_BINARY);
3455 0         if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
3456 0             JSString *left, *right, *str;
3457
3458 0             if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
3459                           TOK_STRING)) {
3460 0                 return JS_FALSE;
3461             }
3462 0             if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
3463 0                 return JS_TRUE;
3464 0             left = ATOM_TO_STRING(pn1->pn_atom);
3465 0             right = ATOM_TO_STRING(pn2->pn_atom);
3466 0             str = js_ConcatStrings(cx, left, right);
3467 0             if (!str)
3468 0                 return JS_FALSE;
3469 0             pn->pn_atom = js_AtomizeString(cx, str, 0);
3470 0             if (!pn->pn_atom)
3471 0                 return JS_FALSE;
3472 0             pn->pn_type = TOK_STRING;
3473 0             pn->pn_op = JSOP_STRING;
3474 0             pn->pn_arity = PN_NULLARY;
3475 0             RecycleTree(pn1, tc);
3476 0             RecycleTree(pn2, tc);
3477 0             break;
3478         }
3479
3480         /* Can't concatenate string literals, let's try numbers. */
3481 0         goto do_binary_op;
3482
3483       case TOK_STAR:
3484         /* The * in 'import *;' parses as a nullary star node. */
3485 0         if (pn->pn_arity == PN_NULLARY)
3486 0             break;
3487         /* FALL THROUGH */
3488
3489       case TOK_SHOP:
3490       case TOK_MINUS:
3491       case TOK_DIVOP:
3492       do_binary_op:
3493 0         if (pn->pn_arity == PN_LIST) {
3494 0             JS_ASSERT(pn->pn_count > 2);
3495 0             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3496 0                 if (!FoldType(cx, pn2, TOK_NUMBER))
3497 0                     return JS_FALSE;
3498                 /* XXX fold only if all operands convert to number */
3499 0                 if (pn2->pn_type != TOK_NUMBER)
3500 0                     break;
3501             }
3502 0             if (!pn2) {
3503 0                 JSOp op = pn->pn_op;
3504
3505 0                 pn2 = pn1->pn_next;
3506 0                 pn3 = pn2->pn_next;
3507 0                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
3508 0                     return JS_FALSE;
3509 0                 while ((pn2 = pn3) != NULL) {
3510 0                     pn3 = pn2->pn_next;
3511 0                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
3512 0                         return JS_FALSE;
3513                 }
3514             }
3515         } else {
3516 0             JS_ASSERT(pn->pn_arity == PN_BINARY);
3517 0             if (!FoldType(cx, pn1, TOK_NUMBER) ||
3518                 !FoldType(cx, pn2, TOK_NUMBER)) {
3519 0                 return JS_FALSE;
3520             }
3521 0             if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
3522 0                 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
3523 0                     return JS_FALSE;
3524             }
3525         }
3526 0         break;
3527
3528       case TOK_UNARYOP:
3529 0         if (pn1->pn_type == TOK_NUMBER) {
3530 0             jsdouble d;
3531 0             int32 i;
3532
3533             /* Operate on one numeric constant. */
3534 0             d = pn1->pn_dval;
3535 0             switch (pn->pn_op) {
3536               case JSOP_BITNOT:
3537 0                 if (!js_DoubleToECMAInt32(cx, d, &i))
3538 0                     return JS_FALSE;
3539 0                 d = ~i;
3540 0                 break;
3541
3542               case JSOP_NEG:
3543 #ifdef HPUX
3544                 /*
3545                  * Negation of a zero doesn't produce a negative
3546                  * zero on HPUX. Perform the operation by bit
3547                  * twiddling.
3548                  */
3549                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3550 #else
3551 0                 d = -d;
3552 #endif
3553 0                 break;
3554
3555               case JSOP_POS:
3556 0                 break;
3557
3558               case JSOP_NOT:
3559 0                 pn->pn_type = TOK_PRIMARY;
3560 0                 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
3561 0                 pn->pn_arity = PN_NULLARY;
3562                 /* FALL THROUGH */
3563
3564               default:
3565                 /* Return early to dodge the common TOK_NUMBER code. */
3566 0                 return JS_TRUE;
3567             }
3568 0             pn->pn_type = TOK_NUMBER;
3569 0             pn->pn_op = JSOP_NUMBER;
3570 0             pn->pn_arity = PN_NULLARY;
3571 0             pn->pn_dval = d;
3572 0             RecycleTree(pn1, tc);
3573         }
3574         break;
3575
3576       default:;
3577     }
3578
3579 0     return JS_TRUE;