Back to index

lightning-sunbird  0.9+nobinonly
jsparse.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS parser.
00043  *
00044  * This is a recursive-descent parser for the JavaScript language specified by
00045  * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic
00046  * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes
00047  * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
00048  * After tree construction, it rewrites trees to fold constants and evaluate
00049  * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to
00050  * generate bytecode.
00051  *
00052  * This parser attempts no error recovery.
00053  */
00054 #include "jsstddef.h"
00055 #include <stdlib.h>
00056 #include <string.h>
00057 #include <math.h>
00058 #include "jstypes.h"
00059 #include "jsarena.h" /* Added by JSIFY */
00060 #include "jsutil.h" /* Added by JSIFY */
00061 #include "jsapi.h"
00062 #include "jsarray.h"
00063 #include "jsatom.h"
00064 #include "jscntxt.h"
00065 #include "jsconfig.h"
00066 #include "jsemit.h"
00067 #include "jsfun.h"
00068 #include "jsinterp.h"
00069 #include "jslock.h"
00070 #include "jsnum.h"
00071 #include "jsobj.h"
00072 #include "jsopcode.h"
00073 #include "jsparse.h"
00074 #include "jsscan.h"
00075 #include "jsscope.h"
00076 #include "jsscript.h"
00077 #include "jsstr.h"
00078 
00079 #if JS_HAS_XML_SUPPORT
00080 #include "jsxml.h"
00081 #endif
00082 
00083 #if JS_HAS_DESTRUCTURING
00084 #include "jsdhash.h"
00085 #endif
00086 
00087 /*
00088  * JS parsers, from lowest to highest precedence.
00089  *
00090  * Each parser takes a context, a token stream, and a tree context struct.
00091  * Each returns a parse node tree or null on error.
00092  */
00093 
00094 typedef JSParseNode *
00095 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
00096 
00097 typedef JSParseNode *
00098 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
00099                JSBool allowCallSyntax);
00100 
00101 typedef JSParseNode *
00102 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
00103                 JSTokenType tt, JSBool afterDot);
00104 
00105 static JSParser FunctionStmt;
00106 static JSParser FunctionExpr;
00107 static JSParser Statements;
00108 static JSParser Statement;
00109 static JSParser Variables;
00110 static JSParser Expr;
00111 static JSParser AssignExpr;
00112 static JSParser CondExpr;
00113 static JSParser OrExpr;
00114 static JSParser AndExpr;
00115 static JSParser BitOrExpr;
00116 static JSParser BitXorExpr;
00117 static JSParser BitAndExpr;
00118 static JSParser EqExpr;
00119 static JSParser RelExpr;
00120 static JSParser ShiftExpr;
00121 static JSParser AddExpr;
00122 static JSParser MulExpr;
00123 static JSParser UnaryExpr;
00124 static JSMemberParser MemberExpr;
00125 static JSPrimaryParser PrimaryExpr;
00126 
00127 /*
00128  * Insist that the next token be of type tt, or report errno and return null.
00129  * NB: this macro uses cx and ts from its lexical environment.
00130  */
00131 #define MUST_MATCH_TOKEN(tt, errno)                                           \
00132     JS_BEGIN_MACRO                                                            \
00133         if (js_GetToken(cx, ts) != tt) {                                      \
00134             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
00135                             errno);                                           \
00136             return NULL;                                                      \
00137         }                                                                     \
00138     JS_END_MACRO
00139 
00140 #define CHECK_RECURSION()                                                     \
00141     JS_BEGIN_MACRO                                                            \
00142         int stackDummy;                                                       \
00143         if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {                           \
00144             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
00145                                         JSMSG_OVER_RECURSED);                 \
00146             return NULL;                                                      \
00147         }                                                                     \
00148     JS_END_MACRO
00149 
00150 #ifdef METER_PARSENODES
00151 static uint32 parsenodes = 0;
00152 static uint32 maxparsenodes = 0;
00153 static uint32 recyclednodes = 0;
00154 #endif
00155 
00156 static JSParseNode *
00157 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
00158 {
00159     JSParseNode *next;
00160 
00161     if (!pn)
00162         return NULL;
00163     JS_ASSERT(pn != tc->nodeList);      /* catch back-to-back dup recycles */
00164     next = pn->pn_next;
00165     pn->pn_next = tc->nodeList;
00166     tc->nodeList = pn;
00167 #ifdef METER_PARSENODES
00168     recyclednodes++;
00169 #endif
00170     return next;
00171 }
00172 
00173 static JSParseNode *
00174 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
00175 {
00176     JSParseNode *pn;
00177 
00178     pn = tc->nodeList;
00179     if (!pn) {
00180         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
00181         if (!pn)
00182             JS_ReportOutOfMemory(cx);
00183     } else {
00184         tc->nodeList = pn->pn_next;
00185 
00186         /* Recycle immediate descendents only, to save work and working set. */
00187         switch (pn->pn_arity) {
00188           case PN_FUNC:
00189             RecycleTree(pn->pn_body, tc);
00190             break;
00191           case PN_LIST:
00192             if (pn->pn_head) {
00193                 /* XXX check for dup recycles in the list */
00194                 *pn->pn_tail = tc->nodeList;
00195                 tc->nodeList = pn->pn_head;
00196 #ifdef METER_PARSENODES
00197                 recyclednodes += pn->pn_count;
00198 #endif
00199             }
00200             break;
00201           case PN_TERNARY:
00202             RecycleTree(pn->pn_kid1, tc);
00203             RecycleTree(pn->pn_kid2, tc);
00204             RecycleTree(pn->pn_kid3, tc);
00205             break;
00206           case PN_BINARY:
00207             RecycleTree(pn->pn_left, tc);
00208             RecycleTree(pn->pn_right, tc);
00209             break;
00210           case PN_UNARY:
00211             RecycleTree(pn->pn_kid, tc);
00212             break;
00213           case PN_NAME:
00214             RecycleTree(pn->pn_expr, tc);
00215             break;
00216           case PN_NULLARY:
00217             break;
00218         }
00219     }
00220 #ifdef METER_PARSENODES
00221     if (pn) {
00222         parsenodes++;
00223         if (parsenodes - recyclednodes > maxparsenodes)
00224             maxparsenodes = parsenodes - recyclednodes;
00225     }
00226 #endif
00227     return pn;
00228 }
00229 
00230 /*
00231  * Allocate a JSParseNode from cx's temporary arena.
00232  */
00233 static JSParseNode *
00234 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
00235              JSTreeContext *tc)
00236 {
00237     JSParseNode *pn;
00238     JSToken *tp;
00239 
00240     pn = NewOrRecycledNode(cx, tc);
00241     if (!pn)
00242         return NULL;
00243     tp = &CURRENT_TOKEN(ts);
00244     pn->pn_type = tp->type;
00245     pn->pn_pos = tp->pos;
00246     pn->pn_op = JSOP_NOP;
00247     pn->pn_arity = arity;
00248     pn->pn_next = NULL;
00249     pn->pn_ts = ts;
00250     pn->pn_source = NULL;
00251     return pn;
00252 }
00253 
00254 static JSParseNode *
00255 NewBinary(JSContext *cx, JSTokenType tt,
00256           JSOp op, JSParseNode *left, JSParseNode *right,
00257           JSTreeContext *tc)
00258 {
00259     JSParseNode *pn, *pn1, *pn2;
00260 
00261     if (!left || !right)
00262         return NULL;
00263 
00264     /*
00265      * Flatten a left-associative (left-heavy) tree of a given operator into
00266      * a list, to reduce js_FoldConstants and js_EmitTree recursion.
00267      */
00268     if (left->pn_type == tt &&
00269         left->pn_op == op &&
00270         (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
00271         if (left->pn_arity != PN_LIST) {
00272             pn1 = left->pn_left, pn2 = left->pn_right;
00273             left->pn_arity = PN_LIST;
00274             PN_INIT_LIST_1(left, pn1);
00275             PN_APPEND(left, pn2);
00276             if (tt == TOK_PLUS) {
00277                 if (pn1->pn_type == TOK_STRING)
00278                     left->pn_extra |= PNX_STRCAT;
00279                 else if (pn1->pn_type != TOK_NUMBER)
00280                     left->pn_extra |= PNX_CANTFOLD;
00281                 if (pn2->pn_type == TOK_STRING)
00282                     left->pn_extra |= PNX_STRCAT;
00283                 else if (pn2->pn_type != TOK_NUMBER)
00284                     left->pn_extra |= PNX_CANTFOLD;
00285             }
00286         }
00287         PN_APPEND(left, right);
00288         left->pn_pos.end = right->pn_pos.end;
00289         if (tt == TOK_PLUS) {
00290             if (right->pn_type == TOK_STRING)
00291                 left->pn_extra |= PNX_STRCAT;
00292             else if (right->pn_type != TOK_NUMBER)
00293                 left->pn_extra |= PNX_CANTFOLD;
00294         }
00295         return left;
00296     }
00297 
00298     /*
00299      * Fold constant addition immediately, to conserve node space and, what's
00300      * more, so js_FoldConstants never sees mixed addition and concatenation
00301      * operations with more than one leading non-string operand in a PN_LIST
00302      * generated for expressions such as 1 + 2 + "pt" (which should evaluate
00303      * to "3pt", not "12pt").
00304      */
00305     if (tt == TOK_PLUS &&
00306         left->pn_type == TOK_NUMBER &&
00307         right->pn_type == TOK_NUMBER) {
00308         left->pn_dval += right->pn_dval;
00309         left->pn_pos.end = right->pn_pos.end;
00310         RecycleTree(right, tc);
00311         return left;
00312     }
00313 
00314     pn = NewOrRecycledNode(cx, tc);
00315     if (!pn)
00316         return NULL;
00317     pn->pn_type = tt;
00318     pn->pn_pos.begin = left->pn_pos.begin;
00319     pn->pn_pos.end = right->pn_pos.end;
00320     pn->pn_op = op;
00321     pn->pn_arity = PN_BINARY;
00322     pn->pn_left = left;
00323     pn->pn_right = right;
00324     pn->pn_next = NULL;
00325     pn->pn_ts = NULL;
00326     pn->pn_source = NULL;
00327     return pn;
00328 }
00329 
00330 #if JS_HAS_GETTER_SETTER
00331 static JSTokenType
00332 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
00333 {
00334     JSAtom *atom;
00335     JSRuntime *rt;
00336     JSOp op;
00337     const char *name;
00338 
00339     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
00340     atom = CURRENT_TOKEN(ts).t_atom;
00341     rt = cx->runtime;
00342     if (atom == rt->atomState.getterAtom)
00343         op = JSOP_GETTER;
00344     else if (atom == rt->atomState.setterAtom)
00345         op = JSOP_SETTER;
00346     else
00347         return TOK_NAME;
00348     if (js_PeekTokenSameLine(cx, ts) != tt)
00349         return TOK_NAME;
00350     (void) js_GetToken(cx, ts);
00351     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
00352         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
00353                                     JSMSG_BAD_GETTER_OR_SETTER,
00354                                     (op == JSOP_GETTER)
00355                                     ? js_getter_str
00356                                     : js_setter_str);
00357         return TOK_ERROR;
00358     }
00359     CURRENT_TOKEN(ts).t_op = op;
00360     if (JS_HAS_STRICT_OPTION(cx)) {
00361         name = js_AtomToPrintableString(cx, atom);
00362         if (!name ||
00363             !js_ReportCompileErrorNumber(cx, ts,
00364                                          JSREPORT_TS |
00365                                          JSREPORT_WARNING |
00366                                          JSREPORT_STRICT,
00367                                          JSMSG_DEPRECATED_USAGE,
00368                                          name)) {
00369             return TOK_ERROR;
00370         }
00371     }
00372     return tt;
00373 }
00374 #endif
00375 
00376 static void
00377 MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp,
00378                 JSStackFrame *newfp)
00379 {
00380     /*
00381      * Always push a new frame if the current frame is special, so that
00382      * Variables gets the correct variables object: the one from the special
00383      * frame's caller.
00384      */
00385     if (oldfp &&
00386         oldfp->varobj &&
00387         oldfp->scopeChain == chain &&
00388         !(oldfp->flags & JSFRAME_SPECIAL)) {
00389         return;
00390     }
00391 
00392     memset(newfp, 0, sizeof *newfp);
00393 
00394     /* Default to sharing the same variables object and scope chain. */
00395     newfp->varobj = newfp->scopeChain = chain;
00396     if (cx->options & JSOPTION_VAROBJFIX) {
00397         while ((chain = JS_GetParent(cx, chain)) != NULL)
00398             newfp->varobj = chain;
00399     }
00400     newfp->down = oldfp;
00401     if (oldfp) {
00402         /*
00403          * In the case of eval and debugger frames, we need to dig down and find
00404          * the real variables objects and function that our new stack frame is
00405          * going to use.
00406          */
00407         newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
00408                                        JSFRAME_SCRIPT_OBJECT);
00409         while (oldfp->flags & JSFRAME_SPECIAL) {
00410             oldfp = oldfp->down;
00411             if (!oldfp)
00412                 break;
00413         }
00414         if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) {
00415             newfp->varobj = oldfp->varobj;
00416             newfp->vars = oldfp->vars;
00417             newfp->fun = oldfp->fun;
00418         }
00419     }
00420     cx->fp = newfp;
00421 }
00422 
00423 /*
00424  * Parse a top-level JS script.
00425  */
00426 JS_FRIEND_API(JSParseNode *)
00427 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
00428 {
00429     JSStackFrame *fp, frame;
00430     JSTreeContext tc;
00431     JSParseNode *pn;
00432 
00433     /*
00434      * Push a compiler frame if we have no frames, or if the top frame is a
00435      * lightweight function activation, or if its scope chain doesn't match
00436      * the one passed to us.
00437      */
00438     fp = cx->fp;
00439     MaybeSetupFrame(cx, chain, fp, &frame);
00440 
00441     /*
00442      * Protect atoms from being collected by a GC activation, which might
00443      * - nest on this thread due to out of memory (the so-called "last ditch"
00444      *   GC attempted within js_NewGCThing), or
00445      * - run for any reason on another thread if this thread is suspended on
00446      *   an object lock before it finishes generating bytecode into a script
00447      *   protected from the GC by a root or a stack frame reference.
00448      */
00449     JS_KEEP_ATOMS(cx->runtime);
00450     TREE_CONTEXT_INIT(&tc);
00451     pn = Statements(cx, ts, &tc);
00452     if (pn) {
00453         if (!js_MatchToken(cx, ts, TOK_EOF)) {
00454             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
00455                                         JSMSG_SYNTAX_ERROR);
00456             pn = NULL;
00457         } else {
00458             pn->pn_type = TOK_LC;
00459             if (!js_FoldConstants(cx, pn, &tc))
00460                 pn = NULL;
00461         }
00462     }
00463 
00464     TREE_CONTEXT_FINISH(&tc);
00465     JS_UNKEEP_ATOMS(cx->runtime);
00466     cx->fp = fp;
00467     return pn;
00468 }
00469 
00470 /*
00471  * Compile a top-level script.
00472  */
00473 JS_FRIEND_API(JSBool)
00474 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
00475                       JSCodeGenerator *cg)
00476 {
00477     JSStackFrame *fp, frame;
00478     uint32 flags;
00479     JSParseNode *pn;
00480     JSBool ok;
00481 #ifdef METER_PARSENODES
00482     void *sbrk(ptrdiff_t), *before = sbrk(0);
00483 #endif
00484 
00485     /*
00486      * Push a compiler frame if we have no frames, or if the top frame is a
00487      * lightweight function activation, or if its scope chain doesn't match
00488      * the one passed to us.
00489      */
00490     fp = cx->fp;
00491     MaybeSetupFrame(cx, chain, fp, &frame);
00492     flags = cx->fp->flags;
00493     cx->fp->flags = flags |
00494                     (JS_HAS_COMPILE_N_GO_OPTION(cx)
00495                      ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
00496                      : JSFRAME_COMPILING);
00497 
00498     /* Prevent GC activation while compiling. */
00499     JS_KEEP_ATOMS(cx->runtime);
00500 
00501     pn = Statements(cx, ts, &cg->treeContext);
00502     if (!pn) {
00503         ok = JS_FALSE;
00504     } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
00505         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
00506                                     JSMSG_SYNTAX_ERROR);
00507         ok = JS_FALSE;
00508     } else {
00509 #ifdef METER_PARSENODES
00510         printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
00511                (char *)sbrk(0) - (char *)before,
00512                parsenodes,
00513                maxparsenodes,
00514                parsenodes - recyclednodes);
00515         before = sbrk(0);
00516 #endif
00517 
00518         /*
00519          * No need to emit bytecode here -- Statements already has, for each
00520          * statement in turn.  Search for TCF_COMPILING in Statements, below.
00521          * That flag is set for every tc == &cg->treeContext, and it implies
00522          * that the tc can be downcast to a cg and used to emit code during
00523          * parsing, rather than at the end of the parse phase.
00524          *
00525          * Nowadays the threaded interpreter needs a stop instruction, so we
00526          * do have to emit that here.
00527          */
00528         JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
00529         ok = js_Emit1(cx, cg, JSOP_STOP) >= 0;
00530     }
00531 
00532 #ifdef METER_PARSENODES
00533     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
00534            (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
00535 #endif
00536 #ifdef JS_ARENAMETER
00537     JS_DumpArenaStats(stdout);
00538 #endif
00539     JS_UNKEEP_ATOMS(cx->runtime);
00540     cx->fp->flags = flags;
00541     cx->fp = fp;
00542     return ok;
00543 }
00544 
00545 /*
00546  * Insist on a final return before control flows out of pn.  Try to be a bit
00547  * smart about loops: do {...; return e2;} while(0) at the end of a function
00548  * that contains an early return e1 will get a strict warning.  Similarly for
00549  * iloops: while (true){...} is treated as though ... returns.
00550  */
00551 #define ENDS_IN_OTHER   0
00552 #define ENDS_IN_RETURN  1
00553 #define ENDS_IN_BREAK   2
00554 
00555 static int
00556 HasFinalReturn(JSParseNode *pn)
00557 {
00558     JSParseNode *pn2, *pn3;
00559     uintN rv, rv2, hasDefault;
00560 
00561     switch (pn->pn_type) {
00562       case TOK_LC:
00563         if (!pn->pn_head)
00564             return ENDS_IN_OTHER;
00565         return HasFinalReturn(PN_LAST(pn));
00566 
00567       case TOK_IF:
00568         if (!pn->pn_kid3)
00569             return ENDS_IN_OTHER;
00570         return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
00571 
00572       case TOK_WHILE:
00573         pn2 = pn->pn_left;
00574         if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
00575             return ENDS_IN_RETURN;
00576         if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
00577             return ENDS_IN_RETURN;
00578         return ENDS_IN_OTHER;
00579 
00580       case TOK_DO:
00581         pn2 = pn->pn_right;
00582         if (pn2->pn_type == TOK_PRIMARY) {
00583             if (pn2->pn_op == JSOP_FALSE)
00584                 return HasFinalReturn(pn->pn_left);
00585             if (pn2->pn_op == JSOP_TRUE)
00586                 return ENDS_IN_RETURN;
00587         }
00588         if (pn2->pn_type == TOK_NUMBER) {
00589             if (pn2->pn_dval == 0)
00590                 return HasFinalReturn(pn->pn_left);
00591             return ENDS_IN_RETURN;
00592         }
00593         return ENDS_IN_OTHER;
00594 
00595       case TOK_FOR:
00596         pn2 = pn->pn_left;
00597         if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
00598             return ENDS_IN_RETURN;
00599         return ENDS_IN_OTHER;
00600 
00601       case TOK_SWITCH:
00602         rv = ENDS_IN_RETURN;
00603         hasDefault = ENDS_IN_OTHER;
00604         pn2 = pn->pn_right;
00605         if (pn2->pn_type == TOK_LEXICALSCOPE)
00606             pn2 = pn2->pn_expr;
00607         for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
00608             if (pn2->pn_type == TOK_DEFAULT)
00609                 hasDefault = ENDS_IN_RETURN;
00610             pn3 = pn2->pn_right;
00611             JS_ASSERT(pn3->pn_type == TOK_LC);
00612             if (pn3->pn_head) {
00613                 rv2 = HasFinalReturn(PN_LAST(pn3));
00614                 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
00615                     /* Falling through to next case or default. */;
00616                 else
00617                     rv &= rv2;
00618             }
00619         }
00620         /* If a final switch has no default case, we judge it harshly. */
00621         rv &= hasDefault;
00622         return rv;
00623 
00624       case TOK_BREAK:
00625         return ENDS_IN_BREAK;
00626 
00627       case TOK_WITH:
00628         return HasFinalReturn(pn->pn_right);
00629 
00630       case TOK_RETURN:
00631         return ENDS_IN_RETURN;
00632 
00633       case TOK_COLON:
00634       case TOK_LEXICALSCOPE:
00635         return HasFinalReturn(pn->pn_expr);
00636 
00637       case TOK_THROW:
00638         return ENDS_IN_RETURN;
00639 
00640       case TOK_TRY:
00641         /* If we have a finally block that returns, we are done. */
00642         if (pn->pn_kid3) {
00643             rv = HasFinalReturn(pn->pn_kid3);
00644             if (rv == ENDS_IN_RETURN)
00645                 return rv;
00646         }
00647 
00648         /* Else check the try block and any and all catch statements. */
00649         rv = HasFinalReturn(pn->pn_kid1);
00650         if (pn->pn_kid2) {
00651             JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
00652             for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
00653                 rv &= HasFinalReturn(pn2);
00654         }
00655         return rv;
00656 
00657       case TOK_CATCH:
00658         /* Check this catch block's body. */
00659         return HasFinalReturn(pn->pn_kid3);
00660 
00661       case TOK_LET:
00662         /* Non-binary let statements are let declarations. */
00663         if (pn->pn_arity != PN_BINARY)
00664             return ENDS_IN_OTHER;
00665         return HasFinalReturn(pn->pn_right);
00666 
00667       default:
00668         return ENDS_IN_OTHER;
00669     }
00670 }
00671 
00672 static JSBool
00673 ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum,
00674                 uintN anonerrnum)
00675 {
00676     JSFunction *fun;
00677     const char *name;
00678 
00679     fun = cx->fp->fun;
00680     if (fun->atom) {
00681         name = js_AtomToPrintableString(cx, fun->atom);
00682     } else {
00683         errnum = anonerrnum;
00684         name = NULL;
00685     }
00686     return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum,
00687                                        name);
00688 }
00689 
00690 static JSBool
00691 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
00692 {
00693     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
00694            ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
00695                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
00696 }
00697 
00698 static JSParseNode *
00699 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
00700              JSTreeContext *tc)
00701 {
00702     JSStackFrame *fp, frame;
00703     JSObject *funobj;
00704     JSStmtInfo stmtInfo;
00705     uintN oldflags, firstLine;
00706     JSParseNode *pn;
00707 
00708     fp = cx->fp;
00709     funobj = fun->object;
00710     if (!fp || fp->fun != fun || fp->varobj != funobj ||
00711         fp->scopeChain != funobj) {
00712         memset(&frame, 0, sizeof frame);
00713         frame.fun = fun;
00714         frame.varobj = frame.scopeChain = funobj;
00715         frame.down = fp;
00716         if (fp)
00717             frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
00718         cx->fp = &frame;
00719     }
00720 
00721     /*
00722      * Set interpreted early so js_EmitTree can test it to decide whether to
00723      * eliminate useless expressions.
00724      */
00725     fun->flags |= JSFUN_INTERPRETED;
00726 
00727     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
00728     stmtInfo.flags = SIF_BODY_BLOCK;
00729 
00730     oldflags = tc->flags;
00731     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
00732     tc->flags |= TCF_IN_FUNCTION;
00733 
00734     /*
00735      * Save the body's first line, and store it in pn->pn_pos.begin.lineno
00736      * later, because we may have not peeked in ts yet, so Statements won't
00737      * acquire a valid pn->pn_pos.begin from the current token.
00738      */
00739     firstLine = ts->lineno;
00740     pn = Statements(cx, ts, tc);
00741 
00742     js_PopStatement(tc);
00743 
00744     /* Check for falling off the end of a function that returns a value. */
00745     if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
00746         if (!CheckFinalReturn(cx, ts, pn))
00747             pn = NULL;
00748     }
00749 
00750     /*
00751      * If we have a parse tree in pn and a code generator in tc, emit this
00752      * function's code.  We must do this here, not in js_CompileFunctionBody,
00753      * in order to detect TCF_IN_FUNCTION among tc->flags.
00754      */
00755     if (pn) {
00756         pn->pn_pos.begin.lineno = firstLine;
00757         if ((tc->flags & TCF_COMPILING)) {
00758             JSCodeGenerator *cg = (JSCodeGenerator *) tc;
00759 
00760             if (!js_FoldConstants(cx, pn, tc) ||
00761                 !js_EmitFunctionBytecode(cx, cg, pn)) {
00762                 pn = NULL;
00763             }
00764         }
00765     }
00766 
00767     cx->fp = fp;
00768     tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
00769     return pn;
00770 }
00771 
00772 /*
00773  * Compile a JS function body, which might appear as the value of an event
00774  * handler attribute in an HTML <INPUT> tag.
00775  */
00776 JSBool
00777 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
00778 {
00779     JSArenaPool codePool, notePool;
00780     JSCodeGenerator funcg;
00781     JSStackFrame *fp, frame;
00782     JSObject *funobj;
00783     JSParseNode *pn;
00784 
00785     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
00786     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
00787     if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
00788                               ts->filename, ts->lineno,
00789                               ts->principals)) {
00790         return JS_FALSE;
00791     }
00792 
00793     /* Prevent GC activation while compiling. */
00794     JS_KEEP_ATOMS(cx->runtime);
00795 
00796     /* Push a JSStackFrame for use by FunctionBody. */
00797     fp = cx->fp;
00798     funobj = fun->object;
00799     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
00800                       fp->scopeChain != funobj));
00801     memset(&frame, 0, sizeof frame);
00802     frame.fun = fun;
00803     frame.varobj = frame.scopeChain = funobj;
00804     frame.down = fp;
00805     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
00806                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
00807                   : JSFRAME_COMPILING;
00808     cx->fp = &frame;
00809 
00810     /*
00811      * Farble the body so that it looks like a block statement to js_EmitTree,
00812      * which is called beneath FunctionBody; see Statements, further below in
00813      * this file.  FunctionBody pushes a STMT_BLOCK record around its call to
00814      * Statements, so Statements will not compile each statement as it loops
00815      * to save JSParseNode space -- it will not compile at all, only build a
00816      * JSParseNode tree.
00817      *
00818      * Therefore we must fold constants, allocate try notes, and generate code
00819      * for this function, including a stop opcode at the end.
00820      */
00821     CURRENT_TOKEN(ts).type = TOK_LC;
00822     pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
00823     if (pn && !js_NewScriptFromCG(cx, &funcg, fun))
00824         pn = NULL;
00825 
00826     /* Restore saved state and release code generation arenas. */
00827     cx->fp = fp;
00828     JS_UNKEEP_ATOMS(cx->runtime);
00829     js_FinishCodeGenerator(cx, &funcg);
00830     JS_FinishArenaPool(&codePool);
00831     JS_FinishArenaPool(&notePool);
00832     return pn != NULL;
00833 }
00834 
00835 /*
00836  * Parameter block types for the several Binder functions.  We use a common
00837  * helper function signature in order to share code among destructuring and
00838  * simple variable declaration parsers.  In the destructuring case, the binder
00839  * function is called indirectly from the variable declaration parser by way
00840  * of CheckDestructuring and its friends.
00841  */
00842 typedef struct BindData BindData;
00843 
00844 typedef JSBool
00845 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
00846 
00847 struct BindData {
00848     JSParseNode             *pn;                /* error source coordinate */
00849     JSTokenStream           *ts;                /* fallback if pn is null */
00850     JSObject                *obj;               /* the variable object */
00851     JSOp                    op;                 /* prolog bytecode or nop */
00852     Binder                  binder;             /* binder, discriminates u */
00853     union {
00854         struct {
00855             JSFunction      *fun;               /* must come first! see next */
00856         } arg;
00857         struct {
00858             JSFunction      *fun;               /* this overlays u.arg.fun */
00859             JSClass         *clasp;
00860             JSPropertyOp    getter;
00861             JSPropertyOp    setter;
00862             uintN           attrs;
00863         } var;
00864         struct {
00865             jsuint          index;
00866             uintN           overflow;
00867         } let;
00868     } u;
00869 };
00870 
00871 /*
00872  * Given BindData *data and JSREPORT_* flags, expand to the second and third
00873  * actual parameters to js_ReportCompileErrorNumber.  Prefer reporting via pn
00874  * to reporting via ts, for better destructuring error pointers.
00875  */
00876 #define BIND_DATA_REPORT_ARGS(data, flags)                                    \
00877     (data)->pn ? (void *)(data)->pn : (void *)(data)->ts,                     \
00878     ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags)
00879 
00880 static JSBool
00881 BumpFormalCount(JSContext *cx, JSFunction *fun)
00882 {
00883     if (fun->nargs == JS_BITMASK(16)) {
00884         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00885                              JSMSG_TOO_MANY_FUN_ARGS);
00886         return JS_FALSE;
00887     }
00888     fun->nargs++;
00889     return JS_TRUE;
00890 }
00891 
00892 static JSBool
00893 BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
00894 {
00895     JSObject *obj, *pobj;
00896     JSProperty *prop;
00897     JSBool ok;
00898     uintN dupflag;
00899     JSFunction *fun;
00900     const char *name;
00901 
00902     obj = data->obj;
00903     ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
00904     if (!ok)
00905         return JS_FALSE;
00906 
00907     dupflag = 0;
00908     if (prop) {
00909         JS_ASSERT(pobj == obj);
00910         name = js_AtomToPrintableString(cx, atom);
00911 
00912         /*
00913          * A duplicate parameter name, a "feature" required by ECMA-262.
00914          * We force a duplicate node on the SCOPE_LAST_PROP(scope) list
00915          * with the same id, distinguished by the SPROP_IS_DUPLICATE flag,
00916          * and not mapped by an entry in scope.
00917          */
00918         ok = name &&
00919              js_ReportCompileErrorNumber(cx,
00920                                          BIND_DATA_REPORT_ARGS(data,
00921                                              JSREPORT_WARNING |
00922                                              JSREPORT_STRICT),
00923                                          JSMSG_DUPLICATE_FORMAL,
00924                                          name);
00925 
00926         OBJ_DROP_PROPERTY(cx, pobj, prop);
00927         if (!ok)
00928             return JS_FALSE;
00929 
00930         dupflag = SPROP_IS_DUPLICATE;
00931     }
00932 
00933     fun = data->u.arg.fun;
00934     if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
00935                               js_GetArgument, js_SetArgument,
00936                               SPROP_INVALID_SLOT,
00937                               JSPROP_PERMANENT | JSPROP_SHARED,
00938                               dupflag | SPROP_HAS_SHORTID,
00939                               fun->nargs)) {
00940         return JS_FALSE;
00941     }
00942 
00943     return BumpFormalCount(cx, fun);
00944 }
00945 
00946 static JSBool
00947 BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom)
00948 {
00949     JSFunction *fun;
00950 
00951     /*
00952      * Can't increase fun->nvars in an active frame, so insist that getter is
00953      * js_GetLocalVariable, not js_GetCallVariable or anything else.
00954      */
00955     if (data->u.var.getter != js_GetLocalVariable)
00956         return JS_TRUE;
00957 
00958     /*
00959      * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
00960      * Instead 'var arguments' always restates the predefined property of the
00961      * activation objects with unhidden name 'arguments'.  Assignment to such
00962      * a variable must be handled specially.
00963      */
00964     if (atom == cx->runtime->atomState.argumentsAtom)
00965         return JS_TRUE;
00966 
00967     fun = data->u.var.fun;
00968     if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
00969                               data->u.var.getter, data->u.var.setter,
00970                               SPROP_INVALID_SLOT,
00971                               data->u.var.attrs | JSPROP_SHARED,
00972                               SPROP_HAS_SHORTID, fun->u.i.nvars)) {
00973         return JS_FALSE;
00974     }
00975     if (fun->u.i.nvars == JS_BITMASK(16)) {
00976         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00977                              JSMSG_TOO_MANY_FUN_VARS);
00978         return JS_FALSE;
00979     }
00980     fun->u.i.nvars++;
00981     return JS_TRUE;
00982 }
00983 
00984 #if JS_HAS_DESTRUCTURING
00985 /*
00986  * Forward declaration to maintain top-down presentation.
00987  */
00988 static JSParseNode *
00989 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
00990                   JSTokenType tt);
00991 
00992 static JSBool
00993 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
00994                      JSTreeContext *tc)
00995 {
00996     JSAtomListElement *ale;
00997     JSFunction *fun;
00998     JSObject *obj, *pobj;
00999     JSProperty *prop;
01000     const char *name;
01001 
01002     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
01003     if (!ale) {
01004         ale = js_IndexAtom(cx, atom, &tc->decls);
01005         if (!ale)
01006             return JS_FALSE;
01007         ALE_SET_JSOP(ale, data->op);
01008     }
01009 
01010     fun = data->u.var.fun;
01011     obj = data->obj;
01012     if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
01013         return JS_FALSE;
01014 
01015     if (prop) {
01016         JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj));
01017         name = js_AtomToPrintableString(cx, atom);
01018         if (!name ||
01019             !js_ReportCompileErrorNumber(cx,
01020                                          BIND_DATA_REPORT_ARGS(data,
01021                                              JSREPORT_WARNING |
01022                                              JSREPORT_STRICT),
01023                                          JSMSG_DUPLICATE_FORMAL,
01024                                          name)) {
01025             return JS_FALSE;
01026         }
01027         OBJ_DROP_PROPERTY(cx, pobj, prop);
01028     } else {
01029         if (!BindLocalVariable(cx, data, atom))
01030             return JS_FALSE;
01031     }
01032     return JS_TRUE;
01033 }
01034 #endif /* JS_HAS_DESTRUCTURING */
01035 
01036 static JSParseNode *
01037 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
01038             JSBool lambda)
01039 {
01040     JSOp op, prevop;
01041     JSParseNode *pn, *body, *result;
01042     JSTokenType tt;
01043     JSAtom *funAtom, *objAtom;
01044     JSStackFrame *fp;
01045     JSObject *varobj, *pobj;
01046     JSAtomListElement *ale;
01047     JSProperty *prop;
01048     JSFunction *fun;
01049     JSTreeContext funtc;
01050 #if JS_HAS_DESTRUCTURING
01051     JSParseNode *item, *list = NULL;
01052 #endif
01053 
01054     /* Make a TOK_FUNCTION node. */
01055 #if JS_HAS_GETTER_SETTER
01056     op = CURRENT_TOKEN(ts).t_op;
01057 #endif
01058     pn = NewParseNode(cx, ts, PN_FUNC, tc);
01059     if (!pn)
01060         return NULL;
01061 
01062     /* Scan the optional function name into funAtom. */
01063     ts->flags |= TSF_KEYWORD_IS_NAME;
01064     tt = js_GetToken(cx, ts);
01065     ts->flags &= ~TSF_KEYWORD_IS_NAME;
01066     if (tt == TOK_NAME) {
01067         funAtom = CURRENT_TOKEN(ts).t_atom;
01068     } else {
01069         funAtom = NULL;
01070         js_UngetToken(ts);
01071     }
01072 
01073     /* Find the nearest variable-declaring scope and use it as our parent. */
01074     fp = cx->fp;
01075     varobj = fp->varobj;
01076 
01077     /*
01078      * Record names for function statements in tc->decls so we know when to
01079      * avoid optimizing variable references that might name a function.
01080      */
01081     if (!lambda && funAtom) {
01082         ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
01083         if (ale) {
01084             prevop = ALE_JSOP(ale);
01085             if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
01086                 const char *name = js_AtomToPrintableString(cx, funAtom);
01087                 if (!name ||
01088                     !js_ReportCompileErrorNumber(cx, ts,
01089                                                  (prevop != JSOP_DEFCONST)
01090                                                  ? JSREPORT_TS |
01091                                                    JSREPORT_WARNING |
01092                                                    JSREPORT_STRICT
01093                                                  : JSREPORT_TS | JSREPORT_ERROR,
01094                                                  JSMSG_REDECLARED_VAR,
01095                                                  (prevop == JSOP_DEFFUN ||
01096                                                   prevop == JSOP_CLOSURE)
01097                                                  ? js_function_str
01098                                                  : (prevop == JSOP_DEFCONST)
01099                                                  ? js_const_str
01100                                                  : js_var_str,
01101                                                  name)) {
01102                     return NULL;
01103                 }
01104             }
01105             if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
01106                 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
01107         } else {
01108             ale = js_IndexAtom(cx, funAtom, &tc->decls);
01109             if (!ale)
01110                 return NULL;
01111         }
01112         ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE);
01113 
01114         /*
01115          * A function nested at top level inside another's body needs only a
01116          * local variable to bind its name to its value, and not an activation
01117          * object property (it might also need the activation property, if the
01118          * outer function contains with statements, e.g., but the stack slot
01119          * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
01120          * JSOP_GETVAR bytecode).
01121          */
01122         if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
01123             JSScopeProperty *sprop;
01124 
01125             /*
01126              * Define a property on the outer function so that BindNameToSlot
01127              * can properly optimize accesses.
01128              */
01129             JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
01130             JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
01131             if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
01132                                          &pobj, &prop)) {
01133                 return NULL;
01134             }
01135             if (prop)
01136                 OBJ_DROP_PROPERTY(cx, pobj, prop);
01137             sprop = NULL;
01138             if (!prop ||
01139                 pobj != varobj ||
01140                 (sprop = (JSScopeProperty *)prop,
01141                  sprop->getter != js_GetLocalVariable)) {
01142                 uintN sflags;
01143 
01144                 /*
01145                  * Use SPROP_IS_DUPLICATE if there is a formal argument of the
01146                  * same name, so the decompiler can find the parameter name.
01147                  */
01148                 sflags = (sprop && sprop->getter == js_GetArgument)
01149                          ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
01150                          : SPROP_HAS_SHORTID;
01151                 if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
01152                                           js_GetLocalVariable,
01153                                           js_SetLocalVariable,
01154                                           SPROP_INVALID_SLOT,
01155                                           JSPROP_PERMANENT | JSPROP_SHARED,
01156                                           sflags, fp->fun->u.i.nvars)) {
01157                     return NULL;
01158                 }
01159                 if (fp->fun->u.i.nvars == JS_BITMASK(16)) {
01160                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01161                                          JSMSG_TOO_MANY_FUN_VARS);
01162                     return NULL;
01163                 }
01164                 fp->fun->u.i.nvars++;
01165             }
01166         }
01167     }
01168 
01169     fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
01170                          funAtom);
01171     if (!fun)
01172         return NULL;
01173 #if JS_HAS_GETTER_SETTER
01174     if (op != JSOP_NOP)
01175         fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
01176 #endif
01177 
01178     /*
01179      * Atomize fun->object early to protect against a last-ditch GC under
01180      * js_LookupHiddenProperty.
01181      *
01182      * Absent use of the new scoped local GC roots API around compiler calls,
01183      * we need to atomize here to protect against a GC activation.  Atoms are
01184      * protected from GC during compilation by the JS_FRIEND_API entry points
01185      * in this file.  There doesn't seem to be any gain in switching from the
01186      * atom-keeping method to the bulkier, slower scoped local roots method.
01187      */
01188     objAtom = js_AtomizeObject(cx, fun->object, 0);
01189     if (!objAtom)
01190         return NULL;
01191 
01192     /* Initialize early for possible flags mutation via DestructuringExpr. */
01193     TREE_CONTEXT_INIT(&funtc);
01194 
01195     /* Now parse formal argument list and compute fun->nargs. */
01196     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
01197     if (!js_MatchToken(cx, ts, TOK_RP)) {
01198         BindData data;
01199 
01200         data.pn = NULL;
01201         data.ts = ts;
01202         data.obj = fun->object;
01203         data.op = JSOP_NOP;
01204         data.binder = BindArg;
01205         data.u.arg.fun = fun;
01206 
01207         do {
01208             tt = js_GetToken(cx, ts);
01209             switch (tt) {
01210 #if JS_HAS_DESTRUCTURING
01211               case TOK_LB:
01212               case TOK_LC:
01213               {
01214                 JSParseNode *lhs, *rhs;
01215                 jsint slot;
01216 
01217                 /*
01218                  * A destructuring formal parameter turns into one or more
01219                  * local variables initialized from properties of a single
01220                  * anonymous positional parameter, so here we must tweak our
01221                  * binder and its data.
01222                  */
01223                 data.op = JSOP_DEFVAR;
01224                 data.binder = BindDestructuringArg;
01225                 data.u.var.clasp = &js_FunctionClass;
01226                 data.u.var.getter = js_GetLocalVariable;
01227                 data.u.var.setter = js_SetLocalVariable;
01228                 data.u.var.attrs = JSPROP_PERMANENT;
01229 
01230                 /*
01231                  * Temporarily transfer the owneship of the recycle list to
01232                  * funtc. See bug 313967.
01233                  */ 
01234                 funtc.nodeList = tc->nodeList;
01235                 tc->nodeList = NULL;
01236                 lhs = DestructuringExpr(cx, &data, &funtc, tt);
01237                 tc->nodeList = funtc.nodeList;
01238                 funtc.nodeList = NULL;
01239                 if (!lhs)
01240                     return NULL;
01241 
01242                 /*
01243                  * Restore the formal parameter binder in case there are more
01244                  * non-destructuring formals in the parameter list.
01245                  */
01246                 data.binder = BindArg;
01247 
01248                 /*
01249                  * Adjust fun->nargs to count the single anonymous positional
01250                  * parameter that is to be destructured.
01251                  */
01252                 slot = fun->nargs;
01253                 if (!BumpFormalCount(cx, fun))
01254                     return NULL;
01255 
01256                 /*
01257                  * Synthesize a destructuring assignment from the single
01258                  * anonymous positional parameter into the destructuring
01259                  * left-hand-side expression and accumulate it in list.
01260                  */
01261                 rhs = NewParseNode(cx, ts, PN_NAME, tc);
01262                 if (!rhs)
01263                     return NULL;
01264                 rhs->pn_type = TOK_NAME;
01265                 rhs->pn_op = JSOP_GETARG;
01266                 rhs->pn_atom = cx->runtime->atomState.emptyAtom;
01267                 rhs->pn_expr = NULL;
01268                 rhs->pn_slot = slot;
01269                 rhs->pn_attrs = 0;
01270 
01271                 item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
01272                 if (!item)
01273                     return NULL;
01274                 if (!list) {
01275                     list = NewParseNode(cx, ts, PN_LIST, tc);
01276                     if (!list)
01277                         return NULL;
01278                     list->pn_type = TOK_COMMA;
01279                     PN_INIT_LIST(list);
01280                 }
01281                 PN_APPEND(list, item);
01282                 break;
01283               }
01284 #endif /* JS_HAS_DESTRUCTURING */
01285 
01286               case TOK_NAME:
01287                 if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc))
01288                     return NULL;
01289                 break;
01290 
01291               default:
01292                 js_ReportCompileErrorNumber(cx, ts,
01293                                             JSREPORT_TS | JSREPORT_ERROR,
01294                                             JSMSG_MISSING_FORMAL);
01295                 return NULL;
01296             }
01297         } while (js_MatchToken(cx, ts, TOK_COMMA));
01298 
01299         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
01300     }
01301 
01302     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
01303     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
01304 
01305     /*
01306      * Temporarily transfer the owneship of the recycle list to funtc.
01307      * See bug 313967.
01308      */ 
01309     funtc.nodeList = tc->nodeList;
01310     tc->nodeList = NULL;
01311     body = FunctionBody(cx, ts, fun, &funtc);
01312     tc->nodeList = funtc.nodeList;
01313     funtc.nodeList = NULL;
01314     
01315     if (!body)
01316         return NULL;
01317 
01318     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
01319     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
01320 
01321 #if JS_HAS_DESTRUCTURING
01322     /*
01323      * If there were destructuring formal parameters, prepend the initializing
01324      * comma expression that we synthesized to body.  If the body is a lexical
01325      * scope node, we must make a special TOK_BODY node, to prepend the formal
01326      * parameter destructuring code without bracing the decompilation of the
01327      * function body's lexical scope.
01328      */
01329     if (list) {
01330         if (body->pn_arity != PN_LIST) {
01331             JSParseNode *block;
01332 
01333             JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE);
01334             JS_ASSERT(body->pn_arity == PN_NAME);
01335 
01336             block = NewParseNode(cx, ts, PN_LIST, tc);
01337             if (!block)
01338                 return NULL;
01339             block->pn_type = TOK_BODY;
01340             block->pn_pos = body->pn_pos;
01341             PN_INIT_LIST_1(block, body);
01342 
01343             body = block;
01344         }
01345 
01346         item = NewParseNode(cx, ts, PN_UNARY, tc);
01347         if (!item)
01348             return NULL;
01349 
01350         item->pn_type = TOK_SEMI;
01351         item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
01352         item->pn_kid = list;
01353         item->pn_next = body->pn_head;
01354         body->pn_head = item;
01355         if (body->pn_tail == &body->pn_head)
01356             body->pn_tail = &item->pn_next;
01357         ++body->pn_count;
01358     }
01359 #endif
01360 
01361     /*
01362      * If we collected flags that indicate nested heavyweight functions, or
01363      * this function contains heavyweight-making statements (references to
01364      * __parent__ or __proto__; use of with, eval, import, or export; and
01365      * assignment to arguments), flag the function as heavyweight (requiring
01366      * a call object per invocation).
01367      */
01368     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
01369         fun->flags |= JSFUN_HEAVYWEIGHT;
01370         tc->flags |= TCF_FUN_HEAVYWEIGHT;
01371     } else {
01372         /*
01373          * If this function is a named statement function not at top-level
01374          * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then
01375          * our enclosing function, if any, must be heavyweight.
01376          *
01377          * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
01378          * so it won't be set here.  Assert that it's not.  We have to check
01379          * it later, in js_EmitTree, after js_EmitFunctionBody has traversed
01380          * the function's body
01381          */
01382         JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
01383         if (!lambda && funAtom && !AT_TOP_LEVEL(tc))
01384             tc->flags |= TCF_FUN_HEAVYWEIGHT;
01385     }
01386 
01387     result = pn;
01388     if (lambda) {
01389         /*
01390          * ECMA ed. 3 standard: function expression, possibly anonymous.
01391          */
01392         op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
01393     } else if (!funAtom) {
01394         /*
01395          * If this anonymous function definition is *not* embedded within a
01396          * larger expression, we treat it as an expression statement, not as
01397          * a function declaration -- and not as a syntax error (as ECMA-262
01398          * Edition 3 would have it).  Backward compatibility trumps all.
01399          */
01400         result = NewParseNode(cx, ts, PN_UNARY, tc);
01401         if (!result)
01402             return NULL;
01403         result->pn_type = TOK_SEMI;
01404         result->pn_pos = pn->pn_pos;
01405         result->pn_kid = pn;
01406         op = JSOP_ANONFUNOBJ;
01407     } else if (!AT_TOP_LEVEL(tc)) {
01408         /*
01409          * ECMA ed. 3 extension: a function expression statement not at the
01410          * top level, e.g., in a compound statement such as the "then" part
01411          * of an "if" statement, binds a closure only if control reaches that
01412          * sub-statement.
01413          */
01414         op = JSOP_CLOSURE;
01415     } else {
01416         op = JSOP_NOP;
01417     }
01418 
01419     pn->pn_funAtom = objAtom;
01420     pn->pn_op = op;
01421     pn->pn_body = body;
01422     pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
01423     pn->pn_tryCount = funtc.tryCount;
01424     TREE_CONTEXT_FINISH(&funtc);
01425     return result;
01426 }
01427 
01428 static JSParseNode *
01429 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
01430 {
01431     return FunctionDef(cx, ts, tc, JS_FALSE);
01432 }
01433 
01434 static JSParseNode *
01435 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
01436 {
01437     return FunctionDef(cx, ts, tc, JS_TRUE);
01438 }
01439 
01440 /*
01441  * Parse the statements in a block, creating a TOK_LC node that lists the
01442  * statements' trees.  If called from block-parsing code, the caller must
01443  * match { before and } after.
01444  */
01445 static JSParseNode *
01446 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
01447 {
01448     JSParseNode *pn, *pn2, *saveBlock;
01449     JSTokenType tt;
01450 
01451     CHECK_RECURSION();
01452 
01453     pn = NewParseNode(cx, ts, PN_LIST, tc);
01454     if (!pn)
01455         return NULL;
01456     saveBlock = tc->blockNode;
01457     tc->blockNode = pn;
01458     PN_INIT_LIST(pn);
01459 
01460     ts->flags |= TSF_OPERAND;
01461     while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
01462         ts->flags &= ~TSF_OPERAND;
01463         pn2 = Statement(cx, ts, tc);
01464         if (!pn2) {
01465             if (ts->flags & TSF_EOF)
01466                 ts->flags |= TSF_UNEXPECTED_EOF;
01467             return NULL;
01468         }
01469         ts->flags |= TSF_OPERAND;
01470 
01471         /* Detect a function statement for the TOK_LC case in Statement. */
01472         if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc))
01473             tc->flags |= TCF_HAS_FUNCTION_STMT;
01474 
01475         /* If compiling top-level statements, emit as we go to save space. */
01476         if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
01477             if (cx->fp->fun &&
01478                 JS_HAS_STRICT_OPTION(cx) &&
01479                 (tc->flags & TCF_RETURN_EXPR)) {
01480                 /*
01481                  * Check pn2 for lack of a final return statement if it is the
01482                  * last statement in the block.
01483                  */
01484                 tt = js_PeekToken(cx, ts);
01485                 if ((tt == TOK_EOF || tt == TOK_RC) &&
01486                     !CheckFinalReturn(cx, ts, pn2)) {
01487                     tt = TOK_ERROR;
01488                     break;
01489                 }
01490 
01491                 /*
01492                  * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
01493                  * CheckFinalReturn again.
01494                  */
01495                 tc->flags &= ~TCF_RETURN_EXPR;
01496             }
01497             if (!js_FoldConstants(cx, pn2, tc) ||
01498                 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
01499                 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
01500                 tt = TOK_ERROR;
01501                 break;
01502             }
01503             RecycleTree(pn2, tc);
01504         } else {
01505             PN_APPEND(pn, pn2);
01506         }
01507     }
01508 
01509     /*
01510      * Handle the case where there was a let declaration under this block.  If
01511      * it replaced tc->blockNode with a new block node then we must refresh pn
01512      * and then restore tc->blockNode.
01513      */
01514     if (tc->blockNode != pn)
01515         pn = tc->blockNode;
01516     tc->blockNode = saveBlock;
01517 
01518     ts->flags &= ~TSF_OPERAND;
01519     if (tt == TOK_ERROR)
01520         return NULL;
01521 
01522     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
01523     return pn;
01524 }
01525 
01526 static JSParseNode *
01527 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
01528 {
01529     JSParseNode *pn, *pn2;
01530 
01531     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
01532     pn = Expr(cx, ts, tc);
01533     if (!pn)
01534         return NULL;
01535     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
01536 
01537     /*
01538      * Check for (a = b) and "correct" it to (a == b) iff b's operator has
01539      * greater precedence than ==.
01540      * XXX not ECMA, but documented in several books -- now a strict warning.
01541      */
01542     if (pn->pn_type == TOK_ASSIGN &&
01543         pn->pn_op == JSOP_NOP &&
01544         pn->pn_right->pn_type > TOK_EQOP)
01545     {
01546         JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
01547         if (!js_ReportCompileErrorNumber(cx, ts,
01548                                          JSREPORT_TS |
01549                                          JSREPORT_WARNING |
01550                                          JSREPORT_STRICT,
01551                                          JSMSG_EQUAL_AS_ASSIGN,
01552                                          rewrite
01553                                          ? "\nAssuming equality test"
01554                                          : "")) {
01555             return NULL;
01556         }
01557         if (rewrite) {
01558             pn->pn_type = TOK_EQOP;
01559             pn->pn_op = (JSOp)cx->jsop_eq;
01560             pn2 = pn->pn_left;
01561             switch (pn2->pn_op) {
01562               case JSOP_SETNAME:
01563                 pn2->pn_op = JSOP_NAME;
01564                 break;
01565               case JSOP_SETPROP:
01566                 pn2->pn_op = JSOP_GETPROP;
01567                 break;
01568               case JSOP_SETELEM:
01569                 pn2->pn_op = JSOP_GETELEM;
01570                 break;
01571               default:
01572                 JS_ASSERT(0);
01573             }
01574         }
01575     }
01576     return pn;
01577 }
01578 
01579 static JSBool
01580 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
01581 {
01582     JSAtom *label;
01583     JSTokenType tt;
01584 
01585     tt = js_PeekTokenSameLine(cx, ts);
01586     if (tt == TOK_ERROR)
01587         return JS_FALSE;
01588     if (tt == TOK_NAME) {
01589         (void) js_GetToken(cx, ts);
01590         label = CURRENT_TOKEN(ts).t_atom;
01591     } else {
01592         label = NULL;
01593     }
01594     pn->pn_atom = label;
01595     return JS_TRUE;
01596 }
01597 
01598 #if JS_HAS_EXPORT_IMPORT
01599 static JSParseNode *
01600 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
01601 {
01602     JSParseNode *pn, *pn2;
01603     JSTokenType tt;
01604 
01605     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
01606     pn = NewParseNode(cx, ts, PN_NAME, tc);
01607     if (!pn)
01608         return NULL;
01609     pn->pn_op = JSOP_NAME;
01610     pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
01611     pn->pn_expr = NULL;
01612     pn->pn_slot = -1;
01613     pn->pn_attrs = 0;
01614 
01615     ts->flags |= TSF_OPERAND;
01616     while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
01617         ts->flags &= ~TSF_OPERAND;
01618         if (pn->pn_op == JSOP_IMPORTALL)
01619             goto bad_import;
01620 
01621         if (tt == TOK_DOT) {
01622             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
01623             if (!pn2)
01624                 return NULL;
01625             ts->flags |= TSF_KEYWORD_IS_NAME;
01626             if (js_MatchToken(cx, ts, TOK_STAR)) {
01627                 pn2->pn_op = JSOP_IMPORTALL;
01628                 pn2->pn_atom = NULL;
01629                 pn2->pn_slot = -1;
01630                 pn2->pn_attrs = 0;
01631             } else {
01632                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
01633                 pn2->pn_op = JSOP_GETPROP;
01634                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
01635                 pn2->pn_slot = -1;
01636                 pn2->pn_attrs = 0;
01637             }
01638             ts->flags &= ~TSF_KEYWORD_IS_NAME;
01639             pn2->pn_expr = pn;
01640             pn2->pn_pos.begin = pn->pn_pos.begin;
01641             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
01642         } else {
01643             /* Make a TOK_LB binary node. */
01644             pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
01645             if (!pn2)
01646                 return NULL;
01647 
01648             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
01649         }
01650 
01651         pn = pn2;
01652         ts->flags |= TSF_OPERAND;
01653     }
01654     ts->flags &= ~TSF_OPERAND;
01655     if (tt == TOK_ERROR)
01656         return NULL;
01657     js_UngetToken(ts);
01658 
01659     switch (pn->pn_op) {
01660       case JSOP_GETPROP:
01661         pn->pn_op = JSOP_IMPORTPROP;
01662         break;
01663       case JSOP_GETELEM:
01664         pn->pn_op = JSOP_IMPORTELEM;
01665         break;
01666       case JSOP_IMPORTALL:
01667         break;
01668       default:
01669         goto bad_import;
01670     }
01671     return pn;
01672 
01673   bad_import:
01674     js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
01675                                 JSMSG_BAD_IMPORT);
01676     return NULL;
01677 }
01678 #endif /* JS_HAS_EXPORT_IMPORT */
01679 
01680 static JSBool
01681 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
01682 {
01683     JSObject *blockObj;
01684     JSScopeProperty *sprop;
01685     JSAtomListElement *ale;
01686 
01687     blockObj = data->obj;
01688     sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
01689     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
01690     if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
01691         const char *name;
01692 
01693         if (sprop) {
01694             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
01695             JS_ASSERT((uint16)sprop->shortid < data->u.let.index);
01696         }
01697 
01698         name = js_AtomToPrintableString(cx, atom);
01699         if (name) {
01700             js_ReportCompileErrorNumber(cx,
01701                                         BIND_DATA_REPORT_ARGS(data,
01702                                                               JSREPORT_ERROR),
01703                                         JSMSG_REDECLARED_VAR,
01704                                         (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
01705                                         ? js_const_str
01706                                         : "variable",
01707                                         name);
01708         }
01709         return JS_FALSE;
01710     }
01711 
01712     if (data->u.let.index == JS_BIT(16)) {
01713         js_ReportCompileErrorNumber(cx,
01714                                     BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR),
01715                                     data->u.let.overflow);
01716         return JS_FALSE;
01717     }
01718 
01719     /* Use JSPROP_ENUMERATE to aid the disassembler. */
01720     return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
01721                                    JSVAL_VOID, NULL, NULL,
01722                                    JSPROP_ENUMERATE | JSPROP_PERMANENT,
01723                                    SPROP_HAS_SHORTID,
01724                                    (intN)data->u.let.index++,
01725                                    NULL);
01726 }
01727 
01728 static JSBool
01729 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
01730 {
01731     JSStmtInfo *stmt;
01732     JSAtomListElement *ale;
01733     JSOp op, prevop;
01734     const char *name;
01735     JSFunction *fun;
01736     JSObject *obj, *pobj;
01737     JSProperty *prop;
01738     JSBool ok;
01739     JSPropertyOp getter, setter;
01740     JSScopeProperty *sprop;
01741 
01742     stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE);
01743     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
01744     op = data->op;
01745     if ((stmt && stmt->type != STMT_WITH) || ale) {
01746         prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
01747         if (JS_HAS_STRICT_OPTION(cx)
01748             ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
01749             : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
01750             name = js_AtomToPrintableString(cx, atom);
01751             if (!name ||
01752                 !js_ReportCompileErrorNumber(cx,
01753                                              BIND_DATA_REPORT_ARGS(data,
01754                                                  (op != JSOP_DEFCONST &&
01755                                                   prevop != JSOP_DEFCONST)
01756                                                  ? JSREPORT_WARNING |
01757                                                    JSREPORT_STRICT
01758                                                  : JSREPORT_ERROR),
01759                                              JSMSG_REDECLARED_VAR,
01760                                              (prevop == JSOP_DEFFUN ||
01761                                               prevop == JSOP_CLOSURE)
01762                                              ? js_function_str
01763                                              : (prevop == JSOP_DEFCONST)
01764                                              ? js_const_str
01765                                              : js_var_str,
01766                                              name)) {
01767                 return JS_FALSE;
01768             }
01769         }
01770         if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
01771             tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
01772     }
01773     if (!ale) {
01774         ale = js_IndexAtom(cx, atom, &tc->decls);
01775         if (!ale)
01776             return JS_FALSE;
01777     }
01778     ALE_SET_JSOP(ale, op);
01779 
01780     fun = data->u.var.fun;
01781     obj = data->obj;
01782     if (!fun) {
01783         /* Don't lookup global variables at compile time. */
01784         prop = NULL;
01785     } else {
01786         JS_ASSERT(OBJ_IS_NATIVE(obj));
01787         if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
01788                                      &pobj, &prop)) {
01789             return JS_FALSE;
01790         }
01791     }
01792 
01793     ok = JS_TRUE;
01794     getter = data->u.var.getter;
01795     setter = data->u.var.setter;
01796 
01797     if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
01798         sprop = (JSScopeProperty *)prop;
01799         if (sprop->getter == js_GetArgument) {
01800             name  = js_AtomToPrintableString(cx, atom);
01801             if (!name) {
01802                 ok = JS_FALSE;
01803             } else if (op == JSOP_DEFCONST) {
01804                 js_ReportCompileErrorNumber(cx,
01805                                             BIND_DATA_REPORT_ARGS(data,
01806                                                 JSREPORT_ERROR),
01807                                             JSMSG_REDECLARED_PARAM,
01808                                             name);
01809                 ok = JS_FALSE;
01810             } else {
01811                 getter = js_GetArgument;
01812                 setter = js_SetArgument;
01813                 ok = js_ReportCompileErrorNumber(cx,
01814                                                  BIND_DATA_REPORT_ARGS(data,
01815                                                      JSREPORT_WARNING |
01816                                                      JSREPORT_STRICT),
01817                                                  JSMSG_VAR_HIDES_ARG,
01818                                                  name);
01819             }
01820         } else {
01821             JS_ASSERT(getter == js_GetLocalVariable);
01822 
01823             if (fun) {
01824                 /* Not an argument, must be a redeclared local var. */
01825                 if (data->u.var.clasp == &js_FunctionClass) {
01826                     JS_ASSERT(sprop->getter == js_GetLocalVariable);
01827                     JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
01828                               (uint16) sprop->shortid < fun->u.i.nvars);
01829                 } else if (data->u.var.clasp == &js_CallClass) {
01830                     if (sprop->getter == js_GetCallVariable) {
01831                         /*
01832                          * Referencing a name introduced by a var statement in
01833                          * the enclosing function.  Check that the slot number
01834                          * we have is in range.
01835                          */
01836                         JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
01837                                   (uint16) sprop->shortid < fun->u.i.nvars);
01838                     } else {
01839                         /*
01840                          * A variable introduced through another eval: don't
01841                          * use the special getters and setters since we can't
01842                          * allocate a slot in the frame.
01843                          */
01844                         getter = sprop->getter;
01845                         setter = sprop->setter;
01846                     }
01847                 }
01848 
01849                 /* Override the old getter and setter, to handle eval. */
01850                 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
01851                                                      0, sprop->attrs,
01852                                                      getter, setter);
01853                 if (!sprop)
01854                     ok = JS_FALSE;
01855             }
01856         }
01857         if (prop)
01858             OBJ_DROP_PROPERTY(cx, pobj, prop);
01859     } else {
01860         /*
01861          * Property not found in current variable scope: we have not seen this
01862          * variable before.  Define a new local variable by adding a property
01863          * to the function's scope, allocating one slot in the function's vars
01864          * frame.  Global variables and any locals declared in with statement
01865          * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes
01866          * generated for slot-less vars.
01867          */
01868         sprop = NULL;
01869         if (prop) {
01870             OBJ_DROP_PROPERTY(cx, pobj, prop);
01871             prop = NULL;
01872         }
01873 
01874         if (cx->fp->scopeChain == obj &&
01875             !js_InWithStatement(tc) &&
01876             !BindLocalVariable(cx, data, atom)) {
01877             return JS_FALSE;
01878         }
01879     }
01880     return ok;
01881 }
01882 
01883 #if JS_HAS_DESTRUCTURING
01884 
01885 static JSBool
01886 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
01887                      JSTreeContext *tc)
01888 {
01889     JSAtom *atom;
01890 
01891     /*
01892      * Destructuring is a form of assignment, so just as for an initialized
01893      * simple variable, we must check for assignment to 'arguments' and flag
01894      * the enclosing function (if any) as heavyweight.
01895      */
01896     JS_ASSERT(pn->pn_type == TOK_NAME);
01897     atom = pn->pn_atom;
01898     if (atom == cx->runtime->atomState.argumentsAtom)
01899         tc->flags |= TCF_FUN_HEAVYWEIGHT;
01900 
01901     data->pn = pn;
01902     if (!data->binder(cx, data, atom, tc))
01903         return JS_FALSE;
01904     data->pn = NULL;
01905 
01906     /*
01907      * Select the appropriate name-setting opcode, which may be specialized
01908      * further for local variable and argument slot optimizations.  At this
01909      * point, we can't select the optimal final opcode, yet we must preserve
01910      * the CONST bit and convey "set", not "get".
01911      */
01912     pn->pn_op = (data->op == JSOP_DEFCONST)
01913                 ? JSOP_SETCONST
01914                 : JSOP_SETNAME;
01915     pn->pn_attrs = data->u.var.attrs;
01916     return JS_TRUE;
01917 }
01918 
01919 /*
01920  * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
01921  * LHS expression except a destructuring initialiser, and R is on the stack.
01922  * Because R is already evaluated, the usual LHS-specialized bytecodes won't
01923  * work.  After pushing R[P] we need to evaluate Q's "reference base" QB and
01924  * then push its property name QN.  At this point the stack looks like
01925  *
01926  *   [... R, R[P], QB, QN]
01927  *
01928  * We need to set QB[QN] = R[P].  This is a job for JSOP_ENUMELEM, which takes
01929  * its operands with left-hand side above right-hand side:
01930  *
01931  *   [rval, lval, xval]
01932  *
01933  * and pops all three values, setting lval[xval] = rval.  But we cannot select
01934  * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
01935  * which can be optimized further.  So we select JSOP_SETNAME.
01936  */
01937 static JSBool
01938 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
01939 {
01940     while (pn->pn_type == TOK_RP)
01941         pn = pn->pn_kid;
01942 
01943     switch (pn->pn_type) {
01944       case TOK_NAME:
01945         if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
01946             tc->flags |= TCF_FUN_HEAVYWEIGHT;
01947         /* FALL THROUGH */
01948       case TOK_DOT:
01949       case TOK_LB:
01950         pn->pn_op = JSOP_SETNAME;
01951         break;
01952 
01953 #if JS_HAS_LVALUE_RETURN
01954       case TOK_LP:
01955         JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
01956         pn->pn_op = JSOP_SETCALL;
01957         break;
01958 #endif
01959 
01960 #if JS_HAS_XML_SUPPORT
01961       case TOK_UNARYOP:
01962         if (pn->pn_op == JSOP_XMLNAME) {
01963             pn->pn_op = JSOP_BINDXMLNAME;
01964             break;
01965         }
01966         /* FALL THROUGH */
01967 #endif
01968 
01969       default:
01970         js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
01971                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
01972         return JS_FALSE;
01973     }
01974 
01975     return JS_TRUE;
01976 }
01977 
01978 typedef struct FindPropValData {
01979     uint32          numvars;    /* # of destructuring vars in left side */
01980     uint32          maxstep;    /* max # of steps searching right side */
01981     JSDHashTable    table;      /* hash table for O(1) right side search */
01982 } FindPropValData;
01983 
01984 typedef struct FindPropValEntry {
01985     JSDHashEntryHdr hdr;
01986     JSParseNode     *pnkey;
01987     JSParseNode     *pnval;
01988 } FindPropValEntry;
01989 
01990 #define ASSERT_VALID_PROPERTY_KEY(pnkey)                                      \
01991     JS_ASSERT((pnkey)->pn_arity == PN_NULLARY &&                              \
01992               ((pnkey)->pn_type == TOK_NUMBER ||                              \
01993                (pnkey)->pn_type == TOK_STRING ||                              \
01994                (pnkey)->pn_type == TOK_NAME))
01995 
01996 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
01997 HashFindPropValKey(JSDHashTable *table, const void *key)
01998 {
01999     const JSParseNode *pnkey = (const JSParseNode *)key;
02000 
02001     ASSERT_VALID_PROPERTY_KEY(pnkey);
02002     return (pnkey->pn_type == TOK_NUMBER)
02003            ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
02004                               JSDOUBLE_LO32(pnkey->pn_dval))
02005            : (JSDHashNumber) pnkey->pn_atom->number;
02006 }
02007 
02008 JS_STATIC_DLL_CALLBACK(JSBool)
02009 MatchFindPropValEntry(JSDHashTable *table,
02010                       const JSDHashEntryHdr *entry,
02011                       const void *key)
02012 {
02013     const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
02014     const JSParseNode *pnkey = (const JSParseNode *)key;
02015 
02016     ASSERT_VALID_PROPERTY_KEY(pnkey);
02017     return pnkey->pn_type == fpve->pnkey->pn_type &&
02018            ((pnkey->pn_type == TOK_NUMBER)
02019             ? pnkey->pn_dval == fpve->pnkey->pn_dval
02020             : pnkey->pn_atom == fpve->pnkey->pn_atom);
02021 }
02022 
02023 static const JSDHashTableOps FindPropValOps = {
02024     JS_DHashAllocTable,
02025     JS_DHashFreeTable,
02026     JS_DHashGetKeyStub,
02027     HashFindPropValKey,
02028     MatchFindPropValEntry,
02029     JS_DHashMoveEntryStub,
02030     JS_DHashClearEntryStub,
02031     JS_DHashFinalizeStub,
02032     NULL
02033 };
02034 
02035 #define STEP_HASH_THRESHOLD     10
02036 #define BIG_DESTRUCTURING        5
02037 #define BIG_OBJECT_INIT         20
02038 
02039 static JSParseNode *
02040 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
02041 {
02042     FindPropValEntry *entry;
02043     JSParseNode *pnhit, *pnprop, *pnkey;
02044     uint32 step;
02045 
02046     /* If we have a hash table, use it as the sole source of truth. */
02047     if (data->table.ops) {
02048         entry = (FindPropValEntry *)
02049                 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
02050         return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
02051     }
02052 
02053     /* If pn is not an object initialiser node, we can't do anything here. */
02054     if (pn->pn_type != TOK_RC)
02055         return NULL;
02056 
02057     /*
02058      * We must search all the way through pn's list, to handle the case of an
02059      * id duplicated for two or more property initialisers.
02060      */
02061     pnhit = NULL;
02062     step = 0;
02063     ASSERT_VALID_PROPERTY_KEY(pnid);
02064     if (pnid->pn_type == TOK_NUMBER) {
02065         for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
02066             JS_ASSERT(pnprop->pn_type == TOK_COLON);
02067             if (pnprop->pn_op == JSOP_NOP) {
02068                 pnkey = pnprop->pn_left;
02069                 ASSERT_VALID_PROPERTY_KEY(pnkey);
02070                 if (pnkey->pn_type == TOK_NUMBER &&
02071                     pnkey->pn_dval == pnid->pn_dval) {
02072                     pnhit = pnprop;
02073                 }
02074                 ++step;
02075             }
02076         }
02077     } else {
02078         for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
02079             JS_ASSERT(pnprop->pn_type == TOK_COLON);
02080             if (pnprop->pn_op == JSOP_NOP) {
02081                 pnkey = pnprop->pn_left;
02082                 ASSERT_VALID_PROPERTY_KEY(pnkey);
02083                 if (pnkey->pn_type == pnid->pn_type &&
02084                     pnkey->pn_atom == pnid->pn_atom) {
02085                     pnhit = pnprop;
02086                 }
02087                 ++step;
02088             }
02089         }
02090     }
02091     if (!pnhit)
02092         return NULL;
02093 
02094     /* Hit via full search -- see whether it's time to create the hash table. */
02095     JS_ASSERT(!data->table.ops);
02096     if (step > data->maxstep) {
02097         data->maxstep = step;
02098         if (step >= STEP_HASH_THRESHOLD &&
02099             data->numvars >= BIG_DESTRUCTURING &&
02100             pn->pn_count >= BIG_OBJECT_INIT &&
02101             JS_DHashTableInit(&data->table, &FindPropValOps, pn,
02102                               sizeof(FindPropValEntry), pn->pn_count)) {
02103 
02104             for (pn = pn->pn_head; pn; pn = pn->pn_next) {
02105                 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
02106                 entry = (FindPropValEntry *)
02107                         JS_DHashTableOperate(&data->table, pn->pn_left,
02108                                              JS_DHASH_ADD);
02109                 entry->pnval = pn->pn_right;
02110             }
02111         }
02112     }
02113     return pnhit->pn_right;
02114 }
02115 
02116 /*
02117  * If data is null, the caller is AssignExpr and instead of binding variables,
02118  * we specialize lvalues in the propery value positions of the left-hand side.
02119  * If right is null, just check for well-formed lvalues.
02120  */
02121 static JSBool
02122 CheckDestructuring(JSContext *cx, BindData *data,
02123                    JSParseNode *left, JSParseNode *right,
02124                    JSTreeContext *tc)
02125 {
02126     JSBool ok;
02127     FindPropValData fpvd;
02128     JSParseNode *lhs, *rhs, *pn, *pn2;
02129 
02130     if (left->pn_type == TOK_ARRAYCOMP) {
02131         js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR,
02132                                     JSMSG_ARRAY_COMP_LEFTSIDE);
02133         return JS_FALSE;
02134     }
02135 
02136     ok = JS_TRUE;
02137     fpvd.table.ops = NULL;
02138     lhs = left->pn_head;
02139     if (lhs && lhs->pn_type == TOK_DEFSHARP) {
02140         pn = lhs;
02141         goto no_var_name;
02142     }
02143 
02144     if (left->pn_type == TOK_RB) {
02145         rhs = (right && right->pn_type == left->pn_type)
02146               ? right->pn_head
02147               : NULL;
02148 
02149         while (lhs) {
02150             pn = lhs, pn2 = rhs;
02151             if (!data) {
02152                 /* Skip parenthesization if not in a variable declaration. */
02153                 while (pn->pn_type == TOK_RP)
02154                     pn = pn->pn_kid;
02155                 if (pn2) {
02156                     while (pn2->pn_type == TOK_RP)
02157                         pn2 = pn2->pn_kid;
02158                 }
02159             }
02160 
02161             /* Nullary comma is an elision; binary comma is an expression.*/
02162             if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
02163                 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
02164                     ok = CheckDestructuring(cx, data, pn, pn2, tc);
02165                 } else {
02166                     if (data) {
02167                         if (pn->pn_type != TOK_NAME)
02168                             goto no_var_name;
02169 
02170                         ok = BindDestructuringVar(cx, data, pn, tc);
02171                     } else {
02172                         ok = BindDestructuringLHS(cx, pn, tc);
02173                     }
02174                 }
02175                 if (!ok)
02176                     goto out;
02177             }
02178 
02179             lhs = lhs->pn_next;
02180             if (rhs)
02181                 rhs = rhs->pn_next;
02182         }
02183     } else {
02184         JS_ASSERT(left->pn_type == TOK_RC);
02185         fpvd.numvars = left->pn_count;
02186         fpvd.maxstep = 0;
02187         rhs = NULL;
02188 
02189         while (lhs) {
02190             JS_ASSERT(lhs->pn_type == TOK_COLON);
02191             pn = lhs->pn_right;
02192             if (!data) {
02193                 /* Skip parenthesization if not in a variable declaration. */
02194                 while (pn->pn_type == TOK_RP)
02195                     pn = pn->pn_kid;
02196             }
02197 
02198             if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
02199                 if (right) {
02200                     rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
02201                     if (rhs && !data) {
02202                         while (rhs->pn_type == TOK_RP)
02203                             rhs = rhs->pn_kid;
02204                     }
02205                 }
02206 
02207                 ok = CheckDestructuring(cx, data, pn, rhs, tc);
02208             } else if (data) {
02209                 if (pn->pn_type != TOK_NAME)
02210                     goto no_var_name;
02211 
02212                 ok = BindDestructuringVar(cx, data, pn, tc);
02213             } else {
02214                 ok = BindDestructuringLHS(cx, pn, tc);
02215             }
02216             if (!ok)
02217                 goto out;
02218 
02219             lhs = lhs->pn_next;
02220         }
02221     }
02222 
02223 out:
02224     if (fpvd.table.ops)
02225         JS_DHashTableFinish(&fpvd.table);
02226     return ok;
02227 
02228 no_var_name:
02229     js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
02230                                 JSMSG_NO_VARIABLE_NAME);
02231     ok = JS_FALSE;
02232     goto out;
02233 }
02234 
02235 static JSParseNode *
02236 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
02237                   JSTokenType tt)
02238 {
02239     JSParseNode *pn;
02240 
02241     pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE);
02242     if (!pn)
02243         return NULL;
02244     if (!CheckDestructuring(cx, data, pn, NULL, tc))
02245         return NULL;
02246     return pn;
02247 }
02248 
02249 #endif /* JS_HAS_DESTRUCTURING */
02250 
02251 extern const char js_with_statement_str[];
02252 
02253 static JSParseNode *
02254 ContainsStmt(JSParseNode *pn, JSTokenType tt)
02255 {
02256     JSParseNode *pn2, *pnt;
02257 
02258     if (!pn)
02259         return NULL;
02260     if (pn->pn_type == tt)
02261         return pn;
02262     switch (pn->pn_arity) {
02263       case PN_LIST:
02264         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
02265             pnt = ContainsStmt(pn2, tt);
02266             if (pnt)
02267                 return pnt;
02268         }
02269         break;
02270       case PN_TERNARY:
02271         pnt = ContainsStmt(pn->pn_kid1, tt);
02272         if (pnt)
02273             return pnt;
02274         pnt = ContainsStmt(pn->pn_kid2, tt);
02275         if (pnt)
02276             return pnt;
02277         return ContainsStmt(pn->pn_kid3, tt);
02278       case PN_BINARY:
02279         /*
02280          * Limit recursion if pn is a binary expression, which can't contain a
02281          * var statement.
02282          */
02283         if (pn->pn_op != JSOP_NOP)
02284             return NULL;
02285         pnt = ContainsStmt(pn->pn_left, tt);
02286         if (pnt)
02287             return pnt;
02288         return ContainsStmt(pn->pn_right, tt);
02289       case PN_UNARY:
02290         if (pn->pn_op != JSOP_NOP)
02291             return NULL;
02292         return ContainsStmt(pn->pn_kid, tt);
02293       case PN_NAME:
02294         return ContainsStmt(pn->pn_expr, tt);
02295       default:;
02296     }
02297     return NULL;
02298 }
02299 
02300 static JSParseNode *
02301 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
02302               JSParser operandParser)
02303 {
02304     JSTokenType tt, tt2;
02305     JSParseNode *pn, *pn2;
02306 
02307     tt = CURRENT_TOKEN(ts).type;
02308     if (!(tc->flags & TCF_IN_FUNCTION)) {
02309         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
02310                                     JSMSG_BAD_RETURN_OR_YIELD,
02311 #if JS_HAS_GENERATORS
02312                                     (tt == TOK_YIELD) ? js_yield_str :
02313 #endif
02314                                     js_return_str);
02315         return NULL;
02316     }
02317 
02318     pn = NewParseNode(cx, ts, PN_UNARY, tc);
02319     if (!pn)
02320         return NULL;
02321 
02322 #if JS_HAS_GENERATORS
02323     if (tt == TOK_YIELD)
02324         tc->flags |= TCF_FUN_IS_GENERATOR;
02325 #endif
02326 
02327     /* This is ugly, but we don't want to require a semicolon. */
02328     ts->flags |= TSF_OPERAND;
02329     tt2 = js_PeekTokenSameLine(cx, ts);
02330     ts->flags &= ~TSF_OPERAND;
02331     if (tt2 == TOK_ERROR)
02332         return NULL;
02333 
02334     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
02335 #if JS_HAS_GENERATORS
02336         && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP))
02337 #endif
02338         ) {
02339         pn2 = operandParser(cx, ts, tc);
02340         if (!pn2)
02341             return NULL;
02342 #if JS_HAS_GENERATORS
02343         if (tt == TOK_RETURN)
02344 #endif
02345             tc->flags |= TCF_RETURN_EXPR;
02346         pn->pn_pos.end = pn2->pn_pos.end;
02347         pn->pn_kid = pn2;
02348     } else {
02349 #if JS_HAS_GENERATORS
02350         if (tt == TOK_RETURN)
02351 #endif
02352             tc->flags |= TCF_RETURN_VOID;
02353         pn->pn_kid = NULL;
02354     }
02355 
02356     if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
02357         /* As in Python (see PEP-255), disallow return v; in generators. */
02358         ReportBadReturn(cx, ts, JSREPORT_ERROR,
02359                         JSMSG_BAD_GENERATOR_RETURN,
02360                         JSMSG_BAD_ANON_GENERATOR_RETURN);
02361         return NULL;
02362     }
02363 
02364     if (JS_HAS_STRICT_OPTION(cx) &&
02365         (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
02366         !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
02367                          JSMSG_NO_RETURN_VALUE,
02368                          JSMSG_ANON_NO_RETURN_VALUE)) {
02369         return NULL;
02370     }
02371 
02372     return pn;
02373 }
02374 
02375 static JSParseNode *
02376 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
02377                  JSStmtInfo *stmtInfo)
02378 {
02379     JSParseNode *pn;
02380     JSObject *obj;
02381     JSAtom *atom;
02382 
02383     pn = NewParseNode(cx, ts, PN_NAME, tc);
02384     if (!pn)
02385         return NULL;
02386 
02387     obj = js_NewBlockObject(cx);
02388     if (!obj)
02389         return NULL;
02390 
02391     atom = js_AtomizeObject(cx, obj, 0);
02392     if (!atom)
02393         return NULL;
02394 
02395     js_PushBlockScope(tc, stmtInfo, atom, -1);
02396     pn->pn_type = TOK_LEXICALSCOPE;
02397     pn->pn_op = JSOP_LEAVEBLOCK;
02398     pn->pn_atom = atom;
02399     pn->pn_expr = NULL;
02400     pn->pn_slot = -1;
02401     pn->pn_attrs = 0;
02402     return pn;
02403 }
02404 
02405 #if JS_HAS_BLOCK_SCOPE
02406 
02407 static JSParseNode *
02408 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
02409 {
02410     JSParseNode *pn, *pnblock, *pnlet;
02411     JSStmtInfo stmtInfo;
02412 
02413     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
02414 
02415     /* Create the let binary node. */
02416     pnlet = NewParseNode(cx, ts, PN_BINARY, tc);
02417     if (!pnlet)
02418         return NULL;
02419 
02420     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
02421 
02422     /* This is a let block or expression of the form: let (a, b, c) .... */
02423     pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
02424     if (!pnblock)
02425         return NULL;
02426     pn = pnblock;
02427     pn->pn_expr = pnlet;
02428 
02429     pnlet->pn_left = Variables(cx, ts, tc);
02430     if (!pnlet->pn_left)
02431         return NULL;
02432     pnlet->pn_left->pn_extra = PNX_POPVAR;
02433 
02434     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
02435 
02436     ts->flags |= TSF_OPERAND;
02437     if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
02438         /*
02439          * If this is really an expression in let statement guise, then we
02440          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
02441          * the return value of the expression.
02442          */
02443         pn = NewParseNode(cx, ts, PN_UNARY, tc);
02444         if (!pn)
02445             return NULL;
02446         pn->pn_type = TOK_SEMI;
02447         pn->pn_num = -1;
02448         pn->pn_kid = pnblock;
02449 
02450         statement = JS_FALSE;
02451     }
02452     ts->flags &= ~TSF_OPERAND;
02453 
02454     if (statement) {
02455         pnlet->pn_right = Statements(cx, ts, tc);
02456         if (!pnlet->pn_right)
02457             return NULL;
02458         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
02459     } else {
02460         /*
02461          * Change pnblock's opcode to the variant that propagates the last
02462          * result down after popping the block, and clear statement.
02463          */
02464         pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
02465         pnlet->pn_right = Expr(cx, ts, tc);
02466         if (!pnlet->pn_right)
02467             return NULL;
02468     }
02469 
02470     js_PopStatement(tc);
02471     return pn;
02472 }
02473 
02474 #endif /* JS_HAS_BLOCK_SCOPE */
02475 
02476 static JSParseNode *
02477 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
02478 {
02479     JSTokenType tt;
02480     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
02481     JSStmtInfo stmtInfo, *stmt, *stmt2;
02482     JSAtom *label;
02483 
02484     CHECK_RECURSION();
02485 
02486     ts->flags |= TSF_OPERAND;
02487     tt = js_GetToken(cx, ts);
02488     ts->flags &= ~TSF_OPERAND;
02489 
02490 #if JS_HAS_GETTER_SETTER
02491     if (tt == TOK_NAME) {
02492         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
02493         if (tt == TOK_ERROR)
02494             return NULL;
02495     }
02496 #endif
02497 
02498     switch (tt) {
02499 #if JS_HAS_EXPORT_IMPORT
02500       case TOK_EXPORT:
02501         pn = NewParseNode(cx, ts, PN_LIST, tc);
02502         if (!pn)
02503             return NULL;
02504         PN_INIT_LIST(pn);
02505         if (js_MatchToken(cx, ts, TOK_STAR)) {
02506             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
02507             if (!pn2)
02508                 return NULL;
02509             PN_APPEND(pn, pn2);
02510         } else {
02511             do {
02512                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
02513                 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
02514                 if (!pn2)
02515                     return NULL;
02516                 pn2->pn_op = JSOP_NAME;
02517                 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
02518                 pn2->pn_expr = NULL;
02519                 pn2->pn_slot = -1;
02520                 pn2->pn_attrs = 0;
02521                 PN_APPEND(pn, pn2);
02522             } while (js_MatchToken(cx, ts, TOK_COMMA));
02523         }
02524         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
02525         tc->flags |= TCF_FUN_HEAVYWEIGHT;
02526         break;
02527 
02528       case TOK_IMPORT:
02529         pn = NewParseNode(cx, ts, PN_LIST, tc);
02530         if (!pn)
02531             return NULL;
02532         PN_INIT_LIST(pn);
02533         do {
02534             pn2 = ImportExpr(cx, ts, tc);
02535             if (!pn2)
02536                 return NULL;
02537             PN_APPEND(pn, pn2);
02538         } while (js_MatchToken(cx, ts, TOK_COMMA));
02539         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
02540         tc->flags |= TCF_FUN_HEAVYWEIGHT;
02541         break;
02542 #endif /* JS_HAS_EXPORT_IMPORT */
02543 
02544       case TOK_FUNCTION:
02545 #if JS_HAS_XML_SUPPORT
02546         ts->flags |= TSF_KEYWORD_IS_NAME;
02547         tt = js_PeekToken(cx, ts);
02548         ts->flags &= ~TSF_KEYWORD_IS_NAME;
02549         if (tt == TOK_DBLCOLON)
02550             goto expression;
02551 #endif
02552         return FunctionStmt(cx, ts, tc);
02553 
02554       case TOK_IF:
02555         /* An IF node has three kids: condition, then, and optional else. */
02556         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
02557         if (!pn)
02558             return NULL;
02559         pn1 = Condition(cx, ts, tc);
02560         if (!pn1)
02561             return NULL;
02562         js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
02563         pn2 = Statement(cx, ts, tc);
02564         if (!pn2)
02565             return NULL;
02566         ts->flags |= TSF_OPERAND;
02567         if (js_MatchToken(cx, ts, TOK_ELSE)) {
02568             ts->flags &= ~TSF_OPERAND;
02569             stmtInfo.type = STMT_ELSE;
02570             pn3 = Statement(cx, ts, tc);
02571             if (!pn3)
02572                 return NULL;
02573             pn->pn_pos.end = pn3->pn_pos.end;
02574         } else {
02575             ts->flags &= ~TSF_OPERAND;
02576             pn3 = NULL;
02577             pn->pn_pos.end = pn2->pn_pos.end;
02578         }
02579         js_PopStatement(tc);
02580         pn->pn_kid1 = pn1;
02581         pn->pn_kid2 = pn2;
02582         pn->pn_kid3 = pn3;
02583         return pn;
02584 
02585       case TOK_SWITCH:
02586       {
02587         JSParseNode *pn5, *saveBlock;
02588         JSBool seenDefault = JS_FALSE;
02589 
02590         pn = NewParseNode(cx, ts, PN_BINARY, tc);
02591         if (!pn)
02592             return NULL;
02593         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
02594 
02595         /* pn1 points to the switch's discriminant. */
02596         pn1 = Expr(cx, ts, tc);
02597         if (!pn1)
02598             return NULL;
02599 
02600         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
02601         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
02602 
02603         /* pn2 is a list of case nodes. The default case has pn_left == NULL */
02604         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
02605         if (!pn2)
02606             return NULL;
02607         saveBlock = tc->blockNode;
02608         tc->blockNode = pn2;
02609         PN_INIT_LIST(pn2);
02610 
02611         js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
02612 
02613         while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
02614             switch (tt) {
02615               case TOK_DEFAULT:
02616                 if (seenDefault) {
02617                     js_ReportCompileErrorNumber(cx, ts,
02618                                                 JSREPORT_TS | JSREPORT_ERROR,
02619                                                 JSMSG_TOO_MANY_DEFAULTS);
02620                     return NULL;
02621                 }
02622                 seenDefault = JS_TRUE;
02623                 /* fall through */
02624 
02625               case TOK_CASE:
02626                 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
02627                 if (!pn3)
02628                     return NULL;
02629                 if (tt == TOK_DEFAULT) {
02630                     pn3->pn_left = NULL;
02631                 } else {
02632                     pn3->pn_left = Expr(cx, ts, tc);
02633                     if (!pn3->pn_left)
02634                         return NULL;
02635                 }
02636                 PN_APPEND(pn2, pn3);
02637                 if (pn2->pn_count == JS_BIT(16)) {
02638                     js_ReportCompileErrorNumber(cx, ts,
02639                                                 JSREPORT_TS | JSREPORT_ERROR,
02640                                                 JSMSG_TOO_MANY_CASES);
02641                     return NULL;
02642                 }
02643                 break;
02644 
02645               case TOK_ERROR:
02646                 return NULL;
02647 
02648               default:
02649                 js_ReportCompileErrorNumber(cx, ts,
02650                                             JSREPORT_TS | JSREPORT_ERROR,
02651                                             JSMSG_BAD_SWITCH);
02652                 return NULL;
02653             }
02654             MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
02655 
02656             pn4 = NewParseNode(cx, ts, PN_LIST, tc);
02657             if (!pn4)
02658                 return NULL;
02659             pn4->pn_type = TOK_LC;
02660             PN_INIT_LIST(pn4);
02661             ts->flags |= TSF_OPERAND;
02662             while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
02663                    tt != TOK_CASE && tt != TOK_DEFAULT) {
02664                 ts->flags &= ~TSF_OPERAND;
02665                 if (tt == TOK_ERROR)
02666                     return NULL;
02667                 pn5 = Statement(cx, ts, tc);
02668                 if (!pn5)
02669                     return NULL;
02670                 pn4->pn_pos.end = pn5->pn_pos.end;
02671                 PN_APPEND(pn4, pn5);
02672                 ts->flags |= TSF_OPERAND;
02673             }
02674             ts->flags &= ~TSF_OPERAND;
02675 
02676             /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
02677             if (pn4->pn_head)
02678                 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
02679             pn3->pn_pos.end = pn4->pn_pos.end;
02680             pn3->pn_right = pn4;
02681         }
02682 
02683         /*
02684          * Handle the case where there was a let declaration in any case in
02685          * the switch body, but not within an inner block.  If it replaced
02686          * tc->blockNode with a new block node then we must refresh pn2 and
02687          * then restore tc->blockNode.
02688          */
02689         if (tc->blockNode != pn2)
02690             pn2 = tc->blockNode;
02691         tc->blockNode = saveBlock;
02692         js_PopStatement(tc);
02693 
02694         pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
02695         pn->pn_left = pn1;
02696         pn->pn_right = pn2;
02697         return pn;
02698       }
02699 
02700       case TOK_WHILE:
02701         pn = NewParseNode(cx, ts, PN_BINARY, tc);
02702         if (!pn)
02703             return NULL;
02704         js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
02705         pn2 = Condition(cx, ts, tc);
02706         if (!pn2)
02707             return NULL;
02708         pn->pn_left = pn2;
02709         pn2 = Statement(cx, ts, tc);
02710         if (!pn2)
02711             return NULL;
02712         js_PopStatement(tc);
02713         pn->pn_pos.end = pn2->pn_pos.end;
02714         pn->pn_right = pn2;
02715         return pn;
02716 
02717       case TOK_DO:
02718         pn = NewParseNode(cx, ts, PN_BINARY, tc);
02719         if (!pn)
02720             return NULL;
02721         js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
02722         pn2 = Statement(cx, ts, tc);
02723         if (!pn2)
02724             return NULL;
02725         pn->pn_left = pn2;
02726         MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
02727         pn2 = Condition(cx, ts, tc);
02728         if (!pn2)
02729             return NULL;
02730         js_PopStatement(tc);
02731         pn->pn_pos.end = pn2->pn_pos.end;
02732         pn->pn_right = pn2;
02733         if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) {
02734             /*
02735              * All legacy and extended versions must do automatic semicolon
02736              * insertion after do-while.  See the testcase and discussion in
02737              * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
02738              */
02739             (void) js_MatchToken(cx, ts, TOK_SEMI);
02740             return pn;
02741         }
02742         break;
02743 
02744       case TOK_FOR:
02745       {
02746 #if JS_HAS_BLOCK_SCOPE
02747         JSParseNode *pnlet;
02748         JSStmtInfo blockInfo;
02749 
02750         pnlet = NULL;
02751 #endif
02752 
02753         /* A FOR node is binary, left is loop control and right is the body. */
02754         pn = NewParseNode(cx, ts, PN_BINARY, tc);
02755         if (!pn)
02756             return NULL;
02757         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
02758 
02759         pn->pn_op = JSOP_FORIN;
02760         if (js_MatchToken(cx, ts, TOK_NAME)) {
02761             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
02762                 pn->pn_op = JSOP_FOREACH;
02763             else
02764                 js_UngetToken(ts);
02765         }
02766 
02767         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
02768         ts->flags |= TSF_OPERAND;
02769         tt = js_PeekToken(cx, ts);
02770         ts->flags &= ~TSF_OPERAND;
02771         if (tt == TOK_SEMI) {
02772             if (pn->pn_op == JSOP_FOREACH)
02773                 goto bad_for_each;
02774 
02775             /* No initializer -- set first kid of left sub-node to null. */
02776             pn1 = NULL;
02777         } else {
02778             /*
02779              * Set pn1 to a var list or an initializing expression.
02780              *
02781              * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
02782              * of the for statement.  This flag will be used by the RelExpr
02783              * production; if it is set, then the 'in' keyword will not be
02784              * recognized as an operator, leaving it available to be parsed as
02785              * part of a for/in loop.
02786              *
02787              * A side effect of this restriction is that (unparenthesized)
02788              * expressions involving an 'in' operator are illegal in the init
02789              * clause of an ordinary for loop.
02790              */
02791             tc->flags |= TCF_IN_FOR_INIT;
02792             if (tt == TOK_VAR) {
02793                 (void) js_GetToken(cx, ts);
02794                 pn1 = Variables(cx, ts, tc);
02795 #if JS_HAS_BLOCK_SCOPE
02796             } else if (tt == TOK_LET) {
02797                 (void) js_GetToken(cx, ts);
02798                 if (js_PeekToken(cx, ts) == TOK_LP) {
02799                     pn1 = LetBlock(cx, ts, tc, JS_FALSE);
02800                     tt = TOK_LEXICALSCOPE;
02801                 } else {
02802                     pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
02803                     if (!pnlet)
02804                         return NULL;
02805                     pn1 = Variables(cx, ts, tc);
02806                 }
02807 #endif
02808             } else {
02809                 pn1 = Expr(cx, ts, tc);
02810                 if (pn1) {
02811                     while (pn1->pn_type == TOK_RP)
02812                         pn1 = pn1->pn_kid;
02813                 }
02814             }
02815             tc->flags &= ~TCF_IN_FOR_INIT;
02816             if (!pn1)
02817                 return NULL;
02818         }
02819 
02820         /*
02821          * We can be sure that it's a for/in loop if there's still an 'in'
02822          * keyword here, even if JavaScript recognizes 'in' as an operator,
02823          * as we've excluded 'in' from being parsed in RelExpr by setting
02824          * the TCF_IN_FOR_INIT flag in our JSTreeContext.
02825          */
02826         if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
02827             stmtInfo.type = STMT_FOR_IN_LOOP;
02828 
02829             /* Check that the left side of the 'in' is valid. */
02830             JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
02831             if (TOKEN_TYPE_IS_DECL(tt)
02832                 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
02833 #if JS_HAS_DESTRUCTURING
02834                    || (pn->pn_op == JSOP_FORIN &&
02835                        (pn1->pn_head->pn_type == TOK_RC ||
02836                         (pn1->pn_head->pn_type == TOK_RB &&
02837                          pn1->pn_head->pn_count != 2) ||
02838                         (pn1->pn_head->pn_type == TOK_ASSIGN &&
02839                          (pn1->pn_head->pn_left->pn_type != TOK_RB ||
02840                           pn1->pn_head->pn_left->pn_count != 2))))
02841 #endif
02842                   )
02843                 : (pn1->pn_type != TOK_NAME &&
02844                    pn1->pn_type != TOK_DOT &&
02845 #if JS_HAS_DESTRUCTURING
02846                    ((pn->pn_op == JSOP_FORIN)
02847                     ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
02848                     : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
02849 #endif
02850 #if JS_HAS_LVALUE_RETURN
02851                    pn1->pn_type != TOK_LP &&
02852 #endif
02853 #if JS_HAS_XML_SUPPORT
02854                    (pn1->pn_type != TOK_UNARYOP ||
02855                     pn1->pn_op != JSOP_XMLNAME) &&
02856 #endif
02857                    pn1->pn_type != TOK_LB)) {
02858                 js_ReportCompileErrorNumber(cx, ts,
02859                                             JSREPORT_TS | JSREPORT_ERROR,
02860                                             JSMSG_BAD_FOR_LEFTSIDE);
02861                 return NULL;
02862             }
02863 
02864             if (TOKEN_TYPE_IS_DECL(tt)) {
02865                 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
02866                 pn1->pn_extra |= PNX_FORINVAR;
02867 
02868                 /*
02869                  * Generate a final POP only if the variable is a simple name
02870                  * (which means it is not a destructuring left-hand side) and
02871                  * it has an initializer.
02872                  */
02873                 pn2 = pn1->pn_head;
02874                 if (pn2->pn_type == TOK_NAME && pn2->pn_expr)
02875                     pn1->pn_extra |= PNX_POPVAR;
02876             } else {
02877                 pn2 = pn1;
02878 #if JS_HAS_LVALUE_RETURN
02879                 if (pn2->pn_type == TOK_LP)
02880                     pn2->pn_op = JSOP_SETCALL;
02881 #endif
02882 #if JS_HAS_XML_SUPPORT
02883                 if (pn2->pn_type == TOK_UNARYOP)
02884                     pn2->pn_op = JSOP_BINDXMLNAME;
02885 #endif
02886             }
02887 
02888             switch (pn2->pn_type) {
02889               case TOK_NAME:
02890                 /* Beware 'for (arguments in ...)' with or without a 'var'. */
02891                 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
02892                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
02893                 break;
02894 
02895 #if JS_HAS_DESTRUCTURING
02896               case TOK_ASSIGN:
02897                 pn2 = pn2->pn_left;
02898                 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
02899                 /* FALL THROUGH */
02900               case TOK_RB:
02901               case TOK_RC:
02902                 /* Check for valid lvalues in var-less destructuring for-in. */
02903                 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
02904                     return NULL;
02905 
02906                 /* Destructuring for-in requires [key, value] enumeration. */
02907                 if (pn->pn_op != JSOP_FOREACH)
02908                     pn->pn_op = JSOP_FOREACHKEYVAL;
02909                 break;
02910 #endif
02911 
02912               default:;
02913             }
02914 
02915             /* Parse the object expression as the right operand of 'in'. */
02916             pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
02917             if (!pn2)
02918                 return NULL;
02919             pn->pn_left = pn2;
02920         } else {
02921             if (pn->pn_op == JSOP_FOREACH)
02922                 goto bad_for_each;
02923             pn->pn_op = JSOP_NOP;
02924 
02925             /* Parse the loop condition or null into pn2. */
02926             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
02927             ts->flags |= TSF_OPERAND;
02928             tt = js_PeekToken(cx, ts);
02929             ts->flags &= ~TSF_OPERAND;
02930             if (tt == TOK_SEMI) {
02931                 pn2 = NULL;
02932             } else {
02933                 pn2 = Expr(cx, ts, tc);
02934                 if (!pn2)
02935                     return NULL;
02936             }
02937 
02938             /* Parse the update expression or null into pn3. */
02939             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
02940             ts->flags |= TSF_OPERAND;
02941             tt = js_PeekToken(cx, ts);
02942             ts->flags &= ~TSF_OPERAND;
02943             if (tt == TOK_RP) {
02944                 pn3 = NULL;
02945             } else {
02946                 pn3 = Expr(cx, ts, tc);
02947                 if (!pn3)
02948                     return NULL;
02949             }
02950 
02951             /* Build the RESERVED node to use as the left kid of pn. */
02952             pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
02953             if (!pn4)
02954                 return NULL;
02955             pn4->pn_type = TOK_RESERVED;
02956             pn4->pn_op = JSOP_NOP;
02957             pn4->pn_kid1 = pn1;
02958             pn4->pn_kid2 = pn2;
02959             pn4->pn_kid3 = pn3;
02960             pn->pn_left = pn4;
02961         }
02962 
02963         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
02964 
02965         /* Parse the loop body into pn->pn_right. */
02966         pn2 = Statement(cx, ts, tc);
02967         if (!pn2)
02968             return NULL;
02969         pn->pn_right = pn2;
02970 
02971         /* Record the absolute line number for source note emission. */
02972         pn->pn_pos.end = pn2->pn_pos.end;
02973 
02974 #if JS_HAS_BLOCK_SCOPE
02975         if (pnlet) {
02976             js_PopStatement(tc);
02977             pnlet->pn_expr = pn;
02978             pn = pnlet;
02979         }
02980 #endif
02981         js_PopStatement(tc);
02982         return pn;
02983 
02984       bad_for_each:
02985         js_ReportCompileErrorNumber(cx, pn,
02986                                     JSREPORT_PN | JSREPORT_ERROR,
02987                                     JSMSG_BAD_FOR_EACH_LOOP);
02988         return NULL;
02989       }
02990 
02991       case TOK_TRY: {
02992         JSParseNode *catchList, *lastCatch;
02993 
02994         /*
02995          * try nodes are ternary.
02996          * kid1 is the try Statement
02997          * kid2 is the catch node list or null
02998          * kid3 is the finally Statement
02999          *
03000          * catch nodes are ternary.
03001          * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
03002          * kid2 is the catch guard or null if no guard
03003          * kid3 is the catch block
03004          *
03005          * catch lvalue nodes are either:
03006          *   TOK_NAME for a single identifier
03007          *   TOK_RB or TOK_RC for a destructuring left-hand side
03008          *
03009          * finally nodes are TOK_LC Statement lists.
03010          */
03011         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
03012         if (!pn)
03013             return NULL;
03014         pn->pn_op = JSOP_NOP;
03015 
03016         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
03017         js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
03018         pn->pn_kid1 = Statements(cx, ts, tc);
03019         if (!pn->pn_kid1)
03020             return NULL;
03021         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
03022         js_PopStatement(tc);
03023 
03024         catchList = NULL;
03025         tt = js_GetToken(cx, ts);
03026         if (tt == TOK_CATCH) {
03027             catchList = NewParseNode(cx, ts, PN_LIST, tc);
03028             if (!catchList)
03029                 return NULL;
03030             catchList->pn_type = TOK_RESERVED;
03031             PN_INIT_LIST(catchList);
03032             lastCatch = NULL;
03033 
03034             do {
03035                 JSParseNode *pnblock;
03036                 BindData data;
03037 
03038                 /* Check for another catch after unconditional catch. */
03039                 if (lastCatch && !lastCatch->pn_kid2) {
03040                     js_ReportCompileErrorNumber(cx, ts,
03041                                                 JSREPORT_TS | JSREPORT_ERROR,
03042                                                 JSMSG_CATCH_AFTER_GENERAL);
03043                     return NULL;
03044                 }
03045 
03046                 /*
03047                  * Create a lexical scope node around the whole catch clause,
03048                  * including the head.
03049                  */
03050                 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
03051                 if (!pnblock)
03052                     return NULL;
03053                 stmtInfo.type = STMT_CATCH;
03054 
03055                 /*
03056                  * Legal catch forms are:
03057                  *   catch (lhs)
03058                  *   catch (lhs if <boolean_expression>)
03059                  * where lhs is a name or a destructuring left-hand side.
03060                  * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
03061                  */
03062                 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
03063                 if (!pn2)
03064                     return NULL;
03065                 pnblock->pn_expr = pn2;
03066                 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
03067 
03068                 /*
03069                  * Contrary to ECMA Ed. 3, the catch variable is lexically
03070                  * scoped, not a property of a new Object instance.  This is
03071                  * an intentional change that anticipates ECMA Ed. 4.
03072                  */
03073                 data.pn = NULL;
03074                 data.ts = ts;
03075                 data.obj = tc->blockChain;
03076                 data.op = JSOP_NOP;
03077                 data.binder = BindLet;
03078                 data.u.let.index = 0;
03079                 data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
03080 
03081                 tt = js_GetToken(cx, ts);
03082                 switch (tt) {
03083 #if JS_HAS_DESTRUCTURING
03084                   case TOK_LB:
03085                   case TOK_LC:
03086                     pn3 = DestructuringExpr(cx, &data, tc, tt);
03087                     if (!pn3)
03088                         return NULL;
03089                     break;
03090 #endif
03091 
03092                   case TOK_NAME:
03093                     label = CURRENT_TOKEN(ts).t_atom;
03094                     if (!data.binder(cx, &data, label, tc))
03095                         return NULL;
03096 
03097                     pn3 = NewParseNode(cx, ts, PN_NAME, tc);
03098                     if (!pn3)
03099                         return NULL;
03100                     pn3->pn_atom = label;
03101                     pn3->pn_expr = NULL;
03102                     pn3->pn_slot = 0;
03103                     pn3->pn_attrs = 0;
03104                     break;
03105 
03106                   default:
03107                     js_ReportCompileErrorNumber(cx, ts,
03108                                                 JSREPORT_TS | JSREPORT_ERROR,
03109                                                 JSMSG_CATCH_IDENTIFIER);
03110                     return NULL;
03111                 }
03112 
03113                 pn2->pn_kid1 = pn3;
03114                 pn2->pn_kid2 = NULL;
03115 #if JS_HAS_CATCH_GUARD
03116                 /*
03117                  * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
03118                  * to avoid conflicting with the JS2/ECMAv4 type annotation
03119                  * catchguard syntax.
03120                  */
03121                 if (js_MatchToken(cx, ts, TOK_IF)) {
03122                     pn2->pn_kid2 = Expr(cx, ts, tc);
03123                     if (!pn2->pn_kid2)
03124                         return NULL;
03125                 }
03126 #endif
03127                 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
03128 
03129                 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
03130                 pn2->pn_kid3 = Statements(cx, ts, tc);
03131                 if (!pn2->pn_kid3)
03132                     return NULL;
03133                 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
03134                 js_PopStatement(tc);
03135 
03136                 PN_APPEND(catchList, pnblock);
03137                 lastCatch = pn2;
03138                 ts->flags |= TSF_OPERAND;
03139                 tt = js_GetToken(cx, ts);
03140                 ts->flags &= ~TSF_OPERAND;
03141             } while (tt == TOK_CATCH);
03142         }
03143         pn->pn_kid2 = catchList;
03144 
03145         if (tt == TOK_FINALLY) {
03146             tc->tryCount++;
03147             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
03148             js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
03149             pn->pn_kid3 = Statements(cx, ts, tc);
03150             if (!pn->pn_kid3)
03151                 return NULL;
03152             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
03153             js_PopStatement(tc);
03154         } else {
03155             js_UngetToken(ts);
03156             pn->pn_kid3 = NULL;
03157         }
03158         if (!catchList && !pn->pn_kid3) {
03159             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03160                                         JSMSG_CATCH_OR_FINALLY);
03161             return NULL;
03162         }
03163         tc->tryCount++;
03164         return pn;
03165       }
03166 
03167       case TOK_THROW:
03168         pn = NewParseNode(cx, ts, PN_UNARY, tc);
03169         if (!pn)
03170             return NULL;
03171 
03172         /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
03173         ts->flags |= TSF_OPERAND;
03174         tt = js_PeekTokenSameLine(cx, ts);
03175         ts->flags &= ~TSF_OPERAND;
03176         if (tt == TOK_ERROR)
03177             return NULL;
03178         if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
03179             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03180                                         JSMSG_SYNTAX_ERROR);
03181             return NULL;
03182         }
03183 
03184         pn2 = Expr(cx, ts, tc);
03185         if (!pn2)
03186             return NULL;
03187         pn->pn_pos.end = pn2->pn_pos.end;
03188         pn->pn_op = JSOP_THROW;
03189         pn->pn_kid = pn2;
03190         break;
03191 
03192       /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
03193       case TOK_CATCH:
03194         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03195                                     JSMSG_CATCH_WITHOUT_TRY);
03196         return NULL;
03197 
03198       case TOK_FINALLY:
03199         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03200                                     JSMSG_FINALLY_WITHOUT_TRY);
03201         return NULL;
03202 
03203       case TOK_BREAK:
03204         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
03205         if (!pn)
03206             return NULL;
03207         if (!MatchLabel(cx, ts, pn))
03208             return NULL;
03209         stmt = tc->topStmt;
03210         label = pn->pn_atom;
03211         if (label) {
03212             for (; ; stmt = stmt->down) {
03213                 if (!stmt) {
03214                     js_ReportCompileErrorNumber(cx, ts,
03215                                                 JSREPORT_TS | JSREPORT_ERROR,
03216                                                 JSMSG_LABEL_NOT_FOUND);
03217                     return NULL;
03218                 }
03219                 if (stmt->type == STMT_LABEL && stmt->atom == label)
03220                     break;
03221             }
03222         } else {
03223             for (; ; stmt = stmt->down) {
03224                 if (!stmt) {
03225                     js_ReportCompileErrorNumber(cx, ts,
03226                                                 JSREPORT_TS | JSREPORT_ERROR,
03227                                                 JSMSG_TOUGH_BREAK);
03228                     return NULL;
03229                 }
03230                 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
03231                     break;
03232             }
03233         }
03234         if (label)
03235             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
03236         break;
03237 
03238       case TOK_CONTINUE:
03239         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
03240         if (!pn)
03241             return NULL;
03242         if (!MatchLabel(cx, ts, pn))
03243             return NULL;
03244         stmt = tc->topStmt;
03245         label = pn->pn_atom;
03246         if (label) {
03247             for (stmt2 = NULL; ; stmt = stmt->down) {
03248                 if (!stmt) {
03249                     js_ReportCompileErrorNumber(cx, ts,
03250                                                 JSREPORT_TS | JSREPORT_ERROR,
03251                                                 JSMSG_LABEL_NOT_FOUND);
03252                     return NULL;
03253                 }
03254                 if (stmt->type == STMT_LABEL) {
03255                     if (stmt->atom == label) {
03256                         if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
03257                             js_ReportCompileErrorNumber(cx, ts,
03258                                                         JSREPORT_TS |
03259                                                         JSREPORT_ERROR,
03260                                                         JSMSG_BAD_CONTINUE);
03261                             return NULL;
03262                         }
03263                         break;
03264                     }
03265                 } else {
03266                     stmt2 = stmt;
03267                 }
03268             }
03269         } else {
03270             for (; ; stmt = stmt->down) {
03271                 if (!stmt) {
03272                     js_ReportCompileErrorNumber(cx, ts,
03273                                                 JSREPORT_TS | JSREPORT_ERROR,
03274                                                 JSMSG_BAD_CONTINUE);
03275                     return NULL;
03276                 }
03277                 if (STMT_IS_LOOP(stmt))
03278                     break;
03279             }
03280         }
03281         if (label)
03282             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
03283         break;
03284 
03285       case TOK_WITH:
03286         pn = NewParseNode(cx, ts, PN_BINARY, tc);
03287         if (!pn)
03288             return NULL;
03289         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
03290         pn2 = Expr(cx, ts, tc);
03291         if (!pn2)
03292             return NULL;
03293         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
03294         pn->pn_left = pn2;
03295 
03296         js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
03297         pn2 = Statement(cx, ts, tc);
03298         if (!pn2)
03299             return NULL;
03300         js_PopStatement(tc);
03301 
03302         pn->pn_pos.end = pn2->pn_pos.end;
03303         pn->pn_right = pn2;
03304         tc->flags |= TCF_FUN_HEAVYWEIGHT;
03305         return pn;
03306 
03307       case TOK_VAR:
03308         pn = Variables(cx, ts, tc);
03309         if (!pn)
03310             return NULL;
03311 
03312         /* Tell js_EmitTree to generate a final POP. */
03313         pn->pn_extra |= PNX_POPVAR;
03314         break;
03315 
03316 #if JS_HAS_BLOCK_SCOPE
03317       case TOK_LET:
03318       {
03319         JSStmtInfo **sip;
03320         JSObject *obj;
03321         JSAtom *atom;
03322 
03323         /* Check for a let statement or let expression. */
03324         if (js_PeekToken(cx, ts) == TOK_LP) {
03325             pn = LetBlock(cx, ts, tc, JS_TRUE);
03326             if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
03327                 return pn;
03328 
03329             /* Let expressions require automatic semicolon insertion. */
03330             JS_ASSERT(pn->pn_type == TOK_SEMI ||
03331                       pn->pn_op == JSOP_LEAVEBLOCKEXPR);
03332             break;
03333         }
03334 
03335         /*
03336          * This is a let declaration. We must convert the nearest JSStmtInfo
03337          * that is a block or a switch body to be our scope statement. Further
03338          * let declarations in this block will find this scope statement and
03339          * use the same block object. If we are the first let declaration in
03340          * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a
03341          * scope statement) then we also need to set tc->blockNode to be our
03342          * TOK_LEXICALSCOPE.
03343          */
03344         sip = &tc->topScopeStmt;
03345         for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
03346             if (STMT_MAYBE_SCOPE(stmt))
03347                 break;
03348             if (stmt == *sip)
03349                 sip = &stmt->downScope;
03350         }
03351 
03352         if (stmt && (stmt->flags & SIF_SCOPE)) {
03353             JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom));
03354             obj = tc->blockChain;
03355         } else {
03356             if (!stmt) {
03357                 /*
03358                  * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749
03359                  *
03360                  * This is a hard case that requires more work. In particular,
03361                  * in many cases, we're trying to emit code as we go. However,
03362                  * this means that we haven't necessarily finished processing
03363                  * all let declarations in the implicit top-level block when
03364                  * we emit a reference to one of them.  For now, punt on this
03365                  * and pretend this is a var declaration.
03366                  */
03367                 CURRENT_TOKEN(ts).type = TOK_VAR;
03368                 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
03369 
03370                 pn = Variables(cx, ts, tc);
03371                 if (!pn)
03372                     return NULL;
03373                 pn->pn_extra |= PNX_POPVAR;
03374                 break;
03375             }
03376 
03377             /* Convert the block statement into a scope statement. */
03378             obj = js_NewBlockObject(cx);
03379             if (!obj)
03380                 return NULL;
03381             atom = js_AtomizeObject(cx, obj, 0);
03382             if (!atom)
03383                 return NULL;
03384 
03385             /*
03386              * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
03387              * list stack, if it isn't already there.  If it is there, but it
03388              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
03389              * block.
03390              */
03391             JS_ASSERT(!(stmt->flags & SIF_SCOPE));
03392             stmt->flags |= SIF_SCOPE;
03393             if (stmt != *sip) {
03394                 JS_ASSERT(!stmt->downScope);
03395                 JS_ASSERT(stmt->type == STMT_BLOCK ||
03396                           stmt->type == STMT_SWITCH ||
03397                           stmt->type == STMT_TRY ||
03398                           stmt->type == STMT_FINALLY);
03399                 stmt->downScope = *sip;
03400                 *sip = stmt;
03401             } else {
03402                 JS_ASSERT(stmt->type == STMT_CATCH);
03403                 JS_ASSERT(stmt->downScope);
03404             }
03405 
03406             obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
03407             tc->blockChain = obj;
03408             stmt->atom = atom;
03409 
03410 #ifdef DEBUG
03411             pn1 = tc->blockNode;
03412             JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
03413 #endif
03414 
03415             /* Create a new lexical scope node for these statements. */
03416             pn1 = NewParseNode(cx, ts, PN_NAME, tc);
03417             if (!pn1)
03418                 return NULL;
03419 
03420             pn1->pn_type = TOK_LEXICALSCOPE;
03421             pn1->pn_op = JSOP_LEAVEBLOCK;
03422             pn1->pn_pos = tc->blockNode->pn_pos;
03423             pn1->pn_atom = atom;
03424             pn1->pn_expr = tc->blockNode;
03425             pn1->pn_slot = -1;
03426             pn1->pn_attrs = 0;
03427             tc->blockNode = pn1;
03428         }
03429 
03430         pn = Variables(cx, ts, tc);
03431         if (!pn)
03432             return NULL;
03433         pn->pn_extra = PNX_POPVAR;
03434         break;
03435       }
03436 #endif /* JS_HAS_BLOCK_SCOPE */
03437 
03438       case TOK_RETURN:
03439         pn = ReturnOrYield(cx, ts, tc, Expr);
03440         if (!pn)
03441             return NULL;
03442         break;
03443 
03444       case TOK_LC:
03445       {
03446         uintN oldflags;
03447 
03448         oldflags = tc->flags;
03449         tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
03450         js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
03451         pn = Statements(cx, ts, tc);
03452         if (!pn)
03453             return NULL;
03454 
03455         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
03456         js_PopStatement(tc);
03457 
03458         /*
03459          * If we contain a function statement and our container is top-level
03460          * or another block, flag pn to preserve braces when decompiling.
03461          */
03462         if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
03463             (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
03464             pn->pn_extra |= PNX_NEEDBRACES;
03465         }
03466         tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
03467         return pn;
03468       }
03469 
03470       case TOK_EOL:
03471       case TOK_SEMI:
03472         pn = NewParseNode(cx, ts, PN_UNARY, tc);
03473         if (!pn)
03474             return NULL;
03475         pn->pn_type = TOK_SEMI;
03476         pn->pn_kid = NULL;
03477         return pn;
03478 
03479 #if JS_HAS_DEBUGGER_KEYWORD
03480       case TOK_DEBUGGER:
03481         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
03482         if (!pn)
03483             return NULL;
03484         pn->pn_type = TOK_DEBUGGER;
03485         tc->flags |= TCF_FUN_HEAVYWEIGHT;
03486         break;
03487 #endif /* JS_HAS_DEBUGGER_KEYWORD */
03488 
03489 #if JS_HAS_XML_SUPPORT
03490       case TOK_DEFAULT:
03491         pn = NewParseNode(cx, ts, PN_UNARY, tc);
03492         if (!pn)
03493             return NULL;
03494         if (!js_MatchToken(cx, ts, TOK_NAME) ||
03495             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
03496             !js_MatchToken(cx, ts, TOK_NAME) ||
03497             CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
03498             !js_MatchToken(cx, ts, TOK_ASSIGN) ||
03499             CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
03500             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03501                                         JSMSG_BAD_DEFAULT_XML_NAMESPACE);
03502             return NULL;
03503         }
03504         pn2 = Expr(cx, ts, tc);
03505         if (!pn2)
03506             return NULL;
03507         pn->pn_op = JSOP_DEFXMLNS;
03508         pn->pn_pos.end = pn2->pn_pos.end;
03509         pn->pn_kid = pn2;
03510         tc->flags |= TCF_HAS_DEFXMLNS;
03511         break;
03512 #endif
03513 
03514       case TOK_ERROR:
03515         return NULL;
03516 
03517       default:
03518 #if JS_HAS_XML_SUPPORT
03519       expression:
03520 #endif
03521         js_UngetToken(ts);
03522         pn2 = Expr(cx, ts, tc);
03523         if (!pn2)
03524             return NULL;
03525 
03526         if (js_PeekToken(cx, ts) == TOK_COLON) {
03527             if (pn2->pn_type != TOK_NAME) {
03528                 js_ReportCompileErrorNumber(cx, ts,
03529                                             JSREPORT_TS | JSREPORT_ERROR,
03530                                             JSMSG_BAD_LABEL);
03531                 return NULL;
03532             }
03533             label = pn2->pn_atom;
03534             for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
03535                 if (stmt->type == STMT_LABEL && stmt->atom == label) {
03536                     js_ReportCompileErrorNumber(cx, ts,
03537                                                 JSREPORT_TS | JSREPORT_ERROR,
03538                                                 JSMSG_DUPLICATE_LABEL);
03539                     return NULL;
03540                 }
03541             }
03542             (void) js_GetToken(cx, ts);
03543 
03544             /* Push a label struct and parse the statement. */
03545             js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
03546             stmtInfo.atom = label;
03547             pn = Statement(cx, ts, tc);
03548             if (!pn)
03549                 return NULL;
03550 
03551             /* Normalize empty statement to empty block for the decompiler. */
03552             if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
03553                 pn->pn_type = TOK_LC;
03554                 pn->pn_arity = PN_LIST;
03555                 PN_INIT_LIST(pn);
03556             }
03557 
03558             /* Pop the label, set pn_expr, and return early. */
03559             js_PopStatement(tc);
03560             pn2->pn_type = TOK_COLON;
03561             pn2->pn_pos.end = pn->pn_pos.end;
03562             pn2->pn_expr = pn;
03563             return pn2;
03564         }
03565 
03566         pn = NewParseNode(cx, ts, PN_UNARY, tc);
03567         if (!pn)
03568             return NULL;
03569         pn->pn_type = TOK_SEMI;
03570         pn->pn_pos = pn2->pn_pos;
03571         pn->pn_kid = pn2;
03572         break;
03573     }
03574 
03575     /* Check termination of this primitive statement. */
03576     if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
03577         ts->flags |= TSF_OPERAND;
03578         tt = js_PeekTokenSameLine(cx, ts);
03579         ts->flags &= ~TSF_OPERAND;
03580         if (tt == TOK_ERROR)
03581             return NULL;
03582         if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
03583             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03584                                         JSMSG_SEMI_BEFORE_STMNT);
03585             return NULL;
03586         }
03587     }
03588 
03589     (void) js_MatchToken(cx, ts, TOK_SEMI);
03590     return pn;
03591 }
03592 
03593 static JSParseNode *
03594 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03595 {
03596     JSTokenType tt;
03597     JSBool let;
03598     JSStmtInfo *scopeStmt;
03599     BindData data;
03600     JSParseNode *pn, *pn2;
03601     JSStackFrame *fp;
03602     JSAtom *atom;
03603 
03604     /*
03605      * The three options here are:
03606      * - TOK_LET: We are parsing a let declaration.
03607      * - TOK_LP: We are parsing the head of a let block.
03608      * - Otherwise, we're parsing var declarations.
03609      */
03610     tt = CURRENT_TOKEN(ts).type;
03611     let = (tt == TOK_LET || tt == TOK_LP);
03612     JS_ASSERT(let || tt == TOK_VAR);
03613 
03614     /* Make sure that Statement set the tree context up correctly. */
03615     scopeStmt = tc->topScopeStmt;
03616     if (let) {
03617         while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
03618             JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
03619             scopeStmt = scopeStmt->downScope;
03620         }
03621         JS_ASSERT(scopeStmt);
03622     }
03623 
03624     data.pn = NULL;
03625     data.ts = ts;
03626     data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
03627     data.binder = let ? BindLet : BindVarOrConst;
03628     pn = NewParseNode(cx, ts, PN_LIST, tc);
03629     if (!pn)
03630         return NULL;
03631     pn->pn_op = data.op;
03632     PN_INIT_LIST(pn);
03633 
03634     /*
03635      * The tricky part of this code is to create special parsenode opcodes for
03636      * getting and setting variables (which will be stored as special slots in
03637      * the frame).  The most complicated case is an eval() inside a function.
03638      * If the evaluated string references variables in the enclosing function,
03639      * then we need to generate the special variable opcodes.  We determine
03640      * this by looking up the variable's id in the current variable object.
03641      * Fortunately, we can avoid doing this for let declared variables.
03642      */
03643     fp = cx->fp;
03644     if (let) {
03645         JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom));
03646         data.obj = tc->blockChain;
03647         data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj);
03648         data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS;
03649     } else {
03650         data.obj = fp->varobj;
03651         data.u.var.fun = fp->fun;
03652         data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj);
03653         if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) {
03654             /* We are compiling code inside a function */
03655             data.u.var.getter = js_GetLocalVariable;
03656             data.u.var.setter = js_SetLocalVariable;
03657         } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) {
03658             /* We are compiling code from an eval inside a function */
03659             data.u.var.getter = js_GetCallVariable;
03660             data.u.var.setter = js_SetCallVariable;
03661         } else {
03662             data.u.var.getter = data.u.var.clasp->getProperty;
03663             data.u.var.setter = data.u.var.clasp->setProperty;
03664         }
03665 
03666         data.u.var.attrs = (data.op == JSOP_DEFCONST)
03667                            ? JSPROP_PERMANENT | JSPROP_READONLY
03668                            : JSPROP_PERMANENT;
03669     }
03670 
03671     do {
03672         tt = js_GetToken(cx, ts);
03673 #if JS_HAS_DESTRUCTURING
03674         if (tt == TOK_LB || tt == TOK_LC) {
03675             pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
03676             if (!pn2)
03677                 return NULL;
03678 
03679             if ((tc->flags & TCF_IN_FOR_INIT) &&
03680                 js_PeekToken(cx, ts) == TOK_IN) {
03681                 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
03682                     return NULL;
03683                 PN_APPEND(pn, pn2);
03684                 continue;
03685             }
03686 
03687             MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
03688             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
03689                 goto bad_var_init;
03690 
03691             pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP,
03692                             pn2, AssignExpr(cx, ts, tc),
03693                             tc);
03694             if (!pn2 ||
03695                 !CheckDestructuring(cx, &data,
03696                                     pn2->pn_left, pn2->pn_right,
03697                                     tc)) {
03698                 return NULL;
03699             }
03700             PN_APPEND(pn, pn2);
03701             continue;
03702         }
03703 #endif
03704 
03705         if (tt != TOK_NAME) {
03706             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03707                                         JSMSG_NO_VARIABLE_NAME);
03708             return NULL;
03709         }
03710         atom = CURRENT_TOKEN(ts).t_atom;
03711         if (!data.binder(cx, &data, atom, tc))
03712             return NULL;
03713 
03714         pn2 = NewParseNode(cx, ts, PN_NAME, tc);
03715         if (!pn2)
03716             return NULL;
03717         pn2->pn_op = JSOP_NAME;
03718         pn2->pn_atom = atom;
03719         pn2->pn_expr = NULL;
03720         pn2->pn_slot = -1;
03721         pn2->pn_attrs = let ? 0 : data.u.var.attrs;
03722         PN_APPEND(pn, pn2);
03723 
03724         if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
03725             if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
03726                 goto bad_var_init;
03727 
03728             pn2->pn_expr = AssignExpr(cx, ts, tc);
03729             if (!pn2->pn_expr)
03730                 return NULL;
03731             pn2->pn_op = (!let && data.op == JSOP_DEFCONST)
03732                          ? JSOP_SETCONST
03733                          : JSOP_SETNAME;
03734             if (!let && atom == cx->runtime->atomState.argumentsAtom)
03735                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
03736         }
03737     } while (js_MatchToken(cx, ts, TOK_COMMA));
03738 
03739     pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
03740     return pn;
03741 
03742 bad_var_init:
03743     js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03744                                 JSMSG_BAD_VAR_INIT);
03745     return NULL;
03746 }
03747 
03748 static JSParseNode *
03749 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03750 {
03751     JSParseNode *pn, *pn2;
03752 
03753     pn = AssignExpr(cx, ts, tc);
03754     if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
03755         pn2 = NewParseNode(cx, ts, PN_LIST, tc);
03756         if (!pn2)
03757             return NULL;
03758         pn2->pn_pos.begin = pn->pn_pos.begin;
03759         PN_INIT_LIST_1(pn2, pn);
03760         pn = pn2;
03761         do {
03762 #if JS_HAS_GENERATORS
03763             pn2 = PN_LAST(pn);
03764             if (pn2->pn_type == TOK_YIELD) {
03765                 js_ReportCompileErrorNumber(cx, pn2,
03766                                             JSREPORT_PN | JSREPORT_ERROR,
03767                                             JSMSG_BAD_YIELD_SYNTAX);
03768                 return NULL;
03769             }
03770 #endif
03771             pn2 = AssignExpr(cx, ts, tc);
03772             if (!pn2)
03773                 return NULL;
03774             PN_APPEND(pn, pn2);
03775         } while (js_MatchToken(cx, ts, TOK_COMMA));
03776         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
03777     }
03778     return pn;
03779 }
03780 
03781 static JSParseNode *
03782 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03783 {
03784     JSParseNode *pn, *pn2;
03785     JSTokenType tt;
03786     JSOp op;
03787 
03788     CHECK_RECURSION();
03789 
03790 #if JS_HAS_GENERATORS
03791     ts->flags |= TSF_OPERAND;
03792     if (js_MatchToken(cx, ts, TOK_YIELD)) {
03793         ts->flags &= ~TSF_OPERAND;
03794         return ReturnOrYield(cx, ts, tc, AssignExpr);
03795     }
03796     ts->flags &= ~TSF_OPERAND;
03797 #endif
03798 
03799     pn = CondExpr(cx, ts, tc);
03800     if (!pn)
03801         return NULL;
03802 
03803     tt = js_GetToken(cx, ts);
03804 #if JS_HAS_GETTER_SETTER
03805     if (tt == TOK_NAME) {
03806         tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
03807         if (tt == TOK_ERROR)
03808             return NULL;
03809     }
03810 #endif
03811     if (tt != TOK_ASSIGN) {
03812         js_UngetToken(ts);
03813         return pn;
03814     }
03815 
03816     op = CURRENT_TOKEN(ts).t_op;
03817     for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
03818         continue;
03819     switch (pn2->pn_type) {
03820       case TOK_NAME:
03821         pn2->pn_op = JSOP_SETNAME;
03822         if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
03823             tc->flags |= TCF_FUN_HEAVYWEIGHT;
03824         break;
03825       case TOK_DOT:
03826         pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
03827                      ? JSOP_SETMETHOD
03828                      : JSOP_SETPROP;
03829         break;
03830       case TOK_LB:
03831         pn2->pn_op = JSOP_SETELEM;
03832         break;
03833 #if JS_HAS_DESTRUCTURING
03834       case TOK_RB:
03835       case TOK_RC:
03836         if (op != JSOP_NOP) {
03837             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03838                                         JSMSG_BAD_DESTRUCT_ASS);
03839             return NULL;
03840         }
03841         pn = AssignExpr(cx, ts, tc);
03842         if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
03843             return NULL;
03844         return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc);
03845 #endif
03846 #if JS_HAS_LVALUE_RETURN
03847       case TOK_LP:
03848         JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
03849         pn2->pn_op = JSOP_SETCALL;
03850         break;
03851 #endif
03852 #if JS_HAS_XML_SUPPORT
03853       case TOK_UNARYOP:
03854         if (pn2->pn_op == JSOP_XMLNAME) {
03855             pn2->pn_op = JSOP_SETXMLNAME;
03856             break;
03857         }
03858         /* FALL THROUGH */
03859 #endif
03860       default:
03861         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
03862                                     JSMSG_BAD_LEFTSIDE_OF_ASS);
03863         return NULL;
03864     }
03865 
03866     return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
03867 }
03868 
03869 static JSParseNode *
03870 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03871 {
03872     JSParseNode *pn, *pn1, *pn2, *pn3;
03873     uintN oldflags;
03874 
03875     pn = OrExpr(cx, ts, tc);
03876     if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
03877         pn1 = pn;
03878         pn = NewParseNode(cx, ts, PN_TERNARY, tc);
03879         if (!pn)
03880             return NULL;
03881         /*
03882          * Always accept the 'in' operator in the middle clause of a ternary,
03883          * where it's unambiguous, even if we might be parsing the init of a
03884          * for statement.
03885          */
03886         oldflags = tc->flags;
03887         tc->flags &= ~TCF_IN_FOR_INIT;
03888         pn2 = AssignExpr(cx, ts, tc);
03889         tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
03890 
03891         if (!pn2)
03892             return NULL;
03893         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
03894         pn3 = AssignExpr(cx, ts, tc);
03895         if (!pn3)
03896             return NULL;
03897         pn->pn_pos.begin = pn1->pn_pos.begin;
03898         pn->pn_pos.end = pn3->pn_pos.end;
03899         pn->pn_kid1 = pn1;
03900         pn->pn_kid2 = pn2;
03901         pn->pn_kid3 = pn3;
03902     }
03903     return pn;
03904 }
03905 
03906 static JSParseNode *
03907 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03908 {
03909     JSParseNode *pn;
03910 
03911     pn = AndExpr(cx, ts, tc);
03912     if (pn && js_MatchToken(cx, ts, TOK_OR))
03913         pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
03914     return pn;
03915 }
03916 
03917 static JSParseNode *
03918 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03919 {
03920     JSParseNode *pn;
03921 
03922     pn = BitOrExpr(cx, ts, tc);
03923     if (pn && js_MatchToken(cx, ts, TOK_AND))
03924         pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
03925     return pn;
03926 }
03927 
03928 static JSParseNode *
03929 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03930 {
03931     JSParseNode *pn;
03932 
03933     pn = BitXorExpr(cx, ts, tc);
03934     while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
03935         pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
03936                        tc);
03937     }
03938     return pn;
03939 }
03940 
03941 static JSParseNode *
03942 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03943 {
03944     JSParseNode *pn;
03945 
03946     pn = BitAndExpr(cx, ts, tc);
03947     while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
03948         pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
03949                        tc);
03950     }
03951     return pn;
03952 }
03953 
03954 static JSParseNode *
03955 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03956 {
03957     JSParseNode *pn;
03958 
03959     pn = EqExpr(cx, ts, tc);
03960     while (pn && js_MatchToken(cx, ts, TOK_BITAND))
03961         pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
03962     return pn;
03963 }
03964 
03965 static JSParseNode *
03966 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03967 {
03968     JSParseNode *pn;
03969     JSOp op;
03970 
03971     pn = RelExpr(cx, ts, tc);
03972     while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
03973         op = CURRENT_TOKEN(ts).t_op;
03974         pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
03975     }
03976     return pn;
03977 }
03978 
03979 static JSParseNode *
03980 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
03981 {
03982     JSParseNode *pn;
03983     JSTokenType tt;
03984     JSOp op;
03985     uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
03986 
03987     /*
03988      * Uses of the in operator in ShiftExprs are always unambiguous,
03989      * so unset the flag that prohibits recognizing it.
03990      */
03991     tc->flags &= ~TCF_IN_FOR_INIT;
03992 
03993     pn = ShiftExpr(cx, ts, tc);
03994     while (pn &&
03995            (js_MatchToken(cx, ts, TOK_RELOP) ||
03996             /*
03997              * Recognize the 'in' token as an operator only if we're not
03998              * currently in the init expr of a for loop.
03999              */
04000             (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
04001             js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
04002         tt = CURRENT_TOKEN(ts).type;
04003         op = CURRENT_TOKEN(ts).t_op;
04004         pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
04005     }
04006     /* Restore previous state of inForInit flag. */
04007     tc->flags |= inForInitFlag;
04008 
04009     return pn;
04010 }
04011 
04012 static JSParseNode *
04013 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04014 {
04015     JSParseNode *pn;
04016     JSOp op;
04017 
04018     pn = AddExpr(cx, ts, tc);
04019     while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
04020         op = CURRENT_TOKEN(ts).t_op;
04021         pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
04022     }
04023     return pn;
04024 }
04025 
04026 static JSParseNode *
04027 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04028 {
04029     JSParseNode *pn;
04030     JSTokenType tt;
04031     JSOp op;
04032 
04033     pn = MulExpr(cx, ts, tc);
04034     while (pn &&
04035            (js_MatchToken(cx, ts, TOK_PLUS) ||
04036             js_MatchToken(cx, ts, TOK_MINUS))) {
04037         tt = CURRENT_TOKEN(ts).type;
04038         op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
04039         pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
04040     }
04041     return pn;
04042 }
04043 
04044 static JSParseNode *
04045 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04046 {
04047     JSParseNode *pn;
04048     JSTokenType tt;
04049     JSOp op;
04050 
04051     pn = UnaryExpr(cx, ts, tc);
04052     while (pn &&
04053            (js_MatchToken(cx, ts, TOK_STAR) ||
04054             js_MatchToken(cx, ts, TOK_DIVOP))) {
04055         tt = CURRENT_TOKEN(ts).type;
04056         op = CURRENT_TOKEN(ts).t_op;
04057         pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
04058     }
04059     return pn;
04060 }
04061 
04062 static JSParseNode *
04063 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
04064            const char *name)
04065 {
04066     while (kid->pn_type == TOK_RP)
04067         kid = kid->pn_kid;
04068     if (kid->pn_type != TOK_NAME &&
04069         kid->pn_type != TOK_DOT &&
04070 #if JS_HAS_LVALUE_RETURN
04071         (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
04072 #endif
04073 #if JS_HAS_XML_SUPPORT
04074         (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
04075 #endif
04076         kid->pn_type != TOK_LB) {
04077         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
04078                                     JSMSG_BAD_OPERAND, name);
04079         return NULL;
04080     }
04081     pn->pn_kid = kid;
04082     return kid;
04083 }
04084 
04085 static const char incop_name_str[][10] = {"increment", "decrement"};
04086 
04087 static JSBool
04088 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04089             JSParseNode *pn, JSParseNode *kid,
04090             JSTokenType tt, JSBool preorder)
04091 {
04092     JSOp op;
04093 
04094     kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
04095     if (!kid)
04096         return JS_FALSE;
04097     switch (kid->pn_type) {
04098       case TOK_NAME:
04099         op = (tt == TOK_INC)
04100              ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
04101              : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
04102         if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
04103             tc->flags |= TCF_FUN_HEAVYWEIGHT;
04104         break;
04105 
04106       case TOK_DOT:
04107         op = (tt == TOK_INC)
04108              ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
04109              : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
04110         break;
04111 
04112 #if JS_HAS_LVALUE_RETURN
04113       case TOK_LP:
04114         JS_ASSERT(kid->pn_op == JSOP_CALL);
04115         kid->pn_op = JSOP_SETCALL;
04116         /* FALL THROUGH */
04117 #endif
04118 #if JS_HAS_XML_SUPPORT
04119       case TOK_UNARYOP:
04120         if (kid->pn_op == JSOP_XMLNAME)
04121             kid->pn_op = JSOP_SETXMLNAME;
04122         /* FALL THROUGH */
04123 #endif
04124       case TOK_LB:
04125         op = (tt == TOK_INC)
04126              ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
04127              : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
04128         break;
04129 
04130       default:
04131         JS_ASSERT(0);
04132         op = JSOP_NOP;
04133     }
04134     pn->pn_op = op;
04135     return JS_TRUE;
04136 }
04137 
04138 static JSParseNode *
04139 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04140 {
04141     JSTokenType tt;
04142     JSParseNode *pn, *pn2;
04143 
04144     CHECK_RECURSION();
04145 
04146     ts->flags |= TSF_OPERAND;
04147     tt = js_GetToken(cx, ts);
04148     ts->flags &= ~TSF_OPERAND;
04149 
04150     switch (tt) {
04151       case TOK_UNARYOP:
04152       case TOK_PLUS:
04153       case TOK_MINUS:
04154         pn = NewParseNode(cx, ts, PN_UNARY, tc);
04155         if (!pn)
04156             return NULL;
04157         pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */
04158         pn->pn_op = CURRENT_TOKEN(ts).t_op;
04159         pn2 = UnaryExpr(cx, ts, tc);
04160         if (!pn2)
04161             return NULL;
04162         pn->pn_pos.end = pn2->pn_pos.end;
04163         pn->pn_kid = pn2;
04164         break;
04165 
04166       case TOK_INC:
04167       case TOK_DEC:
04168         pn = NewParseNode(cx, ts, PN_UNARY, tc);
04169         if (!pn)
04170             return NULL;
04171         pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
04172         if (!pn2)
04173             return NULL;
04174         if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
04175             return NULL;
04176         pn->pn_pos.end = pn2->pn_pos.end;
04177         break;
04178 
04179       case TOK_DELETE:
04180         pn = NewParseNode(cx, ts, PN_UNARY, tc);
04181         if (!pn)
04182             return NULL;
04183         pn2 = UnaryExpr(cx, ts, tc);
04184         if (!pn2)
04185             return NULL;
04186         pn->pn_pos.end = pn2->pn_pos.end;
04187 
04188         /*
04189          * Under ECMA3, deleting any unary expression is valid -- it simply
04190          * returns true. Here we strip off any parentheses.
04191          */
04192         while (pn2->pn_type == TOK_RP)
04193             pn2 = pn2->pn_kid;
04194         pn->pn_kid = pn2;
04195         break;
04196 
04197       case TOK_ERROR:
04198         return NULL;
04199 
04200       default:
04201         js_UngetToken(ts);
04202         pn = MemberExpr(cx, ts, tc, JS_TRUE);
04203         if (!pn)
04204             return NULL;
04205 
04206         /* Don't look across a newline boundary for a postfix incop. */
04207         if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
04208             ts->flags |= TSF_OPERAND;
04209             tt = js_PeekTokenSameLine(cx, ts);
04210             ts->flags &= ~TSF_OPERAND;
04211             if (tt == TOK_INC || tt == TOK_DEC) {
04212                 (void) js_GetToken(cx, ts);
04213                 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
04214                 if (!pn2)
04215                     return NULL;
04216                 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
04217                     return NULL;
04218                 pn2->pn_pos.begin = pn->pn_pos.begin;
04219                 pn = pn2;
04220             }
04221         }
04222         break;
04223     }
04224     return pn;
04225 }
04226 
04227 static JSBool
04228 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04229              JSParseNode *listNode)
04230 {
04231     JSBool matched;
04232 
04233     ts->flags |= TSF_OPERAND;
04234     matched = js_MatchToken(cx, ts, TOK_RP);
04235     ts->flags &= ~TSF_OPERAND;
04236     if (!matched) {
04237         do {
04238             JSParseNode *argNode = AssignExpr(cx, ts, tc);
04239             if (!argNode)
04240                 return JS_FALSE;
04241 #if JS_HAS_GENERATORS
04242             if (argNode->pn_type == TOK_YIELD) {
04243                 js_ReportCompileErrorNumber(cx, argNode,
04244                                             JSREPORT_PN | JSREPORT_ERROR,
04245                                             JSMSG_BAD_YIELD_SYNTAX);
04246                 return JS_FALSE;
04247             }
04248 #endif
04249             PN_APPEND(listNode, argNode);
04250         } while (js_MatchToken(cx, ts, TOK_COMMA));
04251 
04252         if (js_GetToken(cx, ts) != TOK_RP) {
04253             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
04254                                         JSMSG_PAREN_AFTER_ARGS);
04255             return JS_FALSE;
04256         }
04257     }
04258     return JS_TRUE;
04259 }
04260 
04261 static JSParseNode *
04262 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04263            JSBool allowCallSyntax)
04264 {
04265     JSParseNode *pn, *pn2, *pn3;
04266     JSTokenType tt;
04267 
04268     CHECK_RECURSION();
04269 
04270     /* Check for new expression first. */
04271     ts->flags |= TSF_OPERAND;
04272     tt = js_GetToken(cx, ts);
04273     ts->flags &= ~TSF_OPERAND;
04274     if (tt == TOK_NEW) {
04275         pn = NewParseNode(cx, ts, PN_LIST, tc);
04276         if (!pn)
04277             return NULL;
04278         pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
04279         if (!pn2)
04280             return NULL;
04281         pn->pn_op = JSOP_NEW;
04282         PN_INIT_LIST_1(pn, pn2);
04283         pn->pn_pos.begin = pn2->pn_pos.begin;
04284 
04285         if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
04286             return NULL;
04287         if (pn->pn_count > ARGC_LIMIT) {
04288             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04289                                  JSMSG_TOO_MANY_CON_ARGS);
04290             return NULL;
04291         }
04292         pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
04293     } else {
04294         pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
04295         if (!pn)
04296             return NULL;
04297 
04298         if (pn->pn_type == TOK_ANYNAME ||
04299             pn->pn_type == TOK_AT ||
04300             pn->pn_type == TOK_DBLCOLON) {
04301             pn2 = NewOrRecycledNode(cx, tc);
04302             if (!pn2)
04303                 return NULL;
04304             pn2->pn_type = TOK_UNARYOP;
04305             pn2->pn_pos = pn->pn_pos;
04306             pn2->pn_op = JSOP_XMLNAME;
04307             pn2->pn_arity = PN_UNARY;
04308             pn2->pn_kid = pn;
04309             pn2->pn_next = NULL;
04310             pn2->pn_ts = ts;
04311             pn2->pn_source = NULL;
04312             pn = pn2;
04313         }
04314     }
04315 
04316     while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
04317         if (tt == TOK_DOT) {
04318             pn2 = NewParseNode(cx, ts, PN_NAME, tc);
04319             if (!pn2)
04320                 return NULL;
04321             pn2->pn_slot = -1;
04322             pn2->pn_attrs = 0;
04323 #if JS_HAS_XML_SUPPORT
04324             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
04325             tt = js_GetToken(cx, ts);
04326             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
04327             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
04328             if (!pn3)
04329                 return NULL;
04330             tt = pn3->pn_type;
04331             if (tt == TOK_NAME ||
04332                 (tt == TOK_DBLCOLON &&
04333                  pn3->pn_arity == PN_NAME &&
04334                  pn3->pn_expr->pn_type == TOK_FUNCTION)) {
04335                 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
04336                 pn2->pn_expr = pn;
04337                 pn2->pn_atom = pn3->pn_atom;
04338                 RecycleTree(pn3, tc);
04339             } else {
04340                 if (TOKEN_TYPE_IS_XML(tt)) {
04341                     pn2->pn_type = TOK_LB;
04342                     pn2->pn_op = JSOP_GETELEM;
04343                 } else if (tt == TOK_RP) {
04344                     JSParseNode *group = pn3;
04345 
04346                     /* Recycle the useless TOK_RP/JSOP_GROUP node. */
04347                     pn3 = group->pn_kid;
04348                     group->pn_kid = NULL;
04349                     RecycleTree(group, tc);
04350                     pn2->pn_type = TOK_FILTER;
04351                     pn2->pn_op = JSOP_FILTER;
04352 
04353                     /* A filtering predicate is like a with statement. */
04354                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
04355                 } else {
04356                     js_ReportCompileErrorNumber(cx, ts,
04357                                                 JSREPORT_TS | JSREPORT_ERROR,
04358                                                 JSMSG_NAME_AFTER_DOT);
04359                     return NULL;
04360                 }
04361                 pn2->pn_arity = PN_BINARY;
04362                 pn2->pn_left = pn;
04363                 pn2->pn_right = pn3;
04364             }
04365 #else
04366             ts->flags |= TSF_KEYWORD_IS_NAME;
04367             MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
04368             ts->flags &= ~TSF_KEYWORD_IS_NAME;
04369             pn2->pn_op = JSOP_GETPROP;
04370             pn2->pn_expr = pn;
04371             pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
04372 #endif
04373             pn2->pn_pos.begin = pn->pn_pos.begin;
04374             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
04375 #if JS_HAS_XML_SUPPORT
04376         } else if (tt == TOK_DBLDOT) {
04377             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
04378             if (!pn2)
04379                 return NULL;
04380             ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
04381             tt = js_GetToken(cx, ts);
04382             ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
04383             pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
04384             if (!pn3)
04385                 return NULL;
04386             tt = pn3->pn_type;
04387             if (tt == TOK_NAME) {
04388                 pn3->pn_type = TOK_STRING;
04389                 pn3->pn_arity = PN_NULLARY;
04390                 pn3->pn_op = JSOP_QNAMEPART;
04391             } else if (!TOKEN_TYPE_IS_XML(tt)) {
04392                 js_ReportCompileErrorNumber(cx, ts,
04393                                             JSREPORT_TS | JSREPORT_ERROR,
04394                                             JSMSG_NAME_AFTER_DOT);
04395                 return NULL;
04396             }
04397             pn2->pn_op = JSOP_DESCENDANTS;
04398             pn2->pn_left = pn;
04399             pn2->pn_right = pn3;
04400             pn2->pn_pos.begin = pn->pn_pos.begin;
04401             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
04402 #endif
04403         } else if (tt == TOK_LB) {
04404             pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
04405             if (!pn2)
04406                 return NULL;
04407             pn3 = Expr(cx, ts, tc);
04408             if (!pn3)
04409                 return NULL;
04410 
04411             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
04412             pn2->pn_pos.begin = pn->pn_pos.begin;
04413             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
04414 
04415             /* Optimize o['p'] to o.p by rewriting pn2. */
04416             if (pn3->pn_type == TOK_STRING) {
04417                 pn2->pn_type = TOK_DOT;
04418                 pn2->pn_op = JSOP_GETPROP;
04419                 pn2->pn_arity = PN_NAME;
04420                 pn2->pn_expr = pn;
04421                 pn2->pn_atom = pn3->pn_atom;
04422             } else {
04423                 pn2->pn_op = JSOP_GETELEM;
04424                 pn2->pn_left = pn;
04425                 pn2->pn_right = pn3;
04426             }
04427         } else if (allowCallSyntax && tt == TOK_LP) {
04428             pn2 = NewParseNode(cx, ts, PN_LIST, tc);
04429             if (!pn2)
04430                 return NULL;
04431 
04432             /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
04433             pn2->pn_op = JSOP_CALL;
04434             if (pn->pn_op == JSOP_NAME &&
04435                 pn->pn_atom == cx->runtime->atomState.evalAtom) {
04436                 pn2->pn_op = JSOP_EVAL;
04437                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
04438             }
04439 
04440             PN_INIT_LIST_1(pn2, pn);
04441             pn2->pn_pos.begin = pn->pn_pos.begin;
04442 
04443             if (!ArgumentList(cx, ts, tc, pn2))
04444                 return NULL;
04445             if (pn2->pn_count > ARGC_LIMIT) {
04446                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
04447                                      JSMSG_TOO_MANY_FUN_ARGS);
04448                 return NULL;
04449             }
04450             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
04451         } else {
04452             js_UngetToken(ts);
04453             return pn;
04454         }
04455 
04456         pn = pn2;
04457     }
04458     if (tt == TOK_ERROR)
04459         return NULL;
04460     return pn;
04461 }
04462 
04463 static JSParseNode *
04464 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04465 {
04466     uintN oldflags;
04467     JSParseNode *pn;
04468 
04469     /*
04470      * Always accept the 'in' operator in a parenthesized expression,
04471      * where it's unambiguous, even if we might be parsing the init of a
04472      * for statement.
04473      */
04474     oldflags = tc->flags;
04475     tc->flags &= ~TCF_IN_FOR_INIT;
04476     pn = Expr(cx, ts, tc);
04477     tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
04478     return pn;
04479 }
04480 
04481 #if JS_HAS_XML_SUPPORT
04482 
04483 static JSParseNode *
04484 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04485 {
04486     JSParseNode *pn;
04487 
04488     pn = BracketedExpr(cx, ts, tc);
04489     if (!pn)
04490         return NULL;
04491 
04492     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
04493     return pn;
04494 }
04495 
04496 /*
04497  * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
04498  *
04499  *      AttributeIdentifier:
04500  *              @ PropertySelector
04501  *              @ QualifiedIdentifier
04502  *              @ [ Expression ]
04503  *
04504  *      PropertySelector:
04505  *              Identifier
04506  *              *
04507  *
04508  *      QualifiedIdentifier:
04509  *              PropertySelector :: PropertySelector
04510  *              PropertySelector :: [ Expression ]
04511  *
04512  * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
04513  *
04514  *      AttributeIdentifier:
04515  *              @ QualifiedIdentifier
04516  *              @ [ Expression ]
04517  *
04518  *      PropertySelector:
04519  *              Identifier
04520  *              *
04521  *
04522  *      QualifiedIdentifier:
04523  *              PropertySelector :: PropertySelector
04524  *              PropertySelector :: [ Expression ]
04525  *              PropertySelector
04526  *
04527  * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
04528  * for that rule to result in a name node, but ECMA-357 extends the grammar
04529  * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
04530  *
04531  *      QualifiedIdentifier:
04532  *              PropertySelector QualifiedSuffix
04533  *
04534  *      QualifiedSuffix:
04535  *              :: PropertySelector
04536  *              :: [ Expression ]
04537  *              /nothing/
04538  *
04539  * And use this production instead of PrimaryExpression: QualifiedIdentifier:
04540  *
04541  *      PrimaryExpression:
04542  *              Identifier QualifiedSuffix
04543  *
04544  * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
04545  * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
04546  */
04547 static JSParseNode *
04548 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04549 {
04550     JSParseNode *pn;
04551 
04552     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
04553     if (!pn)
04554         return NULL;
04555     if (pn->pn_type == TOK_STAR) {
04556         pn->pn_type = TOK_ANYNAME;
04557         pn->pn_op = JSOP_ANYNAME;
04558         pn->pn_atom = cx->runtime->atomState.starAtom;
04559     } else {
04560         JS_ASSERT(pn->pn_type == TOK_NAME);
04561         pn->pn_op = JSOP_QNAMEPART;
04562         pn->pn_arity = PN_NAME;
04563         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
04564         pn->pn_expr = NULL;
04565         pn->pn_slot = -1;
04566         pn->pn_attrs = 0;
04567     }
04568     return pn;
04569 }
04570 
04571 static JSParseNode *
04572 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
04573                 JSTreeContext *tc)
04574 {
04575     JSParseNode *pn2, *pn3;
04576     JSTokenType tt;
04577 
04578     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
04579     pn2 = NewParseNode(cx, ts, PN_NAME, tc);
04580     if (!pn2)
04581         return NULL;
04582 
04583     /* Left operand of :: must be evaluated if it is an identifier. */
04584     if (pn->pn_op == JSOP_QNAMEPART)
04585         pn->pn_op = JSOP_NAME;
04586 
04587     ts->flags |= TSF_KEYWORD_IS_NAME;
04588     tt = js_GetToken(cx, ts);
04589     ts->flags &= ~TSF_KEYWORD_IS_NAME;
04590     if (tt == TOK_STAR || tt == TOK_NAME) {
04591         /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
04592         pn2->pn_op = JSOP_QNAMECONST;
04593         pn2->pn_atom = (tt == TOK_STAR)
04594                        ? cx->runtime->atomState.starAtom
04595                        : CURRENT_TOKEN(ts).t_atom;
04596         pn2->pn_expr = pn;
04597         pn2->pn_slot = -1;
04598         pn2->pn_attrs = 0;
04599         return pn2;
04600     }
04601 
04602     if (tt != TOK_LB) {
04603         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
04604                                     JSMSG_SYNTAX_ERROR);
04605         return NULL;
04606     }
04607     pn3 = EndBracketedExpr(cx, ts, tc);
04608     if (!pn3)
04609         return NULL;
04610 
04611     pn2->pn_op = JSOP_QNAME;
04612     pn2->pn_arity = PN_BINARY;
04613     pn2->pn_left = pn;
04614     pn2->pn_right = pn3;
04615     return pn2;
04616 }
04617 
04618 static JSParseNode *
04619 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04620 {
04621     JSParseNode *pn;
04622 
04623     pn = PropertySelector(cx, ts, tc);
04624     if (!pn)
04625         return NULL;
04626     if (js_MatchToken(cx, ts, TOK_DBLCOLON))
04627         pn = QualifiedSuffix(cx, ts, pn, tc);
04628     return pn;
04629 }
04630 
04631 static JSParseNode *
04632 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04633 {
04634     JSParseNode *pn, *pn2;
04635     JSTokenType tt;
04636 
04637     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
04638     pn = NewParseNode(cx, ts, PN_UNARY, tc);
04639     if (!pn)
04640         return NULL;
04641     pn->pn_op = JSOP_TOATTRNAME;
04642     ts->flags |= TSF_KEYWORD_IS_NAME;
04643     tt = js_GetToken(cx, ts);
04644     ts->flags &= ~TSF_KEYWORD_IS_NAME;
04645     if (tt == TOK_STAR || tt == TOK_NAME) {
04646         pn2 = QualifiedIdentifier(cx, ts, tc);
04647     } else if (tt == TOK_LB) {
04648         pn2 = EndBracketedExpr(cx, ts, tc);
04649     } else {
04650         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
04651                                     JSMSG_SYNTAX_ERROR);
04652         return NULL;
04653     }
04654     if (!pn2)
04655         return NULL;
04656     pn->pn_kid = pn2;
04657     return pn;
04658 }
04659 
04660 /*
04661  * Make a TOK_LC unary node whose pn_kid is an expression.
04662  */
04663 static JSParseNode *
04664 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
04665 {
04666     JSParseNode *pn, *pn2;
04667     uintN oldflags;
04668 
04669     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
04670     pn = NewParseNode(cx, ts, PN_UNARY, tc);
04671     if (!pn)
04672         return NULL;
04673 
04674     /*
04675      * Turn off XML tag mode, but don't restore it after parsing this braced
04676      * expression.  Instead, simply restore ts's old flags.  This is required
04677      * because XMLExpr is called both from within a tag, and from within text
04678      * contained in an element, but outside of any start, end, or point tag.
04679      */
04680     oldflags = ts->flags;
04681     ts->flags = oldflags & ~TSF_XMLTAGMODE;
04682     pn2 = Expr(cx, ts, tc);
04683     if (!pn2)
04684         return NULL;
04685 
04686     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
04687     ts->flags = oldflags;
04688     pn->pn_kid = pn2;
04689     pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
04690     return pn;
04691 }
04692 
04693 /*
04694  * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
04695  * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI.  When converting
04696  * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
04697  * child of a container tag.
04698  */
04699 static JSParseNode *
04700 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04701 {
04702     JSParseNode *pn;
04703     JSToken *tp;
04704 
04705     pn = NewParseNode(cx, ts, PN_NULLARY, tc);
04706     if (!pn)
04707         return NULL;
04708     tp = &CURRENT_TOKEN(ts);
04709     pn->pn_op = tp->t_op;
04710     pn->pn_atom = tp->t_atom;
04711     if (tp->type == TOK_XMLPI)
04712         pn->pn_atom2 = tp->t_atom2;
04713     return pn;
04714 }
04715 
04716 /*
04717  * Parse the productions:
04718  *
04719  *      XMLNameExpr:
04720  *              XMLName XMLNameExpr?
04721  *              { Expr } XMLNameExpr?
04722  *
04723  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
04724  * a list of names and/or expressions, a single expression, or a single name.
04725  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
04726  * will be TOK_LC.
04727  */
04728 static JSParseNode *
04729 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
04730 {
04731     JSParseNode *pn, *pn2, *list;
04732     JSTokenType tt;
04733 
04734     pn = list = NULL;
04735     do {
04736         tt = CURRENT_TOKEN(ts).type;
04737         if (tt == TOK_LC) {
04738             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
04739             if (!pn2)
04740                 return NULL;
04741         } else {
04742             JS_ASSERT(tt == TOK_XMLNAME);
04743             pn2 = XMLAtomNode(cx, ts, tc);
04744             if (!pn2)
04745                 return NULL;
04746         }
04747 
04748         if (!pn) {
04749             pn = pn2;
04750         } else {
04751             if (!list) {
04752                 list = NewParseNode(cx, ts, PN_LIST, tc);
04753                 if (!list)
04754                     return NULL;
04755                 list->pn_type = TOK_XMLNAME;
04756                 list->pn_pos.begin = pn->pn_pos.begin;
04757                 PN_INIT_LIST_1(list, pn);
04758                 list->pn_extra = PNX_CANTFOLD;
04759                 pn = list;
04760             }
04761             pn->pn_pos.end = pn2->pn_pos.end;
04762             PN_APPEND(pn, pn2);
04763         }
04764     } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
04765 
04766     js_UngetToken(ts);
04767     return pn;
04768 }
04769 
04770 /*
04771  * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
04772  * at compile time into a JSXML tree.
04773  */
04774 #define XML_FOLDABLE(pn)        ((pn)->pn_arity == PN_LIST                    \
04775                                  ? ((pn)->pn_extra & PNX_CANTFOLD) == 0       \
04776                                  : (pn)->pn_type != TOK_LC)
04777 
04778 /*
04779  * Parse the productions:
04780  *
04781  *      XMLTagContent:
04782  *              XMLNameExpr
04783  *              XMLTagContent S XMLNameExpr S? = S? XMLAttr
04784  *              XMLTagContent S XMLNameExpr S? = S? { Expr }
04785  *
04786  * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
04787  * produces a list of name and attribute values and/or braced expressions, a
04788  * single expression, or a single name.
04789  *
04790  * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
04791  * XMLTagContent: XMLNameExpr.  If pn_type is not TOK_XMLNAME but pn_arity is
04792  * PN_LIST, pn_type will be tagtype.  If PN_UNARY, pn_type will be TOK_LC and
04793  * we parsed exactly one expression.
04794  */
04795 static JSParseNode *
04796 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04797               JSTokenType tagtype, JSAtom **namep)
04798 {
04799     JSParseNode *pn, *pn2, *list;
04800     JSTokenType tt;
04801 
04802     pn = XMLNameExpr(cx, ts, tc);
04803     if (!pn)
04804         return NULL;
04805     *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
04806     list = NULL;
04807 
04808     while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
04809         tt = js_GetToken(cx, ts);
04810         if (tt != TOK_XMLNAME && tt != TOK_LC) {
04811             js_UngetToken(ts);
04812             break;
04813         }
04814 
04815         pn2 = XMLNameExpr(cx, ts, tc);
04816         if (!pn2)
04817             return NULL;
04818         if (!list) {
04819             list = NewParseNode(cx, ts, PN_LIST, tc);
04820             if (!list)
04821                 return NULL;
04822             list->pn_type = tagtype;
04823             list->pn_pos.begin = pn->pn_pos.begin;
04824             PN_INIT_LIST_1(list, pn);
04825             pn = list;
04826         }
04827         PN_APPEND(pn, pn2);
04828         if (!XML_FOLDABLE(pn2))
04829             pn->pn_extra |= PNX_CANTFOLD;
04830 
04831         js_MatchToken(cx, ts, TOK_XMLSPACE);
04832         MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
04833         js_MatchToken(cx, ts, TOK_XMLSPACE);
04834 
04835         tt = js_GetToken(cx, ts);
04836         if (tt == TOK_XMLATTR) {
04837             pn2 = XMLAtomNode(cx, ts, tc);
04838         } else if (tt == TOK_LC) {
04839             pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
04840             pn->pn_extra |= PNX_CANTFOLD;
04841         } else {
04842             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
04843                                         JSMSG_BAD_XML_ATTR_VALUE);
04844             return NULL;
04845         }
04846         if (!pn2)
04847             return NULL;
04848         pn->pn_pos.end = pn2->pn_pos.end;
04849         PN_APPEND(pn, pn2);
04850     }
04851 
04852     return pn;
04853 }
04854 
04855 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result)                                \
04856     JS_BEGIN_MACRO                                                            \
04857         if ((tt) <= TOK_EOF) {                                                \
04858             if ((tt) == TOK_EOF) {                                            \
04859                 js_ReportCompileErrorNumber(cx, ts,                           \
04860                                             JSREPORT_TS | JSREPORT_ERROR,     \
04861                                             JSMSG_END_OF_XML_SOURCE);         \
04862             }                                                                 \
04863             return result;                                                    \
04864         }                                                                     \
04865     JS_END_MACRO
04866 
04867 static JSParseNode *
04868 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04869                  JSBool allowList);
04870 
04871 /*
04872  * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
04873  * that opens the end tag for the container.
04874  */
04875 static JSBool
04876 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
04877                   JSTreeContext *tc)
04878 {
04879     JSTokenType tt;
04880     JSParseNode *pn2;
04881     JSAtom *textAtom;
04882 
04883     ts->flags &= ~TSF_XMLTAGMODE;
04884     for (;;) {
04885         ts->flags |= TSF_XMLTEXTMODE;
04886         tt = js_GetToken(cx, ts);
04887         ts->flags &= ~TSF_XMLTEXTMODE;
04888         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
04889 
04890         JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
04891         textAtom = CURRENT_TOKEN(ts).t_atom;
04892         if (textAtom) {
04893             /* Non-zero-length XML text scanned. */
04894             pn2 = XMLAtomNode(cx, ts, tc);
04895             if (!pn2)
04896                 return JS_FALSE;
04897             pn->pn_pos.end = pn2->pn_pos.end;
04898             PN_APPEND(pn, pn2);
04899         }
04900 
04901         ts->flags |= TSF_OPERAND;
04902         tt = js_GetToken(cx, ts);
04903         ts->flags &= ~TSF_OPERAND;
04904         XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
04905         if (tt == TOK_XMLETAGO)
04906             break;
04907 
04908         if (tt == TOK_LC) {
04909             pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
04910             pn->pn_extra |= PNX_CANTFOLD;
04911         } else if (tt == TOK_XMLSTAGO) {
04912             pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
04913             if (pn2) {
04914                 pn2->pn_extra &= ~PNX_XMLROOT;
04915                 pn->pn_extra |= pn2->pn_extra;
04916             }
04917         } else {
04918             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
04919                       tt == TOK_XMLPI);
04920             pn2 = XMLAtomNode(cx, ts, tc);
04921         }
04922         if (!pn2)
04923             return JS_FALSE;
04924         pn->pn_pos.end = pn2->pn_pos.end;
04925         PN_APPEND(pn, pn2);
04926     }
04927 
04928     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
04929     ts->flags |= TSF_XMLTAGMODE;
04930     return JS_TRUE;
04931 }
04932 
04933 /*
04934  * Return a PN_LIST node containing an XML or XMLList Initialiser.
04935  */
04936 static JSParseNode *
04937 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
04938                  JSBool allowList)
04939 {
04940     JSParseNode *pn, *pn2, *list;
04941     JSBool hadSpace;
04942     JSTokenType tt;
04943     JSAtom *startAtom, *endAtom;
04944 
04945     CHECK_RECURSION();
04946 
04947     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
04948     pn = NewParseNode(cx, ts, PN_LIST, tc);
04949     if (!pn)
04950         return NULL;
04951 
04952     ts->flags |= TSF_XMLTAGMODE;
04953     hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
04954     tt = js_GetToken(cx, ts);
04955     if (tt == TOK_ERROR)
04956         return NULL;
04957 
04958     if (tt == TOK_XMLNAME || tt == TOK_LC) {
04959         /*
04960          * XMLElement.  Append the tag and its contents, if any, to pn.
04961          */
04962         pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
04963         if (!pn2)
04964             return NULL;
04965         js_MatchToken(cx, ts, TOK_XMLSPACE);
04966 
04967         tt = js_GetToken(cx, ts);
04968         if (tt == TOK_XMLPTAGC) {
04969             /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
04970             if (pn2->pn_type == TOK_XMLSTAGO) {
04971                 PN_INIT_LIST(pn);
04972                 RecycleTree(pn, tc);
04973                 pn = pn2;
04974             } else {
04975                 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
04976                           pn2->pn_type == TOK_LC);
04977                 PN_INIT_LIST_1(pn, pn2);
04978                 if (!XML_FOLDABLE(pn2))
04979                     pn->pn_extra |= PNX_CANTFOLD;
04980             }
04981             pn->pn_type = TOK_XMLPTAGC;
04982             pn->pn_extra |= PNX_XMLROOT;
04983         } else {
04984             /* We had better have a tag-close (>) at this point. */
04985             if (tt != TOK_XMLTAGC) {
04986                 js_ReportCompileErrorNumber(cx, ts,
04987                                             JSREPORT_TS | JSREPORT_ERROR,
04988                                             JSMSG_BAD_XML_TAG_SYNTAX);
04989                 return NULL;
04990             }
04991             pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
04992 
04993             /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
04994             if (pn2->pn_type != TOK_XMLSTAGO) {
04995                 PN_INIT_LIST_1(pn, pn2);
04996                 if (!XML_FOLDABLE(pn2))
04997                     pn->pn_extra |= PNX_CANTFOLD;
04998                 pn2 = pn;
04999                 pn = NewParseNode(cx, ts, PN_LIST, tc);
05000                 if (!pn)
05001                     return NULL;
05002             }
05003 
05004             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
05005             pn->pn_type = TOK_XMLELEM;
05006             PN_INIT_LIST_1(pn, pn2);
05007             if (!XML_FOLDABLE(pn2))
05008                 pn->pn_extra |= PNX_CANTFOLD;
05009             pn->pn_extra |= PNX_XMLROOT;
05010 
05011             /* Get element contents and delimiting end-tag-open sequence. */
05012             if (!XMLElementContent(cx, ts, pn, tc))
05013                 return NULL;
05014 
05015             js_MatchToken(cx, ts, TOK_XMLSPACE);
05016             tt = js_GetToken(cx, ts);
05017             XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
05018             if (tt != TOK_XMLNAME && tt != TOK_LC) {
05019                 js_ReportCompileErrorNumber(cx, ts,
05020                                             JSREPORT_TS | JSREPORT_ERROR,
05021                                             JSMSG_BAD_XML_TAG_SYNTAX);
05022                 return NULL;
05023             }
05024 
05025             /* Parse end tag; check mismatch at compile-time if we can. */
05026             pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
05027             if (!pn2)
05028                 return NULL;
05029             if (pn2->pn_type == TOK_XMLETAGO) {
05030                 /* Oops, end tag has attributes! */
05031                 js_ReportCompileErrorNumber(cx, ts,
05032                                             JSREPORT_TS | JSREPORT_ERROR,
05033                                             JSMSG_BAD_XML_TAG_SYNTAX);
05034                 return NULL;
05035             }
05036             if (endAtom && startAtom && endAtom != startAtom) {
05037                 JSString *str = ATOM_TO_STRING(startAtom);
05038 
05039                 /* End vs. start tag name mismatch: point to the tag name. */
05040                 js_ReportCompileErrorNumberUC(cx, pn2,
05041                                               JSREPORT_PN | JSREPORT_ERROR,
05042                                               JSMSG_XML_TAG_NAME_MISMATCH,
05043                                               JSSTRING_CHARS(str));
05044                 return NULL;
05045             }
05046 
05047             /* Make a TOK_XMLETAGO list with pn2 as its single child. */
05048             JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
05049             list = NewParseNode(cx, ts, PN_LIST, tc);
05050             if (!list)
05051                 return NULL;
05052             list->pn_type = TOK_XMLETAGO;
05053             PN_INIT_LIST_1(list, pn2);
05054             PN_APPEND(pn, list);
05055             if (!XML_FOLDABLE(pn2)) {
05056                 list->pn_extra |= PNX_CANTFOLD;
05057                 pn->pn_extra |= PNX_CANTFOLD;
05058             }
05059 
05060             js_MatchToken(cx, ts, TOK_XMLSPACE);
05061             MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
05062         }
05063 
05064         /* Set pn_op now that pn has been updated to its final value. */
05065         pn->pn_op = JSOP_TOXML;
05066     } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
05067         /* XMLList Initialiser. */
05068         pn->pn_type = TOK_XMLLIST;
05069         pn->pn_op = JSOP_TOXMLLIST;
05070         PN_INIT_LIST(pn);
05071         pn->pn_extra |= PNX_XMLROOT;
05072         if (!XMLElementContent(cx, ts, pn, tc))
05073             return NULL;
05074 
05075         MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
05076     } else {
05077         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
05078                                     JSMSG_BAD_XML_NAME_SYNTAX);
05079         return NULL;
05080     }
05081 
05082     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
05083     ts->flags &= ~TSF_XMLTAGMODE;
05084     return pn;
05085 }
05086 
05087 static JSParseNode *
05088 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
05089                      JSBool allowList)
05090 {
05091     uint32 oldopts;
05092     JSParseNode *pn;
05093 
05094     /*
05095      * Force XML support to be enabled so that comments and CDATA literals
05096      * are recognized, instead of <! followed by -- starting an HTML comment
05097      * to end of line (used in script tags to hide content from old browsers
05098      * that don't recognize <script>).
05099      */
05100     oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
05101     pn = XMLElementOrList(cx, ts, tc, allowList);
05102     JS_SetOptions(cx, oldopts);
05103     return pn;
05104 }
05105 
05106 JS_FRIEND_API(JSParseNode *)
05107 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
05108                        JSBool allowList)
05109 {
05110     JSStackFrame *fp, frame;
05111     JSParseNode *pn;
05112     JSTreeContext tc;
05113     JSTokenType tt;
05114 
05115     /*
05116      * Push a compiler frame if we have no frames, or if the top frame is a
05117      * lightweight function activation, or if its scope chain doesn't match
05118      * the one passed to us.
05119      */
05120     fp = cx->fp;
05121     MaybeSetupFrame(cx, chain, fp, &frame);
05122     JS_KEEP_ATOMS(cx->runtime);
05123     TREE_CONTEXT_INIT(&tc);
05124 
05125     /* Set XML-only mode to turn off special treatment of {expr} in XML. */
05126     ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
05127     tt = js_GetToken(cx, ts);
05128     ts->flags &= ~TSF_OPERAND;
05129 
05130     if (tt != TOK_XMLSTAGO) {
05131         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
05132                                     JSMSG_BAD_XML_MARKUP);
05133         pn = NULL;
05134     } else {
05135         pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
05136     }
05137 
05138     ts->flags &= ~TSF_XMLONLYMODE;
05139     TREE_CONTEXT_FINISH(&tc);
05140     JS_UNKEEP_ATOMS(cx->runtime);
05141     cx->fp = fp;
05142     return pn;
05143 }
05144 
05145 #endif /* JS_HAS_XMLSUPPORT */
05146 
05147 static JSParseNode *
05148 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
05149             JSTokenType tt, JSBool afterDot)
05150 {
05151     JSParseNode *pn, *pn2, *pn3;
05152     JSOp op;
05153 
05154 #if JS_HAS_SHARP_VARS
05155     JSParseNode *defsharp;
05156     JSBool notsharp;
05157 
05158     defsharp = NULL;
05159     notsharp = JS_FALSE;
05160   again:
05161     /*
05162      * Control flows here after #n= is scanned.  If the following primary is
05163      * not valid after such a "sharp variable" definition, the tt switch case
05164      * should set notsharp.
05165      */
05166 #endif
05167 
05168     CHECK_RECURSION();
05169 
05170 #if JS_HAS_GETTER_SETTER
05171     if (tt == TOK_NAME) {
05172         tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
05173         if (tt == TOK_ERROR)
05174             return NULL;
05175     }
05176 #endif
05177 
05178     switch (tt) {
05179       case TOK_FUNCTION:
05180 #if JS_HAS_XML_SUPPORT
05181         ts->flags |= TSF_KEYWORD_IS_NAME;
05182         if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
05183             ts->flags &= ~TSF_KEYWORD_IS_NAME;
05184             pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
05185             if (!pn2)
05186                 return NULL;
05187             pn2->pn_type = TOK_FUNCTION;
05188             pn = QualifiedSuffix(cx, ts, pn2, tc);
05189             if (!pn)
05190                 return NULL;
05191             break;
05192         }
05193         ts->flags &= ~TSF_KEYWORD_IS_NAME;
05194 #endif
05195         pn = FunctionExpr(cx, ts, tc);
05196         if (!pn)
05197             return NULL;
05198         break;
05199 
05200       case TOK_LB:
05201       {
05202         JSBool matched;
05203         jsuint index;
05204 
05205         pn = NewParseNode(cx, ts, PN_LIST, tc);
05206         if (!pn)
05207             return NULL;
05208         pn->pn_type = TOK_RB;
05209 
05210 #if JS_HAS_SHARP_VARS
05211         if (defsharp) {
05212             PN_INIT_LIST_1(pn, defsharp);
05213             defsharp = NULL;
05214         } else
05215 #endif
05216             PN_INIT_LIST(pn);
05217 
05218         ts->flags |= TSF_OPERAND;
05219         matched = js_MatchToken(cx, ts, TOK_RB);
05220         ts->flags &= ~TSF_OPERAND;
05221         if (!matched) {
05222             for (index = 0; ; index++) {
05223                 if (index == ARRAY_INIT_LIMIT) {
05224                     js_ReportCompileErrorNumber(cx, ts,
05225                                                 JSREPORT_TS | JSREPORT_ERROR,
05226                                                 JSMSG_ARRAY_INIT_TOO_BIG);
05227                     return NULL;
05228                 }
05229 
05230                 ts->flags |= TSF_OPERAND;
05231                 tt = js_PeekToken(cx, ts);
05232                 ts->flags &= ~TSF_OPERAND;
05233                 if (tt == TOK_RB) {
05234                     pn->pn_extra |= PNX_ENDCOMMA;
05235                     break;
05236                 }
05237 
05238                 if (tt == TOK_COMMA) {
05239                     /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
05240                     js_MatchToken(cx, ts, TOK_COMMA);
05241                     pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
05242                 } else {
05243                     pn2 = AssignExpr(cx, ts, tc);
05244                 }
05245                 if (!pn2)
05246                     return NULL;
05247                 PN_APPEND(pn, pn2);
05248 
05249                 if (tt != TOK_COMMA) {
05250                     /* If we didn't already match TOK_COMMA in above case. */
05251                     if (!js_MatchToken(cx, ts, TOK_COMMA))
05252                         break;
05253                 }
05254             }
05255 
05256 #if JS_HAS_GENERATORS
05257             /*
05258              * At this point, (index == 0 && pn->pn_count != 0) implies one
05259              * element initialiser was parsed (possibly with a defsharp before
05260              * the left bracket).
05261              *
05262              * An array comprehension of the form:
05263              *
05264              *   [i * j for (i in o) for (j in p) if (i != j)]
05265              *
05266              * translates to roughly the following let expression:
05267              *
05268              *   let (array = new Array, i, j) {
05269              *     for (i in o) let {
05270              *       for (j in p)
05271              *         if (i != j)
05272              *           array.push(i * j)
05273              *     }
05274              *     array
05275              *   }
05276              *
05277              * where array is a nameless block-local variable.  The "roughly"
05278              * means that an implementation may optimize away the array.push.
05279              * An array comprehension opens exactly one block scope, no matter
05280              * how many for heads it contains.
05281              *
05282              * Each let () {...} or for (let ...) ... compiles to:
05283              *
05284              *   JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
05285              *
05286              * where <o> is a literal object representing the block scope,
05287              * with <n> properties, naming each var declared in the block.
05288              *
05289              * Each var declaration in a let-block binds a name in <o> at
05290              * compile time, and allocates a slot on the operand stack at
05291              * runtime via JSOP_ENTERBLOCK.  A block-local var is accessed
05292              * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
05293              * JSOP_FORLOCAL.  These ops all have an immediate operand, the
05294              * local slot's stack index from fp->spbase.
05295              *
05296              * The array comprehension iteration step, array.push(i * j) in
05297              * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
05298              * where <array> is the index of array's stack slot.
05299              */
05300             if (index == 0 &&
05301                 pn->pn_count != 0 &&
05302                 js_MatchToken(cx, ts, TOK_FOR)) {
05303                 JSParseNode **pnp, *pnexp, *pntop, *pnlet;
05304                 BindData data;
05305                 JSRuntime *rt;
05306                 JSStmtInfo stmtInfo;
05307                 JSAtom *atom;
05308 
05309                 /* Relabel pn as an array comprehension node. */
05310                 pn->pn_type = TOK_ARRAYCOMP;
05311 
05312                 /*
05313                  * Remove the comprehension expression from pn's linked list
05314                  * and save it via pnexp.  We'll re-install it underneath the
05315                  * ARRAYPUSH node after we parse the rest of the comprehension.
05316                  */
05317                 pnexp = PN_LAST(pn);
05318                 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
05319                 pn->pn_tail = (--pn->pn_count == 1)
05320                               ? &pn->pn_head->pn_next
05321                               : &pn->pn_head;
05322                 *pn->pn_tail = NULL;
05323 
05324                 /*
05325                  * Make a parse-node and literal object representing the array
05326                  * comprehension's block scope.
05327                  */
05328                 pntop = PushLexicalScope(cx, ts, tc, &stmtInfo);
05329                 if (!pntop)
05330                     return NULL;
05331                 pnp = &pntop->pn_expr;
05332 
05333                 data.pn = NULL;
05334                 data.ts = ts;
05335                 data.obj = tc->blockChain;
05336                 data.op = JSOP_NOP;
05337                 data.binder = BindLet;
05338                 data.u.let.index = 0;
05339                 data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
05340 
05341                 rt = cx->runtime;
05342                 do {
05343                     /*
05344                      * FOR node is binary, left is control and right is body.
05345                      * Use index to count each block-local let-variable on the
05346                      * left-hand side of IN.
05347                      */
05348                     pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
05349                     if (!pn2)
05350                         return NULL;
05351 
05352                     pn2->pn_op = JSOP_FORIN;
05353                     if (js_MatchToken(cx, ts, TOK_NAME)) {
05354                         if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
05355                             pn2->pn_op = JSOP_FOREACH;
05356                         else
05357                             js_UngetToken(ts);
05358                     }
05359                     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
05360 
05361                     tt = js_GetToken(cx, ts);
05362                     switch (tt) {
05363 #if JS_HAS_DESTRUCTURING
05364                       case TOK_LB:
05365                       case TOK_LC:
05366                         pnlet = DestructuringExpr(cx, &data, tc, tt);
05367                         if (!pnlet)
05368                             return NULL;
05369 
05370                         if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) {
05371                             js_ReportCompileErrorNumber(cx, ts,
05372                                                         JSREPORT_TS |
05373                                                         JSREPORT_ERROR,
05374                                                         JSMSG_BAD_FOR_LEFTSIDE);
05375                             return NULL;
05376                         }
05377 
05378                         /* Destructuring requires [key, value] enumeration. */
05379                         if (pn2->pn_op != JSOP_FOREACH)
05380                             pn2->pn_op = JSOP_FOREACHKEYVAL;
05381                         break;
05382 #endif
05383 
05384                       case TOK_NAME:
05385                         atom = CURRENT_TOKEN(ts).t_atom;
05386                         if (!data.binder(cx, &data, atom, tc))
05387                             return NULL;
05388 
05389                         /*
05390                          * Create a name node with op JSOP_NAME.  We can't set
05391                          * op to JSOP_GETLOCAL here, because we don't yet know
05392                          * the block's depth in the operand stack frame.  The
05393                          * code generator computes that, and it tries to bind
05394                          * all names to slots, so we must let it do the deed.
05395                          */
05396                         pnlet = NewParseNode(cx, ts, PN_NAME, tc);
05397                         if (!pnlet)
05398                             return NULL;
05399                         pnlet->pn_op = JSOP_NAME;
05400                         pnlet->pn_atom = atom;
05401                         pnlet->pn_expr = NULL;
05402                         pnlet->pn_slot = -1;
05403                         pnlet->pn_attrs = 0;
05404                         break;
05405 
05406                       default:
05407                         js_ReportCompileErrorNumber(cx, ts,
05408                                                     JSREPORT_TS|JSREPORT_ERROR,
05409                                                     JSMSG_NO_VARIABLE_NAME);
05410                         return NULL;
05411                     }
05412 
05413                     MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
05414                     pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet,
05415                                     Expr(cx, ts, tc), tc);
05416                     if (!pn3)
05417                         return NULL;
05418 
05419                     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
05420                     pn2->pn_left = pn3;
05421                     *pnp = pn2;
05422                     pnp = &pn2->pn_right;
05423                 } while (js_MatchToken(cx, ts, TOK_FOR));
05424 
05425                 if (js_MatchToken(cx, ts, TOK_IF)) {
05426                     pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
05427                     if (!pn2)
05428                         return NULL;
05429                     pn2->pn_kid1 = Condition(cx, ts, tc);
05430                     if (!pn2->pn_kid1)
05431                         return NULL;
05432                     pn2->pn_kid2 = NULL;
05433                     pn2->pn_kid3 = NULL;
05434                     *pnp = pn2;
05435                     pnp = &pn2->pn_kid2;
05436                 }
05437 
05438                 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
05439                 if (!pn2)
05440                     return NULL;
05441                 pn2->pn_type = TOK_ARRAYPUSH;
05442                 pn2->pn_op = JSOP_ARRAYPUSH;
05443                 pn2->pn_kid = pnexp;
05444                 *pnp = pn2;
05445                 PN_APPEND(pn, pntop);
05446 
05447                 js_PopStatement(tc);
05448             }
05449 #endif /* JS_HAS_GENERATORS */
05450 
05451             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
05452         }
05453         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
05454         return pn;
05455       }
05456 
05457 #if JS_HAS_BLOCK_SCOPE
05458       case TOK_LET:
05459         pn = LetBlock(cx, ts, tc, JS_FALSE);
05460         if (!pn)
05461             return NULL;
05462         break;
05463 #endif
05464 
05465       case TOK_LC:
05466       {
05467         JSBool afterComma;
05468 
05469         pn = NewParseNode(cx, ts, PN_LIST, tc);
05470         if (!pn)
05471             return NULL;
05472         pn->pn_type = TOK_RC;
05473 
05474 #if JS_HAS_SHARP_VARS
05475         if (defsharp) {
05476             PN_INIT_LIST_1(pn, defsharp);
05477             defsharp = NULL;
05478         } else
05479 #endif
05480             PN_INIT_LIST(pn);
05481 
05482         afterComma = JS_FALSE;
05483         for (;;) {
05484             ts->flags |= TSF_KEYWORD_IS_NAME;
05485             tt = js_GetToken(cx, ts);
05486             ts->flags &= ~TSF_KEYWORD_IS_NAME;
05487             switch (tt) {
05488               case TOK_NUMBER:
05489                 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
05490                 if (pn3)
05491                     pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
05492                 break;
05493               case TOK_NAME:
05494 #if JS_HAS_GETTER_SETTER
05495                 {
05496                     JSAtom *atom;
05497                     JSRuntime *rt;
05498 
05499                     atom = CURRENT_TOKEN(ts).t_atom;
05500                     rt = cx->runtime;
05501                     if (atom == rt->atomState.getAtom ||
05502                         atom == rt->atomState.setAtom) {
05503                         op = (atom == rt->atomState.getAtom)
05504                             ? JSOP_GETTER
05505                             : JSOP_SETTER;
05506                         if (js_MatchToken(cx, ts, TOK_NAME)) {
05507                             pn3 = NewParseNode(cx, ts, PN_NAME, tc);
05508                             if (!pn3)
05509                                 return NULL;
05510                             pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
05511                             pn3->pn_expr = NULL;
05512                             pn3->pn_slot = -1;
05513                             pn3->pn_attrs = 0;
05514 
05515                             /* We have to fake a 'function' token here. */
05516                             CURRENT_TOKEN(ts).t_op = JSOP_NOP;
05517                             CURRENT_TOKEN(ts).type = TOK_FUNCTION;
05518                             pn2 = FunctionExpr(cx, ts, tc);
05519                             pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
05520                             goto skip;
05521                         }
05522                     }
05523                     /* else fall thru ... */
05524                 }
05525 #endif
05526               case TOK_STRING:
05527                 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
05528                 if (pn3)
05529                     pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
05530                 break;
05531               case TOK_RC:
05532                 if (afterComma &&
05533                     !js_ReportCompileErrorNumber(cx, ts,
05534                                                  JSREPORT_TS |
05535                                                  JSREPORT_WARNING |
05536                                                  JSREPORT_STRICT,
05537                                                  JSMSG_TRAILING_COMMA)) {
05538                         return NULL;
05539                 }
05540                 goto end_obj_init;
05541               default:
05542                 js_ReportCompileErrorNumber(cx, ts,
05543                                             JSREPORT_TS | JSREPORT_ERROR,
05544                                             JSMSG_BAD_PROP_ID);
05545                 return NULL;
05546             }
05547 
05548             tt = js_GetToken(cx, ts);
05549 #if JS_HAS_GETTER_SETTER
05550             if (tt == TOK_NAME) {
05551                 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
05552                 if (tt == TOK_ERROR)
05553                     return NULL;
05554             }
05555 #endif
05556             if (tt != TOK_COLON) {
05557                 js_ReportCompileErrorNumber(cx, ts,
05558                                             JSREPORT_TS | JSREPORT_ERROR,
05559                                             JSMSG_COLON_AFTER_ID);
05560                 return NULL;
05561             }
05562             op = CURRENT_TOKEN(ts).t_op;
05563             pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc);
05564 #if JS_HAS_GETTER_SETTER
05565           skip:
05566 #endif
05567             if (!pn2)
05568                 return NULL;
05569             PN_APPEND(pn, pn2);
05570 
05571             tt = js_GetToken(cx, ts);
05572             if (tt == TOK_RC)
05573                 goto end_obj_init;
05574             if (tt != TOK_COMMA) {
05575                 js_ReportCompileErrorNumber(cx, ts,
05576                                             JSREPORT_TS | JSREPORT_ERROR,
05577                                             JSMSG_CURLY_AFTER_LIST);
05578                 return NULL;
05579             }
05580             afterComma = JS_TRUE;
05581         }
05582      end_obj_init:
05583         pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
05584         return pn;
05585       }
05586 
05587 #if JS_HAS_SHARP_VARS
05588       case TOK_DEFSHARP:
05589         if (defsharp)
05590             goto badsharp;
05591         defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
05592         if (!defsharp)
05593             return NULL;
05594         defsharp->pn_kid = NULL;
05595         defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
05596         ts->flags |= TSF_OPERAND;
05597         tt = js_GetToken(cx, ts);
05598         ts->flags &= ~TSF_OPERAND;
05599         goto again;
05600 
05601       case TOK_USESHARP:
05602         /* Check for forward/dangling references at runtime, to allow eval. */
05603         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
05604         if (!pn)
05605             return NULL;
05606         pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
05607         notsharp = JS_TRUE;
05608         break;
05609 #endif /* JS_HAS_SHARP_VARS */
05610 
05611       case TOK_LP:
05612         pn = NewParseNode(cx, ts, PN_UNARY, tc);
05613         if (!pn)
05614             return NULL;
05615         pn2 = BracketedExpr(cx, ts, tc);
05616         if (!pn2)
05617             return NULL;
05618 
05619         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
05620         if (pn2->pn_type == TOK_RP ||
05621             (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
05622              !afterDot)) {
05623             /*
05624              * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly
05625              * to help the decompiler look ahead from a JSOP_ENDINIT to see a
05626              * JSOP_GROUP followed by a POP or POPV.  That sequence means the
05627              * parentheses are mandatory, to disambiguate object initialisers
05628              * as expression statements from block statements.
05629              *
05630              * Also drop pn if pn2 is a member or a primary expression of any
05631              * kind.  This is required to avoid generating a JSOP_GROUP that
05632              * will null the |obj| interpreter register, causing |this| in any
05633              * call of that member expression to bind to the global object.
05634              */
05635             pn->pn_kid = NULL;
05636             RecycleTree(pn, tc);
05637             pn = pn2;
05638         } else {
05639             pn->pn_type = TOK_RP;
05640             pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
05641             pn->pn_kid = pn2;
05642         }
05643         break;
05644 
05645 #if JS_HAS_XML_SUPPORT
05646       case TOK_STAR:
05647         pn = QualifiedIdentifier(cx, ts, tc);
05648         if (!pn)
05649             return NULL;
05650         notsharp = JS_TRUE;
05651         break;
05652 
05653       case TOK_AT:
05654         pn = AttributeIdentifier(cx, ts, tc);
05655         if (!pn)
05656             return NULL;
05657         notsharp = JS_TRUE;
05658         break;
05659 
05660       case TOK_XMLSTAGO:
05661         pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
05662         if (!pn)
05663             return NULL;
05664         notsharp = JS_TRUE;     /* XXXbe could be sharp? */
05665         break;
05666 #endif /* JS_HAS_XML_SUPPORT */
05667 
05668       case TOK_STRING:
05669 #if JS_HAS_SHARP_VARS
05670         notsharp = JS_TRUE;
05671         /* FALL THROUGH */
05672 #endif
05673 
05674 #if JS_HAS_XML_SUPPORT
05675       case TOK_XMLCDATA:
05676       case TOK_XMLCOMMENT:
05677       case TOK_XMLPI:
05678 #endif
05679       case TOK_NAME:
05680       case TOK_OBJECT:
05681         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
05682         if (!pn)
05683             return NULL;
05684         pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
05685 #if JS_HAS_XML_SUPPORT
05686         if (tt == TOK_XMLPI)
05687             pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
05688         else
05689 #endif
05690             pn->pn_op = CURRENT_TOKEN(ts).t_op;
05691         if (tt == TOK_NAME) {
05692             pn->pn_arity = PN_NAME;
05693             pn->pn_expr = NULL;
05694             pn->pn_slot = -1;
05695             pn->pn_attrs = 0;
05696 
05697 #if JS_HAS_XML_SUPPORT
05698             if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
05699                 if (afterDot) {
05700                     JSString *str;
05701 
05702                     /*
05703                      * Here PrimaryExpr is called after '.' or '..' and we
05704                      * just scanned .name:: or ..name:: . This is the only
05705                      * case where a keyword after '.' or '..' is not
05706                      * treated as a property name.
05707                      */
05708                     str = ATOM_TO_STRING(pn->pn_atom);
05709                     tt = js_CheckKeyword(JSSTRING_CHARS(str),
05710                                          JSSTRING_LENGTH(str));
05711                     if (tt == TOK_FUNCTION) {
05712                         pn->pn_arity = PN_NULLARY;
05713                         pn->pn_type = TOK_FUNCTION;
05714                     } else if (tt != TOK_EOF) {
05715                         js_ReportCompileErrorNumber(
05716                             cx, ts, JSREPORT_TS | JSREPORT_ERROR,
05717                             JSMSG_KEYWORD_NOT_NS);
05718                         return NULL;
05719                     }
05720                 }
05721                 pn = QualifiedSuffix(cx, ts, pn, tc);
05722                 if (!pn)
05723                     return NULL;
05724                 break;
05725             }
05726 #endif
05727 
05728             /* Unqualified __parent__ and __proto__ uses require activations. */
05729             if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
05730                 pn->pn_atom == cx->runtime->atomState.protoAtom) {
05731                 tc->flags |= TCF_FUN_HEAVYWEIGHT;
05732             } else {
05733                 JSAtomListElement *ale;
05734                 JSStackFrame *fp;
05735                 JSBool loopy;
05736 
05737                 /* Measure optimizable global variable uses. */
05738                 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
05739                 if (ale &&
05740                     !(fp = cx->fp)->fun &&
05741                     fp->scopeChain == fp->varobj &&
05742                     js_IsGlobalReference(tc, pn->pn_atom, &loopy)) {
05743                     tc->globalUses++;
05744                     if (loopy)
05745                         tc->loopyGlobalUses++;
05746                 }
05747             }
05748         }
05749         break;
05750 
05751       case TOK_NUMBER:
05752         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
05753         if (!pn)
05754             return NULL;
05755         pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
05756 #if JS_HAS_SHARP_VARS
05757         notsharp = JS_TRUE;
05758 #endif
05759         break;
05760 
05761       case TOK_PRIMARY:
05762         pn = NewParseNode(cx, ts, PN_NULLARY, tc);
05763         if (!pn)
05764             return NULL;
05765         pn->pn_op = CURRENT_TOKEN(ts).t_op;
05766 #if JS_HAS_SHARP_VARS
05767         notsharp = JS_TRUE;
05768 #endif
05769         break;
05770 
05771 #if !JS_HAS_EXPORT_IMPORT
05772       case TOK_EXPORT:
05773       case TOK_IMPORT:
05774 #endif
05775       case TOK_ERROR:
05776         /* The scanner or one of its subroutines reported the error. */
05777         return NULL;
05778 
05779       default:
05780         js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
05781                                     JSMSG_SYNTAX_ERROR);
05782         return NULL;
05783     }
05784 
05785 #if JS_HAS_SHARP_VARS
05786     if (defsharp) {
05787         if (notsharp) {
05788   badsharp:
05789             js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
05790                                         JSMSG_BAD_SHARP_VAR_DEF);
05791             return NULL;
05792         }
05793         defsharp->pn_kid = pn;
05794         return defsharp;
05795     }
05796 #endif
05797     return pn;
05798 }
05799 
05800 /*
05801  * Fold from one constant type to another.
05802  * XXX handles only strings and numbers for now
05803  */
05804 static JSBool
05805 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
05806 {
05807     if (pn->pn_type != type) {
05808         switch (type) {
05809           case TOK_NUMBER:
05810             if (pn->pn_type == TOK_STRING) {
05811                 jsdouble d;
05812                 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
05813                     return JS_FALSE;
05814                 pn->pn_dval = d;
05815                 pn->pn_type = TOK_NUMBER;
05816                 pn->pn_op = JSOP_NUMBER;
05817             }
05818             break;
05819 
05820           case TOK_STRING:
05821             if (pn->pn_type == TOK_NUMBER) {
05822                 JSString *str = js_NumberToString(cx, pn->pn_dval);
05823                 if (!str)
05824                     return JS_FALSE;
05825                 pn->pn_atom = js_AtomizeString(cx, str, 0);
05826                 if (!pn->pn_atom)
05827                     return JS_FALSE;
05828                 pn->pn_type = TOK_STRING;
05829                 pn->pn_op = JSOP_STRING;
05830             }
05831             break;
05832 
05833           default:;
05834         }
05835     }
05836     return JS_TRUE;
05837 }
05838 
05839 /*
05840  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
05841  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
05842  * a successful call to this function.
05843  */
05844 static JSBool
05845 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
05846                   JSParseNode *pn, JSTreeContext *tc)
05847 {
05848     jsdouble d, d2;
05849     int32 i, j;
05850     uint32 u;
05851 
05852     JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
05853     d = pn1->pn_dval;
05854     d2 = pn2->pn_dval;
05855     switch (op) {
05856       case JSOP_LSH:
05857       case JSOP_RSH:
05858         if (!js_DoubleToECMAInt32(cx, d, &i))
05859             return JS_FALSE;
05860         if (!js_DoubleToECMAInt32(cx, d2, &j))
05861             return JS_FALSE;
05862         j &= 31;
05863         d = (op == JSOP_LSH) ? i << j : i >> j;
05864         break;
05865 
05866       case JSOP_URSH:
05867         if (!js_DoubleToECMAUint32(cx, d, &u))
05868             return JS_FALSE;
05869         if (!js_DoubleToECMAInt32(cx, d2, &j))
05870             return JS_FALSE;
05871         j &= 31;
05872         d = u >> j;
05873         break;
05874 
05875       case JSOP_ADD:
05876         d += d2;
05877         break;
05878 
05879       case JSOP_SUB:
05880         d -= d2;
05881         break;
05882 
05883       case JSOP_MUL:
05884         d *= d2;
05885         break;
05886 
05887       case JSOP_DIV:
05888         if (d2 == 0) {
05889 #if defined(XP_WIN)
05890             /* XXX MSVC miscompiles such that (NaN == 0) */
05891             if (JSDOUBLE_IS_NaN(d2))
05892                 d = *cx->runtime->jsNaN;
05893             else
05894 #endif
05895             if (d == 0 || JSDOUBLE_IS_NaN(d))
05896                 d = *cx->runtime->jsNaN;
05897             else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
05898                 d = *cx->runtime->jsNegativeInfinity;
05899             else
05900                 d = *cx->runtime->jsPositiveInfinity;
05901         } else {
05902             d /= d2;
05903         }
05904         break;
05905 
05906       case JSOP_MOD:
05907         if (d2 == 0) {
05908             d = *cx->runtime->jsNaN;
05909         } else {
05910 #if defined(XP_WIN)
05911           /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
05912           if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
05913 #endif
05914             d = fmod(d, d2);
05915         }
05916         break;
05917 
05918       default:;
05919     }
05920 
05921     /* Take care to allow pn1 or pn2 to alias pn. */
05922     if (pn1 != pn)
05923         RecycleTree(pn1, tc);
05924     if (pn2 != pn)
05925         RecycleTree(pn2, tc);
05926     pn->pn_type = TOK_NUMBER;
05927     pn->pn_op = JSOP_NUMBER;
05928     pn->pn_arity = PN_NULLARY;
05929     pn->pn_dval = d;
05930     return JS_TRUE;
05931 }
05932 
05933 #if JS_HAS_XML_SUPPORT
05934 
05935 static JSBool
05936 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
05937 {
05938     JSTokenType tt;
05939     JSParseNode **pnp, *pn1, *pn2;
05940     JSString *accum, *str;
05941     uint32 i, j;
05942 
05943     JS_ASSERT(pn->pn_arity == PN_LIST);
05944     tt = pn->pn_type;
05945     pnp = &pn->pn_head;
05946     pn1 = *pnp;
05947     accum = NULL;
05948     if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
05949         if (tt == TOK_XMLETAGO)
05950             accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
05951         else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
05952             accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
05953     }
05954 
05955     for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
05956         /* The parser already rejected end-tags with attributes. */
05957         JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
05958         switch (pn2->pn_type) {
05959           case TOK_XMLATTR:
05960             if (!accum)
05961                 goto cantfold;
05962             /* FALL THROUGH */
05963           case TOK_XMLNAME:
05964           case TOK_XMLSPACE:
05965           case TOK_XMLTEXT:
05966           case TOK_STRING:
05967             if (pn2->pn_arity == PN_LIST)
05968                 goto cantfold;
05969             str = ATOM_TO_STRING(pn2->pn_atom);
05970             break;
05971 
05972           case TOK_XMLCDATA:
05973             str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
05974             if (!str)
05975                 return JS_FALSE;
05976             break;
05977 
05978           case TOK_XMLCOMMENT:
05979             str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
05980             if (!str)
05981                 return JS_FALSE;
05982             break;
05983 
05984           case TOK_XMLPI:
05985             str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
05986                                          ATOM_TO_STRING(pn2->pn_atom2));
05987             if (!str)
05988                 return JS_FALSE;
05989             break;
05990 
05991           cantfold:
05992           default:
05993             JS_ASSERT(*pnp == pn1);
05994             if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
05995                 (i & 1) ^ (j & 1)) {
05996 #ifdef DEBUG_brendanXXX
05997                 printf("1: %d, %d => %s\n",
05998                        i, j, accum ? JS_GetStringBytes(accum) : "NULL");
05999 #endif
06000             } else if (accum && pn1 != pn2) {
06001                 while (pn1->pn_next != pn2) {
06002                     pn1 = RecycleTree(pn1, tc);
06003                     --pn->pn_count;
06004                 }
06005                 pn1->pn_type = TOK_XMLTEXT;
06006                 pn1->pn_op = JSOP_STRING;
06007                 pn1->pn_arity = PN_NULLARY;
06008                 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
06009                 if (!pn1->pn_atom)
06010                     return JS_FALSE;
06011                 JS_ASSERT(pnp != &pn1->pn_next);
06012                 *pnp = pn1;
06013             }
06014             pnp = &pn2->pn_next;
06015             pn1 = *pnp;
06016             accum = NULL;
06017             continue;
06018         }
06019 
06020         if (accum) {
06021             str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
06022                   ? js_AddAttributePart(cx, i & 1, accum, str)
06023                   : js_ConcatStrings(cx, accum, str);
06024             if (!str)
06025                 return JS_FALSE;
06026 #ifdef DEBUG_brendanXXX
06027             printf("2: %d, %d => %s (%u)\n",
06028                    i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str));
06029 #endif
06030             ++j;
06031         }
06032         accum = str;
06033     }
06034 
06035     if (accum) {
06036         str = NULL;
06037         if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
06038             if (tt == TOK_XMLPTAGC)
06039                 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
06040             else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
06041                 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
06042         }
06043         if (str) {
06044             accum = js_ConcatStrings(cx, accum, str);
06045             if (!accum)
06046                 return JS_FALSE;
06047         }
06048 
06049         JS_ASSERT(*pnp == pn1);
06050         while (pn1->pn_next) {
06051             pn1 = RecycleTree(pn1, tc);
06052             --pn->pn_count;
06053         }
06054         pn1->pn_type = TOK_XMLTEXT;
06055         pn1->pn_op = JSOP_STRING;
06056         pn1->pn_arity = PN_NULLARY;
06057         pn1->pn_atom = js_AtomizeString(cx, accum, 0);
06058         if (!pn1->pn_atom)
06059             return JS_FALSE;
06060         JS_ASSERT(pnp != &pn1->pn_next);
06061         *pnp = pn1;
06062     }
06063 
06064     if (pn1 && pn->pn_count == 1) {
06065         /*
06066          * Only one node under pn, and it has been folded: move pn1 onto pn
06067          * unless pn is an XML root (in which case we need it to tell the code
06068          * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op).  If pn is an
06069          * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
06070          * extra "<" and "/>" bracketing at runtime.
06071          */
06072         if (!(pn->pn_extra & PNX_XMLROOT)) {
06073             PN_MOVE_NODE(pn, pn1);
06074         } else if (tt == TOK_XMLPTAGC) {
06075             pn->pn_type = TOK_XMLELEM;
06076             pn->pn_op = JSOP_TOXML;
06077         }
06078     }
06079     return JS_TRUE;
06080 }
06081 
06082 #endif /* JS_HAS_XML_SUPPORT */
06083 
06084 static JSBool
06085 StartsWith(JSParseNode *pn, JSTokenType tt)
06086 {
06087 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
06088 
06089 recur:
06090     if (pn->pn_type == tt)
06091         return JS_TRUE;
06092     switch (pn->pn_arity) {
06093       case PN_FUNC:
06094         return  tt == TOK_FUNCTION;
06095       case PN_LIST:
06096         if (pn->pn_head)
06097             TAIL_RECURSE(pn->pn_head);
06098         break;
06099       case PN_TERNARY:
06100         if (pn->pn_kid1)
06101             TAIL_RECURSE(pn->pn_kid1);
06102         break;
06103       case PN_BINARY:
06104         if (pn->pn_left)
06105             TAIL_RECURSE(pn->pn_left);
06106         break;
06107       case PN_UNARY:
06108         /* A parenthesized expression starts with a left parenthesis. */
06109         if (pn->pn_type == TOK_RP)
06110             return tt == TOK_LP;
06111         if (pn->pn_kid)
06112             TAIL_RECURSE(pn->pn_kid);
06113         break;
06114       case PN_NAME:
06115         if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
06116             TAIL_RECURSE(pn->pn_expr);
06117         /* FALL THROUGH */
06118     }
06119     return JS_FALSE;
06120 #undef TAIL_RECURSE
06121 }
06122 
06123 JSBool
06124 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
06125 {
06126     JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
06127     int stackDummy;
06128 
06129     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
06130         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
06131         return JS_FALSE;
06132     }
06133 
06134     switch (pn->pn_arity) {
06135       case PN_FUNC:
06136       {
06137         uint16 oldflags = tc->flags;
06138 
06139         tc->flags = (uint16) pn->pn_flags;
06140         if (!js_FoldConstants(cx, pn->pn_body, tc))
06141             return JS_FALSE;
06142         tc->flags = oldflags;
06143         break;
06144       }
06145 
06146       case PN_LIST:
06147 #if 0 /* JS_HAS_XML_SUPPORT */
06148         switch (pn->pn_type) {
06149           case TOK_XMLELEM:
06150           case TOK_XMLLIST:
06151           case TOK_XMLPTAGC:
06152             /*
06153              * Try to fold this XML parse tree once, from the top down, into
06154              * a JSXML tree with just one object wrapping the tree root.
06155              *
06156              * Certain subtrees could be folded similarly, but we'd have to
06157              * ensure that none used namespace prefixes declared elsewhere in
06158              * its super-tree, and we would have to convert each XML object
06159              * created at runtime for such sub-trees back into a string, and
06160              * concatenate and re-parse anyway.
06161              */
06162             if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT &&
06163                 !(tc->flags & TCF_HAS_DEFXMLNS)) {
06164                 JSObject *obj;
06165                 JSAtom *atom;
06166 
06167                 obj = js_ParseNodeToXMLObject(cx, pn);
06168                 if (!obj)
06169                     return JS_FALSE;
06170                 atom = js_AtomizeObject(cx, obj, 0);
06171                 if (!atom)
06172                     return JS_FALSE;
06173                 pn->pn_op = JSOP_XMLOBJECT;
06174                 pn->pn_arity = PN_NULLARY;
06175                 pn->pn_atom = atom;
06176                 return JS_TRUE;
06177             }
06178 
06179             /*
06180              * Can't fold from parse node to XML tree -- try folding strings
06181              * as much as possible, and folding XML sub-trees bottom up to
06182              * minimize string concatenation and ToXML/ToXMLList operations
06183              * at runtime.
06184              */
06185             break;
06186 
06187           default:;
06188         }
06189 #endif
06190 
06191         /* Save the list head in pn1 for later use. */
06192         for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
06193             if (!js_FoldConstants(cx, pn2, tc))
06194                 return JS_FALSE;
06195         }
06196         break;
06197 
06198       case PN_TERNARY:
06199         /* Any kid may be null (e.g. for (;;)). */
06200         pn1 = pn->pn_kid1;
06201         pn2 = pn->pn_kid2;
06202         pn3 = pn->pn_kid3;
06203         if (pn1 && !js_FoldConstants(cx, pn1, tc))
06204             return JS_FALSE;
06205         if (pn2 && !js_FoldConstants(cx, pn2, tc))
06206             return JS_FALSE;
06207         if (pn3 && !js_FoldConstants(cx, pn3, tc))
06208             return JS_FALSE;
06209         break;
06210 
06211       case PN_BINARY:
06212         /* First kid may be null (for default case in switch). */
06213         pn1 = pn->pn_left;
06214         pn2 = pn->pn_right;
06215         if (pn1 && !js_FoldConstants(cx, pn1, tc))
06216             return JS_FALSE;
06217         if (!js_FoldConstants(cx, pn2, tc))
06218             return JS_FALSE;
06219         break;
06220 
06221       case PN_UNARY:
06222         /* Our kid may be null (e.g. return; vs. return e;). */
06223         pn1 = pn->pn_kid;
06224         if (pn1 && !js_FoldConstants(cx, pn1, tc))
06225             return JS_FALSE;
06226         break;
06227 
06228       case PN_NAME:
06229         /*
06230          * Skip pn1 down along a chain of dotted member expressions to avoid
06231          * excessive recursion.  Our only goal here is to fold constants (if
06232          * any) in the primary expression operand to the left of the first
06233          * dot in the chain.
06234          */
06235         pn1 = pn->pn_expr;
06236         while (pn1 && pn1->pn_arity == PN_NAME)
06237             pn1 = pn1->pn_expr;
06238         if (pn1 && !js_FoldConstants(cx, pn1, tc))
06239             return JS_FALSE;
06240         break;
06241 
06242       case PN_NULLARY:
06243         break;
06244     }
06245 
06246     switch (pn->pn_type) {
06247       case TOK_IF:
06248         if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
06249             break;
06250         /* FALL THROUGH */
06251 
06252       case TOK_HOOK:
06253         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
06254         while (pn1->pn_type == TOK_RP)
06255             pn1 = pn1->pn_kid;
06256         switch (pn1->pn_type) {
06257           case TOK_NUMBER:
06258             if (pn1->pn_dval == 0)
06259                 pn2 = pn3;
06260             break;
06261           case TOK_STRING:
06262             if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
06263                 pn2 = pn3;
06264             break;
06265           case TOK_PRIMARY:
06266             if (pn1->pn_op == JSOP_TRUE)
06267                 break;
06268             if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
06269                 pn2 = pn3;
06270                 break;
06271             }
06272             /* FALL THROUGH */
06273           default:
06274             /* Early return to dodge common code that copies pn2 to pn. */
06275             return JS_TRUE;
06276         }
06277 
06278         if (pn2) {
06279             /*
06280              * pn2 is the then- or else-statement subtree to compile.  Take
06281              * care not to expose an object initialiser, which would be parsed
06282              * as a block, to the Statement parser via eval(uneval(e)) where e
06283              * is '1 ? {p:2, q:3}[i] : r;' or the like.
06284              */
06285             if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) {
06286                 pn->pn_type = TOK_RP;
06287                 pn->pn_arity = PN_UNARY;
06288                 pn->pn_kid = pn2;
06289             } else {
06290                 PN_MOVE_NODE(pn, pn2);
06291             }
06292         }
06293         if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
06294             /*
06295              * False condition and no else, or an empty then-statement was
06296              * moved up over pn.  Either way, make pn an empty block (not an
06297              * empty statement, which does not decompile, even when labeled).
06298              * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
06299              * or an empty statement for a child.
06300              */
06301             pn->pn_type = TOK_LC;
06302             pn->pn_arity = PN_LIST;
06303             PN_INIT_LIST(pn);
06304         }
06305         RecycleTree(pn2, tc);
06306         if (pn3 && pn3 != pn2)
06307             RecycleTree(pn3, tc);
06308         break;
06309 
06310       case TOK_ASSIGN:
06311         /*
06312          * Compound operators such as *= should be subject to folding, in case
06313          * the left-hand side is constant, and so that the decompiler produces
06314          * the same string that you get from decompiling a script or function
06315          * compiled from that same string.  As with +, += is special.
06316          */
06317         if (pn->pn_op == JSOP_NOP)
06318             break;
06319         if (pn->pn_op != JSOP_ADD)
06320             goto do_binary_op;
06321         /* FALL THROUGH */
06322 
06323       case TOK_PLUS:
06324         if (pn->pn_arity == PN_LIST) {
06325             size_t length, length2;
06326             jschar *chars;
06327             JSString *str, *str2;
06328 
06329             /*
06330              * Any string literal term with all others number or string means
06331              * this is a concatenation.  If any term is not a string or number
06332              * literal, we can't fold.
06333              */
06334             JS_ASSERT(pn->pn_count > 2);
06335             if (pn->pn_extra & PNX_CANTFOLD)
06336                 return JS_TRUE;
06337             if (pn->pn_extra != PNX_STRCAT)
06338                 goto do_binary_op;
06339 
06340             /* Ok, we're concatenating: convert non-string constant operands. */
06341             length = 0;
06342             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
06343                 if (!FoldType(cx, pn2, TOK_STRING))
06344                     return JS_FALSE;
06345                 /* XXX fold only if all operands convert to string */
06346                 if (pn2->pn_type != TOK_STRING)
06347                     return JS_TRUE;
06348                 length += ATOM_TO_STRING(pn2->pn_atom)->length;
06349             }
06350 
06351             /* Allocate a new buffer and string descriptor for the result. */
06352             chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
06353             if (!chars)
06354                 return JS_FALSE;
06355             str = js_NewString(cx, chars, length, 0);
06356             if (!str) {
06357                 JS_free(cx, chars);
06358                 return JS_FALSE;
06359             }
06360 
06361             /* Fill the buffer, advancing chars and recycling kids as we go. */
06362             for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
06363                 str2 = ATOM_TO_STRING(pn2->pn_atom);
06364                 length2 = str2->length;
06365                 js_strncpy(chars, str2->chars, length2);
06366                 chars += length2;
06367             }
06368             *chars = 0;
06369 
06370             /* Atomize the result string and mutate pn to refer to it. */
06371             pn->pn_atom = js_AtomizeString(cx, str, 0);
06372             if (!pn->pn_atom)
06373                 return JS_FALSE;
06374             pn->pn_type = TOK_STRING;
06375             pn->pn_op = JSOP_STRING;
06376             pn->pn_arity = PN_NULLARY;
06377             break;
06378         }
06379 
06380         /* Handle a binary string concatenation. */
06381         JS_ASSERT(pn->pn_arity == PN_BINARY);
06382         if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
06383             JSString *left, *right, *str;
06384 
06385             if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
06386                           TOK_STRING)) {
06387                 return JS_FALSE;
06388             }
06389             if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
06390                 return JS_TRUE;
06391             left = ATOM_TO_STRING(pn1->pn_atom);
06392             right = ATOM_TO_STRING(pn2->pn_atom);
06393             str = js_ConcatStrings(cx, left, right);
06394             if (!str)
06395                 return JS_FALSE;
06396             pn->pn_atom = js_AtomizeString(cx, str, 0);
06397             if (!pn->pn_atom)
06398                 return JS_FALSE;
06399             pn->pn_type = TOK_STRING;
06400             pn->pn_op = JSOP_STRING;
06401             pn->pn_arity = PN_NULLARY;
06402             RecycleTree(pn1, tc);
06403             RecycleTree(pn2, tc);
06404             break;
06405         }
06406 
06407         /* Can't concatenate string literals, let's try numbers. */
06408         goto do_binary_op;
06409 
06410       case TOK_STAR:
06411         /* The * in 'import *;' parses as a nullary star node. */
06412         if (pn->pn_arity == PN_NULLARY)
06413             break;
06414         /* FALL THROUGH */
06415 
06416       case TOK_SHOP:
06417       case TOK_MINUS:
06418       case TOK_DIVOP:
06419       do_binary_op:
06420         if (pn->pn_arity == PN_LIST) {
06421             JS_ASSERT(pn->pn_count > 2);
06422             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
06423                 if (!FoldType(cx, pn2, TOK_NUMBER))
06424                     return JS_FALSE;
06425             }
06426             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
06427                 /* XXX fold only if all operands convert to number */
06428                 if (pn2->pn_type != TOK_NUMBER)
06429                     break;
06430             }
06431             if (!pn2) {
06432                 JSOp op = pn->pn_op;
06433 
06434                 pn2 = pn1->pn_next;
06435                 pn3 = pn2->pn_next;
06436                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
06437                     return JS_FALSE;
06438                 while ((pn2 = pn3) != NULL) {
06439                     pn3 = pn2->pn_next;
06440                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
06441                         return JS_FALSE;
06442                 }
06443             }
06444         } else {
06445             JS_ASSERT(pn->pn_arity == PN_BINARY);
06446             if (!FoldType(cx, pn1, TOK_NUMBER) ||
06447                 !FoldType(cx, pn2, TOK_NUMBER)) {
06448                 return JS_FALSE;
06449             }
06450             if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
06451                 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
06452                     return JS_FALSE;
06453             }
06454         }
06455         break;
06456 
06457       case TOK_UNARYOP:
06458         while (pn1->pn_type == TOK_RP)
06459             pn1 = pn1->pn_kid;
06460         if (pn1->pn_type == TOK_NUMBER) {
06461             jsdouble d;
06462             int32 i;
06463 
06464             /* Operate on one numeric constant. */
06465             d = pn1->pn_dval;
06466             switch (pn->pn_op) {
06467               case JSOP_BITNOT:
06468                 if (!js_DoubleToECMAInt32(cx, d, &i))
06469                     return JS_FALSE;
06470                 d = ~i;
06471                 break;
06472 
06473               case JSOP_NEG:
06474 #ifdef HPUX
06475                 /*
06476                  * Negation of a zero doesn't produce a negative
06477                  * zero on HPUX. Perform the operation by bit
06478                  * twiddling.
06479                  */
06480                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
06481 #else
06482                 d = -d;
06483 #endif
06484                 break;
06485 
06486               case JSOP_POS:
06487                 break;
06488 
06489               case JSOP_NOT:
06490                 pn->pn_type = TOK_PRIMARY;
06491                 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
06492                 pn->pn_arity = PN_NULLARY;
06493                 /* FALL THROUGH */
06494 
06495               default:
06496                 /* Return early to dodge the common TOK_NUMBER code. */
06497                 return JS_TRUE;
06498             }
06499             pn->pn_type = TOK_NUMBER;
06500             pn->pn_op = JSOP_NUMBER;
06501             pn->pn_arity = PN_NULLARY;
06502             pn->pn_dval = d;
06503             RecycleTree(pn1, tc);
06504         }
06505         break;
06506 
06507 #if JS_HAS_XML_SUPPORT
06508       case TOK_XMLELEM:
06509       case TOK_XMLLIST:
06510       case TOK_XMLPTAGC:
06511       case TOK_XMLSTAGO:
06512       case TOK_XMLETAGO:
06513       case TOK_XMLNAME:
06514         if (pn->pn_arity == PN_LIST) {
06515             JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
06516             if (!FoldXMLConstants(cx, pn, tc))
06517                 return JS_FALSE;
06518         }
06519         break;
06520 
06521       case TOK_AT:
06522         if (pn1->pn_type == TOK_XMLNAME) {
06523             jsval v;
06524             JSAtom *atom;
06525 
06526             v = ATOM_KEY(pn1->pn_atom);
06527             if (!js_ToAttributeName(cx, &v))
06528                 return JS_FALSE;
06529             JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
06530             atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
06531             if (!atom)
06532                 return JS_FALSE;
06533 
06534             pn->pn_type = TOK_XMLNAME;
06535             pn->pn_op = JSOP_OBJECT;
06536             pn->pn_arity = PN_NULLARY;
06537             pn->pn_atom = atom;
06538             RecycleTree(pn1, tc);
06539         }
06540         break;
06541 #endif /* JS_HAS_XML_SUPPORT */
06542 
06543       default:;
06544     }
06545 
06546     return JS_TRUE;
06547 }