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

Annotation of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (13 years, 6 months ago) by siliconforks
File size: 221813 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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 siliconforks 399 if ((tcflags & TCF_COMPILE_N_GO) && callerFrame && callerFrame->fun) {
547     /*
548     * An eval script in a caller frame needs to have its enclosing function
549     * captured in case it uses an upvar reference, and someone wishes to
550     * decompile it while running.
551     */
552     JSParsedObjectBox *pob = js_NewParsedObjectBox(cx, &pc, callerFrame->callee);
553     pob->emitLink = cg.objectList.lastPob;
554     cg.objectList.lastPob = pob;
555     cg.objectList.length++;
556     }
557    
558 siliconforks 332 /* Inline Statements() to emit as we go to save space. */
559     for (;;) {
560     pc.tokenStream.flags |= TSF_OPERAND;
561     tt = js_PeekToken(cx, &pc.tokenStream);
562     pc.tokenStream.flags &= ~TSF_OPERAND;
563     if (tt <= TOK_EOF) {
564     if (tt == TOK_EOF)
565     break;
566     JS_ASSERT(tt == TOK_ERROR);
567     script = NULL;
568     goto out;
569     }
570    
571     pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
572     if (!pn) {
573     script = NULL;
574     goto out;
575     }
576     JS_ASSERT(!cg.treeContext.blockNode);
577    
578     if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
579     !js_EmitTree(cx, &cg, pn)) {
580     script = NULL;
581     goto out;
582     }
583     RecycleTree(pn, &cg.treeContext);
584     }
585    
586     /*
587     * Global variables and regexps shares the index space with locals. Due to
588     * incremental code generation we need to patch the bytecode to adjust the
589     * local references to skip the globals.
590     */
591     scriptGlobals = cg.treeContext.ngvars + cg.regexpList.length;
592     if (scriptGlobals != 0) {
593     jsbytecode *code, *end;
594     JSOp op;
595     const JSCodeSpec *cs;
596     uintN len, slot;
597    
598     if (scriptGlobals >= SLOTNO_LIMIT)
599     goto too_many_slots;
600     code = CG_BASE(&cg);
601     for (end = code + CG_OFFSET(&cg); code != end; code += len) {
602     JS_ASSERT(code < end);
603     op = (JSOp) *code;
604     cs = &js_CodeSpec[op];
605     len = (cs->length > 0)
606     ? (uintN) cs->length
607     : js_GetVariableBytecodeLength(code);
608     if (JOF_TYPE(cs->format) == JOF_LOCAL ||
609     (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
610     /*
611     * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
612     * emitted only for a function.
613     */
614     JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
615     (op == JSOP_GETLOCALPROP));
616     slot = GET_SLOTNO(code);
617     slot += scriptGlobals;
618     if (slot >= SLOTNO_LIMIT)
619     goto too_many_slots;
620     SET_SLOTNO(code, slot);
621     }
622     }
623     }
624    
625     #ifdef METER_PARSENODES
626     printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
627     (char *)sbrk(0) - (char *)before,
628     parsenodes,
629     maxparsenodes,
630     parsenodes - recyclednodes);
631     before = sbrk(0);
632     #endif
633    
634     /*
635     * Nowadays the threaded interpreter needs a stop instruction, so we
636     * do have to emit that here.
637     */
638     if (js_Emit1(cx, &cg, JSOP_STOP) < 0) {
639     script = NULL;
640     goto out;
641     }
642     #ifdef METER_PARSENODES
643     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
644     (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
645     #endif
646     #ifdef JS_ARENAMETER
647     JS_DumpArenaStats(stdout);
648     #endif
649     script = js_NewScriptFromCG(cx, &cg);
650    
651     #ifdef JS_SCOPE_DEPTH_METER
652     if (script) {
653     JSObject *obj = scopeChain;
654     uintN depth = 1;
655     while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
656     ++depth;
657     JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
658     }
659     #endif
660    
661     out:
662     js_FinishCodeGenerator(cx, &cg);
663     JS_FinishArenaPool(&codePool);
664     JS_FinishArenaPool(&notePool);
665     js_FinishParseContext(cx, &pc);
666     return script;
667    
668     too_many_slots:
669     js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
670     JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
671     script = NULL;
672     goto out;
673     }
674    
675     /*
676     * Insist on a final return before control flows out of pn. Try to be a bit
677     * smart about loops: do {...; return e2;} while(0) at the end of a function
678     * that contains an early return e1 will get a strict warning. Similarly for
679     * iloops: while (true){...} is treated as though ... returns.
680     */
681     #define ENDS_IN_OTHER 0
682     #define ENDS_IN_RETURN 1
683     #define ENDS_IN_BREAK 2
684    
685     static int
686     HasFinalReturn(JSParseNode *pn)
687     {
688     JSParseNode *pn2, *pn3;
689     uintN rv, rv2, hasDefault;
690    
691     switch (pn->pn_type) {
692     case TOK_LC:
693     if (!pn->pn_head)
694     return ENDS_IN_OTHER;
695     return HasFinalReturn(PN_LAST(pn));
696    
697     case TOK_IF:
698     if (!pn->pn_kid3)
699     return ENDS_IN_OTHER;
700     return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
701    
702     case TOK_WHILE:
703     pn2 = pn->pn_left;
704     if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
705     return ENDS_IN_RETURN;
706     if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
707     return ENDS_IN_RETURN;
708     return ENDS_IN_OTHER;
709    
710     case TOK_DO:
711     pn2 = pn->pn_right;
712     if (pn2->pn_type == TOK_PRIMARY) {
713     if (pn2->pn_op == JSOP_FALSE)
714     return HasFinalReturn(pn->pn_left);
715     if (pn2->pn_op == JSOP_TRUE)
716     return ENDS_IN_RETURN;
717     }
718     if (pn2->pn_type == TOK_NUMBER) {
719     if (pn2->pn_dval == 0)
720     return HasFinalReturn(pn->pn_left);
721     return ENDS_IN_RETURN;
722     }
723     return ENDS_IN_OTHER;
724    
725     case TOK_FOR:
726     pn2 = pn->pn_left;
727     if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
728     return ENDS_IN_RETURN;
729     return ENDS_IN_OTHER;
730    
731     case TOK_SWITCH:
732     rv = ENDS_IN_RETURN;
733     hasDefault = ENDS_IN_OTHER;
734     pn2 = pn->pn_right;
735     if (pn2->pn_type == TOK_LEXICALSCOPE)
736     pn2 = pn2->pn_expr;
737     for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
738     if (pn2->pn_type == TOK_DEFAULT)
739     hasDefault = ENDS_IN_RETURN;
740     pn3 = pn2->pn_right;
741     JS_ASSERT(pn3->pn_type == TOK_LC);
742     if (pn3->pn_head) {
743     rv2 = HasFinalReturn(PN_LAST(pn3));
744     if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
745     /* Falling through to next case or default. */;
746     else
747     rv &= rv2;
748     }
749     }
750     /* If a final switch has no default case, we judge it harshly. */
751     rv &= hasDefault;
752     return rv;
753    
754     case TOK_BREAK:
755     return ENDS_IN_BREAK;
756    
757     case TOK_WITH:
758     return HasFinalReturn(pn->pn_right);
759    
760     case TOK_RETURN:
761     return ENDS_IN_RETURN;
762    
763     case TOK_COLON:
764     case TOK_LEXICALSCOPE:
765     return HasFinalReturn(pn->pn_expr);
766    
767     case TOK_THROW:
768     return ENDS_IN_RETURN;
769    
770     case TOK_TRY:
771     /* If we have a finally block that returns, we are done. */
772     if (pn->pn_kid3) {
773     rv = HasFinalReturn(pn->pn_kid3);
774     if (rv == ENDS_IN_RETURN)
775     return rv;
776     }
777    
778     /* Else check the try block and any and all catch statements. */
779     rv = HasFinalReturn(pn->pn_kid1);
780     if (pn->pn_kid2) {
781     JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
782     for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
783     rv &= HasFinalReturn(pn2);
784     }
785     return rv;
786    
787     case TOK_CATCH:
788     /* Check this catch block's body. */
789     return HasFinalReturn(pn->pn_kid3);
790    
791     case TOK_LET:
792     /* Non-binary let statements are let declarations. */
793     if (pn->pn_arity != PN_BINARY)
794     return ENDS_IN_OTHER;
795     return HasFinalReturn(pn->pn_right);
796    
797     default:
798     return ENDS_IN_OTHER;
799     }
800     }
801    
802     static JSBool
803     ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
804     uintN anonerrnum)
805     {
806     const char *name;
807    
808     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
809     if (tc->u.fun->atom) {
810     name = js_AtomToPrintableString(cx, tc->u.fun->atom);
811     } else {
812     errnum = anonerrnum;
813     name = NULL;
814     }
815     return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
816     errnum, name);
817     }
818    
819     static JSBool
820     CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
821     {
822     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
823     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
824     ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
825     JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
826     }
827    
828     static JSParseNode *
829     FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
830     {
831     JSStmtInfo stmtInfo;
832     uintN oldflags, firstLine;
833     JSParseNode *pn;
834    
835     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
836     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
837     stmtInfo.flags = SIF_BODY_BLOCK;
838    
839     oldflags = tc->flags;
840     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
841    
842     /*
843     * Save the body's first line, and store it in pn->pn_pos.begin.lineno
844     * later, because we may have not peeked in ts yet, so Statements won't
845     * acquire a valid pn->pn_pos.begin from the current token.
846     */
847     firstLine = ts->lineno;
848     #if JS_HAS_EXPR_CLOSURES
849     if (CURRENT_TOKEN(ts).type == TOK_LC) {
850     pn = Statements(cx, ts, tc);
851     } else {
852     pn = NewParseNode(cx, ts, PN_UNARY, tc);
853     if (pn) {
854     pn->pn_kid = AssignExpr(cx, ts, tc);
855     if (!pn->pn_kid) {
856     pn = NULL;
857     } else {
858     if (tc->flags & TCF_FUN_IS_GENERATOR) {
859     ReportBadReturn(cx, tc, JSREPORT_ERROR,
860     JSMSG_BAD_GENERATOR_RETURN,
861     JSMSG_BAD_ANON_GENERATOR_RETURN);
862     pn = NULL;
863     } else {
864     pn->pn_type = TOK_RETURN;
865     pn->pn_op = JSOP_RETURN;
866     pn->pn_pos.end = pn->pn_kid->pn_pos.end;
867     }
868     }
869     }
870     }
871     #else
872     pn = Statements(cx, ts, tc);
873     #endif
874    
875     if (pn) {
876     js_PopStatement(tc);
877     pn->pn_pos.begin.lineno = firstLine;
878    
879     /* Check for falling off the end of a function that returns a value. */
880     if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
881     !CheckFinalReturn(cx, tc, pn)) {
882     pn = NULL;
883     }
884     }
885    
886     tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
887     return pn;
888     }
889    
890     /*
891     * Compile a JS function body, which might appear as the value of an event
892     * handler attribute in an HTML <INPUT> tag.
893     */
894     JSBool
895     js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
896     const jschar *chars, size_t length,
897     const char *filename, uintN lineno)
898     {
899     JSParseContext pc;
900     JSArenaPool codePool, notePool;
901     JSCodeGenerator funcg;
902     JSParseNode *pn;
903    
904     if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
905     filename, lineno)) {
906     return JS_FALSE;
907     }
908    
909     /* No early return from this point until js_FinishParseContext call. */
910     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
911     &cx->scriptStackQuota);
912     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
913     &cx->scriptStackQuota);
914     js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
915     pc.tokenStream.lineno);
916     funcg.treeContext.flags |= TCF_IN_FUNCTION;
917     funcg.treeContext.u.fun = fun;
918    
919     /*
920     * Farble the body so that it looks like a block statement to js_EmitTree,
921     * which is called beneath FunctionBody; see Statements, further below in
922     * this file. FunctionBody pushes a STMT_BLOCK record around its call to
923     * Statements, so Statements will not compile each statement as it loops
924     * to save JSParseNode space -- it will not compile at all, only build a
925     * JSParseNode tree.
926     *
927     * Therefore we must fold constants, allocate try notes, and generate code
928     * for this function, including a stop opcode at the end.
929     */
930     CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC;
931     pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext);
932     if (pn) {
933     if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) {
934     js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
935     JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
936     pn = NULL;
937     } else {
938     if (!js_FoldConstants(cx, pn, &funcg.treeContext) ||
939     !js_EmitFunctionScript(cx, &funcg, pn)) {
940     pn = NULL;
941     }
942     }
943     }
944    
945     /* Restore saved state and release code generation arenas. */
946     js_FinishCodeGenerator(cx, &funcg);
947     JS_FinishArenaPool(&codePool);
948     JS_FinishArenaPool(&notePool);
949     js_FinishParseContext(cx, &pc);
950     return pn != NULL;
951     }
952    
953     /*
954     * Parameter block types for the several Binder functions. We use a common
955     * helper function signature in order to share code among destructuring and
956     * simple variable declaration parsers. In the destructuring case, the binder
957     * function is called indirectly from the variable declaration parser by way
958     * of CheckDestructuring and its friends.
959     */
960     typedef struct BindData BindData;
961    
962     typedef JSBool
963     (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
964    
965     struct BindData {
966     JSParseNode *pn; /* error source coordinate */
967     JSOp op; /* prolog bytecode or nop */
968     Binder binder; /* binder, discriminates u */
969     union {
970     struct {
971     uintN overflow;
972     } let;
973     } u;
974     };
975    
976     static JSBool
977     BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
978     {
979     const char *name;
980    
981     /*
982     * Check for a duplicate parameter name, a "feature" required by ECMA-262.
983     */
984     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
985     if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
986     name = js_AtomToPrintableString(cx, atom);
987     if (!name ||
988     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
989     JSREPORT_WARNING | JSREPORT_STRICT,
990     JSMSG_DUPLICATE_FORMAL,
991     name)) {
992     return JS_FALSE;
993     }
994     }
995    
996     return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
997     }
998    
999     static JSBool
1000     BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1001     JSLocalKind localKind)
1002     {
1003     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1004    
1005     /*
1006     * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1007     * Instead 'var arguments' always restates the predefined property of the
1008     * activation objects with unhidden name 'arguments'. Assignment to such
1009     * a variable must be handled specially.
1010     */
1011     if (atom == cx->runtime->atomState.argumentsAtom)
1012     return JS_TRUE;
1013    
1014     return js_AddLocal(cx, fun, atom, localKind);
1015     }
1016    
1017     #if JS_HAS_DESTRUCTURING
1018     /*
1019     * Forward declaration to maintain top-down presentation.
1020     */
1021     static JSParseNode *
1022     DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1023     JSTokenType tt);
1024    
1025     static JSBool
1026     BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1027     JSTreeContext *tc)
1028     {
1029     JSAtomListElement *ale;
1030     const char *name;
1031    
1032     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1033     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1034     if (!ale) {
1035     ale = js_IndexAtom(cx, atom, &tc->decls);
1036     if (!ale)
1037     return JS_FALSE;
1038     ALE_SET_JSOP(ale, data->op);
1039     }
1040    
1041     if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
1042     name = js_AtomToPrintableString(cx, atom);
1043     if (!name ||
1044     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1045     JSREPORT_WARNING | JSREPORT_STRICT,
1046     JSMSG_DUPLICATE_FORMAL,
1047     name)) {
1048     return JS_FALSE;
1049     }
1050     } else {
1051     if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
1052     return JS_FALSE;
1053     }
1054     return JS_TRUE;
1055     }
1056     #endif /* JS_HAS_DESTRUCTURING */
1057    
1058     static JSFunction *
1059     NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
1060     uintN lambda)
1061     {
1062     JSObject *parent;
1063     JSFunction *fun;
1064    
1065     JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1066     parent = (tc->flags & TCF_IN_FUNCTION)
1067     ? FUN_OBJECT(tc->u.fun)
1068     : tc->u.scopeChain;
1069     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1070     parent, atom);
1071     if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1072     STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1073     STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1074     }
1075     return fun;
1076     }
1077    
1078     static JSParseNode *
1079     FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1080     uintN lambda)
1081     {
1082     JSOp op, prevop;
1083     JSParseNode *pn, *body, *result;
1084     JSTokenType tt;
1085     JSAtom *funAtom;
1086     JSParsedObjectBox *funpob;
1087     JSAtomListElement *ale;
1088     JSFunction *fun;
1089     JSTreeContext funtc;
1090     #if JS_HAS_DESTRUCTURING
1091     JSParseNode *item, *list = NULL;
1092     #endif
1093    
1094     /* Make a TOK_FUNCTION node. */
1095     #if JS_HAS_GETTER_SETTER
1096     op = CURRENT_TOKEN(ts).t_op;
1097     #endif
1098     pn = NewParseNode(cx, ts, PN_FUNC, tc);
1099     if (!pn)
1100     return NULL;
1101     #ifdef DEBUG
1102     pn->pn_index = (uint32) -1;
1103     #endif
1104    
1105     /* Scan the optional function name into funAtom. */
1106     ts->flags |= TSF_KEYWORD_IS_NAME;
1107     tt = js_GetToken(cx, ts);
1108     ts->flags &= ~TSF_KEYWORD_IS_NAME;
1109     if (tt == TOK_NAME) {
1110     funAtom = CURRENT_TOKEN(ts).t_atom;
1111     } else {
1112     if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
1113     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1114     JSMSG_SYNTAX_ERROR);
1115     return NULL;
1116     }
1117     funAtom = NULL;
1118     js_UngetToken(ts);
1119     }
1120    
1121     /*
1122     * Record names for function statements in tc->decls so we know when to
1123     * avoid optimizing variable references that might name a function.
1124     */
1125     if (lambda == 0 && funAtom) {
1126     ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1127     if (ale) {
1128     prevop = ALE_JSOP(ale);
1129     if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1130     const char *name = js_AtomToPrintableString(cx, funAtom);
1131     if (!name ||
1132     !js_ReportCompileErrorNumber(cx, ts, NULL,
1133     (prevop != JSOP_DEFCONST)
1134     ? JSREPORT_WARNING |
1135     JSREPORT_STRICT
1136     : JSREPORT_ERROR,
1137     JSMSG_REDECLARED_VAR,
1138     (prevop == JSOP_DEFFUN)
1139     ? js_function_str
1140     : (prevop == JSOP_DEFCONST)
1141     ? js_const_str
1142     : js_var_str,
1143     name)) {
1144     return NULL;
1145     }
1146     }
1147     if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1148     tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1149     } else {
1150     ale = js_IndexAtom(cx, funAtom, &tc->decls);
1151     if (!ale)
1152     return NULL;
1153     }
1154     ALE_SET_JSOP(ale, JSOP_DEFFUN);
1155    
1156     /*
1157     * A function nested at top level inside another's body needs only a
1158     * local variable to bind its name to its value, and not an activation
1159     * object property (it might also need the activation property, if the
1160     * outer function contains with statements, e.g., but the stack slot
1161     * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1162     * JSOP_GETLOCAL bytecode).
1163     */
1164     if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
1165     JSLocalKind localKind;
1166    
1167     /*
1168     * Define a property on the outer function so that BindNameToSlot
1169     * can properly optimize accesses. Note that we need a variable,
1170     * not an argument, for the function statement. Thus we add a
1171     * variable even if the parameter with the given name already
1172     * exists.
1173     */
1174     localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
1175     if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
1176     if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
1177     return NULL;
1178     }
1179     }
1180     }
1181    
1182     fun = NewCompilerFunction(cx, tc, funAtom, lambda);
1183     if (!fun)
1184     return NULL;
1185    
1186     #if JS_HAS_GETTER_SETTER
1187     if (op != JSOP_NOP)
1188     fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1189     #endif
1190    
1191     /*
1192     * Create wrapping box for fun->object early to protect against a
1193     * last-ditch GC.
1194     */
1195     funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
1196     if (!funpob)
1197     return NULL;
1198    
1199     /* Initialize early for possible flags mutation via DestructuringExpr. */
1200     TREE_CONTEXT_INIT(&funtc, tc->parseContext);
1201     funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
1202     funtc.u.fun = fun;
1203    
1204     /* Now parse formal argument list and compute fun->nargs. */
1205     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1206     if (!js_MatchToken(cx, ts, TOK_RP)) {
1207     do {
1208     tt = js_GetToken(cx, ts);
1209     switch (tt) {
1210     #if JS_HAS_DESTRUCTURING
1211     case TOK_LB:
1212     case TOK_LC:
1213     {
1214     BindData data;
1215     JSParseNode *lhs, *rhs;
1216     jsint slot;
1217    
1218     /*
1219     * A destructuring formal parameter turns into one or more
1220     * local variables initialized from properties of a single
1221     * anonymous positional parameter, so here we must tweak our
1222     * binder and its data.
1223     */
1224     data.pn = NULL;
1225     data.op = JSOP_DEFVAR;
1226     data.binder = BindDestructuringArg;
1227     lhs = DestructuringExpr(cx, &data, &funtc, tt);
1228     if (!lhs)
1229     return NULL;
1230    
1231     /*
1232     * Adjust fun->nargs to count the single anonymous positional
1233     * parameter that is to be destructured.
1234     */
1235     slot = fun->nargs;
1236     if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
1237     return NULL;
1238    
1239     /*
1240     * Synthesize a destructuring assignment from the single
1241     * anonymous positional parameter into the destructuring
1242     * left-hand-side expression and accumulate it in list.
1243     */
1244     rhs = NewParseNode(cx, ts, PN_NAME, tc);
1245     if (!rhs)
1246     return NULL;
1247     rhs->pn_type = TOK_NAME;
1248     rhs->pn_op = JSOP_GETARG;
1249     rhs->pn_atom = cx->runtime->atomState.emptyAtom;
1250     rhs->pn_slot = slot;
1251    
1252     item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
1253     if (!item)
1254     return NULL;
1255     if (!list) {
1256     list = NewParseNode(cx, ts, PN_LIST, tc);
1257     if (!list)
1258     return NULL;
1259     list->pn_type = TOK_COMMA;
1260     PN_INIT_LIST(list);
1261     }
1262     PN_APPEND(list, item);
1263     break;
1264     }
1265     #endif /* JS_HAS_DESTRUCTURING */
1266    
1267     case TOK_NAME:
1268     if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc))
1269     return NULL;
1270     break;
1271    
1272     default:
1273     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1274     JSMSG_MISSING_FORMAL);
1275     return NULL;
1276     }
1277     } while (js_MatchToken(cx, ts, TOK_COMMA));
1278    
1279     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1280     }
1281    
1282     #if JS_HAS_EXPR_CLOSURES
1283     ts->flags |= TSF_OPERAND;
1284     tt = js_GetToken(cx, ts);
1285     ts->flags &= ~TSF_OPERAND;
1286     if (tt != TOK_LC) {
1287     js_UngetToken(ts);
1288     fun->flags |= JSFUN_EXPR_CLOSURE;
1289     }
1290     #else
1291     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1292     #endif
1293     pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1294    
1295     body = FunctionBody(cx, ts, &funtc);
1296     if (!body)
1297     return NULL;
1298    
1299     #if JS_HAS_EXPR_CLOSURES
1300     if (tt == TOK_LC)
1301     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1302     else if (lambda == 0)
1303     js_MatchToken(cx, ts, TOK_SEMI);
1304     #else
1305     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1306     #endif
1307     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1308    
1309     #if JS_HAS_DESTRUCTURING
1310     /*
1311     * If there were destructuring formal parameters, prepend the initializing
1312     * comma expression that we synthesized to body. If the body is a lexical
1313 siliconforks 399 * scope node, we must make a special TOK_SEQ node, to prepend the formal
1314 siliconforks 332 * parameter destructuring code without bracing the decompilation of the
1315     * function body's lexical scope.
1316     */
1317     if (list) {
1318     if (body->pn_arity != PN_LIST) {
1319     JSParseNode *block;
1320    
1321     block = NewParseNode(cx, ts, PN_LIST, tc);
1322     if (!block)
1323     return NULL;
1324 siliconforks 399 block->pn_type = TOK_SEQ;
1325 siliconforks 332 block->pn_pos = body->pn_pos;
1326     PN_INIT_LIST_1(block, body);
1327    
1328     body = block;
1329     }
1330    
1331     item = NewParseNode(cx, ts, PN_UNARY, tc);
1332     if (!item)
1333     return NULL;
1334    
1335     item->pn_type = TOK_SEMI;
1336     item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1337     item->pn_kid = list;
1338     item->pn_next = body->pn_head;
1339     body->pn_head = item;
1340     if (body->pn_tail == &body->pn_head)
1341     body->pn_tail = &item->pn_next;
1342     ++body->pn_count;
1343     }
1344     #endif
1345    
1346     /*
1347     * If we collected flags that indicate nested heavyweight functions, or
1348     * this function contains heavyweight-making statements (references to
1349     * __parent__ or __proto__; use of with, or eval; and assignment to
1350     * arguments), flag the function as heavyweight (requiring a call object
1351     * per invocation).
1352     */
1353     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1354     fun->flags |= JSFUN_HEAVYWEIGHT;
1355     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1356     } else {
1357     /*
1358     * If this function is a named statement function not at top-level
1359     * (i.e. not a top-level function definiton or expression), then
1360     * our enclosing function, if any, must be heavyweight.
1361     *
1362     * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1363     * so it won't be set here. Assert that it's not. We have to check
1364     * it later, in js_EmitTree, after js_EmitFunctionScript has traversed
1365     * the function's body.
1366     */
1367     JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
1368     if (lambda == 0 && funAtom && !AT_TOP_LEVEL(tc))
1369     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1370     }
1371    
1372     result = pn;
1373     if (lambda != 0) {
1374     /*
1375     * ECMA ed. 3 standard: function expression, possibly anonymous.
1376     */
1377     op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1378     } else if (!funAtom) {
1379     /*
1380     * If this anonymous function definition is *not* embedded within a
1381     * larger expression, we treat it as an expression statement, not as
1382     * a function declaration -- and not as a syntax error (as ECMA-262
1383     * Edition 3 would have it). Backward compatibility must trump all,
1384     * unless JSOPTION_ANONFUNFIX is set.
1385     */
1386     result = NewParseNode(cx, ts, PN_UNARY, tc);
1387     if (!result)
1388     return NULL;
1389     result->pn_type = TOK_SEMI;
1390     result->pn_pos = pn->pn_pos;
1391     result->pn_kid = pn;
1392     op = JSOP_ANONFUNOBJ;
1393     } else if (!AT_TOP_LEVEL(tc)) {
1394     /*
1395     * ECMA ed. 3 extension: a function expression statement not at the
1396     * top level, e.g., in a compound statement such as the "then" part
1397     * of an "if" statement, binds a closure only if control reaches that
1398     * sub-statement.
1399     */
1400     op = JSOP_DEFFUN;
1401     } else {
1402     op = JSOP_NOP;
1403     }
1404    
1405     pn->pn_funpob = funpob;
1406     pn->pn_op = op;
1407     pn->pn_body = body;
1408     pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS | TCF_COMPILE_N_GO);
1409     TREE_CONTEXT_FINISH(cx, &funtc);
1410     return result;
1411     }
1412    
1413     static JSParseNode *
1414     FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1415     {
1416     return FunctionDef(cx, ts, tc, 0);
1417     }
1418    
1419     static JSParseNode *
1420     FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1421     {
1422     return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
1423     }
1424    
1425     /*
1426     * Parse the statements in a block, creating a TOK_LC node that lists the
1427     * statements' trees. If called from block-parsing code, the caller must
1428     * match { before and } after.
1429     */
1430     static JSParseNode *
1431     Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1432     {
1433     JSParseNode *pn, *pn2, *saveBlock;
1434     JSTokenType tt;
1435    
1436     JS_CHECK_RECURSION(cx, return NULL);
1437    
1438     pn = NewParseNode(cx, ts, PN_LIST, tc);
1439     if (!pn)
1440     return NULL;
1441     saveBlock = tc->blockNode;
1442     tc->blockNode = pn;
1443     PN_INIT_LIST(pn);
1444    
1445     for (;;) {
1446     ts->flags |= TSF_OPERAND;
1447     tt = js_PeekToken(cx, ts);
1448     ts->flags &= ~TSF_OPERAND;
1449     if (tt <= TOK_EOF || tt == TOK_RC) {
1450     if (tt == TOK_ERROR)
1451     return NULL;
1452     break;
1453     }
1454     pn2 = Statement(cx, ts, tc);
1455     if (!pn2) {
1456     if (ts->flags & TSF_EOF)
1457     ts->flags |= TSF_UNEXPECTED_EOF;
1458     return NULL;
1459     }
1460    
1461     if (pn2->pn_type == TOK_FUNCTION) {
1462     /*
1463     * PNX_FUNCDEFS notifies the emitter that the block contains top-
1464     * level function definitions that should be processed before the
1465     * rest of nodes.
1466     *
1467     * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
1468     * is relevant only for function definitions not at top-level,
1469     * which we call function statements.
1470     */
1471     if (AT_TOP_LEVEL(tc))
1472     pn->pn_extra |= PNX_FUNCDEFS;
1473     else
1474     tc->flags |= TCF_HAS_FUNCTION_STMT;
1475     }
1476     PN_APPEND(pn, pn2);
1477     }
1478    
1479     /*
1480     * Handle the case where there was a let declaration under this block. If
1481     * it replaced tc->blockNode with a new block node then we must refresh pn
1482     * and then restore tc->blockNode.
1483     */
1484     if (tc->blockNode != pn)
1485     pn = tc->blockNode;
1486     tc->blockNode = saveBlock;
1487    
1488     pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1489     return pn;
1490     }
1491    
1492     static JSParseNode *
1493     Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1494     {
1495     JSParseNode *pn;
1496    
1497     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1498     pn = ParenExpr(cx, ts, tc, NULL, NULL);
1499     if (!pn)
1500     return NULL;
1501     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1502    
1503     /*
1504     * Check for (a = b) and warn about possible (a == b) mistype iff b's
1505     * operator has greater precedence than ==.
1506     */
1507     if (pn->pn_type == TOK_ASSIGN &&
1508     pn->pn_op == JSOP_NOP &&
1509     pn->pn_right->pn_type > TOK_EQOP)
1510     {
1511     if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1512     JSREPORT_WARNING | JSREPORT_STRICT,
1513     JSMSG_EQUAL_AS_ASSIGN,
1514     "")) {
1515     return NULL;
1516     }
1517     }
1518     return pn;
1519     }
1520    
1521     static JSBool
1522     MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1523     {
1524     JSAtom *label;
1525     JSTokenType tt;
1526    
1527     tt = js_PeekTokenSameLine(cx, ts);
1528     if (tt == TOK_ERROR)
1529     return JS_FALSE;
1530     if (tt == TOK_NAME) {
1531     (void) js_GetToken(cx, ts);
1532     label = CURRENT_TOKEN(ts).t_atom;
1533     } else {
1534     label = NULL;
1535     }
1536     pn->pn_atom = label;
1537     return JS_TRUE;
1538     }
1539    
1540     static JSBool
1541     BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1542     {
1543     JSObject *blockObj;
1544     JSScopeProperty *sprop;
1545     JSAtomListElement *ale;
1546     uintN n;
1547    
1548     blockObj = tc->blockChain;
1549     sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
1550     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1551     if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
1552     const char *name;
1553    
1554     if (sprop) {
1555     JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1556     JS_ASSERT((uint16)sprop->shortid < OBJ_BLOCK_COUNT(cx, blockObj));
1557     }
1558    
1559     name = js_AtomToPrintableString(cx, atom);
1560     if (name) {
1561     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1562     JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
1563     (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
1564     ? js_const_str
1565     : "variable",
1566     name);
1567     }
1568     return JS_FALSE;
1569     }
1570    
1571     n = OBJ_BLOCK_COUNT(cx, blockObj);
1572     if (n == JS_BIT(16)) {
1573     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1574     JSREPORT_ERROR, data->u.let.overflow);
1575     return JS_FALSE;
1576     }
1577    
1578     /* Use JSPROP_ENUMERATE to aid the disassembler. */
1579     return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
1580     JSVAL_VOID, NULL, NULL,
1581     JSPROP_ENUMERATE |
1582     JSPROP_PERMANENT |
1583     JSPROP_SHARED,
1584     SPROP_HAS_SHORTID, (int16) n, NULL);
1585     }
1586    
1587     static JSBool
1588     BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1589     {
1590     JSStmtInfo *stmt;
1591     JSAtomListElement *ale;
1592     JSOp op, prevop;
1593     const char *name;
1594     JSLocalKind localKind;
1595    
1596     stmt = js_LexicalLookup(tc, atom, NULL);
1597     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1598     op = data->op;
1599     if ((stmt && stmt->type != STMT_WITH) || ale) {
1600     prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
1601     if (JS_HAS_STRICT_OPTION(cx)
1602     ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
1603     : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
1604     name = js_AtomToPrintableString(cx, atom);
1605     if (!name ||
1606     !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1607     (op != JSOP_DEFCONST &&
1608     prevop != JSOP_DEFCONST)
1609     ? JSREPORT_WARNING |
1610     JSREPORT_STRICT
1611     : JSREPORT_ERROR,
1612     JSMSG_REDECLARED_VAR,
1613     (prevop == JSOP_DEFFUN)
1614     ? js_function_str
1615     : (prevop == JSOP_DEFCONST)
1616     ? js_const_str
1617     : js_var_str,
1618     name)) {
1619     return JS_FALSE;
1620     }
1621     }
1622     if (op == JSOP_DEFVAR && prevop == JSOP_DEFFUN)
1623     tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1624     }
1625     if (!ale) {
1626     ale = js_IndexAtom(cx, atom, &tc->decls);
1627     if (!ale)
1628     return JS_FALSE;
1629     }
1630     ALE_SET_JSOP(ale, op);
1631    
1632     if (!(tc->flags & TCF_IN_FUNCTION)) {
1633     /*
1634     * Don't lookup global variables or variables in an active frame at
1635     * compile time.
1636     */
1637     return JS_TRUE;
1638     }
1639    
1640     localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL);
1641     if (localKind == JSLOCAL_NONE) {
1642     /*
1643     * Property not found in current variable scope: we have not seen this
1644     * variable before. Define a new local variable by adding a property
1645     * to the function's scope, allocating one slot in the function's vars
1646     * frame. Any locals declared in with statement bodies are handled at
1647     * runtime, by script prolog JSOP_DEFVAR opcodes generated for
1648     * slot-less vars.
1649     */
1650     localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
1651     if (!js_InWithStatement(tc) &&
1652     !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {
1653     return JS_FALSE;
1654     }
1655     } else if (localKind == JSLOCAL_ARG) {
1656     name = js_AtomToPrintableString(cx, atom);
1657     if (!name)
1658     return JS_FALSE;
1659    
1660     if (op == JSOP_DEFCONST) {
1661     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1662     JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
1663     name);
1664     return JS_FALSE;
1665     }
1666     if (!js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
1667     JSREPORT_WARNING | JSREPORT_STRICT,
1668     JSMSG_VAR_HIDES_ARG, name)) {
1669     return JS_FALSE;
1670     }
1671     } else {
1672     /* Not an argument, must be a redeclared local var. */
1673     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1674     }
1675     return JS_TRUE;
1676     }
1677    
1678 siliconforks 399 static JSBool
1679     MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
1680     {
1681     JSParseNode *pn2;
1682    
1683     JS_ASSERT(pn->pn_arity == PN_LIST);
1684     JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
1685     pn2 = pn->pn_head;
1686     if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_flags & TCF_GENEXP_LAMBDA)) {
1687     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
1688     JSREPORT_ERROR, msg);
1689     return JS_FALSE;
1690     }
1691     pn->pn_op = JSOP_SETCALL;
1692     return JS_TRUE;
1693     }
1694    
1695 siliconforks 332 #if JS_HAS_DESTRUCTURING
1696    
1697     static JSBool
1698     BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
1699     JSTreeContext *tc)
1700     {
1701     JSAtom *atom;
1702    
1703     /*
1704     * Destructuring is a form of assignment, so just as for an initialized
1705     * simple variable, we must check for assignment to 'arguments' and flag
1706     * the enclosing function (if any) as heavyweight.
1707     */
1708     JS_ASSERT(pn->pn_type == TOK_NAME);
1709     atom = pn->pn_atom;
1710     if (atom == cx->runtime->atomState.argumentsAtom)
1711     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1712    
1713     data->pn = pn;
1714     if (!data->binder(cx, data, atom, tc))
1715     return JS_FALSE;
1716     data->pn = NULL;
1717    
1718     /*
1719     * Select the appropriate name-setting opcode, which may be specialized
1720     * further for local variable and argument slot optimizations. At this
1721     * point, we can't select the optimal final opcode, yet we must preserve
1722     * the CONST bit and convey "set", not "get".
1723     */
1724     if (data->op == JSOP_DEFCONST) {
1725     pn->pn_op = JSOP_SETCONST;
1726     pn->pn_const = JS_TRUE;
1727     } else {
1728     pn->pn_op = JSOP_SETNAME;
1729     pn->pn_const = JS_FALSE;
1730     }
1731     return JS_TRUE;
1732     }
1733    
1734     /*
1735     * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
1736     * LHS expression except a destructuring initialiser, and R is on the stack.
1737     * Because R is already evaluated, the usual LHS-specialized bytecodes won't
1738     * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
1739     * then push its property name QN. At this point the stack looks like
1740     *
1741     * [... R, R[P], QB, QN]
1742     *
1743     * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
1744     * its operands with left-hand side above right-hand side:
1745     *
1746     * [rval, lval, xval]
1747     *
1748     * and pops all three values, setting lval[xval] = rval. But we cannot select
1749     * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
1750     * which can be optimized further. So we select JSOP_SETNAME.
1751     */
1752     static JSBool
1753     BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
1754     {
1755     while (pn->pn_type == TOK_RP)
1756     pn = pn->pn_kid;
1757    
1758     switch (pn->pn_type) {
1759     case TOK_NAME:
1760     if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
1761     tc->flags |= TCF_FUN_HEAVYWEIGHT;
1762     /* FALL THROUGH */
1763     case TOK_DOT:
1764     case TOK_LB:
1765     pn->pn_op = JSOP_SETNAME;
1766     break;
1767    
1768     #if JS_HAS_LVALUE_RETURN
1769     case TOK_LP:
1770     if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
1771     return JS_FALSE;
1772     break;
1773     #endif
1774    
1775     #if JS_HAS_XML_SUPPORT
1776     case TOK_UNARYOP:
1777     if (pn->pn_op == JSOP_XMLNAME) {
1778     pn->pn_op = JSOP_BINDXMLNAME;
1779     break;
1780     }
1781     /* FALL THROUGH */
1782     #endif
1783    
1784     default:
1785     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn,
1786     JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
1787     return JS_FALSE;
1788     }
1789    
1790     return JS_TRUE;
1791     }
1792    
1793     typedef struct FindPropValData {
1794     uint32 numvars; /* # of destructuring vars in left side */
1795     uint32 maxstep; /* max # of steps searching right side */
1796     JSDHashTable table; /* hash table for O(1) right side search */
1797     } FindPropValData;
1798    
1799     typedef struct FindPropValEntry {
1800     JSDHashEntryHdr hdr;
1801     JSParseNode *pnkey;
1802     JSParseNode *pnval;
1803     } FindPropValEntry;
1804    
1805     #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
1806     JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
1807     ((pnkey)->pn_type == TOK_NUMBER || \
1808     (pnkey)->pn_type == TOK_STRING || \
1809     (pnkey)->pn_type == TOK_NAME))
1810    
1811     static JSDHashNumber
1812     HashFindPropValKey(JSDHashTable *table, const void *key)
1813     {
1814     const JSParseNode *pnkey = (const JSParseNode *)key;
1815    
1816     ASSERT_VALID_PROPERTY_KEY(pnkey);
1817     return (pnkey->pn_type == TOK_NUMBER)
1818     ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
1819     JSDOUBLE_LO32(pnkey->pn_dval))
1820     : ATOM_HASH(pnkey->pn_atom);
1821     }
1822    
1823     static JSBool
1824     MatchFindPropValEntry(JSDHashTable *table,
1825     const JSDHashEntryHdr *entry,
1826     const void *key)
1827     {
1828     const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
1829     const JSParseNode *pnkey = (const JSParseNode *)key;
1830    
1831     ASSERT_VALID_PROPERTY_KEY(pnkey);
1832     return pnkey->pn_type == fpve->pnkey->pn_type &&
1833     ((pnkey->pn_type == TOK_NUMBER)
1834     ? pnkey->pn_dval == fpve->pnkey->pn_dval
1835     : pnkey->pn_atom == fpve->pnkey->pn_atom);
1836     }
1837    
1838     static const JSDHashTableOps FindPropValOps = {
1839     JS_DHashAllocTable,
1840     JS_DHashFreeTable,
1841     HashFindPropValKey,
1842     MatchFindPropValEntry,
1843     JS_DHashMoveEntryStub,
1844     JS_DHashClearEntryStub,
1845     JS_DHashFinalizeStub,
1846     NULL
1847     };
1848    
1849     #define STEP_HASH_THRESHOLD 10
1850     #define BIG_DESTRUCTURING 5
1851     #define BIG_OBJECT_INIT 20
1852    
1853     static JSParseNode *
1854     FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
1855     {
1856     FindPropValEntry *entry;
1857     JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
1858     uint32 step;
1859    
1860     /* If we have a hash table, use it as the sole source of truth. */
1861     if (data->table.ops) {
1862     entry = (FindPropValEntry *)
1863     JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
1864     return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
1865     }
1866    
1867     /* If pn is not an object initialiser node, we can't do anything here. */
1868     if (pn->pn_type != TOK_RC)
1869     return NULL;
1870    
1871     /*
1872     * We must search all the way through pn's list, to handle the case of an
1873     * id duplicated for two or more property initialisers.
1874     */
1875     pnhit = NULL;
1876     step = 0;
1877     ASSERT_VALID_PROPERTY_KEY(pnid);
1878     pnhead = pn->pn_head;
1879     if (pnhead && pnhead->pn_type == TOK_DEFSHARP)
1880     pnhead = pnhead->pn_next;
1881     if (pnid->pn_type == TOK_NUMBER) {
1882     for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
1883     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1884     if (pnprop->pn_op == JSOP_NOP) {
1885     pnkey = pnprop->pn_left;
1886     ASSERT_VALID_PROPERTY_KEY(pnkey);
1887     if (pnkey->pn_type == TOK_NUMBER &&
1888     pnkey->pn_dval == pnid->pn_dval) {
1889     pnhit = pnprop;
1890     }
1891     ++step;
1892     }
1893     }
1894     } else {
1895     for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
1896     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1897     if (pnprop->pn_op == JSOP_NOP) {
1898     pnkey = pnprop->pn_left;
1899     ASSERT_VALID_PROPERTY_KEY(pnkey);
1900     if (pnkey->pn_type == pnid->pn_type &&
1901     pnkey->pn_atom == pnid->pn_atom) {
1902     pnhit = pnprop;
1903     }
1904     ++step;
1905     }
1906     }
1907     }
1908     if (!pnhit)
1909     return NULL;
1910    
1911     /* Hit via full search -- see whether it's time to create the hash table. */
1912     JS_ASSERT(!data->table.ops);
1913     if (step > data->maxstep) {
1914     data->maxstep = step;
1915     if (step >= STEP_HASH_THRESHOLD &&
1916     data->numvars >= BIG_DESTRUCTURING &&
1917     pn->pn_count >= BIG_OBJECT_INIT &&
1918     JS_DHashTableInit(&data->table, &FindPropValOps, pn,
1919     sizeof(FindPropValEntry),
1920     JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
1921     {
1922     for (pn = pnhead; pn; pn = pn->pn_next) {
1923     JS_ASSERT(pnprop->pn_type == TOK_COLON);
1924     ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
1925     entry = (FindPropValEntry *)
1926     JS_DHashTableOperate(&data->table, pn->pn_left,
1927     JS_DHASH_ADD);
1928     entry->pnval = pn->pn_right;
1929     }
1930     }
1931     }
1932     return pnhit->pn_right;
1933     }
1934    
1935     /*
1936     * If data is null, the caller is AssignExpr and instead of binding variables,
1937     * we specialize lvalues in the propery value positions of the left-hand side.
1938     * If right is null, just check for well-formed lvalues.
1939     */
1940     static JSBool
1941     CheckDestructuring(JSContext *cx, BindData *data,
1942     JSParseNode *left, JSParseNode *right,
1943     JSTreeContext *tc)
1944     {
1945     JSBool ok;
1946     FindPropValData fpvd;
1947     JSParseNode *lhs, *rhs, *pn, *pn2;
1948    
1949     if (left->pn_type == TOK_ARRAYCOMP) {
1950     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), left,
1951     JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
1952     return JS_FALSE;
1953     }
1954    
1955     #if JS_HAS_DESTRUCTURING_SHORTHAND
1956     if (right && right->pn_arity == PN_LIST && (right->pn_extra & PNX_SHORTHAND)) {
1957     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), right,
1958     JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
1959     return JS_FALSE;
1960     }
1961     #endif
1962    
1963     fpvd.table.ops = NULL;
1964     lhs = left->pn_head;
1965     if (lhs && lhs->pn_type == TOK_DEFSHARP) {
1966     pn = lhs;
1967     goto no_var_name;
1968     }
1969    
1970     if (left->pn_type == TOK_RB) {
1971     rhs = (right && right->pn_type == left->pn_type)
1972     ? right->pn_head
1973     : NULL;
1974    
1975     while (lhs) {
1976     pn = lhs, pn2 = rhs;
1977     if (!data) {
1978     /* Skip parenthesization if not in a variable declaration. */
1979     while (pn->pn_type == TOK_RP)
1980     pn = pn->pn_kid;
1981     if (pn2) {
1982     while (pn2->pn_type == TOK_RP)
1983     pn2 = pn2->pn_kid;
1984     }
1985     }
1986    
1987     /* Nullary comma is an elision; binary comma is an expression.*/
1988     if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
1989     if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
1990     ok = CheckDestructuring(cx, data, pn, pn2, tc);
1991     } else {
1992     if (data) {
1993     if (pn->pn_type != TOK_NAME)
1994     goto no_var_name;
1995    
1996     ok = BindDestructuringVar(cx, data, pn, tc);
1997     } else {
1998     ok = BindDestructuringLHS(cx, pn, tc);
1999     }
2000     }
2001     if (!ok)
2002     goto out;
2003     }
2004    
2005     lhs = lhs->pn_next;
2006     if (rhs)
2007     rhs = rhs->pn_next;
2008     }
2009     } else {
2010     JS_ASSERT(left->pn_type == TOK_RC);
2011     fpvd.numvars = left->pn_count;
2012     fpvd.maxstep = 0;
2013     rhs = NULL;
2014    
2015     while (lhs) {
2016     JS_ASSERT(lhs->pn_type == TOK_COLON);
2017     pn = lhs->pn_right;
2018     if (!data) {
2019     /* Skip parenthesization if not in a variable declaration. */
2020     while (pn->pn_type == TOK_RP)
2021     pn = pn->pn_kid;
2022     }
2023    
2024     if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2025     if (right) {
2026     rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
2027     if (rhs && !data) {
2028     while (rhs->pn_type == TOK_RP)
2029     rhs = rhs->pn_kid;
2030     }
2031     }
2032    
2033     ok = CheckDestructuring(cx, data, pn, rhs, tc);
2034     } else if (data) {
2035     if (pn->pn_type != TOK_NAME)
2036     goto no_var_name;
2037    
2038     ok = BindDestructuringVar(cx, data, pn, tc);
2039     } else {
2040     ok = BindDestructuringLHS(cx, pn, tc);
2041     }
2042     if (!ok)
2043     goto out;
2044    
2045     lhs = lhs->pn_next;
2046     }
2047     }
2048    
2049     /*
2050     * The catch/finally handler implementation in the interpreter assumes
2051     * that any operation that introduces a new scope (like a "let" or "with"
2052     * block) increases the stack depth. This way, it is possible to restore
2053     * the scope chain based on stack depth of the handler alone. "let" with
2054     * an empty destructuring pattern like in
2055     *
2056     * let [] = 1;
2057     *
2058     * would violate this assumption as the there would be no let locals to
2059     * store on the stack. To satisfy it we add an empty property to such
2060     * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
2061     * slots, would be always positive.
2062     *
2063     * Note that we add such a property even if the block has locals due to
2064     * later let declarations in it. We optimize for code simplicity here,
2065     * not the fastest runtime performance with empty [] or {}.
2066     */
2067     if (data &&
2068     data->binder == BindLet &&
2069     OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
2070     ok = js_DefineNativeProperty(cx, tc->blockChain,
2071     ATOM_TO_JSID(cx->runtime->
2072     atomState.emptyAtom),
2073     JSVAL_VOID, NULL, NULL,
2074     JSPROP_ENUMERATE |
2075     JSPROP_PERMANENT |
2076     JSPROP_SHARED,
2077     SPROP_HAS_SHORTID, 0, NULL);
2078     if (!ok)
2079     goto out;
2080     }
2081    
2082     ok = JS_TRUE;
2083    
2084     out:
2085     if (fpvd.table.ops)
2086     JS_DHashTableFinish(&fpvd.table);
2087     return ok;
2088    
2089     no_var_name:
2090     js_ReportCompileErrorNumber(cx, TS(tc->parseContext), pn, JSREPORT_ERROR,
2091     JSMSG_NO_VARIABLE_NAME);
2092     ok = JS_FALSE;
2093     goto out;
2094     }
2095    
2096     static JSParseNode *
2097     DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
2098     JSTokenType tt)
2099     {
2100     JSParseNode *pn;
2101    
2102     pn = PrimaryExpr(cx, TS(tc->parseContext), tc, tt, JS_FALSE);
2103     if (!pn)
2104     return NULL;
2105     if (!CheckDestructuring(cx, data, pn, NULL, tc))
2106     return NULL;
2107     return pn;
2108     }
2109    
2110 siliconforks 399 // Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
2111     static JSParseNode *
2112     CloneParseTree(JSContext *cx, JSParseNode *opn, JSTreeContext *tc)
2113     {
2114     JSParseNode *pn, *pn2, *opn2;
2115    
2116     pn = NewOrRecycledNode(cx, tc);
2117     if (!pn)
2118     return NULL;
2119     pn->pn_type = opn->pn_type;
2120     pn->pn_pos = opn->pn_pos;
2121     pn->pn_op = opn->pn_op;
2122     pn->pn_arity = opn->pn_arity;
2123    
2124     switch (pn->pn_arity) {
2125     #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
2126    
2127     case PN_FUNC:
2128     NULLCHECK(pn->pn_funpob =
2129     js_NewParsedObjectBox(cx, tc->parseContext, opn->pn_funpob->object));
2130     NULLCHECK(pn->pn_body = CloneParseTree(cx, opn->pn_body, tc));
2131     pn->pn_flags = opn->pn_flags;
2132     pn->pn_index = opn->pn_index;
2133     break;
2134    
2135     case PN_LIST:
2136     PN_INIT_LIST(pn);
2137     for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
2138     NULLCHECK(pn2 = CloneParseTree(cx, opn2, tc));
2139     PN_APPEND(pn, pn2);
2140     }
2141     pn->pn_extra = opn->pn_extra;
2142     break;
2143    
2144     case PN_TERNARY:
2145     NULLCHECK(pn->pn_kid1 = CloneParseTree(cx, opn->pn_kid1, tc));
2146     NULLCHECK(pn->pn_kid2 = CloneParseTree(cx, opn->pn_kid2, tc));
2147     NULLCHECK(pn->pn_kid3 = CloneParseTree(cx, opn->pn_kid3, tc));
2148     break;
2149    
2150     case PN_BINARY:
2151     NULLCHECK(pn->pn_left = CloneParseTree(cx, opn->pn_left, tc));
2152     if (opn->pn_right != opn->pn_left)
2153     NULLCHECK(pn->pn_right = CloneParseTree(cx, opn->pn_right, tc));
2154     else
2155     pn->pn_right = pn->pn_left;
2156     pn->pn_val = opn->pn_val;
2157     pn->pn_iflags = opn->pn_iflags;
2158     break;
2159    
2160     case PN_UNARY:
2161     NULLCHECK(pn->pn_kid = CloneParseTree(cx, opn->pn_kid, tc));
2162     pn->pn_num = opn->pn_num;
2163     pn->pn_hidden = opn->pn_hidden;
2164     break;
2165    
2166     case PN_NAME:
2167     // PN_NAME could mean several arms in pn_u, so copy the whole thing.
2168     pn->pn_u = opn->pn_u;
2169     if (opn->pn_expr)
2170     NULLCHECK(pn->pn_expr = CloneParseTree(cx, opn->pn_expr, tc));
2171     break;
2172    
2173     case PN_NULLARY:
2174     // Even PN_NULLARY may have data (apair for E4X -- what a botch).
2175     pn->pn_u = opn->pn_u;
2176     break;
2177    
2178     #undef NULLCHECK
2179     }
2180     return pn;
2181     }
2182    
2183 siliconforks 332 #endif /* JS_HAS_DESTRUCTURING */
2184    
2185     extern const char js_with_statement_str[];
2186    
2187     static JSParseNode *
2188     ContainsStmt(JSParseNode *pn, JSTokenType tt)
2189     {
2190     JSParseNode *pn2, *pnt;
2191    
2192     if (!pn)
2193     return NULL;
2194     if (pn->pn_type == tt)
2195     return pn;
2196     switch (pn->pn_arity) {
2197     case PN_LIST:
2198     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2199     pnt = ContainsStmt(pn2, tt);
2200     if (pnt)
2201     return pnt;
2202     }
2203     break;
2204     case PN_TERNARY:
2205     pnt = ContainsStmt(pn->pn_kid1, tt);
2206     if (pnt)
2207     return pnt;
2208     pnt = ContainsStmt(pn->pn_kid2, tt);
2209     if (pnt)
2210     return pnt;
2211     return ContainsStmt(pn->pn_kid3, tt);
2212     case PN_BINARY:
2213     /*
2214     * Limit recursion if pn is a binary expression, which can't contain a
2215     * var statement.
2216     */
2217     if (pn->pn_op != JSOP_NOP)
2218     return NULL;
2219     pnt = ContainsStmt(pn->pn_left, tt);
2220     if (pnt)
2221     return pnt;
2222     return ContainsStmt(pn->pn_right, tt);
2223     case PN_UNARY:
2224     if (pn->pn_op != JSOP_NOP)
2225     return NULL;
2226     return ContainsStmt(pn->pn_kid, tt);
2227     case PN_NAME:
2228     return ContainsStmt(pn->pn_expr, tt);
2229     default:;
2230     }
2231     return NULL;
2232     }
2233    
2234     static JSParseNode *
2235     ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2236     JSParser operandParser)
2237     {
2238     JSTokenType tt, tt2;
2239     JSParseNode *pn, *pn2;
2240    
2241     tt = CURRENT_TOKEN(ts).type;
2242     if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
2243     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2244     JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
2245     return NULL;
2246     }
2247    
2248     pn = NewParseNode(cx, ts, PN_UNARY, tc);
2249     if (!pn)
2250     return NULL;
2251    
2252     #if JS_HAS_GENERATORS
2253     if (tt == TOK_YIELD)
2254     tc->flags |= TCF_FUN_IS_GENERATOR;
2255     #endif
2256    
2257     /* This is ugly, but we don't want to require a semicolon. */
2258     ts->flags |= TSF_OPERAND;
2259     tt2 = js_PeekTokenSameLine(cx, ts);
2260     ts->flags &= ~TSF_OPERAND;
2261     if (tt2 == TOK_ERROR)
2262     return NULL;
2263    
2264     if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
2265     #if JS_HAS_GENERATORS
2266     && (tt != TOK_YIELD ||
2267     (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
2268     tt2 != TOK_COLON && tt2 != TOK_COMMA))
2269     #endif
2270     ) {
2271     pn2 = operandParser(cx, ts, tc);
2272     if (!pn2)
2273     return NULL;
2274     #if JS_HAS_GENERATORS
2275     if (tt == TOK_RETURN)
2276     #endif
2277     tc->flags |= TCF_RETURN_EXPR;
2278     pn->pn_pos.end = pn2->pn_pos.end;
2279     pn->pn_kid = pn2;
2280     } else {
2281     #if JS_HAS_GENERATORS
2282     if (tt == TOK_RETURN)
2283     #endif
2284     tc->flags |= TCF_RETURN_VOID;
2285     }
2286    
2287     if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
2288     /* As in Python (see PEP-255), disallow return v; in generators. */
2289     ReportBadReturn(cx, tc, JSREPORT_ERROR,
2290     JSMSG_BAD_GENERATOR_RETURN,
2291     JSMSG_BAD_ANON_GENERATOR_RETURN);
2292     return NULL;
2293     }
2294    
2295     if (JS_HAS_STRICT_OPTION(cx) &&
2296     (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
2297     !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
2298     JSMSG_NO_RETURN_VALUE,
2299     JSMSG_ANON_NO_RETURN_VALUE)) {
2300     return NULL;
2301     }
2302    
2303     return pn;
2304     }
2305    
2306     static JSParseNode *
2307     PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2308     JSStmtInfo *stmtInfo)
2309     {
2310     JSParseNode *pn;
2311     JSObject *obj;
2312     JSParsedObjectBox *blockpob;
2313    
2314     pn = NewParseNode(cx, ts, PN_NAME, tc);
2315     if (!pn)
2316     return NULL;
2317    
2318     obj = js_NewBlockObject(cx);
2319     if (!obj)
2320     return NULL;
2321    
2322     blockpob = js_NewParsedObjectBox(cx, tc->parseContext, obj);
2323     if (!blockpob)
2324     return NULL;
2325    
2326     js_PushBlockScope(tc, stmtInfo, obj, -1);
2327     pn->pn_type = TOK_LEXICALSCOPE;
2328     pn->pn_op = JSOP_LEAVEBLOCK;
2329     pn->pn_pob = blockpob;
2330     pn->pn_slot = -1;
2331     return pn;
2332     }
2333    
2334     #if JS_HAS_BLOCK_SCOPE
2335    
2336     static JSParseNode *
2337     LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
2338     {
2339     JSParseNode *pn, *pnblock, *pnlet;
2340     JSStmtInfo stmtInfo;
2341    
2342     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
2343    
2344     /* Create the let binary node. */
2345     pnlet = NewParseNode(cx, ts, PN_BINARY, tc);
2346     if (!pnlet)
2347     return NULL;
2348    
2349     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
2350    
2351     /* This is a let block or expression of the form: let (a, b, c) .... */
2352     pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
2353     if (!pnblock)
2354     return NULL;
2355     pn = pnblock;
2356     pn->pn_expr = pnlet;
2357    
2358     pnlet->pn_left = Variables(cx, ts, tc);
2359     if (!pnlet->pn_left)
2360     return NULL;
2361     pnlet->pn_left->pn_extra = PNX_POPVAR;
2362    
2363     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
2364    
2365     ts->flags |= TSF_OPERAND;
2366     if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
2367     /*
2368     * If this is really an expression in let statement guise, then we
2369     * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2370     * the return value of the expression.
2371     */
2372     pn = NewParseNode(cx, ts, PN_UNARY, tc);
2373     if (!pn)
2374     return NULL;
2375     pn->pn_type = TOK_SEMI;
2376     pn->pn_num = -1;
2377     pn->pn_kid = pnblock;
2378    
2379     statement = JS_FALSE;
2380     }
2381     ts->flags &= ~TSF_OPERAND;
2382    
2383     if (statement) {
2384     pnlet->pn_right = Statements(cx, ts, tc);
2385     if (!pnlet->pn_right)
2386     return NULL;
2387     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2388     } else {
2389     /*
2390     * Change pnblock's opcode to the variant that propagates the last
2391     * result down after popping the block, and clear statement.
2392     */
2393     pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
2394     pnlet->pn_right = AssignExpr(cx, ts, tc);
2395     if (!pnlet->pn_right)
2396     return NULL;
2397     }
2398    
2399     js_PopStatement(tc);
2400     return pn;
2401     }
2402    
2403     #endif /* JS_HAS_BLOCK_SCOPE */
2404    
2405     static JSParseNode *
2406     Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2407     {
2408     JSTokenType tt;
2409     JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
2410     JSStmtInfo stmtInfo, *stmt, *stmt2;
2411     JSAtom *label;
2412    
2413     JS_CHECK_RECURSION(cx, return NULL);
2414    
2415     ts->flags |= TSF_OPERAND;
2416     tt = js_GetToken(cx, ts);
2417     ts->flags &= ~TSF_OPERAND;
2418    
2419     #if JS_HAS_GETTER_SETTER
2420     if (tt == TOK_NAME) {
2421     tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2422     if (tt == TOK_ERROR)
2423     return NULL;
2424     }
2425     #endif
2426    
2427     switch (tt) {
2428     case TOK_FUNCTION:
2429     #if JS_HAS_XML_SUPPORT
2430     ts->flags |= TSF_KEYWORD_IS_NAME;
2431     tt = js_PeekToken(cx, ts);
2432     ts->flags &= ~TSF_KEYWORD_IS_NAME;
2433     if (tt == TOK_DBLCOLON)
2434     goto expression;
2435     #endif
2436     return FunctionStmt(cx, ts, tc);
2437    
2438     case TOK_IF:
2439     /* An IF node has three kids: condition, then, and optional else. */
2440     pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2441     if (!pn)
2442     return NULL;
2443     pn1 = Condition(cx, ts, tc);
2444     if (!pn1)
2445     return NULL;
2446     js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
2447     pn2 = Statement(cx, ts, tc);
2448     if (!pn2)
2449     return NULL;
2450     ts->flags |= TSF_OPERAND;
2451     if (js_MatchToken(cx, ts, TOK_ELSE)) {
2452     ts->flags &= ~TSF_OPERAND;
2453     stmtInfo.type = STMT_ELSE;
2454     pn3 = Statement(cx, ts, tc);
2455     if (!pn3)
2456     return NULL;
2457     pn->pn_pos.end = pn3->pn_pos.end;
2458     } else {
2459     ts->flags &= ~TSF_OPERAND;
2460     pn3 = NULL;
2461     pn->pn_pos.end = pn2->pn_pos.end;
2462     }
2463     js_PopStatement(tc);
2464     pn->pn_kid1 = pn1;
2465     pn->pn_kid2 = pn2;
2466     pn->pn_kid3 = pn3;
2467     return pn;
2468    
2469     case TOK_SWITCH:
2470     {
2471     JSParseNode *pn5, *saveBlock;
2472     JSBool seenDefault = JS_FALSE;
2473    
2474     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2475     if (!pn)
2476     return NULL;
2477     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
2478    
2479     /* pn1 points to the switch's discriminant. */
2480     pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
2481     if (!pn1)
2482     return NULL;
2483    
2484     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
2485     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
2486    
2487     /* pn2 is a list of case nodes. The default case has pn_left == NULL */
2488     pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2489     if (!pn2)
2490     return NULL;
2491     saveBlock = tc->blockNode;
2492     tc->blockNode = pn2;
2493     PN_INIT_LIST(pn2);
2494    
2495     js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
2496    
2497     while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
2498     switch (tt) {
2499     case TOK_DEFAULT:
2500     if (seenDefault) {
2501     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2502     JSMSG_TOO_MANY_DEFAULTS);
2503     return NULL;
2504     }
2505     seenDefault = JS_TRUE;
2506     /* FALL THROUGH */
2507    
2508     case TOK_CASE:
2509     pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
2510     if (!pn3)
2511     return NULL;
2512     if (tt == TOK_CASE) {
2513     pn3->pn_left = Expr(cx, ts, tc);
2514     if (!pn3->pn_left)
2515     return NULL;
2516     }
2517     PN_APPEND(pn2, pn3);
2518     if (pn2->pn_count == JS_BIT(16)) {
2519     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2520     JSMSG_TOO_MANY_CASES);
2521     return NULL;
2522     }
2523     break;
2524    
2525     case TOK_ERROR:
2526     return NULL;
2527    
2528     default:
2529     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2530     JSMSG_BAD_SWITCH);
2531     return NULL;
2532     }
2533     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
2534    
2535     pn4 = NewParseNode(cx, ts, PN_LIST, tc);
2536     if (!pn4)
2537     return NULL;
2538     pn4->pn_type = TOK_LC;
2539     PN_INIT_LIST(pn4);
2540     ts->flags |= TSF_OPERAND;
2541     while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
2542     tt != TOK_CASE && tt != TOK_DEFAULT) {
2543     ts->flags &= ~TSF_OPERAND;
2544     if (tt == TOK_ERROR)
2545     return NULL;
2546     pn5 = Statement(cx, ts, tc);
2547     if (!pn5)
2548     return NULL;
2549     pn4->pn_pos.end = pn5->pn_pos.end;
2550     PN_APPEND(pn4, pn5);
2551     ts->flags |= TSF_OPERAND;
2552     }
2553     ts->flags &= ~TSF_OPERAND;
2554    
2555     /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
2556     if (pn4->pn_head)
2557     pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
2558     pn3->pn_pos.end = pn4->pn_pos.end;
2559     pn3->pn_right = pn4;
2560     }
2561    
2562     /*
2563     * Handle the case where there was a let declaration in any case in
2564     * the switch body, but not within an inner block. If it replaced
2565     * tc->blockNode with a new block node then we must refresh pn2 and
2566     * then restore tc->blockNode.
2567     */
2568     if (tc->blockNode != pn2)
2569     pn2 = tc->blockNode;
2570     tc->blockNode = saveBlock;
2571     js_PopStatement(tc);
2572    
2573     pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2574     pn->pn_left = pn1;
2575     pn->pn_right = pn2;
2576     return pn;
2577     }
2578    
2579     case TOK_WHILE:
2580     pn = NewParseNode(cx, ts, PN_BINARY, tc);
2581     if (!pn)
2582     return NULL;
2583     js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2584     pn2 = Condition(cx, ts, tc);
2585     if (!pn2)
2586     return NULL;
2587     pn->pn_left = pn2;
2588     pn2 = Statement(cx, ts, tc);
2589     if (!pn2)
2590     return NULL;
2591     js_PopStatement(tc);