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

Contents of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /* -*- 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 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 /* 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 * scope node, we must make a special TOK_SEQ node, to prepend the formal
1314 * 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 block->pn_type = TOK_SEQ;
1325 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 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 #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 // 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 #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);
2592 pn->pn_pos.end = pn2->pn_pos.end;
2593 pn->pn_right = pn2;
2594 return pn;
2595
2596 case TOK_DO:
2597 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2598 if (!pn)
2599 return NULL;
2600 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2601 pn2 = Statement(cx, ts, tc);
2602 if (!pn2)
2603 return NULL;
2604 pn->pn_left = pn2;
2605 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2606 pn2 = Condition(cx, ts, tc);
2607 if (!pn2)
2608 return NULL;
2609 js_PopStatement(tc);
2610 pn->pn_pos.end = pn2->pn_pos.end;
2611 pn->pn_right = pn2;
2612 if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
2613 /*
2614 * All legacy and extended versions must do automatic semicolon
2615 * insertion after do-while. See the testcase and discussion in
2616 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2617 */
2618 (void) js_MatchToken(cx, ts, TOK_SEMI);
2619 return pn;
2620 }
2621 break;
2622
2623 case TOK_FOR:
2624 {
2625 JSParseNode *pnseq = NULL;
2626 #if JS_HAS_BLOCK_SCOPE
2627 JSParseNode *pnlet = NULL;
2628 JSStmtInfo blockInfo;
2629 #endif
2630
2631 /* A FOR node is binary, left is loop control and right is the body. */
2632 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2633 if (!pn)
2634 return NULL;
2635 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2636
2637 pn->pn_op = JSOP_ITER;
2638 pn->pn_iflags = 0;
2639 if (js_MatchToken(cx, ts, TOK_NAME)) {
2640 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
2641 pn->pn_iflags = JSITER_FOREACH;
2642 else
2643 js_UngetToken(ts);
2644 }
2645
2646 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
2647 ts->flags |= TSF_OPERAND;
2648 tt = js_PeekToken(cx, ts);
2649 ts->flags &= ~TSF_OPERAND;
2650 if (tt == TOK_SEMI) {
2651 if (pn->pn_iflags & JSITER_FOREACH)
2652 goto bad_for_each;
2653
2654 /* No initializer -- set first kid of left sub-node to null. */
2655 pn1 = NULL;
2656 } else {
2657 /*
2658 * Set pn1 to a var list or an initializing expression.
2659 *
2660 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2661 * of the for statement. This flag will be used by the RelExpr
2662 * production; if it is set, then the 'in' keyword will not be
2663 * recognized as an operator, leaving it available to be parsed as
2664 * part of a for/in loop.
2665 *
2666 * A side effect of this restriction is that (unparenthesized)
2667 * expressions involving an 'in' operator are illegal in the init
2668 * clause of an ordinary for loop.
2669 */
2670 tc->flags |= TCF_IN_FOR_INIT;
2671 if (tt == TOK_VAR) {
2672 (void) js_GetToken(cx, ts);
2673 pn1 = Variables(cx, ts, tc);
2674 #if JS_HAS_BLOCK_SCOPE
2675 } else if (tt == TOK_LET) {
2676 (void) js_GetToken(cx, ts);
2677 if (js_PeekToken(cx, ts) == TOK_LP) {
2678 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
2679 tt = TOK_LEXICALSCOPE;
2680 } else {
2681 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
2682 if (!pnlet)
2683 return NULL;
2684 blockInfo.flags |= SIF_FOR_BLOCK;
2685 pn1 = Variables(cx, ts, tc);
2686 }
2687 #endif
2688 } else {
2689 pn1 = Expr(cx, ts, tc);
2690 if (pn1) {
2691 while (pn1->pn_type == TOK_RP)
2692 pn1 = pn1->pn_kid;
2693 }
2694 }
2695 tc->flags &= ~TCF_IN_FOR_INIT;
2696 if (!pn1)
2697 return NULL;
2698 }
2699
2700 /*
2701 * We can be sure that it's a for/in loop if there's still an 'in'
2702 * keyword here, even if JavaScript recognizes 'in' as an operator,
2703 * as we've excluded 'in' from being parsed in RelExpr by setting
2704 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
2705 */
2706 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
2707 pn->pn_iflags |= JSITER_ENUMERATE;
2708 stmtInfo.type = STMT_FOR_IN_LOOP;
2709
2710 /* Check that the left side of the 'in' is valid. */
2711 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
2712 if (TOKEN_TYPE_IS_DECL(tt)
2713 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
2714 #if JS_HAS_DESTRUCTURING
2715 || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
2716 pn->pn_op == JSOP_ITER &&
2717 !(pn->pn_iflags & JSITER_FOREACH) &&
2718 (pn1->pn_head->pn_type == TOK_RC ||
2719 (pn1->pn_head->pn_type == TOK_RB &&
2720 pn1->pn_head->pn_count != 2) ||
2721 (pn1->pn_head->pn_type == TOK_ASSIGN &&
2722 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
2723 pn1->pn_head->pn_left->pn_count != 2))))
2724 #endif
2725 )
2726 : (pn1->pn_type != TOK_NAME &&
2727 pn1->pn_type != TOK_DOT &&
2728 #if JS_HAS_DESTRUCTURING
2729 ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
2730 pn->pn_op == JSOP_ITER &&
2731 !(pn->pn_iflags & JSITER_FOREACH))
2732 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
2733 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
2734 #endif
2735 #if JS_HAS_LVALUE_RETURN
2736 pn1->pn_type != TOK_LP &&
2737 #endif
2738 #if JS_HAS_XML_SUPPORT
2739 (pn1->pn_type != TOK_UNARYOP ||
2740 pn1->pn_op != JSOP_XMLNAME) &&
2741 #endif
2742 pn1->pn_type != TOK_LB)) {
2743 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
2744 JSMSG_BAD_FOR_LEFTSIDE);
2745 return NULL;
2746 }
2747
2748 /* pn2 points to the name or destructuring pattern on in's left. */
2749 pn2 = NULL;
2750
2751 if (TOKEN_TYPE_IS_DECL(tt)) {
2752 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
2753 pn1->pn_extra |= PNX_FORINVAR;
2754
2755 /*
2756 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
2757 * 'var', or 'const' to hoist the initializer or the entire
2758 * decl out of the loop head. TOK_VAR is the type for both
2759 * 'var' and 'const'.
2760 */
2761 pn2 = pn1->pn_head;
2762 if (pn2->pn_type == TOK_NAME && pn2->pn_expr
2763 #if JS_HAS_DESTRUCTURING
2764 || pn2->pn_type == TOK_ASSIGN
2765 #endif
2766 ) {
2767 pnseq = NewParseNode(cx, ts, PN_LIST, tc);
2768 if (!pnseq)
2769 return NULL;
2770 pnseq->pn_type = TOK_SEQ;
2771 pnseq->pn_pos.begin = pn->pn_pos.begin;
2772 if (tt == TOK_LET) {
2773 /*
2774 * Hoist just the 'i' from 'for (let x = i in o)' to
2775 * before the loop, glued together via pnseq.
2776 */
2777 pn3 = NewParseNode(cx, ts, PN_UNARY, tc);
2778 if (!pn3)
2779 return NULL;
2780 pn3->pn_type = TOK_SEMI;
2781 pn3->pn_op = JSOP_NOP;
2782 #if JS_HAS_DESTRUCTURING
2783 if (pn2->pn_type == TOK_ASSIGN) {
2784 pn4 = pn2->pn_right;
2785 pn2 = pn1->pn_head = pn2->pn_left;
2786 } else
2787 #endif
2788 {
2789 pn4 = pn2->pn_expr;
2790 pn2->pn_expr = NULL;
2791 }
2792 pn3->pn_pos = pn4->pn_pos;
2793 pn3->pn_kid = pn4;
2794 PN_INIT_LIST_1(pnseq, pn3);
2795 } else {
2796 /*
2797 * All of 'var x = i' is hoisted above 'for (x in o)',
2798 * so clear PNX_FORINVAR.
2799 *
2800 * Request JSOP_POP here since the var is for a simple
2801 * name (it is not a destructuring binding's left-hand
2802 * side) and it has an initializer.
2803 */
2804 pn1->pn_extra &= ~PNX_FORINVAR;
2805 pn1->pn_extra |= PNX_POPVAR;
2806 PN_INIT_LIST_1(pnseq, pn1);
2807
2808 #if JS_HAS_DESTRUCTURING
2809 if (pn2->pn_type == TOK_ASSIGN) {
2810 pn1 = CloneParseTree(cx, pn2->pn_left, tc);
2811 if (!pn1)
2812 return NULL;
2813 } else
2814 #endif
2815 {
2816 pn1 = NewParseNode(cx, ts, PN_NAME, tc);
2817 if (!pn1)
2818 return NULL;
2819 pn1->pn_type = TOK_NAME;
2820 pn1->pn_op = JSOP_NAME;
2821 pn1->pn_pos = pn2->pn_pos;
2822 pn1->pn_atom = pn2->pn_atom;
2823 pn1->pn_expr = NULL;
2824 pn1->pn_slot = -1;
2825 pn1->pn_const = pn2->pn_const;
2826 }
2827 pn2 = pn1;
2828 }
2829 }
2830 }
2831
2832 if (!pn2) {
2833 pn2 = pn1;
2834 #if JS_HAS_LVALUE_RETURN
2835 if (pn2->pn_type == TOK_LP &&
2836 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
2837 return NULL;
2838 }
2839 #endif
2840 #if JS_HAS_XML_SUPPORT
2841 if (pn2->pn_type == TOK_UNARYOP)
2842 pn2->pn_op = JSOP_BINDXMLNAME;
2843 #endif
2844 }
2845
2846 switch (pn2->pn_type) {
2847 case TOK_NAME:
2848 /* Beware 'for (arguments in ...)' with or without a 'var'. */
2849 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2850 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2851 break;
2852
2853 #if JS_HAS_DESTRUCTURING
2854 case TOK_ASSIGN:
2855 pn2 = pn2->pn_left;
2856 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
2857 /* FALL THROUGH */
2858 case TOK_RB:
2859 case TOK_RC:
2860 /* Check for valid lvalues in var-less destructuring for-in. */
2861 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
2862 return NULL;
2863
2864 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
2865 /*
2866 * Destructuring for-in requires [key, value] enumeration
2867 * in JS1.7.
2868 */
2869 JS_ASSERT(pn->pn_op == JSOP_ITER);
2870 if (!(pn->pn_iflags & JSITER_FOREACH))
2871 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
2872 }
2873 break;
2874 #endif
2875
2876 default:;
2877 }
2878
2879 /* Parse the object expression as the right operand of 'in'. */
2880 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
2881 if (!pn2)
2882 return NULL;
2883 pn->pn_left = pn2;
2884 } else {
2885 if (pn->pn_iflags & JSITER_FOREACH)
2886 goto bad_for_each;
2887 pn->pn_op = JSOP_NOP;
2888
2889 /* Parse the loop condition or null into pn2. */
2890 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
2891 ts->flags |= TSF_OPERAND;
2892 tt = js_PeekToken(cx, ts);
2893 ts->flags &= ~TSF_OPERAND;
2894 if (tt == TOK_SEMI) {
2895 pn2 = NULL;
2896 } else {
2897 pn2 = Expr(cx, ts, tc);
2898 if (!pn2)
2899 return NULL;
2900 }
2901
2902 /* Parse the update expression or null into pn3. */
2903 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
2904 ts->flags |= TSF_OPERAND;
2905 tt = js_PeekToken(cx, ts);
2906 ts->flags &= ~TSF_OPERAND;
2907 if (tt == TOK_RP) {
2908 pn3 = NULL;
2909 } else {
2910 pn3 = Expr(cx, ts, tc);
2911 if (!pn3)
2912 return NULL;
2913 }
2914
2915 /* Build the FORHEAD node to use as the left kid of pn. */
2916 pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
2917 if (!pn4)
2918 return NULL;
2919 pn4->pn_type = TOK_FORHEAD;
2920 pn4->pn_op = JSOP_NOP;
2921 pn4->pn_kid1 = pn1;
2922 pn4->pn_kid2 = pn2;
2923 pn4->pn_kid3 = pn3;
2924 pn->pn_left = pn4;
2925 }
2926
2927 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
2928
2929 /* Parse the loop body into pn->pn_right. */
2930 pn2 = Statement(cx, ts, tc);
2931 if (!pn2)
2932 return NULL;
2933 pn->pn_right = pn2;
2934
2935 /* Record the absolute line number for source note emission. */
2936 pn->pn_pos.end = pn2->pn_pos.end;
2937
2938 #if JS_HAS_BLOCK_SCOPE
2939 if (pnlet) {
2940 js_PopStatement(tc);
2941 pnlet->pn_expr = pn;
2942 pn = pnlet;
2943 }
2944 #endif
2945 if (pnseq) {
2946 pnseq->pn_pos.end = pn->pn_pos.end;
2947 PN_APPEND(pnseq, pn);
2948 pn = pnseq;
2949 }
2950 js_PopStatement(tc);
2951 return pn;
2952
2953 bad_for_each:
2954 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
2955 JSMSG_BAD_FOR_EACH_LOOP);
2956 return NULL;
2957 }
2958
2959 case TOK_TRY: {
2960 JSParseNode *catchList, *lastCatch;
2961
2962 /*
2963 * try nodes are ternary.
2964 * kid1 is the try Statement
2965 * kid2 is the catch node list or null
2966 * kid3 is the finally Statement
2967 *
2968 * catch nodes are ternary.
2969 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
2970 * kid2 is the catch guard or null if no guard
2971 * kid3 is the catch block
2972 *
2973 * catch lvalue nodes are either:
2974 * TOK_NAME for a single identifier
2975 * TOK_RB or TOK_RC for a destructuring left-hand side
2976 *
2977 * finally nodes are TOK_LC Statement lists.
2978 */
2979 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2980 if (!pn)
2981 return NULL;
2982 pn->pn_op = JSOP_NOP;
2983
2984 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
2985 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
2986 pn->pn_kid1 = Statements(cx, ts, tc);
2987 if (!pn->pn_kid1)
2988 return NULL;
2989 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
2990 js_PopStatement(tc);
2991
2992 catchList = NULL;
2993 tt = js_GetToken(cx, ts);
2994 if (tt == TOK_CATCH) {
2995 catchList = NewParseNode(cx, ts, PN_LIST, tc);
2996 if (!catchList)
2997 return NULL;
2998 catchList->pn_type = TOK_RESERVED;
2999 PN_INIT_LIST(catchList);
3000 lastCatch = NULL;
3001
3002 do {
3003 JSParseNode *pnblock;
3004 BindData data;
3005
3006 /* Check for another catch after unconditional catch. */
3007 if (lastCatch && !lastCatch->pn_kid2) {
3008 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3009 JSMSG_CATCH_AFTER_GENERAL);
3010 return NULL;
3011 }
3012
3013 /*
3014 * Create a lexical scope node around the whole catch clause,
3015 * including the head.
3016 */
3017 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
3018 if (!pnblock)
3019 return NULL;
3020 stmtInfo.type = STMT_CATCH;
3021
3022 /*
3023 * Legal catch forms are:
3024 * catch (lhs)
3025 * catch (lhs if <boolean_expression>)
3026 * where lhs is a name or a destructuring left-hand side.
3027 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
3028 */
3029 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
3030 if (!pn2)
3031 return NULL;
3032 pnblock->pn_expr = pn2;
3033 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
3034
3035 /*
3036 * Contrary to ECMA Ed. 3, the catch variable is lexically
3037 * scoped, not a property of a new Object instance. This is
3038 * an intentional change that anticipates ECMA Ed. 4.
3039 */
3040 data.pn = NULL;
3041 data.op = JSOP_NOP;
3042 data.binder = BindLet;
3043 data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
3044
3045 tt = js_GetToken(cx, ts);
3046 switch (tt) {
3047 #if JS_HAS_DESTRUCTURING
3048 case TOK_LB:
3049 case TOK_LC:
3050 pn3 = DestructuringExpr(cx, &data, tc, tt);
3051 if (!pn3)
3052 return NULL;
3053 break;
3054 #endif
3055
3056 case TOK_NAME:
3057 label = CURRENT_TOKEN(ts).t_atom;
3058 if (!data.binder(cx, &data, label, tc))
3059 return NULL;
3060
3061 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3062 if (!pn3)
3063 return NULL;
3064 pn3->pn_atom = label;
3065 pn3->pn_slot = -1;
3066 break;
3067
3068 default:
3069 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3070 JSMSG_CATCH_IDENTIFIER);
3071 return NULL;
3072 }
3073
3074 pn2->pn_kid1 = pn3;
3075 #if JS_HAS_CATCH_GUARD
3076 /*
3077 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
3078 * to avoid conflicting with the JS2/ECMAv4 type annotation
3079 * catchguard syntax.
3080 */
3081 if (js_MatchToken(cx, ts, TOK_IF)) {
3082 pn2->pn_kid2 = Expr(cx, ts, tc);
3083 if (!pn2->pn_kid2)
3084 return NULL;
3085 }
3086 #endif
3087 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
3088
3089 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
3090 pn2->pn_kid3 = Statements(cx, ts, tc);
3091 if (!pn2->pn_kid3)
3092 return NULL;
3093 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
3094 js_PopStatement(tc);
3095
3096 PN_APPEND(catchList, pnblock);
3097 lastCatch = pn2;
3098 ts->flags |= TSF_OPERAND;
3099 tt = js_GetToken(cx, ts);
3100 ts->flags &= ~TSF_OPERAND;
3101 } while (tt == TOK_CATCH);
3102 }
3103 pn->pn_kid2 = catchList;
3104
3105 if (tt == TOK_FINALLY) {
3106 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
3107 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
3108 pn->pn_kid3 = Statements(cx, ts, tc);
3109 if (!pn->pn_kid3)
3110 return NULL;
3111 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
3112 js_PopStatement(tc);
3113 } else {
3114 js_UngetToken(ts);
3115 }
3116 if (!catchList && !pn->pn_kid3) {
3117 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3118 JSMSG_CATCH_OR_FINALLY);
3119 return NULL;
3120 }
3121 return pn;
3122 }
3123
3124 case TOK_THROW:
3125 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3126 if (!pn)
3127 return NULL;
3128
3129 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
3130 ts->flags |= TSF_OPERAND;
3131 tt = js_PeekTokenSameLine(cx, ts);
3132 ts->flags &= ~TSF_OPERAND;
3133 if (tt == TOK_ERROR)
3134 return NULL;
3135 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
3136 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3137 JSMSG_SYNTAX_ERROR);
3138 return NULL;
3139 }
3140
3141 pn2 = Expr(cx, ts, tc);
3142 if (!pn2)
3143 return NULL;
3144 pn->pn_pos.end = pn2->pn_pos.end;
3145 pn->pn_op = JSOP_THROW;
3146 pn->pn_kid = pn2;
3147 break;
3148
3149 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
3150 case TOK_CATCH:
3151 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3152 JSMSG_CATCH_WITHOUT_TRY);
3153 return NULL;
3154
3155 case TOK_FINALLY:
3156 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3157 JSMSG_FINALLY_WITHOUT_TRY);
3158 return NULL;
3159
3160 case TOK_BREAK:
3161 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3162 if (!pn)
3163 return NULL;
3164 if (!MatchLabel(cx, ts, pn))
3165 return NULL;
3166 stmt = tc->topStmt;
3167 label = pn->pn_atom;
3168 if (label) {
3169 for (; ; stmt = stmt->down) {
3170 if (!stmt) {
3171 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3172 JSMSG_LABEL_NOT_FOUND);
3173 return NULL;
3174 }
3175 if (stmt->type == STMT_LABEL && stmt->u.label == label)
3176 break;
3177 }
3178 } else {
3179 for (; ; stmt = stmt->down) {
3180 if (!stmt) {
3181 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3182 JSMSG_TOUGH_BREAK);
3183 return NULL;
3184 }
3185 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
3186 break;
3187 }
3188 }
3189 if (label)
3190 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3191 break;
3192
3193 case TOK_CONTINUE:
3194 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3195 if (!pn)
3196 return NULL;
3197 if (!MatchLabel(cx, ts, pn))
3198 return NULL;
3199 stmt = tc->topStmt;
3200 label = pn->pn_atom;
3201 if (label) {
3202 for (stmt2 = NULL; ; stmt = stmt->down) {
3203 if (!stmt) {
3204 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3205 JSMSG_LABEL_NOT_FOUND);
3206 return NULL;
3207 }
3208 if (stmt->type == STMT_LABEL) {
3209 if (stmt->u.label == label) {
3210 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
3211 js_ReportCompileErrorNumber(cx, ts, NULL,
3212 JSREPORT_ERROR,
3213 JSMSG_BAD_CONTINUE);
3214 return NULL;
3215 }
3216 break;
3217 }
3218 } else {
3219 stmt2 = stmt;
3220 }
3221 }
3222 } else {
3223 for (; ; stmt = stmt->down) {
3224 if (!stmt) {
3225 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3226 JSMSG_BAD_CONTINUE);
3227 return NULL;
3228 }
3229 if (STMT_IS_LOOP(stmt))
3230 break;
3231 }
3232 }
3233 if (label)
3234 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3235 break;
3236
3237 case TOK_WITH:
3238 pn = NewParseNode(cx, ts, PN_BINARY, tc);
3239 if (!pn)
3240 return NULL;
3241 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
3242 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
3243 if (!pn2)
3244 return NULL;
3245 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
3246 pn->pn_left = pn2;
3247
3248 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
3249 pn2 = Statement(cx, ts, tc);
3250 if (!pn2)
3251 return NULL;
3252 js_PopStatement(tc);
3253
3254 pn->pn_pos.end = pn2->pn_pos.end;
3255 pn->pn_right = pn2;
3256 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3257 return pn;
3258
3259 case TOK_VAR:
3260 pn = Variables(cx, ts, tc);
3261 if (!pn)
3262 return NULL;
3263
3264 /* Tell js_EmitTree to generate a final POP. */
3265 pn->pn_extra |= PNX_POPVAR;
3266 break;
3267
3268 #if JS_HAS_BLOCK_SCOPE
3269 case TOK_LET:
3270 {
3271 JSObject *obj;
3272 JSParsedObjectBox *blockpob;
3273
3274 /* Check for a let statement or let expression. */
3275 if (js_PeekToken(cx, ts) == TOK_LP) {
3276 pn = LetBlock(cx, ts, tc, JS_TRUE);
3277 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
3278 return pn;
3279
3280 /* Let expressions require automatic semicolon insertion. */
3281 JS_ASSERT(pn->pn_type == TOK_SEMI ||
3282 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
3283 break;
3284 }
3285
3286 /*
3287 * This is a let declaration. We must be directly under a block per
3288 * the proposed ES4 specs, but not an implicit block created due to
3289