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