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

Contents of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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


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