Code

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