/[jscoverage]/trunk/js/jsparse.cpp
ViewVC logotype

Annotation of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (13 years, 8 months ago) by siliconforks
File size: 212795 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 siliconforks 332 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=8 sw=4 et tw=99:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
20     * The Initial Developer of the Original Code is
21     * Netscape Communications Corporation.
22     * Portions created by the Initial Developer are Copyright (C) 1998
23     * the Initial Developer. All Rights Reserved.
24     *
25     * Contributor(s):
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JS parser.
43     *
44     * This is a recursive-descent parser for the JavaScript language specified by
45     * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46     * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47     * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48     * After tree construction, it rewrites trees to fold constants and evaluate
49     * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
50     * generate bytecode.
51     *
52     * This parser attempts no error recovery.
53     */
54     #include "jsstddef.h"
55     #include <stdlib.h>
56     #include <string.h>
57     #include <math.h>
58     #include "jstypes.h"
59     #include "jsarena.h" /* Added by JSIFY */
60     #include "jsutil.h" /* Added by JSIFY */
61     #include "jsapi.h"
62     #include "jsarray.h"
63     #include "jsatom.h"
64     #include "jscntxt.h"
65     #include "jsversion.h"
66     #include "jsemit.h"
67     #include "jsfun.h"
68     #include "jsinterp.h"
69     #include "jsiter.h"
70     #include "jslock.h"
71     #include "jsnum.h"
72     #include "jsobj.h"
73     #include "jsopcode.h"
74     #include "jsparse.h"
75     #include "jsscan.h"
76     #include "jsscope.h"
77     #include "jsscript.h"
78     #include "jsstr.h"
79     #include "jsstaticcheck.h"
80    
81     #if JS_HAS_XML_SUPPORT
82     #include "jsxml.h"
83     #endif
84    
85     #if JS_HAS_DESTRUCTURING
86     #include "jsdhash.h"
87     #endif
88    
89     /*
90     * Asserts to verify assumptions behind pn_ macros.
91     */
92     JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.atom) ==
93     offsetof(JSParseNode, pn_u.apair.atom));
94     JS_STATIC_ASSERT(offsetof(JSParseNode, pn_u.name.slot) ==
95     offsetof(JSParseNode, pn_u.lexical.slot));
96    
97     /*
98     * JS parsers, from lowest to highest precedence.
99     *
100     * Each parser takes a context, a token stream, and a tree context struct.
101     * Each returns a parse node tree or null on error.
102     */
103    
104     typedef JSParseNode *
105     JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
106    
107     typedef JSParseNode *
108     JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
109     JSBool allowCallSyntax);
110    
111     typedef JSParseNode *
112     JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
113     JSTokenType tt, JSBool afterDot);
114    
115     typedef JSParseNode *
116     JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
117     JSParseNode *pn1, JSBool *genexp);
118    
119     static JSParser FunctionStmt;
120     static JSParser FunctionExpr;
121     static JSParser Statements;
122     static JSParser Statement;
123     static JSParser Variables;
124     static JSParser Expr;
125     static JSParser AssignExpr;
126     static JSParser CondExpr;
127     static JSParser OrExpr;
128     static JSParser AndExpr;
129     static JSParser BitOrExpr;
130     static JSParser BitXorExpr;
131     static JSParser BitAndExpr;
132     static JSParser EqExpr;
133     static JSParser RelExpr;
134     static JSParser ShiftExpr;
135     static JSParser AddExpr;
136     static JSParser MulExpr;
137     static JSParser UnaryExpr;
138     static JSMemberParser MemberExpr;
139     static JSPrimaryParser PrimaryExpr;
140     static JSParenParser ParenExpr;
141    
142     /*
143     * Insist that the next token be of type tt, or report errno and return null.
144     * NB: this macro uses cx and ts from its lexical environment.
145     */
146     #define MUST_MATCH_TOKEN(tt, errno) \
147     JS_BEGIN_MACRO \
148     if (js_GetToken(cx, ts) != tt) { \
149     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
150     return NULL; \
151     } \
152     JS_END_MACRO
153    
154     #ifdef METER_PARSENODES
155     static uint32 parsenodes = 0;
156     static uint32 maxparsenodes = 0;
157     static uint32 recyclednodes = 0;
158     #endif
159    
160     JSBool
161     js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
162     JSStackFrame *callerFrame,
163     const jschar *base, size_t length,
164     FILE *fp, const char *filename, uintN lineno)
165     {
166     JS_ASSERT_IF(callerFrame, callerFrame->script);
167    
168     pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
169     if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {
170     JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
171     return JS_FALSE;
172     }
173     if (principals)
174     JSPRINCIPALS_HOLD(cx, principals);
175     pc->principals = principals;
176     pc->callerFrame = callerFrame;
177     pc->nodeList = NULL;
178     pc->traceListHead = NULL;
179    
180     /* Root atoms and objects allocated for the parsed tree. */
181     JS_KEEP_ATOMS(cx->runtime);
182     JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);
183     return JS_TRUE;
184     }
185    
186     void
187     js_FinishParseContext(JSContext *cx, JSParseContext *pc)
188     {
189     if (pc->principals)
190     JSPRINCIPALS_DROP(cx, pc->principals);
191     JS_ASSERT(pc->tempRoot.u.parseContext == pc);
192     JS_POP_TEMP_ROOT(cx, &pc->tempRoot);
193     JS_UNKEEP_ATOMS(cx->runtime);
194     js_CloseTokenStream(cx, TS(pc));
195     JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
196     }
197    
198     void
199     js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
200     JSPrincipals *principals)
201     {
202     JS_ASSERT(!pc->principals);
203     if (principals)
204     JSPRINCIPALS_HOLD(cx, principals);
205     pc->principals = principals;
206     }
207    
208     JSParsedObjectBox *
209     js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj)
210     {
211     JSParsedObjectBox *pob;
212    
213     /*
214     * We use JSContext.tempPool to allocate parsed objects and place them on
215     * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
216     * containing the entries must be alive until we are done with scanning,
217     * parsing and code generation for the whole script or top-level function.
218     */
219     JS_ASSERT(obj);
220     JS_ARENA_ALLOCATE_TYPE(pob, JSParsedObjectBox, &cx->tempPool);
221     if (!pob) {
222     js_ReportOutOfScriptQuota(cx);
223     return NULL;
224     }
225     pob->traceLink = pc->traceListHead;
226     pob->emitLink = NULL;
227     pob->object = obj;
228     pc->traceListHead = pob;
229     return pob;
230     }
231    
232    
233     void
234     js_TraceParseContext(JSTracer *trc, JSParseContext *pc)
235     {
236     JSParsedObjectBox *pob;
237    
238     JS_ASSERT(pc->tempRoot.u.parseContext == pc);
239     pob = pc->traceListHead;
240     while (pob) {
241     JS_CALL_OBJECT_TRACER(trc, pob->object, "parser.object");
242     pob = pob->traceLink;
243     }
244     }
245    
246     static JSParseNode *
247     RecycleTree(JSParseNode *pn, JSTreeContext *tc)
248     {
249     JSParseNode *next;
250    
251     if (!pn)
252     return NULL;
253    
254     /* Catch back-to-back dup recycles. */
255     JS_ASSERT(pn != tc->parseContext->nodeList);
256     next = pn->pn_next;
257     pn->pn_next = tc->parseContext->nodeList;
258     tc->parseContext->nodeList = pn;
259     #ifdef METER_PARSENODES
260     recyclednodes++;
261     #endif
262     return next;
263     }
264    
265     static JSParseNode *
266     NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
267     {
268     JSParseNode *pn;
269    
270     pn = tc->parseContext->nodeList;
271     if (!pn) {
272     JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
273     if (!pn)
274     js_ReportOutOfScriptQuota(cx);
275     } else {
276     tc->parseContext->nodeList = pn->pn_next;
277    
278     /* Recycle immediate descendents only, to save work and working set. */
279     switch (pn->pn_arity) {
280     case PN_FUNC:
281     RecycleTree(pn->pn_body, tc);
282     break;
283     case PN_LIST:
284     if (pn->pn_head) {
285     /* XXX check for dup recycles in the list */
286     *pn->pn_tail = tc->parseContext->nodeList;
287     tc->parseContext->nodeList = pn->pn_head;
288     #ifdef METER_PARSENODES
289     recyclednodes += pn->pn_count;
290     #endif
291     }
292     break;
293     case PN_TERNARY:
294     RecycleTree(pn->pn_kid1, tc);
295     RecycleTree(pn->pn_kid2, tc);
296     RecycleTree(pn->pn_kid3, tc);
297     break;
298     case PN_BINARY:
299     if (pn->pn_left != pn->pn_right)
300     RecycleTree(pn->pn_left, tc);
301     RecycleTree(pn->pn_right, tc);
302     break;
303     case PN_UNARY:
304     RecycleTree(pn->pn_kid, tc);
305     break;
306     case PN_NAME:
307     RecycleTree(pn->pn_expr, tc);
308     break;
309     case PN_NULLARY:
310     break;
311     }
312     }
313     if (pn) {
314     #ifdef METER_PARSENODES
315     parsenodes++;
316     if (parsenodes - recyclednodes > maxparsenodes)
317     maxparsenodes = parsenodes - recyclednodes;
318     #endif
319     memset(&pn->pn_u, 0, sizeof pn->pn_u);
320     pn->pn_next = NULL;
321     }
322     return pn;
323     }
324    
325     /*
326     * Allocate a JSParseNode from cx's temporary arena.
327     */
328     static JSParseNode *
329     NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
330     JSTreeContext *tc)
331     {
332     JSParseNode *pn;
333     JSToken *tp;
334    
335     pn = NewOrRecycledNode(cx, tc);
336     if (!pn)
337     return NULL;
338     tp = &CURRENT_TOKEN(ts);
339     pn->pn_type = tp->type;
340     pn->pn_pos = tp->pos;
341     pn->pn_op = JSOP_NOP;
342     pn->pn_arity = arity;
343     return pn;
344     }
345    
346     static JSParseNode *
347     NewBinary(JSContext *cx, JSTokenType tt,
348     JSOp op, JSParseNode *left, JSParseNode *right,
349     JSTreeContext *tc)
350     {
351     JSParseNode *pn, *pn1, *pn2;
352    
353     if (!left || !right)
354     return NULL;
355    
356     /*
357     * Flatten a left-associative (left-heavy) tree of a given operator into
358     * a list, to reduce js_FoldConstants and js_EmitTree recursion.
359     */
360     if (left->pn_type == tt &&
361     left->pn_op == op &&
362     (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
363     if (left->pn_arity != PN_LIST) {
364     pn1 = left->pn_left, pn2 = left->pn_right;
365     left->pn_arity = PN_LIST;
366     PN_INIT_LIST_1(left, pn1);
367     PN_APPEND(left, pn2);
368     if (tt == TOK_PLUS) {
369     if (pn1->pn_type == TOK_STRING)
370     left->pn_extra |= PNX_STRCAT;
371     else if (pn1->pn_type != TOK_NUMBER)
372     left->pn_extra |= PNX_CANTFOLD;
373     if (pn2->pn_type == TOK_STRING)
374     left->pn_extra |= PNX_STRCAT;
375     else if (pn2->pn_type != TOK_NUMBER)
376     left->pn_extra |= PNX_CANTFOLD;
377     }
378     }
379     PN_APPEND(left, right);
380     left->pn_pos.end = right->pn_pos.end;
381     if (tt == TOK_PLUS) {
382     if (right->pn_type == TOK_STRING)
383     left->pn_extra |= PNX_STRCAT;
384     else if (right->pn_type != TOK_NUMBER)
385     left->pn_extra |= PNX_CANTFOLD;
386     }
387     return left;
388     }
389    
390     /*
391     * Fold constant addition immediately, to conserve node space and, what's
392     * more, so js_FoldConstants never sees mixed addition and concatenation
393     * operations with more than one leading non-string operand in a PN_LIST
394     * generated for expressions such as 1 + 2 + "pt" (which should evaluate
395     * to "3pt", not "12pt").
396     */
397     if (tt == TOK_PLUS &&
398     left->pn_type == TOK_NUMBER &&
399     right->pn_type == TOK_NUMBER) {
400     left->pn_dval += right->pn_dval;
401     left->pn_pos.end = right->pn_pos.end;
402     RecycleTree(right, tc);
403     return left;
404     }
405    
406     pn = NewOrRecycledNode(cx, tc);
407     if (!pn)
408     return NULL;
409     pn->pn_type = tt;
410     pn->pn_pos.begin = left->pn_pos.begin;
411     pn->pn_pos.end = right->pn_pos.end;
412     pn->pn_op = op;
413     pn->pn_arity = PN_BINARY;
414     pn->pn_left = left;
415     pn->pn_right = right;
416     return pn;
417     }
418    
419     #if JS_HAS_GETTER_SETTER
420     static JSTokenType
421     CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
422     {
423     JSAtom *atom;
424     JSRuntime *rt;
425     JSOp op;
426     const char *name;
427    
428     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
429     atom = CURRENT_TOKEN(ts).t_atom;
430     rt = cx->runtime;
431     if (atom == rt->atomState.getterAtom)
432     op = JSOP_GETTER;
433     else if (atom == rt->atomState.setterAtom)
434     op = JSOP_SETTER;
435     else
436     return TOK_NAME;
437     if (js_PeekTokenSameLine(cx, ts) != tt)
438     return TOK_NAME;
439     (void) js_GetToken(cx, ts);
440     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
441     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
442     JSMSG_BAD_GETTER_OR_SETTER,
443     (op == JSOP_GETTER)
444     ? js_getter_str
445     : js_setter_str);
446     return TOK_ERROR;
447     }
448     CURRENT_TOKEN(ts).t_op = op;
449     if (JS_HAS_STRICT_OPTION(cx)) {
450     name = js_AtomToPrintableString(cx, atom);
451     if (!name ||
452     !js_ReportCompileErrorNumber(cx, ts, NULL,
453     JSREPORT_WARNING | JSREPORT_STRICT,
454     JSMSG_DEPRECATED_USAGE,
455     name)) {
456     return TOK_ERROR;
457     }
458     }
459     return tt;
460     }
461     #endif
462    
463     /*
464     * Parse a top-level JS script.
465     */
466     JSParseNode *
467     js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
468     {
469     JSTreeContext tc;
470     JSParseNode *pn;
471    
472     /*
473     * Protect atoms from being collected by a GC activation, which might
474     * - nest on this thread due to out of memory (the so-called "last ditch"
475     * GC attempted within js_NewGCThing), or
476     * - run for any reason on another thread if this thread is suspended on
477     * an object lock before it finishes generating bytecode into a script
478     * protected from the GC by a root or a stack frame reference.
479     */
480     TREE_CONTEXT_INIT(&tc, pc);
481     tc.u.scopeChain = chain;
482     pn = Statements(cx, TS(pc), &tc);
483     if (pn) {
484     if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
485     js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
486     JSMSG_SYNTAX_ERROR);
487     pn = NULL;
488     } else {
489     pn->pn_type = TOK_LC;
490     if (!js_FoldConstants(cx, pn, &tc))
491     pn = NULL;
492     }
493     }
494    
495     TREE_CONTEXT_FINISH(cx, &tc);
496     return pn;
497     }
498    
499     /*
500     * Compile a top-level script.
501     */
502     extern JSScript *
503     js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
504     JSPrincipals *principals, uint32 tcflags,
505     const jschar *chars, size_t length,
506     FILE *file, const char *filename, uintN lineno)
507     {
508     JSParseContext pc;
509     JSArenaPool codePool, notePool;
510     JSCodeGenerator cg;
511     JSTokenType tt;
512     JSParseNode *pn;
513     uint32 scriptGlobals;
514     JSScript *script;
515     #ifdef METER_PARSENODES
516     void *sbrk(ptrdiff_t), *before = sbrk(0);
517     #endif
518    
519     JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
520     TCF_STATIC_DEPTH_MASK)));
521    
522     /*
523     * The scripted callerFrame can only be given for compile-and-go scripts
524     * and non-zero static depth requires callerFrame.
525     */
526     JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
527     JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);
528    
529     if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length,
530     file, filename, lineno)) {
531     return NULL;
532     }
533    
534     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
535     &cx->scriptStackQuota);
536     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
537     &cx->scriptStackQuota);
538     js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
539     pc.tokenStream.lineno);
540    
541     MUST_FLOW_THROUGH("out");
542     cg.treeContext.flags |= (uint16) tcflags;
543     cg.treeContext.u.scopeChain = scopeChain;
544     cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
545    
546     /* Inline Statements() to emit as we go to save space. */
547     for (;;) {
548     pc.tokenStream.flags |= TSF_OPERAND;
549     tt = js_PeekToken(cx, &pc.tokenStream);
550     pc.tokenStream.flags &= ~TSF_OPERAND;
551     if (tt <= TOK_EOF) {
552     if (tt == TOK_EOF)
553     break;
554     JS_ASSERT(tt == TOK_ERROR);
555     script = NULL;
556     goto out;
557     }
558    
559     pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
560     if (!pn) {
561     script = NULL;
562     goto out;
563     }
564     JS_ASSERT(!cg.treeContext.blockNode);
565    
566     if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
567     !js_EmitTree(cx, &cg, pn)) {
568     script = NULL;
569     goto out;
570     }
571     RecycleTree(pn, &cg.treeContext);
572     }
573    
574     /*
575     * Global variables and regexps shares the index space with locals. Due to
576     * incremental code generation we need to patch the bytecode to adjust the
577     * local references to skip the globals.
578     */
579     scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;
580     if (scriptGlobals != 0) {
581     jsbytecode *code, *end;
582     JSOp op;
583     const JSCodeSpec *cs;
584     uintN len, slot;
585    
586     if (scriptGlobals >= SLOTNO_LIMIT)
587     goto too_many_slots;
588     code = CG_BASE(&cg);
589     for (end = code + CG_OFFSET(&cg); code != end; code += len) {
590     JS_ASSERT(code < end);
591     op = (JSOp) *code;
592     cs = &js_CodeSpec[op];
593     len = (cs->length > 0)
594     ? (uintN) cs->length
595     : js_GetVariableBytecodeLength(code);
596     if (JOF_TYPE(cs->format) == JOF_LOCAL ||
597     (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
598     /*
599     * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
600     * emitted only for a function.
601     */
602     JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
603     (op == JSOP_GETLOCALPROP));
604     slot = GET_SLOTNO(code);
605     slot += scriptGlobals;
606     if (slot >= SLOTNO_LIMIT)
607     goto too_many_slots;
608     SET_SLOTNO(code, slot);
609     }
610     }
611     }
612    
613     #ifdef METER_PARSENODES
614     printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
615     (char *)sbrk(0) - (char *)before,
616     parsenodes,
617     maxparsenodes,
618     parsenodes - recyclednodes);
619     before = sbrk(0);
620     #endif
621    
622     /*
623     * Nowadays the threaded interpreter needs a stop instruction, so we
624     * do have to emit that here.
625     */
626     if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
627     script = NULL;
628     goto out;
629     }
630     #ifdef METER_PARSENODES
631     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
632     (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
633     #endif
634     #ifdef JS_ARENAMETER
635     JS_DumpArenaStats(stdout);
636     #endif
637     script = js_NewScriptFromCG(cx, &cg);
638    
639     #ifdef JS_SCOPE_DEPTH_METER
640     if (script) {
641     JSObject *obj = scopeChain;
642     uintN depth = 1;
643     while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
644     ++depth;
645     JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
646     }
647     #endif
648    
649     out:
650     js_FinishCodeGenerator(cx, &cg);
651     JS_FinishArenaPool(&codePool);
652     JS_FinishArenaPool(&notePool);
653     js_FinishParseContext(cx, &pc);
654     return script;
655    
656     too_many_slots:
657     js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
658     JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
659     script = NULL;
660     goto out;
661     }
662    
663     /*
664     * Insist on a final return before control flows out of pn. Try to be a bit
665     * smart about loops: do {...; return e2;} while(0) at the end of a function
666     * that contains an early return e1 will get a strict warning. Similarly for
667     * iloops: while (true){...} is treated as though ... returns.
668     */
669     #define ENDS_IN_OTHER 0
670     #define ENDS_IN_RETURN 1
671     #define ENDS_IN_BREAK 2
672    
673     static int
674     HasFinalReturn(JSParseNode *pn)
675     {
676     JSParseNode *pn2, *pn3;
677     uintN rv, rv2, hasDefault;
678    
679     switch (pn->pn_type) {
680     case TOK_LC:
681     if (!pn->pn_head)
682     return ENDS_IN_OTHER;
683     return HasFinalReturn(PN_LAST(pn));
684    
685     case TOK_IF:
686     if (!pn->pn_kid3)
687     return ENDS_IN_OTHER;
688     return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
689    
690     case TOK_WHILE:
691     pn2 = pn->pn_left;
692     if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
693     return ENDS_IN_RETURN;
694     if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
695     return ENDS_IN_RETURN;
696     return ENDS_IN_OTHER;
697    
698     case TOK_DO:
699     pn2 = pn->pn_right;
700     if (pn2->pn_type == TOK_PRIMARY) {
701     if (pn2->pn_op == JSOP_FALSE)
702     return HasFinalReturn(pn->pn_left);
703     if (pn2->pn_op == JSOP_TRUE)
704     return ENDS_IN_RETURN;
705     }
706     if (pn2->pn_type == TOK_NUMBER) {
707     if (pn2->pn_dval == 0)
708     return HasFinalReturn(pn->pn_left);
709     return ENDS_IN_RETURN;
710     }
711     return ENDS_IN_OTHER;
712    
713     case TOK_FOR:
714     pn2 = pn->pn_left;
715     if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
716     return ENDS_IN_RETURN;
717     return ENDS_IN_OTHER;
718    
719     case TOK_SWITCH:
720     rv = ENDS_IN_RETURN;
721     hasDefault = ENDS_IN_OTHER;
722     pn2 = pn->pn_right;
723     if (pn2->pn_type == TOK_LEXICALSCOPE)
724     pn2 = pn2->pn_expr;
725     for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
726     if (pn2->pn_type == TOK_DEFAULT)
727     hasDefault = ENDS_IN_RETURN;
728     pn3 = pn2->pn_right;
729     JS_ASSERT(pn3->pn_type == TOK_LC);
730     if (pn3->pn_head) {
731     rv2 = HasFinalReturn(PN_LAST(pn3));
732     if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
733     /* Falling through to next case or default. */;
734     else
735     rv &= rv2;
736     }
737     }
738     /* If a final switch has no default case, we judge it harshly. */
739     rv &= hasDefault;
740     return rv;
741    
742     case TOK_BREAK:
743     return ENDS_IN_BREAK;
744    
745     case TOK_WITH:
746     return HasFinalReturn(pn->pn_right);
747    
748     case TOK_RETURN:
749     return ENDS_IN_RETURN;
750    
751     case TOK_COLON:
752     case TOK_LEXICALSCOPE:
753     return HasFinalReturn(pn->pn_expr);
754    
755     case TOK_THROW:
756     return ENDS_IN_RETURN;
757    
758     case TOK_TRY:
759     /* If we have a finally block that returns, we are done. */
760     if (pn->pn_kid3) {
761     rv = HasFinalReturn(pn->pn_kid3);
762     if (rv == ENDS_IN_RETURN)
763     return rv;
764     }
765    
766     /* Else check the try block and any and all catch statements. */
767     rv = HasFinalReturn(pn->pn_kid1);
768     if (pn->pn_kid2) {
769     JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
770     for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
771     rv &= HasFinalReturn(pn2);
772     }
773     return rv;
774    
775     case TOK_CATCH:
776     /* Check this catch block's body. */
777     return HasFinalReturn(pn->pn_kid3);
778    
779     case TOK_LET:
780     /* Non-binary let statements are let declarations. */
781     if (pn->pn_arity != PN_BINARY)
782     return ENDS_IN_OTHER;
783     return HasFinalReturn(pn->pn_right);
784    
785     default:
786     return ENDS_IN_OTHER;
787     }
788     }
789    
790     static JSBool
791     ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
792     uintN anonerrnum)
793     {
794     const char *name;
795    
796     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
797     if (tc->u.fun->atom) {
798     name = js_AtomToPrintableString(cx, tc->u.fun->atom);
799     } else {
800     errnum = anonerrnum;
801     name = NULL;
802     }
803     return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
804     errnum, name);
805     }
806    
807     static JSBool
808     CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
809     {
810     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
811     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
812     ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
813     JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
814     }
815    
816     static JSParseNode *
817     FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
818     {
819     JSStmtInfo stmtInfo;
820     uintN oldflags, firstLine;
821     JSParseNode *pn;
822    
823     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
824     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
825     stmtInfo.flags = SIF_BODY_BLOCK;
826    
827     oldflags = tc->flags;
828     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
829    
830     /*
831     * Save the body's first line, and store it in pn->pn_pos.begin.lineno
832     * later, because we may have not peeked in ts yet, so Statements won't
833     * acquire a valid pn->pn_pos.begin from the current token.
834     */
835     firstLine = ts->lineno;
836     #if JS_HAS_EXPR_CLOSURES
837     if (CURRENT_TOKEN(ts).type == TOK_LC) {
838     pn = Statements(cx, ts, tc);
839     } else {
840     pn = NewParseNode(cx, ts, PN_UNARY, tc);
841     if (pn) {
842     pn->pn_kid = AssignExpr(cx, ts, tc);
843     if (!pn->pn_kid) {
844     pn = NULL;
845     } else {
846     if (tc->flags & TCF_FUN_IS_GENERATOR) {
847     ReportBadReturn(cx, tc, JSREPORT_ERROR,
848     JSMSG_BAD_GENERATOR_RETURN,
849     JSMSG_BAD_ANON_GENERATOR_RETURN);
850     pn = NULL;
851     } else {
852     pn->pn_type = TOK_RETURN;
853     pn->pn_op = JSOP_RETURN;
854     pn->pn_pos.end = pn->pn_kid->pn_pos.end;
855     }
856     }
857     }
858     }
859     #else
860     pn = Statements(cx, ts, tc);
861     #endif
862    
863     if (pn) {
864     js_PopStatement(tc);
865     pn->pn_pos.begin.lineno = firstLine;
866    
867     /* Check for falling off the end of a function that returns a value. */
868     if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
869     !CheckFinalReturn(cx, tc, pn)) {
870     pn = NULL;
871     }
872     }
873    
874     tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
875     return pn;
876     }
877    
878     /*
879     * Compile a JS function body, which might appear as the value of an event
880     * handler attribute in an HTML <INPUT> tag.
881     */
882     JSBool
883     js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
884     const jschar *chars, size_t length,
885     const char *filename, uintN lineno)
886     {
887     JSParseContext pc;
888     JSArenaPool codePool, notePool;
889     JSCodeGenerator funcg;
890     JSParseNode *pn;
891    
892     if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
893     filename, lineno)) {
894     return JS_FALSE;
895     }
896    
897     /* No early return from this point until js_FinishParseContext call. */
898     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
899     &cx->scriptStackQuota);
900     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
901     &cx->scriptStackQuota);
902     js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
903     pc.tokenStream.lineno);
904     funcg.treeContext.flags |= TCF_IN_FUNCTION;
905     funcg.treeContext.u.fun = fun;
906    
907     /*
908     * Farble the body so that it looks like a block statement to js_EmitTree,
909     * which is called beneath FunctionBody; see Statements, further below in
910     * this file. FunctionBody pushes a STMT_BLOCK record around its call to
911     * Statements, so Statements will not compile each statement as it loops
912     * to save JSParseNode space -- it will not compile at all, only build a
913     * JSParseNode tree.
914     *
915     * Therefore we must fold constants, allocate try notes, and generate code
916     * for this function, including a stop opcode at the end.
917     */
918     CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;
919     pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);
920     if (pn) {
921     if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
922     js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
923     JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
924     pn = NULL;
925     } else {
926     if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
927     !js_EmitFunctionScript(cx, &funcg, pn)) {
928     pn = NULL;
929     }
930     }
931     }
932    
933     /* Restore saved state and release code generation arenas. */
934     js_FinishCodeGenerator(cx, &funcg);
935     JS_FinishArenaPool(&codePool);
936     JS_FinishArenaPool(&notePool);
937     js_FinishParseContext(cx, &pc);
938     return pn != NULL;
939     }
940    
941     /*
942     * Parameter block types for the several Binder functions. We use a common
943     * helper function signature in order to share code among destructuring and
944     * simple variable declaration parsers. In the destructuring case, the binder
945     * function is called indirectly from the variable declaration parser by way
946     * of CheckDestructuring and its friends.
947     */
948     typedef struct BindData BindData;
949    
950     typedef JSBool
951     (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
952    
953     struct BindData {
954     JSParseNode *pn; /* error source coordinate */
955     JSOp op; /* prolog bytecode or nop */
956     Binder binder; /* binder, discriminates u */
957     union {
958     struct {
959     uintN overflow;
960     } let;
961     } u;
962     };
963    
964     static JSBool
965     BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
966     {
967     const char *name;
968    
969     /*
970     * Check for a duplicate parameter name, a "feature" required by ECMA-262.
971     */
972     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
973     if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
974     name = js_AtomToPrintableString(cx, atom);
975     if (!name ||
976     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
977     JSREPORT_WARNING | JSREPORT_STRICT,
978     JSMSG_DUPLICATE_FORMAL,
979     name)) {
980     return JS_FALSE;
981     }
982     }
983    
984     return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
985     }
986    
987     static JSBool
988     BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
989     JSLocalKind localKind)
990     {
991     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
992    
993     /*
994     * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
995     * Instead 'var arguments' always restates the predefined property of the
996     * activation objects with unhidden name 'arguments'. Assignment to such
997     * a variable must be handled specially.
998     */
999     if (atom == cx->runtime->atomState.argumentsAtom)
1000     return JS_TRUE;
1001    
1002     return js_AddLocal(cx, fun, atom, localKind);
1003     }
1004    
1005     #if JS_HAS_DESTRUCTURING
1006     /*
1007     * Forward declaration to maintain top-down presentation.
1008     */
1009     static JSParseNode *
1010     DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1011     JSTokenType tt);
1012    
1013     static JSBool
1014     BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1015     JSTreeContext *tc)
1016     {
1017     JSAtomListElement *ale;
1018     const char *name;
1019    
1020     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1021     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1022     if (!ale) {
1023     ale = js_IndexAtom(cx, atom, &tc->decls);
1024     if (!ale)
1025     return JS_FALSE;
1026     ALE_SET_JSOP(ale, data->op);
1027     }
1028    
1029     if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
1030     name = js_AtomToPrintableString(cx, atom);
1031     if (!name ||
1032     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1033     JSREPORT_WARNING | JSREPORT_STRICT,
1034     JSMSG_DUPLICATE_FORMAL,
1035     name)) {
1036     return JS_FALSE;
1037     }
1038     } else {
1039     if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
1040     return JS_FALSE;
1041     }
1042     return JS_TRUE;
1043     }
1044     #endif /* JS_HAS_DESTRUCTURING */
1045    
1046     static JSFunction *
1047     NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
1048     uintN lambda)
1049     {
1050     JSObject *parent;
1051     JSFunction *fun;
1052    
1053     JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1054     parent = (tc->flags & TCF_IN_FUNCTION)
1055     ? FUN_OBJECT(tc->u.fun)
1056     : tc->u.scopeChain;
1057     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1058     parent, atom);
1059     if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1060     STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1061     STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1062     }
1063     return fun;
1064     }
1065    
1066     static JSParseNode *
1067     FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1068     uintN lambda)
1069     {
1070     JSOp op, prevop;
1071     JSParseNode *pn, *body, *result;
1072     JSTokenType tt;
1073     JSAtom *funAtom;
1074     JSParsedObjectBox *funpob;
1075     JSAtomListElement *ale;
1076     JSFunction *fun;
1077     JSTreeContext funtc;
1078     #if JS_HAS_DESTRUCTURING
1079     JSParseNode *item, *list = NULL;
1080     #endif
1081    
1082     /* Make a TOK_FUNCTION node. */
1083     #if JS_HAS_GETTER_SETTER
1084     op = CURRENT_TOKEN(ts).t_op;
1085     #endif
1086     pn = NewParseNode(cx, ts, PN_FUNC, tc);
1087     if (!pn)
1088     return NULL;
1089     #ifdef DEBUG
1090     pn->pn_index = (uint32) -1;
1091     #endif
1092    
1093     /* Scan the optional function name into funAtom. */
1094     ts->flags |= TSF_KEYWORD_IS_NAME;
1095     tt = js_GetToken(cx, ts);
1096     ts->flags &= ~TSF_KEYWORD_IS_NAME;
1097     if (tt == TOK_NAME) {
1098     funAtom = CURRENT_TOKEN(ts).t_atom;
1099     } else {
1100     if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
1101     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1102     JSMSG_SYNTAX_ERROR);
1103     return NULL;
1104     }
1105     funAtom = NULL;
1106     js_UngetToken(ts);
1107     }
1108    
1109     /*
1110     * Record names for function statements in tc->decls so we know when to
1111     * avoid optimizing variable references that might name a function.
1112     */
1113     if (lambda == 0 && funAtom) {
1114     ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1115     if (ale) {
1116     prevop = ALE_JSOP(ale);
1117     if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1118     const char *name = js_AtomToPrintableString(cx, funAtom);
1119     if (!name ||
1120     !js_ReportCompileErrorNumber(cx, ts, NULL,
1121     (prevop != JSOP_DEFCONST)
1122     ? JSREPORT_WARNING |
1123     JSREPORT_STRICT
1124     : JSREPORT_ERROR,
1125     JSMSG_REDECLARED_VAR,
1126     (prevop == JSOP_DEFFUN)
1127     ? js_function_str
1128     : (prevop == JSOP_DEFCONST)
1129     ? js_const_str
1130     : js_var_str,
1131     name)) {
1132     return NULL;
1133     }
1134     }
1135     if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1136     tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1137     } else {
1138     ale = js_IndexAtom(cx, funAtom, &tc->decls);
1139     if (!ale)
1140     return NULL;
1141     }
1142     ALE_SET_JSOP(ale, JSOP_DEFFUN);
1143    
1144     /*
1145     * A function nested at top level inside another's body needs only a
1146     * local variable to bind its name to its value, and not an activation
1147     * object property (it might also need the activation property, if the
1148     * outer function contains with statements, e.g., but the stack slot
1149     * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1150     * JSOP_GETLOCAL bytecode).
1151     */
1152     if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
1153     JSLocalKind localKind;
1154    
1155     /*
1156     * Define a property on the outer function so that BindNameToSlot
1157     * can properly optimize accesses. Note that we need a variable,
1158     * not an argument, for the function statement. Thus we add a
1159     * variable even if the parameter with the given name already
1160     * exists.
1161     */
1162     localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
1163     if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
1164     if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
1165     return NULL;
1166     }
1167     }
1168     }
1169    
1170     fun = NewCompilerFunction(cx, tc, funAtom, lambda);
1171     if (!fun)
1172     return NULL;
1173    
1174     #if JS_HAS_GETTER_SETTER
1175     if (op != JSOP_NOP)
1176     fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1177     #endif
1178    
1179     /*
1180     * Create wrapping box for fun->object early to protect against a
1181     * last-ditch GC.
1182     */
1183     funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
1184     if (!funpob)
1185     return NULL;
1186    
1187     /* Initialize early for possible flags mutation via DestructuringExpr. */
1188     TREE_CONTEXT_INIT(&funtc, tc->parseContext);
1189     funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
1190     funtc.u.fun = fun;
1191    
1192     /* Now parse formal argument list and compute fun->nargs. */
1193     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1194     if (!js_MatchToken(cx, ts, TOK_RP)) {
1195     do {
1196     tt = js_GetToken(cx, ts);
1197     switch (tt) {
1198     #if JS_HAS_DESTRUCTURING
1199     case TOK_LB:
1200     case TOK_LC:
1201     {
1202     BindData data;
1203     JSParseNode *lhs, *rhs;
1204     jsint slot;
1205    
1206     /*
1207     * A destructuring formal parameter turns into one or more
1208     * local variables initialized from properties of a single
1209     * anonymous positional parameter, so here we must tweak our
1210     * binder and its data.
1211     */
1212     data.pn = NULL;
1213     data.op = JSOP_DEFVAR;
1214     data.binder = BindDestructuringArg;
1215     lhs = DestructuringExpr(cx, &data, &funtc, tt);
1216     if (!lhs)
1217     return NULL;
1218    
1219     /*
1220     * Adjust fun->nargs to count the single anonymous positional
1221     * parameter that is to be destructured.
1222     */
1223     slot = fun->nargs;
1224     if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
1225     return NULL;
1226    
1227     /*
1228     * Synthesize a destructuring assignment from the single
1229     * anonymous positional parameter into the destructuring
1230     * left-hand-side expression and accumulate it in list.
1231     */
1232     rhs = NewParseNode(cx, ts, PN_NAME, tc);
1233     if (!rhs)
1234     return NULL;
1235     rhs->pn_type = TOK_NAME;
1236     rhs->pn_op = JSOP_GETARG;
1237     rhs->pn_atom = cx->runtime->atomState.emptyAtom;
1238     rhs->pn_slot = slot;
1239    
1240     item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
1241     if (!item)
1242     return NULL;
1243     if (!list) {
1244     list = NewParseNode(cx, ts, PN_LIST, tc);
1245     if (!list)
1246     return NULL;
1247     list->pn_type = TOK_COMMA;
1248     PN_INIT_LIST(list);
1249     }
1250     PN_APPEND(list, item);
1251     break;
1252     }
1253     #endif /* JS_HAS_DESTRUCTURING */
1254    
1255     case TOK_NAME:
1256     if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
1257     return NULL;
1258     break;
1259    
1260     default:
1261     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1262     JSMSG_MISSING_FORMAL);
1263     return NULL;
1264     }
1265     } while (js_MatchToken(cx, ts, TOK_COMMA));
1266    
1267     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1268     }
1269    
1270     #if JS_HAS_EXPR_CLOSURES
1271     ts->flags |= TSF_OPERAND;
1272     tt = js_GetToken(cx, ts);
1273     ts->flags &= ~TSF_OPERAND;
1274     if (tt != TOK_LC) {
1275     js_UngetToken(ts);
1276     fun->flags |= JSFUN_EXPR_CLOSURE;
1277     }
1278     #else
1279     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1280     #endif
1281     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1282    
1283     body = FunctionBody(cx, ts, &funtc);
1284     if (!body)
1285     return NULL;
1286    
1287     #if JS_HAS_EXPR_CLOSURES
1288     if (tt == TOK_LC)
1289     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1290     else if (lambda == 0)
1291     js_MatchToken(cx, ts, TOK_SEMI);
1292     #else
1293     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1294     #endif
1295     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1296    
1297     #if JS_HAS_DESTRUCTURING
1298     /*
1299     * If there were destructuring formal parameters, prepend the initializing
1300     * comma expression that we synthesized to body. If the body is a lexical
1301     * scope node, we must make a special TOK_BODY node, to prepend the formal
1302     * parameter destructuring code without bracing the decompilation of the
1303     * function body's lexical scope.
1304     */
1305     if (list) {
1306     if (body->pn_arity != PN_LIST) {
1307     JSParseNode *block;
1308    
1309     block = NewParseNode(cx, ts, PN_LIST, tc);
1310     if (!block)
1311     return NULL;
1312     block->pn_type = TOK_BODY;
1313     block->pn_pos = body->pn_pos;
1314     PN_INIT_LIST_1(block, body);
1315    
1316     body = block;
1317     }
1318    
1319     item = NewParseNode(cx, ts, PN_UNARY, tc);
1320     if (!item)
1321     return NULL;
1322    
1323     item->pn_type = TOK_SEMI;
1324     item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1325     item->pn_kid = list;
1326     item->pn_next = body->pn_head;
1327     body->pn_head = item;
1328     if (body->pn_tail == &body->pn_head)
1329     body->pn_tail = &item->pn_next;
1330     ++body->pn_count;
1331     }
1332     #endif
1333    
1334     /*
1335     * If we collected flags that indicate nested heavyweight functions, or
1336     * this function contains heavyweight-making statements (references to
1337     * __parent__ or __proto__; use of with, or eval; and assignment to
1338     * arguments), flag the function as heavyweight (requiring a call object
1339     * per invocation).
1340     */
1341     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1342     fun->flags |= JSFUN_HEAVYWEIGHT;
1343     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1344     } else {
1345     /*
1346     * If this function is a named statement function not at top-level
1347     * (i.e. not a top-level function definiton or expression), then
1348     * our enclosing function, if any, must be heavyweight.
1349     *
1350     * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1351     * so it won't be set here. Assert that it's not. We have to check
1352     * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
1353     * the function's body.
1354     */
1355     JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
1356     if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))
1357     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1358     }
1359    
1360     result = pn;
1361     if (lambda != 0) {
1362     /*
1363     * ECMA ed. 3 standard: function expression, possibly anonymous.
1364     */
1365     op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1366     } else if (!funAtom) {
1367     /*
1368     * If this anonymous function definition is *not* embedded within a
1369     * larger expression, we treat it as an expression statement, not as
1370     * a function declaration -- and not as a syntax error (as ECMA-262
1371     * Edition 3 would have it). Backward compatibility must trump all,
1372     * unless JSOPTION_ANONFUNFIX is set.
1373     */
1374     result = NewParseNode(cx, ts, PN_UNARY, tc);
1375     if (!result)
1376     return NULL;
1377     result->pn_type = TOK_SEMI;
1378     result->pn_pos = pn->pn_pos;
1379     result->pn_kid = pn;
1380     op = JSOP_ANONFUNOBJ;
1381     } else if (!AT_TOP_LEVEL(tc)) {
1382     /*
1383     * ECMA ed. 3 extension: a function expression statement not at the
1384     * top level, e.g., in a compound statement such as the "then" part
1385     * of an "if" statement, binds a closure only if control reaches that
1386     * sub-statement.
1387     */
1388     op = JSOP_DEFFUN;
1389     } else {
1390     op = JSOP_NOP;
1391     }
1392    
1393     pn->pn_funpob = funpob;
1394     pn->pn_op = op;
1395     pn->pn_body = body;
1396     pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
1397     TREE_CONTEXT_FINISH(cx, &funtc);
1398     return result;
1399     }
1400    
1401     static JSParseNode *
1402     FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1403     {
1404     return FunctionDef(cx, ts, tc, 0);
1405     }
1406    
1407     static JSParseNode *
1408     FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1409     {
1410     return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
1411     }
1412    
1413     /*
1414     * Parse the statements in a block, creating a TOK_LC node that lists the
1415     * statements' trees. If called from block-parsing code, the caller must
1416     * match { before and } after.
1417     */
1418     static JSParseNode *
1419     Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1420     {
1421     JSParseNode *pn, *pn2, *saveBlock;
1422     JSTokenType tt;
1423    
1424     JS_CHECK_RECURSION(cx, return NULL);
1425    
1426     pn = NewParseNode(cx, ts, PN_LIST, tc);
1427     if (!pn)
1428     return NULL;
1429     saveBlock = tc->blockNode;
1430     tc->blockNode = pn;
1431     PN_INIT_LIST(pn);
1432    
1433     for (;;) {
1434     ts->flags |= TSF_OPERAND;
1435     tt = js_PeekToken(cx, ts);
1436     ts->flags &= ~TSF_OPERAND;
1437     if (tt <= TOK_EOF || tt == TOK_RC) {
1438     if (tt == TOK_ERROR)
1439     return NULL;
1440     break;
1441     }
1442     pn2 = Statement(cx, ts, tc);
1443     if (!pn2) {
1444     if (ts->flags & TSF_EOF)
1445     ts->flags |= TSF_UNEXPECTED_EOF;
1446     return NULL;
1447     }
1448    
1449     if (pn2->pn_type == TOK_FUNCTION) {
1450     /*
1451     * PNX_FUNCDEFS notifies the emitter that the block contains top-
1452     * level function definitions that should be processed before the
1453     * rest of nodes.
1454     *
1455     * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
1456     * is relevant only for function definitions not at top-level,
1457     * which we call function statements.
1458     */
1459     if (AT_TOP_LEVEL(tc))
1460     pn->pn_extra |= PNX_FUNCDEFS;
1461     else
1462     tc->flags |= TCF_HAS_FUNCTION_STMT;
1463     }
1464     PN_APPEND(pn, pn2);
1465     }
1466    
1467     /*
1468     * Handle the case where there was a let declaration under this block. If
1469     * it replaced tc->blockNode with a new block node then we must refresh pn
1470     * and then restore tc->blockNode.
1471     */
1472     if (tc->blockNode != pn)
1473     pn = tc->blockNode;
1474     tc->blockNode = saveBlock;
1475    
1476     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1477     return pn;
1478     }
1479    
1480     static JSParseNode *
1481     Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1482     {
1483     JSParseNode *pn;
1484    
1485     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1486     pn = ParenExpr(cx, ts, tc, NULL, NULL);
1487     if (!pn)
1488     return NULL;
1489     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1490    
1491     /*
1492     * Check for (a = b) and warn about possible (a == b) mistype iff b's
1493     * operator has greater precedence than ==.
1494     */
1495     if (pn->pn_type == TOK_ASSIGN &&
1496     pn->pn_op == JSOP_NOP &&
1497     pn->pn_right->pn_type > TOK_EQOP)
1498     {
1499     if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1500     JSREPORT_WARNING | JSREPORT_STRICT,
1501     JSMSG_EQUAL_AS_ASSIGN,
1502     "")) {
1503     return NULL;
1504     }
1505     }
1506     return pn;
1507     }
1508    
1509     static JSBool
1510     MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1511     {
1512     JSAtom *label;
1513     JSTokenType tt;
1514    
1515     tt = js_PeekTokenSameLine(cx, ts);
1516     if (tt == TOK_ERROR)
1517     return JS_FALSE;
1518     if (tt == TOK_NAME) {
1519     (void) js_GetToken(cx, ts);
1520     label = CURRENT_TOKEN(ts).t_atom;
1521     } else {
1522     label = NULL;
1523     }
1524     pn->pn_atom = label;
1525     return JS_TRUE;
1526     }
1527    
1528     static JSBool
1529     BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1530     {
1531     JSObject *blockObj;
1532     JSScopeProperty *sprop;
1533     JSAtomListElement *ale;
1534     uintN n;
1535    
1536     blockObj = tc->blockChain;
1537     sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
1538     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1539     if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
1540     const char *name;
1541    
1542     if (sprop) {
1543     JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1544     JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj));
1545     }
1546    
1547     name = js_AtomToPrintableString(cx, atom);
1548     if (name) {
1549     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1550     JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
1551     (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
1552     ? js_const_str
1553     : "variable",
1554     name);
1555     }
1556     return JS_FALSE;
1557     }
1558    
1559     n = OBJ_BLOCK_COUNT(cx, blockObj);
1560     if (n == JS_BIT(16)) {
1561     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1562     JSREPORT_ERROR, data->u.let.overflow);
1563     return JS_FALSE;
1564     }
1565    
1566     /* Use JSPROP_ENUMERATE to aid the disassembler. */
1567     return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
1568     JSVAL_VOID, NULL, NULL,
1569     JSPROP_ENUMERATE |
1570     JSPROP_PERMANENT |
1571     JSPROP_SHARED,
1572     SPROP_HAS_SHORTID, (int16) n, NULL);
1573     }
1574    
1575     static JSBool
1576     BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1577     {
1578     JSStmtInfo *stmt;
1579     JSAtomListElement *ale;
1580     JSOp op, prevop;
1581     const char *name;
1582     JSLocalKind localKind;
1583    
1584     stmt = js_LexicalLookup(tc, atom, NULL);
1585     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1586     op = data->op;
1587     if ((stmt && stmt->type != STMT_WITH) || ale) {
1588     prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
1589     if (JS_HAS_STRICT_OPTION(cx)
1590     ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
1591     : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
1592     name = js_AtomToPrintableString(cx, atom);
1593     if (!name ||
1594     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1595     (op != JSOP_DEFCONST &&
1596     prevop != JSOP_DEFCONST)
1597     ? JSREPORT_WARNING |
1598     JSREPORT_STRICT
1599     : JSREPORT_ERROR,
1600     JSMSG_REDECLARED_VAR,
1601     (prevop == JSOP_DEFFUN)
1602     ? js_function_str
1603     : (prevop == JSOP_DEFCONST)
1604     ? js_const_str
1605     : js_var_str,
1606     name)) {
1607     return JS_FALSE;
1608     }
1609     }
1610     if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)
1611     tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1612     }
1613     if (!ale) {
1614     ale = js_IndexAtom(cx, atom, &tc->decls);
1615     if (!ale)
1616     return JS_FALSE;
1617     }
1618     ALE_SET_JSOP(ale, op);
1619    
1620     if (!(tc->flags & TCF_IN_FUNCTION)) {
1621     /*
1622     * Don't lookup global variables or variables in an active frame at
1623     * compile time.
1624     */
1625     return JS_TRUE;
1626     }
1627    
1628     localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL);
1629     if (localKind == JSLOCAL_NONE) {
1630     /*
1631     * Property not found in current variable scope: we have not seen this
1632     * variable before. Define a new local variable by adding a property
1633     * to the function's scope, allocating one slot in the function's vars
1634     * frame. Any locals declared in with statement bodies are handled at
1635     * runtime, by script prolog JSOP_DEFVAR opcodes generated for
1636     * slot-less vars.
1637     */
1638     localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
1639     if (!js_InWithStatement(tc) &&
1640     !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {
1641     return JS_FALSE;
1642     }
1643     } else if (localKind == JSLOCAL_ARG) {
1644     name = js_AtomToPrintableString(cx, atom);
1645     if (!name)
1646     return JS_FALSE;
1647    
1648     if (op == JSOP_DEFCONST) {
1649     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1650     JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
1651     name);
1652     return JS_FALSE;
1653     }
1654     if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1655     JSREPORT_WARNING | JSREPORT_STRICT,
1656     JSMSG_VAR_HIDES_ARG, name)) {
1657     return JS_FALSE;
1658     }
1659     } else {
1660     /* Not an argument, must be a redeclared local var. */
1661     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1662     }
1663     return JS_TRUE;
1664     }
1665    
1666     #if JS_HAS_DESTRUCTURING
1667    
1668     static JSBool
1669     BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
1670     JSTreeContext *tc)
1671     {
1672     JSAtom *atom;
1673    
1674     /*
1675     * Destructuring is a form of assignment, so just as for an initialized
1676     * simple variable, we must check for assignment to 'arguments' and flag
1677     * the enclosing function (if any) as heavyweight.
1678     */
1679     JS_ASSERT(pn->pn_type == TOK_NAME);
1680     atom = pn->pn_atom;
1681     if (atom == cx->runtime->atomState.argumentsAtom)
1682     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1683    
1684     data->pn = pn;
1685     if (!data->binder(cx, data, atom, tc))
1686     return JS_FALSE;
1687     data->pn = NULL;
1688    
1689     /*
1690     * Select the appropriate name-setting opcode, which may be specialized
1691     * further for local variable and argument slot optimizations. At this
1692     * point, we can't select the optimal final opcode, yet we must preserve
1693     * the CONST bit and convey "set", not "get".
1694     */
1695     if (data->op == JSOP_DEFCONST) {
1696     pn->pn_op = JSOP_SETCONST;
1697     pn->pn_const = JS_TRUE;
1698     } else {
1699     pn->pn_op = JSOP_SETNAME;
1700     pn->pn_const = JS_FALSE;
1701     }
1702     return JS_TRUE;
1703     }
1704    
1705     static JSBool
1706     MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
1707     {
1708     JSParseNode *pn2;
1709    
1710     JS_ASSERT(pn->pn_arity == PN_LIST);
1711     JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
1712     pn2 = pn->pn_head;
1713     if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) {
1714     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
1715     JSREPORT_ERROR, msg);
1716     return JS_FALSE;
1717     }
1718     pn->pn_op = JSOP_SETCALL;
1719     return JS_TRUE;
1720     }
1721    
1722     /*
1723     * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
1724     * LHS expression except a destructuring initialiser, and R is on the stack.
1725     * Because R is already evaluated, the usual LHS-specialized bytecodes won't
1726     * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
1727     * then push its property name QN. At this point the stack looks like
1728     *
1729     * [... R, R[P], QB, QN]
1730     *
1731     * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
1732     * its operands with left-hand side above right-hand side:
1733     *
1734     * [rval, lval, xval]
1735     *
1736     * and pops all three values, setting lval[xval] = rval. But we cannot select
1737     * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
1738     * which can be optimized further. So we select JSOP_SETNAME.
1739     */
1740     static JSBool
1741     BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
1742     {
1743     while (pn->pn_type == TOK_RP)
1744     pn = pn->pn_kid;
1745    
1746     switch (pn->pn_type) {
1747     case TOK_NAME:
1748     if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
1749     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1750     /* FALL THROUGH */
1751     case TOK_DOT:
1752     case TOK_LB:
1753     pn->pn_op = JSOP_SETNAME;
1754     break;
1755    
1756     #if JS_HAS_LVALUE_RETURN
1757     case TOK_LP:
1758     if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
1759     return JS_FALSE;
1760     break;
1761     #endif
1762    
1763     #if JS_HAS_XML_SUPPORT
1764     case TOK_UNARYOP:
1765     if (pn->pn_op == JSOP_XMLNAME) {
1766     pn->pn_op = JSOP_BINDXMLNAME;
1767     break;
1768     }
1769     /* FALL THROUGH */
1770     #endif
1771    
1772     default:
1773     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
1774     JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
1775     return JS_FALSE;
1776     }
1777    
1778     return JS_TRUE;
1779     }
1780    
1781     typedef struct FindPropValData {
1782     uint32 numvars; /* # of destructuring vars in left side */
1783     uint32 maxstep; /* max # of steps searching right side */
1784     JSDHashTable table; /* hash table for O(1) right side search */
1785     } FindPropValData;
1786    
1787     typedef struct FindPropValEntry {
1788     JSDHashEntryHdr hdr;
1789     JSParseNode *pnkey;
1790     JSParseNode *pnval;
1791     } FindPropValEntry;
1792    
1793     #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
1794     JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
1795     ((pnkey)->pn_type == TOK_NUMBER || \
1796     (pnkey)->pn_type == TOK_STRING || \
1797     (pnkey)->pn_type == TOK_NAME))
1798    
1799     static JSDHashNumber
1800     HashFindPropValKey(JSDHashTable *table, const void *key)
1801     {
1802     const JSParseNode *pnkey = (const JSParseNode *)key;
1803    
1804     ASSERT_VALID_PROPERTY_KEY(pnkey);
1805     return (pnkey->pn_type == TOK_NUMBER)
1806     ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
1807     JSDOUBLE_LO32(pnkey->pn_dval))
1808     : ATOM_HASH(pnkey->pn_atom);
1809     }
1810    
1811     static JSBool
1812     MatchFindPropValEntry(JSDHashTable *table,
1813     const JSDHashEntryHdr *entry,
1814     const void *key)
1815     {
1816     const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
1817     const JSParseNode *pnkey = (const JSParseNode *)key;
1818    
1819     ASSERT_VALID_PROPERTY_KEY(pnkey);
1820     return pnkey->pn_type == fpve->pnkey->pn_type &&
1821     ((pnkey->pn_type == TOK_NUMBER)
1822     ? pnkey->pn_dval == fpve->pnkey->pn_dval
1823     : pnkey->pn_atom == fpve->pnkey->pn_atom);
1824     }
1825    
1826     static const JSDHashTableOps FindPropValOps = {
1827     JS_DHashAllocTable,
1828     JS_DHashFreeTable,
1829     HashFindPropValKey,
1830     MatchFindPropValEntry,
1831     JS_DHashMoveEntryStub,
1832     JS_DHashClearEntryStub,
1833     JS_DHashFinalizeStub,
1834     NULL
1835     };
1836    
1837     #define STEP_HASH_THRESHOLD 10
1838     #define BIG_DESTRUCTURING 5
1839     #define BIG_OBJECT_INIT 20
1840    
1841     static JSParseNode *
1842     FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
1843     {
1844     FindPropValEntry *entry;
1845     JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
1846     uint32 step;
1847    
1848     /* If we have a hash table, use it as the sole source of truth. */
1849     if (data->table.ops) {
1850     entry = (FindPropValEntry *)
1851     JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
1852     return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
1853     }
1854    
1855     /* If pn is not an object initialiser node, we can't do anything here. */
1856     if (pn->pn_type != TOK_RC)
1857     return NULL;
1858    
1859     /*
1860     * We must search all the way through pn's list, to handle the case of an
1861     * id duplicated for two or more property initialisers.
1862     */
1863     pnhit = NULL;
1864     step = 0;
1865     ASSERT_VALID_PROPERTY_KEY(pnid);
1866     pnhead = pn->pn_head;
1867     if (pnhead && pnhead->pn_type == TOK_DEFSHARP)
1868     pnhead = pnhead->pn_next;
1869     if (pnid->pn_type == TOK_NUMBER) {
1870     for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
1871     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1872     if (pnprop->pn_op == JSOP_NOP) {
1873     pnkey = pnprop->pn_left;
1874     ASSERT_VALID_PROPERTY_KEY(pnkey);
1875     if (pnkey->pn_type == TOK_NUMBER &&
1876     pnkey->pn_dval == pnid->pn_dval) {
1877     pnhit = pnprop;
1878     }
1879     ++step;
1880     }
1881     }
1882     } else {
1883     for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
1884     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1885     if (pnprop->pn_op == JSOP_NOP) {
1886     pnkey = pnprop->pn_left;
1887     ASSERT_VALID_PROPERTY_KEY(pnkey);
1888     if (pnkey->pn_type == pnid->pn_type &&
1889     pnkey->pn_atom == pnid->pn_atom) {
1890     pnhit = pnprop;
1891     }
1892     ++step;
1893     }
1894     }
1895     }
1896     if (!pnhit)
1897     return NULL;
1898    
1899     /* Hit via full search -- see whether it's time to create the hash table. */
1900     JS_ASSERT(!data->table.ops);
1901     if (step > data->maxstep) {
1902     data->maxstep = step;
1903     if (step >= STEP_HASH_THRESHOLD &&
1904     data->numvars >= BIG_DESTRUCTURING &&
1905     pn->pn_count >= BIG_OBJECT_INIT &&
1906     JS_DHashTableInit(&data->table, &FindPropValOps, pn,
1907     sizeof(FindPropValEntry),
1908     JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
1909     {
1910     for (pn = pnhead; pn; pn = pn->pn_next) {
1911     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1912     ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
1913     entry = (FindPropValEntry *)
1914     JS_DHashTableOperate(&data->table, pn->pn_left,
1915     JS_DHASH_ADD);
1916     entry->pnval = pn->pn_right;
1917     }
1918     }
1919     }
1920     return pnhit->pn_right;
1921     }
1922    
1923     /*
1924     * If data is null, the caller is AssignExpr and instead of binding variables,
1925     * we specialize lvalues in the propery value positions of the left-hand side.
1926     * If right is null, just check for well-formed lvalues.
1927     */
1928     static JSBool
1929     CheckDestructuring(JSContext *cx, BindData *data,
1930     JSParseNode *left, JSParseNode *right,
1931     JSTreeContext *tc)
1932     {
1933     JSBool ok;
1934     FindPropValData fpvd;
1935     JSParseNode *lhs, *rhs, *pn, *pn2;
1936    
1937     if (left->pn_type == TOK_ARRAYCOMP) {
1938     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left,
1939     JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
1940     return JS_FALSE;
1941     }
1942    
1943     #if JS_HAS_DESTRUCTURING_SHORTHAND
1944     if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) {
1945     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right,
1946     JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
1947     return JS_FALSE;
1948     }
1949     #endif
1950    
1951     fpvd.table.ops = NULL;
1952     lhs = left->pn_head;
1953     if (lhs && lhs->pn_type == TOK_DEFSHARP) {
1954     pn = lhs;
1955     goto no_var_name;
1956     }
1957    
1958     if (left->pn_type == TOK_RB) {
1959     rhs = (right && right->pn_type == left->pn_type)
1960     ? right->pn_head
1961     : NULL;
1962    
1963     while (lhs) {
1964     pn = lhs, pn2 = rhs;
1965     if (!data) {
1966     /* Skip parenthesization if not in a variable declaration. */
1967     while (pn->pn_type == TOK_RP)
1968     pn = pn->pn_kid;
1969     if (pn2) {
1970     while (pn2->pn_type == TOK_RP)
1971     pn2 = pn2->pn_kid;
1972     }
1973     }
1974    
1975     /* Nullary comma is an elision; binary comma is an expression.*/
1976     if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
1977     if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
1978     ok = CheckDestructuring(cx, data, pn, pn2, tc);
1979     } else {
1980     if (data) {
1981     if (pn->pn_type != TOK_NAME)
1982     goto no_var_name;
1983    
1984     ok = BindDestructuringVar(cx, data, pn, tc);
1985     } else {
1986     ok = BindDestructuringLHS(cx, pn, tc);
1987     }
1988     }
1989     if (!ok)
1990     goto out;
1991     }
1992    
1993     lhs = lhs->pn_next;
1994     if (rhs)
1995     rhs = rhs->pn_next;
1996     }
1997     } else {
1998     JS_ASSERT(left->pn_type == TOK_RC);
1999     fpvd.numvars = left->pn_count;
2000     fpvd.maxstep = 0;
2001     rhs = NULL;
2002    
2003     while (lhs) {
2004     JS_ASSERT(lhs->pn_type == TOK_COLON);
2005     pn = lhs->pn_right;
2006     if (!data) {
2007     /* Skip parenthesization if not in a variable declaration. */
2008     while (pn->pn_type == TOK_RP)
2009     pn = pn->pn_kid;
2010     }
2011    
2012     if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2013     if (right) {
2014     rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
2015     if (rhs && !data) {
2016     while (rhs->pn_type == TOK_RP)
2017     rhs = rhs->pn_kid;
2018     }
2019     }
2020    
2021     ok = CheckDestructuring(cx, data, pn, rhs, tc);
2022     } else if (data) {
2023     if (pn->pn_type != TOK_NAME)
2024     goto no_var_name;
2025    
2026     ok = BindDestructuringVar(cx, data, pn, tc);
2027     } else {
2028     ok = BindDestructuringLHS(cx, pn, tc);
2029     }
2030     if (!ok)
2031     goto out;
2032    
2033     lhs = lhs->pn_next;
2034     }
2035     }
2036    
2037     /*
2038     * The catch/finally handler implementation in the interpreter assumes
2039     * that any operation that introduces a new scope (like a "let" or "with"
2040     * block) increases the stack depth. This way, it is possible to restore
2041     * the scope chain based on stack depth of the handler alone. "let" with
2042     * an empty destructuring pattern like in
2043     *
2044     * let [] = 1;
2045     *
2046     * would violate this assumption as the there would be no let locals to
2047     * store on the stack. To satisfy it we add an empty property to such
2048     * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
2049     * slots, would be always positive.
2050     *
2051     * Note that we add such a property even if the block has locals due to
2052     * later let declarations in it. We optimize for code simplicity here,
2053     * not the fastest runtime performance with empty [] or {}.
2054     */
2055     if (data &&
2056     data->binder == BindLet &&
2057     OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
2058     ok = js_DefineNativeProperty(cx, tc->blockChain,
2059     ATOM_TO_JSID(cx->runtime->
2060     atomState.emptyAtom),
2061     JSVAL_VOID, NULL, NULL,
2062     JSPROP_ENUMERATE |
2063     JSPROP_PERMANENT |
2064     JSPROP_SHARED,
2065     SPROP_HAS_SHORTID, 0, NULL);
2066     if (!ok)
2067     goto out;
2068     }
2069    
2070     ok = JS_TRUE;
2071    
2072     out:
2073     if (fpvd.table.ops)
2074     JS_DHashTableFinish(&fpvd.table);
2075     return ok;
2076    
2077     no_var_name:
2078     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR,
2079     JSMSG_NO_VARIABLE_NAME);
2080     ok = JS_FALSE;
2081     goto out;
2082     }
2083    
2084     static JSParseNode *
2085     DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
2086     JSTokenType tt)
2087     {
2088     JSParseNode *pn;
2089    
2090     pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE);
2091     if (!pn)
2092     return NULL;
2093     if (!CheckDestructuring(cx, data, pn, NULL, tc))
2094     return NULL;
2095     return pn;
2096     }
2097    
2098     #endif /* JS_HAS_DESTRUCTURING */
2099    
2100     extern const char js_with_statement_str[];
2101    
2102     static JSParseNode *
2103     ContainsStmt(JSParseNode *pn, JSTokenType tt)
2104     {
2105     JSParseNode *pn2, *pnt;
2106    
2107     if (!pn)
2108     return NULL;
2109     if (pn->pn_type == tt)
2110     return pn;
2111     switch (pn->pn_arity) {
2112     case PN_LIST:
2113     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2114     pnt = ContainsStmt(pn2, tt);
2115     if (pnt)
2116     return pnt;
2117     }
2118     break;
2119     case PN_TERNARY:
2120     pnt = ContainsStmt(pn->pn_kid1, tt);
2121     if (pnt)
2122     return pnt;
2123     pnt = ContainsStmt(pn->pn_kid2, tt);
2124     if (pnt)
2125     return pnt;
2126     return ContainsStmt(pn->pn_kid3, tt);
2127     case PN_BINARY:
2128     /*
2129     * Limit recursion if pn is a binary expression, which can't contain a
2130     * var statement.
2131     */
2132     if (pn->pn_op != JSOP_NOP)
2133     return NULL;
2134     pnt = ContainsStmt(pn->pn_left, tt);
2135     if (pnt)
2136     return pnt;
2137     return ContainsStmt(pn->pn_right, tt);
2138     case PN_UNARY:
2139     if (pn->pn_op != JSOP_NOP)
2140     return NULL;
2141     return ContainsStmt(pn->pn_kid, tt);
2142     case PN_NAME:
2143     return ContainsStmt(pn->pn_expr, tt);
2144     default:;
2145     }
2146     return NULL;
2147     }
2148    
2149     static JSParseNode *
2150     ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2151     JSParser operandParser)
2152     {
2153     JSTokenType tt, tt2;
2154     JSParseNode *pn, *pn2;
2155    
2156     tt = CURRENT_TOKEN(ts).type;
2157     if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
2158     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2159     JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
2160     return NULL;
2161     }
2162    
2163     pn = NewParseNode(cx, ts, PN_UNARY, tc);
2164     if (!pn)
2165     return NULL;
2166    
2167     #if JS_HAS_GENERATORS
2168     if (tt == TOK_YIELD)
2169     tc->flags |= TCF_FUN_IS_GENERATOR;
2170     #endif
2171    
2172     /* This is ugly, but we don't want to require a semicolon. */
2173     ts->flags |= TSF_OPERAND;
2174     tt2 = js_PeekTokenSameLine(cx, ts);
2175     ts->flags &= ~TSF_OPERAND;
2176     if (tt2 == TOK_ERROR)
2177     return NULL;
2178    
2179     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
2180     #if JS_HAS_GENERATORS
2181     && (tt != TOK_YIELD ||
2182     (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
2183     tt2 != TOK_COLON && tt2 != TOK_COMMA))
2184     #endif
2185     ) {
2186     pn2 = operandParser(cx, ts, tc);
2187     if (!pn2)
2188     return NULL;
2189     #if JS_HAS_GENERATORS
2190     if (tt == TOK_RETURN)
2191     #endif
2192     tc->flags |= TCF_RETURN_EXPR;
2193     pn->pn_pos.end = pn2->pn_pos.end;
2194     pn->pn_kid = pn2;
2195     } else {
2196     #if JS_HAS_GENERATORS
2197     if (tt == TOK_RETURN)
2198     #endif
2199     tc->flags |= TCF_RETURN_VOID;
2200     }
2201    
2202     if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
2203     /* As in Python (see PEP-255), disallow return v; in generators. */
2204     ReportBadReturn(cx, tc, JSREPORT_ERROR,
2205     JSMSG_BAD_GENERATOR_RETURN,
2206     JSMSG_BAD_ANON_GENERATOR_RETURN);
2207     return NULL;
2208     }
2209    
2210     if (JS_HAS_STRICT_OPTION(cx) &&
2211     (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
2212     !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
2213     JSMSG_NO_RETURN_VALUE,
2214     JSMSG_ANON_NO_RETURN_VALUE)) {
2215     return NULL;
2216     }
2217    
2218     return pn;
2219     }
2220    
2221     static JSParseNode *
2222     PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2223     JSStmtInfo *stmtInfo)
2224     {
2225     JSParseNode *pn;
2226     JSObject *obj;
2227     JSParsedObjectBox *blockpob;
2228    
2229     pn = NewParseNode(cx, ts, PN_NAME, tc);
2230     if (!pn)
2231     return NULL;
2232    
2233     obj = js_NewBlockObject(cx);
2234     if (!obj)
2235     return NULL;
2236    
2237     blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj);
2238     if (!blockpob)
2239     return NULL;
2240    
2241     js_PushBlockScope(tc, stmtInfo, obj, -1);
2242     pn->pn_type = TOK_LEXICALSCOPE;
2243     pn->pn_op = JSOP_LEAVEBLOCK;
2244     pn->pn_pob = blockpob;
2245     pn->pn_slot = -1;
2246     return pn;
2247     }
2248    
2249     #if JS_HAS_BLOCK_SCOPE
2250    
2251     static JSParseNode *
2252     LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
2253     {
2254     JSParseNode *pn, *pnblock, *pnlet;
2255     JSStmtInfo stmtInfo;
2256    
2257     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
2258    
2259     /* Create the let binary node. */
2260     pnlet = NewParseNode(cx, ts, PN_BINARY, tc);
2261     if (!pnlet)
2262     return NULL;
2263    
2264     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
2265    
2266     /* This is a let block or expression of the form: let (a, b, c) .... */
2267     pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
2268     if (!pnblock)
2269     return NULL;
2270     pn = pnblock;
2271     pn->pn_expr = pnlet;
2272    
2273     pnlet->pn_left = Variables(cx, ts, tc);
2274     if (!pnlet->pn_left)
2275     return NULL;
2276     pnlet->pn_left->pn_extra = PNX_POPVAR;
2277    
2278     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
2279    
2280     ts->flags |= TSF_OPERAND;
2281     if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
2282     /*
2283     * If this is really an expression in let statement guise, then we
2284     * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2285     * the return value of the expression.
2286     */
2287     pn = NewParseNode(cx, ts, PN_UNARY, tc);
2288     if (!pn)
2289     return NULL;
2290     pn->pn_type = TOK_SEMI;
2291     pn->pn_num = -1;
2292     pn->pn_kid = pnblock;
2293    
2294     statement = JS_FALSE;
2295     }
2296     ts->flags &= ~TSF_OPERAND;
2297    
2298     if (statement) {
2299     pnlet->pn_right = Statements(cx, ts, tc);
2300     if (!pnlet->pn_right)
2301     return NULL;
2302     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2303     } else {
2304     /*
2305     * Change pnblock's opcode to the variant that propagates the last
2306     * result down after popping the block, and clear statement.
2307     */
2308     pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
2309     pnlet->pn_right = AssignExpr(cx, ts, tc);
2310     if (!pnlet->pn_right)
2311     return NULL;
2312     }
2313    
2314     js_PopStatement(tc);
2315     return pn;
2316     }
2317    
2318     #endif /* JS_HAS_BLOCK_SCOPE */
2319    
2320     static JSParseNode *
2321     Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2322     {
2323     JSTokenType tt;
2324     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
2325     JSStmtInfo stmtInfo, *stmt, *stmt2;
2326     JSAtom *label;
2327    
2328     JS_CHECK_RECURSION(cx, return NULL);
2329    
2330     ts->flags |= TSF_OPERAND;
2331     tt = js_GetToken(cx, ts);
2332     ts->flags &= ~TSF_OPERAND;
2333    
2334     #if JS_HAS_GETTER_SETTER
2335     if (tt == TOK_NAME) {
2336     tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2337     if (tt == TOK_ERROR)
2338     return NULL;
2339     }
2340     #endif
2341    
2342     switch (tt) {
2343     case TOK_FUNCTION:
2344     #if JS_HAS_XML_SUPPORT
2345     ts->flags |= TSF_KEYWORD_IS_NAME;
2346     tt = js_PeekToken(cx, ts);
2347     ts->flags &= ~TSF_KEYWORD_IS_NAME;
2348     if (tt == TOK_DBLCOLON)
2349     goto expression;
2350     #endif
2351     return FunctionStmt(cx, ts, tc);
2352    
2353     case TOK_IF:
2354     /* An IF node has three kids: condition, then, and optional else. */
2355     pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2356     if (!pn)
2357     return NULL;
2358     pn1 = Condition(cx, ts, tc);
2359     if (!pn1)
2360     return NULL;
2361     js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
2362     pn2 = Statement(cx, ts, tc);
2363     if (!pn2)
2364     return NULL;
2365     ts->flags |= TSF_OPERAND;
2366     if (js_MatchToken(cx, ts, TOK_ELSE)) {
2367     ts->flags &= ~TSF_OPERAND;
2368     stmtInfo.type = STMT_ELSE;
2369     pn3 = Statement(cx, ts, tc);
2370     if (!pn3)
2371     return NULL;
2372     pn->pn_pos.end = pn3->pn_pos.end;
2373     } else {
2374     ts->flags &= ~TSF_OPERAND;
2375     pn3 = NULL;
2376     pn->pn_pos.end = pn2->pn_pos.end;
2377     }
2378     js_PopStatement(tc);
2379     pn->pn_kid1 = pn1;
2380     pn->pn_kid2 = pn2;
2381     pn->pn_kid3 = pn3;
2382     return pn;
2383    
2384     case TOK_SWITCH:
2385     {
2386     JSParseNode *pn5, *saveBlock;
2387     JSBool seenDefault = JS_FALSE;
2388    
2389     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2390     if (!pn)
2391     return NULL;
2392     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
2393    
2394     /* pn1 points to the switch's discriminant. */
2395     pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
2396     if (!pn1)
2397     return NULL;
2398    
2399     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
2400     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
2401    
2402     /* pn2 is a list of case nodes. The default case has pn_left == NULL */
2403     pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2404     if (!pn2)
2405     return NULL;
2406     saveBlock = tc->blockNode;
2407     tc->blockNode = pn2;
2408     PN_INIT_LIST(pn2);
2409    
2410     js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
2411    
2412     while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
2413     switch (tt) {
2414     case TOK_DEFAULT:
2415     if (seenDefault) {
2416     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2417     JSMSG_TOO_MANY_DEFAULTS);
2418     return NULL;
2419     }
2420     seenDefault = JS_TRUE;
2421     /* FALL THROUGH */
2422    
2423     case TOK_CASE:
2424     pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
2425     if (!pn3)
2426     return NULL;
2427     if (tt == TOK_CASE) {
2428     pn3->pn_left = Expr(cx, ts, tc);
2429     if (!pn3->pn_left)
2430     return NULL;
2431     }
2432     PN_APPEND(pn2, pn3);
2433     if (pn2->pn_count == JS_BIT(16)) {
2434     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2435     JSMSG_TOO_MANY_CASES);
2436     return NULL;
2437     }
2438     break;
2439    
2440     case TOK_ERROR:
2441     return NULL;
2442    
2443     default:
2444     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2445     JSMSG_BAD_SWITCH);
2446     return NULL;
2447     }
2448     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
2449    
2450     pn4 = NewParseNode(cx, ts, PN_LIST, tc);
2451     if (!pn4)
2452     return NULL;
2453     pn4->pn_type = TOK_LC;
2454     PN_INIT_LIST(pn4);
2455     ts->flags |= TSF_OPERAND;
2456     while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
2457     tt != TOK_CASE && tt != TOK_DEFAULT) {
2458     ts->flags &= ~TSF_OPERAND;
2459     if (tt == TOK_ERROR)
2460     return NULL;
2461     pn5 = Statement(cx, ts, tc);
2462     if (!pn5)
2463     return NULL;
2464     pn4->pn_pos.end = pn5->pn_pos.end;
2465     PN_APPEND(pn4, pn5);
2466     ts->flags |= TSF_OPERAND;
2467     }
2468     ts->flags &= ~TSF_OPERAND;
2469    
2470     /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
2471     if (pn4->pn_head)
2472     pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
2473     pn3->pn_pos.end = pn4->pn_pos.end;
2474     pn3->pn_right = pn4;
2475     }
2476    
2477     /*
2478     * Handle the case where there was a let declaration in any case in
2479     * the switch body, but not within an inner block. If it replaced
2480     * tc->blockNode with a new block node then we must refresh pn2 and
2481     * then restore tc->blockNode.
2482     */
2483     if (tc->blockNode != pn2)
2484     pn2 = tc->blockNode;
2485     tc->blockNode = saveBlock;
2486     js_PopStatement(tc);
2487    
2488     pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2489     pn->pn_left = pn1;
2490     pn->pn_right = pn2;
2491     return pn;
2492     }
2493    
2494     case TOK_WHILE:
2495     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2496     if (!pn)
2497     return NULL;
2498     js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2499     pn2 = Condition(cx, ts, tc);
2500     if (!pn2)
2501     return NULL;
2502     pn->pn_left = pn2;
2503     pn2 = Statement(cx, ts, tc);
2504     if (!pn2)
2505     return NULL;
2506     js_PopStatement(tc);
2507     pn->pn_pos.end = pn2->pn_pos.end;
2508     pn->pn_right = pn2;
2509     return pn;
2510    
2511     case TOK_DO:
2512     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2513     if (!pn)
2514     return NULL;
2515     js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2516     pn2 = Statement(cx, ts, tc);
2517     if (!pn2)
2518     return NULL;
2519     pn->pn_left = pn2;
2520     MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2521     pn2 = Condition(cx, ts, tc);
2522     if (!pn2)
2523     return NULL;
2524     js_PopStatement(tc);
2525     pn->pn_pos.end = pn2->pn_pos.end;
2526     pn->pn_right = pn2;
2527     if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
2528     /*
2529     * All legacy and extended versions must do automatic semicolon
2530     * insertion after do-while. See the testcase and discussion in
2531     * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2532     */
2533     (void) js_MatchToken(cx, ts, TOK_SEMI);
2534     return pn;
2535     }
2536     break;
2537    
2538     case TOK_FOR:
2539     {
2540     #if JS_HAS_BLOCK_SCOPE
2541     JSParseNode *pnlet;
2542     JSStmtInfo blockInfo;
2543    
2544     pnlet = NULL;
2545     #endif
2546    
2547     /* A FOR node is binary, left is loop control and right is the body. */
2548     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2549     if (!pn)
2550     return NULL;
2551     js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2552    
2553     pn->pn_op = JSOP_ITER;
2554     pn->pn_iflags = 0;
2555     if (js_MatchToken(cx, ts, TOK_NAME)) {
2556     if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
2557     pn->pn_iflags = JSITER_FOREACH;
2558     else
2559     js_UngetToken(ts);
2560     }
2561    
2562     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
2563     ts->flags |= TSF_OPERAND;
2564     tt = js_PeekToken(cx, ts);
2565     ts->flags &= ~TSF_OPERAND;
2566     if (tt == TOK_SEMI) {
2567     if (pn->pn_iflags & JSITER_FOREACH)
2568     goto bad_for_each;
2569    
2570     /* No initializer -- set first kid of left sub-node to null. */
2571     pn1 = NULL;
2572     } else {
2573     /*
2574     * Set pn1 to a var list or an initializing expression.
2575     *
2576     * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2577     * of the for statement. This flag will be used by the RelExpr
2578     * production; if it is set, then the 'in' keyword will not be
2579     * recognized as an operator, leaving it available to be parsed as
2580     * part of a for/in loop.
2581     *
2582