Code

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