Bug 471703 - Don't optimize group assignment given holey RHS (r=igor).
[mozilla-central.git] / js / src / jsparse.cpp
blobcadc269b10882482e323597d730b0e5a7c427e7f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS parser.
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.
52 * This parser attempts no error recovery.
54 #include <stdlib.h>
55 #include <string.h>
56 #include <math.h>
57 #include "jstypes.h"
58 #include "jsstdint.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"
81 #if JS_HAS_XML_SUPPORT
82 #include "jsxml.h"
83 #endif
85 #if JS_HAS_DESTRUCTURING
86 #include "jsdhash.h"
87 #endif
90 * Asserts to verify assumptions behind pn_ macros.
92 #define pn_offsetof(m) offsetof(JSParseNode, m)
94 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
95 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
97 #undef pn_offsetof
100 * JS parsers, from lowest to highest precedence.
102 * Each parser takes a context, a token stream, and a tree context struct.
103 * Each returns a parse node tree or null on error.
106 typedef JSParseNode *
107 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
109 typedef JSParseNode *
110 JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
111 bool inLetHead);
113 typedef JSParseNode *
114 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
115 JSBool allowCallSyntax);
117 typedef JSParseNode *
118 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
119 JSTokenType tt, JSBool afterDot);
121 typedef JSParseNode *
122 JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
123 JSParseNode *pn1, JSBool *genexp);
125 static JSParser FunctionStmt;
126 static JSParser FunctionExpr;
127 static JSParser Statements;
128 static JSParser Statement;
129 static JSVariablesParser Variables;
130 static JSParser Expr;
131 static JSParser AssignExpr;
132 static JSParser CondExpr;
133 static JSParser OrExpr;
134 static JSParser AndExpr;
135 static JSParser BitOrExpr;
136 static JSParser BitXorExpr;
137 static JSParser BitAndExpr;
138 static JSParser EqExpr;
139 static JSParser RelExpr;
140 static JSParser ShiftExpr;
141 static JSParser AddExpr;
142 static JSParser MulExpr;
143 static JSParser UnaryExpr;
144 static JSMemberParser MemberExpr;
145 static JSPrimaryParser PrimaryExpr;
146 static JSParenParser ParenExpr;
149 * Insist that the next token be of type tt, or report errno and return null.
150 * NB: this macro uses cx and ts from its lexical environment.
152 #define MUST_MATCH_TOKEN(tt, errno) \
153 JS_BEGIN_MACRO \
154 if (js_GetToken(cx, ts) != tt) { \
155 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
156 return NULL; \
158 JS_END_MACRO
160 #ifdef METER_PARSENODES
161 static uint32 parsenodes = 0;
162 static uint32 maxparsenodes = 0;
163 static uint32 recyclednodes = 0;
164 #endif
166 void
167 JSParseNode::become(JSParseNode *pn2)
169 JS_ASSERT(!pn_defn);
170 JS_ASSERT(!pn2->pn_defn);
172 JS_ASSERT(!pn_used);
173 if (pn2->pn_used) {
174 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
175 while (*pnup != pn2)
176 pnup = &(*pnup)->pn_link;
177 *pnup = this;
178 pn_link = pn2->pn_link;
179 pn_used = true;
180 pn2->pn_link = NULL;
181 pn2->pn_used = false;
184 /* If this is a function node fix up the pn_funbox->node back-pointer. */
185 if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
186 pn2->pn_funbox->node = this;
188 pn_type = pn2->pn_type;
189 pn_op = pn2->pn_op;
190 pn_arity = pn2->pn_arity;
191 pn_u = pn2->pn_u;
192 pn2->clear();
195 void
196 JSParseNode::clear()
198 pn_type = TOK_EOF;
199 pn_op = JSOP_NOP;
200 pn_used = pn_defn = false;
201 pn_arity = PN_NULLARY;
204 bool
205 JSCompiler::init(const jschar *base, size_t length,
206 FILE *fp, const char *filename, uintN lineno)
208 JSContext *cx = context;
210 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
211 if (!js_InitTokenStream(cx, TS(this), base, length, fp, filename, lineno)) {
212 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
213 return false;
216 /* Root atoms and objects allocated for the parsed tree. */
217 JS_KEEP_ATOMS(cx->runtime);
218 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
219 return true;
222 JSCompiler::~JSCompiler()
224 JSContext *cx = context;
226 if (principals)
227 JSPRINCIPALS_DROP(cx, principals);
228 JS_ASSERT(tempRoot.u.compiler == this);
229 JS_POP_TEMP_ROOT(cx, &tempRoot);
230 JS_UNKEEP_ATOMS(cx->runtime);
231 js_CloseTokenStream(cx, TS(this));
232 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
235 void
236 JSCompiler::setPrincipals(JSPrincipals *prin)
238 JS_ASSERT(!principals);
239 if (prin)
240 JSPRINCIPALS_HOLD(context, prin);
241 principals = prin;
244 JSObjectBox *
245 JSCompiler::newObjectBox(JSObject *obj)
247 JS_ASSERT(obj);
250 * We use JSContext.tempPool to allocate parsed objects and place them on
251 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
252 * containing the entries must be alive until we are done with scanning,
253 * parsing and code generation for the whole script or top-level function.
255 JSObjectBox *objbox;
256 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
257 if (!objbox) {
258 js_ReportOutOfScriptQuota(context);
259 return NULL;
261 objbox->traceLink = traceListHead;
262 traceListHead = objbox;
263 objbox->emitLink = NULL;
264 objbox->object = obj;
265 return objbox;
268 JSFunctionBox *
269 JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
271 JS_ASSERT(obj);
272 JS_ASSERT(HAS_FUNCTION_CLASS(obj));
275 * We use JSContext.tempPool to allocate parsed objects and place them on
276 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
277 * containing the entries must be alive until we are done with scanning,
278 * parsing and code generation for the whole script or top-level function.
280 JSFunctionBox *funbox;
281 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
282 if (!funbox) {
283 js_ReportOutOfScriptQuota(context);
284 return NULL;
286 funbox->traceLink = traceListHead;
287 traceListHead = funbox;
288 funbox->emitLink = NULL;
289 funbox->object = obj;
290 funbox->node = fn;
291 funbox->siblings = tc->functionList;
292 tc->functionList = funbox;
293 ++tc->compiler->functionCount;
294 funbox->kids = NULL;
295 funbox->parent = tc->funbox;
296 funbox->queued = false;
297 funbox->level = tc->staticLevel;
298 funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
299 return funbox;
302 void
303 JSCompiler::trace(JSTracer *trc)
305 JSObjectBox *objbox;
307 JS_ASSERT(tempRoot.u.compiler == this);
308 objbox = traceListHead;
309 while (objbox) {
310 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
311 objbox = objbox->traceLink;
315 static void
316 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
318 static void
319 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
321 JSFunctionBox *funbox = pn->pn_funbox;
322 if (funbox) {
323 JS_ASSERT(funbox->node == pn);
324 funbox->node = NULL;
326 JSFunctionBox **funboxp = &tc->functionList;
327 while (*funboxp) {
328 if (*funboxp == funbox) {
329 *funboxp = funbox->siblings;
330 break;
332 funboxp = &(*funboxp)->siblings;
335 uint16 oldflags = tc->flags;
336 JSFunctionBox *oldlist = tc->functionList;
338 tc->flags = (uint16) funbox->tcflags;
339 tc->functionList = funbox->kids;
340 UnlinkFunctionBoxes(pn->pn_body, tc);
341 funbox->kids = tc->functionList;
342 tc->flags = oldflags;
343 tc->functionList = oldlist;
345 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
346 pn->pn_funbox = NULL;
350 static void
351 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
353 if (pn) {
354 switch (pn->pn_arity) {
355 case PN_NULLARY:
356 return;
357 case PN_UNARY:
358 UnlinkFunctionBoxes(pn->pn_kid, tc);
359 return;
360 case PN_BINARY:
361 UnlinkFunctionBoxes(pn->pn_left, tc);
362 UnlinkFunctionBoxes(pn->pn_right, tc);
363 return;
364 case PN_TERNARY:
365 UnlinkFunctionBoxes(pn->pn_kid1, tc);
366 UnlinkFunctionBoxes(pn->pn_kid2, tc);
367 UnlinkFunctionBoxes(pn->pn_kid3, tc);
368 return;
369 case PN_LIST:
370 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
371 UnlinkFunctionBoxes(pn2, tc);
372 return;
373 case PN_FUNC:
374 UnlinkFunctionBox(pn, tc);
375 return;
376 case PN_NAME:
377 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
378 return;
379 case PN_NAMESET:
380 UnlinkFunctionBoxes(pn->pn_tree, tc);
385 static void
386 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
388 static JSParseNode *
389 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
391 JSParseNode *next, **head;
393 if (!pn)
394 return NULL;
396 /* Catch back-to-back dup recycles. */
397 JS_ASSERT(pn != tc->compiler->nodeList);
398 next = pn->pn_next;
399 if (pn->pn_used || pn->pn_defn) {
401 * JSAtomLists own definition nodes along with their used-node chains.
402 * Defer recycling such nodes until we unwind to top level to avoid
403 * linkage overhead or (alternatively) unlinking runtime complexity.
404 * Yes, this means dead code can contribute to static analysis results!
406 * Do recycle kids here, since they are no longer needed.
408 pn->pn_next = NULL;
409 RecycleFuncNameKids(pn, tc);
410 } else {
411 UnlinkFunctionBoxes(pn, tc);
412 head = &tc->compiler->nodeList;
413 pn->pn_next = *head;
414 *head = pn;
415 #ifdef METER_PARSENODES
416 recyclednodes++;
417 #endif
419 return next;
422 static void
423 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
425 switch (pn->pn_arity) {
426 case PN_FUNC:
427 UnlinkFunctionBox(pn, tc);
428 /* FALL THROUGH */
430 case PN_NAME:
432 * Only a definition node might have a non-null strong pn_expr link
433 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
434 * Every node with the pn_used flag set has a non-null pn_lexdef
435 * weak reference to its definition node.
437 if (!pn->pn_used && pn->pn_expr) {
438 RecycleTree(pn->pn_expr, tc);
439 pn->pn_expr = NULL;
441 break;
443 default:
444 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
448 static JSParseNode *
449 NewOrRecycledNode(JSTreeContext *tc)
451 JSParseNode *pn, *pn2;
453 pn = tc->compiler->nodeList;
454 if (!pn) {
455 JSContext *cx = tc->compiler->context;
457 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
458 if (!pn)
459 js_ReportOutOfScriptQuota(cx);
460 } else {
461 tc->compiler->nodeList = pn->pn_next;
463 /* Recycle immediate descendents only, to save work and working set. */
464 switch (pn->pn_arity) {
465 case PN_FUNC:
466 RecycleTree(pn->pn_body, tc);
467 break;
468 case PN_LIST:
469 pn2 = pn->pn_head;
470 if (pn2) {
471 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
472 pn2 = pn2->pn_next;
473 if (pn2) {
474 pn2 = pn->pn_head;
475 do {
476 pn2 = RecycleTree(pn2, tc);
477 } while (pn2);
478 } else {
479 *pn->pn_tail = tc->compiler->nodeList;
480 tc->compiler->nodeList = pn->pn_head;
481 #ifdef METER_PARSENODES
482 recyclednodes += pn->pn_count;
483 #endif
484 break;
487 break;
488 case PN_TERNARY:
489 RecycleTree(pn->pn_kid1, tc);
490 RecycleTree(pn->pn_kid2, tc);
491 RecycleTree(pn->pn_kid3, tc);
492 break;
493 case PN_BINARY:
494 if (pn->pn_left != pn->pn_right)
495 RecycleTree(pn->pn_left, tc);
496 RecycleTree(pn->pn_right, tc);
497 break;
498 case PN_UNARY:
499 RecycleTree(pn->pn_kid, tc);
500 break;
501 case PN_NAME:
502 if (!pn->pn_used)
503 RecycleTree(pn->pn_expr, tc);
504 break;
505 case PN_NULLARY:
506 break;
509 if (pn) {
510 #ifdef METER_PARSENODES
511 parsenodes++;
512 if (parsenodes - recyclednodes > maxparsenodes)
513 maxparsenodes = parsenodes - recyclednodes;
514 #endif
515 pn->pn_used = pn->pn_defn = false;
516 memset(&pn->pn_u, 0, sizeof pn->pn_u);
517 pn->pn_next = NULL;
519 return pn;
522 static inline void
523 InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
525 pn->pn_type = type;
526 pn->pn_op = op;
527 pn->pn_arity = arity;
528 JS_ASSERT(!pn->pn_used);
529 JS_ASSERT(!pn->pn_defn);
530 pn->pn_next = pn->pn_link = NULL;
534 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
535 * temporary arena.
537 static JSParseNode *
538 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
540 JSParseNode *pn;
541 JSToken *tp;
543 pn = NewOrRecycledNode(tc);
544 if (!pn)
545 return NULL;
546 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
547 InitParseNode(pn, tp->type, JSOP_NOP, arity);
548 pn->pn_pos = tp->pos;
549 return pn;
552 static inline void
553 InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
555 pn->pn_expr = NULL;
556 pn->pn_cookie = FREE_UPVAR_COOKIE;
557 pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
558 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
559 pn->pn_dflags |= PND_BLOCKCHILD;
560 pn->pn_blockid = tc->blockid();
563 static JSParseNode *
564 NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc)
566 JSParseNode *pn;
568 pn = NewParseNode(PN_NAME, tc);
569 if (pn) {
570 pn->pn_atom = atom;
571 InitNameNodeCommon(pn, tc);
573 return pn;
576 static JSParseNode *
577 NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
578 JSTreeContext *tc)
580 JSParseNode *pn, *pn1, *pn2;
582 if (!left || !right)
583 return NULL;
586 * Flatten a left-associative (left-heavy) tree of a given operator into
587 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
589 if (PN_TYPE(left) == tt &&
590 PN_OP(left) == op &&
591 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
592 if (left->pn_arity != PN_LIST) {
593 pn1 = left->pn_left, pn2 = left->pn_right;
594 left->pn_arity = PN_LIST;
595 left->initList(pn1);
596 left->append(pn2);
597 if (tt == TOK_PLUS) {
598 if (pn1->pn_type == TOK_STRING)
599 left->pn_xflags |= PNX_STRCAT;
600 else if (pn1->pn_type != TOK_NUMBER)
601 left->pn_xflags |= PNX_CANTFOLD;
602 if (pn2->pn_type == TOK_STRING)
603 left->pn_xflags |= PNX_STRCAT;
604 else if (pn2->pn_type != TOK_NUMBER)
605 left->pn_xflags |= PNX_CANTFOLD;
608 left->append(right);
609 left->pn_pos.end = right->pn_pos.end;
610 if (tt == TOK_PLUS) {
611 if (right->pn_type == TOK_STRING)
612 left->pn_xflags |= PNX_STRCAT;
613 else if (right->pn_type != TOK_NUMBER)
614 left->pn_xflags |= PNX_CANTFOLD;
616 return left;
620 * Fold constant addition immediately, to conserve node space and, what's
621 * more, so js_FoldConstants never sees mixed addition and concatenation
622 * operations with more than one leading non-string operand in a PN_LIST
623 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
624 * to "3pt", not "12pt").
626 if (tt == TOK_PLUS &&
627 left->pn_type == TOK_NUMBER &&
628 right->pn_type == TOK_NUMBER) {
629 left->pn_dval += right->pn_dval;
630 left->pn_pos.end = right->pn_pos.end;
631 RecycleTree(right, tc);
632 return left;
635 pn = NewOrRecycledNode(tc);
636 if (!pn)
637 return NULL;
638 InitParseNode(pn, tt, op, PN_BINARY);
639 pn->pn_pos.begin = left->pn_pos.begin;
640 pn->pn_pos.end = right->pn_pos.end;
641 pn->pn_left = left;
642 pn->pn_right = right;
643 return pn;
646 #if JS_HAS_GETTER_SETTER
647 static JSTokenType
648 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
650 JSAtom *atom;
651 JSRuntime *rt;
652 JSOp op;
653 const char *name;
655 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
656 atom = CURRENT_TOKEN(ts).t_atom;
657 rt = cx->runtime;
658 if (atom == rt->atomState.getterAtom)
659 op = JSOP_GETTER;
660 else if (atom == rt->atomState.setterAtom)
661 op = JSOP_SETTER;
662 else
663 return TOK_NAME;
664 if (js_PeekTokenSameLine(cx, ts) != tt)
665 return TOK_NAME;
666 (void) js_GetToken(cx, ts);
667 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
668 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
669 JSMSG_BAD_GETTER_OR_SETTER,
670 (op == JSOP_GETTER)
671 ? js_getter_str
672 : js_setter_str);
673 return TOK_ERROR;
675 CURRENT_TOKEN(ts).t_op = op;
676 if (JS_HAS_STRICT_OPTION(cx)) {
677 name = js_AtomToPrintableString(cx, atom);
678 if (!name ||
679 !js_ReportCompileErrorNumber(cx, ts, NULL,
680 JSREPORT_WARNING | JSREPORT_STRICT,
681 JSMSG_DEPRECATED_USAGE,
682 name)) {
683 return TOK_ERROR;
686 return tt;
688 #endif
690 static bool
691 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
693 if (tc->blockidGen == JS_BIT(20)) {
694 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
695 JSMSG_NEED_DIET, "program");
696 return false;
698 blockid = tc->blockidGen++;
699 return true;
702 static bool
703 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
705 JS_ASSERT(tc->topStmt);
706 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
707 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
708 if (!GenerateBlockId(tc, tc->topStmt->blockid))
709 return false;
710 pn->pn_blockid = tc->topStmt->blockid;
711 return true;
715 * Parse a top-level JS script.
717 JSParseNode *
718 JSCompiler::parse(JSObject *chain)
721 * Protect atoms from being collected by a GC activation, which might
722 * - nest on this thread due to out of memory (the so-called "last ditch"
723 * GC attempted within js_NewGCThing), or
724 * - run for any reason on another thread if this thread is suspended on
725 * an object lock before it finishes generating bytecode into a script
726 * protected from the GC by a root or a stack frame reference.
728 JSTreeContext tc(this);
729 tc.scopeChain = chain;
730 if (!GenerateBlockId(&tc, tc.bodyid))
731 return NULL;
733 JSParseNode *pn = Statements(context, TS(this), &tc);
734 if (pn) {
735 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
736 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
737 JSMSG_SYNTAX_ERROR);
738 pn = NULL;
739 } else {
740 if (!js_FoldConstants(context, pn, &tc))
741 pn = NULL;
744 return pn;
747 static inline bool
748 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
751 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
752 * (0xffffffff) and other cookies with that level.
754 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
755 * practically speaking it leaves more than enough room for upvars. In fact
756 * we might want to split cookie fields giving fewer bits for skip and more
757 * for slot, but only based on evidence.
759 if (staticLevel >= FREE_STATIC_LEVEL) {
760 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
761 JSMSG_TOO_DEEP, js_function_str);
762 return false;
764 tc->staticLevel = staticLevel;
765 return true;
769 * Compile a top-level script.
771 JSScript *
772 JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
773 JSPrincipals *principals, uint32 tcflags,
774 const jschar *chars, size_t length,
775 FILE *file, const char *filename, uintN lineno,
776 JSString *source /* = NULL */)
778 JSCompiler jsc(cx, principals, callerFrame);
779 JSArenaPool codePool, notePool;
780 JSTokenType tt;
781 JSParseNode *pn;
782 uint32 scriptGlobals;
783 JSScript *script;
784 #ifdef METER_PARSENODES
785 void *sbrk(ptrdiff_t), *before = sbrk(0);
786 #endif
788 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
789 TCF_STATIC_LEVEL_MASK)));
792 * The scripted callerFrame can only be given for compile-and-go scripts
793 * and non-zero static level requires callerFrame.
795 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
796 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
798 if (!jsc.init(chars, length, file, filename, lineno))
799 return NULL;
801 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
802 &cx->scriptStackQuota);
803 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
804 &cx->scriptStackQuota);
806 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
808 MUST_FLOW_THROUGH("out");
809 cg.flags |= (uint16) tcflags;
810 cg.scopeChain = scopeChain;
811 if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
812 return NULL;
815 * If funbox is non-null after we create the new script, callerFrame->fun
816 * was saved in the 0th object table entry.
818 JSObjectBox *funbox = NULL;
820 if (tcflags & TCF_COMPILE_N_GO) {
821 if (source) {
823 * Save eval program source in script->atomMap.vector[0] for the
824 * eval cache (see obj_eval in jsobj.cpp).
826 JSAtom *atom = js_AtomizeString(cx, source, 0);
827 if (!atom || !cg.atomList.add(&jsc, atom))
828 return NULL;
831 if (callerFrame && callerFrame->fun) {
833 * An eval script in a caller frame needs to have its enclosing
834 * function captured in case it refers to an upvar, and someone
835 * wishes to decompile it while it's running.
837 funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
838 if (!funbox)
839 return NULL;
840 funbox->emitLink = cg.objectList.lastbox;
841 cg.objectList.lastbox = funbox;
842 cg.objectList.length++;
847 * Inline Statements to emit as we go to save AST space. We must generate
848 * our script-body blockid since we aren't calling Statements.
850 uint32 bodyid;
851 if (!GenerateBlockId(&cg, bodyid))
852 return NULL;
853 cg.bodyid = bodyid;
855 /* Null script early in case of error, to reduce our code footprint. */
856 script = NULL;
857 #if JS_HAS_XML_SUPPORT
858 pn = NULL;
859 bool onlyXML = true;
860 #endif
862 for (;;) {
863 jsc.tokenStream.flags |= TSF_OPERAND;
864 tt = js_PeekToken(cx, &jsc.tokenStream);
865 jsc.tokenStream.flags &= ~TSF_OPERAND;
866 if (tt <= TOK_EOF) {
867 if (tt == TOK_EOF)
868 break;
869 JS_ASSERT(tt == TOK_ERROR);
870 goto out;
873 pn = Statement(cx, &jsc.tokenStream, &cg);
874 if (!pn)
875 goto out;
876 JS_ASSERT(!cg.blockNode);
878 if (!js_FoldConstants(cx, pn, &cg))
879 goto out;
881 if (cg.functionList) {
882 if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
883 goto out;
884 cg.functionList = NULL;
887 if (!js_EmitTree(cx, &cg, pn))
888 goto out;
889 #if JS_HAS_XML_SUPPORT
890 if (PN_TYPE(pn) != TOK_SEMI ||
891 !pn->pn_kid ||
892 !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
893 onlyXML = false;
895 #endif
896 RecycleTree(pn, &cg);
899 #if JS_HAS_XML_SUPPORT
901 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
902 * For background, see:
904 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
906 if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
907 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
908 JSMSG_XML_WHOLE_PROGRAM);
909 goto out;
911 #endif
914 * Global variables and regexps share the index space with locals. Due to
915 * incremental code generation we need to patch the bytecode to adjust the
916 * local references to skip the globals.
918 scriptGlobals = cg.ngvars + cg.regexpList.length;
919 if (scriptGlobals != 0) {
920 jsbytecode *code, *end;
921 JSOp op;
922 const JSCodeSpec *cs;
923 uintN len, slot;
925 if (scriptGlobals >= SLOTNO_LIMIT)
926 goto too_many_slots;
927 code = CG_BASE(&cg);
928 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
929 JS_ASSERT(code < end);
930 op = (JSOp) *code;
931 cs = &js_CodeSpec[op];
932 len = (cs->length > 0)
933 ? (uintN) cs->length
934 : js_GetVariableBytecodeLength(code);
935 if (JOF_TYPE(cs->format) == JOF_LOCAL ||
936 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
938 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
939 * emitted only for a function.
941 JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
942 (op == JSOP_GETLOCALPROP));
943 slot = GET_SLOTNO(code);
944 slot += scriptGlobals;
945 if (slot >= SLOTNO_LIMIT)
946 goto too_many_slots;
947 SET_SLOTNO(code, slot);
952 #ifdef METER_PARSENODES
953 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
954 (char *)sbrk(0) - (char *)before,
955 parsenodes,
956 maxparsenodes,
957 parsenodes - recyclednodes);
958 before = sbrk(0);
959 #endif
962 * Nowadays the threaded interpreter needs a stop instruction, so we
963 * do have to emit that here.
965 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
966 goto out;
967 #ifdef METER_PARSENODES
968 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
969 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
970 #endif
971 #ifdef JS_ARENAMETER
972 JS_DumpArenaStats(stdout);
973 #endif
974 script = js_NewScriptFromCG(cx, &cg);
975 if (script && funbox)
976 script->flags |= JSSF_SAVED_CALLER_FUN;
978 #ifdef JS_SCOPE_DEPTH_METER
979 if (script) {
980 JSObject *obj = scopeChain;
981 uintN depth = 1;
982 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
983 ++depth;
984 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
986 #endif
988 out:
989 JS_FinishArenaPool(&codePool);
990 JS_FinishArenaPool(&notePool);
991 return script;
993 too_many_slots:
994 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
995 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
996 script = NULL;
997 goto out;
1001 * Insist on a final return before control flows out of pn. Try to be a bit
1002 * smart about loops: do {...; return e2;} while(0) at the end of a function
1003 * that contains an early return e1 will get a strict warning. Similarly for
1004 * iloops: while (true){...} is treated as though ... returns.
1006 #define ENDS_IN_OTHER 0
1007 #define ENDS_IN_RETURN 1
1008 #define ENDS_IN_BREAK 2
1010 static int
1011 HasFinalReturn(JSParseNode *pn)
1013 JSParseNode *pn2, *pn3;
1014 uintN rv, rv2, hasDefault;
1016 switch (pn->pn_type) {
1017 case TOK_LC:
1018 if (!pn->pn_head)
1019 return ENDS_IN_OTHER;
1020 return HasFinalReturn(pn->last());
1022 case TOK_IF:
1023 if (!pn->pn_kid3)
1024 return ENDS_IN_OTHER;
1025 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1027 case TOK_WHILE:
1028 pn2 = pn->pn_left;
1029 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1030 return ENDS_IN_RETURN;
1031 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1032 return ENDS_IN_RETURN;
1033 return ENDS_IN_OTHER;
1035 case TOK_DO:
1036 pn2 = pn->pn_right;
1037 if (pn2->pn_type == TOK_PRIMARY) {
1038 if (pn2->pn_op == JSOP_FALSE)
1039 return HasFinalReturn(pn->pn_left);
1040 if (pn2->pn_op == JSOP_TRUE)
1041 return ENDS_IN_RETURN;
1043 if (pn2->pn_type == TOK_NUMBER) {
1044 if (pn2->pn_dval == 0)
1045 return HasFinalReturn(pn->pn_left);
1046 return ENDS_IN_RETURN;
1048 return ENDS_IN_OTHER;
1050 case TOK_FOR:
1051 pn2 = pn->pn_left;
1052 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1053 return ENDS_IN_RETURN;
1054 return ENDS_IN_OTHER;
1056 case TOK_SWITCH:
1057 rv = ENDS_IN_RETURN;
1058 hasDefault = ENDS_IN_OTHER;
1059 pn2 = pn->pn_right;
1060 if (pn2->pn_type == TOK_LEXICALSCOPE)
1061 pn2 = pn2->expr();
1062 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1063 if (pn2->pn_type == TOK_DEFAULT)
1064 hasDefault = ENDS_IN_RETURN;
1065 pn3 = pn2->pn_right;
1066 JS_ASSERT(pn3->pn_type == TOK_LC);
1067 if (pn3->pn_head) {
1068 rv2 = HasFinalReturn(pn3->last());
1069 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1070 /* Falling through to next case or default. */;
1071 else
1072 rv &= rv2;
1075 /* If a final switch has no default case, we judge it harshly. */
1076 rv &= hasDefault;
1077 return rv;
1079 case TOK_BREAK:
1080 return ENDS_IN_BREAK;
1082 case TOK_WITH:
1083 return HasFinalReturn(pn->pn_right);
1085 case TOK_RETURN:
1086 return ENDS_IN_RETURN;
1088 case TOK_COLON:
1089 case TOK_LEXICALSCOPE:
1090 return HasFinalReturn(pn->expr());
1092 case TOK_THROW:
1093 return ENDS_IN_RETURN;
1095 case TOK_TRY:
1096 /* If we have a finally block that returns, we are done. */
1097 if (pn->pn_kid3) {
1098 rv = HasFinalReturn(pn->pn_kid3);
1099 if (rv == ENDS_IN_RETURN)
1100 return rv;
1103 /* Else check the try block and any and all catch statements. */
1104 rv = HasFinalReturn(pn->pn_kid1);
1105 if (pn->pn_kid2) {
1106 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1107 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1108 rv &= HasFinalReturn(pn2);
1110 return rv;
1112 case TOK_CATCH:
1113 /* Check this catch block's body. */
1114 return HasFinalReturn(pn->pn_kid3);
1116 case TOK_LET:
1117 /* Non-binary let statements are let declarations. */
1118 if (pn->pn_arity != PN_BINARY)
1119 return ENDS_IN_OTHER;
1120 return HasFinalReturn(pn->pn_right);
1122 default:
1123 return ENDS_IN_OTHER;
1127 static JSBool
1128 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1129 uintN anonerrnum)
1131 const char *name;
1133 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1134 if (tc->fun->atom) {
1135 name = js_AtomToPrintableString(cx, tc->fun->atom);
1136 } else {
1137 errnum = anonerrnum;
1138 name = NULL;
1140 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1141 errnum, name);
1144 static JSBool
1145 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1147 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1148 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1149 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1150 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1153 static JSParseNode *
1154 FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1156 JSStmtInfo stmtInfo;
1157 uintN oldflags, firstLine;
1158 JSParseNode *pn;
1160 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1161 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1162 stmtInfo.flags = SIF_BODY_BLOCK;
1164 oldflags = tc->flags;
1165 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1168 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1169 * later, because we may have not peeked in ts yet, so Statements won't
1170 * acquire a valid pn->pn_pos.begin from the current token.
1172 firstLine = ts->lineno;
1173 #if JS_HAS_EXPR_CLOSURES
1174 if (CURRENT_TOKEN(ts).type == TOK_LC) {
1175 pn = Statements(cx, ts, tc);
1176 } else {
1177 pn = NewParseNode(PN_UNARY, tc);
1178 if (pn) {
1179 pn->pn_kid = AssignExpr(cx, ts, tc);
1180 if (!pn->pn_kid) {
1181 pn = NULL;
1182 } else {
1183 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1184 ReportBadReturn(cx, tc, JSREPORT_ERROR,
1185 JSMSG_BAD_GENERATOR_RETURN,
1186 JSMSG_BAD_ANON_GENERATOR_RETURN);
1187 pn = NULL;
1188 } else {
1189 pn->pn_type = TOK_RETURN;
1190 pn->pn_op = JSOP_RETURN;
1191 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1196 #else
1197 pn = Statements(cx, ts, tc);
1198 #endif
1200 if (pn) {
1201 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1202 js_PopStatement(tc);
1203 pn->pn_pos.begin.lineno = firstLine;
1205 /* Check for falling off the end of a function that returns a value. */
1206 if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
1207 !CheckFinalReturn(cx, tc, pn)) {
1208 pn = NULL;
1212 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1213 return pn;
1216 static JSAtomListElement *
1217 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1219 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1220 if (!ale)
1221 return NULL;
1223 JSDefinition *dn = (JSDefinition *)
1224 NewNameNode(tc->compiler->context, TS(tc->compiler), pn->pn_atom, tc);
1225 if (!dn)
1226 return NULL;
1228 ALE_SET_DEFN(ale, dn);
1229 dn->pn_defn = true;
1230 dn->pn_dflags |= PND_FORWARD | PND_PLACEHOLDER;
1231 pn->pn_dflags |= PND_FORWARD;
1232 return ale;
1235 static bool
1236 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1238 JS_ASSERT(!pn->pn_used);
1239 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1241 JSHashEntry **hep;
1242 JSAtomListElement *ale = NULL;
1243 JSAtomList *list = NULL;
1245 if (let)
1246 ale = (list = &tc->decls)->rawLookup(atom, hep);
1247 if (!ale)
1248 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1250 if (ale) {
1251 JSDefinition *dn = ALE_DEFN(ale);
1252 if (dn != pn) {
1253 JSParseNode **pnup = &dn->dn_uses;
1254 JSParseNode *pnu;
1255 uintN start = let ? pn->pn_blockid : tc->bodyid;
1257 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1258 JS_ASSERT(pnu->pn_used);
1259 pnu->pn_lexdef = (JSDefinition *) pn;
1260 pn->pn_dflags |= pnu->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1261 pnup = &pnu->pn_link;
1264 if (pnu != dn->dn_uses) {
1265 *pnup = pn->dn_uses;
1266 pn->dn_uses = dn->dn_uses;
1267 dn->dn_uses = pnu;
1269 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1270 list->rawRemove(tc->compiler, ale, hep);
1275 ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1276 if (!ale)
1277 return false;
1278 ALE_SET_DEFN(ale, pn);
1279 pn->pn_defn = true;
1280 pn->pn_dflags &= ~PND_PLACEHOLDER;
1281 return true;
1284 static void
1285 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1287 JS_ASSERT(!pn->pn_used);
1288 JS_ASSERT(!pn->pn_defn);
1289 JS_ASSERT(pn != dn->dn_uses);
1290 pn->pn_link = dn->dn_uses;
1291 dn->dn_uses = pn;
1292 pn->pn_used = true;
1293 pn->pn_lexdef = dn;
1296 static void
1297 ForgetUse(JSParseNode *pn)
1299 if (!pn->pn_used) {
1300 JS_ASSERT(!pn->pn_defn);
1301 return;
1304 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1305 JSParseNode *pnu;
1306 while ((pnu = *pnup) != pn)
1307 pnup = &pnu->pn_link;
1308 *pnup = pn->pn_link;
1309 pn->pn_used = false;
1312 static JSParseNode *
1313 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1315 JSParseNode *lhs = NewOrRecycledNode(tc);
1316 if (!lhs)
1317 return NULL;
1318 *lhs = *pn;
1320 if (pn->pn_used) {
1321 JSDefinition *dn = pn->pn_lexdef;
1322 JSParseNode **pnup = &dn->dn_uses;
1324 while (*pnup != pn)
1325 pnup = &(*pnup)->pn_link;
1326 *pnup = lhs;
1327 lhs->pn_link = pn->pn_link;
1328 pn->pn_link = NULL;
1331 pn->pn_type = TOK_ASSIGN;
1332 pn->pn_op = JSOP_NOP;
1333 pn->pn_arity = PN_BINARY;
1334 pn->pn_used = pn->pn_defn = false;
1335 pn->pn_left = lhs;
1336 pn->pn_right = rhs;
1337 return lhs;
1340 static JSParseNode *
1341 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1344 * If dn is var, const, or let, and it has an initializer, then we must
1345 * rewrite it to be an assignment node, whose freshly allocated left-hand
1346 * side becomes a use of pn.
1348 if (dn->isBindingForm()) {
1349 JSParseNode *rhs = dn->expr();
1350 if (rhs) {
1351 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1352 if (!lhs)
1353 return NULL;
1354 //pn->dn_uses = lhs;
1355 dn = (JSDefinition *) lhs;
1358 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1359 } else if (dn->kind() == JSDefinition::FUNCTION) {
1360 JS_ASSERT(dn->isTopLevel());
1361 JS_ASSERT(dn->pn_op == JSOP_NOP);
1362 dn->pn_type = TOK_NAME;
1363 dn->pn_arity = PN_NAME;
1364 dn->pn_atom = atom;
1367 /* Now make dn no longer a definition, rather a use of pn. */
1368 JS_ASSERT(dn->pn_type == TOK_NAME);
1369 JS_ASSERT(dn->pn_arity == PN_NAME);
1370 JS_ASSERT(dn->pn_atom == atom);
1372 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1373 JS_ASSERT(pnu->pn_used);
1374 JS_ASSERT(!pnu->pn_defn);
1375 pnu->pn_lexdef = (JSDefinition *) pn;
1376 pn->pn_dflags |= pnu->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1378 pn->pn_dflags |= dn->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
1379 pn->dn_uses = dn;
1381 dn->pn_defn = false;
1382 dn->pn_used = true;
1383 dn->pn_lexdef = (JSDefinition *) pn;
1384 dn->pn_cookie = FREE_UPVAR_COOKIE;
1385 dn->pn_dflags &= ~PND_BOUND;
1386 return dn;
1389 static bool
1390 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1392 JSParseNode *argpn, *argsbody;
1394 /* Flag tc so we don't have to lookup arguments on every use. */
1395 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1396 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1399 * Make an argument definition node, distinguished by being in tc->decls
1400 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1401 * list node returned via pn->pn_body.
1403 argpn = NewNameNode(tc->compiler->context, TS(tc->compiler), atom, tc);
1404 if (!argpn)
1405 return false;
1406 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1408 /* Arguments are initialized by definition. */
1409 argpn->pn_dflags |= PND_INITIALIZED;
1410 if (!Define(argpn, atom, tc))
1411 return false;
1413 argsbody = pn->pn_body;
1414 if (!argsbody) {
1415 argsbody = NewParseNode(PN_LIST, tc);
1416 if (!argsbody)
1417 return false;
1418 argsbody->pn_type = TOK_ARGSBODY;
1419 argsbody->pn_op = JSOP_NOP;
1420 argsbody->makeEmpty();
1421 pn->pn_body = argsbody;
1423 argsbody->append(argpn);
1425 argpn->pn_op = JSOP_GETARG;
1426 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1427 argpn->pn_dflags |= PND_BOUND;
1428 return true;
1432 * Compile a JS function body, which might appear as the value of an event
1433 * handler attribute in an HTML <INPUT> tag.
1435 bool
1436 JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1437 const jschar *chars, size_t length,
1438 const char *filename, uintN lineno)
1440 JSCompiler jsc(cx, principals);
1442 if (!jsc.init(chars, length, NULL, filename, lineno))
1443 return false;
1445 /* No early return from after here until the js_FinishArenaPool calls. */
1446 JSArenaPool codePool, notePool;
1447 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
1448 &cx->scriptStackQuota);
1449 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
1450 &cx->scriptStackQuota);
1452 JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1453 funcg.flags |= TCF_IN_FUNCTION;
1454 funcg.fun = fun;
1455 if (!GenerateBlockId(&funcg, funcg.bodyid))
1456 return NULL;
1458 /* FIXME: make Function format the source for a function definition. */
1459 jsc.tokenStream.tokens[0].type = TOK_NAME;
1460 JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1461 if (fn) {
1462 fn->pn_body = NULL;
1463 fn->pn_cookie = FREE_UPVAR_COOKIE;
1465 uintN nargs = fun->nargs;
1466 if (nargs) {
1467 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1468 if (!names) {
1469 fn = NULL;
1470 } else {
1471 for (uintN i = 0; i < nargs; i++) {
1472 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1473 if (!DefineArg(fn, name, i, &funcg)) {
1474 fn = NULL;
1475 break;
1483 * Farble the body so that it looks like a block statement to js_EmitTree,
1484 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1485 * done parsing, we must fold constants, analyze any nested functions, and
1486 * generate code for this function, including a stop opcode at the end.
1488 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1489 JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1490 if (pn) {
1491 if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1492 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1493 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1494 pn = NULL;
1495 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1496 /* js_FoldConstants reported the error already. */
1497 pn = NULL;
1498 } else if (funcg.functionList &&
1499 !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1500 pn = NULL;
1501 } else {
1502 if (fn->pn_body) {
1503 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1504 fn->pn_body->append(pn);
1505 pn = fn->pn_body;
1508 if (!js_EmitFunctionScript(cx, &funcg, pn))
1509 pn = NULL;
1513 /* Restore saved state and release code generation arenas. */
1514 JS_FinishArenaPool(&codePool);
1515 JS_FinishArenaPool(&notePool);
1516 return pn != NULL;
1520 * Parameter block types for the several Binder functions. We use a common
1521 * helper function signature in order to share code among destructuring and
1522 * simple variable declaration parsers. In the destructuring case, the binder
1523 * function is called indirectly from the variable declaration parser by way
1524 * of CheckDestructuring and its friends.
1526 typedef struct BindData BindData;
1528 typedef JSBool
1529 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1531 struct BindData {
1532 JSParseNode *pn; /* name node for definition processing and
1533 error source coordinates */
1534 JSOp op; /* prolog bytecode or nop */
1535 Binder binder; /* binder, discriminates u */
1536 union {
1537 struct {
1538 uintN overflow;
1539 } let;
1543 static JSBool
1544 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1545 JSLocalKind localKind)
1547 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1550 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1551 * Instead 'var arguments' always restates the predefined property of the
1552 * activation objects whose name is 'arguments'. Assignment to such a
1553 * variable must be handled specially.
1555 if (atom == cx->runtime->atomState.argumentsAtom)
1556 return JS_TRUE;
1558 return js_AddLocal(cx, fun, atom, localKind);
1561 #if JS_HAS_DESTRUCTURING
1563 * Forward declaration to maintain top-down presentation.
1565 static JSParseNode *
1566 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1567 JSTokenType tt);
1569 static JSBool
1570 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1571 JSTreeContext *tc)
1573 JSAtomListElement *ale;
1574 JSParseNode *pn;
1576 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1577 ale = tc->decls.lookup(atom);
1578 pn = data->pn;
1579 if (!ale && !Define(pn, atom, tc))
1580 return JS_FALSE;
1582 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1583 if (localKind != JSLOCAL_NONE) {
1584 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1585 JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1586 return JS_FALSE;
1589 uintN index = tc->fun->u.i.nvars;
1590 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR))
1591 return JS_FALSE;
1592 pn->pn_op = JSOP_SETLOCAL;
1593 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1594 pn->pn_dflags |= PND_BOUND;
1595 return JS_TRUE;
1597 #endif /* JS_HAS_DESTRUCTURING */
1599 JSFunction *
1600 JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1602 JSObject *parent;
1603 JSFunction *fun;
1605 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1608 * Find the global compilation context in order to pre-set the newborn
1609 * function's parent slot to tc->scopeChain. If the global context is a
1610 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1611 * clear parent and proto.
1613 while (tc->parent)
1614 tc = tc->parent;
1615 parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1617 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1618 parent, atom);
1620 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1621 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1622 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1624 return fun;
1627 static JSBool
1628 MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1630 JSTokenType tt;
1632 ts->flags |= TSF_OPERAND;
1633 tt = js_PeekTokenSameLine(cx, ts);
1634 ts->flags &= ~TSF_OPERAND;
1635 if (tt == TOK_ERROR)
1636 return JS_FALSE;
1637 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1638 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1639 JSMSG_SEMI_BEFORE_STMNT);
1640 return JS_FALSE;
1642 (void) js_MatchToken(cx, ts, TOK_SEMI);
1643 return JS_TRUE;
1646 bool
1647 JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
1649 if (!markFunArgs(funbox, tcflags))
1650 return false;
1651 setFunctionKinds(funbox, tcflags);
1652 return true;
1656 * Mark as funargs any functions that reach up to one or more upvars across an
1657 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1659 * function f(o, p) {
1660 * o.m = function o_m(a) {
1661 * function g() { return p; }
1662 * function h() { return a; }
1663 * return g() + h();
1667 * but without this extra marking phase, function g will not be marked as a
1668 * funarg since it is called from within its parent scope. But g reaches up to
1669 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1670 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1671 * nor uses an upvar "above" o_m's level.
1673 * If function g itself contained lambdas that contained non-lambdas that reach
1674 * up above its level, then those non-lambdas would have to be marked too. This
1675 * process is potentially exponential in the number of functions, but generally
1676 * not so complex. But it can't be done during a single recursive traversal of
1677 * the funbox tree, so we must use a work queue.
1679 static void
1680 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1682 do {
1683 JSParseNode *fn = funbox->node;
1684 int fnlevel = level;
1687 * An eval can leak funbox, functions along its ancestor line, and its
1688 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1689 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1690 * already been marked as funargs by this point. Therefore we have to
1691 * flag only funbox->node and funbox->kids' nodes here.
1693 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1694 fn->setFunArg();
1695 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1696 kid->node->setFunArg();
1699 if (fn->isFunArg()) {
1700 queue->push(funbox);
1701 fnlevel = int(funbox->level);
1702 } else {
1703 JSParseNode *pn = fn->pn_body;
1705 if (pn->pn_type == TOK_UPVARS) {
1706 JSAtomList upvars(pn->pn_names);
1707 JS_ASSERT(upvars.count != 0);
1709 JSAtomListIterator iter(&upvars);
1710 JSAtomListElement *ale;
1712 while ((ale = iter()) != NULL) {
1713 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1715 if (!lexdep->isFreeVar() && int(lexdep->frameLevel()) <= fnlevel) {
1716 fn->setFunArg();
1717 queue->push(funbox);
1718 fnlevel = int(funbox->level);
1719 break;
1725 if (funbox->kids)
1726 FindFunArgs(funbox->kids, fnlevel, queue);
1727 } while ((funbox = funbox->siblings) != NULL);
1730 bool
1731 JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1733 JSFunctionBoxQueue queue;
1734 if (!queue.init(functionCount))
1735 return false;
1737 FindFunArgs(funbox, -1, &queue);
1738 while ((funbox = queue.pull()) != NULL) {
1739 JSParseNode *fn = funbox->node;
1740 JS_ASSERT(fn->isFunArg());
1742 JSParseNode *pn = fn->pn_body;
1743 if (pn->pn_type == TOK_UPVARS) {
1744 JSAtomList upvars(pn->pn_names);
1745 JS_ASSERT(upvars.count != 0);
1747 JSAtomListIterator iter(&upvars);
1748 JSAtomListElement *ale;
1750 while ((ale = iter()) != NULL) {
1751 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1753 if (!lexdep->isFreeVar() &&
1754 !lexdep->isFunArg() &&
1755 lexdep->kind() == JSDefinition::FUNCTION) {
1757 * Mark this formerly-Algol-like function as a funarg,
1758 * since it is referenced from a funarg and can no longer
1759 * use JSOP_{GET,CALL}UPVAR to access upvars.
1761 * Progress is guaranteed since we set PND_FUNARG here,
1762 * which suppresses revisiting this function (namely the
1763 * !lexdep->isFunArg() test just above).
1765 lexdep->setFunArg();
1767 JSFunctionBox *afunbox = lexdep->pn_funbox;
1768 queue.push(afunbox);
1771 * Walk over nested functions again, now that we have
1772 * changed the level across which it is unsafe to access
1773 * upvars using the runtime dynamic link (frame chain).
1775 if (afunbox->kids)
1776 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1781 return true;
1784 static uint32
1785 MinBlockId(JSParseNode *fn, uint32 id)
1787 if (fn->pn_blockid < id)
1788 return false;
1789 if (fn->pn_defn) {
1790 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1791 if (pn->pn_blockid < id)
1792 return false;
1795 return true;
1798 static bool
1799 OneBlockId(JSParseNode *fn, uint32 id)
1801 if (fn->pn_blockid != id)
1802 return false;
1803 if (fn->pn_defn) {
1804 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1805 if (pn->pn_blockid != id)
1806 return false;
1809 return true;
1812 void
1813 JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
1815 #ifdef JS_FUNCTION_METERING
1816 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1817 #else
1818 # define FUN_METER(x) ((void)0)
1819 #endif
1820 JSFunctionBox *parent = funbox->parent;
1822 for (;;) {
1823 JSParseNode *fn = funbox->node;
1825 if (funbox->kids)
1826 setFunctionKinds(funbox->kids, tcflags);
1828 JSParseNode *pn = fn->pn_body;
1829 JSFunction *fun = (JSFunction *) funbox->object;
1831 FUN_METER(allfun);
1832 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1833 FUN_METER(heavy);
1834 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
1835 } else if (pn->pn_type != TOK_UPVARS) {
1837 * No lexical dependencies => null closure, for best performance.
1838 * A null closure needs no scope chain, but alas we've coupled
1839 * principals-finding to scope (for good fundamental reasons, but
1840 * the implementation overloads the parent slot and we should fix
1841 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1843 * In more detail: the ES3 spec allows the implementation to create
1844 * "joined function objects", or not, at its discretion. But real-
1845 * world implementations always create unique function objects for
1846 * closures, and this can be detected via mutation. Open question:
1847 * do popular implementations create unique function objects for
1848 * null closures?
1850 * FIXME: bug 476950.
1852 FUN_METER(nofreeupvar);
1853 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1854 } else {
1855 JSAtomList upvars(pn->pn_names);
1856 JS_ASSERT(upvars.count != 0);
1858 JSAtomListIterator iter(&upvars);
1859 JSAtomListElement *ale;
1861 if (!fn->isFunArg()) {
1863 * This function is Algol-like, it never escapes. So long as it
1864 * does not assign to outer variables, it needs only an upvars
1865 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1866 * bytecode to reach up the frame stack at runtime based on
1867 * those upvars' cookies.
1869 * Any assignments to upvars from functions called by this one
1870 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1871 * which load from stack homes when interpreting or from native
1872 * stack slots when executing a trace.
1874 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1875 * nested function to assign to an outer lexical variable, so
1876 * we defer adding yet more code footprint in the absence of
1877 * evidence motivating these opcodes.
1879 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
1880 uintN nupvars = 0;
1883 * Check that at least one outer lexical binding was assigned
1884 * to (global variables don't count). This is conservative: we
1885 * could limit assignments to those in the current function,
1886 * but that's too much work. As with flat closures (handled
1887 * below), we optimize for the case where outer bindings are
1888 * not reassigned anywhere.
1890 while ((ale = iter()) != NULL) {
1891 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1893 if (!lexdep->isFreeVar()) {
1894 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
1895 ++nupvars;
1896 if (lexdep->isAssigned())
1897 break;
1900 if (!ale)
1901 mutation = false;
1903 if (nupvars == 0) {
1904 FUN_METER(onlyfreevar);
1905 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1906 } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
1908 * Algol-like functions can read upvars using the dynamic
1909 * link (cx->fp/fp->down). They do not need to entrain and
1910 * search their environment.
1912 FUN_METER(display);
1913 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1914 } else {
1915 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
1916 FUN_METER(setupvar);
1918 } else {
1919 uintN nupvars = 0;
1922 * For each lexical dependency from this closure to an outer
1923 * binding, analyze whether it is safe to copy the binding's
1924 * value into a flat closure slot when the closure is formed.
1926 while ((ale = iter()) != NULL) {
1927 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1929 if (!lexdep->isFreeVar()) {
1930 ++nupvars;
1933 * Consider the current function (the lambda, innermost
1934 * below) using a var x defined two static levels up:
1936 * function f() {
1937 * // z = g();
1938 * var x = 42;
1939 * function g() {
1940 * return function () { return x; };
1942 * return g();
1945 * So long as (1) the initialization in 'var x = 42'
1946 * dominates all uses of g and (2) x is not reassigned,
1947 * it is safe to optimize the lambda to a flat closure.
1948 * Uncommenting the early call to g makes it unsafe to
1949 * so optimize (z could name a global setter that calls
1950 * its argument).
1952 JSFunctionBox *afunbox = funbox;
1953 uintN lexdepLevel = lexdep->frameLevel();
1955 JS_ASSERT(lexdepLevel <= funbox->level);
1956 while (afunbox->level != lexdepLevel) {
1957 afunbox = afunbox->parent;
1960 * We can't form a flat closure that reaches up
1961 * across a funarg that encloses the closure, or
1962 * into the top level (to a 'let' variable in an
1963 * enclosing block in global code; this is the
1964 * !afunbox case).
1966 if (!afunbox || afunbox->node->isFunArg()) {
1967 JS_ASSERT_IF(!afunbox,
1968 lexdep->isLet() ||
1969 (!(tcflags & TCF_IN_FUNCTION) &&
1970 callerFrame && callerFrame->fun));
1971 goto break2;
1976 * If afunbox's function (which is at the same level as
1977 * lexdep) is not a lambda, it will be hoisted, so it
1978 * could capture the undefined value that by default
1979 * initializes var/let/const bindings. And if lexdep is
1980 * a function that comes at (meaning a function refers
1981 * to its own name) or strictly after afunbox, we also
1982 * break to defeat the flat closure optimization.
1984 JSFunction *afun = (JSFunction *) afunbox->object;
1985 if (!(afun->flags & JSFUN_LAMBDA)) {
1986 if (lexdep->isBindingForm())
1987 break;
1988 if (lexdep->pn_pos >= afunbox->node->pn_pos)
1989 break;
1992 if (!lexdep->isInitialized())
1993 break;
1995 JSDefinition::Kind lexdepKind = lexdep->kind();
1996 if (lexdepKind != JSDefinition::CONST) {
1997 if (lexdep->isAssigned())
1998 break;
2001 * Any formal could be mutated behind our back via
2002 * the arguments object, so deoptimize if the outer
2003 * function uses arguments.
2005 * In a Function constructor call where the final
2006 * argument -- the body source for the function to
2007 * create -- contains a nested function definition
2008 * or expression, afunbox->parent will be null. The
2009 * body source might use |arguments| outside of any
2010 * nested functions it may contain, so we have to
2011 * check the tcflags parameter that was passed in
2012 * from JSCompiler::compileFunctionBody.
2014 if (lexdepKind == JSDefinition::ARG &&
2015 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2016 TCF_FUN_USES_ARGUMENTS)) {
2017 break;
2022 * Check quick-and-dirty dominance relation. Function
2023 * definitions dominate their uses thanks to hoisting.
2024 * Other binding forms hoist as undefined, of course,
2025 * so check forward-reference and blockid relations.
2027 if (lexdepKind != JSDefinition::FUNCTION) {
2028 if (lexdep->isForward())
2029 break;
2032 * Watch out for code such as
2034 * (function () {
2035 * ...
2036 * var jQuery = ... = function (...) {
2037 * return new jQuery.foo.bar(baz);
2039 * ...
2040 * })();
2042 * where the jQuery var is not reassigned, but of
2043 * course is not initialized at the time that the
2044 * would-be-flat closure containing the jQuery
2045 * upvar is formed.
2047 if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2048 break;
2050 if (lexdep->isTopLevel()
2051 ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2052 : !lexdep->isBlockChild() ||
2053 !afunbox->node->isBlockChild() ||
2054 !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2055 break;
2061 break2:
2062 if (nupvars == 0) {
2063 FUN_METER(onlyfreevar);
2064 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2065 } else if (!ale) {
2067 * We made it all the way through the upvar loop, so it's
2068 * safe to optimize to a flat closure.
2070 FUN_METER(flat);
2071 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2072 switch (PN_OP(fn)) {
2073 case JSOP_DEFFUN:
2074 fn->pn_op = JSOP_DEFFUN_FC;
2075 break;
2076 case JSOP_DEFLOCALFUN:
2077 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2078 break;
2079 case JSOP_LAMBDA:
2080 fn->pn_op = JSOP_LAMBDA_FC;
2081 break;
2082 default:
2083 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2084 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2086 } else {
2087 FUN_METER(badfunarg);
2092 if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2093 if (pn->pn_type != TOK_UPVARS) {
2094 if (parent)
2095 parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2096 } else {
2097 JSAtomList upvars(pn->pn_names);
2098 JS_ASSERT(upvars.count != 0);
2100 JSAtomListIterator iter(&upvars);
2101 JSAtomListElement *ale;
2104 * One or more upvars cannot be safely snapshot into a flat
2105 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2106 * all upvars, and for each non-free upvar, ensure that its
2107 * containing function has been flagged as heavyweight.
2109 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2110 * generating any code for a tree of nested functions.
2112 while ((ale = iter()) != NULL) {
2113 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2115 if (!lexdep->isFreeVar()) {
2116 JSFunctionBox *afunbox = funbox->parent;
2117 uintN lexdepLevel = lexdep->frameLevel();
2119 while (afunbox) {
2121 * NB: afunbox->level is the static level of
2122 * the definition or expression of the function
2123 * parsed into afunbox, not the static level of
2124 * its body. Therefore we must add 1 to match
2125 * lexdep's level to find the afunbox whose
2126 * body contains the lexdep definition.
2128 if (afunbox->level + 1U == lexdepLevel) {
2129 afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2130 break;
2132 afunbox = afunbox->parent;
2134 if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2135 tcflags |= TCF_FUN_HEAVYWEIGHT;
2141 funbox = funbox->siblings;
2142 if (!funbox)
2143 break;
2144 JS_ASSERT(funbox->parent == parent);
2146 #undef FUN_METER
2149 const char js_argument_str[] = "argument";
2150 const char js_variable_str[] = "variable";
2151 const char js_unknown_str[] = "unknown";
2153 const char *
2154 JSDefinition::kindString(Kind kind)
2156 static const char *table[] = {
2157 js_var_str, js_const_str, js_let_str,
2158 js_function_str, js_argument_str, js_unknown_str
2161 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2162 return table[kind];
2165 static JSFunctionBox *
2166 EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2167 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2169 JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2170 if (!fun)
2171 return NULL;
2173 /* Create box for fun->object early to protect against last-ditch GC. */
2174 JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2175 if (!funbox)
2176 return NULL;
2178 /* Initialize non-default members of funtc. */
2179 funtc->flags |= funbox->tcflags;
2180 funtc->blockidGen = tc->blockidGen;
2181 if (!GenerateBlockId(funtc, funtc->bodyid))
2182 return NULL;
2183 funtc->fun = fun;
2184 funtc->funbox = funbox;
2185 funtc->parent = tc;
2186 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2187 return NULL;
2189 return funbox;
2192 static bool
2193 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2194 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2196 tc->blockidGen = funtc->blockidGen;
2198 fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2200 fn->pn_dflags |= PND_INITIALIZED;
2201 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2202 fn->pn_dflags & PND_TOPLEVEL);
2203 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2204 fn->pn_dflags |= PND_BLOCKCHILD;
2207 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2208 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2209 * params and body. We do this only if there are lexical dependencies not
2210 * satisfied by the function's declarations, to avoid penalizing functions
2211 * that use only their arguments and other local bindings.
2213 if (funtc->lexdeps.count != 0) {
2214 JSAtomListIterator iter(&funtc->lexdeps);
2215 JSAtomListElement *ale;
2216 int foundCallee = 0;
2218 while ((ale = iter()) != NULL) {
2219 JSAtom *atom = ALE_ATOM(ale);
2220 JSDefinition *dn = ALE_DEFN(ale);
2221 JS_ASSERT(dn->isPlaceholder());
2223 if (atom == funAtom && lambda != 0) {
2224 dn->pn_op = JSOP_CALLEE;
2225 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2226 dn->pn_dflags |= PND_BOUND;
2229 * If this named function expression uses its own name other
2230 * than to call itself, flag this function as using arguments,
2231 * as if it had used arguments.callee instead of its own name.
2233 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2234 * we are out of tcflags bits at the moment. If it deoptimizes
2235 * code unfairly (see JSCompiler::setFunctionKinds, where this
2236 * flag is interpreted in its broader sense, not only to mean
2237 * "this function might leak arguments.callee"), we can perhaps
2238 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2239 * use that more precisely, both here and for unnamed function
2240 * expressions.
2242 if (dn->isFunArg())
2243 fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
2244 foundCallee = 1;
2245 continue;
2248 if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2249 dn->isAssigned()) {
2251 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2252 * any use of dn in funtc assigns. See NoteLValue for the easy
2253 * backward-reference case; this is the hard forward-reference
2254 * case where we pay a higher price.
2256 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2257 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2258 fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2259 break;
2264 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2265 if (!outer_ale)
2266 outer_ale = tc->lexdeps.lookup(atom);
2267 if (outer_ale) {
2269 * Insert dn's uses list at the front of outer_dn's list.
2271 * Without loss of generality or correctness, we allow a dn to
2272 * be in inner and outer lexdeps, since the purpose of lexdeps
2273 * is one-pass coordination of name use and definition across
2274 * functions, and if different dn's are used we'll merge lists
2275 * when leaving the inner function.
2277 * The dn == outer_dn case arises with generator expressions
2278 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2279 * case), and nowhere else, currently.
2281 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2283 if (dn != outer_dn) {
2284 JSParseNode **pnup = &dn->dn_uses;
2285 JSParseNode *pnu;
2287 while ((pnu = *pnup) != NULL) {
2288 pnu->pn_lexdef = outer_dn;
2289 pnup = &pnu->pn_link;
2293 * Make dn be a use that redirects to outer_dn, because we
2294 * can't replace dn with outer_dn in all the pn_namesets in
2295 * the AST where it may be. Instead we make it forward to
2296 * outer_dn. See JSDefinition::resolve.
2298 *pnup = outer_dn->dn_uses;
2299 outer_dn->dn_uses = dn;
2300 outer_dn->pn_dflags |= (dn->pn_dflags & ~PND_PLACEHOLDER);
2301 dn->pn_defn = false;
2302 dn->pn_used = true;
2303 dn->pn_lexdef = outer_dn;
2305 } else {
2306 /* Add an outer lexical dependency for ale's definition. */
2307 outer_ale = tc->lexdeps.add(tc->compiler, atom);
2308 if (!outer_ale)
2309 return false;
2310 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2314 if (funtc->lexdeps.count - foundCallee != 0) {
2315 JSParseNode *body = fn->pn_body;
2317 fn->pn_body = NewParseNode(PN_NAMESET, tc);
2318 if (!fn->pn_body)
2319 return false;
2321 fn->pn_body->pn_type = TOK_UPVARS;
2322 fn->pn_body->pn_pos = body->pn_pos;
2323 if (foundCallee)
2324 funtc->lexdeps.remove(tc->compiler, funAtom);
2325 fn->pn_body->pn_names = funtc->lexdeps;
2326 fn->pn_body->pn_tree = body;
2329 funtc->lexdeps.clear();
2332 return true;
2335 static JSParseNode *
2336 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2337 uintN lambda)
2339 JSOp op;
2340 JSParseNode *pn, *body, *result;
2341 JSTokenType tt;
2342 JSAtom *funAtom;
2343 JSAtomListElement *ale;
2344 #if JS_HAS_DESTRUCTURING
2345 JSParseNode *item, *list = NULL;
2346 bool destructuringArg = false, duplicatedArg = false;
2347 #endif
2349 /* Make a TOK_FUNCTION node. */
2350 #if JS_HAS_GETTER_SETTER
2351 op = CURRENT_TOKEN(ts).t_op;
2352 #endif
2353 pn = NewParseNode(PN_FUNC, tc);
2354 if (!pn)
2355 return NULL;
2356 pn->pn_body = NULL;
2357 pn->pn_cookie = FREE_UPVAR_COOKIE;
2360 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2361 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2363 * Also treat function sub-statements (non-lambda, non-top-level functions)
2364 * as escaping funargs, since we can't statically analyze their definitions
2365 * and uses.
2367 bool topLevel = tc->atTopLevel();
2368 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2370 /* Scan the optional function name into funAtom. */
2371 ts->flags |= TSF_KEYWORD_IS_NAME;
2372 tt = js_GetToken(cx, ts);
2373 ts->flags &= ~TSF_KEYWORD_IS_NAME;
2374 if (tt == TOK_NAME) {
2375 funAtom = CURRENT_TOKEN(ts).t_atom;
2376 } else {
2377 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
2378 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2379 JSMSG_SYNTAX_ERROR);
2380 return NULL;
2382 funAtom = NULL;
2383 js_UngetToken(ts);
2387 * Record names for function statements in tc->decls so we know when to
2388 * avoid optimizing variable references that might name a function.
2390 if (lambda == 0 && funAtom) {
2391 ale = tc->decls.lookup(funAtom);
2392 if (ale) {
2393 JSDefinition *dn = ALE_DEFN(ale);
2394 JSDefinition::Kind dn_kind = dn->kind();
2396 JS_ASSERT(!dn->pn_used);
2397 JS_ASSERT(dn->pn_defn);
2399 if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2400 const char *name = js_AtomToPrintableString(cx, funAtom);
2401 if (!name ||
2402 !js_ReportCompileErrorNumber(cx, ts, NULL,
2403 (dn_kind != JSDefinition::CONST)
2404 ? JSREPORT_WARNING | JSREPORT_STRICT
2405 : JSREPORT_ERROR,
2406 JSMSG_REDECLARED_VAR,
2407 JSDefinition::kindString(dn_kind),
2408 name)) {
2409 return NULL;
2413 if (topLevel) {
2414 ALE_SET_DEFN(ale, pn);
2415 pn->pn_defn = true;
2416 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2418 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2419 return NULL;
2421 } else if (topLevel) {
2423 * If this function was used before it was defined, claim the
2424 * pre-created definition node for this function that PrimaryExpr
2425 * put in tc->lexdeps on first forward reference, and recycle pn.
2427 JSHashEntry **hep;
2429 ale = tc->lexdeps.rawLookup(funAtom, hep);
2430 if (ale) {
2431 JSDefinition *fn = ALE_DEFN(ale);
2433 JS_ASSERT(fn->pn_defn);
2434 fn->pn_type = TOK_FUNCTION;
2435 fn->pn_arity = PN_FUNC;
2436 fn->pn_pos.begin = pn->pn_pos.begin;
2437 fn->pn_body = NULL;
2438 fn->pn_cookie = FREE_UPVAR_COOKIE;
2440 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2441 RecycleTree(pn, tc);
2442 pn = fn;
2445 if (!Define(pn, funAtom, tc))
2446 return NULL;
2450 * A function nested at top level inside another's body needs only a
2451 * local variable to bind its name to its value, and not an activation
2452 * object property (it might also need the activation property, if the
2453 * outer function contains with statements, e.g., but the stack slot
2454 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2455 * JSOP_GETLOCAL bytecode).
2457 if (topLevel) {
2458 pn->pn_dflags |= PND_TOPLEVEL;
2460 if (tc->flags & TCF_IN_FUNCTION) {
2461 JSLocalKind localKind;
2462 uintN index;
2465 * Define a local in the outer function so that BindNameToSlot
2466 * can properly optimize accesses. Note that we need a local
2467 * variable, not an argument, for the function statement. Thus
2468 * we add a variable even if a parameter with the given name
2469 * already exists.
2471 localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2472 switch (localKind) {
2473 case JSLOCAL_NONE:
2474 case JSLOCAL_ARG:
2475 index = tc->fun->u.i.nvars;
2476 if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2477 return NULL;
2478 /* FALL THROUGH */
2480 case JSLOCAL_VAR:
2481 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2482 pn->pn_dflags |= PND_BOUND;
2483 break;
2485 default:;
2491 /* Initialize early for possible flags mutation via DestructuringExpr. */
2492 JSTreeContext funtc(tc->compiler);
2494 JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2495 if (!funbox)
2496 return NULL;
2498 JSFunction *fun = (JSFunction *) funbox->object;
2500 #if JS_HAS_GETTER_SETTER
2501 if (op != JSOP_NOP)
2502 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2503 #endif
2505 /* Now parse formal argument list and compute fun->nargs. */
2506 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2507 if (!js_MatchToken(cx, ts, TOK_RP)) {
2508 do {
2509 tt = js_GetToken(cx, ts);
2510 switch (tt) {
2511 #if JS_HAS_DESTRUCTURING
2512 case TOK_LB:
2513 case TOK_LC:
2515 BindData data;
2516 JSParseNode *lhs, *rhs;
2517 jsint slot;
2519 /* See comment below in the TOK_NAME case. */
2520 if (duplicatedArg)
2521 goto report_dup_and_destructuring;
2522 destructuringArg = true;
2525 * A destructuring formal parameter turns into one or more
2526 * local variables initialized from properties of a single
2527 * anonymous positional parameter, so here we must tweak our
2528 * binder and its data.
2530 data.pn = NULL;
2531 data.op = JSOP_DEFVAR;
2532 data.binder = BindDestructuringArg;
2533 lhs = DestructuringExpr(cx, &data, &funtc, tt);
2534 if (!lhs)
2535 return NULL;
2538 * Adjust fun->nargs to count the single anonymous positional
2539 * parameter that is to be destructured.
2541 slot = fun->nargs;
2542 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
2543 return NULL;
2546 * Synthesize a destructuring assignment from the single
2547 * anonymous positional parameter into the destructuring
2548 * left-hand-side expression and accumulate it in list.
2550 rhs = NewNameNode(cx, ts, cx->runtime->atomState.emptyAtom, &funtc);
2551 if (!rhs)
2552 return NULL;
2553 rhs->pn_type = TOK_NAME;
2554 rhs->pn_op = JSOP_GETARG;
2555 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2556 rhs->pn_dflags |= PND_BOUND;
2558 item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2559 if (!item)
2560 return NULL;
2561 if (!list) {
2562 list = NewParseNode(PN_LIST, &funtc);
2563 if (!list)
2564 return NULL;
2565 list->pn_type = TOK_COMMA;
2566 list->makeEmpty();
2568 list->append(item);
2569 break;
2571 #endif /* JS_HAS_DESTRUCTURING */
2573 case TOK_NAME:
2576 * Check for a duplicate parameter name, a "feature" that
2577 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2578 * soon to be an ES3.1 strict error.
2580 * Further, if any argument is a destructuring pattern, forbid
2581 * duplicates. We will report the error either now if we have
2582 * seen a destructuring pattern already, or later when we find
2583 * the first pattern.
2585 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2586 if (JS_HAS_STRICT_OPTION(cx) &&
2587 js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2588 #if JS_HAS_DESTRUCTURING
2589 if (destructuringArg)
2590 goto report_dup_and_destructuring;
2591 duplicatedArg = true;
2592 #endif
2593 const char *name = js_AtomToPrintableString(cx, atom);
2594 if (!name ||
2595 !js_ReportCompileErrorNumber(cx, TS(funtc.compiler),
2596 NULL,
2597 JSREPORT_WARNING |
2598 JSREPORT_STRICT,
2599 JSMSG_DUPLICATE_FORMAL,
2600 name)) {
2601 return NULL;
2604 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2605 return NULL;
2606 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2607 return NULL;
2608 break;
2611 default:
2612 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2613 JSMSG_MISSING_FORMAL);
2614 /* FALL THROUGH */
2615 case TOK_ERROR:
2616 return NULL;
2618 #if JS_HAS_DESTRUCTURING
2619 report_dup_and_destructuring:
2620 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
2621 JSREPORT_ERROR,
2622 JSMSG_DESTRUCT_DUP_ARG);
2623 return NULL;
2624 #endif
2626 } while (js_MatchToken(cx, ts, TOK_COMMA));
2628 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2631 #if JS_HAS_EXPR_CLOSURES
2632 ts->flags |= TSF_OPERAND;
2633 tt = js_GetToken(cx, ts);
2634 ts->flags &= ~TSF_OPERAND;
2635 if (tt != TOK_LC) {
2636 js_UngetToken(ts);
2637 fun->flags |= JSFUN_EXPR_CLOSURE;
2639 #else
2640 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2641 #endif
2643 body = FunctionBody(cx, ts, &funtc);
2644 if (!body)
2645 return NULL;
2647 #if JS_HAS_EXPR_CLOSURES
2648 if (tt == TOK_LC)
2649 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2650 else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2651 return NULL;
2652 #else
2653 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2654 #endif
2655 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2657 #if JS_HAS_DESTRUCTURING
2659 * If there were destructuring formal parameters, prepend the initializing
2660 * comma expression that we synthesized to body. If the body is a lexical
2661 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2662 * parameter destructuring code without bracing the decompilation of the
2663 * function body's lexical scope.
2665 if (list) {
2666 if (body->pn_arity != PN_LIST) {
2667 JSParseNode *block;
2669 block = NewParseNode(PN_LIST, tc);
2670 if (!block)
2671 return NULL;
2672 block->pn_type = TOK_SEQ;
2673 block->pn_pos = body->pn_pos;
2674 block->initList(body);
2676 body = block;
2679 item = NewParseNode(PN_UNARY, tc);
2680 if (!item)
2681 return NULL;
2683 item->pn_type = TOK_SEMI;
2684 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2685 item->pn_kid = list;
2686 item->pn_next = body->pn_head;
2687 body->pn_head = item;
2688 if (body->pn_tail == &body->pn_head)
2689 body->pn_tail = &item->pn_next;
2690 ++body->pn_count;
2691 body->pn_xflags |= PNX_DESTRUCT;
2693 #endif
2696 * If we collected flags that indicate nested heavyweight functions, or
2697 * this function contains heavyweight-making statements (with statement,
2698 * visible eval call, or assignment to 'arguments'), flag the function as
2699 * heavyweight (requiring a call object per invocation).
2701 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2702 fun->flags |= JSFUN_HEAVYWEIGHT;
2703 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2704 } else {
2706 * If this function is a named statement function not at top-level
2707 * (i.e. not a top-level function definiton or expression), then our
2708 * enclosing function, if any, must be heavyweight.
2710 if (!topLevel && lambda == 0 && funAtom)
2711 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2714 result = pn;
2715 if (lambda != 0) {
2717 * ECMA ed. 3 standard: function expression, possibly anonymous.
2719 op = JSOP_LAMBDA;
2720 } else if (!funAtom) {
2722 * If this anonymous function definition is *not* embedded within a
2723 * larger expression, we treat it as an expression statement, not as
2724 * a function declaration -- and not as a syntax error (as ECMA-262
2725 * Edition 3 would have it). Backward compatibility must trump all,
2726 * unless JSOPTION_ANONFUNFIX is set.
2728 result = NewParseNode(PN_UNARY, tc);
2729 if (!result)
2730 return NULL;
2731 result->pn_type = TOK_SEMI;
2732 result->pn_pos = pn->pn_pos;
2733 result->pn_kid = pn;
2734 op = JSOP_LAMBDA;
2735 } else if (!topLevel) {
2737 * ECMA ed. 3 extension: a function expression statement not at the
2738 * top level, e.g., in a compound statement such as the "then" part
2739 * of an "if" statement, binds a closure only if control reaches that
2740 * sub-statement.
2742 op = JSOP_DEFFUN;
2743 } else {
2744 op = JSOP_NOP;
2747 funbox->kids = funtc.functionList;
2749 pn->pn_funbox = funbox;
2750 pn->pn_op = op;
2751 if (pn->pn_body)
2752 pn->pn_body->append(body);
2753 else
2754 pn->pn_body = body;
2756 pn->pn_blockid = tc->blockid();
2758 if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2759 return NULL;
2761 return result;
2764 static JSParseNode *
2765 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2767 return FunctionDef(cx, ts, tc, 0);
2770 static JSParseNode *
2771 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2773 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
2777 * Parse the statements in a block, creating a TOK_LC node that lists the
2778 * statements' trees. If called from block-parsing code, the caller must
2779 * match { before and } after.
2781 static JSParseNode *
2782 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2784 JSParseNode *pn, *pn2, *saveBlock;
2785 JSTokenType tt;
2787 JS_CHECK_RECURSION(cx, return NULL);
2789 pn = NewParseNode(PN_LIST, tc);
2790 if (!pn)
2791 return NULL;
2792 pn->pn_type = TOK_LC;
2793 pn->makeEmpty();
2794 pn->pn_blockid = tc->blockid();
2795 saveBlock = tc->blockNode;
2796 tc->blockNode = pn;
2798 for (;;) {
2799 ts->flags |= TSF_OPERAND;
2800 tt = js_PeekToken(cx, ts);
2801 ts->flags &= ~TSF_OPERAND;
2802 if (tt <= TOK_EOF || tt == TOK_RC) {
2803 if (tt == TOK_ERROR) {
2804 if (ts->flags & TSF_EOF)
2805 ts->flags |= TSF_UNEXPECTED_EOF;
2806 return NULL;
2808 break;
2810 pn2 = Statement(cx, ts, tc);
2811 if (!pn2) {
2812 if (ts->flags & TSF_EOF)
2813 ts->flags |= TSF_UNEXPECTED_EOF;
2814 return NULL;
2817 if (pn2->pn_type == TOK_FUNCTION) {
2819 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2820 * level function definitions that should be processed before the
2821 * rest of nodes.
2823 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2824 * is relevant only for function definitions not at top-level,
2825 * which we call function statements.
2827 if (tc->atTopLevel())
2828 pn->pn_xflags |= PNX_FUNCDEFS;
2829 else
2830 tc->flags |= TCF_HAS_FUNCTION_STMT;
2832 pn->append(pn2);
2836 * Handle the case where there was a let declaration under this block. If
2837 * it replaced tc->blockNode with a new block node then we must refresh pn
2838 * and then restore tc->blockNode.
2840 if (tc->blockNode != pn)
2841 pn = tc->blockNode;
2842 tc->blockNode = saveBlock;
2844 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2845 return pn;
2848 static JSParseNode *
2849 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2851 JSParseNode *pn;
2853 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
2854 pn = ParenExpr(cx, ts, tc, NULL, NULL);
2855 if (!pn)
2856 return NULL;
2857 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
2860 * Check for (a = b) and warn about possible (a == b) mistype iff b's
2861 * operator has greater precedence than ==.
2863 if (pn->pn_type == TOK_ASSIGN &&
2864 pn->pn_op == JSOP_NOP &&
2865 pn->pn_right->pn_type > TOK_EQOP)
2867 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2868 JSREPORT_WARNING | JSREPORT_STRICT,
2869 JSMSG_EQUAL_AS_ASSIGN,
2870 "")) {
2871 return NULL;
2874 return pn;
2877 static JSBool
2878 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
2880 JSAtom *label;
2881 JSTokenType tt;
2883 tt = js_PeekTokenSameLine(cx, ts);
2884 if (tt == TOK_ERROR)
2885 return JS_FALSE;
2886 if (tt == TOK_NAME) {
2887 (void) js_GetToken(cx, ts);
2888 label = CURRENT_TOKEN(ts).t_atom;
2889 } else {
2890 label = NULL;
2892 pn->pn_atom = label;
2893 return JS_TRUE;
2896 static JSBool
2897 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
2899 JSParseNode *pn;
2900 JSObject *blockObj;
2901 JSAtomListElement *ale;
2902 jsint n;
2905 * Top-level 'let' is the same as 'var' currently -- this may change in a
2906 * successor standard to ES3.1 that specifies 'let'.
2908 JS_ASSERT(!tc->atTopLevel());
2910 pn = data->pn;
2911 blockObj = tc->blockChain;
2912 ale = tc->decls.lookup(atom);
2913 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
2914 const char *name = js_AtomToPrintableString(cx, atom);
2915 if (name) {
2916 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
2917 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
2918 (ale && ALE_DEFN(ale)->isConst())
2919 ? js_const_str
2920 : js_variable_str,
2921 name);
2923 return JS_FALSE;
2926 n = OBJ_BLOCK_COUNT(cx, blockObj);
2927 if (n == JS_BIT(16)) {
2928 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
2929 JSREPORT_ERROR, data->let.overflow);
2930 return JS_FALSE;
2934 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
2935 * This is balanced by PopStatement, defined immediately below.
2937 if (!Define(pn, atom, tc, true))
2938 return JS_FALSE;
2941 * Assign block-local index to pn->pn_cookie right away, encoding it as an
2942 * upvar cookie whose skip tells the current static level. The emitter will
2943 * adjust the node's slot based on its stack depth model -- and, for global
2944 * and eval code, JSCompiler::compileScript will adjust the slot again to
2945 * include script->nfixed.
2947 pn->pn_op = JSOP_GETLOCAL;
2948 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
2949 pn->pn_dflags |= PND_LET | PND_BOUND;
2952 * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
2953 * property before storing pn in a reserved slot, since block_reserveSlots
2954 * depends on OBJ_SCOPE(blockObj)->entryCount.
2956 if (!js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), JSVAL_VOID,
2957 NULL, NULL,
2958 JSPROP_ENUMERATE |
2959 JSPROP_PERMANENT |
2960 JSPROP_SHARED,
2961 SPROP_HAS_SHORTID, (int16) n, NULL)) {
2962 return JS_FALSE;
2966 * Store pn temporarily in what would be reserved slots in a cloned block
2967 * object (once the prototype's final population is known, after all 'let'
2968 * bindings for this block have been parsed). We will free these reserved
2969 * slots in jsemit.cpp:EmitEnterBlock.
2971 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
2972 if (slot >= STOBJ_NSLOTS(blockObj) &&
2973 !js_ReallocSlots(cx, blockObj, slot + 1, JS_FALSE)) {
2974 return JS_FALSE;
2976 blockObj->map->freeslot = slot + 1;
2977 STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn));
2978 return JS_TRUE;
2981 static void
2982 PopStatement(JSTreeContext *tc)
2984 JSStmtInfo *stmt = tc->topStmt;
2986 if (stmt->flags & SIF_SCOPE) {
2987 JSObject *obj = stmt->blockObj;
2988 JSScope *scope = OBJ_SCOPE(obj);
2989 JS_ASSERT(scope->object == obj);
2991 for (JSScopeProperty *sprop = scope->lastProp; sprop; sprop = sprop->parent) {
2992 JSAtom *atom = JSID_TO_ATOM(sprop->id);
2994 /* Beware the empty destructuring dummy. */
2995 if (atom == tc->compiler->context->runtime->atomState.emptyAtom)
2996 continue;
2997 tc->decls.remove(tc->compiler, atom);
3000 js_PopStatement(tc);
3003 static inline bool
3004 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3006 while (stmt->downScope) {
3007 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3008 if (!stmt)
3009 return false;
3010 if (stmt->type == STMT_BLOCK)
3011 return true;
3013 return false;
3016 static JSBool
3017 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3019 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3020 JSParseNode *pn = data->pn;
3022 if (stmt && stmt->type == STMT_WITH) {
3023 pn->pn_op = JSOP_NAME;
3024 return JS_TRUE;
3027 JSAtomListElement *ale = tc->decls.lookup(atom);
3028 JSOp op = data->op;
3030 if (stmt || ale) {
3031 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3032 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3033 const char *name;
3035 if (dn_kind == JSDefinition::ARG) {
3036 name = js_AtomToPrintableString(cx, atom);
3037 if (!name)
3038 return JS_FALSE;
3040 if (op == JSOP_DEFCONST) {
3041 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3042 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3043 name);
3044 return JS_FALSE;
3046 if (!js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3047 JSREPORT_WARNING | JSREPORT_STRICT,
3048 JSMSG_VAR_HIDES_ARG, name)) {
3049 return JS_FALSE;
3051 } else {
3052 if (JS_HAS_STRICT_OPTION(cx)
3053 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3054 : op == JSOP_DEFCONST ||
3055 dn_kind == JSDefinition::CONST ||
3056 (dn_kind == JSDefinition::LET &&
3057 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom)))) {
3058 name = js_AtomToPrintableString(cx, atom);
3059 if (!name ||
3060 !js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3061 (op != JSOP_DEFCONST &&
3062 dn_kind != JSDefinition::CONST &&
3063 dn_kind != JSDefinition::LET)
3064 ? JSREPORT_WARNING | JSREPORT_STRICT
3065 : JSREPORT_ERROR,
3066 JSMSG_REDECLARED_VAR,
3067 JSDefinition::kindString(dn_kind),
3068 name)) {
3069 return JS_FALSE;
3075 if (!ale) {
3076 if (!Define(pn, atom, tc))
3077 return JS_FALSE;
3078 } else {
3080 * A var declaration never recreates an existing binding, it restates
3081 * it and possibly reinitializes its value. Beware that if pn becomes a
3082 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3083 * const (typically a const would ;-), then pn must be rewritten into a
3084 * TOK_ASSIGN node. See Variables, further below.
3086 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3087 * There the x definition is hoisted but the x = 2 assignment mutates
3088 * the block-local binding of x.
3090 JSDefinition *dn = ALE_DEFN(ale);
3092 if (!pn->pn_used) {
3093 /* Make pnu be a fresh name node that uses dn. */
3094 JSParseNode *pnu = pn;
3096 if (pn->pn_defn) {
3097 pnu = NewNameNode(cx, TS(tc->compiler), atom, tc);
3098 if (!pnu)
3099 return JS_FALSE;
3102 LinkUseToDef(pnu, dn, tc);
3103 pnu->pn_op = JSOP_NAME;
3106 while (dn->kind() == JSDefinition::LET) {
3107 do {
3108 ale = ALE_NEXT(ale);
3109 } while (ale && ALE_ATOM(ale) != atom);
3110 if (!ale)
3111 break;
3112 dn = ALE_DEFN(ale);
3115 if (ale) {
3116 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3117 dn->kind() == JSDefinition::CONST);
3118 return JS_TRUE;
3122 * A var or const that is shadowed by one or more let bindings of the
3123 * same name, but that has not been declared until this point, must be
3124 * hoisted above the let bindings.
3126 if (!pn->pn_defn) {
3127 JSHashEntry **hep;
3129 ale = tc->lexdeps.rawLookup(atom, hep);
3130 if (ale) {
3131 pn = ALE_DEFN(ale);
3132 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
3133 } else {
3134 JSParseNode *pn2 = NewNameNode(cx, TS(tc->compiler), atom, tc);
3135 if (!pn2)
3136 return JS_FALSE;
3138 /* The token stream may be past the location for pn. */
3139 pn2->pn_type = TOK_NAME;
3140 pn2->pn_pos = pn->pn_pos;
3141 pn = pn2;
3143 pn->pn_op = JSOP_NAME;
3146 ale = tc->decls.add(tc->compiler, atom, JSAtomList::HOIST);
3147 if (!ale)
3148 return JS_FALSE;
3149 ALE_SET_DEFN(ale, pn);
3150 pn->pn_defn = true;
3151 pn->pn_dflags &= ~PND_PLACEHOLDER;
3154 if (data->op == JSOP_DEFCONST)
3155 pn->pn_dflags |= PND_CONST;
3157 if (!(tc->flags & TCF_IN_FUNCTION)) {
3159 * If we are generating global or eval-called-from-global code, bind a
3160 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3161 * up global variable access by memoizing name-to-slot mappings in the
3162 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3163 * can't be done due to a pre-existing property of the same name as the
3164 * var or const but incompatible attributes/getter/setter/etc, these
3165 * ops devolve to JSOP_NAME, etc.
3167 * For now, don't try to lookup eval frame variables at compile time.
3168 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3169 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3171 pn->pn_op = JSOP_NAME;
3172 if ((tc->flags & TCF_COMPILING) && !tc->compiler->callerFrame) {
3173 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3175 /* Index atom so we can map fast global number to name. */
3176 ale = cg->atomList.add(tc->compiler, atom);
3177 if (!ale)
3178 return JS_FALSE;
3180 /* Defend against cg->ngvars 16-bit overflow. */
3181 uintN slot = ALE_INDEX(ale);
3182 if ((slot + 1) >> 16)
3183 return JS_TRUE;
3185 if ((uint16)(slot + 1) > cg->ngvars)
3186 cg->ngvars = (uint16)(slot + 1);
3188 pn->pn_op = JSOP_GETGVAR;
3189 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3190 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3192 return JS_TRUE;
3195 if (atom == cx->runtime->atomState.argumentsAtom) {
3196 pn->pn_op = JSOP_ARGUMENTS;
3197 pn->pn_dflags |= PND_BOUND;
3198 return JS_TRUE;
3201 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3202 if (localKind == JSLOCAL_NONE) {
3204 * Property not found in current variable scope: we have not seen this
3205 * variable before. Define a new local variable by adding a property to
3206 * the function's scope and allocating one slot in the function's vars
3207 * frame. Any locals declared in a with statement body are handled at
3208 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3209 * and heavyweight-function-local vars.
3211 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3213 uintN index = tc->fun->u.i.nvars;
3214 if (!BindLocalVariable(cx, tc->fun, atom, localKind))
3215 return JS_FALSE;
3216 pn->pn_op = JSOP_GETLOCAL;
3217 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3218 pn->pn_dflags |= PND_BOUND;
3219 return JS_TRUE;
3222 if (localKind == JSLOCAL_ARG) {
3223 /* We checked errors and strict warnings earlier -- see above. */
3224 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3225 } else {
3226 /* Not an argument, must be a redeclared local var. */
3227 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3229 pn->pn_op = JSOP_NAME;
3230 return JS_TRUE;
3233 static JSBool
3234 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3236 JSParseNode *pn2;
3238 JS_ASSERT(pn->pn_arity == PN_LIST);
3239 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3240 pn2 = pn->pn_head;
3241 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3242 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR, msg);
3243 return JS_FALSE;
3245 pn->pn_op = JSOP_SETCALL;
3246 return JS_TRUE;
3249 static void
3250 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3252 if (pn->pn_used) {
3253 JSDefinition *dn = pn->pn_lexdef;
3256 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3257 * occur as direct kids of the same block with no forward refs to x.
3259 if (dn->isBlockChild() &&
3260 pn->isBlockChild() &&
3261 dn->pn_blockid == pn->pn_blockid &&
3262 !(~dn->pn_dflags & (PND_INITIALIZED | PND_FORWARD)) &&
3263 dn->dn_uses == pn) {
3264 dflag = PND_INITIALIZED;
3267 dn->pn_dflags |= dflag;
3269 if (dn->frameLevel() != tc->staticLevel) {
3271 * The above condition takes advantage of the all-ones nature of
3272 * FREE_UPVAR_COOKIE, and the reserved frame level JS_BITMASK(16).
3273 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3275 JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3276 dn->frameLevel() < tc->staticLevel);
3277 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3281 pn->pn_dflags |= dflag;
3283 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3284 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3287 #if JS_HAS_DESTRUCTURING
3289 static JSBool
3290 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3291 JSTreeContext *tc)
3293 JSAtom *atom;
3296 * Destructuring is a form of assignment, so just as for an initialized
3297 * simple variable, we must check for assignment to 'arguments' and flag
3298 * the enclosing function (if any) as heavyweight.
3300 JS_ASSERT(pn->pn_type == TOK_NAME);
3301 atom = pn->pn_atom;
3302 if (atom == cx->runtime->atomState.argumentsAtom)
3303 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3305 data->pn = pn;
3306 if (!data->binder(cx, data, atom, tc))
3307 return JS_FALSE;
3310 * Select the appropriate name-setting opcode, respecting eager selection
3311 * done by the data->binder function.
3313 if (pn->pn_dflags & PND_BOUND) {
3314 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3315 ? JSOP_SETNAME
3316 : (pn->pn_dflags & PND_GVAR)
3317 ? JSOP_SETGVAR
3318 : JSOP_SETLOCAL;
3319 } else {
3320 pn->pn_op = (data->op == JSOP_DEFCONST)
3321 ? JSOP_SETCONST
3322 : JSOP_SETNAME;
3325 if (data->op == JSOP_DEFCONST)
3326 pn->pn_dflags |= PND_CONST;
3328 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3329 return JS_TRUE;
3333 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3334 * LHS expression except a destructuring initialiser, and R is on the stack.
3335 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3336 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3337 * then push its property name QN. At this point the stack looks like
3339 * [... R, R[P], QB, QN]
3341 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3342 * its operands with left-hand side above right-hand side:
3344 * [rval, lval, xval]
3346 * and pops all three values, setting lval[xval] = rval. But we cannot select
3347 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3348 * which can be optimized further. So we select JSOP_SETNAME.
3350 static JSBool
3351 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3353 while (pn->pn_type == TOK_RP)
3354 pn = pn->pn_kid;
3356 switch (pn->pn_type) {
3357 case TOK_NAME:
3358 NoteLValue(cx, pn, tc);
3359 /* FALL THROUGH */
3361 case TOK_DOT:
3362 case TOK_LB:
3363 pn->pn_op = JSOP_SETNAME;
3364 break;
3366 #if JS_HAS_LVALUE_RETURN
3367 case TOK_LP:
3368 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3369 return JS_FALSE;
3370 break;
3371 #endif
3373 #if JS_HAS_XML_SUPPORT
3374 case TOK_UNARYOP:
3375 if (pn->pn_op == JSOP_XMLNAME) {
3376 pn->pn_op = JSOP_BINDXMLNAME;
3377 break;
3379 /* FALL THROUGH */
3380 #endif
3382 default:
3383 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3384 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3385 return JS_FALSE;
3388 return JS_TRUE;
3391 typedef struct FindPropValData {
3392 uint32 numvars; /* # of destructuring vars in left side */
3393 uint32 maxstep; /* max # of steps searching right side */
3394 JSDHashTable table; /* hash table for O(1) right side search */
3395 } FindPropValData;
3397 typedef struct FindPropValEntry {
3398 JSDHashEntryHdr hdr;
3399 JSParseNode *pnkey;
3400 JSParseNode *pnval;
3401 } FindPropValEntry;
3403 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3404 JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
3405 ((pnkey)->pn_type == TOK_NUMBER || \
3406 (pnkey)->pn_type == TOK_STRING || \
3407 (pnkey)->pn_type == TOK_NAME))
3409 static JSDHashNumber
3410 HashFindPropValKey(JSDHashTable *table, const void *key)
3412 const JSParseNode *pnkey = (const JSParseNode *)key;
3414 ASSERT_VALID_PROPERTY_KEY(pnkey);
3415 return (pnkey->pn_type == TOK_NUMBER)
3416 ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
3417 JSDOUBLE_LO32(pnkey->pn_dval))
3418 : ATOM_HASH(pnkey->pn_atom);
3421 static JSBool
3422 MatchFindPropValEntry(JSDHashTable *table,
3423 const JSDHashEntryHdr *entry,
3424 const void *key)
3426 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3427 const JSParseNode *pnkey = (const JSParseNode *)key;
3429 ASSERT_VALID_PROPERTY_KEY(pnkey);
3430 return pnkey->pn_type == fpve->pnkey->pn_type &&
3431 ((pnkey->pn_type == TOK_NUMBER)
3432 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3433 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3436 static const JSDHashTableOps FindPropValOps = {
3437 JS_DHashAllocTable,
3438 JS_DHashFreeTable,
3439 HashFindPropValKey,
3440 MatchFindPropValEntry,
3441 JS_DHashMoveEntryStub,
3442 JS_DHashClearEntryStub,
3443 JS_DHashFinalizeStub,
3444 NULL
3447 #define STEP_HASH_THRESHOLD 10
3448 #define BIG_DESTRUCTURING 5
3449 #define BIG_OBJECT_INIT 20
3451 static JSParseNode *
3452 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3454 FindPropValEntry *entry;
3455 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3456 uint32 step;
3458 /* If we have a hash table, use it as the sole source of truth. */
3459 if (data->table.ops) {
3460 entry = (FindPropValEntry *)
3461 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3462 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3465 /* If pn is not an object initialiser node, we can't do anything here. */
3466 if (pn->pn_type != TOK_RC)
3467 return NULL;
3470 * We must search all the way through pn's list, to handle the case of an
3471 * id duplicated for two or more property initialisers.
3473 pnhit = NULL;
3474 step = 0;
3475 ASSERT_VALID_PROPERTY_KEY(pnid);
3476 pnhead = pn->pn_head;
3477 if (pnid->pn_type == TOK_NUMBER) {
3478 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3479 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3480 if (pnprop->pn_op == JSOP_NOP) {
3481 pnkey = pnprop->pn_left;
3482 ASSERT_VALID_PROPERTY_KEY(pnkey);
3483 if (pnkey->pn_type == TOK_NUMBER &&
3484 pnkey->pn_dval == pnid->pn_dval) {
3485 pnhit = pnprop;
3487 ++step;
3490 } else {
3491 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3492 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3493 if (pnprop->pn_op == JSOP_NOP) {
3494 pnkey = pnprop->pn_left;
3495 ASSERT_VALID_PROPERTY_KEY(pnkey);
3496 if (pnkey->pn_type == pnid->pn_type &&
3497 pnkey->pn_atom == pnid->pn_atom) {
3498 pnhit = pnprop;
3500 ++step;
3504 if (!pnhit)
3505 return NULL;
3507 /* Hit via full search -- see whether it's time to create the hash table. */
3508 JS_ASSERT(!data->table.ops);
3509 if (step > data->maxstep) {
3510 data->maxstep = step;
3511 if (step >= STEP_HASH_THRESHOLD &&
3512 data->numvars >= BIG_DESTRUCTURING &&
3513 pn->pn_count >= BIG_OBJECT_INIT &&
3514 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3515 sizeof(FindPropValEntry),
3516 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3518 for (pn = pnhead; pn; pn = pn->pn_next) {
3519 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3520 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3521 entry = (FindPropValEntry *)
3522 JS_DHashTableOperate(&data->table, pn->pn_left,
3523 JS_DHASH_ADD);
3524 entry->pnval = pn->pn_right;
3528 return pnhit->pn_right;
3532 * If data is null, the caller is AssignExpr and instead of binding variables,
3533 * we specialize lvalues in the propery value positions of the left-hand side.
3534 * If right is null, just check for well-formed lvalues.
3536 static JSBool
3537 CheckDestructuring(JSContext *cx, BindData *data,
3538 JSParseNode *left, JSParseNode *right,
3539 JSTreeContext *tc)
3541 JSBool ok;
3542 FindPropValData fpvd;
3543 JSParseNode *lhs, *rhs, *pn, *pn2;
3545 if (left->pn_type == TOK_ARRAYCOMP) {
3546 js_ReportCompileErrorNumber(cx, TS(tc->compiler), left,
3547 JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
3548 return JS_FALSE;
3551 #if JS_HAS_DESTRUCTURING_SHORTHAND
3552 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3553 js_ReportCompileErrorNumber(cx, TS(tc->compiler), right,
3554 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3555 return JS_FALSE;
3557 #endif
3559 fpvd.table.ops = NULL;
3560 lhs = left->pn_head;
3561 if (left->pn_type == TOK_RB) {
3562 rhs = (right && right->pn_type == left->pn_type)
3563 ? right->pn_head
3564 : NULL;
3566 while (lhs) {
3567 pn = lhs, pn2 = rhs;
3568 if (!data) {
3569 /* Skip parenthesization if not in a variable declaration. */
3570 while (pn->pn_type == TOK_RP)
3571 pn = pn->pn_kid;
3572 if (pn2) {
3573 while (pn2->pn_type == TOK_RP)
3574 pn2 = pn2->pn_kid;
3578 /* Nullary comma is an elision; binary comma is an expression.*/
3579 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3580 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3581 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3582 } else {
3583 if (data) {
3584 if (pn->pn_type != TOK_NAME)
3585 goto no_var_name;
3587 ok = BindDestructuringVar(cx, data, pn, tc);
3588 } else {
3589 ok = BindDestructuringLHS(cx, pn, tc);
3592 if (!ok)
3593 goto out;
3596 lhs = lhs->pn_next;
3597 if (rhs)
3598 rhs = rhs->pn_next;
3600 } else {
3601 JS_ASSERT(left->pn_type == TOK_RC);
3602 fpvd.numvars = left->pn_count;
3603 fpvd.maxstep = 0;
3604 rhs = NULL;
3606 while (lhs) {
3607 JS_ASSERT(lhs->pn_type == TOK_COLON);
3608 pn = lhs->pn_right;
3609 if (!data) {
3610 /* Skip parenthesization if not in a variable declaration. */
3611 while (pn->pn_type == TOK_RP)
3612 pn = pn->pn_kid;
3615 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3616 if (right) {
3617 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3618 if (rhs && !data) {
3619 while (rhs->pn_type == TOK_RP)
3620 rhs = rhs->pn_kid;
3624 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3625 } else if (data) {
3626 if (pn->pn_type != TOK_NAME)
3627 goto no_var_name;
3629 ok = BindDestructuringVar(cx, data, pn, tc);
3630 } else {
3631 ok = BindDestructuringLHS(cx, pn, tc);
3633 if (!ok)
3634 goto out;
3636 lhs = lhs->pn_next;
3641 * The catch/finally handler implementation in the interpreter assumes
3642 * that any operation that introduces a new scope (like a "let" or "with"
3643 * block) increases the stack depth. This way, it is possible to restore
3644 * the scope chain based on stack depth of the handler alone. "let" with
3645 * an empty destructuring pattern like in
3647 * let [] = 1;
3649 * would violate this assumption as the there would be no let locals to
3650 * store on the stack. To satisfy it we add an empty property to such
3651 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3652 * slots, would be always positive.
3654 * Note that we add such a property even if the block has locals due to
3655 * later let declarations in it. We optimize for code simplicity here,
3656 * not the fastest runtime performance with empty [] or {}.
3658 if (data &&
3659 data->binder == BindLet &&
3660 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3661 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3662 ATOM_TO_JSID(cx->runtime->
3663 atomState.emptyAtom),
3664 JSVAL_VOID, NULL, NULL,
3665 JSPROP_ENUMERATE |
3666 JSPROP_PERMANENT |
3667 JSPROP_SHARED,
3668 SPROP_HAS_SHORTID, 0, NULL);
3669 if (!ok)
3670 goto out;
3673 ok = JS_TRUE;
3675 out:
3676 if (fpvd.table.ops)
3677 JS_DHashTableFinish(&fpvd.table);
3678 return ok;
3680 no_var_name:
3681 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
3682 JSMSG_NO_VARIABLE_NAME);
3683 ok = JS_FALSE;
3684 goto out;
3687 static JSParseNode *
3688 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
3689 JSTokenType tt)
3691 JSTokenStream *ts;
3692 JSParseNode *pn;
3694 ts = TS(tc->compiler);
3695 ts->flags |= TSF_DESTRUCTURING;
3696 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3697 ts->flags &= ~TSF_DESTRUCTURING;
3698 if (!pn)
3699 return NULL;
3700 if (!CheckDestructuring(cx, data, pn, NULL, tc))
3701 return NULL;
3702 return pn;
3706 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3707 * This function assumes the cloned tree is for use in the same statement and
3708 * binding context as the original tree.
3710 static JSParseNode *
3711 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
3713 JSParseNode *pn, *pn2, *opn2;
3715 pn = NewOrRecycledNode(tc);
3716 if (!pn)
3717 return NULL;
3718 pn->pn_type = opn->pn_type;
3719 pn->pn_pos = opn->pn_pos;
3720 pn->pn_op = opn->pn_op;
3721 pn->pn_used = opn->pn_used;
3722 pn->pn_defn = opn->pn_defn;
3723 pn->pn_arity = opn->pn_arity;
3725 switch (pn->pn_arity) {
3726 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3728 case PN_FUNC:
3729 NULLCHECK(pn->pn_funbox =
3730 tc->compiler->newFunctionBox(opn->pn_funbox->object, pn, tc));
3731 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
3732 pn->pn_cookie = opn->pn_cookie;
3733 pn->pn_dflags = opn->pn_dflags;
3734 pn->pn_blockid = opn->pn_blockid;
3735 break;
3737 case PN_LIST:
3738 pn->makeEmpty();
3739 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
3740 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
3741 pn->append(pn2);
3743 pn->pn_xflags = opn->pn_xflags;
3744 break;
3746 case PN_TERNARY:
3747 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
3748 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
3749 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
3750 break;
3752 case PN_BINARY:
3753 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
3754 if (opn->pn_right != opn->pn_left)
3755 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
3756 else
3757 pn->pn_right = pn->pn_left;
3758 pn->pn_val = opn->pn_val;
3759 pn->pn_iflags = opn->pn_iflags;
3760 break;
3762 case PN_UNARY:
3763 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
3764 pn->pn_num = opn->pn_num;
3765 pn->pn_hidden = opn->pn_hidden;
3766 break;
3768 case PN_NAME:
3769 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3770 pn->pn_u = opn->pn_u;
3771 if (opn->pn_used) {
3773 * The old name is a use of its pn_lexdef. Make the clone also be a
3774 * use of that definition.
3776 JSDefinition *dn = pn->pn_lexdef;
3778 pn->pn_link = dn->dn_uses;
3779 dn->dn_uses = pn;
3780 } else if (opn->pn_expr) {
3781 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
3784 * If the old name is a definition, the new one has pn_defn set.
3785 * Make the old name a use of the new node.
3787 if (opn->pn_defn) {
3788 opn->pn_defn = false;
3789 LinkUseToDef(opn, (JSDefinition *) pn, tc);
3792 break;
3794 case PN_NAMESET:
3795 pn->pn_names = opn->pn_names;
3796 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
3797 break;
3799 case PN_NULLARY:
3800 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3801 pn->pn_u = opn->pn_u;
3802 break;
3804 #undef NULLCHECK
3806 return pn;
3809 #endif /* JS_HAS_DESTRUCTURING */
3811 extern const char js_with_statement_str[];
3813 static JSParseNode *
3814 ContainsStmt(JSParseNode *pn, JSTokenType tt)
3816 JSParseNode *pn2, *pnt;
3818 if (!pn)
3819 return NULL;
3820 if (PN_TYPE(pn) == tt)
3821 return pn;
3822 switch (pn->pn_arity) {
3823 case PN_LIST:
3824 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3825 pnt = ContainsStmt(pn2, tt);
3826 if (pnt)
3827 return pnt;
3829 break;
3830 case PN_TERNARY:
3831 pnt = ContainsStmt(pn->pn_kid1, tt);
3832 if (pnt)
3833 return pnt;
3834 pnt = ContainsStmt(pn->pn_kid2, tt);
3835 if (pnt)
3836 return pnt;
3837 return ContainsStmt(pn->pn_kid3, tt);
3838 case PN_BINARY:
3840 * Limit recursion if pn is a binary expression, which can't contain a
3841 * var statement.
3843 if (pn->pn_op != JSOP_NOP)
3844 return NULL;
3845 pnt = ContainsStmt(pn->pn_left, tt);
3846 if (pnt)
3847 return pnt;
3848 return ContainsStmt(pn->pn_right, tt);
3849 case PN_UNARY:
3850 if (pn->pn_op != JSOP_NOP)
3851 return NULL;
3852 return ContainsStmt(pn->pn_kid, tt);
3853 case PN_NAME:
3854 return ContainsStmt(pn->maybeExpr(), tt);
3855 case PN_NAMESET:
3856 return ContainsStmt(pn->pn_tree, tt);
3857 default:;
3859 return NULL;
3862 static JSParseNode *
3863 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3864 JSParser operandParser)
3866 JSTokenType tt, tt2;
3867 JSParseNode *pn, *pn2;
3869 tt = CURRENT_TOKEN(ts).type;
3870 if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
3871 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3872 JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
3873 return NULL;
3876 pn = NewParseNode(PN_UNARY, tc);
3877 if (!pn)
3878 return NULL;
3880 #if JS_HAS_GENERATORS
3881 if (tt == TOK_YIELD)
3882 tc->flags |= TCF_FUN_IS_GENERATOR;
3883 #endif
3885 /* This is ugly, but we don't want to require a semicolon. */
3886 ts->flags |= TSF_OPERAND;
3887 tt2 = js_PeekTokenSameLine(cx, ts);
3888 ts->flags &= ~TSF_OPERAND;
3889 if (tt2 == TOK_ERROR)
3890 return NULL;
3892 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
3893 #if JS_HAS_GENERATORS
3894 && (tt != TOK_YIELD ||
3895 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
3896 tt2 != TOK_COLON && tt2 != TOK_COMMA))
3897 #endif
3899 pn2 = operandParser(cx, ts, tc);
3900 if (!pn2)
3901 return NULL;
3902 #if JS_HAS_GENERATORS
3903 if (tt == TOK_RETURN)
3904 #endif
3905 tc->flags |= TCF_RETURN_EXPR;
3906 pn->pn_pos.end = pn2->pn_pos.end;
3907 pn->pn_kid = pn2;
3908 } else {
3909 #if JS_HAS_GENERATORS
3910 if (tt == TOK_RETURN)
3911 #endif
3912 tc->flags |= TCF_RETURN_VOID;
3915 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
3916 /* As in Python (see PEP-255), disallow return v; in generators. */
3917 ReportBadReturn(cx, tc, JSREPORT_ERROR,
3918 JSMSG_BAD_GENERATOR_RETURN,
3919 JSMSG_BAD_ANON_GENERATOR_RETURN);
3920 return NULL;
3923 if (JS_HAS_STRICT_OPTION(cx) &&
3924 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
3925 !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
3926 JSMSG_NO_RETURN_VALUE,
3927 JSMSG_ANON_NO_RETURN_VALUE)) {
3928 return NULL;
3931 return pn;
3934 static JSParseNode *
3935 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3936 JSStmtInfo *stmt)
3938 JSParseNode *pn;
3939 JSObject *obj;
3940 JSObjectBox *blockbox;
3942 pn = NewParseNode(PN_NAME, tc);
3943 if (!pn)
3944 return NULL;
3946 obj = js_NewBlockObject(cx);
3947 if (!obj)
3948 return NULL;
3950 blockbox = tc->compiler->newObjectBox(obj);
3951 if (!blockbox)
3952 return NULL;
3954 js_PushBlockScope(tc, stmt, obj, -1);
3955 pn->pn_type = TOK_LEXICALSCOPE;
3956 pn->pn_op = JSOP_LEAVEBLOCK;
3957 pn->pn_objbox = blockbox;
3958 pn->pn_cookie = FREE_UPVAR_COOKIE;
3959 pn->pn_dflags = 0;
3960 if (!GenerateBlockId(tc, stmt->blockid))
3961 return NULL;
3962 pn->pn_blockid = stmt->blockid;
3963 return pn;
3966 #if JS_HAS_BLOCK_SCOPE
3968 static JSParseNode *
3969 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
3971 JSParseNode *pn, *pnblock, *pnlet;
3972 JSStmtInfo stmtInfo;
3974 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
3976 /* Create the let binary node. */
3977 pnlet = NewParseNode(PN_BINARY, tc);
3978 if (!pnlet)
3979 return NULL;
3981 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
3983 /* This is a let block or expression of the form: let (a, b, c) .... */
3984 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
3985 if (!pnblock)
3986 return NULL;
3987 pn = pnblock;
3988 pn->pn_expr = pnlet;
3990 pnlet->pn_left = Variables(cx, ts, tc, true);
3991 if (!pnlet->pn_left)
3992 return NULL;
3993 pnlet->pn_left->pn_xflags = PNX_POPVAR;
3995 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
3997 ts->flags |= TSF_OPERAND;
3998 if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
4000 * If this is really an expression in let statement guise, then we
4001 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4002 * the return value of the expression.
4004 pn = NewParseNode(PN_UNARY, tc);
4005 if (!pn)
4006 return NULL;
4007 pn->pn_type = TOK_SEMI;
4008 pn->pn_num = -1;
4009 pn->pn_kid = pnblock;
4011 statement = JS_FALSE;
4013 ts->flags &= ~TSF_OPERAND;
4015 if (statement) {
4016 pnlet->pn_right = Statements(cx, ts, tc);
4017 if (!pnlet->pn_right)
4018 return NULL;
4019 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4020 } else {
4022 * Change pnblock's opcode to the variant that propagates the last
4023 * result down after popping the block, and clear statement.
4025 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4026 pnlet->pn_right = AssignExpr(cx, ts, tc);
4027 if (!pnlet->pn_right)
4028 return NULL;
4031 PopStatement(tc);
4032 return pn;
4035 #endif /* JS_HAS_BLOCK_SCOPE */
4037 static bool
4038 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4040 js_PushStatement(tc, stmt, type, -1);
4041 return GenerateBlockId(tc, stmt->blockid);
4044 static JSParseNode *
4045 NewBindingNode(JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc, bool let = false)
4047 JSParseNode *pn = NULL;
4049 JSAtomListElement *ale = tc->decls.lookup(atom);
4050 if (ale) {
4051 pn = ALE_DEFN(ale);
4052 JS_ASSERT(!pn->isPlaceholder());
4053 } else {
4054 ale = tc->lexdeps.lookup(atom);
4055 if (ale) {
4056 pn = ALE_DEFN(ale);
4057 JS_ASSERT(pn->isPlaceholder());
4061 if (pn) {
4062 JS_ASSERT(pn->pn_defn);
4065 * A let binding at top level becomes a var before we get here, so if
4066 * pn and tc have the same blockid then that id must not be the bodyid.
4067 * If pn is a forward placeholder definition from the same or a higher
4068 * block then we claim it.
4070 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4071 pn->pn_blockid != tc->bodyid);
4073 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4074 JS_ASSERT(pn->isForward());
4075 if (let)
4076 pn->pn_blockid = tc->blockid();
4078 tc->lexdeps.remove(tc->compiler, atom);
4079 return pn;
4083 /* Make a new node for this declarator name (or destructuring pattern). */
4084 pn = NewNameNode(tc->compiler->context, ts, atom, tc);
4085 if (!pn)
4086 return NULL;
4087 return pn;
4090 #if JS_HAS_BLOCK_SCOPE
4091 static bool
4092 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4094 if (!pn)
4095 return true;
4097 switch (pn->pn_arity) {
4098 case PN_LIST:
4099 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4100 RebindLets(pn2, tc);
4101 break;
4103 case PN_TERNARY:
4104 RebindLets(pn->pn_kid1, tc);
4105 RebindLets(pn->pn_kid2, tc);
4106 RebindLets(pn->pn_kid3, tc);
4107 break;
4109 case PN_BINARY:
4110 RebindLets(pn->pn_left, tc);
4111 RebindLets(pn->pn_right, tc);
4112 break;
4114 case PN_UNARY:
4115 RebindLets(pn->pn_kid, tc);
4116 break;
4118 case PN_FUNC:
4119 RebindLets(pn->pn_body, tc);
4120 break;
4122 case PN_NAME:
4123 RebindLets(pn->maybeExpr(), tc);
4125 if (pn->pn_defn) {
4126 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4127 } else if (pn->pn_used) {
4128 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4129 ForgetUse(pn);
4131 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4132 if (ale) {
4133 while ((ale = ALE_NEXT(ale)) != NULL) {
4134 if (ALE_ATOM(ale) == pn->pn_atom) {
4135 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4136 return true;
4141 ale = tc->lexdeps.lookup(pn->pn_atom);
4142 if (!ale) {
4143 ale = MakePlaceholder(pn, tc);
4144 if (!ale)
4145 return NULL;
4147 JSDefinition *dn = ALE_DEFN(ale);
4148 dn->pn_type = TOK_NAME;
4149 dn->pn_op = JSOP_NOP;
4150 dn->pn_dflags |= pn->pn_dflags & PND_FUNARG;
4152 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4155 break;
4157 case PN_NAMESET:
4158 RebindLets(pn->pn_tree, tc);
4159 break;
4162 return true;
4164 #endif /* JS_HAS_BLOCK_SCOPE */
4166 static JSParseNode *
4167 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4169 JSTokenType tt;
4170 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4171 JSStmtInfo stmtInfo, *stmt, *stmt2;
4172 JSAtom *label;
4174 JS_CHECK_RECURSION(cx, return NULL);
4176 ts->flags |= TSF_OPERAND;
4177 tt = js_GetToken(cx, ts);
4178 ts->flags &= ~TSF_OPERAND;
4180 #if JS_HAS_GETTER_SETTER
4181 if (tt == TOK_NAME) {
4182 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
4183 if (tt == TOK_ERROR)
4184 return NULL;
4186 #endif
4188 switch (tt) {
4189 case TOK_FUNCTION:
4190 #if JS_HAS_XML_SUPPORT
4191 ts->flags |= TSF_KEYWORD_IS_NAME;
4192 tt = js_PeekToken(cx, ts);
4193 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4194 if (tt == TOK_DBLCOLON)
4195 goto expression;
4196 #endif
4197 return FunctionStmt(cx, ts, tc);
4199 case TOK_IF:
4200 /* An IF node has three kids: condition, then, and optional else. */
4201 pn = NewParseNode(PN_TERNARY, tc);
4202 if (!pn)
4203 return NULL;
4204 pn1 = Condition(cx, ts, tc);
4205 if (!pn1)
4206 return NULL;
4207 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4208 pn2 = Statement(cx, ts, tc);
4209 if (!pn2)
4210 return NULL;
4211 ts->flags |= TSF_OPERAND;
4212 if (js_MatchToken(cx, ts, TOK_ELSE)) {
4213 ts->flags &= ~TSF_OPERAND;
4214 stmtInfo.type = STMT_ELSE;
4215 pn3 = Statement(cx, ts, tc);
4216 if (!pn3)
4217 return NULL;
4218 pn->pn_pos.end = pn3->pn_pos.end;
4219 } else {
4220 ts->flags &= ~TSF_OPERAND;
4221 pn3 = NULL;
4222 pn->pn_pos.end = pn2->pn_pos.end;
4224 PopStatement(tc);
4225 pn->pn_kid1 = pn1;
4226 pn->pn_kid2 = pn2;
4227 pn->pn_kid3 = pn3;
4228 return pn;
4230 case TOK_SWITCH:
4232 JSParseNode *pn5, *saveBlock;
4233 JSBool seenDefault = JS_FALSE;
4235 pn = NewParseNode(PN_BINARY, tc);
4236 if (!pn)
4237 return NULL;
4238 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4240 /* pn1 points to the switch's discriminant. */
4241 pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
4242 if (!pn1)
4243 return NULL;
4245 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4246 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4249 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4250 * because that function states tc->topStmt->blockid.
4252 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4254 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4255 pn2 = NewParseNode(PN_LIST, tc);
4256 if (!pn2)
4257 return NULL;
4258 pn2->makeEmpty();
4259 if (!GenerateBlockIdForStmtNode(pn2, tc))
4260 return NULL;
4261 saveBlock = tc->blockNode;
4262 tc->blockNode = pn2;
4264 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
4265 switch (tt) {
4266 case TOK_DEFAULT:
4267 if (seenDefault) {
4268 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4269 JSMSG_TOO_MANY_DEFAULTS);
4270 return NULL;
4272 seenDefault = JS_TRUE;
4273 /* FALL THROUGH */
4275 case TOK_CASE:
4276 pn3 = NewParseNode(PN_BINARY, tc);
4277 if (!pn3)
4278 return NULL;
4279 if (tt == TOK_CASE) {
4280 pn3->pn_left = Expr(cx, ts, tc);
4281 if (!pn3->pn_left)
4282 return NULL;
4284 pn2->append(pn3);
4285 if (pn2->pn_count == JS_BIT(16)) {
4286 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4287 JSMSG_TOO_MANY_CASES);
4288 return NULL;
4290 break;
4292 case TOK_ERROR:
4293 return NULL;
4295 default:
4296 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4297 JSMSG_BAD_SWITCH);
4298 return NULL;
4300 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4302 pn4 = NewParseNode(PN_LIST, tc);
4303 if (!pn4)
4304 return NULL;
4305 pn4->pn_type = TOK_LC;
4306 pn4->makeEmpty();
4307 ts->flags |= TSF_OPERAND;
4308 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
4309 tt != TOK_CASE && tt != TOK_DEFAULT) {
4310 ts->flags &= ~TSF_OPERAND;
4311 if (tt == TOK_ERROR)
4312 return NULL;
4313 pn5 = Statement(cx, ts, tc);
4314 if (!pn5)
4315 return NULL;
4316 pn4->pn_pos.end = pn5->pn_pos.end;
4317 pn4->append(pn5);
4318 ts->flags |= TSF_OPERAND;
4320 ts->flags &= ~TSF_OPERAND;
4322 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4323 if (pn4->pn_head)
4324 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4325 pn3->pn_pos.end = pn4->pn_pos.end;
4326 pn3->pn_right = pn4;
4330 * Handle the case where there was a let declaration in any case in
4331 * the switch body, but not within an inner block. If it replaced
4332 * tc->blockNode with a new block node then we must refresh pn2 and
4333 * then restore tc->blockNode.
4335 if (tc->blockNode != pn2)
4336 pn2 = tc->blockNode;
4337 tc->blockNode = saveBlock;
4338 PopStatement(tc);
4340 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4341 pn->pn_left = pn1;
4342 pn->pn_right = pn2;
4343 return pn;
4346 case TOK_WHILE:
4347 pn = NewParseNode(PN_BINARY, tc);
4348 if (!pn)
4349 return NULL;
4350 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4351 pn2 = Condition(cx, ts, tc);
4352 if (!pn2)
4353 return NULL;
4354 pn->pn_left = pn2;
4355 pn2 = Statement(cx, ts, tc);
4356 if (!pn2)
4357 return NULL;
4358 PopStatement(tc);
4359 pn->pn_pos.end = pn2->pn_pos.end;
4360 pn->pn_right = pn2;
4361 return pn;
4363 case TOK_DO:
4364 pn = NewParseNode(PN_BINARY, tc);
4365 if (!pn)
4366 return NULL;
4367 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4368 pn2 = Statement(cx, ts, tc);
4369 if (!pn2)
4370 return NULL;
4371 pn->pn_left = pn2;
4372 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4373 pn2 = Condition(cx, ts, tc);
4374 if (!pn2)
4375 return NULL;
4376 PopStatement(tc);
4377 pn->pn_pos.end = pn2->pn_pos.end;
4378 pn->pn_right = pn2;
4379 if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
4381 * All legacy and extended versions must do automatic semicolon
4382 * insertion after do-while. See the testcase and discussion in
4383 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4385 (void) js_MatchToken(cx, ts, TOK_SEMI);
4386 return pn;
4388 break;
4390 case TOK_FOR:
4392 JSParseNode *pnseq = NULL;
4393 #if JS_HAS_BLOCK_SCOPE
4394 JSParseNode *pnlet = NULL;
4395 JSStmtInfo blockInfo;
4396 #endif
4398 /* A FOR node is binary, left is loop control and right is the body. */
4399 pn = NewParseNode(PN_BINARY, tc);
4400 if (!pn)
4401 return NULL;
4402 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4404 pn->pn_op = JSOP_ITER;
4405 pn->pn_iflags = 0;
4406 if (js_MatchToken(cx, ts, TOK_NAME)) {
4407 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
4408 pn->pn_iflags = JSITER_FOREACH;
4409 else
4410 js_UngetToken(ts);
4413 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4414 ts->flags |= TSF_OPERAND;
4415 tt = js_PeekToken(cx, ts);
4416 ts->flags &= ~TSF_OPERAND;
4418 #if JS_HAS_BLOCK_SCOPE
4419 bool let = false;
4420 #endif
4422 if (tt == TOK_SEMI) {
4423 if (pn->pn_iflags & JSITER_FOREACH)
4424 goto bad_for_each;
4426 /* No initializer -- set first kid of left sub-node to null. */
4427 pn1 = NULL;
4428 } else {
4430 * Set pn1 to a var list or an initializing expression.
4432 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4433 * of the for statement. This flag will be used by the RelExpr
4434 * production; if it is set, then the 'in' keyword will not be
4435 * recognized as an operator, leaving it available to be parsed as
4436 * part of a for/in loop.
4438 * A side effect of this restriction is that (unparenthesized)
4439 * expressions involving an 'in' operator are illegal in the init
4440 * clause of an ordinary for loop.
4442 tc->flags |= TCF_IN_FOR_INIT;
4443 if (tt == TOK_VAR) {
4444 (void) js_GetToken(cx, ts);
4445 pn1 = Variables(cx, ts, tc, false);
4446 #if JS_HAS_BLOCK_SCOPE
4447 } else if (tt == TOK_LET) {
4448 let = true;
4449 (void) js_GetToken(cx, ts);
4450 if (js_PeekToken(cx, ts) == TOK_LP) {
4451 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
4452 tt = TOK_LEXICALSCOPE;
4453 } else {
4454 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
4455 if (!pnlet)
4456 return NULL;
4457 blockInfo.flags |= SIF_FOR_BLOCK;
4458 pn1 = Variables(cx, ts, tc, false);
4460 #endif
4461 } else {
4462 pn1 = Expr(cx, ts, tc);
4463 if (pn1) {
4464 while (pn1->pn_type == TOK_RP)
4465 pn1 = pn1->pn_kid;
4468 tc->flags &= ~TCF_IN_FOR_INIT;
4469 if (!pn1)
4470 return NULL;
4474 * We can be sure that it's a for/in loop if there's still an 'in'
4475 * keyword here, even if JavaScript recognizes 'in' as an operator,
4476 * as we've excluded 'in' from being parsed in RelExpr by setting
4477 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4479 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
4480 pn->pn_iflags |= JSITER_ENUMERATE;
4481 stmtInfo.type = STMT_FOR_IN_LOOP;
4483 /* Check that the left side of the 'in' is valid. */
4484 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || PN_TYPE(pn1) == tt);
4485 if (TOKEN_TYPE_IS_DECL(tt)
4486 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4487 #if JS_HAS_DESTRUCTURING
4488 || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4489 pn->pn_op == JSOP_ITER &&
4490 !(pn->pn_iflags & JSITER_FOREACH) &&
4491 (pn1->pn_head->pn_type == TOK_RC ||
4492 (pn1->pn_head->pn_type == TOK_RB &&
4493 pn1->pn_head->pn_count != 2) ||
4494 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4495 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4496 pn1->pn_head->pn_left->pn_count != 2))))
4497 #endif
4499 : (pn1->pn_type != TOK_NAME &&
4500 pn1->pn_type != TOK_DOT &&
4501 #if JS_HAS_DESTRUCTURING
4502 ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4503 pn->pn_op == JSOP_ITER &&
4504 !(pn->pn_iflags & JSITER_FOREACH))
4505 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4506 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4507 #endif
4508 #if JS_HAS_LVALUE_RETURN
4509 pn1->pn_type != TOK_LP &&
4510 #endif
4511 #if JS_HAS_XML_SUPPORT
4512 (pn1->pn_type != TOK_UNARYOP ||
4513 pn1->pn_op != JSOP_XMLNAME) &&
4514 #endif
4515 pn1->pn_type != TOK_LB)) {
4516 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
4517 JSMSG_BAD_FOR_LEFTSIDE);
4518 return NULL;
4521 /* pn2 points to the name or destructuring pattern on in's left. */
4522 pn2 = NULL;
4523 uintN dflag = PND_ASSIGNED;
4525 if (TOKEN_TYPE_IS_DECL(tt)) {
4526 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4527 pn1->pn_xflags |= PNX_FORINVAR;
4530 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4531 * 'var', or 'const' to hoist the initializer or the entire
4532 * decl out of the loop head. TOK_VAR is the type for both
4533 * 'var' and 'const'.
4535 pn2 = pn1->pn_head;
4536 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4537 #if JS_HAS_DESTRUCTURING
4538 || pn2->pn_type == TOK_ASSIGN
4539 #endif
4541 pnseq = NewParseNode(PN_LIST, tc);
4542 if (!pnseq)
4543 return NULL;
4544 pnseq->pn_type = TOK_SEQ;
4545 pnseq->pn_pos.begin = pn->pn_pos.begin;
4547 #if JS_HAS_BLOCK_SCOPE
4548 if (tt == TOK_LET) {
4550 * Hoist just the 'i' from 'for (let x = i in o)' to
4551 * before the loop, glued together via pnseq.
4553 pn3 = NewParseNode(PN_UNARY, tc);
4554 if (!pn3)
4555 return NULL;
4556 pn3->pn_type = TOK_SEMI;
4557 pn3->pn_op = JSOP_NOP;
4558 #if JS_HAS_DESTRUCTURING
4559 if (pn2->pn_type == TOK_ASSIGN) {
4560 pn4 = pn2->pn_right;
4561 pn2 = pn1->pn_head = pn2->pn_left;
4562 } else
4563 #endif
4565 pn4 = pn2->pn_expr;
4566 pn2->pn_expr = NULL;
4568 if (!RebindLets(pn4, tc))
4569 return NULL;
4570 pn3->pn_pos = pn4->pn_pos;
4571 pn3->pn_kid = pn4;
4572 pnseq->initList(pn3);
4573 } else
4574 #endif /* JS_HAS_BLOCK_SCOPE */
4576 dflag = PND_INITIALIZED;
4579 * All of 'var x = i' is hoisted above 'for (x in o)',
4580 * so clear PNX_FORINVAR.
4582 * Request JSOP_POP here since the var is for a simple
4583 * name (it is not a destructuring binding's left-hand
4584 * side) and it has an initializer.
4586 pn1->pn_xflags &= ~PNX_FORINVAR;
4587 pn1->pn_xflags |= PNX_POPVAR;
4588 pnseq->initList(pn1);
4590 #if JS_HAS_DESTRUCTURING
4591 if (pn2->pn_type == TOK_ASSIGN) {
4592 pn1 = CloneParseTree(pn2->pn_left, tc);
4593 if (!pn1)
4594 return NULL;
4595 } else
4596 #endif
4598 JS_ASSERT(pn2->pn_type == TOK_NAME);
4599 pn1 = NewNameNode(cx, ts, pn2->pn_atom, tc);
4600 if (!pn1)
4601 return NULL;
4602 pn1->pn_type = TOK_NAME;
4603 pn1->pn_op = JSOP_NAME;
4604 pn1->pn_pos = pn2->pn_pos;
4605 if (pn2->pn_defn)
4606 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4608 pn2 = pn1;
4613 if (!pn2) {
4614 pn2 = pn1;
4615 #if JS_HAS_LVALUE_RETURN
4616 if (pn2->pn_type == TOK_LP &&
4617 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4618 return NULL;
4620 #endif
4621 #if JS_HAS_XML_SUPPORT
4622 if (pn2->pn_type == TOK_UNARYOP)
4623 pn2->pn_op = JSOP_BINDXMLNAME;
4624 #endif
4627 switch (pn2->pn_type) {
4628 case TOK_NAME:
4629 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4630 NoteLValue(cx, pn2, tc, dflag);
4631 break;
4633 #if JS_HAS_DESTRUCTURING
4634 case TOK_ASSIGN:
4635 pn2 = pn2->pn_left;
4636 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4637 /* FALL THROUGH */
4638 case TOK_RB:
4639 case TOK_RC:
4640 /* Check for valid lvalues in var-less destructuring for-in. */
4641 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
4642 return NULL;
4644 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
4646 * Destructuring for-in requires [key, value] enumeration
4647 * in JS1.7.
4649 JS_ASSERT(pn->pn_op == JSOP_ITER);
4650 if (!(pn->pn_iflags & JSITER_FOREACH))
4651 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4653 break;
4654 #endif
4656 default:;
4660 * Parse the object expression as the right operand of 'in', first
4661 * removing the top statement from the statement-stack if this is a
4662 * 'for (let x in y)' loop.
4664 #if JS_HAS_BLOCK_SCOPE
4665 JSStmtInfo *save = tc->topStmt;
4666 if (let)
4667 tc->topStmt = save->down;
4668 #endif
4669 pn2 = Expr(cx, ts, tc);
4670 #if JS_HAS_BLOCK_SCOPE
4671 if (let)
4672 tc->topStmt = save;
4673 #endif
4675 pn2 = NewBinary(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4676 if (!pn2)
4677 return NULL;
4678 pn->pn_left = pn2;
4679 } else {
4680 if (pn->pn_iflags & JSITER_FOREACH)
4681 goto bad_for_each;
4682 pn->pn_op = JSOP_NOP;
4684 /* Parse the loop condition or null into pn2. */
4685 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4686 ts->flags |= TSF_OPERAND;
4687 tt = js_PeekToken(cx, ts);
4688 ts->flags &= ~TSF_OPERAND;
4689 if (tt == TOK_SEMI) {
4690 pn2 = NULL;
4691 } else {
4692 pn2 = Expr(cx, ts, tc);
4693 if (!pn2)
4694 return NULL;
4697 /* Parse the update expression or null into pn3. */
4698 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4699 ts->flags |= TSF_OPERAND;
4700 tt = js_PeekToken(cx, ts);
4701 ts->flags &= ~TSF_OPERAND;
4702 if (tt == TOK_RP) {
4703 pn3 = NULL;
4704 } else {
4705 pn3 = Expr(cx, ts, tc);
4706 if (!pn3)
4707 return NULL;
4710 /* Build the FORHEAD node to use as the left kid of pn. */
4711 pn4 = NewParseNode(PN_TERNARY, tc);
4712 if (!pn4)
4713 return NULL;
4714 pn4->pn_type = TOK_FORHEAD;
4715 pn4->pn_op = JSOP_NOP;
4716 pn4->pn_kid1 = pn1;
4717 pn4->pn_kid2 = pn2;
4718 pn4->pn_kid3 = pn3;
4719 pn->pn_left = pn4;
4722 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
4724 /* Parse the loop body into pn->pn_right. */
4725 pn2 = Statement(cx, ts, tc);
4726 if (!pn2)
4727 return NULL;
4728 pn->pn_right = pn2;
4730 /* Record the absolute line number for source note emission. */
4731 pn->pn_pos.end = pn2->pn_pos.end;
4733 #if JS_HAS_BLOCK_SCOPE
4734 if (pnlet) {
4735 PopStatement(tc);
4736 pnlet->pn_expr = pn;
4737 pn = pnlet;
4739 #endif
4740 if (pnseq) {
4741 pnseq->pn_pos.end = pn->pn_pos.end;
4742 pnseq->append(pn);
4743 pn = pnseq;
4745 PopStatement(tc);
4746 return pn;
4748 bad_for_each:
4749 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
4750 JSMSG_BAD_FOR_EACH_LOOP);
4751 return NULL;
4754 case TOK_TRY: {
4755 JSParseNode *catchList, *lastCatch;
4758 * try nodes are ternary.
4759 * kid1 is the try Statement
4760 * kid2 is the catch node list or null
4761 * kid3 is the finally Statement
4763 * catch nodes are ternary.
4764 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4765 * kid2 is the catch guard or null if no guard
4766 * kid3 is the catch block
4768 * catch lvalue nodes are either:
4769 * TOK_NAME for a single identifier
4770 * TOK_RB or TOK_RC for a destructuring left-hand side
4772 * finally nodes are TOK_LC Statement lists.
4774 pn = NewParseNode(PN_TERNARY, tc);
4775 if (!pn)
4776 return NULL;
4777 pn->pn_op = JSOP_NOP;
4779 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
4780 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
4781 return NULL;
4782 pn->pn_kid1 = Statements(cx, ts, tc);
4783 if (!pn->pn_kid1)
4784 return NULL;
4785 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
4786 PopStatement(tc);
4788 catchList = NULL;
4789 tt = js_GetToken(cx, ts);
4790 if (tt == TOK_CATCH) {
4791 catchList = NewParseNode(PN_LIST, tc);
4792 if (!catchList)
4793 return NULL;
4794 catchList->pn_type = TOK_RESERVED;
4795 catchList->makeEmpty();
4796 lastCatch = NULL;
4798 do {
4799 JSParseNode *pnblock;
4800 BindData data;
4802 /* Check for another catch after unconditional catch. */
4803 if (lastCatch && !lastCatch->pn_kid2) {
4804 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4805 JSMSG_CATCH_AFTER_GENERAL);
4806 return NULL;
4810 * Create a lexical scope node around the whole catch clause,
4811 * including the head.
4813 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
4814 if (!pnblock)
4815 return NULL;
4816 stmtInfo.type = STMT_CATCH;
4819 * Legal catch forms are:
4820 * catch (lhs)
4821 * catch (lhs if <boolean_expression>)
4822 * where lhs is a name or a destructuring left-hand side.
4823 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
4825 pn2 = NewParseNode(PN_TERNARY, tc);
4826 if (!pn2)
4827 return NULL;
4828 pnblock->pn_expr = pn2;
4829 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
4832 * Contrary to ECMA Ed. 3, the catch variable is lexically
4833 * scoped, not a property of a new Object instance. This is
4834 * an intentional change that anticipates ECMA Ed. 4.
4836 data.pn = NULL;
4837 data.op = JSOP_NOP;
4838 data.binder = BindLet;
4839 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
4841 tt = js_GetToken(cx, ts);
4842 switch (tt) {
4843 #if JS_HAS_DESTRUCTURING
4844 case TOK_LB:
4845 case TOK_LC:
4846 pn3 = DestructuringExpr(cx, &data, tc, tt);
4847 if (!pn3)
4848 return NULL;
4849 break;
4850 #endif
4852 case TOK_NAME:
4853 label = CURRENT_TOKEN(ts).t_atom;
4854 pn3 = NewBindingNode(ts, label, tc, true);
4855 if (!pn3)
4856 return NULL;
4857 data.pn = pn3;
4858 if (!data.binder(cx, &data, label, tc))
4859 return NULL;
4860 break;
4862 default:
4863 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4864 JSMSG_CATCH_IDENTIFIER);
4865 return NULL;
4868 pn2->pn_kid1 = pn3;
4869 #if JS_HAS_CATCH_GUARD
4871 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
4872 * to avoid conflicting with the JS2/ECMAv4 type annotation
4873 * catchguard syntax.
4875 if (js_MatchToken(cx, ts, TOK_IF)) {
4876 pn2->pn_kid2 = Expr(cx, ts, tc);
4877 if (!pn2->pn_kid2)
4878 return NULL;
4880 #endif
4881 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
4883 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
4884 pn2->pn_kid3 = Statements(cx, ts, tc);
4885 if (!pn2->pn_kid3)
4886 return NULL;
4887 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
4888 PopStatement(tc);
4890 catchList->append(pnblock);
4891 lastCatch = pn2;
4892 ts->flags |= TSF_OPERAND;
4893 tt = js_GetToken(cx, ts);
4894 ts->flags &= ~TSF_OPERAND;
4895 } while (tt == TOK_CATCH);
4897 pn->pn_kid2 = catchList;
4899 if (tt == TOK_FINALLY) {
4900 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
4901 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
4902 return NULL;
4903 pn->pn_kid3 = Statements(cx, ts, tc);
4904 if (!pn->pn_kid3)
4905 return NULL;
4906 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
4907 PopStatement(tc);
4908 } else {
4909 js_UngetToken(ts);
4911 if (!catchList && !pn->pn_kid3) {
4912 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4913 JSMSG_CATCH_OR_FINALLY);
4914 return NULL;
4916 return pn;
4919 case TOK_THROW:
4920 pn = NewParseNode(PN_UNARY, tc);
4921 if (!pn)
4922 return NULL;
4924 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
4925 ts->flags |= TSF_OPERAND;
4926 tt = js_PeekTokenSameLine(cx, ts);
4927 ts->flags &= ~TSF_OPERAND;
4928 if (tt == TOK_ERROR)
4929 return NULL;
4930 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
4931 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4932 JSMSG_SYNTAX_ERROR);
4933 return NULL;
4936 pn2 = Expr(cx, ts, tc);
4937 if (!pn2)
4938 return NULL;
4939 pn->pn_pos.end = pn2->pn_pos.end;
4940 pn->pn_op = JSOP_THROW;
4941 pn->pn_kid = pn2;
4942 break;
4944 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
4945 case TOK_CATCH:
4946 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4947 JSMSG_CATCH_WITHOUT_TRY);
4948 return NULL;
4950 case TOK_FINALLY:
4951 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4952 JSMSG_FINALLY_WITHOUT_TRY);
4953 return NULL;
4955 case TOK_BREAK:
4956 pn = NewParseNode(PN_NULLARY, tc);
4957 if (!pn)
4958 return NULL;
4959 if (!MatchLabel(cx, ts, pn))
4960 return NULL;
4961 stmt = tc->topStmt;
4962 label = pn->pn_atom;
4963 if (label) {
4964 for (; ; stmt = stmt->down) {
4965 if (!stmt) {
4966 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4967 JSMSG_LABEL_NOT_FOUND);
4968 return NULL;
4970 if (stmt->type == STMT_LABEL && stmt->label == label)
4971 break;
4973 } else {
4974 for (; ; stmt = stmt->down) {
4975 if (!stmt) {
4976 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4977 JSMSG_TOUGH_BREAK);
4978 return NULL;
4980 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
4981 break;
4984 if (label)
4985 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4986 break;
4988 case TOK_CONTINUE:
4989 pn = NewParseNode(PN_NULLARY, tc);
4990 if (!pn)
4991 return NULL;
4992 if (!MatchLabel(cx, ts, pn))
4993 return NULL;
4994 stmt = tc->topStmt;
4995 label = pn->pn_atom;
4996 if (label) {
4997 for (stmt2 = NULL; ; stmt = stmt->down) {
4998 if (!stmt) {
4999 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5000 JSMSG_LABEL_NOT_FOUND);
5001 return NULL;
5003 if (stmt->type == STMT_LABEL) {
5004 if (stmt->label == label) {
5005 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5006 js_ReportCompileErrorNumber(cx, ts, NULL,
5007 JSREPORT_ERROR,
5008 JSMSG_BAD_CONTINUE);
5009 return NULL;
5011 break;
5013 } else {
5014 stmt2 = stmt;
5017 } else {
5018 for (; ; stmt = stmt->down) {
5019 if (!stmt) {
5020 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5021 JSMSG_BAD_CONTINUE);
5022 return NULL;
5024 if (STMT_IS_LOOP(stmt))
5025 break;
5028 if (label)
5029 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5030 break;
5032 case TOK_WITH:
5033 pn = NewParseNode(PN_BINARY, tc);
5034 if (!pn)
5035 return NULL;
5036 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5037 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
5038 if (!pn2)
5039 return NULL;
5040 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5041 pn->pn_left = pn2;
5043 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5044 pn2 = Statement(cx, ts, tc);
5045 if (!pn2)
5046 return NULL;
5047 PopStatement(tc);
5049 pn->pn_pos.end = pn2->pn_pos.end;
5050 pn->pn_right = pn2;
5051 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5052 return pn;
5054 case TOK_VAR:
5055 pn = Variables(cx, ts, tc, false);
5056 if (!pn)
5057 return NULL;
5059 /* Tell js_EmitTree to generate a final POP. */
5060 pn->pn_xflags |= PNX_POPVAR;
5061 break;
5063 #if JS_HAS_BLOCK_SCOPE
5064 case TOK_LET:
5066 JSObject *obj;
5067 JSObjectBox *blockbox;
5069 /* Check for a let statement or let expression. */
5070 if (js_PeekToken(cx, ts) == TOK_LP) {
5071 pn = LetBlock(cx, ts, tc, JS_TRUE);
5072 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5073 return pn;
5075 /* Let expressions require automatic semicolon insertion. */
5076 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5077 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5078 break;
5082 * This is a let declaration. We must be directly under a block per
5083 * the proposed ES4 specs, but not an implicit block created due to
5084 * 'for (let ...)'. If we pass this error test, make the enclosing
5085 * JSStmtInfo be our scope. Further let declarations in this block
5086 * will find this scope statement and use the same block object.
5088 * If we are the first let declaration in this block (i.e., when the
5089 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5090 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5092 stmt = tc->topStmt;
5093 if (stmt &&
5094 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5095 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5096 JSMSG_LET_DECL_NOT_IN_BLOCK);
5097 return NULL;
5100 if (stmt && (stmt->flags & SIF_SCOPE)) {
5101 JS_ASSERT(tc->blockChain == stmt->blockObj);
5102 obj = tc->blockChain;
5103 } else {
5104 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5106 * ES4 specifies that let at top level and at body-block scope
5107 * does not shadow var, so convert back to var.
5109 CURRENT_TOKEN(ts).type = TOK_VAR;
5110 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
5112 pn = Variables(cx, ts, tc, false);
5113 if (!pn)
5114 return NULL;
5115 pn->pn_xflags |= PNX_POPVAR;
5116 break;
5120 * Some obvious assertions here, but they may help clarify the
5121 * situation. This stmt is not yet a scope, so it must not be a
5122 * catch block (catch is a lexical scope by definition).
5124 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5125 JS_ASSERT(stmt != tc->topScopeStmt);
5126 JS_ASSERT(stmt->type == STMT_BLOCK ||
5127 stmt->type == STMT_SWITCH ||
5128 stmt->type == STMT_TRY ||
5129 stmt->type == STMT_FINALLY);
5130 JS_ASSERT(!stmt->downScope);
5132 /* Convert the block statement into a scope statement. */
5133 JSObject *obj = js_NewBlockObject(tc->compiler->context);
5134 if (!obj)
5135 return NULL;
5137 blockbox = tc->compiler->newObjectBox(obj);
5138 if (!blockbox)
5139 return NULL;
5142 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5143 * list stack, if it isn't already there. If it is there, but it
5144 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5145 * block.
5147 stmt->flags |= SIF_SCOPE;
5148 stmt->downScope = tc->topScopeStmt;
5149 tc->topScopeStmt = stmt;
5150 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5151 (tc->maxScopeDepth = tc->scopeDepth));
5153 STOBJ_SET_PARENT(obj, tc->blockChain);
5154 tc->blockChain = obj;
5155 stmt->blockObj = obj;
5157 #ifdef DEBUG
5158 pn1 = tc->blockNode;
5159 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5160 #endif
5162 /* Create a new lexical scope node for these statements. */
5163 pn1 = NewParseNode(PN_NAME, tc);
5164 if (!pn1)
5165 return NULL;
5167 pn1->pn_type = TOK_LEXICALSCOPE;
5168 pn1->pn_op = JSOP_LEAVEBLOCK;
5169 pn1->pn_pos = tc->blockNode->pn_pos;
5170 pn1->pn_objbox = blockbox;
5171 pn1->pn_expr = tc->blockNode;
5172 pn1->pn_blockid = tc->blockNode->pn_blockid;
5173 tc->blockNode = pn1;
5176 pn = Variables(cx, ts, tc, false);
5177 if (!pn)
5178 return NULL;
5179 pn->pn_xflags = PNX_POPVAR;
5180 break;
5182 #endif /* JS_HAS_BLOCK_SCOPE */
5184 case TOK_RETURN:
5185 pn = ReturnOrYield(cx, ts, tc, Expr);
5186 if (!pn)
5187 return NULL;
5188 break;
5190 case TOK_LC:
5192 uintN oldflags;
5194 oldflags = tc->flags;
5195 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5196 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5197 return NULL;
5198 pn = Statements(cx, ts, tc);
5199 if (!pn)
5200 return NULL;
5202 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5203 PopStatement(tc);
5206 * If we contain a function statement and our container is top-level
5207 * or another block, flag pn to preserve braces when decompiling.
5209 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5210 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5211 pn->pn_xflags |= PNX_NEEDBRACES;
5213 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5214 return pn;
5217 case TOK_EOL:
5218 case TOK_SEMI:
5219 pn = NewParseNode(PN_UNARY, tc);
5220 if (!pn)
5221 return NULL;
5222 pn->pn_type = TOK_SEMI;
5223 return pn;
5225 #if JS_HAS_DEBUGGER_KEYWORD
5226 case TOK_DEBUGGER:
5227 pn = NewParseNode(PN_NULLARY, tc);
5228 if (!pn)
5229 return NULL;
5230 pn->pn_type = TOK_DEBUGGER;
5231 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5232 break;
5233 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5235 #if JS_HAS_XML_SUPPORT
5236 case TOK_DEFAULT:
5237 pn = NewParseNode(PN_UNARY, tc);
5238 if (!pn)
5239 return NULL;
5240 if (!js_MatchToken(cx, ts, TOK_NAME) ||
5241 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
5242 !js_MatchToken(cx, ts, TOK_NAME) ||
5243 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
5244 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
5245 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
5246 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5247 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5248 return NULL;
5250 pn2 = Expr(cx, ts, tc);
5251 if (!pn2)
5252 return NULL;
5253 pn->pn_op = JSOP_DEFXMLNS;
5254 pn->pn_pos.end = pn2->pn_pos.end;
5255 pn->pn_kid = pn2;
5256 break;
5257 #endif
5259 case TOK_ERROR:
5260 return NULL;
5262 default:
5263 #if JS_HAS_XML_SUPPORT
5264 expression:
5265 #endif
5266 js_UngetToken(ts);
5267 pn2 = Expr(cx, ts, tc);
5268 if (!pn2)
5269 return NULL;
5271 if (js_PeekToken(cx, ts) == TOK_COLON) {
5272 if (pn2->pn_type != TOK_NAME) {
5273 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5274 JSMSG_BAD_LABEL);
5275 return NULL;
5277 label = pn2->pn_atom;
5278 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5279 if (stmt->type == STMT_LABEL && stmt->label == label) {
5280 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5281 JSMSG_DUPLICATE_LABEL);
5282 return NULL;
5285 ForgetUse(pn2);
5287 (void) js_GetToken(cx, ts);
5289 /* Push a label struct and parse the statement. */
5290 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5291 stmtInfo.label = label;
5292 pn = Statement(cx, ts, tc);
5293 if (!pn)
5294 return NULL;
5296 /* Normalize empty statement to empty block for the decompiler. */
5297 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5298 pn->pn_type = TOK_LC;
5299 pn->pn_arity = PN_LIST;
5300 pn->makeEmpty();
5303 /* Pop the label, set pn_expr, and return early. */
5304 PopStatement(tc);
5305 pn2->pn_type = TOK_COLON;
5306 pn2->pn_pos.end = pn->pn_pos.end;
5307 pn2->pn_expr = pn;
5308 return pn2;
5311 pn = NewParseNode(PN_UNARY, tc);
5312 if (!pn)
5313 return NULL;
5314 pn->pn_type = TOK_SEMI;
5315 pn->pn_pos = pn2->pn_pos;
5316 pn->pn_kid = pn2;
5317 break;
5320 /* Check termination of this primitive statement. */
5321 return MatchOrInsertSemicolon(cx, ts) ? pn : NULL;
5324 static void
5325 NoteArgumentsUse(JSTreeContext *tc)
5327 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
5328 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5329 if (tc->funbox)
5330 tc->funbox->node->pn_dflags |= PND_FUNARG;
5333 static JSParseNode *
5334 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, bool inLetHead)
5336 JSTokenType tt;
5337 bool let;
5338 JSStmtInfo *scopeStmt;
5339 BindData data;
5340 JSParseNode *pn, *pn2;
5341 JSAtom *atom;
5344 * The three options here are:
5345 * - TOK_LET: We are parsing a let declaration.
5346 * - TOK_LP: We are parsing the head of a let block.
5347 * - Otherwise, we're parsing var declarations.
5349 tt = CURRENT_TOKEN(ts).type;
5350 let = (tt == TOK_LET || tt == TOK_LP);
5351 JS_ASSERT(let || tt == TOK_VAR);
5353 #if JS_HAS_BLOCK_SCOPE
5354 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5355 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5356 #endif
5358 /* Make sure that Statement set up the tree context correctly. */
5359 scopeStmt = tc->topScopeStmt;
5360 if (let) {
5361 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5362 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5363 scopeStmt = scopeStmt->downScope;
5365 JS_ASSERT(scopeStmt);
5368 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
5369 pn = NewParseNode(PN_LIST, tc);
5370 if (!pn)
5371 return NULL;
5372 pn->pn_op = data.op;
5373 pn->makeEmpty();
5376 * SpiderMonkey const is really "write once per initialization evaluation"
5377 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5378 * this code will change soon.
5380 if (let) {
5381 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5382 data.binder = BindLet;
5383 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5384 } else {
5385 data.binder = BindVarOrConst;
5388 do {
5389 tt = js_GetToken(cx, ts);
5390 #if JS_HAS_DESTRUCTURING
5391 if (tt == TOK_LB || tt == TOK_LC) {
5392 ts->flags |= TSF_DESTRUCTURING;
5393 pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
5394 ts->flags &= ~TSF_DESTRUCTURING;
5395 if (!pn2)
5396 return NULL;
5398 if ((tc->flags & TCF_IN_FOR_INIT) &&
5399 js_PeekToken(cx, ts) == TOK_IN) {
5400 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
5401 return NULL;
5402 pn->append(pn2);
5403 continue;
5406 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5407 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5408 goto bad_var_init;
5410 #if JS_HAS_BLOCK_SCOPE
5411 if (popScope) {
5412 tc->topStmt = save->down;
5413 tc->topScopeStmt = saveScope->downScope;
5415 #endif
5416 JSParseNode *init = AssignExpr(cx, ts, tc);
5417 #if JS_HAS_BLOCK_SCOPE
5418 if (popScope) {
5419 tc->topStmt = save;
5420 tc->topScopeStmt = saveScope;
5422 #endif
5424 pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5425 if (!pn2 ||
5426 !CheckDestructuring(cx, &data,
5427 pn2->pn_left, pn2->pn_right,
5428 tc)) {
5429 return NULL;
5431 pn->append(pn2);
5432 continue;
5434 #endif /* JS_HAS_DESTRUCTURING */
5436 if (tt != TOK_NAME) {
5437 if (tt != TOK_ERROR) {
5438 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5439 JSMSG_NO_VARIABLE_NAME);
5441 return NULL;
5444 atom = CURRENT_TOKEN(ts).t_atom;
5445 pn2 = NewBindingNode(ts, atom, tc, let);
5446 if (!pn2)
5447 return NULL;
5448 if (data.op == JSOP_DEFCONST)
5449 pn2->pn_dflags |= PND_CONST;
5450 data.pn = pn2;
5451 if (!data.binder(cx, &data, atom, tc))
5452 return NULL;
5453 pn->append(pn2);
5455 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
5456 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5457 goto bad_var_init;
5459 #if JS_HAS_BLOCK_SCOPE
5460 if (popScope) {
5461 tc->topStmt = save->down;
5462 tc->topScopeStmt = saveScope->downScope;
5464 #endif
5465 JSParseNode *init = AssignExpr(cx, ts, tc);
5466 #if JS_HAS_BLOCK_SCOPE
5467 if (popScope) {
5468 tc->topStmt = save;
5469 tc->topScopeStmt = saveScope;
5471 #endif
5472 if (!init)
5473 return NULL;
5475 if (pn2->pn_used) {
5476 pn2 = MakeAssignment(pn2, init, tc);
5477 if (!pn2)
5478 return NULL;
5479 } else {
5480 pn2->pn_expr = init;
5483 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5484 ? JSOP_SETNAME
5485 : (pn2->pn_dflags & PND_GVAR)
5486 ? JSOP_SETGVAR
5487 : (pn2->pn_dflags & PND_BOUND)
5488 ? JSOP_SETLOCAL
5489 : (data.op == JSOP_DEFCONST)
5490 ? JSOP_SETCONST
5491 : JSOP_SETNAME;
5493 NoteLValue(cx, pn2, tc, PND_INITIALIZED);
5495 /* The declarator's position must include the initializer. */
5496 pn2->pn_pos.end = init->pn_pos.end;
5498 if ((tc->flags & TCF_IN_FUNCTION) &&
5499 atom == cx->runtime->atomState.argumentsAtom) {
5500 NoteArgumentsUse(tc);
5501 if (!let)
5502 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5505 } while (js_MatchToken(cx, ts, TOK_COMMA));
5507 pn->pn_pos.end = pn->last()->pn_pos.end;
5508 return pn;
5510 bad_var_init:
5511 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5512 JSMSG_BAD_VAR_INIT);
5513 return NULL;
5516 static JSParseNode *
5517 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5519 JSParseNode *pn, *pn2;
5521 pn = AssignExpr(cx, ts, tc);
5522 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
5523 pn2 = NewParseNode(PN_LIST, tc);
5524 if (!pn2)
5525 return NULL;
5526 pn2->pn_pos.begin = pn->pn_pos.begin;
5527 pn2->initList(pn);
5528 pn = pn2;
5529 do {
5530 #if JS_HAS_GENERATORS
5531 pn2 = pn->last();
5532 if (pn2->pn_type == TOK_YIELD) {
5533 js_ReportCompileErrorNumber(cx, ts, pn2, JSREPORT_ERROR,
5534 JSMSG_BAD_GENERATOR_SYNTAX,
5535 js_yield_str);
5536 return NULL;
5538 #endif
5539 pn2 = AssignExpr(cx, ts, tc);
5540 if (!pn2)
5541 return NULL;
5542 pn->append(pn2);
5543 } while (js_MatchToken(cx, ts, TOK_COMMA));
5544 pn->pn_pos.end = pn->last()->pn_pos.end;
5546 return pn;
5549 static JSParseNode *
5550 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5552 JSParseNode *pn, *pn2;
5553 JSTokenType tt;
5554 JSOp op;
5556 JS_CHECK_RECURSION(cx, return NULL);
5558 #if JS_HAS_GENERATORS
5559 ts->flags |= TSF_OPERAND;
5560 if (js_MatchToken(cx, ts, TOK_YIELD)) {
5561 ts->flags &= ~TSF_OPERAND;
5562 return ReturnOrYield(cx, ts, tc, AssignExpr);
5564 ts->flags &= ~TSF_OPERAND;
5565 #endif
5567 pn = CondExpr(cx, ts, tc);
5568 if (!pn)
5569 return NULL;
5571 tt = js_GetToken(cx, ts);
5572 #if JS_HAS_GETTER_SETTER
5573 if (tt == TOK_NAME) {
5574 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
5575 if (tt == TOK_ERROR)
5576 return NULL;
5578 #endif
5579 if (tt != TOK_ASSIGN) {
5580 js_UngetToken(ts);
5581 return pn;
5584 op = CURRENT_TOKEN(ts).t_op;
5585 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
5586 continue;
5587 switch (pn2->pn_type) {
5588 case TOK_NAME:
5589 pn2->pn_op = JSOP_SETNAME;
5590 NoteLValue(cx, pn2, tc);
5591 break;
5592 case TOK_DOT:
5593 pn2->pn_op = JSOP_SETPROP;
5594 break;
5595 case TOK_LB:
5596 pn2->pn_op = JSOP_SETELEM;
5597 break;
5598 #if JS_HAS_DESTRUCTURING
5599 case TOK_RB:
5600 case TOK_RC:
5601 if (op != JSOP_NOP) {
5602 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5603 JSMSG_BAD_DESTRUCT_ASS);
5604 return NULL;
5606 pn = AssignExpr(cx, ts, tc);
5607 if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
5608 return NULL;
5609 return NewBinary(TOK_ASSIGN, op, pn2, pn, tc);
5610 #endif
5611 #if JS_HAS_LVALUE_RETURN
5612 case TOK_LP:
5613 if (!MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5614 return NULL;
5615 break;
5616 #endif
5617 #if JS_HAS_XML_SUPPORT
5618 case TOK_UNARYOP:
5619 if (pn2->pn_op == JSOP_XMLNAME) {
5620 pn2->pn_op = JSOP_SETXMLNAME;
5621 break;
5623 /* FALL THROUGH */
5624 #endif
5625 default:
5626 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5627 JSMSG_BAD_LEFTSIDE_OF_ASS);
5628 return NULL;
5631 return NewBinary(TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
5634 static JSParseNode *
5635 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5637 JSParseNode *pn, *pn1, *pn2, *pn3;
5638 uintN oldflags;
5640 pn = OrExpr(cx, ts, tc);
5641 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
5642 pn1 = pn;
5643 pn = NewParseNode(PN_TERNARY, tc);
5644 if (!pn)
5645 return NULL;
5647 * Always accept the 'in' operator in the middle clause of a ternary,
5648 * where it's unambiguous, even if we might be parsing the init of a
5649 * for statement.
5651 oldflags = tc->flags;
5652 tc->flags &= ~TCF_IN_FOR_INIT;
5653 pn2 = AssignExpr(cx, ts, tc);
5654 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5656 if (!pn2)
5657 return NULL;
5658 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
5659 pn3 = AssignExpr(cx, ts, tc);
5660 if (!pn3)
5661 return NULL;
5662 pn->pn_pos.begin = pn1->pn_pos.begin;
5663 pn->pn_pos.end = pn3->pn_pos.end;
5664 pn->pn_kid1 = pn1;
5665 pn->pn_kid2 = pn2;
5666 pn->pn_kid3 = pn3;
5668 return pn;
5671 static JSParseNode *
5672 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5674 JSParseNode *pn;
5676 pn = AndExpr(cx, ts, tc);
5677 while (pn && js_MatchToken(cx, ts, TOK_OR))
5678 pn = NewBinary(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
5679 return pn;
5682 static JSParseNode *
5683 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5685 JSParseNode *pn;
5687 pn = BitOrExpr(cx, ts, tc);
5688 while (pn && js_MatchToken(cx, ts, TOK_AND))
5689 pn = NewBinary(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
5690 return pn;
5693 static JSParseNode *
5694 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5696 JSParseNode *pn;
5698 pn = BitXorExpr(cx, ts, tc);
5699 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
5700 pn = NewBinary(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
5701 tc);
5703 return pn;
5706 static JSParseNode *
5707 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5709 JSParseNode *pn;
5711 pn = BitAndExpr(cx, ts, tc);
5712 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
5713 pn = NewBinary(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
5714 tc);
5716 return pn;
5719 static JSParseNode *
5720 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5722 JSParseNode *pn;
5724 pn = EqExpr(cx, ts, tc);
5725 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
5726 pn = NewBinary(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
5727 return pn;
5730 static JSParseNode *
5731 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5733 JSParseNode *pn;
5734 JSOp op;
5736 pn = RelExpr(cx, ts, tc);
5737 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
5738 op = CURRENT_TOKEN(ts).t_op;
5739 pn = NewBinary(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
5741 return pn;
5744 static JSParseNode *
5745 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5747 JSParseNode *pn;
5748 JSTokenType tt;
5749 JSOp op;
5750 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
5753 * Uses of the in operator in ShiftExprs are always unambiguous,
5754 * so unset the flag that prohibits recognizing it.
5756 tc->flags &= ~TCF_IN_FOR_INIT;
5758 pn = ShiftExpr(cx, ts, tc);
5759 while (pn &&
5760 (js_MatchToken(cx, ts, TOK_RELOP) ||
5762 * Recognize the 'in' token as an operator only if we're not
5763 * currently in the init expr of a for loop.
5765 (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
5766 js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
5767 tt = CURRENT_TOKEN(ts).type;
5768 op = CURRENT_TOKEN(ts).t_op;
5769 pn = NewBinary(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
5771 /* Restore previous state of inForInit flag. */
5772 tc->flags |= inForInitFlag;
5774 return pn;
5777 static JSParseNode *
5778 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5780 JSParseNode *pn;
5781 JSOp op;
5783 pn = AddExpr(cx, ts, tc);
5784 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
5785 op = CURRENT_TOKEN(ts).t_op;
5786 pn = NewBinary(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
5788 return pn;
5791 static JSParseNode *
5792 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5794 JSParseNode *pn;
5795 JSTokenType tt;
5796 JSOp op;
5798 pn = MulExpr(cx, ts, tc);
5799 while (pn &&
5800 (js_MatchToken(cx, ts, TOK_PLUS) ||
5801 js_MatchToken(cx, ts, TOK_MINUS))) {
5802 tt = CURRENT_TOKEN(ts).type;
5803 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
5804 pn = NewBinary(tt, op, pn, MulExpr(cx, ts, tc), tc);
5806 return pn;
5809 static JSParseNode *
5810 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5812 JSParseNode *pn;
5813 JSTokenType tt;
5814 JSOp op;
5816 pn = UnaryExpr(cx, ts, tc);
5817 while (pn &&
5818 (js_MatchToken(cx, ts, TOK_STAR) ||
5819 js_MatchToken(cx, ts, TOK_DIVOP))) {
5820 tt = CURRENT_TOKEN(ts).type;
5821 op = CURRENT_TOKEN(ts).t_op;
5822 pn = NewBinary(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
5824 return pn;
5827 static JSParseNode *
5828 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
5829 const char *name)
5831 while (kid->pn_type == TOK_RP)
5832 kid = kid->pn_kid;
5833 if (kid->pn_type != TOK_NAME &&
5834 kid->pn_type != TOK_DOT &&
5835 #if JS_HAS_LVALUE_RETURN
5836 (kid->pn_type != TOK_LP ||
5837 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
5838 #endif
5839 #if JS_HAS_XML_SUPPORT
5840 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
5841 #endif
5842 kid->pn_type != TOK_LB) {
5843 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5844 JSMSG_BAD_OPERAND, name);
5845 return NULL;
5847 pn->pn_kid = kid;
5848 return kid;
5851 static const char incop_name_str[][10] = {"increment", "decrement"};
5853 static JSBool
5854 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5855 JSParseNode *pn, JSParseNode *kid,
5856 JSTokenType tt, JSBool preorder)
5858 JSOp op;
5860 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
5861 if (!kid)
5862 return JS_FALSE;
5863 switch (kid->pn_type) {
5864 case TOK_NAME:
5865 op = (tt == TOK_INC)
5866 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
5867 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
5868 NoteLValue(cx, kid, tc);
5869 break;
5871 case TOK_DOT:
5872 op = (tt == TOK_INC)
5873 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
5874 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
5875 break;
5877 #if JS_HAS_LVALUE_RETURN
5878 case TOK_LP:
5879 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
5880 return JS_FALSE;
5881 /* FALL THROUGH */
5882 #endif
5883 #if JS_HAS_XML_SUPPORT
5884 case TOK_UNARYOP:
5885 if (kid->pn_op == JSOP_XMLNAME)
5886 kid->pn_op = JSOP_SETXMLNAME;
5887 /* FALL THROUGH */
5888 #endif
5889 case TOK_LB:
5890 op = (tt == TOK_INC)
5891 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
5892 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
5893 break;
5895 default:
5896 JS_ASSERT(0);
5897 op = JSOP_NOP;
5899 pn->pn_op = op;
5900 return JS_TRUE;
5903 static JSParseNode *
5904 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5906 JSTokenType tt;
5907 JSParseNode *pn, *pn2;
5909 JS_CHECK_RECURSION(cx, return NULL);
5911 ts->flags |= TSF_OPERAND;
5912 tt = js_GetToken(cx, ts);
5913 ts->flags &= ~TSF_OPERAND;
5915 switch (tt) {
5916 case TOK_UNARYOP:
5917 case TOK_PLUS:
5918 case TOK_MINUS:
5919 pn = NewParseNode(PN_UNARY, tc);
5920 if (!pn)
5921 return NULL;
5922 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
5923 pn->pn_op = CURRENT_TOKEN(ts).t_op;
5924 pn2 = UnaryExpr(cx, ts, tc);
5925 if (!pn2)
5926 return NULL;
5927 pn->pn_pos.end = pn2->pn_pos.end;
5928 pn->pn_kid = pn2;
5929 break;
5931 case TOK_INC:
5932 case TOK_DEC:
5933 pn = NewParseNode(PN_UNARY, tc);
5934 if (!pn)
5935 return NULL;
5936 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
5937 if (!pn2)
5938 return NULL;
5939 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
5940 return NULL;
5941 pn->pn_pos.end = pn2->pn_pos.end;
5942 break;
5944 case TOK_DELETE:
5945 pn = NewParseNode(PN_UNARY, tc);
5946 if (!pn)
5947 return NULL;
5948 pn2 = UnaryExpr(cx, ts, tc);
5949 if (!pn2)
5950 return NULL;
5951 pn->pn_pos.end = pn2->pn_pos.end;
5954 * Under ECMA3, deleting any unary expression is valid -- it simply
5955 * returns true. Here we strip off any parentheses and fold constants
5956 * before checking for a call expression, in order to rule out delete
5957 * of a generator expression.
5959 while (pn2->pn_type == TOK_RP)
5960 pn2 = pn2->pn_kid;
5961 if (!js_FoldConstants(cx, pn2, tc))
5962 return NULL;
5963 switch (pn2->pn_type) {
5964 case TOK_LP:
5965 if (pn2->pn_op != JSOP_SETCALL &&
5966 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
5967 return NULL;
5969 break;
5970 case TOK_NAME:
5971 pn2->pn_op = JSOP_DELNAME;
5972 break;
5973 default:;
5975 pn->pn_kid = pn2;
5976 break;
5978 case TOK_ERROR:
5979 return NULL;
5981 default:
5982 js_UngetToken(ts);
5983 pn = MemberExpr(cx, ts, tc, JS_TRUE);
5984 if (!pn)
5985 return NULL;
5987 /* Don't look across a newline boundary for a postfix incop. */
5988 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
5989 ts->flags |= TSF_OPERAND;
5990 tt = js_PeekTokenSameLine(cx, ts);
5991 ts->flags &= ~TSF_OPERAND;
5992 if (tt == TOK_INC || tt == TOK_DEC) {
5993 (void) js_GetToken(cx, ts);
5994 pn2 = NewParseNode(PN_UNARY, tc);
5995 if (!pn2)
5996 return NULL;
5997 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
5998 return NULL;
5999 pn2->pn_pos.begin = pn->pn_pos.begin;
6000 pn = pn2;
6003 break;
6005 return pn;
6008 #if JS_HAS_GENERATORS
6011 * A dedicated helper for transplanting the comprehension expression E in
6013 * [E for (V in I)] // array comprehension
6014 * (E for (V in I)) // generator expression
6016 * from its initial location in the AST, on the left of the 'for', to its final
6017 * position on the right. To avoid a separate pass we do this by adjusting the
6018 * blockids and name binding links that were established when E was parsed.
6020 * A generator expression desugars like so:
6022 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6024 * so the transplanter must adjust static level as well as blockid. E's source
6025 * coordinates in root->pn_pos are critical to deciding which binding links to
6026 * preserve and which to cut.
6028 * NB: This is not a general tree transplanter -- it knows in particular that
6029 * the one or more bindings induced by V have not yet been created.
6031 class CompExprTransplanter {
6032 JSParseNode *root;
6033 JSTreeContext *tc;
6034 bool genexp;
6035 uintN adjust;
6036 uintN funcLevel;
6038 public:
6039 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6040 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6044 bool transplant(JSParseNode *pn);
6048 * Any definitions nested within the comprehension expression of a generator
6049 * expression must move "down" one static level, which of course increases the
6050 * upvar-frame-skip count.
6052 static void
6053 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6055 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6056 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6058 JS_ASSERT(level >= tc->staticLevel);
6059 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6063 static void
6064 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6066 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6067 pn->pn_blockid += adjust;
6068 if (pn->pn_blockid >= tc->blockidGen)
6069 tc->blockidGen = pn->pn_blockid + 1;
6072 bool
6073 CompExprTransplanter::transplant(JSParseNode *pn)
6075 if (!pn)
6076 return true;
6078 switch (pn->pn_arity) {
6079 case PN_LIST:
6080 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6081 transplant(pn2);
6082 if (pn->pn_pos >= root->pn_pos)
6083 AdjustBlockId(pn, adjust, tc);
6084 break;
6086 case PN_TERNARY:
6087 transplant(pn->pn_kid1);
6088 transplant(pn->pn_kid2);
6089 transplant(pn->pn_kid3);
6090 break;
6092 case PN_BINARY:
6093 transplant(pn->pn_left);
6094 transplant(pn->pn_right);
6095 break;
6097 case PN_UNARY:
6098 transplant(pn->pn_kid);
6099 break;
6101 case PN_FUNC:
6104 * Only the first level of transplant recursion through functions needs
6105 * to reparent the funbox, since all descendant functions are correctly
6106 * linked under the top-most funbox. But every visit to this case needs
6107 * to update funbox->level.
6109 * Recall that funbox->level is the static level of the code containing
6110 * the definition or expression of the function and not the static level
6111 * of the function's body.
6113 JSFunctionBox *funbox = pn->pn_funbox;
6115 funbox->level = tc->staticLevel + funcLevel;
6116 if (++funcLevel == 1 && genexp) {
6117 JSFunctionBox *parent = tc->funbox;
6119 JSFunctionBox **funboxp = &tc->parent->functionList;
6120 while (*funboxp != funbox)
6121 funboxp = &(*funboxp)->siblings;
6122 *funboxp = funbox->siblings;
6124 funbox->parent = parent;
6125 funbox->siblings = parent->kids;
6126 parent->kids = funbox;
6127 funbox->level = tc->staticLevel;
6129 /* FALL THROUGH */
6132 case PN_NAME:
6133 transplant(pn->maybeExpr());
6134 if (pn->pn_arity == PN_FUNC)
6135 --funcLevel;
6137 if (pn->pn_defn) {
6138 if (genexp)
6139 BumpStaticLevel(pn, tc);
6140 } else if (pn->pn_used) {
6141 JS_ASSERT(pn->pn_op != JSOP_NOP);
6142 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6144 JSDefinition *dn = pn->pn_lexdef;
6145 JS_ASSERT(dn->pn_defn);
6148 * Adjust the definition's block id only if it is a placeholder not
6149 * to the left of the root node, and if pn is the last use visited
6150 * in the comprehension expression (to avoid adjusting the blockid
6151 * multiple times).
6153 * Non-placeholder definitions within the comprehension expression
6154 * will be visited further below.
6156 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6157 if (genexp)
6158 BumpStaticLevel(dn, tc);
6159 AdjustBlockId(dn, adjust, tc);
6162 JSAtom *atom = pn->pn_atom;
6163 #ifdef DEBUG
6164 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6165 JS_ASSERT(!stmt || stmt != tc->topStmt);
6166 #endif
6167 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6168 JS_ASSERT(!tc->decls.lookup(atom));
6170 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6171 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
6172 if (!ale)
6173 return NULL;
6175 if (dn->pn_pos >= root->pn_pos) {
6176 tc->parent->lexdeps.remove(tc->compiler, atom);
6177 } else {
6178 JSDefinition *dn2 = (JSDefinition *)
6179 NewNameNode(tc->compiler->context, TS(tc->compiler), dn->pn_atom, tc);
6180 if (!dn2)
6181 return NULL;
6183 dn2->pn_type = dn->pn_type;
6184 dn2->pn_pos = root->pn_pos;
6185 dn2->pn_defn = true;
6186 dn2->pn_dflags |= PND_FORWARD | PND_PLACEHOLDER;
6188 JSParseNode **pnup = &dn->dn_uses;
6189 JSParseNode *pnu;
6190 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6191 pnu->pn_lexdef = dn2;
6192 dn2->pn_dflags |= pnu->pn_dflags & (PND_ASSIGNED | PND_FUNARG);
6193 pnup = &pnu->pn_link;
6195 dn2->dn_uses = dn->dn_uses;
6196 dn->dn_uses = *pnup;
6197 *pnup = NULL;
6199 dn = dn2;
6202 ALE_SET_DEFN(ale, dn);
6207 if (pn->pn_pos >= root->pn_pos)
6208 AdjustBlockId(pn, adjust, tc);
6209 break;
6211 case PN_NAMESET:
6212 transplant(pn->pn_tree);
6213 break;
6215 return true;
6219 * Starting from a |for| keyword after the first array initialiser element or
6220 * an expression in an open parenthesis, parse the tail of the comprehension
6221 * or generator expression signified by this |for| keyword in context.
6223 * Return null on failure, else return the top-most parse node for the array
6224 * comprehension or generator expression, with a unary node as the body of the
6225 * (possibly nested) for-loop, initialized by |type, op, kid|.
6227 static JSParseNode *
6228 ComprehensionTail(JSParseNode *kid, uintN blockid, JSTreeContext *tc,
6229 JSTokenType type = TOK_SEMI, JSOp op = JSOP_NOP)
6231 JSContext *cx = tc->compiler->context;
6232 JSTokenStream *ts = TS(tc->compiler);
6234 uintN adjust;
6235 JSParseNode *pn, *pn2, *pn3, **pnp;
6236 JSStmtInfo stmtInfo;
6237 BindData data;
6238 JSTokenType tt;
6239 JSAtom *atom;
6241 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
6243 if (type == TOK_SEMI) {
6245 * Generator expression desugars to an immediately applied lambda that
6246 * yields the next value from a for-in loop (possibly nested, and with
6247 * optional if guard). Make pn be the TOK_LC body node.
6249 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6250 if (!pn)
6251 return NULL;
6252 adjust = pn->pn_blockid - blockid;
6253 } else {
6254 JS_ASSERT(type == TOK_ARRAYPUSH);
6257 * Make a parse-node and literal object representing the block scope of
6258 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6259 * aka the array initialiser case, has passed the blockid to claim for
6260 * the comprehension's block scope. We allocate that id or one above it
6261 * here, by calling js_PushLexicalScope.
6263 * In the case of a comprehension expression that has nested blocks
6264 * (e.g., let expressions), we will allocate a higher blockid but then
6265 * slide all blocks "to the right" to make room for the comprehension's
6266 * block scope.
6268 adjust = tc->blockid();
6269 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6270 if (!pn)
6271 return NULL;
6273 JS_ASSERT(blockid <= pn->pn_blockid);
6274 JS_ASSERT(blockid < tc->blockidGen);
6275 JS_ASSERT(tc->bodyid < blockid);
6276 pn->pn_blockid = stmtInfo.blockid = blockid;
6277 JS_ASSERT(adjust < blockid);
6278 adjust = blockid - adjust;
6281 pnp = &pn->pn_expr;
6283 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6284 transplanter.transplant(kid);
6286 data.pn = NULL;
6287 data.op = JSOP_NOP;
6288 data.binder = BindLet;
6289 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6291 do {
6293 * FOR node is binary, left is loop control and right is body. Use
6294 * index to count each block-local let-variable on the left-hand side
6295 * of the IN.
6297 pn2 = NewParseNode(PN_BINARY, tc);
6298 if (!pn2)
6299 return NULL;
6301 pn2->pn_op = JSOP_ITER;
6302 pn2->pn_iflags = JSITER_ENUMERATE;
6303 if (js_MatchToken(cx, ts, TOK_NAME)) {
6304 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
6305 pn2->pn_iflags |= JSITER_FOREACH;
6306 else
6307 js_UngetToken(ts);
6309 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6311 atom = NULL;
6312 tt = js_GetToken(cx, ts);
6313 switch (tt) {
6314 #if JS_HAS_DESTRUCTURING
6315 case TOK_LB:
6316 case TOK_LC:
6317 ts->flags |= TSF_DESTRUCTURING;
6318 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6319 ts->flags &= ~TSF_DESTRUCTURING;
6320 if (!pn3)
6321 return NULL;
6322 break;
6323 #endif
6325 case TOK_NAME:
6326 atom = CURRENT_TOKEN(ts).t_atom;
6329 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6330 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6331 * in the operand stack frame. The code generator computes that,
6332 * and it tries to bind all names to slots, so we must let it do
6333 * the deed.
6335 pn3 = NewBindingNode(ts, atom, tc, true);
6336 if (!pn3)
6337 return NULL;
6338 break;
6340 default:
6341 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6342 JSMSG_NO_VARIABLE_NAME);
6344 case TOK_ERROR:
6345 return NULL;
6348 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6349 JSParseNode *pn4 = Expr(cx, ts, tc);
6350 if (!pn4)
6351 return NULL;
6352 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6354 switch (tt) {
6355 #if JS_HAS_DESTRUCTURING
6356 case TOK_LB:
6357 case TOK_LC:
6358 if (!CheckDestructuring(cx, &data, pn3, NULL, tc))
6359 return NULL;
6361 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
6362 /* Destructuring requires [key, value] enumeration in JS1.7. */
6363 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6364 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6365 JSMSG_BAD_FOR_LEFTSIDE);
6366 return NULL;
6369 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6370 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6371 if (!(pn2->pn_iflags & JSITER_FOREACH))
6372 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6374 break;
6375 #endif
6377 case TOK_NAME:
6378 data.pn = pn3;
6379 if (!data.binder(cx, &data, atom, tc))
6380 return NULL;
6381 break;
6383 default:;
6386 pn2->pn_left = NewBinary(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6387 if (!pn2->pn_left)
6388 return NULL;
6389 *pnp = pn2;
6390 pnp = &pn2->pn_right;
6391 } while (js_MatchToken(cx, ts, TOK_FOR));
6393 if (js_MatchToken(cx, ts, TOK_IF)) {
6394 pn2 = NewParseNode(PN_TERNARY, tc);
6395 if (!pn2)
6396 return NULL;
6397 pn2->pn_kid1 = Condition(cx, ts, tc);
6398 if (!pn2->pn_kid1)
6399 return NULL;
6400 *pnp = pn2;
6401 pnp = &pn2->pn_kid2;
6404 pn2 = NewParseNode(PN_UNARY, tc);
6405 if (!pn2)
6406 return NULL;
6407 pn2->pn_type = type;
6408 pn2->pn_op = op;
6409 pn2->pn_kid = kid;
6410 *pnp = pn2;
6412 if (type == TOK_ARRAYPUSH)
6413 PopStatement(tc);
6414 return pn;
6417 #if JS_HAS_GENERATOR_EXPRS
6420 * Starting from a |for| keyword after an expression, parse the comprehension
6421 * tail completing this generator expression. Wrap the expression at kid in a
6422 * generator function that is immediately called to evaluate to the generator
6423 * iterator that is the value of this generator expression.
6425 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6426 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6427 * expression-statement node that constitutes the body of the |for| loop(s) in
6428 * the generator function.
6430 * Note how unlike Python, we do not evaluate the expression to the right of
6431 * the first |in| in the chain of |for| heads. Instead, a generator expression
6432 * is merely sugar for a generator function expression and its application.
6434 static JSParseNode *
6435 GeneratorExpr(JSParseNode *pn, JSParseNode *kid, JSTreeContext *tc)
6437 /* Initialize pn, connecting it to kid. */
6438 JS_ASSERT(pn->pn_arity == PN_UNARY);
6439 pn->pn_type = TOK_YIELD;
6440 pn->pn_op = JSOP_YIELD;
6441 pn->pn_pos = kid->pn_pos;
6442 pn->pn_kid = kid;
6443 pn->pn_hidden = JS_TRUE;
6445 /* Make a new node for the desugared generator function. */
6446 JSParseNode *genfn = NewParseNode(PN_FUNC, tc);
6447 if (!genfn)
6448 return NULL;
6449 genfn->pn_type = TOK_FUNCTION;
6450 genfn->pn_op = JSOP_LAMBDA;
6451 JS_ASSERT(!genfn->pn_body);
6452 genfn->pn_dflags = PND_FUNARG;
6455 JSTreeContext gentc(tc->compiler);
6457 JSFunctionBox *funbox = EnterFunction(genfn, tc, &gentc);
6458 if (!funbox)
6459 return NULL;
6462 * We assume conservatively that any deoptimization flag in tc->flags
6463 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6464 * propagate these flags into genfn. For code simplicity we also do
6465 * not detect if the flags were only set in the kid and could be
6466 * removed from tc->flags.
6468 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6469 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6470 funbox->tcflags |= gentc.flags;
6471 genfn->pn_funbox = funbox;
6472 genfn->pn_blockid = gentc.bodyid;
6474 JSParseNode *body = ComprehensionTail(pn, tc->blockid(), &gentc);
6475 if (!body)
6476 return NULL;
6477 JS_ASSERT(!genfn->pn_body);
6478 genfn->pn_body = body;
6479 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6480 genfn->pn_pos.end = body->pn_pos.end = CURRENT_TOKEN(TS(tc->compiler)).pos.end;
6482 if (!LeaveFunction(genfn, &gentc, tc))
6483 return NULL;
6487 * Our result is a call expression that invokes the anonymous generator
6488 * function object.
6490 JSParseNode *result = NewParseNode(PN_LIST, tc);
6491 if (!result)
6492 return NULL;
6493 result->pn_type = TOK_LP;
6494 result->pn_op = JSOP_CALL;
6495 result->pn_pos.begin = genfn->pn_pos.begin;
6496 result->initList(genfn);
6497 return result;
6500 static const char js_generator_str[] = "generator";
6502 #endif /* JS_HAS_GENERATOR_EXPRS */
6503 #endif /* JS_HAS_GENERATORS */
6505 static JSBool
6506 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6507 JSParseNode *listNode)
6509 JSBool matched;
6511 ts->flags |= TSF_OPERAND;
6512 matched = js_MatchToken(cx, ts, TOK_RP);
6513 ts->flags &= ~TSF_OPERAND;
6514 if (!matched) {
6515 do {
6516 JSParseNode *argNode = AssignExpr(cx, ts, tc);
6517 if (!argNode)
6518 return JS_FALSE;
6519 #if JS_HAS_GENERATORS
6520 if (argNode->pn_type == TOK_YIELD &&
6521 js_PeekToken(cx, ts) == TOK_COMMA) {
6522 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6523 JSMSG_BAD_GENERATOR_SYNTAX,
6524 js_yield_str);
6525 return JS_FALSE;
6527 #endif
6528 #if JS_HAS_GENERATOR_EXPRS
6529 if (js_MatchToken(cx, ts, TOK_FOR)) {
6530 JSParseNode *pn = NewParseNode(PN_UNARY, tc);
6531 if (!pn)
6532 return JS_FALSE;
6533 argNode = GeneratorExpr(pn, argNode, tc);
6534 if (!argNode)
6535 return JS_FALSE;
6536 if (listNode->pn_count > 1 ||
6537 js_PeekToken(cx, ts) == TOK_COMMA) {
6538 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6539 JSMSG_BAD_GENERATOR_SYNTAX,
6540 js_generator_str);
6541 return JS_FALSE;
6544 #endif
6545 listNode->append(argNode);
6546 } while (js_MatchToken(cx, ts, TOK_COMMA));
6548 if (js_GetToken(cx, ts) != TOK_RP) {
6549 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6550 JSMSG_PAREN_AFTER_ARGS);
6551 return JS_FALSE;
6554 return JS_TRUE;
6557 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6558 static JSParseNode *
6559 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6561 while (pn->pn_type == TOK_RP)
6562 pn = pn->pn_kid;
6563 if (pn->pn_type == TOK_FUNCTION) {
6564 JS_ASSERT(pn->pn_arity == PN_FUNC);
6566 JSFunctionBox *funbox = pn->pn_funbox;
6567 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6568 if (!(funbox->tcflags & TCF_FUN_USES_ARGUMENTS))
6569 pn->pn_dflags &= ~PND_FUNARG;
6571 return pn;
6574 static JSParseNode *
6575 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6576 JSBool allowCallSyntax)
6578 JSParseNode *pn, *pn2, *pn3;
6579 JSTokenType tt;
6581 JS_CHECK_RECURSION(cx, return NULL);
6583 /* Check for new expression first. */
6584 ts->flags |= TSF_OPERAND;
6585 tt = js_GetToken(cx, ts);
6586 ts->flags &= ~TSF_OPERAND;
6587 if (tt == TOK_NEW) {
6588 pn = NewParseNode(PN_LIST, tc);
6589 if (!pn)
6590 return NULL;
6591 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
6592 if (!pn2)
6593 return NULL;
6594 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6595 pn->pn_op = JSOP_NEW;
6596 pn->initList(pn2);
6597 pn->pn_pos.begin = pn2->pn_pos.begin;
6599 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
6600 return NULL;
6601 if (pn->pn_count > ARGC_LIMIT) {
6602 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6603 JSMSG_TOO_MANY_CON_ARGS);
6604 return NULL;
6606 pn->pn_pos.end = pn->last()->pn_pos.end;
6607 } else {
6608 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6609 if (!pn)
6610 return NULL;
6612 if (pn->pn_type == TOK_ANYNAME ||
6613 pn->pn_type == TOK_AT ||
6614 pn->pn_type == TOK_DBLCOLON) {
6615 pn2 = NewOrRecycledNode(tc);
6616 if (!pn2)
6617 return NULL;
6618 pn2->pn_type = TOK_UNARYOP;
6619 pn2->pn_pos = pn->pn_pos;
6620 pn2->pn_op = JSOP_XMLNAME;
6621 pn2->pn_arity = PN_UNARY;
6622 pn2->pn_kid = pn;
6623 pn = pn2;
6627 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
6628 if (tt == TOK_DOT) {
6629 pn2 = NewNameNode(cx, ts, NULL, tc);
6630 if (!pn2)
6631 return NULL;
6632 #if JS_HAS_XML_SUPPORT
6633 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6634 tt = js_GetToken(cx, ts);
6635 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6636 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6637 if (!pn3)
6638 return NULL;
6639 tt = PN_TYPE(pn3);
6640 if (tt == TOK_NAME) {
6641 pn2->pn_op = JSOP_GETPROP;
6642 pn2->pn_expr = pn;
6643 pn2->pn_atom = pn3->pn_atom;
6644 RecycleTree(pn3, tc);
6645 } else {
6646 if (TOKEN_TYPE_IS_XML(tt)) {
6647 pn2->pn_type = TOK_LB;
6648 pn2->pn_op = JSOP_GETELEM;
6649 } else if (tt == TOK_RP) {
6650 JSParseNode *group = pn3;
6652 /* Recycle the useless TOK_RP node. */
6653 pn3 = group->pn_kid;
6654 group->pn_kid = NULL;
6655 RecycleTree(group, tc);
6656 pn2->pn_type = TOK_FILTER;
6657 pn2->pn_op = JSOP_FILTER;
6659 /* A filtering predicate is like a with statement. */
6660 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6661 } else {
6662 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6663 JSMSG_NAME_AFTER_DOT);
6664 return NULL;
6666 pn2->pn_arity = PN_BINARY;
6667 pn2->pn_left = pn;
6668 pn2->pn_right = pn3;
6670 #else
6671 ts->flags |= TSF_KEYWORD_IS_NAME;
6672 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
6673 ts->flags &= ~TSF_KEYWORD_IS_NAME;
6674 pn2->pn_op = JSOP_GETPROP;
6675 pn2->pn_expr = pn;
6676 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
6677 #endif
6678 pn2->pn_pos.begin = pn->pn_pos.begin;
6679 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6680 #if JS_HAS_XML_SUPPORT
6681 } else if (tt == TOK_DBLDOT) {
6682 pn2 = NewParseNode(PN_BINARY, tc);
6683 if (!pn2)
6684 return NULL;
6685 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6686 tt = js_GetToken(cx, ts);
6687 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6688 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6689 if (!pn3)
6690 return NULL;
6691 tt = PN_TYPE(pn3);
6692 if (tt == TOK_NAME) {
6693 pn3->pn_type = TOK_STRING;
6694 pn3->pn_arity = PN_NULLARY;
6695 pn3->pn_op = JSOP_QNAMEPART;
6696 } else if (!TOKEN_TYPE_IS_XML(tt)) {
6697 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6698 JSMSG_NAME_AFTER_DOT);
6699 return NULL;
6701 pn2->pn_op = JSOP_DESCENDANTS;
6702 pn2->pn_left = pn;
6703 pn2->pn_right = pn3;
6704 pn2->pn_pos.begin = pn->pn_pos.begin;
6705 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6706 #endif
6707 } else if (tt == TOK_LB) {
6708 pn2 = NewParseNode(PN_BINARY, tc);
6709 if (!pn2)
6710 return NULL;
6711 pn3 = Expr(cx, ts, tc);
6712 if (!pn3)
6713 return NULL;
6715 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
6716 pn2->pn_pos.begin = pn->pn_pos.begin;
6717 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6720 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6721 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6722 * the interpreter from fast property access. However, if the
6723 * bracketed string is a uint32, we rewrite pn3 to be a number
6724 * instead of a string.
6726 do {
6727 if (pn3->pn_type == TOK_STRING) {
6728 jsuint index;
6730 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
6731 pn2->pn_type = TOK_DOT;
6732 pn2->pn_op = JSOP_GETPROP;
6733 pn2->pn_arity = PN_NAME;
6734 pn2->pn_expr = pn;
6735 pn2->pn_atom = pn3->pn_atom;
6736 break;
6738 pn3->pn_type = TOK_NUMBER;
6739 pn3->pn_op = JSOP_DOUBLE;
6740 pn3->pn_dval = index;
6742 pn2->pn_op = JSOP_GETELEM;
6743 pn2->pn_left = pn;
6744 pn2->pn_right = pn3;
6745 } while (0);
6746 } else if (allowCallSyntax && tt == TOK_LP) {
6747 pn2 = NewParseNode(PN_LIST, tc);
6748 if (!pn2)
6749 return NULL;
6750 pn2->pn_op = JSOP_CALL;
6752 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6753 pn = CheckForImmediatelyAppliedLambda(pn);
6754 if (pn->pn_op == JSOP_NAME) {
6755 if (pn->pn_atom == cx->runtime->atomState.evalAtom) {
6756 /* Select JSOP_EVAL and flag tc as heavyweight. */
6757 pn2->pn_op = JSOP_EVAL;
6758 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6760 } else if (pn->pn_op == JSOP_GETPROP) {
6761 if (pn->pn_atom == cx->runtime->atomState.applyAtom ||
6762 pn->pn_atom == cx->runtime->atomState.callAtom) {
6763 /* Select JSOP_APPLY given foo.apply(...). */
6764 pn2->pn_op = JSOP_APPLY;
6768 pn2->initList(pn);
6769 pn2->pn_pos.begin = pn->pn_pos.begin;
6771 if (!ArgumentList(cx, ts, tc, pn2))
6772 return NULL;
6773 if (pn2->pn_count > ARGC_LIMIT) {
6774 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6775 JSMSG_TOO_MANY_FUN_ARGS);
6776 return NULL;
6778 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6779 } else {
6780 js_UngetToken(ts);
6781 return pn;
6784 pn = pn2;
6786 if (tt == TOK_ERROR)
6787 return NULL;
6788 return pn;
6791 static JSParseNode *
6792 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6794 uintN oldflags;
6795 JSParseNode *pn;
6798 * Always accept the 'in' operator in a parenthesized expression,
6799 * where it's unambiguous, even if we might be parsing the init of a
6800 * for statement.
6802 oldflags = tc->flags;
6803 tc->flags &= ~TCF_IN_FOR_INIT;
6804 pn = Expr(cx, ts, tc);
6805 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
6806 return pn;
6809 #if JS_HAS_XML_SUPPORT
6811 static JSParseNode *
6812 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6814 JSParseNode *pn;
6816 pn = BracketedExpr(cx, ts, tc);
6817 if (!pn)
6818 return NULL;
6820 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
6821 return pn;
6825 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
6827 * AttributeIdentifier:
6828 * @ PropertySelector
6829 * @ QualifiedIdentifier
6830 * @ [ Expression ]
6832 * PropertySelector:
6833 * Identifier
6836 * QualifiedIdentifier:
6837 * PropertySelector :: PropertySelector
6838 * PropertySelector :: [ Expression ]
6840 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
6842 * AttributeIdentifier:
6843 * @ QualifiedIdentifier
6844 * @ [ Expression ]
6846 * PropertySelector:
6847 * Identifier
6850 * QualifiedIdentifier:
6851 * PropertySelector :: PropertySelector
6852 * PropertySelector :: [ Expression ]
6853 * PropertySelector
6855 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
6856 * for that rule to result in a name node, but ECMA-357 extends the grammar
6857 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
6859 * QualifiedIdentifier:
6860 * PropertySelector QualifiedSuffix
6862 * QualifiedSuffix:
6863 * :: PropertySelector
6864 * :: [ Expression ]
6865 * /nothing/
6867 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
6869 * PrimaryExpression:
6870 * Identifier QualifiedSuffix
6872 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
6873 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
6875 static JSParseNode *
6876 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6878 JSParseNode *pn;
6880 pn = NewParseNode(PN_NULLARY, tc);
6881 if (!pn)
6882 return NULL;
6883 if (pn->pn_type == TOK_STAR) {
6884 pn->pn_type = TOK_ANYNAME;
6885 pn->pn_op = JSOP_ANYNAME;
6886 pn->pn_atom = cx->runtime->atomState.starAtom;
6887 } else {
6888 JS_ASSERT(pn->pn_type == TOK_NAME);
6889 pn->pn_op = JSOP_QNAMEPART;
6890 pn->pn_arity = PN_NAME;
6891 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
6892 pn->pn_cookie = FREE_UPVAR_COOKIE;
6894 return pn;
6897 static JSParseNode *
6898 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
6899 JSTreeContext *tc)
6901 JSParseNode *pn2, *pn3;
6902 JSTokenType tt;
6904 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
6905 pn2 = NewNameNode(cx, ts, NULL, tc);
6906 if (!pn2)
6907 return NULL;
6909 /* Left operand of :: must be evaluated if it is an identifier. */
6910 if (pn->pn_op == JSOP_QNAMEPART)
6911 pn->pn_op = JSOP_NAME;
6913 ts->flags |= TSF_KEYWORD_IS_NAME;
6914 tt = js_GetToken(cx, ts);
6915 ts->flags &= ~TSF_KEYWORD_IS_NAME;
6916 if (tt == TOK_STAR || tt == TOK_NAME) {
6917 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
6918 pn2->pn_op = JSOP_QNAMECONST;
6919 pn2->pn_pos.begin = pn->pn_pos.begin;
6920 pn2->pn_atom = (tt == TOK_STAR)
6921 ? cx->runtime->atomState.starAtom
6922 : CURRENT_TOKEN(ts).t_atom;
6923 pn2->pn_expr = pn;
6924 pn2->pn_cookie = FREE_UPVAR_COOKIE;
6925 return pn2;
6928 if (tt != TOK_LB) {
6929 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6930 JSMSG_SYNTAX_ERROR);
6931 return NULL;
6933 pn3 = EndBracketedExpr(cx, ts, tc);
6934 if (!pn3)
6935 return NULL;
6937 pn2->pn_op = JSOP_QNAME;
6938 pn2->pn_arity = PN_BINARY;
6939 pn2->pn_pos.begin = pn->pn_pos.begin;
6940 pn2->pn_pos.end = pn3->pn_pos.end;
6941 pn2->pn_left = pn;
6942 pn2->pn_right = pn3;
6943 return pn2;
6946 static JSParseNode *
6947 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6949 JSParseNode *pn;
6951 pn = PropertySelector(cx, ts, tc);
6952 if (!pn)
6953 return NULL;
6954 if (js_MatchToken(cx, ts, TOK_DBLCOLON))
6955 pn = QualifiedSuffix(cx, ts, pn, tc);
6956 return pn;
6959 static JSParseNode *
6960 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6962 JSParseNode *pn, *pn2;
6963 JSTokenType tt;
6965 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
6966 pn = NewParseNode(PN_UNARY, tc);
6967 if (!pn)
6968 return NULL;
6969 pn->pn_op = JSOP_TOATTRNAME;
6970 ts->flags |= TSF_KEYWORD_IS_NAME;
6971 tt = js_GetToken(cx, ts);
6972 ts->flags &= ~TSF_KEYWORD_IS_NAME;
6973 if (tt == TOK_STAR || tt == TOK_NAME) {
6974 pn2 = QualifiedIdentifier(cx, ts, tc);
6975 } else if (tt == TOK_LB) {
6976 pn2 = EndBracketedExpr(cx, ts, tc);
6977 } else {
6978 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6979 JSMSG_SYNTAX_ERROR);
6980 return NULL;
6982 if (!pn2)
6983 return NULL;
6984 pn->pn_kid = pn2;
6985 return pn;
6989 * Make a TOK_LC unary node whose pn_kid is an expression.
6991 static JSParseNode *
6992 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
6994 JSParseNode *pn, *pn2;
6995 uintN oldflags;
6997 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
6998 pn = NewParseNode(PN_UNARY, tc);
6999 if (!pn)
7000 return NULL;
7003 * Turn off XML tag mode, but don't restore it after parsing this braced
7004 * expression. Instead, simply restore ts's old flags. This is required
7005 * because XMLExpr is called both from within a tag, and from within text
7006 * contained in an element, but outside of any start, end, or point tag.
7008 oldflags = ts->flags;
7009 ts->flags = oldflags & ~TSF_XMLTAGMODE;
7010 pn2 = Expr(cx, ts, tc);
7011 if (!pn2)
7012 return NULL;
7014 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7015 ts->flags = oldflags;
7016 pn->pn_kid = pn2;
7017 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7018 return pn;
7022 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7023 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7024 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7025 * child of a container tag.
7027 static JSParseNode *
7028 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7030 JSParseNode *pn;
7031 JSToken *tp;
7033 pn = NewParseNode(PN_NULLARY, tc);
7034 if (!pn)
7035 return NULL;
7036 tp = &CURRENT_TOKEN(ts);
7037 pn->pn_op = tp->t_op;
7038 pn->pn_atom = tp->t_atom;
7039 if (tp->type == TOK_XMLPI)
7040 pn->pn_atom2 = tp->t_atom2;
7041 return pn;
7045 * Parse the productions:
7047 * XMLNameExpr:
7048 * XMLName XMLNameExpr?
7049 * { Expr } XMLNameExpr?
7051 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7052 * a list of names and/or expressions, a single expression, or a single name.
7053 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7054 * will be TOK_LC.
7056 static JSParseNode *
7057 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7059 JSParseNode *pn, *pn2, *list;
7060 JSTokenType tt;
7062 pn = list = NULL;
7063 do {
7064 tt = CURRENT_TOKEN(ts).type;
7065 if (tt == TOK_LC) {
7066 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7067 if (!pn2)
7068 return NULL;
7069 } else {
7070 JS_ASSERT(tt == TOK_XMLNAME);
7071 pn2 = XMLAtomNode(cx, ts, tc);
7072 if (!pn2)
7073 return NULL;
7076 if (!pn) {
7077 pn = pn2;
7078 } else {
7079 if (!list) {
7080 list = NewParseNode(PN_LIST, tc);
7081 if (!list)
7082 return NULL;
7083 list->pn_type = TOK_XMLNAME;
7084 list->pn_pos.begin = pn->pn_pos.begin;
7085 list->initList(pn);
7086 list->pn_xflags = PNX_CANTFOLD;
7087 pn = list;
7089 pn->pn_pos.end = pn2->pn_pos.end;
7090 pn->append(pn2);
7092 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
7094 js_UngetToken(ts);
7095 return pn;
7099 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7100 * at compile time into a JSXML tree.
7102 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7103 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7104 : (pn)->pn_type != TOK_LC)
7107 * Parse the productions:
7109 * XMLTagContent:
7110 * XMLNameExpr
7111 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7112 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7114 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7115 * produces a list of name and attribute values and/or braced expressions, a
7116 * single expression, or a single name.
7118 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7119 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7120 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7121 * we parsed exactly one expression.
7123 static JSParseNode *
7124 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7125 JSTokenType tagtype, JSAtom **namep)
7127 JSParseNode *pn, *pn2, *list;
7128 JSTokenType tt;
7130 pn = XMLNameExpr(cx, ts, tc);
7131 if (!pn)
7132 return NULL;
7133 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7134 list = NULL;
7136 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
7137 tt = js_GetToken(cx, ts);
7138 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7139 js_UngetToken(ts);
7140 break;
7143 pn2 = XMLNameExpr(cx, ts, tc);
7144 if (!pn2)
7145 return NULL;
7146 if (!list) {
7147 list = NewParseNode(PN_LIST, tc);
7148 if (!list)
7149 return NULL;
7150 list->pn_type = tagtype;
7151 list->pn_pos.begin = pn->pn_pos.begin;
7152 list->initList(pn);
7153 pn = list;
7155 pn->append(pn2);
7156 if (!XML_FOLDABLE(pn2))
7157 pn->pn_xflags |= PNX_CANTFOLD;
7159 js_MatchToken(cx, ts, TOK_XMLSPACE);
7160 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7161 js_MatchToken(cx, ts, TOK_XMLSPACE);
7163 tt = js_GetToken(cx, ts);
7164 if (tt == TOK_XMLATTR) {
7165 pn2 = XMLAtomNode(cx, ts, tc);
7166 } else if (tt == TOK_LC) {
7167 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7168 pn->pn_xflags |= PNX_CANTFOLD;
7169 } else {
7170 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7171 JSMSG_BAD_XML_ATTR_VALUE);
7172 return NULL;
7174 if (!pn2)
7175 return NULL;
7176 pn->pn_pos.end = pn2->pn_pos.end;
7177 pn->append(pn2);
7180 return pn;
7183 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7184 JS_BEGIN_MACRO \
7185 if ((tt) <= TOK_EOF) { \
7186 if ((tt) == TOK_EOF) { \
7187 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7188 JSMSG_END_OF_XML_SOURCE); \
7190 return result; \
7192 JS_END_MACRO
7194 static JSParseNode *
7195 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7196 JSBool allowList);
7199 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7200 * that opens the end tag for the container.
7202 static JSBool
7203 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7204 JSTreeContext *tc)
7206 JSTokenType tt;
7207 JSParseNode *pn2;
7208 JSAtom *textAtom;
7210 ts->flags &= ~TSF_XMLTAGMODE;
7211 for (;;) {
7212 ts->flags |= TSF_XMLTEXTMODE;
7213 tt = js_GetToken(cx, ts);
7214 ts->flags &= ~TSF_XMLTEXTMODE;
7215 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7217 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7218 textAtom = CURRENT_TOKEN(ts).t_atom;
7219 if (textAtom) {
7220 /* Non-zero-length XML text scanned. */
7221 pn2 = XMLAtomNode(cx, ts, tc);
7222 if (!pn2)
7223 return JS_FALSE;
7224 pn->pn_pos.end = pn2->pn_pos.end;
7225 pn->append(pn2);
7228 ts->flags |= TSF_OPERAND;
7229 tt = js_GetToken(cx, ts);
7230 ts->flags &= ~TSF_OPERAND;
7231 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7232 if (tt == TOK_XMLETAGO)
7233 break;
7235 if (tt == TOK_LC) {
7236 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
7237 pn->pn_xflags |= PNX_CANTFOLD;
7238 } else if (tt == TOK_XMLSTAGO) {
7239 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
7240 if (pn2) {
7241 pn2->pn_xflags &= ~PNX_XMLROOT;
7242 pn->pn_xflags |= pn2->pn_xflags;
7244 } else {
7245 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7246 tt == TOK_XMLPI);
7247 pn2 = XMLAtomNode(cx, ts, tc);
7249 if (!pn2)
7250 return JS_FALSE;
7251 pn->pn_pos.end = pn2->pn_pos.end;
7252 pn->append(pn2);
7255 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
7256 ts->flags |= TSF_XMLTAGMODE;
7257 return JS_TRUE;
7261 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7263 static JSParseNode *
7264 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7265 JSBool allowList)
7267 JSParseNode *pn, *pn2, *list;
7268 JSTokenType tt;
7269 JSAtom *startAtom, *endAtom;
7271 JS_CHECK_RECURSION(cx, return NULL);
7273 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
7274 pn = NewParseNode(PN_LIST, tc);
7275 if (!pn)
7276 return NULL;
7278 ts->flags |= TSF_XMLTAGMODE;
7279 tt = js_GetToken(cx, ts);
7280 if (tt == TOK_ERROR)
7281 return NULL;
7283 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7285 * XMLElement. Append the tag and its contents, if any, to pn.
7287 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
7288 if (!pn2)
7289 return NULL;
7290 js_MatchToken(cx, ts, TOK_XMLSPACE);
7292 tt = js_GetToken(cx, ts);
7293 if (tt == TOK_XMLPTAGC) {
7294 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7295 if (pn2->pn_type == TOK_XMLSTAGO) {
7296 pn->makeEmpty();
7297 RecycleTree(pn, tc);
7298 pn = pn2;
7299 } else {
7300 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7301 pn2->pn_type == TOK_LC);
7302 pn->initList(pn2);
7303 if (!XML_FOLDABLE(pn2))
7304 pn->pn_xflags |= PNX_CANTFOLD;
7306 pn->pn_type = TOK_XMLPTAGC;
7307 pn->pn_xflags |= PNX_XMLROOT;
7308 } else {
7309 /* We had better have a tag-close (>) at this point. */
7310 if (tt != TOK_XMLTAGC) {
7311 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7312 JSMSG_BAD_XML_TAG_SYNTAX);
7313 return NULL;
7315 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7317 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7318 if (pn2->pn_type != TOK_XMLSTAGO) {
7319 pn->initList(pn2);
7320 if (!XML_FOLDABLE(pn2))
7321 pn->pn_xflags |= PNX_CANTFOLD;
7322 pn2 = pn;
7323 pn = NewParseNode(PN_LIST, tc);
7324 if (!pn)
7325 return NULL;
7328 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7329 pn->pn_type = TOK_XMLELEM;
7330 pn->pn_pos.begin = pn2->pn_pos.begin;
7331 pn->initList(pn2);
7332 if (!XML_FOLDABLE(pn2))
7333 pn->pn_xflags |= PNX_CANTFOLD;
7334 pn->pn_xflags |= PNX_XMLROOT;
7336 /* Get element contents and delimiting end-tag-open sequence. */
7337 if (!XMLElementContent(cx, ts, pn, tc))
7338 return NULL;
7340 tt = js_GetToken(cx, ts);
7341 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7342 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7343 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7344 JSMSG_BAD_XML_TAG_SYNTAX);
7345 return NULL;
7348 /* Parse end tag; check mismatch at compile-time if we can. */
7349 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
7350 if (!pn2)
7351 return NULL;
7352 if (pn2->pn_type == TOK_XMLETAGO) {
7353 /* Oops, end tag has attributes! */
7354 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7355 JSMSG_BAD_XML_TAG_SYNTAX);
7356 return NULL;
7358 if (endAtom && startAtom && endAtom != startAtom) {
7359 JSString *str = ATOM_TO_STRING(startAtom);
7361 /* End vs. start tag name mismatch: point to the tag name. */
7362 js_ReportCompileErrorNumber(cx, ts, pn2,
7363 JSREPORT_UC | JSREPORT_ERROR,
7364 JSMSG_XML_TAG_NAME_MISMATCH,
7365 JSSTRING_CHARS(str));
7366 return NULL;
7369 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7370 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7371 list = NewParseNode(PN_LIST, tc);
7372 if (!list)
7373 return NULL;
7374 list->pn_type = TOK_XMLETAGO;
7375 list->initList(pn2);
7376 pn->append(list);
7377 if (!XML_FOLDABLE(pn2)) {
7378 list->pn_xflags |= PNX_CANTFOLD;
7379 pn->pn_xflags |= PNX_CANTFOLD;
7382 js_MatchToken(cx, ts, TOK_XMLSPACE);
7383 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7386 /* Set pn_op now that pn has been updated to its final value. */
7387 pn->pn_op = JSOP_TOXML;
7388 } else if (allowList && tt == TOK_XMLTAGC) {
7389 /* XMLList Initialiser. */
7390 pn->pn_type = TOK_XMLLIST;
7391 pn->pn_op = JSOP_TOXMLLIST;
7392 pn->makeEmpty();
7393 pn->pn_xflags |= PNX_XMLROOT;
7394 if (!XMLElementContent(cx, ts, pn, tc))
7395 return NULL;
7397 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7398 } else {
7399 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7400 JSMSG_BAD_XML_NAME_SYNTAX);
7401 return NULL;
7404 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7405 ts->flags &= ~TSF_XMLTAGMODE;
7406 return pn;
7409 static JSParseNode *
7410 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7411 JSBool allowList)
7413 uint32 oldopts;
7414 JSParseNode *pn;
7417 * Force XML support to be enabled so that comments and CDATA literals
7418 * are recognized, instead of <! followed by -- starting an HTML comment
7419 * to end of line (used in script tags to hide content from old browsers
7420 * that don't recognize <script>).
7422 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
7423 pn = XMLElementOrList(cx, ts, tc, allowList);
7424 JS_SetOptions(cx, oldopts);
7425 return pn;
7428 JSParseNode *
7429 JSCompiler::parseXMLText(JSObject *chain, bool allowList)
7432 * Push a compiler frame if we have no frames, or if the top frame is a
7433 * lightweight function activation, or if its scope chain doesn't match
7434 * the one passed to us.
7436 JSTreeContext tc(this);
7437 tc.scopeChain = chain;
7439 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7440 TS(this)->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
7441 JSTokenType tt = js_GetToken(context, TS(this));
7442 TS(this)->flags &= ~TSF_OPERAND;
7444 JSParseNode *pn;
7445 if (tt != TOK_XMLSTAGO) {
7446 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
7447 JSMSG_BAD_XML_MARKUP);
7448 pn = NULL;
7449 } else {
7450 pn = XMLElementOrListRoot(context, TS(this), &tc, allowList);
7453 TS(this)->flags &= ~TSF_XMLONLYMODE;
7454 return pn;
7457 #endif /* JS_HAS_XMLSUPPORT */
7459 #if JS_HAS_BLOCK_SCOPE
7461 * Check whether blockid is an active scoping statement in tc. This code is
7462 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7463 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7464 * and let blocks and expressions (not let declarations).
7466 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7467 * due to hoisting, let in a for loop head, let block, or let expression acts
7468 * like Scheme's let: initializers are evaluated without the new let bindings
7469 * being in scope.
7471 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7472 * bindings push on the front of the tc->decls JSAtomList (either the singular
7473 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7474 * scope bindings of the same name.
7476 * This simplifies binding lookup code at the price of a linear search here,
7477 * but only if code uses let (var predominates), and even then this function's
7478 * loop iterates more than once only in crazy cases.
7480 static inline bool
7481 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7483 if (blockid > tc->blockid())
7484 return false;
7485 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7486 if (stmt->blockid == blockid)
7487 return true;
7489 return false;
7491 #endif
7493 static JSParseNode *
7494 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7495 JSTokenType tt, JSBool afterDot)
7497 JSParseNode *pn, *pn2, *pn3;
7498 JSOp op;
7500 JS_CHECK_RECURSION(cx, return NULL);
7502 #if JS_HAS_GETTER_SETTER
7503 if (tt == TOK_NAME) {
7504 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
7505 if (tt == TOK_ERROR)
7506 return NULL;
7508 #endif
7510 switch (tt) {
7511 case TOK_FUNCTION:
7512 #if JS_HAS_XML_SUPPORT
7513 ts->flags |= TSF_KEYWORD_IS_NAME;
7514 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7515 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7516 pn2 = NewParseNode(PN_NULLARY, tc);
7517 if (!pn2)
7518 return NULL;
7519 pn2->pn_type = TOK_FUNCTION;
7520 pn = QualifiedSuffix(cx, ts, pn2, tc);
7521 if (!pn)
7522 return NULL;
7523 break;
7525 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7526 #endif
7527 pn = FunctionExpr(cx, ts, tc);
7528 if (!pn)
7529 return NULL;
7530 break;
7532 case TOK_LB:
7534 JSBool matched;
7535 jsuint index;
7537 pn = NewParseNode(PN_LIST, tc);
7538 if (!pn)
7539 return NULL;
7540 pn->pn_type = TOK_RB;
7541 pn->pn_op = JSOP_NEWINIT;
7542 pn->makeEmpty();
7544 #if JS_HAS_GENERATORS
7545 pn->pn_blockid = tc->blockidGen;
7546 #endif
7548 ts->flags |= TSF_OPERAND;
7549 matched = js_MatchToken(cx, ts, TOK_RB);
7550 ts->flags &= ~TSF_OPERAND;
7551 if (!matched) {
7552 for (index = 0; ; index++) {
7553 if (index == ARRAY_INIT_LIMIT) {
7554 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7555 JSMSG_ARRAY_INIT_TOO_BIG);
7556 return NULL;
7559 ts->flags |= TSF_OPERAND;
7560 tt = js_PeekToken(cx, ts);
7561 ts->flags &= ~TSF_OPERAND;
7562 if (tt == TOK_RB) {
7563 pn->pn_xflags |= PNX_ENDCOMMA;
7564 break;
7567 if (tt == TOK_COMMA) {
7568 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7569 js_MatchToken(cx, ts, TOK_COMMA);
7570 pn2 = NewParseNode(PN_NULLARY, tc);
7571 pn->pn_xflags |= PNX_HOLEY;
7572 } else {
7573 pn2 = AssignExpr(cx, ts, tc);
7575 if (!pn2)
7576 return NULL;
7577 pn->append(pn2);
7579 if (tt != TOK_COMMA) {
7580 /* If we didn't already match TOK_COMMA in above case. */
7581 if (!js_MatchToken(cx, ts, TOK_COMMA))
7582 break;
7586 #if JS_HAS_GENERATORS
7588 * At this point, (index == 0 && pn->pn_count != 0) implies one
7589 * element initialiser was parsed.
7591 * An array comprehension of the form:
7593 * [i * j for (i in o) for (j in p) if (i != j)]
7595 * translates to roughly the following let expression:
7597 * let (array = new Array, i, j) {
7598 * for (i in o) let {
7599 * for (j in p)
7600 * if (i != j)
7601 * array.push(i * j)
7603 * array
7606 * where array is a nameless block-local variable. The "roughly"
7607 * means that an implementation may optimize away the array.push.
7608 * An array comprehension opens exactly one block scope, no matter
7609 * how many for heads it contains.
7611 * Each let () {...} or for (let ...) ... compiles to:
7613 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7615 * where <o> is a literal object representing the block scope,
7616 * with <n> properties, naming each var declared in the block.
7618 * Each var declaration in a let-block binds a name in <o> at
7619 * compile time, and allocates a slot on the operand stack at
7620 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7621 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7622 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7623 * local slot's stack index from fp->spbase.
7625 * The array comprehension iteration step, array.push(i * j) in
7626 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7627 * where <array> is the index of array's stack slot.
7629 if (index == 0 &&
7630 pn->pn_count != 0 &&
7631 js_MatchToken(cx, ts, TOK_FOR)) {
7632 JSParseNode *pnexp, *pntop;
7634 /* Relabel pn as an array comprehension node. */
7635 pn->pn_type = TOK_ARRAYCOMP;
7638 * Remove the comprehension expression from pn's linked list
7639 * and save it via pnexp. We'll re-install it underneath the
7640 * ARRAYPUSH node after we parse the rest of the comprehension.
7642 pnexp = pn->last();
7643 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7644 pn->pn_tail = (--pn->pn_count == 1)
7645 ? &pn->pn_head->pn_next
7646 : &pn->pn_head;
7647 *pn->pn_tail = NULL;
7649 pntop = ComprehensionTail(pnexp, pn->pn_blockid, tc,
7650 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7651 if (!pntop)
7652 return NULL;
7653 pn->append(pntop);
7655 #endif /* JS_HAS_GENERATORS */
7657 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7659 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7660 return pn;
7663 case TOK_LC:
7665 JSBool afterComma;
7666 JSParseNode *pnval;
7668 pn = NewParseNode(PN_LIST, tc);
7669 if (!pn)
7670 return NULL;
7671 pn->pn_type = TOK_RC;
7672 pn->pn_op = JSOP_NEWINIT;
7673 pn->makeEmpty();
7675 afterComma = JS_FALSE;
7676 for (;;) {
7677 ts->flags |= TSF_KEYWORD_IS_NAME;
7678 tt = js_GetToken(cx, ts);
7679 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7680 switch (tt) {
7681 case TOK_NUMBER:
7682 pn3 = NewParseNode(PN_NULLARY, tc);
7683 if (pn3)
7684 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
7685 break;
7686 case TOK_NAME:
7687 #if JS_HAS_GETTER_SETTER
7689 JSAtom *atom;
7691 atom = CURRENT_TOKEN(ts).t_atom;
7692 if (atom == cx->runtime->atomState.getAtom)
7693 op = JSOP_GETTER;
7694 else if (atom == cx->runtime->atomState.setAtom)
7695 op = JSOP_SETTER;
7696 else
7697 goto property_name;
7699 ts->flags |= TSF_KEYWORD_IS_NAME;
7700 tt = js_GetToken(cx, ts);
7701 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7702 if (tt != TOK_NAME) {
7703 js_UngetToken(ts);
7704 goto property_name;
7706 pn3 = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
7707 if (!pn3)
7708 return NULL;
7710 /* We have to fake a 'function' token here. */
7711 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
7712 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
7713 pn2 = FunctionExpr(cx, ts, tc);
7714 pn2 = NewBinary(TOK_COLON, op, pn3, pn2, tc);
7715 goto skip;
7717 property_name:
7718 #endif
7719 case TOK_STRING:
7720 pn3 = NewParseNode(PN_NULLARY, tc);
7721 if (pn3)
7722 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
7723 break;
7724 case TOK_RC:
7725 if (afterComma &&
7726 !js_ReportCompileErrorNumber(cx, ts, NULL,
7727 JSREPORT_WARNING |
7728 JSREPORT_STRICT,
7729 JSMSG_TRAILING_COMMA)) {
7730 return NULL;
7732 goto end_obj_init;
7733 default:
7734 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7735 JSMSG_BAD_PROP_ID);
7736 return NULL;
7739 tt = js_GetToken(cx, ts);
7740 #if JS_HAS_GETTER_SETTER
7741 if (tt == TOK_NAME) {
7742 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
7743 if (tt == TOK_ERROR)
7744 return NULL;
7746 #endif
7748 if (tt != TOK_COLON) {
7749 #if JS_HAS_DESTRUCTURING_SHORTHAND
7750 if (tt != TOK_COMMA && tt != TOK_RC) {
7751 #endif
7752 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7753 JSMSG_COLON_AFTER_ID);
7754 return NULL;
7755 #if JS_HAS_DESTRUCTURING_SHORTHAND
7759 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7760 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7762 js_UngetToken(ts);
7763 pn->pn_xflags |= PNX_DESTRUCT;
7764 pnval = pn3;
7765 if (pnval->pn_type == TOK_NAME) {
7766 pnval->pn_arity = PN_NAME;
7767 InitNameNodeCommon(pnval, tc);
7769 op = JSOP_NOP;
7770 #endif
7771 } else {
7772 op = CURRENT_TOKEN(ts).t_op;
7773 pnval = AssignExpr(cx, ts, tc);
7776 pn2 = NewBinary(TOK_COLON, op, pn3, pnval, tc);
7777 #if JS_HAS_GETTER_SETTER
7778 skip:
7779 #endif
7780 if (!pn2)
7781 return NULL;
7782 pn->append(pn2);
7784 tt = js_GetToken(cx, ts);
7785 if (tt == TOK_RC)
7786 goto end_obj_init;
7787 if (tt != TOK_COMMA) {
7788 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7789 JSMSG_CURLY_AFTER_LIST);
7790 return NULL;
7792 afterComma = JS_TRUE;
7795 end_obj_init:
7796 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7797 return pn;
7800 #if JS_HAS_BLOCK_SCOPE
7801 case TOK_LET:
7802 pn = LetBlock(cx, ts, tc, JS_FALSE);
7803 if (!pn)
7804 return NULL;
7805 break;
7806 #endif
7808 #if JS_HAS_SHARP_VARS
7809 case TOK_DEFSHARP:
7810 pn = NewParseNode(PN_UNARY, tc);
7811 if (!pn)
7812 return NULL;
7813 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
7814 ts->flags |= TSF_OPERAND;
7815 tt = js_GetToken(cx, ts);
7816 ts->flags &= ~TSF_OPERAND;
7817 if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
7818 #if JS_HAS_XML_SUPPORT
7819 tt == TOK_STAR || tt == TOK_AT ||
7820 tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
7821 #endif
7822 tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
7823 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7824 JSMSG_BAD_SHARP_VAR_DEF);
7825 return NULL;
7827 pn->pn_kid = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
7828 if (!pn->pn_kid)
7829 return NULL;
7830 tc->flags |= TCF_HAS_SHARPS;
7831 break;
7833 case TOK_USESHARP:
7834 /* Check for forward/dangling references at runtime, to allow eval. */
7835 pn = NewParseNode(PN_NULLARY, tc);
7836 if (!pn)
7837 return NULL;
7838 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
7839 tc->flags |= TCF_HAS_SHARPS;
7840 break;
7841 #endif /* JS_HAS_SHARP_VARS */
7843 case TOK_LP:
7845 JSBool genexp;
7847 pn = NewParseNode(PN_UNARY, tc);
7848 if (!pn)
7849 return NULL;
7850 pn2 = ParenExpr(cx, ts, tc, pn, &genexp);
7851 if (!pn2)
7852 return NULL;
7853 if (genexp)
7854 return pn2;
7855 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
7857 /* Check if parentheses were unnecessary. */
7858 if (pn2->pn_type == TOK_RP ||
7859 (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
7860 !afterDot)) {
7861 RecycleTree(pn, tc);
7862 pn = pn2;
7863 } else {
7864 pn->pn_type = TOK_RP;
7865 pn->pn_kid = pn2;
7867 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7868 break;
7871 #if JS_HAS_XML_SUPPORT
7872 case TOK_STAR:
7873 pn = QualifiedIdentifier(cx, ts, tc);
7874 if (!pn)
7875 return NULL;
7876 break;
7878 case TOK_AT:
7879 pn = AttributeIdentifier(cx, ts, tc);
7880 if (!pn)
7881 return NULL;
7882 break;
7884 case TOK_XMLSTAGO:
7885 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
7886 if (!pn)
7887 return NULL;
7888 break;
7889 #endif /* JS_HAS_XML_SUPPORT */
7891 case TOK_STRING:
7892 #if JS_HAS_SHARP_VARS
7893 /* FALL THROUGH */
7894 #endif
7896 #if JS_HAS_XML_SUPPORT
7897 case TOK_XMLCDATA:
7898 case TOK_XMLCOMMENT:
7899 case TOK_XMLPI:
7900 #endif
7901 pn = NewParseNode(PN_NULLARY, tc);
7902 if (!pn)
7903 return NULL;
7904 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
7905 #if JS_HAS_XML_SUPPORT
7906 if (tt == TOK_XMLPI)
7907 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
7908 else
7909 #endif
7910 pn->pn_op = CURRENT_TOKEN(ts).t_op;
7911 break;
7913 case TOK_NAME:
7914 pn = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
7915 if (!pn)
7916 return NULL;
7917 JS_ASSERT(CURRENT_TOKEN(ts).t_op == JSOP_NAME);
7918 pn->pn_op = JSOP_NAME;
7920 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
7921 pn->pn_atom == cx->runtime->atomState.argumentsAtom) {
7923 * Flag arguments usage so we can avoid unsafe optimizations such
7924 * as formal parameter assignment analysis (because of the hated
7925 * feature whereby arguments alias formals). We do this even for
7926 * a reference of the form foo.arguments, which ancient code may
7927 * still use instead of arguments (more hate).
7929 NoteArgumentsUse(tc);
7932 * Bind early to JSOP_ARGUMENTS to relieve later code from having
7933 * to do this work (new rule for the emitter to count on).
7935 if (!afterDot && !(ts->flags & TSF_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
7936 pn->pn_op = JSOP_ARGUMENTS;
7937 pn->pn_dflags |= PND_BOUND;
7939 } else if ((!afterDot
7940 #if JS_HAS_XML_SUPPORT
7941 || js_PeekToken(cx, ts) == TOK_DBLCOLON
7942 #endif
7943 ) && !(ts->flags & TSF_DESTRUCTURING)) {
7944 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
7945 if (!stmt || stmt->type != STMT_WITH) {
7946 JSDefinition *dn;
7948 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
7949 if (ale) {
7950 dn = ALE_DEFN(ale);
7951 #if JS_HAS_BLOCK_SCOPE
7952 if (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc))
7953 ale = NULL;
7954 #endif
7957 if (ale) {
7958 dn = ALE_DEFN(ale);
7959 } else {
7960 ale = tc->lexdeps.lookup(pn->pn_atom);
7961 if (ale) {
7962 dn = ALE_DEFN(ale);
7963 } else {
7965 * No definition before this use in any lexical scope.
7966 * Add a mapping in tc->lexdeps from pn->pn_atom to a
7967 * new node for the forward-referenced definition. This
7968 * placeholder definition node will be adopted when we
7969 * parse the real defining declaration form, or left as
7970 * a free variable definition if we never see the real
7971 * definition.
7973 ale = MakePlaceholder(pn, tc);
7974 if (!ale)
7975 return NULL;
7976 dn = ALE_DEFN(ale);
7979 * In case this is a forward reference to a function,
7980 * we pessimistically set PND_FUNARG if the next token
7981 * is not a left parenthesis.
7983 * If the definition eventually parsed into dn is not a
7984 * function, this flag won't hurt, and if we do parse a
7985 * function with pn's name, then the PND_FUNARG flag is
7986 * necessary for safe cx->display-based optimization of
7987 * the closure's static link.
7989 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
7990 JS_ASSERT(dn->pn_op == JSOP_NOP);
7991 if (js_PeekToken(cx, ts) != TOK_LP)
7992 dn->pn_dflags |= PND_FUNARG;
7996 JS_ASSERT(dn->pn_defn);
7997 LinkUseToDef(pn, dn, tc);
7999 /* Here we handle the backward function reference case. */
8000 if (js_PeekToken(cx, ts) != TOK_LP)
8001 dn->pn_dflags |= PND_FUNARG;
8003 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8007 #if JS_HAS_XML_SUPPORT
8008 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
8009 if (afterDot) {
8010 JSString *str;
8013 * Here PrimaryExpr is called after . or .. followed by a name
8014 * followed by ::. This is the only case where a keyword after
8015 * . or .. is not treated as a property name.
8017 str = ATOM_TO_STRING(pn->pn_atom);
8018 tt = js_CheckKeyword(JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
8019 if (tt == TOK_FUNCTION) {
8020 pn->pn_arity = PN_NULLARY;
8021 pn->pn_type = TOK_FUNCTION;
8022 } else if (tt != TOK_EOF) {
8023 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8024 JSMSG_KEYWORD_NOT_NS);
8025 return NULL;
8028 pn = QualifiedSuffix(cx, ts, pn, tc);
8029 if (!pn)
8030 return NULL;
8032 #endif
8033 break;
8035 case TOK_REGEXP:
8037 JSObject *obj;
8039 pn = NewParseNode(PN_NULLARY, tc);
8040 if (!pn)
8041 return NULL;
8043 /* Token stream ensures that tokenbuf is NUL-terminated. */
8044 JS_ASSERT(*ts->tokenbuf.ptr == (jschar) 0);
8045 obj = js_NewRegExpObject(cx, ts,
8046 ts->tokenbuf.base,
8047 ts->tokenbuf.ptr - ts->tokenbuf.base,
8048 CURRENT_TOKEN(ts).t_reflags);
8049 if (!obj)
8050 return NULL;
8051 if (!(tc->flags & TCF_COMPILE_N_GO)) {
8052 STOBJ_CLEAR_PARENT(obj);
8053 STOBJ_CLEAR_PROTO(obj);
8056 pn->pn_objbox = tc->compiler->newObjectBox(obj);
8057 if (!pn->pn_objbox)
8058 return NULL;
8060 pn->pn_op = JSOP_REGEXP;
8061 break;
8064 case TOK_NUMBER:
8065 pn = NewParseNode(PN_NULLARY, tc);
8066 if (!pn)
8067 return NULL;
8068 pn->pn_op = JSOP_DOUBLE;
8069 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
8070 break;
8072 case TOK_PRIMARY:
8073 pn = NewParseNode(PN_NULLARY, tc);
8074 if (!pn)
8075 return NULL;
8076 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8077 break;
8079 case TOK_ERROR:
8080 /* The scanner or one of its subroutines reported the error. */
8081 return NULL;
8083 default:
8084 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8085 JSMSG_SYNTAX_ERROR);
8086 return NULL;
8088 return pn;
8091 static JSParseNode *
8092 ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
8093 JSParseNode *pn1, JSBool *genexp)
8095 JSTokenPtr begin;
8096 JSParseNode *pn;
8098 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
8099 begin = CURRENT_TOKEN(ts).pos.begin;
8101 if (genexp)
8102 *genexp = JS_FALSE;
8103 pn = BracketedExpr(cx, ts, tc);
8104 if (!pn)
8105 return NULL;
8107 #if JS_HAS_GENERATOR_EXPRS
8108 if (js_MatchToken(cx, ts, TOK_FOR)) {
8109 if (pn->pn_type == TOK_YIELD) {
8110 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
8111 JSMSG_BAD_GENERATOR_SYNTAX,
8112 js_yield_str);
8113 return NULL;
8115 if (pn->pn_type == TOK_COMMA) {
8116 js_ReportCompileErrorNumber(cx, ts, pn->last(), JSREPORT_ERROR,
8117 JSMSG_BAD_GENERATOR_SYNTAX,
8118 js_generator_str);
8119 return NULL;
8121 if (!pn1) {
8122 pn1 = NewParseNode(PN_UNARY, tc);
8123 if (!pn1)
8124 return NULL;
8126 pn = GeneratorExpr(pn1, pn, tc);
8127 if (!pn)
8128 return NULL;
8129 pn->pn_pos.begin = begin;
8130 if (genexp) {
8131 if (js_GetToken(cx, ts) != TOK_RP) {
8132 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8133 JSMSG_BAD_GENERATOR_SYNTAX,
8134 js_generator_str);
8135 return NULL;
8137 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8138 *genexp = JS_TRUE;
8141 #endif /* JS_HAS_GENERATOR_EXPRS */
8143 return pn;
8147 * Fold from one constant type to another.
8148 * XXX handles only strings and numbers for now
8150 static JSBool
8151 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
8153 if (PN_TYPE(pn) != type) {
8154 switch (type) {
8155 case TOK_NUMBER:
8156 if (pn->pn_type == TOK_STRING) {
8157 jsdouble d;
8158 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8159 return JS_FALSE;
8160 pn->pn_dval = d;
8161 pn->pn_type = TOK_NUMBER;
8162 pn->pn_op = JSOP_DOUBLE;
8164 break;
8166 case TOK_STRING:
8167 if (pn->pn_type == TOK_NUMBER) {
8168 JSString *str = js_NumberToString(cx, pn->pn_dval);
8169 if (!str)
8170 return JS_FALSE;
8171 pn->pn_atom = js_AtomizeString(cx, str, 0);
8172 if (!pn->pn_atom)
8173 return JS_FALSE;
8174 pn->pn_type = TOK_STRING;
8175 pn->pn_op = JSOP_STRING;
8177 break;
8179 default:;
8182 return JS_TRUE;
8186 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8187 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8188 * a successful call to this function.
8190 static JSBool
8191 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8192 JSParseNode *pn, JSTreeContext *tc)
8194 jsdouble d, d2;
8195 int32 i, j;
8197 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8198 d = pn1->pn_dval;
8199 d2 = pn2->pn_dval;
8200 switch (op) {
8201 case JSOP_LSH:
8202 case JSOP_RSH:
8203 i = js_DoubleToECMAInt32(d);
8204 j = js_DoubleToECMAInt32(d2);
8205 j &= 31;
8206 d = (op == JSOP_LSH) ? i << j : i >> j;
8207 break;
8209 case JSOP_URSH:
8210 j = js_DoubleToECMAInt32(d2);
8211 j &= 31;
8212 d = js_DoubleToECMAUint32(d) >> j;
8213 break;
8215 case JSOP_ADD:
8216 d += d2;
8217 break;
8219 case JSOP_SUB:
8220 d -= d2;
8221 break;
8223 case JSOP_MUL:
8224 d *= d2;
8225 break;
8227 case JSOP_DIV:
8228 if (d2 == 0) {
8229 #if defined(XP_WIN)
8230 /* XXX MSVC miscompiles such that (NaN == 0) */
8231 if (JSDOUBLE_IS_NaN(d2))
8232 d = *cx->runtime->jsNaN;
8233 else
8234 #endif
8235 if (d == 0 || JSDOUBLE_IS_NaN(d))
8236 d = *cx->runtime->jsNaN;
8237 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
8238 d = *cx->runtime->jsNegativeInfinity;
8239 else
8240 d = *cx->runtime->jsPositiveInfinity;
8241 } else {
8242 d /= d2;
8244 break;
8246 case JSOP_MOD:
8247 if (d2 == 0) {
8248 d = *cx->runtime->jsNaN;
8249 } else {
8250 #if defined(XP_WIN)
8251 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
8252 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
8253 #endif
8254 d = fmod(d, d2);
8256 break;
8258 default:;
8261 /* Take care to allow pn1 or pn2 to alias pn. */
8262 if (pn1 != pn)
8263 RecycleTree(pn1, tc);
8264 if (pn2 != pn)
8265 RecycleTree(pn2, tc);
8266 pn->pn_type = TOK_NUMBER;
8267 pn->pn_op = JSOP_DOUBLE;
8268 pn->pn_arity = PN_NULLARY;
8269 pn->pn_dval = d;
8270 return JS_TRUE;
8273 #if JS_HAS_XML_SUPPORT
8275 static JSBool
8276 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8278 JSTokenType tt;
8279 JSParseNode **pnp, *pn1, *pn2;
8280 JSString *accum, *str;
8281 uint32 i, j;
8282 JSTempValueRooter tvr;
8284 JS_ASSERT(pn->pn_arity == PN_LIST);
8285 tt = PN_TYPE(pn);
8286 pnp = &pn->pn_head;
8287 pn1 = *pnp;
8288 accum = NULL;
8289 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8290 if (tt == TOK_XMLETAGO)
8291 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8292 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8293 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8297 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8298 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8299 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8300 * Therefore, we have to add additonal protection from GC nesting under
8301 * js_ConcatStrings.
8303 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8304 /* The parser already rejected end-tags with attributes. */
8305 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8306 switch (pn2->pn_type) {
8307 case TOK_XMLATTR:
8308 if (!accum)
8309 goto cantfold;
8310 /* FALL THROUGH */
8311 case TOK_XMLNAME:
8312 case TOK_XMLSPACE:
8313 case TOK_XMLTEXT:
8314 case TOK_STRING:
8315 if (pn2->pn_arity == PN_LIST)
8316 goto cantfold;
8317 str = ATOM_TO_STRING(pn2->pn_atom);
8318 break;
8320 case TOK_XMLCDATA:
8321 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8322 if (!str)
8323 return JS_FALSE;
8324 break;
8326 case TOK_XMLCOMMENT:
8327 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8328 if (!str)
8329 return JS_FALSE;
8330 break;
8332 case TOK_XMLPI:
8333 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8334 ATOM_TO_STRING(pn2->pn_atom2));
8335 if (!str)
8336 return JS_FALSE;
8337 break;
8339 cantfold:
8340 default:
8341 JS_ASSERT(*pnp == pn1);
8342 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8343 (i & 1) ^ (j & 1)) {
8344 #ifdef DEBUG_brendanXXX
8345 printf("1: %d, %d => ", i, j);
8346 if (accum)
8347 js_FileEscapedString(stdout, accum, 0);
8348 else
8349 fputs("NULL", stdout);
8350 fputc('\n', stdout);
8351 #endif
8352 } else if (accum && pn1 != pn2) {
8353 while (pn1->pn_next != pn2) {
8354 pn1 = RecycleTree(pn1, tc);
8355 --pn->pn_count;
8357 pn1->pn_type = TOK_XMLTEXT;
8358 pn1->pn_op = JSOP_STRING;
8359 pn1->pn_arity = PN_NULLARY;
8360 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8361 if (!pn1->pn_atom)
8362 return JS_FALSE;
8363 JS_ASSERT(pnp != &pn1->pn_next);
8364 *pnp = pn1;
8366 pnp = &pn2->pn_next;
8367 pn1 = *pnp;
8368 accum = NULL;
8369 continue;
8372 if (accum) {
8373 JS_PUSH_TEMP_ROOT_STRING(cx, accum, &tvr);
8374 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8375 ? js_AddAttributePart(cx, i & 1, accum, str)
8376 : js_ConcatStrings(cx, accum, str);
8377 JS_POP_TEMP_ROOT(cx, &tvr);
8378 if (!str)
8379 return JS_FALSE;
8380 #ifdef DEBUG_brendanXXX
8381 printf("2: %d, %d => ", i, j);
8382 js_FileEscapedString(stdout, str, 0);
8383 printf(" (%u)\n", JSSTRING_LENGTH(str));
8384 #endif
8385 ++j;
8387 accum = str;
8390 if (accum) {
8391 str = NULL;
8392 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8393 if (tt == TOK_XMLPTAGC)
8394 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8395 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8396 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8398 if (str) {
8399 accum = js_ConcatStrings(cx, accum, str);
8400 if (!accum)
8401 return JS_FALSE;
8404 JS_ASSERT(*pnp == pn1);
8405 while (pn1->pn_next) {
8406 pn1 = RecycleTree(pn1, tc);
8407 --pn->pn_count;
8409 pn1->pn_type = TOK_XMLTEXT;
8410 pn1->pn_op = JSOP_STRING;
8411 pn1->pn_arity = PN_NULLARY;
8412 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8413 if (!pn1->pn_atom)
8414 return JS_FALSE;
8415 JS_ASSERT(pnp != &pn1->pn_next);
8416 *pnp = pn1;
8419 if (pn1 && pn->pn_count == 1) {
8421 * Only one node under pn, and it has been folded: move pn1 onto pn
8422 * unless pn is an XML root (in which case we need it to tell the code
8423 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8424 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8425 * extra "<" and "/>" bracketing at runtime.
8427 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8428 pn->become(pn1);
8429 } else if (tt == TOK_XMLPTAGC) {
8430 pn->pn_type = TOK_XMLELEM;
8431 pn->pn_op = JSOP_TOXML;
8434 return JS_TRUE;
8437 #endif /* JS_HAS_XML_SUPPORT */
8439 static JSBool
8440 StartsWith(JSParseNode *pn, JSTokenType tt)
8442 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
8444 recur:
8445 if (PN_TYPE(pn) == tt)
8446 return JS_TRUE;
8447 switch (pn->pn_arity) {
8448 case PN_FUNC:
8449 return tt == TOK_FUNCTION;
8450 case PN_LIST:
8451 if (pn->pn_head)
8452 TAIL_RECURSE(pn->pn_head);
8453 break;
8454 case PN_TERNARY:
8455 if (pn->pn_kid1)
8456 TAIL_RECURSE(pn->pn_kid1);
8457 break;
8458 case PN_BINARY:
8459 if (pn->pn_left)
8460 TAIL_RECURSE(pn->pn_left);
8461 break;
8462 case PN_UNARY:
8463 /* A parenthesized expression starts with a left parenthesis. */
8464 if (pn->pn_type == TOK_RP)
8465 return tt == TOK_LP;
8466 if (pn->pn_kid)
8467 TAIL_RECURSE(pn->pn_kid);
8468 break;
8469 case PN_NAME:
8470 if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
8471 TAIL_RECURSE(pn->expr());
8472 break;
8473 case PN_NAMESET:
8474 TAIL_RECURSE(pn->pn_tree);
8476 return JS_FALSE;
8478 #undef TAIL_RECURSE
8481 static int
8482 Boolish(JSParseNode *pn)
8484 switch (pn->pn_op) {
8485 case JSOP_DOUBLE:
8486 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8488 case JSOP_STRING:
8489 return JSSTRING_LENGTH(ATOM_TO_STRING(pn->pn_atom)) != 0;
8491 #if JS_HAS_GENERATOR_EXPRS
8492 case JSOP_CALL:
8495 * A generator expression as an if or loop condition has no effects, it
8496 * simply results in a truthy object reference. This condition folding
8497 * is needed for the decompiler. See bug 442342 and bug 443074.
8499 if (pn->pn_count != 1)
8500 break;
8501 JSParseNode *pn2 = pn->pn_head;
8502 if (pn2->pn_type != TOK_FUNCTION)
8503 break;
8504 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8505 break;
8506 /* FALL THROUGH */
8508 #endif
8510 case JSOP_DEFFUN:
8511 case JSOP_LAMBDA:
8512 case JSOP_THIS:
8513 case JSOP_TRUE:
8514 return 1;
8516 case JSOP_NULL:
8517 case JSOP_FALSE:
8518 return 0;
8520 default:;
8522 return -1;
8525 JSBool
8526 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8528 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8530 JS_CHECK_RECURSION(cx, return JS_FALSE);
8532 switch (pn->pn_arity) {
8533 case PN_FUNC:
8535 uint16 oldflags = tc->flags;
8536 JSFunctionBox *oldlist = tc->functionList;
8538 tc->flags = (uint16) pn->pn_funbox->tcflags;
8539 tc->functionList = pn->pn_funbox->kids;
8540 if (!js_FoldConstants(cx, pn->pn_body, tc))
8541 return JS_FALSE;
8542 pn->pn_funbox->kids = tc->functionList;
8543 tc->flags = oldflags;
8544 tc->functionList = oldlist;
8545 break;
8548 case PN_LIST:
8550 /* Propagate inCond through logical connectives. */
8551 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8553 /* Save the list head in pn1 for later use. */
8554 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
8555 if (!js_FoldConstants(cx, pn2, tc, cond))
8556 return JS_FALSE;
8558 break;
8561 case PN_TERNARY:
8562 /* Any kid may be null (e.g. for (;;)). */
8563 pn1 = pn->pn_kid1;
8564 pn2 = pn->pn_kid2;
8565 pn3 = pn->pn_kid3;
8566 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8567 return JS_FALSE;
8568 if (pn2) {
8569 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8570 return JS_FALSE;
8571 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8572 RecycleTree(pn2, tc);
8573 pn->pn_kid2 = NULL;
8576 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8577 return JS_FALSE;
8578 break;
8580 case PN_BINARY:
8581 pn1 = pn->pn_left;
8582 pn2 = pn->pn_right;
8584 /* Propagate inCond through logical connectives. */
8585 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8586 if (!js_FoldConstants(cx, pn1, tc, inCond))
8587 return JS_FALSE;
8588 if (!js_FoldConstants(cx, pn2, tc, inCond))
8589 return JS_FALSE;
8590 break;
8593 /* First kid may be null (for default case in switch). */
8594 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8595 return JS_FALSE;
8596 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8597 return JS_FALSE;
8598 break;
8600 case PN_UNARY:
8601 /* Our kid may be null (e.g. return; vs. return e;). */
8602 pn1 = pn->pn_kid;
8603 if (pn1 &&
8604 !js_FoldConstants(cx, pn1, tc,
8605 (inCond && pn->pn_type == TOK_RP) ||
8606 pn->pn_op == JSOP_NOT)) {
8607 return JS_FALSE;
8609 break;
8611 case PN_NAME:
8613 * Skip pn1 down along a chain of dotted member expressions to avoid
8614 * excessive recursion. Our only goal here is to fold constants (if
8615 * any) in the primary expression operand to the left of the first
8616 * dot in the chain.
8618 if (!pn->pn_used) {
8619 pn1 = pn->pn_expr;
8620 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8621 pn1 = pn1->pn_expr;
8622 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8623 return JS_FALSE;
8625 break;
8627 case PN_NAMESET:
8628 pn1 = pn->pn_tree;
8629 if (!js_FoldConstants(cx, pn1, tc))
8630 return JS_FALSE;
8631 break;
8633 case PN_NULLARY:
8634 break;
8637 switch (pn->pn_type) {
8638 case TOK_IF:
8639 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8640 break;
8641 /* FALL THROUGH */
8643 case TOK_HOOK:
8644 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8645 while (pn1->pn_type == TOK_RP)
8646 pn1 = pn1->pn_kid;
8647 switch (pn1->pn_type) {
8648 case TOK_NUMBER:
8649 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8650 pn2 = pn3;
8651 break;
8652 case TOK_STRING:
8653 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
8654 pn2 = pn3;
8655 break;
8656 case TOK_PRIMARY:
8657 if (pn1->pn_op == JSOP_TRUE)
8658 break;
8659 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8660 pn2 = pn3;
8661 break;
8663 /* FALL THROUGH */
8664 default:
8665 /* Early return to dodge common code that copies pn2 to pn. */
8666 return JS_TRUE;
8669 #if JS_HAS_GENERATOR_EXPRS
8670 /* Don't fold a trailing |if (0)| in a generator expression. */
8671 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8672 break;
8673 #endif
8675 if (pn2) {
8677 * pn2 is the then- or else-statement subtree to compile. Take
8678 * care not to expose an object initialiser, which would be parsed
8679 * as a block, to the Statement parser via eval(uneval(e)) where e
8680 * is '1 ? {p:2, q:3}[i] : r;' or the like.
8682 if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) {
8683 pn->pn_type = TOK_RP;
8684 pn->pn_arity = PN_UNARY;
8685 pn->pn_kid = pn2;
8686 if (pn3 && pn3 != pn2)
8687 RecycleTree(pn3, tc);
8688 break;
8690 if (!pn2->pn_defn)
8691 pn->become(pn2);
8693 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8695 * False condition and no else, or an empty then-statement was
8696 * moved up over pn. Either way, make pn an empty block (not an
8697 * empty statement, which does not decompile, even when labeled).
8698 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8699 * or an empty statement for a child.
8701 pn->pn_type = TOK_LC;
8702 pn->pn_arity = PN_LIST;
8703 pn->makeEmpty();
8705 RecycleTree(pn2, tc);
8706 if (pn3 && pn3 != pn2)
8707 RecycleTree(pn3, tc);
8708 break;
8710 case TOK_OR:
8711 case TOK_AND:
8712 if (inCond) {
8713 if (pn->pn_arity == PN_LIST) {
8714 JSParseNode **pnp = &pn->pn_head;
8715 JS_ASSERT(*pnp == pn1);
8716 do {
8717 int cond = Boolish(pn1);
8718 if (cond == (pn->pn_type == TOK_OR)) {
8719 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
8720 pn3 = pn2->pn_next;
8721 RecycleTree(pn2, tc);
8722 --pn->pn_count;
8724 pn1->pn_next = NULL;
8725 break;
8727 if (cond != -1) {
8728 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8729 if (pn->pn_count == 1)
8730 break;
8731 *pnp = pn1->pn_next;
8732 RecycleTree(pn1, tc);
8733 --pn->pn_count;
8734 } else {
8735 pnp = &pn1->pn_next;
8737 } while ((pn1 = *pnp) != NULL);
8739 // We may have to change arity from LIST to BINARY.
8740 pn1 = pn->pn_head;
8741 if (pn->pn_count == 2) {
8742 pn2 = pn1->pn_next;
8743 pn1->pn_next = NULL;
8744 JS_ASSERT(!pn2->pn_next);
8745 pn->pn_arity = PN_BINARY;
8746 pn->pn_left = pn1;
8747 pn->pn_right = pn2;
8748 } else if (pn->pn_count == 1) {
8749 pn->become(pn1);
8750 RecycleTree(pn1, tc);
8752 } else {
8753 int cond = Boolish(pn1);
8754 if (cond == (pn->pn_type == TOK_OR)) {
8755 RecycleTree(pn2, tc);
8756 pn->become(pn1);
8757 } else if (cond != -1) {
8758 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8759 RecycleTree(pn1, tc);
8760 pn->become(pn2);
8764 break;
8766 case TOK_ASSIGN:
8768 * Compound operators such as *= should be subject to folding, in case
8769 * the left-hand side is constant, and so that the decompiler produces
8770 * the same string that you get from decompiling a script or function
8771 * compiled from that same string. As with +, += is special.
8773 if (pn->pn_op == JSOP_NOP)
8774 break;
8775 if (pn->pn_op != JSOP_ADD)
8776 goto do_binary_op;
8777 /* FALL THROUGH */
8779 case TOK_PLUS:
8780 if (pn->pn_arity == PN_LIST) {
8781 size_t length, length2;
8782 jschar *chars;
8783 JSString *str, *str2;
8786 * Any string literal term with all others number or string means
8787 * this is a concatenation. If any term is not a string or number
8788 * literal, we can't fold.
8790 JS_ASSERT(pn->pn_count > 2);
8791 if (pn->pn_xflags & PNX_CANTFOLD)
8792 return JS_TRUE;
8793 if (pn->pn_xflags != PNX_STRCAT)
8794 goto do_binary_op;
8796 /* Ok, we're concatenating: convert non-string constant operands. */
8797 length = 0;
8798 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8799 if (!FoldType(cx, pn2, TOK_STRING))
8800 return JS_FALSE;
8801 /* XXX fold only if all operands convert to string */
8802 if (pn2->pn_type != TOK_STRING)
8803 return JS_TRUE;
8804 length += JSFLATSTR_LENGTH(ATOM_TO_STRING(pn2->pn_atom));
8807 /* Allocate a new buffer and string descriptor for the result. */
8808 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
8809 if (!chars)
8810 return JS_FALSE;
8811 str = js_NewString(cx, chars, length);
8812 if (!str) {
8813 JS_free(cx, chars);
8814 return JS_FALSE;
8817 /* Fill the buffer, advancing chars and recycling kids as we go. */
8818 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
8819 str2 = ATOM_TO_STRING(pn2->pn_atom);
8820 length2 = JSFLATSTR_LENGTH(str2);
8821 js_strncpy(chars, JSFLATSTR_CHARS(str2), length2);
8822 chars += length2;
8824 *chars = 0;
8826 /* Atomize the result string and mutate pn to refer to it. */
8827 pn->pn_atom = js_AtomizeString(cx, str, 0);
8828 if (!pn->pn_atom)
8829 return JS_FALSE;
8830 pn->pn_type = TOK_STRING;
8831 pn->pn_op = JSOP_STRING;
8832 pn->pn_arity = PN_NULLARY;
8833 break;
8836 /* Handle a binary string concatenation. */
8837 JS_ASSERT(pn->pn_arity == PN_BINARY);
8838 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
8839 JSString *left, *right, *str;
8841 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
8842 TOK_STRING)) {
8843 return JS_FALSE;
8845 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
8846 return JS_TRUE;
8847 left = ATOM_TO_STRING(pn1->pn_atom);
8848 right = ATOM_TO_STRING(pn2->pn_atom);
8849 str = js_ConcatStrings(cx, left, right);
8850 if (!str)
8851 return JS_FALSE;
8852 pn->pn_atom = js_AtomizeString(cx, str, 0);
8853 if (!pn->pn_atom)
8854 return JS_FALSE;
8855 pn->pn_type = TOK_STRING;
8856 pn->pn_op = JSOP_STRING;
8857 pn->pn_arity = PN_NULLARY;
8858 RecycleTree(pn1, tc);
8859 RecycleTree(pn2, tc);
8860 break;
8863 /* Can't concatenate string literals, let's try numbers. */
8864 goto do_binary_op;
8866 case TOK_STAR:
8867 case TOK_SHOP:
8868 case TOK_MINUS:
8869 case TOK_DIVOP:
8870 do_binary_op:
8871 if (pn->pn_arity == PN_LIST) {
8872 JS_ASSERT(pn->pn_count > 2);
8873 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8874 if (!FoldType(cx, pn2, TOK_NUMBER))
8875 return JS_FALSE;
8877 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8878 /* XXX fold only if all operands convert to number */
8879 if (pn2->pn_type != TOK_NUMBER)
8880 break;
8882 if (!pn2) {
8883 JSOp op = PN_OP(pn);
8885 pn2 = pn1->pn_next;
8886 pn3 = pn2->pn_next;
8887 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
8888 return JS_FALSE;
8889 while ((pn2 = pn3) != NULL) {
8890 pn3 = pn2->pn_next;
8891 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
8892 return JS_FALSE;
8895 } else {
8896 JS_ASSERT(pn->pn_arity == PN_BINARY);
8897 if (!FoldType(cx, pn1, TOK_NUMBER) ||
8898 !FoldType(cx, pn2, TOK_NUMBER)) {
8899 return JS_FALSE;
8901 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
8902 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
8903 return JS_FALSE;
8906 break;
8908 case TOK_UNARYOP:
8909 while (pn1->pn_type == TOK_RP)
8910 pn1 = pn1->pn_kid;
8911 if (pn1->pn_type == TOK_NUMBER) {
8912 jsdouble d;
8914 /* Operate on one numeric constant. */
8915 d = pn1->pn_dval;
8916 switch (pn->pn_op) {
8917 case JSOP_BITNOT:
8918 d = ~js_DoubleToECMAInt32(d);
8919 break;
8921 case JSOP_NEG:
8922 #ifdef HPUX
8924 * Negation of a zero doesn't produce a negative
8925 * zero on HPUX. Perform the operation by bit
8926 * twiddling.
8928 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
8929 #else
8930 d = -d;
8931 #endif
8932 break;
8934 case JSOP_POS:
8935 break;
8937 case JSOP_NOT:
8938 pn->pn_type = TOK_PRIMARY;
8939 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
8940 pn->pn_arity = PN_NULLARY;
8941 /* FALL THROUGH */
8943 default:
8944 /* Return early to dodge the common TOK_NUMBER code. */
8945 return JS_TRUE;
8947 pn->pn_type = TOK_NUMBER;
8948 pn->pn_op = JSOP_DOUBLE;
8949 pn->pn_arity = PN_NULLARY;
8950 pn->pn_dval = d;
8951 RecycleTree(pn1, tc);
8952 } else if (pn1->pn_type == TOK_PRIMARY) {
8953 if (pn->pn_op == JSOP_NOT &&
8954 (pn1->pn_op == JSOP_TRUE ||
8955 pn1->pn_op == JSOP_FALSE)) {
8956 pn->become(pn1);
8957 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
8958 RecycleTree(pn1, tc);
8961 break;
8963 #if JS_HAS_XML_SUPPORT
8964 case TOK_XMLELEM:
8965 case TOK_XMLLIST:
8966 case TOK_XMLPTAGC:
8967 case TOK_XMLSTAGO:
8968 case TOK_XMLETAGO:
8969 case TOK_XMLNAME:
8970 if (pn->pn_arity == PN_LIST) {
8971 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
8972 if (!FoldXMLConstants(cx, pn, tc))
8973 return JS_FALSE;
8975 break;
8977 case TOK_AT:
8978 if (pn1->pn_type == TOK_XMLNAME) {
8979 jsval v;
8980 JSObjectBox *xmlbox;
8982 v = ATOM_KEY(pn1->pn_atom);
8983 if (!js_ToAttributeName(cx, &v))
8984 return JS_FALSE;
8985 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
8987 xmlbox = tc->compiler->newObjectBox(JSVAL_TO_OBJECT(v));
8988 if (!xmlbox)
8989 return JS_FALSE;
8991 pn->pn_type = TOK_XMLNAME;
8992 pn->pn_op = JSOP_OBJECT;
8993 pn->pn_arity = PN_NULLARY;
8994 pn->pn_objbox = xmlbox;
8995 RecycleTree(pn1, tc);
8997 break;
8998 #endif /* JS_HAS_XML_SUPPORT */
9000 default:;
9003 if (inCond) {
9004 int cond = Boolish(pn);
9005 if (cond >= 0) {
9006 switch (pn->pn_arity) {
9007 case PN_LIST:
9008 pn2 = pn->pn_head;
9009 do {
9010 pn3 = pn2->pn_next;
9011 RecycleTree(pn2, tc);
9012 } while ((pn2 = pn3) != NULL);
9013 break;
9014 case PN_FUNC:
9015 RecycleFuncNameKids(pn, tc);
9016 break;
9017 case PN_NULLARY:
9018 break;
9019 default:
9020 JS_NOT_REACHED("unhandled arity");
9022 pn->pn_type = TOK_PRIMARY;
9023 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9024 pn->pn_arity = PN_NULLARY;
9028 return JS_TRUE;