Bug 461269 - Remove TOK_RP nodes from the parse tree. r=brendan.
[mozilla-central.git] / js / src / jsparse.cpp
blob9470843c0410c0a4a9c9af74db8716a9fe9fc44d
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"
80 #include "jslibmath.h"
82 #if JS_HAS_XML_SUPPORT
83 #include "jsxml.h"
84 #endif
86 #if JS_HAS_DESTRUCTURING
87 #include "jsdhash.h"
88 #endif
91 * Asserts to verify assumptions behind pn_ macros.
93 #define pn_offsetof(m) offsetof(JSParseNode, m)
95 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
96 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
98 #undef pn_offsetof
101 * JS parsers, from lowest to highest precedence.
103 * Each parser takes a context, a token stream, and a tree context struct.
104 * Each returns a parse node tree or null on error.
107 typedef JSParseNode *
108 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
110 typedef JSParseNode *
111 JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
112 bool inLetHead);
114 typedef JSParseNode *
115 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
116 JSBool allowCallSyntax);
118 typedef JSParseNode *
119 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
120 JSTokenType tt, JSBool afterDot);
122 typedef JSParseNode *
123 JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
124 JSParseNode *pn1, JSBool *genexp);
126 static JSParser FunctionStmt;
127 static JSParser FunctionExpr;
128 static JSParser Statements;
129 static JSParser Statement;
130 static JSVariablesParser Variables;
131 static JSParser Expr;
132 static JSParser AssignExpr;
133 static JSParser CondExpr;
134 static JSParser OrExpr;
135 static JSParser AndExpr;
136 static JSParser BitOrExpr;
137 static JSParser BitXorExpr;
138 static JSParser BitAndExpr;
139 static JSParser EqExpr;
140 static JSParser RelExpr;
141 static JSParser ShiftExpr;
142 static JSParser AddExpr;
143 static JSParser MulExpr;
144 static JSParser UnaryExpr;
145 static JSMemberParser MemberExpr;
146 static JSPrimaryParser PrimaryExpr;
147 static JSParenParser ParenExpr;
150 * Insist that the next token be of type tt, or report errno and return null.
151 * NB: this macro uses cx and ts from its lexical environment.
153 #define MUST_MATCH_TOKEN(tt, errno) \
154 JS_BEGIN_MACRO \
155 if (js_GetToken(cx, ts) != tt) { \
156 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
157 return NULL; \
159 JS_END_MACRO
161 #ifdef METER_PARSENODES
162 static uint32 parsenodes = 0;
163 static uint32 maxparsenodes = 0;
164 static uint32 recyclednodes = 0;
165 #endif
167 void
168 JSParseNode::become(JSParseNode *pn2)
170 JS_ASSERT(!pn_defn);
171 JS_ASSERT(!pn2->pn_defn);
173 JS_ASSERT(!pn_used);
174 if (pn2->pn_used) {
175 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
176 while (*pnup != pn2)
177 pnup = &(*pnup)->pn_link;
178 *pnup = this;
179 pn_link = pn2->pn_link;
180 pn_used = true;
181 pn2->pn_link = NULL;
182 pn2->pn_used = false;
185 /* If this is a function node fix up the pn_funbox->node back-pointer. */
186 if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
187 pn2->pn_funbox->node = this;
189 pn_type = pn2->pn_type;
190 pn_op = pn2->pn_op;
191 pn_arity = pn2->pn_arity;
192 pn_parens = pn2->pn_parens;
193 pn_u = pn2->pn_u;
194 pn2->clear();
197 void
198 JSParseNode::clear()
200 pn_type = TOK_EOF;
201 pn_op = JSOP_NOP;
202 pn_used = pn_defn = false;
203 pn_arity = PN_NULLARY;
204 pn_parens = false;
207 bool
208 JSCompiler::init(const jschar *base, size_t length,
209 FILE *fp, const char *filename, uintN lineno)
211 JSContext *cx = context;
213 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
214 if (!js_InitTokenStream(cx, TS(this), base, length, fp, filename, lineno)) {
215 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
216 return false;
219 /* Root atoms and objects allocated for the parsed tree. */
220 JS_KEEP_ATOMS(cx->runtime);
221 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
222 return true;
225 JSCompiler::~JSCompiler()
227 JSContext *cx = context;
229 if (principals)
230 JSPRINCIPALS_DROP(cx, principals);
231 JS_ASSERT(tempRoot.u.compiler == this);
232 JS_POP_TEMP_ROOT(cx, &tempRoot);
233 JS_UNKEEP_ATOMS(cx->runtime);
234 js_CloseTokenStream(cx, TS(this));
235 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
238 void
239 JSCompiler::setPrincipals(JSPrincipals *prin)
241 JS_ASSERT(!principals);
242 if (prin)
243 JSPRINCIPALS_HOLD(context, prin);
244 principals = prin;
247 JSObjectBox *
248 JSCompiler::newObjectBox(JSObject *obj)
250 JS_ASSERT(obj);
253 * We use JSContext.tempPool to allocate parsed objects and place them on
254 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
255 * containing the entries must be alive until we are done with scanning,
256 * parsing and code generation for the whole script or top-level function.
258 JSObjectBox *objbox;
259 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
260 if (!objbox) {
261 js_ReportOutOfScriptQuota(context);
262 return NULL;
264 objbox->traceLink = traceListHead;
265 traceListHead = objbox;
266 objbox->emitLink = NULL;
267 objbox->object = obj;
268 return objbox;
271 JSFunctionBox *
272 JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
274 JS_ASSERT(obj);
275 JS_ASSERT(HAS_FUNCTION_CLASS(obj));
278 * We use JSContext.tempPool to allocate parsed objects and place them on
279 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
280 * containing the entries must be alive until we are done with scanning,
281 * parsing and code generation for the whole script or top-level function.
283 JSFunctionBox *funbox;
284 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
285 if (!funbox) {
286 js_ReportOutOfScriptQuota(context);
287 return NULL;
289 funbox->traceLink = traceListHead;
290 traceListHead = funbox;
291 funbox->emitLink = NULL;
292 funbox->object = obj;
293 funbox->node = fn;
294 funbox->siblings = tc->functionList;
295 tc->functionList = funbox;
296 ++tc->compiler->functionCount;
297 funbox->kids = NULL;
298 funbox->parent = tc->funbox;
299 funbox->queued = false;
300 funbox->inLoop = false;
301 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
302 if (STMT_IS_LOOP(stmt)) {
303 funbox->inLoop = true;
304 break;
307 funbox->level = tc->staticLevel;
308 funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
309 return funbox;
312 void
313 JSCompiler::trace(JSTracer *trc)
315 JSObjectBox *objbox;
317 JS_ASSERT(tempRoot.u.compiler == this);
318 objbox = traceListHead;
319 while (objbox) {
320 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
321 objbox = objbox->traceLink;
325 static void
326 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
328 static void
329 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
331 JSFunctionBox *funbox = pn->pn_funbox;
332 if (funbox) {
333 JS_ASSERT(funbox->node == pn);
334 funbox->node = NULL;
336 JSFunctionBox **funboxp = &tc->functionList;
337 while (*funboxp) {
338 if (*funboxp == funbox) {
339 *funboxp = funbox->siblings;
340 break;
342 funboxp = &(*funboxp)->siblings;
345 uint16 oldflags = tc->flags;
346 JSFunctionBox *oldlist = tc->functionList;
348 tc->flags = (uint16) funbox->tcflags;
349 tc->functionList = funbox->kids;
350 UnlinkFunctionBoxes(pn->pn_body, tc);
351 funbox->kids = tc->functionList;
352 tc->flags = oldflags;
353 tc->functionList = oldlist;
355 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
356 pn->pn_funbox = NULL;
360 static void
361 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
363 if (pn) {
364 switch (pn->pn_arity) {
365 case PN_NULLARY:
366 return;
367 case PN_UNARY:
368 UnlinkFunctionBoxes(pn->pn_kid, tc);
369 return;
370 case PN_BINARY:
371 UnlinkFunctionBoxes(pn->pn_left, tc);
372 UnlinkFunctionBoxes(pn->pn_right, tc);
373 return;
374 case PN_TERNARY:
375 UnlinkFunctionBoxes(pn->pn_kid1, tc);
376 UnlinkFunctionBoxes(pn->pn_kid2, tc);
377 UnlinkFunctionBoxes(pn->pn_kid3, tc);
378 return;
379 case PN_LIST:
380 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
381 UnlinkFunctionBoxes(pn2, tc);
382 return;
383 case PN_FUNC:
384 UnlinkFunctionBox(pn, tc);
385 return;
386 case PN_NAME:
387 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
388 return;
389 case PN_NAMESET:
390 UnlinkFunctionBoxes(pn->pn_tree, tc);
395 static void
396 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
398 static JSParseNode *
399 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
401 JSParseNode *next, **head;
403 if (!pn)
404 return NULL;
406 /* Catch back-to-back dup recycles. */
407 JS_ASSERT(pn != tc->compiler->nodeList);
408 next = pn->pn_next;
409 if (pn->pn_used || pn->pn_defn) {
411 * JSAtomLists own definition nodes along with their used-node chains.
412 * Defer recycling such nodes until we unwind to top level to avoid
413 * linkage overhead or (alternatively) unlinking runtime complexity.
414 * Yes, this means dead code can contribute to static analysis results!
416 * Do recycle kids here, since they are no longer needed.
418 pn->pn_next = NULL;
419 RecycleFuncNameKids(pn, tc);
420 } else {
421 UnlinkFunctionBoxes(pn, tc);
422 head = &tc->compiler->nodeList;
423 pn->pn_next = *head;
424 *head = pn;
425 #ifdef METER_PARSENODES
426 recyclednodes++;
427 #endif
429 return next;
432 static void
433 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
435 switch (pn->pn_arity) {
436 case PN_FUNC:
437 UnlinkFunctionBox(pn, tc);
438 /* FALL THROUGH */
440 case PN_NAME:
442 * Only a definition node might have a non-null strong pn_expr link
443 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
444 * Every node with the pn_used flag set has a non-null pn_lexdef
445 * weak reference to its definition node.
447 if (!pn->pn_used && pn->pn_expr) {
448 RecycleTree(pn->pn_expr, tc);
449 pn->pn_expr = NULL;
451 break;
453 default:
454 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
458 static JSParseNode *
459 NewOrRecycledNode(JSTreeContext *tc)
461 JSParseNode *pn, *pn2;
463 pn = tc->compiler->nodeList;
464 if (!pn) {
465 JSContext *cx = tc->compiler->context;
467 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
468 if (!pn)
469 js_ReportOutOfScriptQuota(cx);
470 } else {
471 tc->compiler->nodeList = pn->pn_next;
473 /* Recycle immediate descendents only, to save work and working set. */
474 switch (pn->pn_arity) {
475 case PN_FUNC:
476 RecycleTree(pn->pn_body, tc);
477 break;
478 case PN_LIST:
479 pn2 = pn->pn_head;
480 if (pn2) {
481 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
482 pn2 = pn2->pn_next;
483 if (pn2) {
484 pn2 = pn->pn_head;
485 do {
486 pn2 = RecycleTree(pn2, tc);
487 } while (pn2);
488 } else {
489 *pn->pn_tail = tc->compiler->nodeList;
490 tc->compiler->nodeList = pn->pn_head;
491 #ifdef METER_PARSENODES
492 recyclednodes += pn->pn_count;
493 #endif
494 break;
497 break;
498 case PN_TERNARY:
499 RecycleTree(pn->pn_kid1, tc);
500 RecycleTree(pn->pn_kid2, tc);
501 RecycleTree(pn->pn_kid3, tc);
502 break;
503 case PN_BINARY:
504 if (pn->pn_left != pn->pn_right)
505 RecycleTree(pn->pn_left, tc);
506 RecycleTree(pn->pn_right, tc);
507 break;
508 case PN_UNARY:
509 RecycleTree(pn->pn_kid, tc);
510 break;
511 case PN_NAME:
512 if (!pn->pn_used)
513 RecycleTree(pn->pn_expr, tc);
514 break;
515 case PN_NULLARY:
516 break;
519 if (pn) {
520 #ifdef METER_PARSENODES
521 parsenodes++;
522 if (parsenodes - recyclednodes > maxparsenodes)
523 maxparsenodes = parsenodes - recyclednodes;
524 #endif
525 pn->pn_used = pn->pn_defn = false;
526 memset(&pn->pn_u, 0, sizeof pn->pn_u);
527 pn->pn_next = NULL;
529 return pn;
532 static inline void
533 InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
535 pn->pn_type = type;
536 pn->pn_op = op;
537 pn->pn_arity = arity;
538 pn->pn_parens = false;
539 JS_ASSERT(!pn->pn_used);
540 JS_ASSERT(!pn->pn_defn);
541 pn->pn_next = pn->pn_link = NULL;
545 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
546 * temporary arena.
548 static JSParseNode *
549 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
551 JSParseNode *pn;
552 JSToken *tp;
554 pn = NewOrRecycledNode(tc);
555 if (!pn)
556 return NULL;
557 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
558 InitParseNode(pn, tp->type, JSOP_NOP, arity);
559 pn->pn_pos = tp->pos;
560 return pn;
563 static inline void
564 InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
566 pn->pn_expr = NULL;
567 pn->pn_cookie = FREE_UPVAR_COOKIE;
568 pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
569 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
570 pn->pn_dflags |= PND_BLOCKCHILD;
571 pn->pn_blockid = tc->blockid();
574 static JSParseNode *
575 NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc)
577 JSParseNode *pn;
579 pn = NewParseNode(PN_NAME, tc);
580 if (pn) {
581 pn->pn_atom = atom;
582 InitNameNodeCommon(pn, tc);
584 return pn;
587 static JSParseNode *
588 NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
589 JSTreeContext *tc)
591 JSParseNode *pn, *pn1, *pn2;
593 if (!left || !right)
594 return NULL;
597 * Flatten a left-associative (left-heavy) tree of a given operator into
598 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
600 if (PN_TYPE(left) == tt &&
601 PN_OP(left) == op &&
602 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
603 if (left->pn_arity != PN_LIST) {
604 pn1 = left->pn_left, pn2 = left->pn_right;
605 left->pn_arity = PN_LIST;
606 left->pn_parens = false;
607 left->initList(pn1);
608 left->append(pn2);
609 if (tt == TOK_PLUS) {
610 if (pn1->pn_type == TOK_STRING)
611 left->pn_xflags |= PNX_STRCAT;
612 else if (pn1->pn_type != TOK_NUMBER)
613 left->pn_xflags |= PNX_CANTFOLD;
614 if (pn2->pn_type == TOK_STRING)
615 left->pn_xflags |= PNX_STRCAT;
616 else if (pn2->pn_type != TOK_NUMBER)
617 left->pn_xflags |= PNX_CANTFOLD;
620 left->append(right);
621 left->pn_pos.end = right->pn_pos.end;
622 if (tt == TOK_PLUS) {
623 if (right->pn_type == TOK_STRING)
624 left->pn_xflags |= PNX_STRCAT;
625 else if (right->pn_type != TOK_NUMBER)
626 left->pn_xflags |= PNX_CANTFOLD;
628 return left;
632 * Fold constant addition immediately, to conserve node space and, what's
633 * more, so js_FoldConstants never sees mixed addition and concatenation
634 * operations with more than one leading non-string operand in a PN_LIST
635 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
636 * to "3pt", not "12pt").
638 if (tt == TOK_PLUS &&
639 left->pn_type == TOK_NUMBER &&
640 right->pn_type == TOK_NUMBER) {
641 left->pn_dval += right->pn_dval;
642 left->pn_pos.end = right->pn_pos.end;
643 RecycleTree(right, tc);
644 return left;
647 pn = NewOrRecycledNode(tc);
648 if (!pn)
649 return NULL;
650 InitParseNode(pn, tt, op, PN_BINARY);
651 pn->pn_pos.begin = left->pn_pos.begin;
652 pn->pn_pos.end = right->pn_pos.end;
653 pn->pn_left = left;
654 pn->pn_right = right;
655 return pn;
658 #if JS_HAS_GETTER_SETTER
659 static JSTokenType
660 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
662 JSAtom *atom;
663 JSRuntime *rt;
664 JSOp op;
665 const char *name;
667 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
668 atom = CURRENT_TOKEN(ts).t_atom;
669 rt = cx->runtime;
670 if (atom == rt->atomState.getterAtom)
671 op = JSOP_GETTER;
672 else if (atom == rt->atomState.setterAtom)
673 op = JSOP_SETTER;
674 else
675 return TOK_NAME;
676 if (js_PeekTokenSameLine(cx, ts) != tt)
677 return TOK_NAME;
678 (void) js_GetToken(cx, ts);
679 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
680 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
681 JSMSG_BAD_GETTER_OR_SETTER,
682 (op == JSOP_GETTER)
683 ? js_getter_str
684 : js_setter_str);
685 return TOK_ERROR;
687 CURRENT_TOKEN(ts).t_op = op;
688 if (JS_HAS_STRICT_OPTION(cx)) {
689 name = js_AtomToPrintableString(cx, atom);
690 if (!name ||
691 !js_ReportCompileErrorNumber(cx, ts, NULL,
692 JSREPORT_WARNING | JSREPORT_STRICT,
693 JSMSG_DEPRECATED_USAGE,
694 name)) {
695 return TOK_ERROR;
698 return tt;
700 #endif
702 static bool
703 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
705 if (tc->blockidGen == JS_BIT(20)) {
706 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
707 JSMSG_NEED_DIET, "program");
708 return false;
710 blockid = tc->blockidGen++;
711 return true;
714 static bool
715 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
717 JS_ASSERT(tc->topStmt);
718 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
719 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
720 if (!GenerateBlockId(tc, tc->topStmt->blockid))
721 return false;
722 pn->pn_blockid = tc->topStmt->blockid;
723 return true;
727 * Parse a top-level JS script.
729 JSParseNode *
730 JSCompiler::parse(JSObject *chain)
733 * Protect atoms from being collected by a GC activation, which might
734 * - nest on this thread due to out of memory (the so-called "last ditch"
735 * GC attempted within js_NewGCThing), or
736 * - run for any reason on another thread if this thread is suspended on
737 * an object lock before it finishes generating bytecode into a script
738 * protected from the GC by a root or a stack frame reference.
740 JSTreeContext tc(this);
741 tc.scopeChain = chain;
742 if (!GenerateBlockId(&tc, tc.bodyid))
743 return NULL;
745 JSParseNode *pn = Statements(context, TS(this), &tc);
746 if (pn) {
747 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
748 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
749 JSMSG_SYNTAX_ERROR);
750 pn = NULL;
751 } else {
752 if (!js_FoldConstants(context, pn, &tc))
753 pn = NULL;
756 return pn;
759 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
761 static inline bool
762 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
765 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
766 * (0xffffffff) and other cookies with that level.
768 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
769 * practically speaking it leaves more than enough room for upvars. In fact
770 * we might want to split cookie fields giving fewer bits for skip and more
771 * for slot, but only based on evidence.
773 if (staticLevel >= FREE_STATIC_LEVEL) {
774 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
775 JSMSG_TOO_DEEP, js_function_str);
776 return false;
778 tc->staticLevel = staticLevel;
779 return true;
783 * Compile a top-level script.
785 JSScript *
786 JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
787 JSPrincipals *principals, uint32 tcflags,
788 const jschar *chars, size_t length,
789 FILE *file, const char *filename, uintN lineno,
790 JSString *source /* = NULL */)
792 JSCompiler jsc(cx, principals, callerFrame);
793 JSArenaPool codePool, notePool;
794 JSTokenType tt;
795 JSParseNode *pn;
796 uint32 scriptGlobals;
797 JSScript *script;
798 #ifdef METER_PARSENODES
799 void *sbrk(ptrdiff_t), *before = sbrk(0);
800 #endif
802 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
803 TCF_STATIC_LEVEL_MASK)));
806 * The scripted callerFrame can only be given for compile-and-go scripts
807 * and non-zero static level requires callerFrame.
809 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
810 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
812 if (!jsc.init(chars, length, file, filename, lineno))
813 return NULL;
815 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
816 &cx->scriptStackQuota);
817 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
818 &cx->scriptStackQuota);
820 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
822 MUST_FLOW_THROUGH("out");
824 /* Null script early in case of error, to reduce our code footprint. */
825 script = NULL;
827 cg.flags |= uint16(tcflags);
828 cg.scopeChain = scopeChain;
829 if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
830 goto out;
833 * If funbox is non-null after we create the new script, callerFrame->fun
834 * was saved in the 0th object table entry.
836 JSObjectBox *funbox;
837 funbox = NULL;
839 if (tcflags & TCF_COMPILE_N_GO) {
840 if (source) {
842 * Save eval program source in script->atomMap.vector[0] for the
843 * eval cache (see obj_eval in jsobj.cpp).
845 JSAtom *atom = js_AtomizeString(cx, source, 0);
846 if (!atom || !cg.atomList.add(&jsc, atom))
847 goto out;
850 if (callerFrame && callerFrame->fun) {
852 * An eval script in a caller frame needs to have its enclosing
853 * function captured in case it refers to an upvar, and someone
854 * wishes to decompile it while it's running.
856 funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
857 if (!funbox)
858 goto out;
859 funbox->emitLink = cg.objectList.lastbox;
860 cg.objectList.lastbox = funbox;
861 cg.objectList.length++;
866 * Inline Statements to emit as we go to save AST space. We must generate
867 * our script-body blockid since we aren't calling Statements.
869 uint32 bodyid;
870 if (!GenerateBlockId(&cg, bodyid))
871 goto out;
872 cg.bodyid = bodyid;
874 #if JS_HAS_XML_SUPPORT
875 pn = NULL;
876 bool onlyXML;
877 onlyXML = true;
878 #endif
880 for (;;) {
881 jsc.tokenStream.flags |= TSF_OPERAND;
882 tt = js_PeekToken(cx, &jsc.tokenStream);
883 jsc.tokenStream.flags &= ~TSF_OPERAND;
884 if (tt <= TOK_EOF) {
885 if (tt == TOK_EOF)
886 break;
887 JS_ASSERT(tt == TOK_ERROR);
888 goto out;
891 pn = Statement(cx, &jsc.tokenStream, &cg);
892 if (!pn)
893 goto out;
894 JS_ASSERT(!cg.blockNode);
896 if (!js_FoldConstants(cx, pn, &cg))
897 goto out;
899 if (cg.functionList) {
900 if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
901 goto out;
902 cg.functionList = NULL;
905 if (!js_EmitTree(cx, &cg, pn))
906 goto out;
907 #if JS_HAS_XML_SUPPORT
908 if (PN_TYPE(pn) != TOK_SEMI ||
909 !pn->pn_kid ||
910 !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
911 onlyXML = false;
913 #endif
914 RecycleTree(pn, &cg);
917 #if JS_HAS_XML_SUPPORT
919 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
920 * For background, see:
922 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
924 if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
925 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
926 JSMSG_XML_WHOLE_PROGRAM);
927 goto out;
929 #endif
932 * Global variables and regexps share the index space with locals. Due to
933 * incremental code generation we need to patch the bytecode to adjust the
934 * local references to skip the globals.
936 scriptGlobals = cg.ngvars + cg.regexpList.length;
937 if (scriptGlobals != 0) {
938 jsbytecode *code, *end;
939 JSOp op;
940 const JSCodeSpec *cs;
941 uintN len, slot;
943 if (scriptGlobals >= SLOTNO_LIMIT)
944 goto too_many_slots;
945 code = CG_BASE(&cg);
946 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
947 JS_ASSERT(code < end);
948 op = (JSOp) *code;
949 cs = &js_CodeSpec[op];
950 len = (cs->length > 0)
951 ? (uintN) cs->length
952 : js_GetVariableBytecodeLength(code);
953 if (JOF_TYPE(cs->format) == JOF_LOCAL ||
954 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
956 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
957 * emitted only for a function.
959 JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
960 (op == JSOP_GETLOCALPROP));
961 slot = GET_SLOTNO(code);
962 slot += scriptGlobals;
963 if (slot >= SLOTNO_LIMIT)
964 goto too_many_slots;
965 SET_SLOTNO(code, slot);
970 #ifdef METER_PARSENODES
971 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
972 (char *)sbrk(0) - (char *)before,
973 parsenodes,
974 maxparsenodes,
975 parsenodes - recyclednodes);
976 before = sbrk(0);
977 #endif
980 * Nowadays the threaded interpreter needs a stop instruction, so we
981 * do have to emit that here.
983 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
984 goto out;
985 #ifdef METER_PARSENODES
986 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
987 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
988 #endif
989 #ifdef JS_ARENAMETER
990 JS_DumpArenaStats(stdout);
991 #endif
992 script = js_NewScriptFromCG(cx, &cg);
993 if (script && funbox)
994 script->flags |= JSSF_SAVED_CALLER_FUN;
996 #ifdef JS_SCOPE_DEPTH_METER
997 if (script) {
998 JSObject *obj = scopeChain;
999 uintN depth = 1;
1000 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
1001 ++depth;
1002 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1004 #endif
1006 out:
1007 JS_FinishArenaPool(&codePool);
1008 JS_FinishArenaPool(&notePool);
1009 return script;
1011 too_many_slots:
1012 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1013 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1014 script = NULL;
1015 goto out;
1019 * Insist on a final return before control flows out of pn. Try to be a bit
1020 * smart about loops: do {...; return e2;} while(0) at the end of a function
1021 * that contains an early return e1 will get a strict warning. Similarly for
1022 * iloops: while (true){...} is treated as though ... returns.
1024 #define ENDS_IN_OTHER 0
1025 #define ENDS_IN_RETURN 1
1026 #define ENDS_IN_BREAK 2
1028 static int
1029 HasFinalReturn(JSParseNode *pn)
1031 JSParseNode *pn2, *pn3;
1032 uintN rv, rv2, hasDefault;
1034 switch (pn->pn_type) {
1035 case TOK_LC:
1036 if (!pn->pn_head)
1037 return ENDS_IN_OTHER;
1038 return HasFinalReturn(pn->last());
1040 case TOK_IF:
1041 if (!pn->pn_kid3)
1042 return ENDS_IN_OTHER;
1043 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1045 case TOK_WHILE:
1046 pn2 = pn->pn_left;
1047 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1048 return ENDS_IN_RETURN;
1049 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1050 return ENDS_IN_RETURN;
1051 return ENDS_IN_OTHER;
1053 case TOK_DO:
1054 pn2 = pn->pn_right;
1055 if (pn2->pn_type == TOK_PRIMARY) {
1056 if (pn2->pn_op == JSOP_FALSE)
1057 return HasFinalReturn(pn->pn_left);
1058 if (pn2->pn_op == JSOP_TRUE)
1059 return ENDS_IN_RETURN;
1061 if (pn2->pn_type == TOK_NUMBER) {
1062 if (pn2->pn_dval == 0)
1063 return HasFinalReturn(pn->pn_left);
1064 return ENDS_IN_RETURN;
1066 return ENDS_IN_OTHER;
1068 case TOK_FOR:
1069 pn2 = pn->pn_left;
1070 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1071 return ENDS_IN_RETURN;
1072 return ENDS_IN_OTHER;
1074 case TOK_SWITCH:
1075 rv = ENDS_IN_RETURN;
1076 hasDefault = ENDS_IN_OTHER;
1077 pn2 = pn->pn_right;
1078 if (pn2->pn_type == TOK_LEXICALSCOPE)
1079 pn2 = pn2->expr();
1080 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1081 if (pn2->pn_type == TOK_DEFAULT)
1082 hasDefault = ENDS_IN_RETURN;
1083 pn3 = pn2->pn_right;
1084 JS_ASSERT(pn3->pn_type == TOK_LC);
1085 if (pn3->pn_head) {
1086 rv2 = HasFinalReturn(pn3->last());
1087 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1088 /* Falling through to next case or default. */;
1089 else
1090 rv &= rv2;
1093 /* If a final switch has no default case, we judge it harshly. */
1094 rv &= hasDefault;
1095 return rv;
1097 case TOK_BREAK:
1098 return ENDS_IN_BREAK;
1100 case TOK_WITH:
1101 return HasFinalReturn(pn->pn_right);
1103 case TOK_RETURN:
1104 return ENDS_IN_RETURN;
1106 case TOK_COLON:
1107 case TOK_LEXICALSCOPE:
1108 return HasFinalReturn(pn->expr());
1110 case TOK_THROW:
1111 return ENDS_IN_RETURN;
1113 case TOK_TRY:
1114 /* If we have a finally block that returns, we are done. */
1115 if (pn->pn_kid3) {
1116 rv = HasFinalReturn(pn->pn_kid3);
1117 if (rv == ENDS_IN_RETURN)
1118 return rv;
1121 /* Else check the try block and any and all catch statements. */
1122 rv = HasFinalReturn(pn->pn_kid1);
1123 if (pn->pn_kid2) {
1124 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1125 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1126 rv &= HasFinalReturn(pn2);
1128 return rv;
1130 case TOK_CATCH:
1131 /* Check this catch block's body. */
1132 return HasFinalReturn(pn->pn_kid3);
1134 case TOK_LET:
1135 /* Non-binary let statements are let declarations. */
1136 if (pn->pn_arity != PN_BINARY)
1137 return ENDS_IN_OTHER;
1138 return HasFinalReturn(pn->pn_right);
1140 default:
1141 return ENDS_IN_OTHER;
1145 static JSBool
1146 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1147 uintN anonerrnum)
1149 const char *name;
1151 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1152 if (tc->fun->atom) {
1153 name = js_AtomToPrintableString(cx, tc->fun->atom);
1154 } else {
1155 errnum = anonerrnum;
1156 name = NULL;
1158 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1159 errnum, name);
1162 static JSBool
1163 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1165 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1166 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1167 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1168 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1171 static JSParseNode *
1172 FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1174 JSStmtInfo stmtInfo;
1175 uintN oldflags, firstLine;
1176 JSParseNode *pn;
1178 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1179 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1180 stmtInfo.flags = SIF_BODY_BLOCK;
1182 oldflags = tc->flags;
1183 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1186 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1187 * later, because we may have not peeked in ts yet, so Statements won't
1188 * acquire a valid pn->pn_pos.begin from the current token.
1190 firstLine = ts->lineno;
1191 #if JS_HAS_EXPR_CLOSURES
1192 if (CURRENT_TOKEN(ts).type == TOK_LC) {
1193 pn = Statements(cx, ts, tc);
1194 } else {
1195 pn = NewParseNode(PN_UNARY, tc);
1196 if (pn) {
1197 pn->pn_kid = AssignExpr(cx, ts, tc);
1198 if (!pn->pn_kid) {
1199 pn = NULL;
1200 } else {
1201 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1202 ReportBadReturn(cx, tc, JSREPORT_ERROR,
1203 JSMSG_BAD_GENERATOR_RETURN,
1204 JSMSG_BAD_ANON_GENERATOR_RETURN);
1205 pn = NULL;
1206 } else {
1207 pn->pn_type = TOK_RETURN;
1208 pn->pn_op = JSOP_RETURN;
1209 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1214 #else
1215 pn = Statements(cx, ts, tc);
1216 #endif
1218 if (pn) {
1219 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1220 js_PopStatement(tc);
1221 pn->pn_pos.begin.lineno = firstLine;
1223 /* Check for falling off the end of a function that returns a value. */
1224 if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
1225 !CheckFinalReturn(cx, tc, pn)) {
1226 pn = NULL;
1230 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1231 return pn;
1234 static JSAtomListElement *
1235 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1237 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1238 if (!ale)
1239 return NULL;
1241 JSDefinition *dn = (JSDefinition *)
1242 NewNameNode(tc->compiler->context, TS(tc->compiler), pn->pn_atom, tc);
1243 if (!dn)
1244 return NULL;
1246 ALE_SET_DEFN(ale, dn);
1247 dn->pn_defn = true;
1248 dn->pn_dflags |= PND_PLACEHOLDER;
1249 return ale;
1252 static bool
1253 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1255 JS_ASSERT(!pn->pn_used);
1256 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1258 JSHashEntry **hep;
1259 JSAtomListElement *ale = NULL;
1260 JSAtomList *list = NULL;
1262 if (let)
1263 ale = (list = &tc->decls)->rawLookup(atom, hep);
1264 if (!ale)
1265 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1267 if (ale) {
1268 JSDefinition *dn = ALE_DEFN(ale);
1269 if (dn != pn) {
1270 JSParseNode **pnup = &dn->dn_uses;
1271 JSParseNode *pnu;
1272 uintN start = let ? pn->pn_blockid : tc->bodyid;
1274 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1275 JS_ASSERT(pnu->pn_used);
1276 pnu->pn_lexdef = (JSDefinition *) pn;
1277 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1278 pnup = &pnu->pn_link;
1281 if (pnu != dn->dn_uses) {
1282 *pnup = pn->dn_uses;
1283 pn->dn_uses = dn->dn_uses;
1284 dn->dn_uses = pnu;
1286 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1287 list->rawRemove(tc->compiler, ale, hep);
1292 ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1293 if (!ale)
1294 return false;
1295 ALE_SET_DEFN(ale, pn);
1296 pn->pn_defn = true;
1297 pn->pn_dflags &= ~PND_PLACEHOLDER;
1298 return true;
1301 static void
1302 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1304 JS_ASSERT(!pn->pn_used);
1305 JS_ASSERT(!pn->pn_defn);
1306 JS_ASSERT(pn != dn->dn_uses);
1307 pn->pn_link = dn->dn_uses;
1308 dn->dn_uses = pn;
1309 pn->pn_used = true;
1310 pn->pn_lexdef = dn;
1313 static void
1314 ForgetUse(JSParseNode *pn)
1316 if (!pn->pn_used) {
1317 JS_ASSERT(!pn->pn_defn);
1318 return;
1321 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1322 JSParseNode *pnu;
1323 while ((pnu = *pnup) != pn)
1324 pnup = &pnu->pn_link;
1325 *pnup = pn->pn_link;
1326 pn->pn_used = false;
1329 static JSParseNode *
1330 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1332 JSParseNode *lhs = NewOrRecycledNode(tc);
1333 if (!lhs)
1334 return NULL;
1335 *lhs = *pn;
1337 if (pn->pn_used) {
1338 JSDefinition *dn = pn->pn_lexdef;
1339 JSParseNode **pnup = &dn->dn_uses;
1341 while (*pnup != pn)
1342 pnup = &(*pnup)->pn_link;
1343 *pnup = lhs;
1344 lhs->pn_link = pn->pn_link;
1345 pn->pn_link = NULL;
1348 pn->pn_type = TOK_ASSIGN;
1349 pn->pn_op = JSOP_NOP;
1350 pn->pn_arity = PN_BINARY;
1351 pn->pn_parens = false;
1352 pn->pn_used = pn->pn_defn = false;
1353 pn->pn_left = lhs;
1354 pn->pn_right = rhs;
1355 return lhs;
1358 static JSParseNode *
1359 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1362 * If dn is var, const, or let, and it has an initializer, then we must
1363 * rewrite it to be an assignment node, whose freshly allocated left-hand
1364 * side becomes a use of pn.
1366 if (dn->isBindingForm()) {
1367 JSParseNode *rhs = dn->expr();
1368 if (rhs) {
1369 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1370 if (!lhs)
1371 return NULL;
1372 //pn->dn_uses = lhs;
1373 dn = (JSDefinition *) lhs;
1376 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1377 } else if (dn->kind() == JSDefinition::FUNCTION) {
1378 JS_ASSERT(dn->isTopLevel());
1379 JS_ASSERT(dn->pn_op == JSOP_NOP);
1380 dn->pn_type = TOK_NAME;
1381 dn->pn_arity = PN_NAME;
1382 dn->pn_atom = atom;
1385 /* Now make dn no longer a definition, rather a use of pn. */
1386 JS_ASSERT(dn->pn_type == TOK_NAME);
1387 JS_ASSERT(dn->pn_arity == PN_NAME);
1388 JS_ASSERT(dn->pn_atom == atom);
1390 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1391 JS_ASSERT(pnu->pn_used);
1392 JS_ASSERT(!pnu->pn_defn);
1393 pnu->pn_lexdef = (JSDefinition *) pn;
1394 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1396 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1397 pn->dn_uses = dn;
1399 dn->pn_defn = false;
1400 dn->pn_used = true;
1401 dn->pn_lexdef = (JSDefinition *) pn;
1402 dn->pn_cookie = FREE_UPVAR_COOKIE;
1403 dn->pn_dflags &= ~PND_BOUND;
1404 return dn;
1407 static bool
1408 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1410 JSParseNode *argpn, *argsbody;
1412 /* Flag tc so we don't have to lookup arguments on every use. */
1413 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1414 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1417 * Make an argument definition node, distinguished by being in tc->decls
1418 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1419 * list node returned via pn->pn_body.
1421 argpn = NewNameNode(tc->compiler->context, TS(tc->compiler), atom, tc);
1422 if (!argpn)
1423 return false;
1424 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1426 /* Arguments are initialized by definition. */
1427 argpn->pn_dflags |= PND_INITIALIZED;
1428 if (!Define(argpn, atom, tc))
1429 return false;
1431 argsbody = pn->pn_body;
1432 if (!argsbody) {
1433 argsbody = NewParseNode(PN_LIST, tc);
1434 if (!argsbody)
1435 return false;
1436 argsbody->pn_type = TOK_ARGSBODY;
1437 argsbody->pn_op = JSOP_NOP;
1438 argsbody->makeEmpty();
1439 pn->pn_body = argsbody;
1441 argsbody->append(argpn);
1443 argpn->pn_op = JSOP_GETARG;
1444 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1445 argpn->pn_dflags |= PND_BOUND;
1446 return true;
1450 * Compile a JS function body, which might appear as the value of an event
1451 * handler attribute in an HTML <INPUT> tag.
1453 bool
1454 JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1455 const jschar *chars, size_t length,
1456 const char *filename, uintN lineno)
1458 JSCompiler jsc(cx, principals);
1460 if (!jsc.init(chars, length, NULL, filename, lineno))
1461 return false;
1463 /* No early return from after here until the js_FinishArenaPool calls. */
1464 JSArenaPool codePool, notePool;
1465 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
1466 &cx->scriptStackQuota);
1467 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
1468 &cx->scriptStackQuota);
1470 JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1471 funcg.flags |= TCF_IN_FUNCTION;
1472 funcg.fun = fun;
1473 if (!GenerateBlockId(&funcg, funcg.bodyid))
1474 return NULL;
1476 /* FIXME: make Function format the source for a function definition. */
1477 jsc.tokenStream.tokens[0].type = TOK_NAME;
1478 JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1479 if (fn) {
1480 fn->pn_body = NULL;
1481 fn->pn_cookie = FREE_UPVAR_COOKIE;
1483 uintN nargs = fun->nargs;
1484 if (nargs) {
1485 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1486 if (!names) {
1487 fn = NULL;
1488 } else {
1489 for (uintN i = 0; i < nargs; i++) {
1490 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1491 if (!DefineArg(fn, name, i, &funcg)) {
1492 fn = NULL;
1493 break;
1501 * Farble the body so that it looks like a block statement to js_EmitTree,
1502 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1503 * done parsing, we must fold constants, analyze any nested functions, and
1504 * generate code for this function, including a stop opcode at the end.
1506 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1507 JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1508 if (pn) {
1509 if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1510 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1511 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1512 pn = NULL;
1513 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1514 /* js_FoldConstants reported the error already. */
1515 pn = NULL;
1516 } else if (funcg.functionList &&
1517 !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1518 pn = NULL;
1519 } else {
1520 if (fn->pn_body) {
1521 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1522 fn->pn_body->append(pn);
1523 fn->pn_body->pn_pos = pn->pn_pos;
1524 pn = fn->pn_body;
1527 if (!js_EmitFunctionScript(cx, &funcg, pn))
1528 pn = NULL;
1532 /* Restore saved state and release code generation arenas. */
1533 JS_FinishArenaPool(&codePool);
1534 JS_FinishArenaPool(&notePool);
1535 return pn != NULL;
1539 * Parameter block types for the several Binder functions. We use a common
1540 * helper function signature in order to share code among destructuring and
1541 * simple variable declaration parsers. In the destructuring case, the binder
1542 * function is called indirectly from the variable declaration parser by way
1543 * of CheckDestructuring and its friends.
1545 typedef struct BindData BindData;
1547 typedef JSBool
1548 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1550 struct BindData {
1551 BindData() : fresh(true) {}
1553 JSParseNode *pn; /* name node for definition processing and
1554 error source coordinates */
1555 JSOp op; /* prolog bytecode or nop */
1556 Binder binder; /* binder, discriminates u */
1557 union {
1558 struct {
1559 uintN overflow;
1560 } let;
1562 bool fresh;
1565 static JSBool
1566 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1567 JSLocalKind localKind)
1569 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1572 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1573 * Instead 'var arguments' always restates the predefined property of the
1574 * activation objects whose name is 'arguments'. Assignment to such a
1575 * variable must be handled specially.
1577 if (atom == cx->runtime->atomState.argumentsAtom)
1578 return JS_TRUE;
1580 return js_AddLocal(cx, fun, atom, localKind);
1583 #if JS_HAS_DESTRUCTURING
1585 * Forward declaration to maintain top-down presentation.
1587 static JSParseNode *
1588 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1589 JSTokenType tt);
1591 static JSBool
1592 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1593 JSTreeContext *tc)
1595 JSAtomListElement *ale;
1596 JSParseNode *pn;
1598 /* Flag tc so we don't have to lookup arguments on every use. */
1599 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1600 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1602 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1603 ale = tc->decls.lookup(atom);
1604 pn = data->pn;
1605 if (!ale && !Define(pn, atom, tc))
1606 return JS_FALSE;
1608 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1609 if (localKind != JSLOCAL_NONE) {
1610 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1611 JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1612 return JS_FALSE;
1615 uintN index = tc->fun->u.i.nvars;
1616 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR))
1617 return JS_FALSE;
1618 pn->pn_op = JSOP_SETLOCAL;
1619 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1620 pn->pn_dflags |= PND_BOUND;
1621 return JS_TRUE;
1623 #endif /* JS_HAS_DESTRUCTURING */
1625 JSFunction *
1626 JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1628 JSObject *parent;
1629 JSFunction *fun;
1631 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1634 * Find the global compilation context in order to pre-set the newborn
1635 * function's parent slot to tc->scopeChain. If the global context is a
1636 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1637 * clear parent and proto.
1639 while (tc->parent)
1640 tc = tc->parent;
1641 parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1643 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1644 parent, atom);
1646 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1647 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1648 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1650 return fun;
1653 static JSBool
1654 MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1656 JSTokenType tt;
1658 ts->flags |= TSF_OPERAND;
1659 tt = js_PeekTokenSameLine(cx, ts);
1660 ts->flags &= ~TSF_OPERAND;
1661 if (tt == TOK_ERROR)
1662 return JS_FALSE;
1663 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1664 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1665 JSMSG_SEMI_BEFORE_STMNT);
1666 return JS_FALSE;
1668 (void) js_MatchToken(cx, ts, TOK_SEMI);
1669 return JS_TRUE;
1672 bool
1673 JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
1675 if (!markFunArgs(funbox, tcflags))
1676 return false;
1677 setFunctionKinds(funbox, tcflags);
1678 return true;
1682 * Mark as funargs any functions that reach up to one or more upvars across an
1683 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1685 * function f(o, p) {
1686 * o.m = function o_m(a) {
1687 * function g() { return p; }
1688 * function h() { return a; }
1689 * return g() + h();
1693 * but without this extra marking phase, function g will not be marked as a
1694 * funarg since it is called from within its parent scope. But g reaches up to
1695 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1696 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1697 * nor uses an upvar "above" o_m's level.
1699 * If function g itself contained lambdas that contained non-lambdas that reach
1700 * up above its level, then those non-lambdas would have to be marked too. This
1701 * process is potentially exponential in the number of functions, but generally
1702 * not so complex. But it can't be done during a single recursive traversal of
1703 * the funbox tree, so we must use a work queue.
1705 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1706 * between the static level of the bodies of funbox and its peers (which must
1707 * be funbox->level + 1), and the static level of the nearest upvar among all
1708 * the upvars contained by funbox and its peers. If there are no upvars, return
1709 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1711 static uintN
1712 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1714 uintN allskipmin = FREE_STATIC_LEVEL;
1716 do {
1717 JSParseNode *fn = funbox->node;
1718 JSFunction *fun = (JSFunction *) funbox->object;
1719 int fnlevel = level;
1722 * An eval can leak funbox, functions along its ancestor line, and its
1723 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1724 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1725 * already been marked as funargs by this point. Therefore we have to
1726 * flag only funbox->node and funbox->kids' nodes here.
1728 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1729 fn->setFunArg();
1730 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1731 kid->node->setFunArg();
1735 * Compute in skipmin the least distance from fun's static level up to
1736 * an upvar, whether used directly by fun, or indirectly by a function
1737 * nested in fun.
1739 uintN skipmin = FREE_STATIC_LEVEL;
1740 JSParseNode *pn = fn->pn_body;
1742 if (pn->pn_type == TOK_UPVARS) {
1743 JSAtomList upvars(pn->pn_names);
1744 JS_ASSERT(upvars.count != 0);
1746 JSAtomListIterator iter(&upvars);
1747 JSAtomListElement *ale;
1749 while ((ale = iter()) != NULL) {
1750 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1752 if (!lexdep->isFreeVar()) {
1753 uintN upvarLevel = lexdep->frameLevel();
1755 if (int(upvarLevel) <= fnlevel)
1756 fn->setFunArg();
1758 uintN skip = (funbox->level + 1) - upvarLevel;
1759 if (skip < skipmin)
1760 skipmin = skip;
1766 * If this function escapes, whether directly (the parser detects such
1767 * escapes) or indirectly (because this non-escaping function uses an
1768 * upvar that reaches across an outer function boundary where the outer
1769 * function escapes), enqueue it for further analysis, and bump fnlevel
1770 * to trap any non-escaping children.
1772 if (fn->isFunArg()) {
1773 queue->push(funbox);
1774 fnlevel = int(funbox->level);
1778 * Now process the current function's children, and recalibrate their
1779 * cumulative skipmin to be relative to the current static level.
1781 if (funbox->kids) {
1782 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1784 JS_ASSERT(kidskipmin != 0);
1785 if (kidskipmin != FREE_STATIC_LEVEL) {
1786 --kidskipmin;
1787 if (kidskipmin != 0 && kidskipmin < skipmin)
1788 skipmin = kidskipmin;
1793 * Finally, after we've traversed all of the current function's kids,
1794 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1795 * with allskipmin, but minimize across funbox and all of its siblings,
1796 * to compute our return value.
1798 if (skipmin != FREE_STATIC_LEVEL) {
1799 fun->u.i.skipmin = skipmin;
1800 if (skipmin < allskipmin)
1801 allskipmin = skipmin;
1803 } while ((funbox = funbox->siblings) != NULL);
1805 return allskipmin;
1808 bool
1809 JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1811 JSFunctionBoxQueue queue;
1812 if (!queue.init(functionCount))
1813 return false;
1815 FindFunArgs(funbox, -1, &queue);
1816 while ((funbox = queue.pull()) != NULL) {
1817 JSParseNode *fn = funbox->node;
1818 JS_ASSERT(fn->isFunArg());
1820 JSParseNode *pn = fn->pn_body;
1821 if (pn->pn_type == TOK_UPVARS) {
1822 JSAtomList upvars(pn->pn_names);
1823 JS_ASSERT(upvars.count != 0);
1825 JSAtomListIterator iter(&upvars);
1826 JSAtomListElement *ale;
1828 while ((ale = iter()) != NULL) {
1829 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1831 if (!lexdep->isFreeVar() &&
1832 !lexdep->isFunArg() &&
1833 lexdep->kind() == JSDefinition::FUNCTION) {
1835 * Mark this formerly-Algol-like function as an escaping
1836 * function (i.e., as a funarg), because it is used from a
1837 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1838 * access upvars.
1840 * Progress is guaranteed because we set the funarg flag
1841 * here, which suppresses revisiting this function (thanks
1842 * to the !lexdep->isFunArg() test just above).
1844 lexdep->setFunArg();
1846 JSFunctionBox *afunbox = lexdep->pn_funbox;
1847 queue.push(afunbox);
1850 * Walk over nested functions again, now that we have
1851 * changed the level across which it is unsafe to access
1852 * upvars using the runtime dynamic link (frame chain).
1854 if (afunbox->kids)
1855 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1860 return true;
1863 static uint32
1864 MinBlockId(JSParseNode *fn, uint32 id)
1866 if (fn->pn_blockid < id)
1867 return false;
1868 if (fn->pn_defn) {
1869 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1870 if (pn->pn_blockid < id)
1871 return false;
1874 return true;
1877 static bool
1878 OneBlockId(JSParseNode *fn, uint32 id)
1880 if (fn->pn_blockid != id)
1881 return false;
1882 if (fn->pn_defn) {
1883 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1884 if (pn->pn_blockid != id)
1885 return false;
1888 return true;
1891 void
1892 JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
1894 #ifdef JS_FUNCTION_METERING
1895 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1896 #else
1897 # define FUN_METER(x) ((void)0)
1898 #endif
1899 JSFunctionBox *parent = funbox->parent;
1901 for (;;) {
1902 JSParseNode *fn = funbox->node;
1904 if (funbox->kids)
1905 setFunctionKinds(funbox->kids, tcflags);
1907 JSParseNode *pn = fn->pn_body;
1908 JSFunction *fun = (JSFunction *) funbox->object;
1910 FUN_METER(allfun);
1911 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1912 FUN_METER(heavy);
1913 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
1914 } else if (pn->pn_type != TOK_UPVARS) {
1916 * No lexical dependencies => null closure, for best performance.
1917 * A null closure needs no scope chain, but alas we've coupled
1918 * principals-finding to scope (for good fundamental reasons, but
1919 * the implementation overloads the parent slot and we should fix
1920 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1922 * In more detail: the ES3 spec allows the implementation to create
1923 * "joined function objects", or not, at its discretion. But real-
1924 * world implementations always create unique function objects for
1925 * closures, and this can be detected via mutation. Open question:
1926 * do popular implementations create unique function objects for
1927 * null closures?
1929 * FIXME: bug 476950.
1931 FUN_METER(nofreeupvar);
1932 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1933 } else {
1934 JSAtomList upvars(pn->pn_names);
1935 JS_ASSERT(upvars.count != 0);
1937 JSAtomListIterator iter(&upvars);
1938 JSAtomListElement *ale;
1940 if (!fn->isFunArg()) {
1942 * This function is Algol-like, it never escapes. So long as it
1943 * does not assign to outer variables, it needs only an upvars
1944 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1945 * bytecode to reach up the frame stack at runtime based on
1946 * those upvars' cookies.
1948 * Any assignments to upvars from functions called by this one
1949 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1950 * which load from stack homes when interpreting or from native
1951 * stack slots when executing a trace.
1953 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1954 * nested function to assign to an outer lexical variable, so
1955 * we defer adding yet more code footprint in the absence of
1956 * evidence motivating these opcodes.
1958 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
1959 uintN nupvars = 0;
1962 * Check that at least one outer lexical binding was assigned
1963 * to (global variables don't count). This is conservative: we
1964 * could limit assignments to those in the current function,
1965 * but that's too much work. As with flat closures (handled
1966 * below), we optimize for the case where outer bindings are
1967 * not reassigned anywhere.
1969 while ((ale = iter()) != NULL) {
1970 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1972 if (!lexdep->isFreeVar()) {
1973 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
1974 ++nupvars;
1975 if (lexdep->isAssigned())
1976 break;
1979 if (!ale)
1980 mutation = false;
1982 if (nupvars == 0) {
1983 FUN_METER(onlyfreevar);
1984 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1985 } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
1987 * Algol-like functions can read upvars using the dynamic
1988 * link (cx->fp/fp->down). They do not need to entrain and
1989 * search their environment.
1991 FUN_METER(display);
1992 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1993 } else {
1994 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
1995 FUN_METER(setupvar);
1997 } else {
1998 uintN nupvars = 0;
2001 * For each lexical dependency from this closure to an outer
2002 * binding, analyze whether it is safe to copy the binding's
2003 * value into a flat closure slot when the closure is formed.
2005 while ((ale = iter()) != NULL) {
2006 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2008 if (!lexdep->isFreeVar()) {
2009 ++nupvars;
2012 * Consider the current function (the lambda, innermost
2013 * below) using a var x defined two static levels up:
2015 * function f() {
2016 * // z = g();
2017 * var x = 42;
2018 * function g() {
2019 * return function () { return x; };
2021 * return g();
2024 * So long as (1) the initialization in 'var x = 42'
2025 * dominates all uses of g and (2) x is not reassigned,
2026 * it is safe to optimize the lambda to a flat closure.
2027 * Uncommenting the early call to g makes it unsafe to
2028 * so optimize (z could name a global setter that calls
2029 * its argument).
2031 JSFunctionBox *afunbox = funbox;
2032 uintN lexdepLevel = lexdep->frameLevel();
2034 JS_ASSERT(lexdepLevel <= funbox->level);
2035 while (afunbox->level != lexdepLevel) {
2036 afunbox = afunbox->parent;
2039 * afunbox can't be null because we are sure
2040 * to find a function box whose level == lexdepLevel
2041 * before walking off the top of the funbox tree.
2042 * See bug 493260 comments 16-18.
2044 * Assert but check anyway, to check future changes
2045 * that bind eval upvars in the parser.
2047 JS_ASSERT(afunbox);
2050 * If this function is reaching up across an
2051 * enclosing funarg, we cannot make a flat
2052 * closure. The display stops working once the
2053 * funarg escapes.
2055 if (!afunbox || afunbox->node->isFunArg())
2056 goto break2;
2060 * If afunbox's function (which is at the same level as
2061 * lexdep) is in a loop, pessimistically assume the
2062 * variable initializer may be in the same loop. A flat
2063 * closure would then be unsafe, as the captured
2064 * variable could be assigned after the closure is
2065 * created. See bug 493232.
2067 if (afunbox->inLoop)
2068 break;
2071 * with and eval defeat lexical scoping; eval anywhere
2072 * in a variable's scope can assign to it. Both defeat
2073 * the flat closure optimization. The parser detects
2074 * these cases and flags the function heavyweight.
2076 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags)
2077 & TCF_FUN_HEAVYWEIGHT) {
2078 break;
2082 * If afunbox's function is not a lambda, it will be
2083 * hoisted, so it could capture the undefined value
2084 * that by default initializes var/let/const
2085 * bindings. And if lexdep is a function that comes at
2086 * (meaning a function refers to its own name) or
2087 * strictly after afunbox, we also break to defeat the
2088 * flat closure optimization.
2090 JSFunction *afun = (JSFunction *) afunbox->object;
2091 if (!(afun->flags & JSFUN_LAMBDA)) {
2092 if (lexdep->isBindingForm())
2093 break;
2094 if (lexdep->pn_pos >= afunbox->node->pn_pos)
2095 break;
2098 if (!lexdep->isInitialized())
2099 break;
2101 JSDefinition::Kind lexdepKind = lexdep->kind();
2102 if (lexdepKind != JSDefinition::CONST) {
2103 if (lexdep->isAssigned())
2104 break;
2107 * Any formal could be mutated behind our back via
2108 * the arguments object, so deoptimize if the outer
2109 * function uses arguments.
2111 * In a Function constructor call where the final
2112 * argument -- the body source for the function to
2113 * create -- contains a nested function definition
2114 * or expression, afunbox->parent will be null. The
2115 * body source might use |arguments| outside of any
2116 * nested functions it may contain, so we have to
2117 * check the tcflags parameter that was passed in
2118 * from JSCompiler::compileFunctionBody.
2120 if (lexdepKind == JSDefinition::ARG &&
2121 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2122 TCF_FUN_USES_ARGUMENTS)) {
2123 break;
2128 * Check quick-and-dirty dominance relation. Function
2129 * definitions dominate their uses thanks to hoisting.
2130 * Other binding forms hoist as undefined, of course,
2131 * so check forward-reference and blockid relations.
2133 if (lexdepKind != JSDefinition::FUNCTION) {
2135 * Watch out for code such as
2137 * (function () {
2138 * ...
2139 * var jQuery = ... = function (...) {
2140 * return new jQuery.foo.bar(baz);
2142 * ...
2143 * })();
2145 * where the jQuery var is not reassigned, but of
2146 * course is not initialized at the time that the
2147 * would-be-flat closure containing the jQuery
2148 * upvar is formed.
2150 if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2151 break;
2153 if (lexdep->isTopLevel()
2154 ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2155 : !lexdep->isBlockChild() ||
2156 !afunbox->node->isBlockChild() ||
2157 !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2158 break;
2164 break2:
2165 if (nupvars == 0) {
2166 FUN_METER(onlyfreevar);
2167 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2168 } else if (!ale) {
2170 * We made it all the way through the upvar loop, so it's
2171 * safe to optimize to a flat closure.
2173 FUN_METER(flat);
2174 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2175 switch (PN_OP(fn)) {
2176 case JSOP_DEFFUN:
2177 fn->pn_op = JSOP_DEFFUN_FC;
2178 break;
2179 case JSOP_DEFLOCALFUN:
2180 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2181 break;
2182 case JSOP_LAMBDA:
2183 fn->pn_op = JSOP_LAMBDA_FC;
2184 break;
2185 default:
2186 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2187 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2189 } else {
2190 FUN_METER(badfunarg);
2195 if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2196 if (pn->pn_type != TOK_UPVARS) {
2197 if (parent)
2198 parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2199 } else {
2200 JSAtomList upvars(pn->pn_names);
2201 JS_ASSERT(upvars.count != 0);
2203 JSAtomListIterator iter(&upvars);
2204 JSAtomListElement *ale;
2207 * One or more upvars cannot be safely snapshot into a flat
2208 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2209 * all upvars, and for each non-free upvar, ensure that its
2210 * containing function has been flagged as heavyweight.
2212 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2213 * generating any code for a tree of nested functions.
2215 while ((ale = iter()) != NULL) {
2216 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2218 if (!lexdep->isFreeVar()) {
2219 JSFunctionBox *afunbox = funbox->parent;
2220 uintN lexdepLevel = lexdep->frameLevel();
2222 while (afunbox) {
2224 * NB: afunbox->level is the static level of
2225 * the definition or expression of the function
2226 * parsed into afunbox, not the static level of
2227 * its body. Therefore we must add 1 to match
2228 * lexdep's level to find the afunbox whose
2229 * body contains the lexdep definition.
2231 if (afunbox->level + 1U == lexdepLevel ||
2232 (lexdepLevel == 0 && lexdep->isLet())) {
2233 afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2234 break;
2236 afunbox = afunbox->parent;
2238 if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2239 tcflags |= TCF_FUN_HEAVYWEIGHT;
2245 funbox = funbox->siblings;
2246 if (!funbox)
2247 break;
2248 JS_ASSERT(funbox->parent == parent);
2250 #undef FUN_METER
2253 const char js_argument_str[] = "argument";
2254 const char js_variable_str[] = "variable";
2255 const char js_unknown_str[] = "unknown";
2257 const char *
2258 JSDefinition::kindString(Kind kind)
2260 static const char *table[] = {
2261 js_var_str, js_const_str, js_let_str,
2262 js_function_str, js_argument_str, js_unknown_str
2265 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2266 return table[kind];
2269 static JSFunctionBox *
2270 EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2271 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2273 JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2274 if (!fun)
2275 return NULL;
2277 /* Create box for fun->object early to protect against last-ditch GC. */
2278 JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2279 if (!funbox)
2280 return NULL;
2282 /* Initialize non-default members of funtc. */
2283 funtc->flags |= funbox->tcflags;
2284 funtc->blockidGen = tc->blockidGen;
2285 if (!GenerateBlockId(funtc, funtc->bodyid))
2286 return NULL;
2287 funtc->fun = fun;
2288 funtc->funbox = funbox;
2289 funtc->parent = tc;
2290 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2291 return NULL;
2293 return funbox;
2296 static bool
2297 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2298 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2300 tc->blockidGen = funtc->blockidGen;
2302 fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2304 fn->pn_dflags |= PND_INITIALIZED;
2305 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2306 fn->pn_dflags & PND_TOPLEVEL);
2307 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2308 fn->pn_dflags |= PND_BLOCKCHILD;
2311 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2312 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2313 * params and body. We do this only if there are lexical dependencies not
2314 * satisfied by the function's declarations, to avoid penalizing functions
2315 * that use only their arguments and other local bindings.
2317 if (funtc->lexdeps.count != 0) {
2318 JSAtomListIterator iter(&funtc->lexdeps);
2319 JSAtomListElement *ale;
2320 int foundCallee = 0;
2322 while ((ale = iter()) != NULL) {
2323 JSAtom *atom = ALE_ATOM(ale);
2324 JSDefinition *dn = ALE_DEFN(ale);
2325 JS_ASSERT(dn->isPlaceholder());
2327 if (atom == funAtom && lambda != 0) {
2328 dn->pn_op = JSOP_CALLEE;
2329 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2330 dn->pn_dflags |= PND_BOUND;
2333 * If this named function expression uses its own name other
2334 * than to call itself, flag this function as using arguments,
2335 * as if it had used arguments.callee instead of its own name.
2337 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2338 * we are out of tcflags bits at the moment. If it deoptimizes
2339 * code unfairly (see JSCompiler::setFunctionKinds, where this
2340 * flag is interpreted in its broader sense, not only to mean
2341 * "this function might leak arguments.callee"), we can perhaps
2342 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2343 * use that more precisely, both here and for unnamed function
2344 * expressions.
2346 if (dn->isFunArg())
2347 fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
2348 foundCallee = 1;
2349 continue;
2352 if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2353 dn->isAssigned()) {
2355 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2356 * any use of dn in funtc assigns. See NoteLValue for the easy
2357 * backward-reference case; this is the hard forward-reference
2358 * case where we pay a higher price.
2360 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2361 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2362 fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2363 break;
2368 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2369 if (!outer_ale)
2370 outer_ale = tc->lexdeps.lookup(atom);
2371 if (outer_ale) {
2373 * Insert dn's uses list at the front of outer_dn's list.
2375 * Without loss of generality or correctness, we allow a dn to
2376 * be in inner and outer lexdeps, since the purpose of lexdeps
2377 * is one-pass coordination of name use and definition across
2378 * functions, and if different dn's are used we'll merge lists
2379 * when leaving the inner function.
2381 * The dn == outer_dn case arises with generator expressions
2382 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2383 * case), and nowhere else, currently.
2385 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2387 if (dn != outer_dn) {
2388 JSParseNode **pnup = &dn->dn_uses;
2389 JSParseNode *pnu;
2391 while ((pnu = *pnup) != NULL) {
2392 pnu->pn_lexdef = outer_dn;
2393 pnup = &pnu->pn_link;
2397 * Make dn be a use that redirects to outer_dn, because we
2398 * can't replace dn with outer_dn in all the pn_namesets in
2399 * the AST where it may be. Instead we make it forward to
2400 * outer_dn. See JSDefinition::resolve.
2402 *pnup = outer_dn->dn_uses;
2403 outer_dn->dn_uses = dn;
2404 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2405 dn->pn_defn = false;
2406 dn->pn_used = true;
2407 dn->pn_lexdef = outer_dn;
2409 } else {
2410 /* Add an outer lexical dependency for ale's definition. */
2411 outer_ale = tc->lexdeps.add(tc->compiler, atom);
2412 if (!outer_ale)
2413 return false;
2414 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2418 if (funtc->lexdeps.count - foundCallee != 0) {
2419 JSParseNode *body = fn->pn_body;
2421 fn->pn_body = NewParseNode(PN_NAMESET, tc);
2422 if (!fn->pn_body)
2423 return false;
2425 fn->pn_body->pn_type = TOK_UPVARS;
2426 fn->pn_body->pn_pos = body->pn_pos;
2427 if (foundCallee)
2428 funtc->lexdeps.remove(tc->compiler, funAtom);
2429 fn->pn_body->pn_names = funtc->lexdeps;
2430 fn->pn_body->pn_tree = body;
2433 funtc->lexdeps.clear();
2436 return true;
2439 static JSParseNode *
2440 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2441 uintN lambda)
2443 JSOp op;
2444 JSParseNode *pn, *body, *result;
2445 JSTokenType tt;
2446 JSAtom *funAtom;
2447 JSAtomListElement *ale;
2448 #if JS_HAS_DESTRUCTURING
2449 JSParseNode *item, *list = NULL;
2450 bool destructuringArg = false, duplicatedArg = false;
2451 #endif
2453 /* Make a TOK_FUNCTION node. */
2454 #if JS_HAS_GETTER_SETTER
2455 op = CURRENT_TOKEN(ts).t_op;
2456 #endif
2457 pn = NewParseNode(PN_FUNC, tc);
2458 if (!pn)
2459 return NULL;
2460 pn->pn_body = NULL;
2461 pn->pn_cookie = FREE_UPVAR_COOKIE;
2464 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2465 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2467 * Also treat function sub-statements (non-lambda, non-top-level functions)
2468 * as escaping funargs, since we can't statically analyze their definitions
2469 * and uses.
2471 bool topLevel = tc->atTopLevel();
2472 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2474 /* Scan the optional function name into funAtom. */
2475 ts->flags |= TSF_KEYWORD_IS_NAME;
2476 tt = js_GetToken(cx, ts);
2477 ts->flags &= ~TSF_KEYWORD_IS_NAME;
2478 if (tt == TOK_NAME) {
2479 funAtom = CURRENT_TOKEN(ts).t_atom;
2480 } else {
2481 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
2482 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2483 JSMSG_SYNTAX_ERROR);
2484 return NULL;
2486 funAtom = NULL;
2487 js_UngetToken(ts);
2491 * Record names for function statements in tc->decls so we know when to
2492 * avoid optimizing variable references that might name a function.
2494 if (lambda == 0 && funAtom) {
2495 ale = tc->decls.lookup(funAtom);
2496 if (ale) {
2497 JSDefinition *dn = ALE_DEFN(ale);
2498 JSDefinition::Kind dn_kind = dn->kind();
2500 JS_ASSERT(!dn->pn_used);
2501 JS_ASSERT(dn->pn_defn);
2503 if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2504 const char *name = js_AtomToPrintableString(cx, funAtom);
2505 if (!name ||
2506 !js_ReportCompileErrorNumber(cx, ts, NULL,
2507 (dn_kind != JSDefinition::CONST)
2508 ? JSREPORT_WARNING | JSREPORT_STRICT
2509 : JSREPORT_ERROR,
2510 JSMSG_REDECLARED_VAR,
2511 JSDefinition::kindString(dn_kind),
2512 name)) {
2513 return NULL;
2517 if (topLevel) {
2518 ALE_SET_DEFN(ale, pn);
2519 pn->pn_defn = true;
2520 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2522 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2523 return NULL;
2525 } else if (topLevel) {
2527 * If this function was used before it was defined, claim the
2528 * pre-created definition node for this function that PrimaryExpr
2529 * put in tc->lexdeps on first forward reference, and recycle pn.
2531 JSHashEntry **hep;
2533 ale = tc->lexdeps.rawLookup(funAtom, hep);
2534 if (ale) {
2535 JSDefinition *fn = ALE_DEFN(ale);
2537 JS_ASSERT(fn->pn_defn);
2538 fn->pn_type = TOK_FUNCTION;
2539 fn->pn_arity = PN_FUNC;
2540 fn->pn_pos.begin = pn->pn_pos.begin;
2541 fn->pn_body = NULL;
2542 fn->pn_cookie = FREE_UPVAR_COOKIE;
2544 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2545 RecycleTree(pn, tc);
2546 pn = fn;
2549 if (!Define(pn, funAtom, tc))
2550 return NULL;
2554 * A function nested at top level inside another's body needs only a
2555 * local variable to bind its name to its value, and not an activation
2556 * object property (it might also need the activation property, if the
2557 * outer function contains with statements, e.g., but the stack slot
2558 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2559 * JSOP_GETLOCAL bytecode).
2561 if (topLevel) {
2562 pn->pn_dflags |= PND_TOPLEVEL;
2564 if (tc->flags & TCF_IN_FUNCTION) {
2565 JSLocalKind localKind;
2566 uintN index;
2569 * Define a local in the outer function so that BindNameToSlot
2570 * can properly optimize accesses. Note that we need a local
2571 * variable, not an argument, for the function statement. Thus
2572 * we add a variable even if a parameter with the given name
2573 * already exists.
2575 localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2576 switch (localKind) {
2577 case JSLOCAL_NONE:
2578 case JSLOCAL_ARG:
2579 index = tc->fun->u.i.nvars;
2580 if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2581 return NULL;
2582 /* FALL THROUGH */
2584 case JSLOCAL_VAR:
2585 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2586 pn->pn_dflags |= PND_BOUND;
2587 break;
2589 default:;
2595 /* Initialize early for possible flags mutation via DestructuringExpr. */
2596 JSTreeContext funtc(tc->compiler);
2598 JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2599 if (!funbox)
2600 return NULL;
2602 JSFunction *fun = (JSFunction *) funbox->object;
2604 #if JS_HAS_GETTER_SETTER
2605 if (op != JSOP_NOP)
2606 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2607 #endif
2609 /* Now parse formal argument list and compute fun->nargs. */
2610 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2611 if (!js_MatchToken(cx, ts, TOK_RP)) {
2612 do {
2613 tt = js_GetToken(cx, ts);
2614 switch (tt) {
2615 #if JS_HAS_DESTRUCTURING
2616 case TOK_LB:
2617 case TOK_LC:
2619 BindData data;
2620 JSParseNode *lhs, *rhs;
2621 jsint slot;
2623 /* See comment below in the TOK_NAME case. */
2624 if (duplicatedArg)
2625 goto report_dup_and_destructuring;
2626 destructuringArg = true;
2629 * A destructuring formal parameter turns into one or more
2630 * local variables initialized from properties of a single
2631 * anonymous positional parameter, so here we must tweak our
2632 * binder and its data.
2634 data.pn = NULL;
2635 data.op = JSOP_DEFVAR;
2636 data.binder = BindDestructuringArg;
2637 lhs = DestructuringExpr(cx, &data, &funtc, tt);
2638 if (!lhs)
2639 return NULL;
2642 * Adjust fun->nargs to count the single anonymous positional
2643 * parameter that is to be destructured.
2645 slot = fun->nargs;
2646 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
2647 return NULL;
2650 * Synthesize a destructuring assignment from the single
2651 * anonymous positional parameter into the destructuring
2652 * left-hand-side expression and accumulate it in list.
2654 rhs = NewNameNode(cx, ts, cx->runtime->atomState.emptyAtom, &funtc);
2655 if (!rhs)
2656 return NULL;
2657 rhs->pn_type = TOK_NAME;
2658 rhs->pn_op = JSOP_GETARG;
2659 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2660 rhs->pn_dflags |= PND_BOUND;
2662 item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2663 if (!item)
2664 return NULL;
2665 if (!list) {
2666 list = NewParseNode(PN_LIST, &funtc);
2667 if (!list)
2668 return NULL;
2669 list->pn_type = TOK_COMMA;
2670 list->makeEmpty();
2672 list->append(item);
2673 break;
2675 #endif /* JS_HAS_DESTRUCTURING */
2677 case TOK_NAME:
2680 * Check for a duplicate parameter name, a "feature" that
2681 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2682 * soon to be an ES3.1 strict error.
2684 * Further, if any argument is a destructuring pattern, forbid
2685 * duplicates. We will report the error either now if we have
2686 * seen a destructuring pattern already, or later when we find
2687 * the first pattern.
2689 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2690 if (JS_HAS_STRICT_OPTION(cx) &&
2691 js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2692 #if JS_HAS_DESTRUCTURING
2693 if (destructuringArg)
2694 goto report_dup_and_destructuring;
2695 duplicatedArg = true;
2696 #endif
2697 const char *name = js_AtomToPrintableString(cx, atom);
2698 if (!name ||
2699 !js_ReportCompileErrorNumber(cx, TS(funtc.compiler),
2700 NULL,
2701 JSREPORT_WARNING |
2702 JSREPORT_STRICT,
2703 JSMSG_DUPLICATE_FORMAL,
2704 name)) {
2705 return NULL;
2708 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2709 return NULL;
2710 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2711 return NULL;
2712 break;
2715 default:
2716 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2717 JSMSG_MISSING_FORMAL);
2718 /* FALL THROUGH */
2719 case TOK_ERROR:
2720 return NULL;
2722 #if JS_HAS_DESTRUCTURING
2723 report_dup_and_destructuring:
2724 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
2725 JSREPORT_ERROR,
2726 JSMSG_DESTRUCT_DUP_ARG);
2727 return NULL;
2728 #endif
2730 } while (js_MatchToken(cx, ts, TOK_COMMA));
2732 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2735 #if JS_HAS_EXPR_CLOSURES
2736 ts->flags |= TSF_OPERAND;
2737 tt = js_GetToken(cx, ts);
2738 ts->flags &= ~TSF_OPERAND;
2739 if (tt != TOK_LC) {
2740 js_UngetToken(ts);
2741 fun->flags |= JSFUN_EXPR_CLOSURE;
2743 #else
2744 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2745 #endif
2747 body = FunctionBody(cx, ts, &funtc);
2748 if (!body)
2749 return NULL;
2751 #if JS_HAS_EXPR_CLOSURES
2752 if (tt == TOK_LC)
2753 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2754 else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2755 return NULL;
2756 #else
2757 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2758 #endif
2759 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2761 #if JS_HAS_DESTRUCTURING
2763 * If there were destructuring formal parameters, prepend the initializing
2764 * comma expression that we synthesized to body. If the body is a lexical
2765 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2766 * parameter destructuring code without bracing the decompilation of the
2767 * function body's lexical scope.
2769 if (list) {
2770 if (body->pn_arity != PN_LIST) {
2771 JSParseNode *block;
2773 block = NewParseNode(PN_LIST, tc);
2774 if (!block)
2775 return NULL;
2776 block->pn_type = TOK_SEQ;
2777 block->pn_pos = body->pn_pos;
2778 block->initList(body);
2780 body = block;
2783 item = NewParseNode(PN_UNARY, tc);
2784 if (!item)
2785 return NULL;
2787 item->pn_type = TOK_SEMI;
2788 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2789 item->pn_kid = list;
2790 item->pn_next = body->pn_head;
2791 body->pn_head = item;
2792 if (body->pn_tail == &body->pn_head)
2793 body->pn_tail = &item->pn_next;
2794 ++body->pn_count;
2795 body->pn_xflags |= PNX_DESTRUCT;
2797 #endif
2800 * If we collected flags that indicate nested heavyweight functions, or
2801 * this function contains heavyweight-making statements (with statement,
2802 * visible eval call, or assignment to 'arguments'), flag the function as
2803 * heavyweight (requiring a call object per invocation).
2805 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2806 fun->flags |= JSFUN_HEAVYWEIGHT;
2807 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2808 } else {
2810 * If this function is a named statement function not at top-level
2811 * (i.e. not a top-level function definiton or expression), then our
2812 * enclosing function, if any, must be heavyweight.
2814 if (!topLevel && lambda == 0 && funAtom)
2815 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2818 result = pn;
2819 if (lambda != 0) {
2821 * ECMA ed. 3 standard: function expression, possibly anonymous.
2823 op = JSOP_LAMBDA;
2824 } else if (!funAtom) {
2826 * If this anonymous function definition is *not* embedded within a
2827 * larger expression, we treat it as an expression statement, not as
2828 * a function declaration -- and not as a syntax error (as ECMA-262
2829 * Edition 3 would have it). Backward compatibility must trump all,
2830 * unless JSOPTION_ANONFUNFIX is set.
2832 result = NewParseNode(PN_UNARY, tc);
2833 if (!result)
2834 return NULL;
2835 result->pn_type = TOK_SEMI;
2836 result->pn_pos = pn->pn_pos;
2837 result->pn_kid = pn;
2838 op = JSOP_LAMBDA;
2839 } else if (!topLevel) {
2841 * ECMA ed. 3 extension: a function expression statement not at the
2842 * top level, e.g., in a compound statement such as the "then" part
2843 * of an "if" statement, binds a closure only if control reaches that
2844 * sub-statement.
2846 op = JSOP_DEFFUN;
2847 } else {
2848 op = JSOP_NOP;
2851 funbox->kids = funtc.functionList;
2853 pn->pn_funbox = funbox;
2854 pn->pn_op = op;
2855 if (pn->pn_body) {
2856 pn->pn_body->append(body);
2857 pn->pn_body->pn_pos = body->pn_pos;
2858 } else {
2859 pn->pn_body = body;
2862 pn->pn_blockid = tc->blockid();
2864 if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2865 return NULL;
2867 return result;
2870 static JSParseNode *
2871 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2873 return FunctionDef(cx, ts, tc, 0);
2876 static JSParseNode *
2877 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2879 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
2883 * Parse the statements in a block, creating a TOK_LC node that lists the
2884 * statements' trees. If called from block-parsing code, the caller must
2885 * match { before and } after.
2887 static JSParseNode *
2888 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2890 JSParseNode *pn, *pn2, *saveBlock;
2891 JSTokenType tt;
2893 JS_CHECK_RECURSION(cx, return NULL);
2895 pn = NewParseNode(PN_LIST, tc);
2896 if (!pn)
2897 return NULL;
2898 pn->pn_type = TOK_LC;
2899 pn->makeEmpty();
2900 pn->pn_blockid = tc->blockid();
2901 saveBlock = tc->blockNode;
2902 tc->blockNode = pn;
2904 for (;;) {
2905 ts->flags |= TSF_OPERAND;
2906 tt = js_PeekToken(cx, ts);
2907 ts->flags &= ~TSF_OPERAND;
2908 if (tt <= TOK_EOF || tt == TOK_RC) {
2909 if (tt == TOK_ERROR) {
2910 if (ts->flags & TSF_EOF)
2911 ts->flags |= TSF_UNEXPECTED_EOF;
2912 return NULL;
2914 break;
2916 pn2 = Statement(cx, ts, tc);
2917 if (!pn2) {
2918 if (ts->flags & TSF_EOF)
2919 ts->flags |= TSF_UNEXPECTED_EOF;
2920 return NULL;
2923 if (pn2->pn_type == TOK_FUNCTION) {
2925 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2926 * level function definitions that should be processed before the
2927 * rest of nodes.
2929 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2930 * is relevant only for function definitions not at top-level,
2931 * which we call function statements.
2933 if (tc->atTopLevel())
2934 pn->pn_xflags |= PNX_FUNCDEFS;
2935 else
2936 tc->flags |= TCF_HAS_FUNCTION_STMT;
2938 pn->append(pn2);
2942 * Handle the case where there was a let declaration under this block. If
2943 * it replaced tc->blockNode with a new block node then we must refresh pn
2944 * and then restore tc->blockNode.
2946 if (tc->blockNode != pn)
2947 pn = tc->blockNode;
2948 tc->blockNode = saveBlock;
2950 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2951 return pn;
2954 static JSParseNode *
2955 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2957 JSParseNode *pn;
2959 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
2960 pn = ParenExpr(cx, ts, tc, NULL, NULL);
2961 if (!pn)
2962 return NULL;
2963 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
2965 /* Check for (a = b) and warn about possible (a == b) mistype. */
2966 if (pn->pn_type == TOK_ASSIGN &&
2967 pn->pn_op == JSOP_NOP &&
2968 !pn->pn_parens &&
2969 !js_ReportCompileErrorNumber(cx, ts, NULL,
2970 JSREPORT_WARNING | JSREPORT_STRICT,
2971 JSMSG_EQUAL_AS_ASSIGN,
2972 "")) {
2973 return NULL;
2975 return pn;
2978 static JSBool
2979 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
2981 JSAtom *label;
2982 JSTokenType tt;
2984 tt = js_PeekTokenSameLine(cx, ts);
2985 if (tt == TOK_ERROR)
2986 return JS_FALSE;
2987 if (tt == TOK_NAME) {
2988 (void) js_GetToken(cx, ts);
2989 label = CURRENT_TOKEN(ts).t_atom;
2990 } else {
2991 label = NULL;
2993 pn->pn_atom = label;
2994 return JS_TRUE;
2997 static JSBool
2998 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3000 JSParseNode *pn;
3001 JSObject *blockObj;
3002 JSAtomListElement *ale;
3003 jsint n;
3006 * Top-level 'let' is the same as 'var' currently -- this may change in a
3007 * successor standard to ES3.1 that specifies 'let'.
3009 JS_ASSERT(!tc->atTopLevel());
3011 pn = data->pn;
3012 blockObj = tc->blockChain;
3013 ale = tc->decls.lookup(atom);
3014 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3015 const char *name = js_AtomToPrintableString(cx, atom);
3016 if (name) {
3017 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3018 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3019 (ale && ALE_DEFN(ale)->isConst())
3020 ? js_const_str
3021 : js_variable_str,
3022 name);
3024 return JS_FALSE;
3027 n = OBJ_BLOCK_COUNT(cx, blockObj);
3028 if (n == JS_BIT(16)) {
3029 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3030 JSREPORT_ERROR, data->let.overflow);
3031 return JS_FALSE;
3035 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3036 * This is balanced by PopStatement, defined immediately below.
3038 if (!Define(pn, atom, tc, true))
3039 return JS_FALSE;
3042 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3043 * upvar cookie whose skip tells the current static level. The emitter will
3044 * adjust the node's slot based on its stack depth model -- and, for global
3045 * and eval code, JSCompiler::compileScript will adjust the slot again to
3046 * include script->nfixed.
3048 pn->pn_op = JSOP_GETLOCAL;
3049 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3050 pn->pn_dflags |= PND_LET | PND_BOUND;
3053 * Use JSPROP_ENUMERATE to aid the disassembler. Define the let binding's
3054 * property before storing pn in a reserved slot, since block_reserveSlots
3055 * depends on OBJ_SCOPE(blockObj)->entryCount.
3057 if (!js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), JSVAL_VOID,
3058 NULL, NULL,
3059 JSPROP_ENUMERATE |
3060 JSPROP_PERMANENT |
3061 JSPROP_SHARED,
3062 SPROP_HAS_SHORTID, (int16) n, NULL)) {
3063 return JS_FALSE;
3067 * Store pn temporarily in what would be reserved slots in a cloned block
3068 * object (once the prototype's final population is known, after all 'let'
3069 * bindings for this block have been parsed). We will free these reserved
3070 * slots in jsemit.cpp:EmitEnterBlock.
3072 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3073 if (slot >= STOBJ_NSLOTS(blockObj) &&
3074 !js_GrowSlots(cx, blockObj, slot + 1)) {
3075 return JS_FALSE;
3077 OBJ_SCOPE(blockObj)->freeslot = slot + 1;
3078 STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn));
3079 return JS_TRUE;
3082 static void
3083 PopStatement(JSTreeContext *tc)
3085 JSStmtInfo *stmt = tc->topStmt;
3087 if (stmt->flags & SIF_SCOPE) {
3088 JSObject *obj = stmt->blockObj;
3089 JSScope *scope = OBJ_SCOPE(obj);
3090 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3092 for (JSScopeProperty *sprop = scope->lastProp; sprop; sprop = sprop->parent) {
3093 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3095 /* Beware the empty destructuring dummy. */
3096 if (atom == tc->compiler->context->runtime->atomState.emptyAtom)
3097 continue;
3098 tc->decls.remove(tc->compiler, atom);
3102 * The block scope will not be modified again. It may be shared. Clear
3103 * scope->object to make scope->owned() false.
3105 scope->object = NULL;
3107 js_PopStatement(tc);
3110 static inline bool
3111 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3113 while (stmt->downScope) {
3114 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3115 if (!stmt)
3116 return false;
3117 if (stmt->type == STMT_BLOCK)
3118 return true;
3120 return false;
3123 static JSBool
3124 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3126 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3127 JSParseNode *pn = data->pn;
3129 if (stmt && stmt->type == STMT_WITH) {
3130 pn->pn_op = JSOP_NAME;
3131 data->fresh = false;
3132 return JS_TRUE;
3135 JSAtomListElement *ale = tc->decls.lookup(atom);
3136 JSOp op = data->op;
3138 if (stmt || ale) {
3139 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3140 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3141 const char *name;
3143 if (dn_kind == JSDefinition::ARG) {
3144 name = js_AtomToPrintableString(cx, atom);
3145 if (!name)
3146 return JS_FALSE;
3148 if (op == JSOP_DEFCONST) {
3149 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3150 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3151 name);
3152 return JS_FALSE;
3154 if (!js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3155 JSREPORT_WARNING | JSREPORT_STRICT,
3156 JSMSG_VAR_HIDES_ARG, name)) {
3157 return JS_FALSE;
3159 } else {
3160 bool error = (op == JSOP_DEFCONST ||
3161 dn_kind == JSDefinition::CONST ||
3162 (dn_kind == JSDefinition::LET &&
3163 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3165 if (JS_HAS_STRICT_OPTION(cx)
3166 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3167 : error) {
3168 name = js_AtomToPrintableString(cx, atom);
3169 if (!name ||
3170 !js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3171 !error
3172 ? JSREPORT_WARNING | JSREPORT_STRICT
3173 : JSREPORT_ERROR,
3174 JSMSG_REDECLARED_VAR,
3175 JSDefinition::kindString(dn_kind),
3176 name)) {
3177 return JS_FALSE;
3183 if (!ale) {
3184 if (!Define(pn, atom, tc))
3185 return JS_FALSE;
3186 } else {
3188 * A var declaration never recreates an existing binding, it restates
3189 * it and possibly reinitializes its value. Beware that if pn becomes a
3190 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3191 * const (typically a const would ;-), then pn must be rewritten into a
3192 * TOK_ASSIGN node. See Variables, further below.
3194 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3195 * There the x definition is hoisted but the x = 2 assignment mutates
3196 * the block-local binding of x.
3198 JSDefinition *dn = ALE_DEFN(ale);
3200 data->fresh = false;
3202 if (!pn->pn_used) {
3203 /* Make pnu be a fresh name node that uses dn. */
3204 JSParseNode *pnu = pn;
3206 if (pn->pn_defn) {
3207 pnu = NewNameNode(cx, TS(tc->compiler), atom, tc);
3208 if (!pnu)
3209 return JS_FALSE;
3212 LinkUseToDef(pnu, dn, tc);
3213 pnu->pn_op = JSOP_NAME;
3216 while (dn->kind() == JSDefinition::LET) {
3217 do {
3218 ale = ALE_NEXT(ale);
3219 } while (ale && ALE_ATOM(ale) != atom);
3220 if (!ale)
3221 break;
3222 dn = ALE_DEFN(ale);
3225 if (ale) {
3226 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3227 dn->kind() == JSDefinition::CONST);
3228 return JS_TRUE;
3232 * A var or const that is shadowed by one or more let bindings of the
3233 * same name, but that has not been declared until this point, must be
3234 * hoisted above the let bindings.
3236 if (!pn->pn_defn) {
3237 JSHashEntry **hep;
3239 ale = tc->lexdeps.rawLookup(atom, hep);
3240 if (ale) {
3241 pn = ALE_DEFN(ale);
3242 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
3243 } else {
3244 JSParseNode *pn2 = NewNameNode(cx, TS(tc->compiler), atom, tc);
3245 if (!pn2)
3246 return JS_FALSE;
3248 /* The token stream may be past the location for pn. */
3249 pn2->pn_type = TOK_NAME;
3250 pn2->pn_pos = pn->pn_pos;
3251 pn = pn2;
3253 pn->pn_op = JSOP_NAME;
3256 ale = tc->decls.add(tc->compiler, atom, JSAtomList::HOIST);
3257 if (!ale)
3258 return JS_FALSE;
3259 ALE_SET_DEFN(ale, pn);
3260 pn->pn_defn = true;
3261 pn->pn_dflags &= ~PND_PLACEHOLDER;
3264 if (data->op == JSOP_DEFCONST)
3265 pn->pn_dflags |= PND_CONST;
3267 if (!(tc->flags & TCF_IN_FUNCTION)) {
3269 * If we are generating global or eval-called-from-global code, bind a
3270 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3271 * up global variable access by memoizing name-to-slot mappings in the
3272 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3273 * can't be done due to a pre-existing property of the same name as the
3274 * var or const but incompatible attributes/getter/setter/etc, these
3275 * ops devolve to JSOP_NAME, etc.
3277 * For now, don't try to lookup eval frame variables at compile time.
3278 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3279 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3281 pn->pn_op = JSOP_NAME;
3282 if ((tc->flags & TCF_COMPILING) && !tc->compiler->callerFrame) {
3283 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3285 /* Index atom so we can map fast global number to name. */
3286 ale = cg->atomList.add(tc->compiler, atom);
3287 if (!ale)
3288 return JS_FALSE;
3290 /* Defend against cg->ngvars 16-bit overflow. */
3291 uintN slot = ALE_INDEX(ale);
3292 if ((slot + 1) >> 16)
3293 return JS_TRUE;
3295 if ((uint16)(slot + 1) > cg->ngvars)
3296 cg->ngvars = (uint16)(slot + 1);
3298 pn->pn_op = JSOP_GETGVAR;
3299 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3300 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3302 return JS_TRUE;
3305 if (atom == cx->runtime->atomState.argumentsAtom) {
3306 pn->pn_op = JSOP_ARGUMENTS;
3307 pn->pn_dflags |= PND_BOUND;
3308 return JS_TRUE;
3311 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3312 if (localKind == JSLOCAL_NONE) {
3314 * Property not found in current variable scope: we have not seen this
3315 * variable before. Define a new local variable by adding a property to
3316 * the function's scope and allocating one slot in the function's vars
3317 * frame. Any locals declared in a with statement body are handled at
3318 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3319 * and heavyweight-function-local vars.
3321 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3323 uintN index = tc->fun->u.i.nvars;
3324 if (!BindLocalVariable(cx, tc->fun, atom, localKind))
3325 return JS_FALSE;
3326 pn->pn_op = JSOP_GETLOCAL;
3327 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3328 pn->pn_dflags |= PND_BOUND;
3329 return JS_TRUE;
3332 if (localKind == JSLOCAL_ARG) {
3333 /* We checked errors and strict warnings earlier -- see above. */
3334 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3335 } else {
3336 /* Not an argument, must be a redeclared local var. */
3337 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3339 pn->pn_op = JSOP_NAME;
3340 return JS_TRUE;
3343 static JSBool
3344 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3346 JSParseNode *pn2;
3348 JS_ASSERT(pn->pn_arity == PN_LIST);
3349 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3350 pn2 = pn->pn_head;
3351 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3352 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR, msg);
3353 return JS_FALSE;
3355 pn->pn_op = JSOP_SETCALL;
3356 return JS_TRUE;
3359 static void
3360 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3362 if (pn->pn_used) {
3363 JSDefinition *dn = pn->pn_lexdef;
3366 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3367 * occur as direct kids of the same block with no forward refs to x.
3369 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3370 dn->isBlockChild() &&
3371 pn->isBlockChild() &&
3372 dn->pn_blockid == pn->pn_blockid &&
3373 dn->pn_pos.end <= pn->pn_pos.begin &&
3374 dn->dn_uses == pn) {
3375 dflag = PND_INITIALIZED;
3378 dn->pn_dflags |= dflag;
3380 if (dn->frameLevel() != tc->staticLevel) {
3382 * The above condition takes advantage of the all-ones nature of
3383 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3384 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3386 JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3387 dn->frameLevel() < tc->staticLevel);
3388 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3392 pn->pn_dflags |= dflag;
3394 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3395 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3398 #if JS_HAS_DESTRUCTURING
3400 static JSBool
3401 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3402 JSTreeContext *tc)
3404 JSAtom *atom;
3407 * Destructuring is a form of assignment, so just as for an initialized
3408 * simple variable, we must check for assignment to 'arguments' and flag
3409 * the enclosing function (if any) as heavyweight.
3411 JS_ASSERT(pn->pn_type == TOK_NAME);
3412 atom = pn->pn_atom;
3413 if (atom == cx->runtime->atomState.argumentsAtom)
3414 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3416 data->pn = pn;
3417 if (!data->binder(cx, data, atom, tc))
3418 return JS_FALSE;
3421 * Select the appropriate name-setting opcode, respecting eager selection
3422 * done by the data->binder function.
3424 if (pn->pn_dflags & PND_BOUND) {
3425 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3426 ? JSOP_SETNAME
3427 : (pn->pn_dflags & PND_GVAR)
3428 ? JSOP_SETGVAR
3429 : JSOP_SETLOCAL;
3430 } else {
3431 pn->pn_op = (data->op == JSOP_DEFCONST)
3432 ? JSOP_SETCONST
3433 : JSOP_SETNAME;
3436 if (data->op == JSOP_DEFCONST)
3437 pn->pn_dflags |= PND_CONST;
3439 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3440 return JS_TRUE;
3444 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3445 * LHS expression except a destructuring initialiser, and R is on the stack.
3446 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3447 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3448 * then push its property name QN. At this point the stack looks like
3450 * [... R, R[P], QB, QN]
3452 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3453 * its operands with left-hand side above right-hand side:
3455 * [rval, lval, xval]
3457 * and pops all three values, setting lval[xval] = rval. But we cannot select
3458 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3459 * which can be optimized further. So we select JSOP_SETNAME.
3461 static JSBool
3462 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3464 switch (pn->pn_type) {
3465 case TOK_NAME:
3466 NoteLValue(cx, pn, tc);
3467 /* FALL THROUGH */
3469 case TOK_DOT:
3470 case TOK_LB:
3471 pn->pn_op = JSOP_SETNAME;
3472 break;
3474 #if JS_HAS_LVALUE_RETURN
3475 case TOK_LP:
3476 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3477 return JS_FALSE;
3478 break;
3479 #endif
3481 #if JS_HAS_XML_SUPPORT
3482 case TOK_UNARYOP:
3483 if (pn->pn_op == JSOP_XMLNAME) {
3484 pn->pn_op = JSOP_BINDXMLNAME;
3485 break;
3487 /* FALL THROUGH */
3488 #endif
3490 default:
3491 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3492 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3493 return JS_FALSE;
3496 return JS_TRUE;
3499 typedef struct FindPropValData {
3500 uint32 numvars; /* # of destructuring vars in left side */
3501 uint32 maxstep; /* max # of steps searching right side */
3502 JSDHashTable table; /* hash table for O(1) right side search */
3503 } FindPropValData;
3505 typedef struct FindPropValEntry {
3506 JSDHashEntryHdr hdr;
3507 JSParseNode *pnkey;
3508 JSParseNode *pnval;
3509 } FindPropValEntry;
3511 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3512 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3513 ((pnkey)->pn_type == TOK_NUMBER || \
3514 (pnkey)->pn_type == TOK_STRING || \
3515 (pnkey)->pn_type == TOK_NAME)) || \
3516 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3518 static JSDHashNumber
3519 HashFindPropValKey(JSDHashTable *table, const void *key)
3521 const JSParseNode *pnkey = (const JSParseNode *)key;
3523 ASSERT_VALID_PROPERTY_KEY(pnkey);
3524 return (pnkey->pn_type == TOK_NUMBER)
3525 ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
3526 JSDOUBLE_LO32(pnkey->pn_dval))
3527 : ATOM_HASH(pnkey->pn_atom);
3530 static JSBool
3531 MatchFindPropValEntry(JSDHashTable *table,
3532 const JSDHashEntryHdr *entry,
3533 const void *key)
3535 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3536 const JSParseNode *pnkey = (const JSParseNode *)key;
3538 ASSERT_VALID_PROPERTY_KEY(pnkey);
3539 return pnkey->pn_type == fpve->pnkey->pn_type &&
3540 ((pnkey->pn_type == TOK_NUMBER)
3541 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3542 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3545 static const JSDHashTableOps FindPropValOps = {
3546 JS_DHashAllocTable,
3547 JS_DHashFreeTable,
3548 HashFindPropValKey,
3549 MatchFindPropValEntry,
3550 JS_DHashMoveEntryStub,
3551 JS_DHashClearEntryStub,
3552 JS_DHashFinalizeStub,
3553 NULL
3556 #define STEP_HASH_THRESHOLD 10
3557 #define BIG_DESTRUCTURING 5
3558 #define BIG_OBJECT_INIT 20
3560 static JSParseNode *
3561 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3563 FindPropValEntry *entry;
3564 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3565 uint32 step;
3567 /* If we have a hash table, use it as the sole source of truth. */
3568 if (data->table.ops) {
3569 entry = (FindPropValEntry *)
3570 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3571 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3574 /* If pn is not an object initialiser node, we can't do anything here. */
3575 if (pn->pn_type != TOK_RC)
3576 return NULL;
3579 * We must search all the way through pn's list, to handle the case of an
3580 * id duplicated for two or more property initialisers.
3582 pnhit = NULL;
3583 step = 0;
3584 ASSERT_VALID_PROPERTY_KEY(pnid);
3585 pnhead = pn->pn_head;
3586 if (pnid->pn_type == TOK_NUMBER) {
3587 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3588 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3589 if (pnprop->pn_op == JSOP_NOP) {
3590 pnkey = pnprop->pn_left;
3591 ASSERT_VALID_PROPERTY_KEY(pnkey);
3592 if (pnkey->pn_type == TOK_NUMBER &&
3593 pnkey->pn_dval == pnid->pn_dval) {
3594 pnhit = pnprop;
3596 ++step;
3599 } else {
3600 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3601 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3602 if (pnprop->pn_op == JSOP_NOP) {
3603 pnkey = pnprop->pn_left;
3604 ASSERT_VALID_PROPERTY_KEY(pnkey);
3605 if (pnkey->pn_type == pnid->pn_type &&
3606 pnkey->pn_atom == pnid->pn_atom) {
3607 pnhit = pnprop;
3609 ++step;
3613 if (!pnhit)
3614 return NULL;
3616 /* Hit via full search -- see whether it's time to create the hash table. */
3617 JS_ASSERT(!data->table.ops);
3618 if (step > data->maxstep) {
3619 data->maxstep = step;
3620 if (step >= STEP_HASH_THRESHOLD &&
3621 data->numvars >= BIG_DESTRUCTURING &&
3622 pn->pn_count >= BIG_OBJECT_INIT &&
3623 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3624 sizeof(FindPropValEntry),
3625 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3627 for (pn = pnhead; pn; pn = pn->pn_next) {
3628 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3629 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3630 entry = (FindPropValEntry *)
3631 JS_DHashTableOperate(&data->table, pn->pn_left,
3632 JS_DHASH_ADD);
3633 entry->pnval = pn->pn_right;
3637 return pnhit->pn_right;
3641 * If data is null, the caller is AssignExpr and instead of binding variables,
3642 * we specialize lvalues in the propery value positions of the left-hand side.
3643 * If right is null, just check for well-formed lvalues.
3645 * See also UndominateInitializers, immediately below. If you change either of
3646 * these functions, you might have to change the other to match.
3648 static JSBool
3649 CheckDestructuring(JSContext *cx, BindData *data,
3650 JSParseNode *left, JSParseNode *right,
3651 JSTreeContext *tc)
3653 JSBool ok;
3654 FindPropValData fpvd;
3655 JSParseNode *lhs, *rhs, *pn, *pn2;
3657 if (left->pn_type == TOK_ARRAYCOMP) {
3658 js_ReportCompileErrorNumber(cx, TS(tc->compiler), left,
3659 JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
3660 return JS_FALSE;
3663 #if JS_HAS_DESTRUCTURING_SHORTHAND
3664 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3665 js_ReportCompileErrorNumber(cx, TS(tc->compiler), right,
3666 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3667 return JS_FALSE;
3669 #endif
3671 fpvd.table.ops = NULL;
3672 lhs = left->pn_head;
3673 if (left->pn_type == TOK_RB) {
3674 rhs = (right && right->pn_type == left->pn_type)
3675 ? right->pn_head
3676 : NULL;
3678 while (lhs) {
3679 pn = lhs, pn2 = rhs;
3681 /* Nullary comma is an elision; binary comma is an expression.*/
3682 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3683 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3684 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3685 } else {
3686 if (data) {
3687 if (pn->pn_type != TOK_NAME)
3688 goto no_var_name;
3690 ok = BindDestructuringVar(cx, data, pn, tc);
3691 } else {
3692 ok = BindDestructuringLHS(cx, pn, tc);
3695 if (!ok)
3696 goto out;
3699 lhs = lhs->pn_next;
3700 if (rhs)
3701 rhs = rhs->pn_next;
3703 } else {
3704 JS_ASSERT(left->pn_type == TOK_RC);
3705 fpvd.numvars = left->pn_count;
3706 fpvd.maxstep = 0;
3707 rhs = NULL;
3709 while (lhs) {
3710 JS_ASSERT(lhs->pn_type == TOK_COLON);
3711 pn = lhs->pn_right;
3713 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3714 if (right)
3715 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3716 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3717 } else if (data) {
3718 if (pn->pn_type != TOK_NAME)
3719 goto no_var_name;
3721 ok = BindDestructuringVar(cx, data, pn, tc);
3722 } else {
3723 ok = BindDestructuringLHS(cx, pn, tc);
3725 if (!ok)
3726 goto out;
3728 lhs = lhs->pn_next;
3733 * The catch/finally handler implementation in the interpreter assumes
3734 * that any operation that introduces a new scope (like a "let" or "with"
3735 * block) increases the stack depth. This way, it is possible to restore
3736 * the scope chain based on stack depth of the handler alone. "let" with
3737 * an empty destructuring pattern like in
3739 * let [] = 1;
3741 * would violate this assumption as the there would be no let locals to
3742 * store on the stack. To satisfy it we add an empty property to such
3743 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3744 * slots, would be always positive.
3746 * Note that we add such a property even if the block has locals due to
3747 * later let declarations in it. We optimize for code simplicity here,
3748 * not the fastest runtime performance with empty [] or {}.
3750 if (data &&
3751 data->binder == BindLet &&
3752 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3753 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3754 ATOM_TO_JSID(cx->runtime->
3755 atomState.emptyAtom),
3756 JSVAL_VOID, NULL, NULL,
3757 JSPROP_ENUMERATE |
3758 JSPROP_PERMANENT |
3759 JSPROP_SHARED,
3760 SPROP_HAS_SHORTID, 0, NULL);
3761 if (!ok)
3762 goto out;
3765 ok = JS_TRUE;
3767 out:
3768 if (fpvd.table.ops)
3769 JS_DHashTableFinish(&fpvd.table);
3770 return ok;
3772 no_var_name:
3773 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
3774 JSMSG_NO_VARIABLE_NAME);
3775 ok = JS_FALSE;
3776 goto out;
3780 * This is a greatly pared down version of CheckDestructuring that extends the
3781 * pn_pos.end source coordinate of each name in a destructuring binding such as
3783 * var [x, y] = [function () y, 42];
3785 * to cover its corresponding initializer, so that the initialized binding does
3786 * not appear to dominate any closures in its initializer. See bug 496134.
3788 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3789 * not very precise. With one-pass SSA construction from structured source code
3790 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3791 * Languages", Brandis and Mössenböck), we could do much better.
3793 * See CheckDestructuring, immediately above. If you change either of these
3794 * functions, you might have to change the other to match.
3796 static JSBool
3797 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3799 FindPropValData fpvd;
3800 JSParseNode *lhs, *rhs;
3802 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3803 JS_ASSERT(right);
3805 #if JS_HAS_DESTRUCTURING_SHORTHAND
3806 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3807 js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
3808 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3809 return JS_FALSE;
3811 #endif
3813 if (right->pn_type != left->pn_type)
3814 return JS_TRUE;
3816 fpvd.table.ops = NULL;
3817 lhs = left->pn_head;
3818 if (left->pn_type == TOK_RB) {
3819 rhs = right->pn_head;
3821 while (lhs && rhs) {
3822 /* Nullary comma is an elision; binary comma is an expression.*/
3823 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3824 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3825 if (!UndominateInitializers(lhs, rhs, tc))
3826 return JS_FALSE;
3827 } else {
3828 lhs->pn_pos.end = rhs->pn_pos.end;
3832 lhs = lhs->pn_next;
3833 rhs = rhs->pn_next;
3835 } else {
3836 JS_ASSERT(left->pn_type == TOK_RC);
3837 fpvd.numvars = left->pn_count;
3838 fpvd.maxstep = 0;
3840 while (lhs) {
3841 JS_ASSERT(lhs->pn_type == TOK_COLON);
3842 JSParseNode *pn = lhs->pn_right;
3844 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3845 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3846 if (rhs && !UndominateInitializers(pn, rhs, tc))
3847 return JS_FALSE;
3848 } else {
3849 if (rhs)
3850 pn->pn_pos.end = rhs->pn_pos.end;
3853 lhs = lhs->pn_next;
3856 return JS_TRUE;
3859 static JSParseNode *
3860 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
3861 JSTokenType tt)
3863 JSTokenStream *ts;
3864 JSParseNode *pn;
3866 ts = TS(tc->compiler);
3867 ts->flags |= TSF_DESTRUCTURING;
3868 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3869 ts->flags &= ~TSF_DESTRUCTURING;
3870 if (!pn)
3871 return NULL;
3872 if (!CheckDestructuring(cx, data, pn, NULL, tc))
3873 return NULL;
3874 return pn;
3878 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3879 * This function assumes the cloned tree is for use in the same statement and
3880 * binding context as the original tree.
3882 static JSParseNode *
3883 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
3885 JSParseNode *pn, *pn2, *opn2;
3887 pn = NewOrRecycledNode(tc);
3888 if (!pn)
3889 return NULL;
3890 pn->pn_type = opn->pn_type;
3891 pn->pn_pos = opn->pn_pos;
3892 pn->pn_op = opn->pn_op;
3893 pn->pn_used = opn->pn_used;
3894 pn->pn_defn = opn->pn_defn;
3895 pn->pn_arity = opn->pn_arity;
3896 pn->pn_parens = opn->pn_parens;
3898 switch (pn->pn_arity) {
3899 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3901 case PN_FUNC:
3902 NULLCHECK(pn->pn_funbox =
3903 tc->compiler->newFunctionBox(opn->pn_funbox->object, pn, tc));
3904 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
3905 pn->pn_cookie = opn->pn_cookie;
3906 pn->pn_dflags = opn->pn_dflags;
3907 pn->pn_blockid = opn->pn_blockid;
3908 break;
3910 case PN_LIST:
3911 pn->makeEmpty();
3912 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
3913 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
3914 pn->append(pn2);
3916 pn->pn_xflags = opn->pn_xflags;
3917 break;
3919 case PN_TERNARY:
3920 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
3921 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
3922 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
3923 break;
3925 case PN_BINARY:
3926 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
3927 if (opn->pn_right != opn->pn_left)
3928 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
3929 else
3930 pn->pn_right = pn->pn_left;
3931 pn->pn_val = opn->pn_val;
3932 pn->pn_iflags = opn->pn_iflags;
3933 break;
3935 case PN_UNARY:
3936 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
3937 pn->pn_num = opn->pn_num;
3938 pn->pn_hidden = opn->pn_hidden;
3939 break;
3941 case PN_NAME:
3942 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3943 pn->pn_u = opn->pn_u;
3944 if (opn->pn_used) {
3946 * The old name is a use of its pn_lexdef. Make the clone also be a
3947 * use of that definition.
3949 JSDefinition *dn = pn->pn_lexdef;
3951 pn->pn_link = dn->dn_uses;
3952 dn->dn_uses = pn;
3953 } else if (opn->pn_expr) {
3954 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
3957 * If the old name is a definition, the new one has pn_defn set.
3958 * Make the old name a use of the new node.
3960 if (opn->pn_defn) {
3961 opn->pn_defn = false;
3962 LinkUseToDef(opn, (JSDefinition *) pn, tc);
3965 break;
3967 case PN_NAMESET:
3968 pn->pn_names = opn->pn_names;
3969 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
3970 break;
3972 case PN_NULLARY:
3973 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3974 pn->pn_u = opn->pn_u;
3975 break;
3977 #undef NULLCHECK
3979 return pn;
3982 #endif /* JS_HAS_DESTRUCTURING */
3984 extern const char js_with_statement_str[];
3986 static JSParseNode *
3987 ContainsStmt(JSParseNode *pn, JSTokenType tt)
3989 JSParseNode *pn2, *pnt;
3991 if (!pn)
3992 return NULL;
3993 if (PN_TYPE(pn) == tt)
3994 return pn;
3995 switch (pn->pn_arity) {
3996 case PN_LIST:
3997 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3998 pnt = ContainsStmt(pn2, tt);
3999 if (pnt)
4000 return pnt;
4002 break;
4003 case PN_TERNARY:
4004 pnt = ContainsStmt(pn->pn_kid1, tt);
4005 if (pnt)
4006 return pnt;
4007 pnt = ContainsStmt(pn->pn_kid2, tt);
4008 if (pnt)
4009 return pnt;
4010 return ContainsStmt(pn->pn_kid3, tt);
4011 case PN_BINARY:
4013 * Limit recursion if pn is a binary expression, which can't contain a
4014 * var statement.
4016 if (pn->pn_op != JSOP_NOP)
4017 return NULL;
4018 pnt = ContainsStmt(pn->pn_left, tt);
4019 if (pnt)
4020 return pnt;
4021 return ContainsStmt(pn->pn_right, tt);
4022 case PN_UNARY:
4023 if (pn->pn_op != JSOP_NOP)
4024 return NULL;
4025 return ContainsStmt(pn->pn_kid, tt);
4026 case PN_NAME:
4027 return ContainsStmt(pn->maybeExpr(), tt);
4028 case PN_NAMESET:
4029 return ContainsStmt(pn->pn_tree, tt);
4030 default:;
4032 return NULL;
4035 static JSParseNode *
4036 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4037 JSParser operandParser)
4039 JSTokenType tt, tt2;
4040 JSParseNode *pn, *pn2;
4042 tt = CURRENT_TOKEN(ts).type;
4043 if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
4044 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4045 JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4046 return NULL;
4049 pn = NewParseNode(PN_UNARY, tc);
4050 if (!pn)
4051 return NULL;
4053 #if JS_HAS_GENERATORS
4054 if (tt == TOK_YIELD)
4055 tc->flags |= TCF_FUN_IS_GENERATOR;
4056 #endif
4058 /* This is ugly, but we don't want to require a semicolon. */
4059 ts->flags |= TSF_OPERAND;
4060 tt2 = js_PeekTokenSameLine(cx, ts);
4061 ts->flags &= ~TSF_OPERAND;
4062 if (tt2 == TOK_ERROR)
4063 return NULL;
4065 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4066 #if JS_HAS_GENERATORS
4067 && (tt != TOK_YIELD ||
4068 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4069 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4070 #endif
4072 pn2 = operandParser(cx, ts, tc);
4073 if (!pn2)
4074 return NULL;
4075 #if JS_HAS_GENERATORS
4076 if (tt == TOK_RETURN)
4077 #endif
4078 tc->flags |= TCF_RETURN_EXPR;
4079 pn->pn_pos.end = pn2->pn_pos.end;
4080 pn->pn_kid = pn2;
4081 } else {
4082 #if JS_HAS_GENERATORS
4083 if (tt == TOK_RETURN)
4084 #endif
4085 tc->flags |= TCF_RETURN_VOID;
4088 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4089 /* As in Python (see PEP-255), disallow return v; in generators. */
4090 ReportBadReturn(cx, tc, JSREPORT_ERROR,
4091 JSMSG_BAD_GENERATOR_RETURN,
4092 JSMSG_BAD_ANON_GENERATOR_RETURN);
4093 return NULL;
4096 if (JS_HAS_STRICT_OPTION(cx) &&
4097 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4098 !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4099 JSMSG_NO_RETURN_VALUE,
4100 JSMSG_ANON_NO_RETURN_VALUE)) {
4101 return NULL;
4104 return pn;
4107 static JSParseNode *
4108 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4109 JSStmtInfo *stmt)
4111 JSParseNode *pn;
4112 JSObject *obj;
4113 JSObjectBox *blockbox;
4115 pn = NewParseNode(PN_NAME, tc);
4116 if (!pn)
4117 return NULL;
4119 obj = js_NewBlockObject(cx);
4120 if (!obj)
4121 return NULL;
4123 blockbox = tc->compiler->newObjectBox(obj);
4124 if (!blockbox)
4125 return NULL;
4127 js_PushBlockScope(tc, stmt, obj, -1);
4128 pn->pn_type = TOK_LEXICALSCOPE;
4129 pn->pn_op = JSOP_LEAVEBLOCK;
4130 pn->pn_objbox = blockbox;
4131 pn->pn_cookie = FREE_UPVAR_COOKIE;
4132 pn->pn_dflags = 0;
4133 if (!GenerateBlockId(tc, stmt->blockid))
4134 return NULL;
4135 pn->pn_blockid = stmt->blockid;
4136 return pn;
4139 #if JS_HAS_BLOCK_SCOPE
4141 static JSParseNode *
4142 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
4144 JSParseNode *pn, *pnblock, *pnlet;
4145 JSStmtInfo stmtInfo;
4147 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
4149 /* Create the let binary node. */
4150 pnlet = NewParseNode(PN_BINARY, tc);
4151 if (!pnlet)
4152 return NULL;
4154 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4156 /* This is a let block or expression of the form: let (a, b, c) .... */
4157 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
4158 if (!pnblock)
4159 return NULL;
4160 pn = pnblock;
4161 pn->pn_expr = pnlet;
4163 pnlet->pn_left = Variables(cx, ts, tc, true);
4164 if (!pnlet->pn_left)
4165 return NULL;
4166 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4168 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4170 ts->flags |= TSF_OPERAND;
4171 if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
4173 * If this is really an expression in let statement guise, then we
4174 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4175 * the return value of the expression.
4177 pn = NewParseNode(PN_UNARY, tc);
4178 if (!pn)
4179 return NULL;
4180 pn->pn_type = TOK_SEMI;
4181 pn->pn_num = -1;
4182 pn->pn_kid = pnblock;
4184 statement = JS_FALSE;
4186 ts->flags &= ~TSF_OPERAND;
4188 if (statement) {
4189 pnlet->pn_right = Statements(cx, ts, tc);
4190 if (!pnlet->pn_right)
4191 return NULL;
4192 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4193 } else {
4195 * Change pnblock's opcode to the variant that propagates the last
4196 * result down after popping the block, and clear statement.
4198 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4199 pnlet->pn_right = AssignExpr(cx, ts, tc);
4200 if (!pnlet->pn_right)
4201 return NULL;
4204 PopStatement(tc);
4205 return pn;
4208 #endif /* JS_HAS_BLOCK_SCOPE */
4210 static bool
4211 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4213 js_PushStatement(tc, stmt, type, -1);
4214 return GenerateBlockId(tc, stmt->blockid);
4217 static JSParseNode *
4218 NewBindingNode(JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc, bool let = false)
4220 JSParseNode *pn = NULL;
4222 JSAtomListElement *ale = tc->decls.lookup(atom);
4223 if (ale) {
4224 pn = ALE_DEFN(ale);
4225 JS_ASSERT(!pn->isPlaceholder());
4226 } else {
4227 ale = tc->lexdeps.lookup(atom);
4228 if (ale) {
4229 pn = ALE_DEFN(ale);
4230 JS_ASSERT(pn->isPlaceholder());
4234 if (pn) {
4235 JS_ASSERT(pn->pn_defn);
4238 * A let binding at top level becomes a var before we get here, so if
4239 * pn and tc have the same blockid then that id must not be the bodyid.
4240 * If pn is a forward placeholder definition from the same or a higher
4241 * block then we claim it.
4243 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4244 pn->pn_blockid != tc->bodyid);
4246 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4247 if (let)
4248 pn->pn_blockid = tc->blockid();
4250 tc->lexdeps.remove(tc->compiler, atom);
4251 return pn;
4255 /* Make a new node for this declarator name (or destructuring pattern). */
4256 pn = NewNameNode(tc->compiler->context, ts, atom, tc);
4257 if (!pn)
4258 return NULL;
4259 return pn;
4262 #if JS_HAS_BLOCK_SCOPE
4263 static bool
4264 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4266 if (!pn)
4267 return true;
4269 switch (pn->pn_arity) {
4270 case PN_LIST:
4271 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4272 RebindLets(pn2, tc);
4273 break;
4275 case PN_TERNARY:
4276 RebindLets(pn->pn_kid1, tc);
4277 RebindLets(pn->pn_kid2, tc);
4278 RebindLets(pn->pn_kid3, tc);
4279 break;
4281 case PN_BINARY:
4282 RebindLets(pn->pn_left, tc);
4283 RebindLets(pn->pn_right, tc);
4284 break;
4286 case PN_UNARY:
4287 RebindLets(pn->pn_kid, tc);
4288 break;
4290 case PN_FUNC:
4291 RebindLets(pn->pn_body, tc);
4292 break;
4294 case PN_NAME:
4295 RebindLets(pn->maybeExpr(), tc);
4297 if (pn->pn_defn) {
4298 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4299 } else if (pn->pn_used) {
4300 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4301 ForgetUse(pn);
4303 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4304 if (ale) {
4305 while ((ale = ALE_NEXT(ale)) != NULL) {
4306 if (ALE_ATOM(ale) == pn->pn_atom) {
4307 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4308 return true;
4313 ale = tc->lexdeps.lookup(pn->pn_atom);
4314 if (!ale) {
4315 ale = MakePlaceholder(pn, tc);
4316 if (!ale)
4317 return NULL;
4319 JSDefinition *dn = ALE_DEFN(ale);
4320 dn->pn_type = TOK_NAME;
4321 dn->pn_op = JSOP_NOP;
4322 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
4324 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4327 break;
4329 case PN_NAMESET:
4330 RebindLets(pn->pn_tree, tc);
4331 break;
4334 return true;
4336 #endif /* JS_HAS_BLOCK_SCOPE */
4338 static JSParseNode *
4339 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4341 JSTokenType tt;
4342 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4343 JSStmtInfo stmtInfo, *stmt, *stmt2;
4344 JSAtom *label;
4346 JS_CHECK_RECURSION(cx, return NULL);
4348 ts->flags |= TSF_OPERAND;
4349 tt = js_GetToken(cx, ts);
4350 ts->flags &= ~TSF_OPERAND;
4352 #if JS_HAS_GETTER_SETTER
4353 if (tt == TOK_NAME) {
4354 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
4355 if (tt == TOK_ERROR)
4356 return NULL;
4358 #endif
4360 switch (tt) {
4361 case TOK_FUNCTION:
4362 #if JS_HAS_XML_SUPPORT
4363 ts->flags |= TSF_KEYWORD_IS_NAME;
4364 tt = js_PeekToken(cx, ts);
4365 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4366 if (tt == TOK_DBLCOLON)
4367 goto expression;
4368 #endif
4369 return FunctionStmt(cx, ts, tc);
4371 case TOK_IF:
4372 /* An IF node has three kids: condition, then, and optional else. */
4373 pn = NewParseNode(PN_TERNARY, tc);
4374 if (!pn)
4375 return NULL;
4376 pn1 = Condition(cx, ts, tc);
4377 if (!pn1)
4378 return NULL;
4379 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4380 pn2 = Statement(cx, ts, tc);
4381 if (!pn2)
4382 return NULL;
4383 ts->flags |= TSF_OPERAND;
4384 if (js_MatchToken(cx, ts, TOK_ELSE)) {
4385 ts->flags &= ~TSF_OPERAND;
4386 stmtInfo.type = STMT_ELSE;
4387 pn3 = Statement(cx, ts, tc);
4388 if (!pn3)
4389 return NULL;
4390 pn->pn_pos.end = pn3->pn_pos.end;
4391 } else {
4392 ts->flags &= ~TSF_OPERAND;
4393 pn3 = NULL;
4394 pn->pn_pos.end = pn2->pn_pos.end;
4396 PopStatement(tc);
4397 pn->pn_kid1 = pn1;
4398 pn->pn_kid2 = pn2;
4399 pn->pn_kid3 = pn3;
4400 return pn;
4402 case TOK_SWITCH:
4404 JSParseNode *pn5, *saveBlock;
4405 JSBool seenDefault = JS_FALSE;
4407 pn = NewParseNode(PN_BINARY, tc);
4408 if (!pn)
4409 return NULL;
4410 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4412 /* pn1 points to the switch's discriminant. */
4413 pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
4414 if (!pn1)
4415 return NULL;
4417 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4418 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4421 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4422 * because that function states tc->topStmt->blockid.
4424 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4426 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4427 pn2 = NewParseNode(PN_LIST, tc);
4428 if (!pn2)
4429 return NULL;
4430 pn2->makeEmpty();
4431 if (!GenerateBlockIdForStmtNode(pn2, tc))
4432 return NULL;
4433 saveBlock = tc->blockNode;
4434 tc->blockNode = pn2;
4436 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
4437 switch (tt) {
4438 case TOK_DEFAULT:
4439 if (seenDefault) {
4440 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4441 JSMSG_TOO_MANY_DEFAULTS);
4442 return NULL;
4444 seenDefault = JS_TRUE;
4445 /* FALL THROUGH */
4447 case TOK_CASE:
4448 pn3 = NewParseNode(PN_BINARY, tc);
4449 if (!pn3)
4450 return NULL;
4451 if (tt == TOK_CASE) {
4452 pn3->pn_left = Expr(cx, ts, tc);
4453 if (!pn3->pn_left)
4454 return NULL;
4456 pn2->append(pn3);
4457 if (pn2->pn_count == JS_BIT(16)) {
4458 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4459 JSMSG_TOO_MANY_CASES);
4460 return NULL;
4462 break;
4464 case TOK_ERROR:
4465 return NULL;
4467 default:
4468 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4469 JSMSG_BAD_SWITCH);
4470 return NULL;
4472 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4474 pn4 = NewParseNode(PN_LIST, tc);
4475 if (!pn4)
4476 return NULL;
4477 pn4->pn_type = TOK_LC;
4478 pn4->makeEmpty();
4479 ts->flags |= TSF_OPERAND;
4480 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
4481 tt != TOK_CASE && tt != TOK_DEFAULT) {
4482 ts->flags &= ~TSF_OPERAND;
4483 if (tt == TOK_ERROR)
4484 return NULL;
4485 pn5 = Statement(cx, ts, tc);
4486 if (!pn5)
4487 return NULL;
4488 pn4->pn_pos.end = pn5->pn_pos.end;
4489 pn4->append(pn5);
4490 ts->flags |= TSF_OPERAND;
4492 ts->flags &= ~TSF_OPERAND;
4494 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4495 if (pn4->pn_head)
4496 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4497 pn3->pn_pos.end = pn4->pn_pos.end;
4498 pn3->pn_right = pn4;
4502 * Handle the case where there was a let declaration in any case in
4503 * the switch body, but not within an inner block. If it replaced
4504 * tc->blockNode with a new block node then we must refresh pn2 and
4505 * then restore tc->blockNode.
4507 if (tc->blockNode != pn2)
4508 pn2 = tc->blockNode;
4509 tc->blockNode = saveBlock;
4510 PopStatement(tc);
4512 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4513 pn->pn_left = pn1;
4514 pn->pn_right = pn2;
4515 return pn;
4518 case TOK_WHILE:
4519 pn = NewParseNode(PN_BINARY, tc);
4520 if (!pn)
4521 return NULL;
4522 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4523 pn2 = Condition(cx, ts, tc);
4524 if (!pn2)
4525 return NULL;
4526 pn->pn_left = pn2;
4527 pn2 = Statement(cx, ts, tc);
4528 if (!pn2)
4529 return NULL;
4530 PopStatement(tc);
4531 pn->pn_pos.end = pn2->pn_pos.end;
4532 pn->pn_right = pn2;
4533 return pn;
4535 case TOK_DO:
4536 pn = NewParseNode(PN_BINARY, tc);
4537 if (!pn)
4538 return NULL;
4539 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4540 pn2 = Statement(cx, ts, tc);
4541 if (!pn2)
4542 return NULL;
4543 pn->pn_left = pn2;
4544 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4545 pn2 = Condition(cx, ts, tc);
4546 if (!pn2)
4547 return NULL;
4548 PopStatement(tc);
4549 pn->pn_pos.end = pn2->pn_pos.end;
4550 pn->pn_right = pn2;
4551 if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
4553 * All legacy and extended versions must do automatic semicolon
4554 * insertion after do-while. See the testcase and discussion in
4555 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4557 (void) js_MatchToken(cx, ts, TOK_SEMI);
4558 return pn;
4560 break;
4562 case TOK_FOR:
4564 JSParseNode *pnseq = NULL;
4565 #if JS_HAS_BLOCK_SCOPE
4566 JSParseNode *pnlet = NULL;
4567 JSStmtInfo blockInfo;
4568 #endif
4570 /* A FOR node is binary, left is loop control and right is the body. */
4571 pn = NewParseNode(PN_BINARY, tc);
4572 if (!pn)
4573 return NULL;
4574 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4576 pn->pn_op = JSOP_ITER;
4577 pn->pn_iflags = 0;
4578 if (js_MatchToken(cx, ts, TOK_NAME)) {
4579 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
4580 pn->pn_iflags = JSITER_FOREACH;
4581 else
4582 js_UngetToken(ts);
4585 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4586 ts->flags |= TSF_OPERAND;
4587 tt = js_PeekToken(cx, ts);
4588 ts->flags &= ~TSF_OPERAND;
4590 #if JS_HAS_BLOCK_SCOPE
4591 bool let = false;
4592 #endif
4594 if (tt == TOK_SEMI) {
4595 if (pn->pn_iflags & JSITER_FOREACH)
4596 goto bad_for_each;
4598 /* No initializer -- set first kid of left sub-node to null. */
4599 pn1 = NULL;
4600 } else {
4602 * Set pn1 to a var list or an initializing expression.
4604 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4605 * of the for statement. This flag will be used by the RelExpr
4606 * production; if it is set, then the 'in' keyword will not be
4607 * recognized as an operator, leaving it available to be parsed as
4608 * part of a for/in loop.
4610 * A side effect of this restriction is that (unparenthesized)
4611 * expressions involving an 'in' operator are illegal in the init
4612 * clause of an ordinary for loop.
4614 tc->flags |= TCF_IN_FOR_INIT;
4615 if (tt == TOK_VAR) {
4616 (void) js_GetToken(cx, ts);
4617 pn1 = Variables(cx, ts, tc, false);
4618 #if JS_HAS_BLOCK_SCOPE
4619 } else if (tt == TOK_LET) {
4620 let = true;
4621 (void) js_GetToken(cx, ts);
4622 if (js_PeekToken(cx, ts) == TOK_LP) {
4623 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
4624 tt = TOK_LEXICALSCOPE;
4625 } else {
4626 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
4627 if (!pnlet)
4628 return NULL;
4629 blockInfo.flags |= SIF_FOR_BLOCK;
4630 pn1 = Variables(cx, ts, tc, false);
4632 #endif
4633 } else {
4634 pn1 = Expr(cx, ts, tc);
4636 tc->flags &= ~TCF_IN_FOR_INIT;
4637 if (!pn1)
4638 return NULL;
4642 * We can be sure that it's a for/in loop if there's still an 'in'
4643 * keyword here, even if JavaScript recognizes 'in' as an operator,
4644 * as we've excluded 'in' from being parsed in RelExpr by setting
4645 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4647 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
4648 pn->pn_iflags |= JSITER_ENUMERATE;
4649 stmtInfo.type = STMT_FOR_IN_LOOP;
4651 /* Check that the left side of the 'in' is valid. */
4652 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || PN_TYPE(pn1) == tt);
4653 if (TOKEN_TYPE_IS_DECL(tt)
4654 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4655 #if JS_HAS_DESTRUCTURING
4656 || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4657 pn->pn_op == JSOP_ITER &&
4658 !(pn->pn_iflags & JSITER_FOREACH) &&
4659 (pn1->pn_head->pn_type == TOK_RC ||
4660 (pn1->pn_head->pn_type == TOK_RB &&
4661 pn1->pn_head->pn_count != 2) ||
4662 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4663 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4664 pn1->pn_head->pn_left->pn_count != 2))))
4665 #endif
4667 : (pn1->pn_type != TOK_NAME &&
4668 pn1->pn_type != TOK_DOT &&
4669 #if JS_HAS_DESTRUCTURING
4670 ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4671 pn->pn_op == JSOP_ITER &&
4672 !(pn->pn_iflags & JSITER_FOREACH))
4673 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4674 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4675 #endif
4676 #if JS_HAS_LVALUE_RETURN
4677 pn1->pn_type != TOK_LP &&
4678 #endif
4679 #if JS_HAS_XML_SUPPORT
4680 (pn1->pn_type != TOK_UNARYOP ||
4681 pn1->pn_op != JSOP_XMLNAME) &&
4682 #endif
4683 pn1->pn_type != TOK_LB)) {
4684 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
4685 JSMSG_BAD_FOR_LEFTSIDE);
4686 return NULL;
4689 /* pn2 points to the name or destructuring pattern on in's left. */
4690 pn2 = NULL;
4691 uintN dflag = PND_ASSIGNED;
4693 if (TOKEN_TYPE_IS_DECL(tt)) {
4694 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4695 pn1->pn_xflags |= PNX_FORINVAR;
4698 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4699 * 'var', or 'const' to hoist the initializer or the entire
4700 * decl out of the loop head. TOK_VAR is the type for both
4701 * 'var' and 'const'.
4703 pn2 = pn1->pn_head;
4704 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4705 #if JS_HAS_DESTRUCTURING
4706 || pn2->pn_type == TOK_ASSIGN
4707 #endif
4709 pnseq = NewParseNode(PN_LIST, tc);
4710 if (!pnseq)
4711 return NULL;
4712 pnseq->pn_type = TOK_SEQ;
4713 pnseq->pn_pos.begin = pn->pn_pos.begin;
4715 #if JS_HAS_BLOCK_SCOPE
4716 if (tt == TOK_LET) {
4718 * Hoist just the 'i' from 'for (let x = i in o)' to
4719 * before the loop, glued together via pnseq.
4721 pn3 = NewParseNode(PN_UNARY, tc);
4722 if (!pn3)
4723 return NULL;
4724 pn3->pn_type = TOK_SEMI;
4725 pn3->pn_op = JSOP_NOP;
4726 #if JS_HAS_DESTRUCTURING
4727 if (pn2->pn_type == TOK_ASSIGN) {
4728 pn4 = pn2->pn_right;
4729 pn2 = pn1->pn_head = pn2->pn_left;
4730 } else
4731 #endif
4733 pn4 = pn2->pn_expr;
4734 pn2->pn_expr = NULL;
4736 if (!RebindLets(pn4, tc))
4737 return NULL;
4738 pn3->pn_pos = pn4->pn_pos;
4739 pn3->pn_kid = pn4;
4740 pnseq->initList(pn3);
4741 } else
4742 #endif /* JS_HAS_BLOCK_SCOPE */
4744 dflag = PND_INITIALIZED;
4747 * All of 'var x = i' is hoisted above 'for (x in o)',
4748 * so clear PNX_FORINVAR.
4750 * Request JSOP_POP here since the var is for a simple
4751 * name (it is not a destructuring binding's left-hand
4752 * side) and it has an initializer.
4754 pn1->pn_xflags &= ~PNX_FORINVAR;
4755 pn1->pn_xflags |= PNX_POPVAR;
4756 pnseq->initList(pn1);
4758 #if JS_HAS_DESTRUCTURING
4759 if (pn2->pn_type == TOK_ASSIGN) {
4760 pn1 = CloneParseTree(pn2->pn_left, tc);
4761 if (!pn1)
4762 return NULL;
4763 } else
4764 #endif
4766 JS_ASSERT(pn2->pn_type == TOK_NAME);
4767 pn1 = NewNameNode(cx, ts, pn2->pn_atom, tc);
4768 if (!pn1)
4769 return NULL;
4770 pn1->pn_type = TOK_NAME;
4771 pn1->pn_op = JSOP_NAME;
4772 pn1->pn_pos = pn2->pn_pos;
4773 if (pn2->pn_defn)
4774 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4776 pn2 = pn1;
4781 if (!pn2) {
4782 pn2 = pn1;
4783 #if JS_HAS_LVALUE_RETURN
4784 if (pn2->pn_type == TOK_LP &&
4785 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4786 return NULL;
4788 #endif
4789 #if JS_HAS_XML_SUPPORT
4790 if (pn2->pn_type == TOK_UNARYOP)
4791 pn2->pn_op = JSOP_BINDXMLNAME;
4792 #endif
4795 switch (pn2->pn_type) {
4796 case TOK_NAME:
4797 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4798 NoteLValue(cx, pn2, tc, dflag);
4799 break;
4801 #if JS_HAS_DESTRUCTURING
4802 case TOK_ASSIGN:
4803 pn2 = pn2->pn_left;
4804 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4805 /* FALL THROUGH */
4806 case TOK_RB:
4807 case TOK_RC:
4808 /* Check for valid lvalues in var-less destructuring for-in. */
4809 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
4810 return NULL;
4812 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
4814 * Destructuring for-in requires [key, value] enumeration
4815 * in JS1.7.
4817 JS_ASSERT(pn->pn_op == JSOP_ITER);
4818 if (!(pn->pn_iflags & JSITER_FOREACH))
4819 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4821 break;
4822 #endif
4824 default:;
4828 * Parse the object expression as the right operand of 'in', first
4829 * removing the top statement from the statement-stack if this is a
4830 * 'for (let x in y)' loop.
4832 #if JS_HAS_BLOCK_SCOPE
4833 JSStmtInfo *save = tc->topStmt;
4834 if (let)
4835 tc->topStmt = save->down;
4836 #endif
4837 pn2 = Expr(cx, ts, tc);
4838 #if JS_HAS_BLOCK_SCOPE
4839 if (let)
4840 tc->topStmt = save;
4841 #endif
4843 pn2 = NewBinary(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4844 if (!pn2)
4845 return NULL;
4846 pn->pn_left = pn2;
4847 } else {
4848 if (pn->pn_iflags & JSITER_FOREACH)
4849 goto bad_for_each;
4850 pn->pn_op = JSOP_NOP;
4852 /* Parse the loop condition or null into pn2. */
4853 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4854 ts->flags |= TSF_OPERAND;
4855 tt = js_PeekToken(cx, ts);
4856 ts->flags &= ~TSF_OPERAND;
4857 if (tt == TOK_SEMI) {
4858 pn2 = NULL;
4859 } else {
4860 pn2 = Expr(cx, ts, tc);
4861 if (!pn2)
4862 return NULL;
4865 /* Parse the update expression or null into pn3. */
4866 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4867 ts->flags |= TSF_OPERAND;
4868 tt = js_PeekToken(cx, ts);
4869 ts->flags &= ~TSF_OPERAND;
4870 if (tt == TOK_RP) {
4871 pn3 = NULL;
4872 } else {
4873 pn3 = Expr(cx, ts, tc);
4874 if (!pn3)
4875 return NULL;
4878 /* Build the FORHEAD node to use as the left kid of pn. */
4879 pn4 = NewParseNode(PN_TERNARY, tc);
4880 if (!pn4)
4881 return NULL;
4882 pn4->pn_type = TOK_FORHEAD;
4883 pn4->pn_op = JSOP_NOP;
4884 pn4->pn_kid1 = pn1;
4885 pn4->pn_kid2 = pn2;
4886 pn4->pn_kid3 = pn3;
4887 pn->pn_left = pn4;
4890 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
4892 /* Parse the loop body into pn->pn_right. */
4893 pn2 = Statement(cx, ts, tc);
4894 if (!pn2)
4895 return NULL;
4896 pn->pn_right = pn2;
4898 /* Record the absolute line number for source note emission. */
4899 pn->pn_pos.end = pn2->pn_pos.end;
4901 #if JS_HAS_BLOCK_SCOPE
4902 if (pnlet) {
4903 PopStatement(tc);
4904 pnlet->pn_expr = pn;
4905 pn = pnlet;
4907 #endif
4908 if (pnseq) {
4909 pnseq->pn_pos.end = pn->pn_pos.end;
4910 pnseq->append(pn);
4911 pn = pnseq;
4913 PopStatement(tc);
4914 return pn;
4916 bad_for_each:
4917 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
4918 JSMSG_BAD_FOR_EACH_LOOP);
4919 return NULL;
4922 case TOK_TRY: {
4923 JSParseNode *catchList, *lastCatch;
4926 * try nodes are ternary.
4927 * kid1 is the try Statement
4928 * kid2 is the catch node list or null
4929 * kid3 is the finally Statement
4931 * catch nodes are ternary.
4932 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4933 * kid2 is the catch guard or null if no guard
4934 * kid3 is the catch block
4936 * catch lvalue nodes are either:
4937 * TOK_NAME for a single identifier
4938 * TOK_RB or TOK_RC for a destructuring left-hand side
4940 * finally nodes are TOK_LC Statement lists.
4942 pn = NewParseNode(PN_TERNARY, tc);
4943 if (!pn)
4944 return NULL;
4945 pn->pn_op = JSOP_NOP;
4947 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
4948 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
4949 return NULL;
4950 pn->pn_kid1 = Statements(cx, ts, tc);
4951 if (!pn->pn_kid1)
4952 return NULL;
4953 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
4954 PopStatement(tc);
4956 catchList = NULL;
4957 tt = js_GetToken(cx, ts);
4958 if (tt == TOK_CATCH) {
4959 catchList = NewParseNode(PN_LIST, tc);
4960 if (!catchList)
4961 return NULL;
4962 catchList->pn_type = TOK_RESERVED;
4963 catchList->makeEmpty();
4964 lastCatch = NULL;
4966 do {
4967 JSParseNode *pnblock;
4968 BindData data;
4970 /* Check for another catch after unconditional catch. */
4971 if (lastCatch && !lastCatch->pn_kid2) {
4972 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4973 JSMSG_CATCH_AFTER_GENERAL);
4974 return NULL;
4978 * Create a lexical scope node around the whole catch clause,
4979 * including the head.
4981 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
4982 if (!pnblock)
4983 return NULL;
4984 stmtInfo.type = STMT_CATCH;
4987 * Legal catch forms are:
4988 * catch (lhs)
4989 * catch (lhs if <boolean_expression>)
4990 * where lhs is a name or a destructuring left-hand side.
4991 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
4993 pn2 = NewParseNode(PN_TERNARY, tc);
4994 if (!pn2)
4995 return NULL;
4996 pnblock->pn_expr = pn2;
4997 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5000 * Contrary to ECMA Ed. 3, the catch variable is lexically
5001 * scoped, not a property of a new Object instance. This is
5002 * an intentional change that anticipates ECMA Ed. 4.
5004 data.pn = NULL;
5005 data.op = JSOP_NOP;
5006 data.binder = BindLet;
5007 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5009 tt = js_GetToken(cx, ts);
5010 switch (tt) {
5011 #if JS_HAS_DESTRUCTURING
5012 case TOK_LB:
5013 case TOK_LC:
5014 pn3 = DestructuringExpr(cx, &data, tc, tt);
5015 if (!pn3)
5016 return NULL;
5017 break;
5018 #endif
5020 case TOK_NAME:
5021 label = CURRENT_TOKEN(ts).t_atom;
5022 pn3 = NewBindingNode(ts, label, tc, true);
5023 if (!pn3)
5024 return NULL;
5025 data.pn = pn3;
5026 if (!data.binder(cx, &data, label, tc))
5027 return NULL;
5028 break;
5030 default:
5031 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5032 JSMSG_CATCH_IDENTIFIER);
5033 return NULL;
5036 pn2->pn_kid1 = pn3;
5037 #if JS_HAS_CATCH_GUARD
5039 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5040 * to avoid conflicting with the JS2/ECMAv4 type annotation
5041 * catchguard syntax.
5043 if (js_MatchToken(cx, ts, TOK_IF)) {
5044 pn2->pn_kid2 = Expr(cx, ts, tc);
5045 if (!pn2->pn_kid2)
5046 return NULL;
5048 #endif
5049 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5051 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5052 pn2->pn_kid3 = Statements(cx, ts, tc);
5053 if (!pn2->pn_kid3)
5054 return NULL;
5055 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5056 PopStatement(tc);
5058 catchList->append(pnblock);
5059 lastCatch = pn2;
5060 ts->flags |= TSF_OPERAND;
5061 tt = js_GetToken(cx, ts);
5062 ts->flags &= ~TSF_OPERAND;
5063 } while (tt == TOK_CATCH);
5065 pn->pn_kid2 = catchList;
5067 if (tt == TOK_FINALLY) {
5068 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5069 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5070 return NULL;
5071 pn->pn_kid3 = Statements(cx, ts, tc);
5072 if (!pn->pn_kid3)
5073 return NULL;
5074 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5075 PopStatement(tc);
5076 } else {
5077 js_UngetToken(ts);
5079 if (!catchList && !pn->pn_kid3) {
5080 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5081 JSMSG_CATCH_OR_FINALLY);
5082 return NULL;
5084 return pn;
5087 case TOK_THROW:
5088 pn = NewParseNode(PN_UNARY, tc);
5089 if (!pn)
5090 return NULL;
5092 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5093 ts->flags |= TSF_OPERAND;
5094 tt = js_PeekTokenSameLine(cx, ts);
5095 ts->flags &= ~TSF_OPERAND;
5096 if (tt == TOK_ERROR)
5097 return NULL;
5098 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5099 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5100 JSMSG_SYNTAX_ERROR);
5101 return NULL;
5104 pn2 = Expr(cx, ts, tc);
5105 if (!pn2)
5106 return NULL;
5107 pn->pn_pos.end = pn2->pn_pos.end;
5108 pn->pn_op = JSOP_THROW;
5109 pn->pn_kid = pn2;
5110 break;
5112 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5113 case TOK_CATCH:
5114 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5115 JSMSG_CATCH_WITHOUT_TRY);
5116 return NULL;
5118 case TOK_FINALLY:
5119 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5120 JSMSG_FINALLY_WITHOUT_TRY);
5121 return NULL;
5123 case TOK_BREAK:
5124 pn = NewParseNode(PN_NULLARY, tc);
5125 if (!pn)
5126 return NULL;
5127 if (!MatchLabel(cx, ts, pn))
5128 return NULL;
5129 stmt = tc->topStmt;
5130 label = pn->pn_atom;
5131 if (label) {
5132 for (; ; stmt = stmt->down) {
5133 if (!stmt) {
5134 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5135 JSMSG_LABEL_NOT_FOUND);
5136 return NULL;
5138 if (stmt->type == STMT_LABEL && stmt->label == label)
5139 break;
5141 } else {
5142 for (; ; stmt = stmt->down) {
5143 if (!stmt) {
5144 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5145 JSMSG_TOUGH_BREAK);
5146 return NULL;
5148 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5149 break;
5152 if (label)
5153 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5154 break;
5156 case TOK_CONTINUE:
5157 pn = NewParseNode(PN_NULLARY, tc);
5158 if (!pn)
5159 return NULL;
5160 if (!MatchLabel(cx, ts, pn))
5161 return NULL;
5162 stmt = tc->topStmt;
5163 label = pn->pn_atom;
5164 if (label) {
5165 for (stmt2 = NULL; ; stmt = stmt->down) {
5166 if (!stmt) {
5167 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5168 JSMSG_LABEL_NOT_FOUND);
5169 return NULL;
5171 if (stmt->type == STMT_LABEL) {
5172 if (stmt->label == label) {
5173 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5174 js_ReportCompileErrorNumber(cx, ts, NULL,
5175 JSREPORT_ERROR,
5176 JSMSG_BAD_CONTINUE);
5177 return NULL;
5179 break;
5181 } else {
5182 stmt2 = stmt;
5185 } else {
5186 for (; ; stmt = stmt->down) {
5187 if (!stmt) {
5188 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5189 JSMSG_BAD_CONTINUE);
5190 return NULL;
5192 if (STMT_IS_LOOP(stmt))
5193 break;
5196 if (label)
5197 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5198 break;
5200 case TOK_WITH:
5201 pn = NewParseNode(PN_BINARY, tc);
5202 if (!pn)
5203 return NULL;
5204 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5205 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
5206 if (!pn2)
5207 return NULL;
5208 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5209 pn->pn_left = pn2;
5211 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5212 pn2 = Statement(cx, ts, tc);
5213 if (!pn2)
5214 return NULL;
5215 PopStatement(tc);
5217 pn->pn_pos.end = pn2->pn_pos.end;
5218 pn->pn_right = pn2;
5219 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5220 return pn;
5222 case TOK_VAR:
5223 pn = Variables(cx, ts, tc, false);
5224 if (!pn)
5225 return NULL;
5227 /* Tell js_EmitTree to generate a final POP. */
5228 pn->pn_xflags |= PNX_POPVAR;
5229 break;
5231 #if JS_HAS_BLOCK_SCOPE
5232 case TOK_LET:
5234 JSObject *obj;
5235 JSObjectBox *blockbox;
5237 /* Check for a let statement or let expression. */
5238 if (js_PeekToken(cx, ts) == TOK_LP) {
5239 pn = LetBlock(cx, ts, tc, JS_TRUE);
5240 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5241 return pn;
5243 /* Let expressions require automatic semicolon insertion. */
5244 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5245 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5246 break;
5250 * This is a let declaration. We must be directly under a block per
5251 * the proposed ES4 specs, but not an implicit block created due to
5252 * 'for (let ...)'. If we pass this error test, make the enclosing
5253 * JSStmtInfo be our scope. Further let declarations in this block
5254 * will find this scope statement and use the same block object.
5256 * If we are the first let declaration in this block (i.e., when the
5257 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5258 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5260 stmt = tc->topStmt;
5261 if (stmt &&
5262 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5263 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5264 JSMSG_LET_DECL_NOT_IN_BLOCK);
5265 return NULL;
5268 if (stmt && (stmt->flags & SIF_SCOPE)) {
5269 JS_ASSERT(tc->blockChain == stmt->blockObj);
5270 obj = tc->blockChain;
5271 } else {
5272 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5274 * ES4 specifies that let at top level and at body-block scope
5275 * does not shadow var, so convert back to var.
5277 CURRENT_TOKEN(ts).type = TOK_VAR;
5278 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
5280 pn = Variables(cx, ts, tc, false);
5281 if (!pn)
5282 return NULL;
5283 pn->pn_xflags |= PNX_POPVAR;
5284 break;
5288 * Some obvious assertions here, but they may help clarify the
5289 * situation. This stmt is not yet a scope, so it must not be a
5290 * catch block (catch is a lexical scope by definition).
5292 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5293 JS_ASSERT(stmt != tc->topScopeStmt);
5294 JS_ASSERT(stmt->type == STMT_BLOCK ||
5295 stmt->type == STMT_SWITCH ||
5296 stmt->type == STMT_TRY ||
5297 stmt->type == STMT_FINALLY);
5298 JS_ASSERT(!stmt->downScope);
5300 /* Convert the block statement into a scope statement. */
5301 JSObject *obj = js_NewBlockObject(tc->compiler->context);
5302 if (!obj)
5303 return NULL;
5305 blockbox = tc->compiler->newObjectBox(obj);
5306 if (!blockbox)
5307 return NULL;
5310 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5311 * list stack, if it isn't already there. If it is there, but it
5312 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5313 * block.
5315 stmt->flags |= SIF_SCOPE;
5316 stmt->downScope = tc->topScopeStmt;
5317 tc->topScopeStmt = stmt;
5318 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5319 (tc->maxScopeDepth = tc->scopeDepth));
5321 STOBJ_SET_PARENT(obj, tc->blockChain);
5322 tc->blockChain = obj;
5323 stmt->blockObj = obj;
5325 #ifdef DEBUG
5326 pn1 = tc->blockNode;
5327 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5328 #endif
5330 /* Create a new lexical scope node for these statements. */
5331 pn1 = NewParseNode(PN_NAME, tc);
5332 if (!pn1)
5333 return NULL;
5335 pn1->pn_type = TOK_LEXICALSCOPE;
5336 pn1->pn_op = JSOP_LEAVEBLOCK;
5337 pn1->pn_pos = tc->blockNode->pn_pos;
5338 pn1->pn_objbox = blockbox;
5339 pn1->pn_expr = tc->blockNode;
5340 pn1->pn_blockid = tc->blockNode->pn_blockid;
5341 tc->blockNode = pn1;
5344 pn = Variables(cx, ts, tc, false);
5345 if (!pn)
5346 return NULL;
5347 pn->pn_xflags = PNX_POPVAR;
5348 break;
5350 #endif /* JS_HAS_BLOCK_SCOPE */
5352 case TOK_RETURN:
5353 pn = ReturnOrYield(cx, ts, tc, Expr);
5354 if (!pn)
5355 return NULL;
5356 break;
5358 case TOK_LC:
5360 uintN oldflags;
5362 oldflags = tc->flags;
5363 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5364 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5365 return NULL;
5366 pn = Statements(cx, ts, tc);
5367 if (!pn)
5368 return NULL;
5370 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5371 PopStatement(tc);
5374 * If we contain a function statement and our container is top-level
5375 * or another block, flag pn to preserve braces when decompiling.
5377 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5378 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5379 pn->pn_xflags |= PNX_NEEDBRACES;
5381 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5382 return pn;
5385 case TOK_EOL:
5386 case TOK_SEMI:
5387 pn = NewParseNode(PN_UNARY, tc);
5388 if (!pn)
5389 return NULL;
5390 pn->pn_type = TOK_SEMI;
5391 return pn;
5393 #if JS_HAS_DEBUGGER_KEYWORD
5394 case TOK_DEBUGGER:
5395 pn = NewParseNode(PN_NULLARY, tc);
5396 if (!pn)
5397 return NULL;
5398 pn->pn_type = TOK_DEBUGGER;
5399 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5400 break;
5401 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5403 #if JS_HAS_XML_SUPPORT
5404 case TOK_DEFAULT:
5405 pn = NewParseNode(PN_UNARY, tc);
5406 if (!pn)
5407 return NULL;
5408 if (!js_MatchToken(cx, ts, TOK_NAME) ||
5409 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
5410 !js_MatchToken(cx, ts, TOK_NAME) ||
5411 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
5412 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
5413 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
5414 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5415 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5416 return NULL;
5418 pn2 = Expr(cx, ts, tc);
5419 if (!pn2)
5420 return NULL;
5421 pn->pn_op = JSOP_DEFXMLNS;
5422 pn->pn_pos.end = pn2->pn_pos.end;
5423 pn->pn_kid = pn2;
5424 break;
5425 #endif
5427 case TOK_ERROR:
5428 return NULL;
5430 default:
5431 #if JS_HAS_XML_SUPPORT
5432 expression:
5433 #endif
5434 js_UngetToken(ts);
5435 pn2 = Expr(cx, ts, tc);
5436 if (!pn2)
5437 return NULL;
5439 if (js_PeekToken(cx, ts) == TOK_COLON) {
5440 if (pn2->pn_type != TOK_NAME) {
5441 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5442 JSMSG_BAD_LABEL);
5443 return NULL;
5445 label = pn2->pn_atom;
5446 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5447 if (stmt->type == STMT_LABEL && stmt->label == label) {
5448 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5449 JSMSG_DUPLICATE_LABEL);
5450 return NULL;
5453 ForgetUse(pn2);
5455 (void) js_GetToken(cx, ts);
5457 /* Push a label struct and parse the statement. */
5458 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5459 stmtInfo.label = label;
5460 pn = Statement(cx, ts, tc);
5461 if (!pn)
5462 return NULL;
5464 /* Normalize empty statement to empty block for the decompiler. */
5465 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5466 pn->pn_type = TOK_LC;
5467 pn->pn_arity = PN_LIST;
5468 pn->makeEmpty();
5471 /* Pop the label, set pn_expr, and return early. */
5472 PopStatement(tc);
5473 pn2->pn_type = TOK_COLON;
5474 pn2->pn_pos.end = pn->pn_pos.end;
5475 pn2->pn_expr = pn;
5476 return pn2;
5479 pn = NewParseNode(PN_UNARY, tc);
5480 if (!pn)
5481 return NULL;
5482 pn->pn_type = TOK_SEMI;
5483 pn->pn_pos = pn2->pn_pos;
5484 pn->pn_kid = pn2;
5485 break;
5488 /* Check termination of this primitive statement. */
5489 return MatchOrInsertSemicolon(cx, ts) ? pn : NULL;
5492 static void
5493 NoteArgumentsUse(JSTreeContext *tc)
5495 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
5496 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5497 if (tc->funbox)
5498 tc->funbox->node->pn_dflags |= PND_FUNARG;
5501 static JSParseNode *
5502 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, bool inLetHead)
5504 JSTokenType tt;
5505 bool let;
5506 JSStmtInfo *scopeStmt;
5507 BindData data;
5508 JSParseNode *pn, *pn2;
5509 JSAtom *atom;
5512 * The three options here are:
5513 * - TOK_LET: We are parsing a let declaration.
5514 * - TOK_LP: We are parsing the head of a let block.
5515 * - Otherwise, we're parsing var declarations.
5517 tt = CURRENT_TOKEN(ts).type;
5518 let = (tt == TOK_LET || tt == TOK_LP);
5519 JS_ASSERT(let || tt == TOK_VAR);
5521 #if JS_HAS_BLOCK_SCOPE
5522 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5523 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5524 #endif
5526 /* Make sure that Statement set up the tree context correctly. */
5527 scopeStmt = tc->topScopeStmt;
5528 if (let) {
5529 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5530 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5531 scopeStmt = scopeStmt->downScope;
5533 JS_ASSERT(scopeStmt);
5536 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
5537 pn = NewParseNode(PN_LIST, tc);
5538 if (!pn)
5539 return NULL;
5540 pn->pn_op = data.op;
5541 pn->makeEmpty();
5544 * SpiderMonkey const is really "write once per initialization evaluation"
5545 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5546 * this code will change soon.
5548 if (let) {
5549 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5550 data.binder = BindLet;
5551 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5552 } else {
5553 data.binder = BindVarOrConst;
5556 do {
5557 tt = js_GetToken(cx, ts);
5558 #if JS_HAS_DESTRUCTURING
5559 if (tt == TOK_LB || tt == TOK_LC) {
5560 ts->flags |= TSF_DESTRUCTURING;
5561 pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
5562 ts->flags &= ~TSF_DESTRUCTURING;
5563 if (!pn2)
5564 return NULL;
5566 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
5567 return NULL;
5568 if ((tc->flags & TCF_IN_FOR_INIT) &&
5569 js_PeekToken(cx, ts) == TOK_IN) {
5570 pn->append(pn2);
5571 continue;
5574 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5575 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5576 goto bad_var_init;
5578 #if JS_HAS_BLOCK_SCOPE
5579 if (popScope) {
5580 tc->topStmt = save->down;
5581 tc->topScopeStmt = saveScope->downScope;
5583 #endif
5584 JSParseNode *init = AssignExpr(cx, ts, tc);
5585 #if JS_HAS_BLOCK_SCOPE
5586 if (popScope) {
5587 tc->topStmt = save;
5588 tc->topScopeStmt = saveScope;
5590 #endif
5592 if (!init || !UndominateInitializers(pn2, init, tc))
5593 return NULL;
5595 pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5596 if (!pn2)
5597 return NULL;
5598 pn->append(pn2);
5599 continue;
5601 #endif /* JS_HAS_DESTRUCTURING */
5603 if (tt != TOK_NAME) {
5604 if (tt != TOK_ERROR) {
5605 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5606 JSMSG_NO_VARIABLE_NAME);
5608 return NULL;
5611 atom = CURRENT_TOKEN(ts).t_atom;
5612 pn2 = NewBindingNode(ts, atom, tc, let);
5613 if (!pn2)
5614 return NULL;
5615 if (data.op == JSOP_DEFCONST)
5616 pn2->pn_dflags |= PND_CONST;
5617 data.pn = pn2;
5618 if (!data.binder(cx, &data, atom, tc))
5619 return NULL;
5620 pn->append(pn2);
5622 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
5623 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5624 goto bad_var_init;
5626 #if JS_HAS_BLOCK_SCOPE
5627 if (popScope) {
5628 tc->topStmt = save->down;
5629 tc->topScopeStmt = saveScope->downScope;
5631 #endif
5632 JSParseNode *init = AssignExpr(cx, ts, tc);
5633 #if JS_HAS_BLOCK_SCOPE
5634 if (popScope) {
5635 tc->topStmt = save;
5636 tc->topScopeStmt = saveScope;
5638 #endif
5639 if (!init)
5640 return NULL;
5642 if (pn2->pn_used) {
5643 pn2 = MakeAssignment(pn2, init, tc);
5644 if (!pn2)
5645 return NULL;
5646 } else {
5647 pn2->pn_expr = init;
5650 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5651 ? JSOP_SETNAME
5652 : (pn2->pn_dflags & PND_GVAR)
5653 ? JSOP_SETGVAR
5654 : (pn2->pn_dflags & PND_BOUND)
5655 ? JSOP_SETLOCAL
5656 : (data.op == JSOP_DEFCONST)
5657 ? JSOP_SETCONST
5658 : JSOP_SETNAME;
5660 NoteLValue(cx, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5662 /* The declarator's position must include the initializer. */
5663 pn2->pn_pos.end = init->pn_pos.end;
5665 if ((tc->flags & TCF_IN_FUNCTION) &&
5666 atom == cx->runtime->atomState.argumentsAtom) {
5667 NoteArgumentsUse(tc);
5668 if (!let)
5669 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5672 } while (js_MatchToken(cx, ts, TOK_COMMA));
5674 pn->pn_pos.end = pn->last()->pn_pos.end;
5675 return pn;
5677 bad_var_init:
5678 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5679 JSMSG_BAD_VAR_INIT);
5680 return NULL;
5683 static JSParseNode *
5684 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5686 JSParseNode *pn, *pn2;
5688 pn = AssignExpr(cx, ts, tc);
5689 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
5690 pn2 = NewParseNode(PN_LIST, tc);
5691 if (!pn2)
5692 return NULL;
5693 pn2->pn_pos.begin = pn->pn_pos.begin;
5694 pn2->initList(pn);
5695 pn = pn2;
5696 do {
5697 #if JS_HAS_GENERATORS
5698 pn2 = pn->last();
5699 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
5700 js_ReportCompileErrorNumber(cx, ts, pn2, JSREPORT_ERROR,
5701 JSMSG_BAD_GENERATOR_SYNTAX,
5702 js_yield_str);
5703 return NULL;
5705 #endif
5706 pn2 = AssignExpr(cx, ts, tc);
5707 if (!pn2)
5708 return NULL;
5709 pn->append(pn2);
5710 } while (js_MatchToken(cx, ts, TOK_COMMA));
5711 pn->pn_pos.end = pn->last()->pn_pos.end;
5713 return pn;
5716 static JSParseNode *
5717 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5719 JSParseNode *pn, *rhs;
5720 JSTokenType tt;
5721 JSOp op;
5723 JS_CHECK_RECURSION(cx, return NULL);
5725 #if JS_HAS_GENERATORS
5726 ts->flags |= TSF_OPERAND;
5727 if (js_MatchToken(cx, ts, TOK_YIELD)) {
5728 ts->flags &= ~TSF_OPERAND;
5729 return ReturnOrYield(cx, ts, tc, AssignExpr);
5731 ts->flags &= ~TSF_OPERAND;
5732 #endif
5734 pn = CondExpr(cx, ts, tc);
5735 if (!pn)
5736 return NULL;
5738 tt = js_GetToken(cx, ts);
5739 #if JS_HAS_GETTER_SETTER
5740 if (tt == TOK_NAME) {
5741 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
5742 if (tt == TOK_ERROR)
5743 return NULL;
5745 #endif
5746 if (tt != TOK_ASSIGN) {
5747 js_UngetToken(ts);
5748 return pn;
5751 op = CURRENT_TOKEN(ts).t_op;
5752 switch (pn->pn_type) {
5753 case TOK_NAME:
5754 pn->pn_op = JSOP_SETNAME;
5755 NoteLValue(cx, pn, tc);
5756 break;
5757 case TOK_DOT:
5758 pn->pn_op = JSOP_SETPROP;
5759 break;
5760 case TOK_LB:
5761 pn->pn_op = JSOP_SETELEM;
5762 break;
5763 #if JS_HAS_DESTRUCTURING
5764 case TOK_RB:
5765 case TOK_RC:
5766 if (op != JSOP_NOP) {
5767 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5768 JSMSG_BAD_DESTRUCT_ASS);
5769 return NULL;
5771 rhs = AssignExpr(cx, ts, tc);
5772 if (!rhs || !CheckDestructuring(cx, NULL, pn, rhs, tc))
5773 return NULL;
5774 return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
5775 #endif
5776 #if JS_HAS_LVALUE_RETURN
5777 case TOK_LP:
5778 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5779 return NULL;
5780 break;
5781 #endif
5782 #if JS_HAS_XML_SUPPORT
5783 case TOK_UNARYOP:
5784 if (pn->pn_op == JSOP_XMLNAME) {
5785 pn->pn_op = JSOP_SETXMLNAME;
5786 break;
5788 /* FALL THROUGH */
5789 #endif
5790 default:
5791 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5792 JSMSG_BAD_LEFTSIDE_OF_ASS);
5793 return NULL;
5796 rhs = AssignExpr(cx, ts, tc);
5797 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
5798 JSDefinition *dn = pn->pn_lexdef;
5801 * If the definition is not flagged as assigned, we must have imputed
5802 * the initialized flag to it, to optimize for flat closures. But that
5803 * optimization uses source coordinates to check dominance relations,
5804 * so we must extend the end of the definition to cover the right-hand
5805 * side of this assignment, i.e., the initializer.
5807 if (!dn->isAssigned()) {
5808 JS_ASSERT(dn->isInitialized());
5809 dn->pn_pos.end = rhs->pn_pos.end;
5813 return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
5816 static JSParseNode *
5817 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5819 JSParseNode *pn, *pn1, *pn2, *pn3;
5820 uintN oldflags;
5822 pn = OrExpr(cx, ts, tc);
5823 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
5824 pn1 = pn;
5825 pn = NewParseNode(PN_TERNARY, tc);
5826 if (!pn)
5827 return NULL;
5829 * Always accept the 'in' operator in the middle clause of a ternary,
5830 * where it's unambiguous, even if we might be parsing the init of a
5831 * for statement.
5833 oldflags = tc->flags;
5834 tc->flags &= ~TCF_IN_FOR_INIT;
5835 pn2 = AssignExpr(cx, ts, tc);
5836 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5838 if (!pn2)
5839 return NULL;
5840 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
5841 pn3 = AssignExpr(cx, ts, tc);
5842 if (!pn3)
5843 return NULL;
5844 pn->pn_pos.begin = pn1->pn_pos.begin;
5845 pn->pn_pos.end = pn3->pn_pos.end;
5846 pn->pn_kid1 = pn1;
5847 pn->pn_kid2 = pn2;
5848 pn->pn_kid3 = pn3;
5850 return pn;
5853 static JSParseNode *
5854 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5856 JSParseNode *pn;
5858 pn = AndExpr(cx, ts, tc);
5859 while (pn && js_MatchToken(cx, ts, TOK_OR))
5860 pn = NewBinary(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
5861 return pn;
5864 static JSParseNode *
5865 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5867 JSParseNode *pn;
5869 pn = BitOrExpr(cx, ts, tc);
5870 while (pn && js_MatchToken(cx, ts, TOK_AND))
5871 pn = NewBinary(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
5872 return pn;
5875 static JSParseNode *
5876 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5878 JSParseNode *pn;
5880 pn = BitXorExpr(cx, ts, tc);
5881 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
5882 pn = NewBinary(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
5883 tc);
5885 return pn;
5888 static JSParseNode *
5889 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5891 JSParseNode *pn;
5893 pn = BitAndExpr(cx, ts, tc);
5894 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
5895 pn = NewBinary(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
5896 tc);
5898 return pn;
5901 static JSParseNode *
5902 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5904 JSParseNode *pn;
5906 pn = EqExpr(cx, ts, tc);
5907 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
5908 pn = NewBinary(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
5909 return pn;
5912 static JSParseNode *
5913 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5915 JSParseNode *pn;
5916 JSOp op;
5918 pn = RelExpr(cx, ts, tc);
5919 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
5920 op = CURRENT_TOKEN(ts).t_op;
5921 pn = NewBinary(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
5923 return pn;
5926 static JSParseNode *
5927 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5929 JSParseNode *pn;
5930 JSTokenType tt;
5931 JSOp op;
5932 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
5935 * Uses of the in operator in ShiftExprs are always unambiguous,
5936 * so unset the flag that prohibits recognizing it.
5938 tc->flags &= ~TCF_IN_FOR_INIT;
5940 pn = ShiftExpr(cx, ts, tc);
5941 while (pn &&
5942 (js_MatchToken(cx, ts, TOK_RELOP) ||
5944 * Recognize the 'in' token as an operator only if we're not
5945 * currently in the init expr of a for loop.
5947 (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
5948 js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
5949 tt = CURRENT_TOKEN(ts).type;
5950 op = CURRENT_TOKEN(ts).t_op;
5951 pn = NewBinary(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
5953 /* Restore previous state of inForInit flag. */
5954 tc->flags |= inForInitFlag;
5956 return pn;
5959 static JSParseNode *
5960 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5962 JSParseNode *pn;
5963 JSOp op;
5965 pn = AddExpr(cx, ts, tc);
5966 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
5967 op = CURRENT_TOKEN(ts).t_op;
5968 pn = NewBinary(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
5970 return pn;
5973 static JSParseNode *
5974 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5976 JSParseNode *pn;
5977 JSTokenType tt;
5978 JSOp op;
5980 pn = MulExpr(cx, ts, tc);
5981 while (pn &&
5982 (js_MatchToken(cx, ts, TOK_PLUS) ||
5983 js_MatchToken(cx, ts, TOK_MINUS))) {
5984 tt = CURRENT_TOKEN(ts).type;
5985 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
5986 pn = NewBinary(tt, op, pn, MulExpr(cx, ts, tc), tc);
5988 return pn;
5991 static JSParseNode *
5992 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5994 JSParseNode *pn;
5995 JSTokenType tt;
5996 JSOp op;
5998 pn = UnaryExpr(cx, ts, tc);
5999 while (pn &&
6000 (js_MatchToken(cx, ts, TOK_STAR) ||
6001 js_MatchToken(cx, ts, TOK_DIVOP))) {
6002 tt = CURRENT_TOKEN(ts).type;
6003 op = CURRENT_TOKEN(ts).t_op;
6004 pn = NewBinary(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
6006 return pn;
6009 static JSParseNode *
6010 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
6011 const char *name)
6013 if (kid->pn_type != TOK_NAME &&
6014 kid->pn_type != TOK_DOT &&
6015 #if JS_HAS_LVALUE_RETURN
6016 (kid->pn_type != TOK_LP ||
6017 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6018 #endif
6019 #if JS_HAS_XML_SUPPORT
6020 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6021 #endif
6022 kid->pn_type != TOK_LB) {
6023 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6024 JSMSG_BAD_OPERAND, name);
6025 return NULL;
6027 pn->pn_kid = kid;
6028 return kid;
6031 static const char incop_name_str[][10] = {"increment", "decrement"};
6033 static JSBool
6034 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6035 JSParseNode *pn, JSParseNode *kid,
6036 JSTokenType tt, JSBool preorder)
6038 JSOp op;
6040 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
6041 if (!kid)
6042 return JS_FALSE;
6043 switch (kid->pn_type) {
6044 case TOK_NAME:
6045 op = (tt == TOK_INC)
6046 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6047 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6048 NoteLValue(cx, kid, tc);
6049 break;
6051 case TOK_DOT:
6052 op = (tt == TOK_INC)
6053 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6054 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6055 break;
6057 #if JS_HAS_LVALUE_RETURN
6058 case TOK_LP:
6059 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6060 return JS_FALSE;
6061 /* FALL THROUGH */
6062 #endif
6063 #if JS_HAS_XML_SUPPORT
6064 case TOK_UNARYOP:
6065 if (kid->pn_op == JSOP_XMLNAME)
6066 kid->pn_op = JSOP_SETXMLNAME;
6067 /* FALL THROUGH */
6068 #endif
6069 case TOK_LB:
6070 op = (tt == TOK_INC)
6071 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6072 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6073 break;
6075 default:
6076 JS_ASSERT(0);
6077 op = JSOP_NOP;
6079 pn->pn_op = op;
6080 return JS_TRUE;
6083 static JSParseNode *
6084 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6086 JSTokenType tt;
6087 JSParseNode *pn, *pn2;
6089 JS_CHECK_RECURSION(cx, return NULL);
6091 ts->flags |= TSF_OPERAND;
6092 tt = js_GetToken(cx, ts);
6093 ts->flags &= ~TSF_OPERAND;
6095 switch (tt) {
6096 case TOK_UNARYOP:
6097 case TOK_PLUS:
6098 case TOK_MINUS:
6099 pn = NewParseNode(PN_UNARY, tc);
6100 if (!pn)
6101 return NULL;
6102 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6103 pn->pn_op = CURRENT_TOKEN(ts).t_op;
6104 pn2 = UnaryExpr(cx, ts, tc);
6105 if (!pn2)
6106 return NULL;
6107 pn->pn_pos.end = pn2->pn_pos.end;
6108 pn->pn_kid = pn2;
6109 break;
6111 case TOK_INC:
6112 case TOK_DEC:
6113 pn = NewParseNode(PN_UNARY, tc);
6114 if (!pn)
6115 return NULL;
6116 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
6117 if (!pn2)
6118 return NULL;
6119 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
6120 return NULL;
6121 pn->pn_pos.end = pn2->pn_pos.end;
6122 break;
6124 case TOK_DELETE:
6125 pn = NewParseNode(PN_UNARY, tc);
6126 if (!pn)
6127 return NULL;
6128 pn2 = UnaryExpr(cx, ts, tc);
6129 if (!pn2)
6130 return NULL;
6131 pn->pn_pos.end = pn2->pn_pos.end;
6134 * Under ECMA3, deleting any unary expression is valid -- it simply
6135 * returns true. Here we fold constants before checking for a call
6136 * expression, in order to rule out delete of a generator expression.
6138 if (!js_FoldConstants(cx, pn2, tc))
6139 return NULL;
6140 switch (pn2->pn_type) {
6141 case TOK_LP:
6142 if (pn2->pn_op != JSOP_SETCALL &&
6143 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6144 return NULL;
6146 break;
6147 case TOK_NAME:
6148 pn2->pn_op = JSOP_DELNAME;
6149 break;
6150 default:;
6152 pn->pn_kid = pn2;
6153 break;
6155 case TOK_ERROR:
6156 return NULL;
6158 default:
6159 js_UngetToken(ts);
6160 pn = MemberExpr(cx, ts, tc, JS_TRUE);
6161 if (!pn)
6162 return NULL;
6164 /* Don't look across a newline boundary for a postfix incop. */
6165 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
6166 ts->flags |= TSF_OPERAND;
6167 tt = js_PeekTokenSameLine(cx, ts);
6168 ts->flags &= ~TSF_OPERAND;
6169 if (tt == TOK_INC || tt == TOK_DEC) {
6170 (void) js_GetToken(cx, ts);
6171 pn2 = NewParseNode(PN_UNARY, tc);
6172 if (!pn2)
6173 return NULL;
6174 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
6175 return NULL;
6176 pn2->pn_pos.begin = pn->pn_pos.begin;
6177 pn = pn2;
6180 break;
6182 return pn;
6185 #if JS_HAS_GENERATORS
6188 * A dedicated helper for transplanting the comprehension expression E in
6190 * [E for (V in I)] // array comprehension
6191 * (E for (V in I)) // generator expression
6193 * from its initial location in the AST, on the left of the 'for', to its final
6194 * position on the right. To avoid a separate pass we do this by adjusting the
6195 * blockids and name binding links that were established when E was parsed.
6197 * A generator expression desugars like so:
6199 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6201 * so the transplanter must adjust static level as well as blockid. E's source
6202 * coordinates in root->pn_pos are critical to deciding which binding links to
6203 * preserve and which to cut.
6205 * NB: This is not a general tree transplanter -- it knows in particular that
6206 * the one or more bindings induced by V have not yet been created.
6208 class CompExprTransplanter {
6209 JSParseNode *root;
6210 JSTreeContext *tc;
6211 bool genexp;
6212 uintN adjust;
6213 uintN funcLevel;
6215 public:
6216 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6217 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6221 bool transplant(JSParseNode *pn);
6225 * Any definitions nested within the comprehension expression of a generator
6226 * expression must move "down" one static level, which of course increases the
6227 * upvar-frame-skip count.
6229 static bool
6230 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6232 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6233 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6235 JS_ASSERT(level >= tc->staticLevel);
6236 if (level >= FREE_STATIC_LEVEL) {
6237 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
6238 JSMSG_TOO_DEEP, js_function_str);
6239 return false;
6242 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6244 return true;
6247 static void
6248 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6250 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6251 pn->pn_blockid += adjust;
6252 if (pn->pn_blockid >= tc->blockidGen)
6253 tc->blockidGen = pn->pn_blockid + 1;
6256 bool
6257 CompExprTransplanter::transplant(JSParseNode *pn)
6259 if (!pn)
6260 return true;
6262 switch (pn->pn_arity) {
6263 case PN_LIST:
6264 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6265 transplant(pn2);
6266 if (pn->pn_pos >= root->pn_pos)
6267 AdjustBlockId(pn, adjust, tc);
6268 break;
6270 case PN_TERNARY:
6271 transplant(pn->pn_kid1);
6272 transplant(pn->pn_kid2);
6273 transplant(pn->pn_kid3);
6274 break;
6276 case PN_BINARY:
6277 transplant(pn->pn_left);
6279 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6280 if (pn->pn_right != pn->pn_left)
6281 transplant(pn->pn_right);
6282 break;
6284 case PN_UNARY:
6285 transplant(pn->pn_kid);
6286 break;
6288 case PN_FUNC:
6291 * Only the first level of transplant recursion through functions needs
6292 * to reparent the funbox, since all descendant functions are correctly
6293 * linked under the top-most funbox. But every visit to this case needs
6294 * to update funbox->level.
6296 * Recall that funbox->level is the static level of the code containing
6297 * the definition or expression of the function and not the static level
6298 * of the function's body.
6300 JSFunctionBox *funbox = pn->pn_funbox;
6302 funbox->level = tc->staticLevel + funcLevel;
6303 if (++funcLevel == 1 && genexp) {
6304 JSFunctionBox *parent = tc->funbox;
6306 JSFunctionBox **funboxp = &tc->parent->functionList;
6307 while (*funboxp != funbox)
6308 funboxp = &(*funboxp)->siblings;
6309 *funboxp = funbox->siblings;
6311 funbox->parent = parent;
6312 funbox->siblings = parent->kids;
6313 parent->kids = funbox;
6314 funbox->level = tc->staticLevel;
6316 /* FALL THROUGH */
6319 case PN_NAME:
6320 transplant(pn->maybeExpr());
6321 if (pn->pn_arity == PN_FUNC)
6322 --funcLevel;
6324 if (pn->pn_defn) {
6325 if (genexp && !BumpStaticLevel(pn, tc))
6326 return false;
6327 } else if (pn->pn_used) {
6328 JS_ASSERT(pn->pn_op != JSOP_NOP);
6329 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6331 JSDefinition *dn = pn->pn_lexdef;
6332 JS_ASSERT(dn->pn_defn);
6335 * Adjust the definition's block id only if it is a placeholder not
6336 * to the left of the root node, and if pn is the last use visited
6337 * in the comprehension expression (to avoid adjusting the blockid
6338 * multiple times).
6340 * Non-placeholder definitions within the comprehension expression
6341 * will be visited further below.
6343 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6344 if (genexp && !BumpStaticLevel(dn, tc))
6345 return false;
6346 AdjustBlockId(dn, adjust, tc);
6349 JSAtom *atom = pn->pn_atom;
6350 #ifdef DEBUG
6351 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6352 JS_ASSERT(!stmt || stmt != tc->topStmt);
6353 #endif
6354 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6355 JS_ASSERT(!tc->decls.lookup(atom));
6357 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6358 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
6359 if (!ale)
6360 return false;
6362 if (dn->pn_pos >= root->pn_pos) {
6363 tc->parent->lexdeps.remove(tc->compiler, atom);
6364 } else {
6365 JSDefinition *dn2 = (JSDefinition *)
6366 NewNameNode(tc->compiler->context, TS(tc->compiler), dn->pn_atom, tc);
6367 if (!dn2)
6368 return false;
6370 dn2->pn_type = dn->pn_type;
6371 dn2->pn_pos = root->pn_pos;
6372 dn2->pn_defn = true;
6373 dn2->pn_dflags |= PND_PLACEHOLDER;
6375 JSParseNode **pnup = &dn->dn_uses;
6376 JSParseNode *pnu;
6377 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6378 pnu->pn_lexdef = dn2;
6379 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6380 pnup = &pnu->pn_link;
6382 dn2->dn_uses = dn->dn_uses;
6383 dn->dn_uses = *pnup;
6384 *pnup = NULL;
6386 dn = dn2;
6389 ALE_SET_DEFN(ale, dn);
6394 if (pn->pn_pos >= root->pn_pos)
6395 AdjustBlockId(pn, adjust, tc);
6396 break;
6398 case PN_NAMESET:
6399 transplant(pn->pn_tree);
6400 break;
6402 return true;
6406 * Starting from a |for| keyword after the first array initialiser element or
6407 * an expression in an open parenthesis, parse the tail of the comprehension
6408 * or generator expression signified by this |for| keyword in context.
6410 * Return null on failure, else return the top-most parse node for the array
6411 * comprehension or generator expression, with a unary node as the body of the
6412 * (possibly nested) for-loop, initialized by |type, op, kid|.
6414 static JSParseNode *
6415 ComprehensionTail(JSParseNode *kid, uintN blockid, JSTreeContext *tc,
6416 JSTokenType type = TOK_SEMI, JSOp op = JSOP_NOP)
6418 JSContext *cx = tc->compiler->context;
6419 JSTokenStream *ts = TS(tc->compiler);
6421 uintN adjust;
6422 JSParseNode *pn, *pn2, *pn3, **pnp;
6423 JSStmtInfo stmtInfo;
6424 BindData data;
6425 JSTokenType tt;
6426 JSAtom *atom;
6428 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
6430 if (type == TOK_SEMI) {
6432 * Generator expression desugars to an immediately applied lambda that
6433 * yields the next value from a for-in loop (possibly nested, and with
6434 * optional if guard). Make pn be the TOK_LC body node.
6436 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6437 if (!pn)
6438 return NULL;
6439 adjust = pn->pn_blockid - blockid;
6440 } else {
6441 JS_ASSERT(type == TOK_ARRAYPUSH);
6444 * Make a parse-node and literal object representing the block scope of
6445 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6446 * aka the array initialiser case, has passed the blockid to claim for
6447 * the comprehension's block scope. We allocate that id or one above it
6448 * here, by calling js_PushLexicalScope.
6450 * In the case of a comprehension expression that has nested blocks
6451 * (e.g., let expressions), we will allocate a higher blockid but then
6452 * slide all blocks "to the right" to make room for the comprehension's
6453 * block scope.
6455 adjust = tc->blockid();
6456 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6457 if (!pn)
6458 return NULL;
6460 JS_ASSERT(blockid <= pn->pn_blockid);
6461 JS_ASSERT(blockid < tc->blockidGen);
6462 JS_ASSERT(tc->bodyid < blockid);
6463 pn->pn_blockid = stmtInfo.blockid = blockid;
6464 JS_ASSERT(adjust < blockid);
6465 adjust = blockid - adjust;
6468 pnp = &pn->pn_expr;
6470 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6471 transplanter.transplant(kid);
6473 data.pn = NULL;
6474 data.op = JSOP_NOP;
6475 data.binder = BindLet;
6476 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6478 do {
6480 * FOR node is binary, left is loop control and right is body. Use
6481 * index to count each block-local let-variable on the left-hand side
6482 * of the IN.
6484 pn2 = NewParseNode(PN_BINARY, tc);
6485 if (!pn2)
6486 return NULL;
6488 pn2->pn_op = JSOP_ITER;
6489 pn2->pn_iflags = JSITER_ENUMERATE;
6490 if (js_MatchToken(cx, ts, TOK_NAME)) {
6491 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
6492 pn2->pn_iflags |= JSITER_FOREACH;
6493 else
6494 js_UngetToken(ts);
6496 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6498 atom = NULL;
6499 tt = js_GetToken(cx, ts);
6500 switch (tt) {
6501 #if JS_HAS_DESTRUCTURING
6502 case TOK_LB:
6503 case TOK_LC:
6504 ts->flags |= TSF_DESTRUCTURING;
6505 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6506 ts->flags &= ~TSF_DESTRUCTURING;
6507 if (!pn3)
6508 return NULL;
6509 break;
6510 #endif
6512 case TOK_NAME:
6513 atom = CURRENT_TOKEN(ts).t_atom;
6516 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6517 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6518 * in the operand stack frame. The code generator computes that,
6519 * and it tries to bind all names to slots, so we must let it do
6520 * the deed.
6522 pn3 = NewBindingNode(ts, atom, tc, true);
6523 if (!pn3)
6524 return NULL;
6525 break;
6527 default:
6528 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6529 JSMSG_NO_VARIABLE_NAME);
6531 case TOK_ERROR:
6532 return NULL;
6535 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6536 JSParseNode *pn4 = Expr(cx, ts, tc);
6537 if (!pn4)
6538 return NULL;
6539 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6541 switch (tt) {
6542 #if JS_HAS_DESTRUCTURING
6543 case TOK_LB:
6544 case TOK_LC:
6545 if (!CheckDestructuring(cx, &data, pn3, NULL, tc))
6546 return NULL;
6548 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
6549 /* Destructuring requires [key, value] enumeration in JS1.7. */
6550 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6551 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6552 JSMSG_BAD_FOR_LEFTSIDE);
6553 return NULL;
6556 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6557 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6558 if (!(pn2->pn_iflags & JSITER_FOREACH))
6559 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6561 break;
6562 #endif
6564 case TOK_NAME:
6565 data.pn = pn3;
6566 if (!data.binder(cx, &data, atom, tc))
6567 return NULL;
6568 break;
6570 default:;
6573 pn2->pn_left = NewBinary(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6574 if (!pn2->pn_left)
6575 return NULL;
6576 *pnp = pn2;
6577 pnp = &pn2->pn_right;
6578 } while (js_MatchToken(cx, ts, TOK_FOR));
6580 if (js_MatchToken(cx, ts, TOK_IF)) {
6581 pn2 = NewParseNode(PN_TERNARY, tc);
6582 if (!pn2)
6583 return NULL;
6584 pn2->pn_kid1 = Condition(cx, ts, tc);
6585 if (!pn2->pn_kid1)
6586 return NULL;
6587 *pnp = pn2;
6588 pnp = &pn2->pn_kid2;
6591 pn2 = NewParseNode(PN_UNARY, tc);
6592 if (!pn2)
6593 return NULL;
6594 pn2->pn_type = type;
6595 pn2->pn_op = op;
6596 pn2->pn_kid = kid;
6597 *pnp = pn2;
6599 if (type == TOK_ARRAYPUSH)
6600 PopStatement(tc);
6601 return pn;
6604 #if JS_HAS_GENERATOR_EXPRS
6607 * Starting from a |for| keyword after an expression, parse the comprehension
6608 * tail completing this generator expression. Wrap the expression at kid in a
6609 * generator function that is immediately called to evaluate to the generator
6610 * iterator that is the value of this generator expression.
6612 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6613 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6614 * expression-statement node that constitutes the body of the |for| loop(s) in
6615 * the generator function.
6617 * Note how unlike Python, we do not evaluate the expression to the right of
6618 * the first |in| in the chain of |for| heads. Instead, a generator expression
6619 * is merely sugar for a generator function expression and its application.
6621 static JSParseNode *
6622 GeneratorExpr(JSParseNode *pn, JSParseNode *kid, JSTreeContext *tc)
6624 /* Initialize pn, connecting it to kid. */
6625 JS_ASSERT(pn->pn_arity == PN_UNARY);
6626 pn->pn_type = TOK_YIELD;
6627 pn->pn_op = JSOP_YIELD;
6628 pn->pn_parens = true;
6629 pn->pn_pos = kid->pn_pos;
6630 pn->pn_kid = kid;
6631 pn->pn_hidden = true;
6633 /* Make a new node for the desugared generator function. */
6634 JSParseNode *genfn = NewParseNode(PN_FUNC, tc);
6635 if (!genfn)
6636 return NULL;
6637 genfn->pn_type = TOK_FUNCTION;
6638 genfn->pn_op = JSOP_LAMBDA;
6639 JS_ASSERT(!genfn->pn_body);
6640 genfn->pn_dflags = PND_FUNARG;
6643 JSTreeContext gentc(tc->compiler);
6645 JSFunctionBox *funbox = EnterFunction(genfn, tc, &gentc);
6646 if (!funbox)
6647 return NULL;
6650 * We assume conservatively that any deoptimization flag in tc->flags
6651 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6652 * propagate these flags into genfn. For code simplicity we also do
6653 * not detect if the flags were only set in the kid and could be
6654 * removed from tc->flags.
6656 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6657 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6658 funbox->tcflags |= gentc.flags;
6659 genfn->pn_funbox = funbox;
6660 genfn->pn_blockid = gentc.bodyid;
6662 JSParseNode *body = ComprehensionTail(pn, tc->blockid(), &gentc);
6663 if (!body)
6664 return NULL;
6665 JS_ASSERT(!genfn->pn_body);
6666 genfn->pn_body = body;
6667 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6668 genfn->pn_pos.end = body->pn_pos.end = CURRENT_TOKEN(TS(tc->compiler)).pos.end;
6670 if (!LeaveFunction(genfn, &gentc, tc))
6671 return NULL;
6675 * Our result is a call expression that invokes the anonymous generator
6676 * function object.
6678 JSParseNode *result = NewParseNode(PN_LIST, tc);
6679 if (!result)
6680 return NULL;
6681 result->pn_type = TOK_LP;
6682 result->pn_op = JSOP_CALL;
6683 result->pn_pos.begin = genfn->pn_pos.begin;
6684 result->initList(genfn);
6685 return result;
6688 static const char js_generator_str[] = "generator";
6690 #endif /* JS_HAS_GENERATOR_EXPRS */
6691 #endif /* JS_HAS_GENERATORS */
6693 static JSBool
6694 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6695 JSParseNode *listNode)
6697 JSBool matched;
6699 ts->flags |= TSF_OPERAND;
6700 matched = js_MatchToken(cx, ts, TOK_RP);
6701 ts->flags &= ~TSF_OPERAND;
6702 if (!matched) {
6703 do {
6704 JSParseNode *argNode = AssignExpr(cx, ts, tc);
6705 if (!argNode)
6706 return JS_FALSE;
6707 #if JS_HAS_GENERATORS
6708 if (argNode->pn_type == TOK_YIELD &&
6709 !argNode->pn_parens &&
6710 js_PeekToken(cx, ts) == TOK_COMMA) {
6711 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6712 JSMSG_BAD_GENERATOR_SYNTAX,
6713 js_yield_str);
6714 return JS_FALSE;
6716 #endif
6717 #if JS_HAS_GENERATOR_EXPRS
6718 if (js_MatchToken(cx, ts, TOK_FOR)) {
6719 JSParseNode *pn = NewParseNode(PN_UNARY, tc);
6720 if (!pn)
6721 return JS_FALSE;
6722 argNode = GeneratorExpr(pn, argNode, tc);
6723 if (!argNode)
6724 return JS_FALSE;
6725 if (listNode->pn_count > 1 ||
6726 js_PeekToken(cx, ts) == TOK_COMMA) {
6727 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6728 JSMSG_BAD_GENERATOR_SYNTAX,
6729 js_generator_str);
6730 return JS_FALSE;
6733 #endif
6734 listNode->append(argNode);
6735 } while (js_MatchToken(cx, ts, TOK_COMMA));
6737 if (js_GetToken(cx, ts) != TOK_RP) {
6738 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6739 JSMSG_PAREN_AFTER_ARGS);
6740 return JS_FALSE;
6743 return JS_TRUE;
6746 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6747 static JSParseNode *
6748 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6750 while (pn->pn_type == TOK_RP)
6751 pn = pn->pn_kid;
6752 if (pn->pn_type == TOK_FUNCTION) {
6753 JS_ASSERT(pn->pn_arity == PN_FUNC);
6755 JSFunctionBox *funbox = pn->pn_funbox;
6756 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6757 if (!(funbox->tcflags & TCF_FUN_USES_ARGUMENTS))
6758 pn->pn_dflags &= ~PND_FUNARG;
6760 return pn;
6763 static JSParseNode *
6764 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6765 JSBool allowCallSyntax)
6767 JSParseNode *pn, *pn2, *pn3;
6768 JSTokenType tt;
6770 JS_CHECK_RECURSION(cx, return NULL);
6772 /* Check for new expression first. */
6773 ts->flags |= TSF_OPERAND;
6774 tt = js_GetToken(cx, ts);
6775 ts->flags &= ~TSF_OPERAND;
6776 if (tt == TOK_NEW) {
6777 pn = NewParseNode(PN_LIST, tc);
6778 if (!pn)
6779 return NULL;
6780 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
6781 if (!pn2)
6782 return NULL;
6783 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6784 pn->pn_op = JSOP_NEW;
6785 pn->initList(pn2);
6786 pn->pn_pos.begin = pn2->pn_pos.begin;
6788 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
6789 return NULL;
6790 if (pn->pn_count > ARGC_LIMIT) {
6791 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6792 JSMSG_TOO_MANY_CON_ARGS);
6793 return NULL;
6795 pn->pn_pos.end = pn->last()->pn_pos.end;
6796 } else {
6797 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6798 if (!pn)
6799 return NULL;
6801 if (pn->pn_type == TOK_ANYNAME ||
6802 pn->pn_type == TOK_AT ||
6803 pn->pn_type == TOK_DBLCOLON) {
6804 pn2 = NewOrRecycledNode(tc);
6805 if (!pn2)
6806 return NULL;
6807 pn2->pn_type = TOK_UNARYOP;
6808 pn2->pn_pos = pn->pn_pos;
6809 pn2->pn_op = JSOP_XMLNAME;
6810 pn2->pn_arity = PN_UNARY;
6811 pn2->pn_parens = false;
6812 pn2->pn_kid = pn;
6813 pn = pn2;
6817 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
6818 if (tt == TOK_DOT) {
6819 pn2 = NewNameNode(cx, ts, NULL, tc);
6820 if (!pn2)
6821 return NULL;
6822 #if JS_HAS_XML_SUPPORT
6823 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6824 tt = js_GetToken(cx, ts);
6825 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6826 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6827 if (!pn3)
6828 return NULL;
6830 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6831 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
6832 pn2->pn_op = JSOP_GETPROP;
6833 pn2->pn_expr = pn;
6834 pn2->pn_atom = pn3->pn_atom;
6835 RecycleTree(pn3, tc);
6836 } else {
6837 if (tt == TOK_LP) {
6838 pn2->pn_type = TOK_FILTER;
6839 pn2->pn_op = JSOP_FILTER;
6841 /* A filtering predicate is like a with statement. */
6842 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6843 } else if (TOKEN_TYPE_IS_XML(PN_TYPE(pn3))) {
6844 pn2->pn_type = TOK_LB;
6845 pn2->pn_op = JSOP_GETELEM;
6846 } else {
6847 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6848 JSMSG_NAME_AFTER_DOT);
6849 return NULL;
6851 pn2->pn_arity = PN_BINARY;
6852 pn2->pn_left = pn;
6853 pn2->pn_right = pn3;
6855 #else
6856 ts->flags |= TSF_KEYWORD_IS_NAME;
6857 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
6858 ts->flags &= ~TSF_KEYWORD_IS_NAME;
6859 pn2->pn_op = JSOP_GETPROP;
6860 pn2->pn_expr = pn;
6861 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
6862 #endif
6863 pn2->pn_pos.begin = pn->pn_pos.begin;
6864 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6865 #if JS_HAS_XML_SUPPORT
6866 } else if (tt == TOK_DBLDOT) {
6867 pn2 = NewParseNode(PN_BINARY, tc);
6868 if (!pn2)
6869 return NULL;
6870 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6871 tt = js_GetToken(cx, ts);
6872 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6873 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6874 if (!pn3)
6875 return NULL;
6876 tt = PN_TYPE(pn3);
6877 if (tt == TOK_NAME) {
6878 pn3->pn_type = TOK_STRING;
6879 pn3->pn_arity = PN_NULLARY;
6880 pn3->pn_op = JSOP_QNAMEPART;
6881 } else if (!TOKEN_TYPE_IS_XML(tt)) {
6882 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6883 JSMSG_NAME_AFTER_DOT);
6884 return NULL;
6886 pn2->pn_op = JSOP_DESCENDANTS;
6887 pn2->pn_left = pn;
6888 pn2->pn_right = pn3;
6889 pn2->pn_pos.begin = pn->pn_pos.begin;
6890 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6891 #endif
6892 } else if (tt == TOK_LB) {
6893 pn2 = NewParseNode(PN_BINARY, tc);
6894 if (!pn2)
6895 return NULL;
6896 pn3 = Expr(cx, ts, tc);
6897 if (!pn3)
6898 return NULL;
6900 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
6901 pn2->pn_pos.begin = pn->pn_pos.begin;
6902 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6905 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6906 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6907 * the interpreter from fast property access. However, if the
6908 * bracketed string is a uint32, we rewrite pn3 to be a number
6909 * instead of a string.
6911 do {
6912 if (pn3->pn_type == TOK_STRING) {
6913 jsuint index;
6915 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
6916 pn2->pn_type = TOK_DOT;
6917 pn2->pn_op = JSOP_GETPROP;
6918 pn2->pn_arity = PN_NAME;
6919 pn2->pn_expr = pn;
6920 pn2->pn_atom = pn3->pn_atom;
6921 break;
6923 pn3->pn_type = TOK_NUMBER;
6924 pn3->pn_op = JSOP_DOUBLE;
6925 pn3->pn_dval = index;
6927 pn2->pn_op = JSOP_GETELEM;
6928 pn2->pn_left = pn;
6929 pn2->pn_right = pn3;
6930 } while (0);
6931 } else if (allowCallSyntax && tt == TOK_LP) {
6932 pn2 = NewParseNode(PN_LIST, tc);
6933 if (!pn2)
6934 return NULL;
6935 pn2->pn_op = JSOP_CALL;
6937 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6938 pn = CheckForImmediatelyAppliedLambda(pn);
6939 if (pn->pn_op == JSOP_NAME) {
6940 if (pn->pn_atom == cx->runtime->atomState.evalAtom) {
6941 /* Select JSOP_EVAL and flag tc as heavyweight. */
6942 pn2->pn_op = JSOP_EVAL;
6943 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6945 } else if (pn->pn_op == JSOP_GETPROP) {
6946 if (pn->pn_atom == cx->runtime->atomState.applyAtom ||
6947 pn->pn_atom == cx->runtime->atomState.callAtom) {
6948 /* Select JSOP_APPLY given foo.apply(...). */
6949 pn2->pn_op = JSOP_APPLY;
6953 pn2->initList(pn);
6954 pn2->pn_pos.begin = pn->pn_pos.begin;
6956 if (!ArgumentList(cx, ts, tc, pn2))
6957 return NULL;
6958 if (pn2->pn_count > ARGC_LIMIT) {
6959 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6960 JSMSG_TOO_MANY_FUN_ARGS);
6961 return NULL;
6963 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6964 } else {
6965 js_UngetToken(ts);
6966 return pn;
6969 pn = pn2;
6971 if (tt == TOK_ERROR)
6972 return NULL;
6973 return pn;
6976 static JSParseNode *
6977 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6979 uintN oldflags;
6980 JSParseNode *pn;
6983 * Always accept the 'in' operator in a parenthesized expression,
6984 * where it's unambiguous, even if we might be parsing the init of a
6985 * for statement.
6987 oldflags = tc->flags;
6988 tc->flags &= ~TCF_IN_FOR_INIT;
6989 pn = Expr(cx, ts, tc);
6990 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
6991 return pn;
6994 #if JS_HAS_XML_SUPPORT
6996 static JSParseNode *
6997 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6999 JSParseNode *pn;
7001 pn = BracketedExpr(cx, ts, tc);
7002 if (!pn)
7003 return NULL;
7005 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7006 return pn;
7010 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7012 * AttributeIdentifier:
7013 * @ PropertySelector
7014 * @ QualifiedIdentifier
7015 * @ [ Expression ]
7017 * PropertySelector:
7018 * Identifier
7021 * QualifiedIdentifier:
7022 * PropertySelector :: PropertySelector
7023 * PropertySelector :: [ Expression ]
7025 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7027 * AttributeIdentifier:
7028 * @ QualifiedIdentifier
7029 * @ [ Expression ]
7031 * PropertySelector:
7032 * Identifier
7035 * QualifiedIdentifier:
7036 * PropertySelector :: PropertySelector
7037 * PropertySelector :: [ Expression ]
7038 * PropertySelector
7040 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7041 * for that rule to result in a name node, but ECMA-357 extends the grammar
7042 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7044 * QualifiedIdentifier:
7045 * PropertySelector QualifiedSuffix
7047 * QualifiedSuffix:
7048 * :: PropertySelector
7049 * :: [ Expression ]
7050 * /nothing/
7052 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7054 * PrimaryExpression:
7055 * Identifier QualifiedSuffix
7057 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7058 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7060 static JSParseNode *
7061 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7063 JSParseNode *pn;
7065 pn = NewParseNode(PN_NULLARY, tc);
7066 if (!pn)
7067 return NULL;
7068 if (pn->pn_type == TOK_STAR) {
7069 pn->pn_type = TOK_ANYNAME;
7070 pn->pn_op = JSOP_ANYNAME;
7071 pn->pn_atom = cx->runtime->atomState.starAtom;
7072 } else {
7073 JS_ASSERT(pn->pn_type == TOK_NAME);
7074 pn->pn_op = JSOP_QNAMEPART;
7075 pn->pn_arity = PN_NAME;
7076 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
7077 pn->pn_cookie = FREE_UPVAR_COOKIE;
7079 return pn;
7082 static JSParseNode *
7083 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7084 JSTreeContext *tc)
7086 JSParseNode *pn2, *pn3;
7087 JSTokenType tt;
7089 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
7090 pn2 = NewNameNode(cx, ts, NULL, tc);
7091 if (!pn2)
7092 return NULL;
7094 /* Left operand of :: must be evaluated if it is an identifier. */
7095 if (pn->pn_op == JSOP_QNAMEPART)
7096 pn->pn_op = JSOP_NAME;
7098 ts->flags |= TSF_KEYWORD_IS_NAME;
7099 tt = js_GetToken(cx, ts);
7100 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7101 if (tt == TOK_STAR || tt == TOK_NAME) {
7102 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7103 pn2->pn_op = JSOP_QNAMECONST;
7104 pn2->pn_pos.begin = pn->pn_pos.begin;
7105 pn2->pn_atom = (tt == TOK_STAR)
7106 ? cx->runtime->atomState.starAtom
7107 : CURRENT_TOKEN(ts).t_atom;
7108 pn2->pn_expr = pn;
7109 pn2->pn_cookie = FREE_UPVAR_COOKIE;
7110 return pn2;
7113 if (tt != TOK_LB) {
7114 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7115 JSMSG_SYNTAX_ERROR);
7116 return NULL;
7118 pn3 = EndBracketedExpr(cx, ts, tc);
7119 if (!pn3)
7120 return NULL;
7122 pn2->pn_op = JSOP_QNAME;
7123 pn2->pn_arity = PN_BINARY;
7124 pn2->pn_pos.begin = pn->pn_pos.begin;
7125 pn2->pn_pos.end = pn3->pn_pos.end;
7126 pn2->pn_left = pn;
7127 pn2->pn_right = pn3;
7128 return pn2;
7131 static JSParseNode *
7132 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7134 JSParseNode *pn;
7136 pn = PropertySelector(cx, ts, tc);
7137 if (!pn)
7138 return NULL;
7139 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7140 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7141 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7142 pn = QualifiedSuffix(cx, ts, pn, tc);
7144 return pn;
7147 static JSParseNode *
7148 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7150 JSParseNode *pn, *pn2;
7151 JSTokenType tt;
7153 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
7154 pn = NewParseNode(PN_UNARY, tc);
7155 if (!pn)
7156 return NULL;
7157 pn->pn_op = JSOP_TOATTRNAME;
7158 ts->flags |= TSF_KEYWORD_IS_NAME;
7159 tt = js_GetToken(cx, ts);
7160 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7161 if (tt == TOK_STAR || tt == TOK_NAME) {
7162 pn2 = QualifiedIdentifier(cx, ts, tc);
7163 } else if (tt == TOK_LB) {
7164 pn2 = EndBracketedExpr(cx, ts, tc);
7165 } else {
7166 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7167 JSMSG_SYNTAX_ERROR);
7168 return NULL;
7170 if (!pn2)
7171 return NULL;
7172 pn->pn_kid = pn2;
7173 return pn;
7177 * Make a TOK_LC unary node whose pn_kid is an expression.
7179 static JSParseNode *
7180 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
7182 JSParseNode *pn, *pn2;
7183 uintN oldflags;
7185 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
7186 pn = NewParseNode(PN_UNARY, tc);
7187 if (!pn)
7188 return NULL;
7191 * Turn off XML tag mode, but don't restore it after parsing this braced
7192 * expression. Instead, simply restore ts's old flags. This is required
7193 * because XMLExpr is called both from within a tag, and from within text
7194 * contained in an element, but outside of any start, end, or point tag.
7196 oldflags = ts->flags;
7197 ts->flags = oldflags & ~TSF_XMLTAGMODE;
7198 pn2 = Expr(cx, ts, tc);
7199 if (!pn2)
7200 return NULL;
7202 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7203 ts->flags = oldflags;
7204 pn->pn_kid = pn2;
7205 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7206 return pn;
7210 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7211 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7212 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7213 * child of a container tag.
7215 static JSParseNode *
7216 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7218 JSParseNode *pn;
7219 JSToken *tp;
7221 pn = NewParseNode(PN_NULLARY, tc);
7222 if (!pn)
7223 return NULL;
7224 tp = &CURRENT_TOKEN(ts);
7225 pn->pn_op = tp->t_op;
7226 pn->pn_atom = tp->t_atom;
7227 if (tp->type == TOK_XMLPI)
7228 pn->pn_atom2 = tp->t_atom2;
7229 return pn;
7233 * Parse the productions:
7235 * XMLNameExpr:
7236 * XMLName XMLNameExpr?
7237 * { Expr } XMLNameExpr?
7239 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7240 * a list of names and/or expressions, a single expression, or a single name.
7241 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7242 * will be TOK_LC.
7244 static JSParseNode *
7245 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7247 JSParseNode *pn, *pn2, *list;
7248 JSTokenType tt;
7250 pn = list = NULL;
7251 do {
7252 tt = CURRENT_TOKEN(ts).type;
7253 if (tt == TOK_LC) {
7254 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7255 if (!pn2)
7256 return NULL;
7257 } else {
7258 JS_ASSERT(tt == TOK_XMLNAME);
7259 pn2 = XMLAtomNode(cx, ts, tc);
7260 if (!pn2)
7261 return NULL;
7264 if (!pn) {
7265 pn = pn2;
7266 } else {
7267 if (!list) {
7268 list = NewParseNode(PN_LIST, tc);
7269 if (!list)
7270 return NULL;
7271 list->pn_type = TOK_XMLNAME;
7272 list->pn_pos.begin = pn->pn_pos.begin;
7273 list->initList(pn);
7274 list->pn_xflags = PNX_CANTFOLD;
7275 pn = list;
7277 pn->pn_pos.end = pn2->pn_pos.end;
7278 pn->append(pn2);
7280 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
7282 js_UngetToken(ts);
7283 return pn;
7287 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7288 * at compile time into a JSXML tree.
7290 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7291 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7292 : (pn)->pn_type != TOK_LC)
7295 * Parse the productions:
7297 * XMLTagContent:
7298 * XMLNameExpr
7299 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7300 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7302 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7303 * produces a list of name and attribute values and/or braced expressions, a
7304 * single expression, or a single name.
7306 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7307 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7308 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7309 * we parsed exactly one expression.
7311 static JSParseNode *
7312 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7313 JSTokenType tagtype, JSAtom **namep)
7315 JSParseNode *pn, *pn2, *list;
7316 JSTokenType tt;
7318 pn = XMLNameExpr(cx, ts, tc);
7319 if (!pn)
7320 return NULL;
7321 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7322 list = NULL;
7324 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
7325 tt = js_GetToken(cx, ts);
7326 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7327 js_UngetToken(ts);
7328 break;
7331 pn2 = XMLNameExpr(cx, ts, tc);
7332 if (!pn2)
7333 return NULL;
7334 if (!list) {
7335 list = NewParseNode(PN_LIST, tc);
7336 if (!list)
7337 return NULL;
7338 list->pn_type = tagtype;
7339 list->pn_pos.begin = pn->pn_pos.begin;
7340 list->initList(pn);
7341 pn = list;
7343 pn->append(pn2);
7344 if (!XML_FOLDABLE(pn2))
7345 pn->pn_xflags |= PNX_CANTFOLD;
7347 js_MatchToken(cx, ts, TOK_XMLSPACE);
7348 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7349 js_MatchToken(cx, ts, TOK_XMLSPACE);
7351 tt = js_GetToken(cx, ts);
7352 if (tt == TOK_XMLATTR) {
7353 pn2 = XMLAtomNode(cx, ts, tc);
7354 } else if (tt == TOK_LC) {
7355 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7356 pn->pn_xflags |= PNX_CANTFOLD;
7357 } else {
7358 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7359 JSMSG_BAD_XML_ATTR_VALUE);
7360 return NULL;
7362 if (!pn2)
7363 return NULL;
7364 pn->pn_pos.end = pn2->pn_pos.end;
7365 pn->append(pn2);
7368 return pn;
7371 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7372 JS_BEGIN_MACRO \
7373 if ((tt) <= TOK_EOF) { \
7374 if ((tt) == TOK_EOF) { \
7375 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7376 JSMSG_END_OF_XML_SOURCE); \
7378 return result; \
7380 JS_END_MACRO
7382 static JSParseNode *
7383 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7384 JSBool allowList);
7387 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7388 * that opens the end tag for the container.
7390 static JSBool
7391 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7392 JSTreeContext *tc)
7394 JSTokenType tt;
7395 JSParseNode *pn2;
7396 JSAtom *textAtom;
7398 ts->flags &= ~TSF_XMLTAGMODE;
7399 for (;;) {
7400 ts->flags |= TSF_XMLTEXTMODE;
7401 tt = js_GetToken(cx, ts);
7402 ts->flags &= ~TSF_XMLTEXTMODE;
7403 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7405 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7406 textAtom = CURRENT_TOKEN(ts).t_atom;
7407 if (textAtom) {
7408 /* Non-zero-length XML text scanned. */
7409 pn2 = XMLAtomNode(cx, ts, tc);
7410 if (!pn2)
7411 return JS_FALSE;
7412 pn->pn_pos.end = pn2->pn_pos.end;
7413 pn->append(pn2);
7416 ts->flags |= TSF_OPERAND;
7417 tt = js_GetToken(cx, ts);
7418 ts->flags &= ~TSF_OPERAND;
7419 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7420 if (tt == TOK_XMLETAGO)
7421 break;
7423 if (tt == TOK_LC) {
7424 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
7425 pn->pn_xflags |= PNX_CANTFOLD;
7426 } else if (tt == TOK_XMLSTAGO) {
7427 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
7428 if (pn2) {
7429 pn2->pn_xflags &= ~PNX_XMLROOT;
7430 pn->pn_xflags |= pn2->pn_xflags;
7432 } else {
7433 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7434 tt == TOK_XMLPI);
7435 pn2 = XMLAtomNode(cx, ts, tc);
7437 if (!pn2)
7438 return JS_FALSE;
7439 pn->pn_pos.end = pn2->pn_pos.end;
7440 pn->append(pn2);
7443 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
7444 ts->flags |= TSF_XMLTAGMODE;
7445 return JS_TRUE;
7449 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7451 static JSParseNode *
7452 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7453 JSBool allowList)
7455 JSParseNode *pn, *pn2, *list;
7456 JSTokenType tt;
7457 JSAtom *startAtom, *endAtom;
7459 JS_CHECK_RECURSION(cx, return NULL);
7461 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
7462 pn = NewParseNode(PN_LIST, tc);
7463 if (!pn)
7464 return NULL;
7466 ts->flags |= TSF_XMLTAGMODE;
7467 tt = js_GetToken(cx, ts);
7468 if (tt == TOK_ERROR)
7469 return NULL;
7471 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7473 * XMLElement. Append the tag and its contents, if any, to pn.
7475 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
7476 if (!pn2)
7477 return NULL;
7478 js_MatchToken(cx, ts, TOK_XMLSPACE);
7480 tt = js_GetToken(cx, ts);
7481 if (tt == TOK_XMLPTAGC) {
7482 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7483 if (pn2->pn_type == TOK_XMLSTAGO) {
7484 pn->makeEmpty();
7485 RecycleTree(pn, tc);
7486 pn = pn2;
7487 } else {
7488 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7489 pn2->pn_type == TOK_LC);
7490 pn->initList(pn2);
7491 if (!XML_FOLDABLE(pn2))
7492 pn->pn_xflags |= PNX_CANTFOLD;
7494 pn->pn_type = TOK_XMLPTAGC;
7495 pn->pn_xflags |= PNX_XMLROOT;
7496 } else {
7497 /* We had better have a tag-close (>) at this point. */
7498 if (tt != TOK_XMLTAGC) {
7499 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7500 JSMSG_BAD_XML_TAG_SYNTAX);
7501 return NULL;
7503 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7505 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7506 if (pn2->pn_type != TOK_XMLSTAGO) {
7507 pn->initList(pn2);
7508 if (!XML_FOLDABLE(pn2))
7509 pn->pn_xflags |= PNX_CANTFOLD;
7510 pn2 = pn;
7511 pn = NewParseNode(PN_LIST, tc);
7512 if (!pn)
7513 return NULL;
7516 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7517 pn->pn_type = TOK_XMLELEM;
7518 pn->pn_pos.begin = pn2->pn_pos.begin;
7519 pn->initList(pn2);
7520 if (!XML_FOLDABLE(pn2))
7521 pn->pn_xflags |= PNX_CANTFOLD;
7522 pn->pn_xflags |= PNX_XMLROOT;
7524 /* Get element contents and delimiting end-tag-open sequence. */
7525 if (!XMLElementContent(cx, ts, pn, tc))
7526 return NULL;
7528 tt = js_GetToken(cx, ts);
7529 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7530 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7531 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7532 JSMSG_BAD_XML_TAG_SYNTAX);
7533 return NULL;
7536 /* Parse end tag; check mismatch at compile-time if we can. */
7537 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
7538 if (!pn2)
7539 return NULL;
7540 if (pn2->pn_type == TOK_XMLETAGO) {
7541 /* Oops, end tag has attributes! */
7542 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7543 JSMSG_BAD_XML_TAG_SYNTAX);
7544 return NULL;
7546 if (endAtom && startAtom && endAtom != startAtom) {
7547 JSString *str = ATOM_TO_STRING(startAtom);
7549 /* End vs. start tag name mismatch: point to the tag name. */
7550 js_ReportCompileErrorNumber(cx, ts, pn2,
7551 JSREPORT_UC | JSREPORT_ERROR,
7552 JSMSG_XML_TAG_NAME_MISMATCH,
7553 str->chars());
7554 return NULL;
7557 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7558 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7559 list = NewParseNode(PN_LIST, tc);
7560 if (!list)
7561 return NULL;
7562 list->pn_type = TOK_XMLETAGO;
7563 list->initList(pn2);
7564 pn->append(list);
7565 if (!XML_FOLDABLE(pn2)) {
7566 list->pn_xflags |= PNX_CANTFOLD;
7567 pn->pn_xflags |= PNX_CANTFOLD;
7570 js_MatchToken(cx, ts, TOK_XMLSPACE);
7571 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7574 /* Set pn_op now that pn has been updated to its final value. */
7575 pn->pn_op = JSOP_TOXML;
7576 } else if (allowList && tt == TOK_XMLTAGC) {
7577 /* XMLList Initialiser. */
7578 pn->pn_type = TOK_XMLLIST;
7579 pn->pn_op = JSOP_TOXMLLIST;
7580 pn->makeEmpty();
7581 pn->pn_xflags |= PNX_XMLROOT;
7582 if (!XMLElementContent(cx, ts, pn, tc))
7583 return NULL;
7585 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7586 } else {
7587 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7588 JSMSG_BAD_XML_NAME_SYNTAX);
7589 return NULL;
7592 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7593 ts->flags &= ~TSF_XMLTAGMODE;
7594 return pn;
7597 static JSParseNode *
7598 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7599 JSBool allowList)
7601 uint32 oldopts;
7602 JSParseNode *pn;
7605 * Force XML support to be enabled so that comments and CDATA literals
7606 * are recognized, instead of <! followed by -- starting an HTML comment
7607 * to end of line (used in script tags to hide content from old browsers
7608 * that don't recognize <script>).
7610 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
7611 pn = XMLElementOrList(cx, ts, tc, allowList);
7612 JS_SetOptions(cx, oldopts);
7613 return pn;
7616 JSParseNode *
7617 JSCompiler::parseXMLText(JSObject *chain, bool allowList)
7620 * Push a compiler frame if we have no frames, or if the top frame is a
7621 * lightweight function activation, or if its scope chain doesn't match
7622 * the one passed to us.
7624 JSTreeContext tc(this);
7625 tc.scopeChain = chain;
7627 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7628 TS(this)->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
7629 JSTokenType tt = js_GetToken(context, TS(this));
7630 TS(this)->flags &= ~TSF_OPERAND;
7632 JSParseNode *pn;
7633 if (tt != TOK_XMLSTAGO) {
7634 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
7635 JSMSG_BAD_XML_MARKUP);
7636 pn = NULL;
7637 } else {
7638 pn = XMLElementOrListRoot(context, TS(this), &tc, allowList);
7641 TS(this)->flags &= ~TSF_XMLONLYMODE;
7642 return pn;
7645 #endif /* JS_HAS_XMLSUPPORT */
7647 #if JS_HAS_BLOCK_SCOPE
7649 * Check whether blockid is an active scoping statement in tc. This code is
7650 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7651 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7652 * and let blocks and expressions (not let declarations).
7654 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7655 * due to hoisting, let in a for loop head, let block, or let expression acts
7656 * like Scheme's let: initializers are evaluated without the new let bindings
7657 * being in scope.
7659 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7660 * bindings push on the front of the tc->decls JSAtomList (either the singular
7661 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7662 * scope bindings of the same name.
7664 * This simplifies binding lookup code at the price of a linear search here,
7665 * but only if code uses let (var predominates), and even then this function's
7666 * loop iterates more than once only in crazy cases.
7668 static inline bool
7669 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7671 if (blockid > tc->blockid())
7672 return false;
7673 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7674 if (stmt->blockid == blockid)
7675 return true;
7677 return false;
7679 #endif
7681 static JSParseNode *
7682 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7683 JSTokenType tt, JSBool afterDot)
7685 JSParseNode *pn, *pn2, *pn3;
7686 JSOp op;
7688 JS_CHECK_RECURSION(cx, return NULL);
7690 #if JS_HAS_GETTER_SETTER
7691 if (tt == TOK_NAME) {
7692 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
7693 if (tt == TOK_ERROR)
7694 return NULL;
7696 #endif
7698 switch (tt) {
7699 case TOK_FUNCTION:
7700 #if JS_HAS_XML_SUPPORT
7701 ts->flags |= TSF_KEYWORD_IS_NAME;
7702 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7703 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7704 pn2 = NewParseNode(PN_NULLARY, tc);
7705 if (!pn2)
7706 return NULL;
7707 pn2->pn_type = TOK_FUNCTION;
7708 pn = QualifiedSuffix(cx, ts, pn2, tc);
7709 if (!pn)
7710 return NULL;
7711 break;
7713 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7714 #endif
7715 pn = FunctionExpr(cx, ts, tc);
7716 if (!pn)
7717 return NULL;
7718 break;
7720 case TOK_LB:
7722 JSBool matched;
7723 jsuint index;
7725 pn = NewParseNode(PN_LIST, tc);
7726 if (!pn)
7727 return NULL;
7728 pn->pn_type = TOK_RB;
7729 pn->pn_op = JSOP_NEWINIT;
7730 pn->makeEmpty();
7732 #if JS_HAS_GENERATORS
7733 pn->pn_blockid = tc->blockidGen;
7734 #endif
7736 ts->flags |= TSF_OPERAND;
7737 matched = js_MatchToken(cx, ts, TOK_RB);
7738 ts->flags &= ~TSF_OPERAND;
7739 if (!matched) {
7740 for (index = 0; ; index++) {
7741 if (index == ARRAY_INIT_LIMIT) {
7742 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7743 JSMSG_ARRAY_INIT_TOO_BIG);
7744 return NULL;
7747 ts->flags |= TSF_OPERAND;
7748 tt = js_PeekToken(cx, ts);
7749 ts->flags &= ~TSF_OPERAND;
7750 if (tt == TOK_RB) {
7751 pn->pn_xflags |= PNX_ENDCOMMA;
7752 break;
7755 if (tt == TOK_COMMA) {
7756 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7757 js_MatchToken(cx, ts, TOK_COMMA);
7758 pn2 = NewParseNode(PN_NULLARY, tc);
7759 pn->pn_xflags |= PNX_HOLEY;
7760 } else {
7761 pn2 = AssignExpr(cx, ts, tc);
7763 if (!pn2)
7764 return NULL;
7765 pn->append(pn2);
7767 if (tt != TOK_COMMA) {
7768 /* If we didn't already match TOK_COMMA in above case. */
7769 if (!js_MatchToken(cx, ts, TOK_COMMA))
7770 break;
7774 #if JS_HAS_GENERATORS
7776 * At this point, (index == 0 && pn->pn_count != 0) implies one
7777 * element initialiser was parsed.
7779 * An array comprehension of the form:
7781 * [i * j for (i in o) for (j in p) if (i != j)]
7783 * translates to roughly the following let expression:
7785 * let (array = new Array, i, j) {
7786 * for (i in o) let {
7787 * for (j in p)
7788 * if (i != j)
7789 * array.push(i * j)
7791 * array
7794 * where array is a nameless block-local variable. The "roughly"
7795 * means that an implementation may optimize away the array.push.
7796 * An array comprehension opens exactly one block scope, no matter
7797 * how many for heads it contains.
7799 * Each let () {...} or for (let ...) ... compiles to:
7801 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7803 * where <o> is a literal object representing the block scope,
7804 * with <n> properties, naming each var declared in the block.
7806 * Each var declaration in a let-block binds a name in <o> at
7807 * compile time, and allocates a slot on the operand stack at
7808 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7809 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7810 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7811 * local slot's stack index from fp->spbase.
7813 * The array comprehension iteration step, array.push(i * j) in
7814 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7815 * where <array> is the index of array's stack slot.
7817 if (index == 0 &&
7818 pn->pn_count != 0 &&
7819 js_MatchToken(cx, ts, TOK_FOR)) {
7820 JSParseNode *pnexp, *pntop;
7822 /* Relabel pn as an array comprehension node. */
7823 pn->pn_type = TOK_ARRAYCOMP;
7826 * Remove the comprehension expression from pn's linked list
7827 * and save it via pnexp. We'll re-install it underneath the
7828 * ARRAYPUSH node after we parse the rest of the comprehension.
7830 pnexp = pn->last();
7831 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7832 pn->pn_tail = (--pn->pn_count == 1)
7833 ? &pn->pn_head->pn_next
7834 : &pn->pn_head;
7835 *pn->pn_tail = NULL;
7837 pntop = ComprehensionTail(pnexp, pn->pn_blockid, tc,
7838 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7839 if (!pntop)
7840 return NULL;
7841 pn->append(pntop);
7843 #endif /* JS_HAS_GENERATORS */
7845 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7847 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7848 return pn;
7851 case TOK_LC:
7853 JSBool afterComma;
7854 JSParseNode *pnval;
7856 pn = NewParseNode(PN_LIST, tc);
7857 if (!pn)
7858 return NULL;
7859 pn->pn_type = TOK_RC;
7860 pn->pn_op = JSOP_NEWINIT;
7861 pn->makeEmpty();
7863 afterComma = JS_FALSE;
7864 for (;;) {
7865 ts->flags |= TSF_KEYWORD_IS_NAME;
7866 tt = js_GetToken(cx, ts);
7867 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7868 switch (tt) {
7869 case TOK_NUMBER:
7870 pn3 = NewParseNode(PN_NULLARY, tc);
7871 if (pn3)
7872 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
7873 break;
7874 case TOK_NAME:
7875 #if JS_HAS_GETTER_SETTER
7877 JSAtom *atom;
7879 atom = CURRENT_TOKEN(ts).t_atom;
7880 if (atom == cx->runtime->atomState.getAtom)
7881 op = JSOP_GETTER;
7882 else if (atom == cx->runtime->atomState.setAtom)
7883 op = JSOP_SETTER;
7884 else
7885 goto property_name;
7887 ts->flags |= TSF_KEYWORD_IS_NAME;
7888 tt = js_GetToken(cx, ts);
7889 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7890 if (tt != TOK_NAME) {
7891 js_UngetToken(ts);
7892 goto property_name;
7894 pn3 = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
7895 if (!pn3)
7896 return NULL;
7898 /* We have to fake a 'function' token here. */
7899 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
7900 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
7901 pn2 = FunctionExpr(cx, ts, tc);
7902 pn2 = NewBinary(TOK_COLON, op, pn3, pn2, tc);
7903 goto skip;
7905 property_name:
7906 #endif
7907 case TOK_STRING:
7908 pn3 = NewParseNode(PN_NULLARY, tc);
7909 if (pn3)
7910 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
7911 break;
7912 case TOK_RC:
7913 if (afterComma &&
7914 !js_ReportCompileErrorNumber(cx, ts, NULL,
7915 JSREPORT_WARNING |
7916 JSREPORT_STRICT,
7917 JSMSG_TRAILING_COMMA)) {
7918 return NULL;
7920 goto end_obj_init;
7921 default:
7922 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7923 JSMSG_BAD_PROP_ID);
7924 return NULL;
7927 tt = js_GetToken(cx, ts);
7928 #if JS_HAS_GETTER_SETTER
7929 if (tt == TOK_NAME) {
7930 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
7931 if (tt == TOK_ERROR)
7932 return NULL;
7934 #endif
7936 if (tt != TOK_COLON) {
7937 #if JS_HAS_DESTRUCTURING_SHORTHAND
7938 if (tt != TOK_COMMA && tt != TOK_RC) {
7939 #endif
7940 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7941 JSMSG_COLON_AFTER_ID);
7942 return NULL;
7943 #if JS_HAS_DESTRUCTURING_SHORTHAND
7947 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7948 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7950 js_UngetToken(ts);
7951 pn->pn_xflags |= PNX_DESTRUCT;
7952 pnval = pn3;
7953 if (pnval->pn_type == TOK_NAME) {
7954 pnval->pn_arity = PN_NAME;
7955 InitNameNodeCommon(pnval, tc);
7957 op = JSOP_NOP;
7958 #endif
7959 } else {
7960 op = CURRENT_TOKEN(ts).t_op;
7961 pnval = AssignExpr(cx, ts, tc);
7964 pn2 = NewBinary(TOK_COLON, op, pn3, pnval, tc);
7965 #if JS_HAS_GETTER_SETTER
7966 skip:
7967 #endif
7968 if (!pn2)
7969 return NULL;
7970 pn->append(pn2);
7972 tt = js_GetToken(cx, ts);
7973 if (tt == TOK_RC)
7974 goto end_obj_init;
7975 if (tt != TOK_COMMA) {
7976 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7977 JSMSG_CURLY_AFTER_LIST);
7978 return NULL;
7980 afterComma = JS_TRUE;
7983 end_obj_init:
7984 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7985 return pn;
7988 #if JS_HAS_BLOCK_SCOPE
7989 case TOK_LET:
7990 pn = LetBlock(cx, ts, tc, JS_FALSE);
7991 if (!pn)
7992 return NULL;
7993 break;
7994 #endif
7996 #if JS_HAS_SHARP_VARS
7997 case TOK_DEFSHARP:
7998 pn = NewParseNode(PN_UNARY, tc);
7999 if (!pn)
8000 return NULL;
8001 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8002 ts->flags |= TSF_OPERAND;
8003 tt = js_GetToken(cx, ts);
8004 ts->flags &= ~TSF_OPERAND;
8005 if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
8006 #if JS_HAS_XML_SUPPORT
8007 tt == TOK_STAR || tt == TOK_AT ||
8008 tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
8009 #endif
8010 tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
8011 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8012 JSMSG_BAD_SHARP_VAR_DEF);
8013 return NULL;
8015 pn->pn_kid = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
8016 if (!pn->pn_kid)
8017 return NULL;
8018 tc->flags |= TCF_HAS_SHARPS;
8019 break;
8021 case TOK_USESHARP:
8022 /* Check for forward/dangling references at runtime, to allow eval. */
8023 pn = NewParseNode(PN_NULLARY, tc);
8024 if (!pn)
8025 return NULL;
8026 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8027 tc->flags |= TCF_HAS_SHARPS;
8028 break;
8029 #endif /* JS_HAS_SHARP_VARS */
8031 case TOK_LP:
8033 JSBool genexp;
8035 pn = ParenExpr(cx, ts, tc, NULL, &genexp);
8036 if (!pn)
8037 return NULL;
8038 pn->pn_parens = true;
8039 if (!genexp)
8040 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8041 break;
8044 #if JS_HAS_XML_SUPPORT
8045 case TOK_STAR:
8046 pn = QualifiedIdentifier(cx, ts, tc);
8047 if (!pn)
8048 return NULL;
8049 break;
8051 case TOK_AT:
8052 pn = AttributeIdentifier(cx, ts, tc);
8053 if (!pn)
8054 return NULL;
8055 break;
8057 case TOK_XMLSTAGO:
8058 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
8059 if (!pn)
8060 return NULL;
8061 break;
8062 #endif /* JS_HAS_XML_SUPPORT */
8064 case TOK_STRING:
8065 #if JS_HAS_SHARP_VARS
8066 /* FALL THROUGH */
8067 #endif
8069 #if JS_HAS_XML_SUPPORT
8070 case TOK_XMLCDATA:
8071 case TOK_XMLCOMMENT:
8072 case TOK_XMLPI:
8073 #endif
8074 pn = NewParseNode(PN_NULLARY, tc);
8075 if (!pn)
8076 return NULL;
8077 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
8078 #if JS_HAS_XML_SUPPORT
8079 if (tt == TOK_XMLPI)
8080 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
8081 else
8082 #endif
8083 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8084 break;
8086 case TOK_NAME:
8087 pn = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
8088 if (!pn)
8089 return NULL;
8090 JS_ASSERT(CURRENT_TOKEN(ts).t_op == JSOP_NAME);
8091 pn->pn_op = JSOP_NAME;
8093 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8094 pn->pn_atom == cx->runtime->atomState.argumentsAtom) {
8096 * Flag arguments usage so we can avoid unsafe optimizations such
8097 * as formal parameter assignment analysis (because of the hated
8098 * feature whereby arguments alias formals). We do this even for
8099 * a reference of the form foo.arguments, which ancient code may
8100 * still use instead of arguments (more hate).
8102 NoteArgumentsUse(tc);
8105 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8106 * to do this work (new rule for the emitter to count on).
8108 if (!afterDot && !(ts->flags & TSF_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8109 pn->pn_op = JSOP_ARGUMENTS;
8110 pn->pn_dflags |= PND_BOUND;
8112 } else if ((!afterDot
8113 #if JS_HAS_XML_SUPPORT
8114 || js_PeekToken(cx, ts) == TOK_DBLCOLON
8115 #endif
8116 ) && !(ts->flags & TSF_DESTRUCTURING)) {
8117 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8118 if (!stmt || stmt->type != STMT_WITH) {
8119 JSDefinition *dn;
8121 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8122 if (ale) {
8123 dn = ALE_DEFN(ale);
8124 #if JS_HAS_BLOCK_SCOPE
8126 * Skip out-of-scope let bindings along an ALE list or hash
8127 * chain. These can happen due to |let (x = x) x| block and
8128 * expression bindings, where the x on the right of = comes
8129 * from an outer scope. See bug 496532.
8131 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8132 do {
8133 ale = ALE_NEXT(ale);
8134 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8135 if (!ale)
8136 break;
8137 dn = ALE_DEFN(ale);
8139 #endif
8142 if (ale) {
8143 dn = ALE_DEFN(ale);
8144 } else {
8145 ale = tc->lexdeps.lookup(pn->pn_atom);
8146 if (ale) {
8147 dn = ALE_DEFN(ale);
8148 } else {
8150 * No definition before this use in any lexical scope.
8151 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8152 * new node for the forward-referenced definition. This
8153 * placeholder definition node will be adopted when we
8154 * parse the real defining declaration form, or left as
8155 * a free variable definition if we never see the real
8156 * definition.
8158 ale = MakePlaceholder(pn, tc);
8159 if (!ale)
8160 return NULL;
8161 dn = ALE_DEFN(ale);
8164 * In case this is a forward reference to a function,
8165 * we pessimistically set PND_FUNARG if the next token
8166 * is not a left parenthesis.
8168 * If the definition eventually parsed into dn is not a
8169 * function, this flag won't hurt, and if we do parse a
8170 * function with pn's name, then the PND_FUNARG flag is
8171 * necessary for safe cx->display-based optimization of
8172 * the closure's static link.
8174 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8175 JS_ASSERT(dn->pn_op == JSOP_NOP);
8176 if (js_PeekToken(cx, ts) != TOK_LP)
8177 dn->pn_dflags |= PND_FUNARG;
8181 JS_ASSERT(dn->pn_defn);
8182 LinkUseToDef(pn, dn, tc);
8184 /* Here we handle the backward function reference case. */
8185 if (js_PeekToken(cx, ts) != TOK_LP)
8186 dn->pn_dflags |= PND_FUNARG;
8188 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8192 #if JS_HAS_XML_SUPPORT
8193 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
8194 if (afterDot) {
8195 JSString *str;
8198 * Here PrimaryExpr is called after . or .. followed by a name
8199 * followed by ::. This is the only case where a keyword after
8200 * . or .. is not treated as a property name.
8202 str = ATOM_TO_STRING(pn->pn_atom);
8203 tt = js_CheckKeyword(str->chars(), str->length());
8204 if (tt == TOK_FUNCTION) {
8205 pn->pn_arity = PN_NULLARY;
8206 pn->pn_type = TOK_FUNCTION;
8207 } else if (tt != TOK_EOF) {
8208 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8209 JSMSG_KEYWORD_NOT_NS);
8210 return NULL;
8213 pn = QualifiedSuffix(cx, ts, pn, tc);
8214 if (!pn)
8215 return NULL;
8217 #endif
8218 break;
8220 case TOK_REGEXP:
8222 JSObject *obj;
8224 pn = NewParseNode(PN_NULLARY, tc);
8225 if (!pn)
8226 return NULL;
8228 /* Token stream ensures that tokenbuf is NUL-terminated. */
8229 JS_ASSERT(*ts->tokenbuf.ptr == (jschar) 0);
8230 obj = js_NewRegExpObject(cx, ts,
8231 ts->tokenbuf.base,
8232 ts->tokenbuf.ptr - ts->tokenbuf.base,
8233 CURRENT_TOKEN(ts).t_reflags);
8234 if (!obj)
8235 return NULL;
8236 if (!(tc->flags & TCF_COMPILE_N_GO)) {
8237 STOBJ_CLEAR_PARENT(obj);
8238 STOBJ_CLEAR_PROTO(obj);
8241 pn->pn_objbox = tc->compiler->newObjectBox(obj);
8242 if (!pn->pn_objbox)
8243 return NULL;
8245 pn->pn_op = JSOP_REGEXP;
8246 break;
8249 case TOK_NUMBER:
8250 pn = NewParseNode(PN_NULLARY, tc);
8251 if (!pn)
8252 return NULL;
8253 pn->pn_op = JSOP_DOUBLE;
8254 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
8255 break;
8257 case TOK_PRIMARY:
8258 pn = NewParseNode(PN_NULLARY, tc);
8259 if (!pn)
8260 return NULL;
8261 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8262 break;
8264 case TOK_ERROR:
8265 /* The scanner or one of its subroutines reported the error. */
8266 return NULL;
8268 default:
8269 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8270 JSMSG_SYNTAX_ERROR);
8271 return NULL;
8273 return pn;
8276 static JSParseNode *
8277 ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
8278 JSParseNode *pn1, JSBool *genexp)
8280 JSTokenPtr begin;
8281 JSParseNode *pn;
8283 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
8284 begin = CURRENT_TOKEN(ts).pos.begin;
8286 if (genexp)
8287 *genexp = JS_FALSE;
8288 pn = BracketedExpr(cx, ts, tc);
8289 if (!pn)
8290 return NULL;
8292 #if JS_HAS_GENERATOR_EXPRS
8293 if (js_MatchToken(cx, ts, TOK_FOR)) {
8294 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
8295 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
8296 JSMSG_BAD_GENERATOR_SYNTAX,
8297 js_yield_str);
8298 return NULL;
8300 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
8301 js_ReportCompileErrorNumber(cx, ts, pn->last(), JSREPORT_ERROR,
8302 JSMSG_BAD_GENERATOR_SYNTAX,
8303 js_generator_str);
8304 return NULL;
8306 if (!pn1) {
8307 pn1 = NewParseNode(PN_UNARY, tc);
8308 if (!pn1)
8309 return NULL;
8311 pn = GeneratorExpr(pn1, pn, tc);
8312 if (!pn)
8313 return NULL;
8314 pn->pn_pos.begin = begin;
8315 if (genexp) {
8316 if (js_GetToken(cx, ts) != TOK_RP) {
8317 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8318 JSMSG_BAD_GENERATOR_SYNTAX,
8319 js_generator_str);
8320 return NULL;
8322 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8323 *genexp = JS_TRUE;
8326 #endif /* JS_HAS_GENERATOR_EXPRS */
8328 return pn;
8332 * Fold from one constant type to another.
8333 * XXX handles only strings and numbers for now
8335 static JSBool
8336 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
8338 if (PN_TYPE(pn) != type) {
8339 switch (type) {
8340 case TOK_NUMBER:
8341 if (pn->pn_type == TOK_STRING) {
8342 jsdouble d;
8343 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8344 return JS_FALSE;
8345 pn->pn_dval = d;
8346 pn->pn_type = TOK_NUMBER;
8347 pn->pn_op = JSOP_DOUBLE;
8349 break;
8351 case TOK_STRING:
8352 if (pn->pn_type == TOK_NUMBER) {
8353 JSString *str = js_NumberToString(cx, pn->pn_dval);
8354 if (!str)
8355 return JS_FALSE;
8356 pn->pn_atom = js_AtomizeString(cx, str, 0);
8357 if (!pn->pn_atom)
8358 return JS_FALSE;
8359 pn->pn_type = TOK_STRING;
8360 pn->pn_op = JSOP_STRING;
8362 break;
8364 default:;
8367 return JS_TRUE;
8371 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8372 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8373 * a successful call to this function.
8375 static JSBool
8376 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8377 JSParseNode *pn, JSTreeContext *tc)
8379 jsdouble d, d2;
8380 int32 i, j;
8382 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8383 d = pn1->pn_dval;
8384 d2 = pn2->pn_dval;
8385 switch (op) {
8386 case JSOP_LSH:
8387 case JSOP_RSH:
8388 i = js_DoubleToECMAInt32(d);
8389 j = js_DoubleToECMAInt32(d2);
8390 j &= 31;
8391 d = (op == JSOP_LSH) ? i << j : i >> j;
8392 break;
8394 case JSOP_URSH:
8395 j = js_DoubleToECMAInt32(d2);
8396 j &= 31;
8397 d = js_DoubleToECMAUint32(d) >> j;
8398 break;
8400 case JSOP_ADD:
8401 d += d2;
8402 break;
8404 case JSOP_SUB:
8405 d -= d2;
8406 break;
8408 case JSOP_MUL:
8409 d *= d2;
8410 break;
8412 case JSOP_DIV:
8413 if (d2 == 0) {
8414 #if defined(XP_WIN)
8415 /* XXX MSVC miscompiles such that (NaN == 0) */
8416 if (JSDOUBLE_IS_NaN(d2))
8417 d = *cx->runtime->jsNaN;
8418 else
8419 #endif
8420 if (d == 0 || JSDOUBLE_IS_NaN(d))
8421 d = *cx->runtime->jsNaN;
8422 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
8423 d = *cx->runtime->jsNegativeInfinity;
8424 else
8425 d = *cx->runtime->jsPositiveInfinity;
8426 } else {
8427 d /= d2;
8429 break;
8431 case JSOP_MOD:
8432 if (d2 == 0) {
8433 d = *cx->runtime->jsNaN;
8434 } else {
8435 d = js_fmod(d, d2);
8437 break;
8439 default:;
8442 /* Take care to allow pn1 or pn2 to alias pn. */
8443 if (pn1 != pn)
8444 RecycleTree(pn1, tc);
8445 if (pn2 != pn)
8446 RecycleTree(pn2, tc);
8447 pn->pn_type = TOK_NUMBER;
8448 pn->pn_op = JSOP_DOUBLE;
8449 pn->pn_arity = PN_NULLARY;
8450 pn->pn_dval = d;
8451 return JS_TRUE;
8454 #if JS_HAS_XML_SUPPORT
8456 static JSBool
8457 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8459 JSTokenType tt;
8460 JSParseNode **pnp, *pn1, *pn2;
8461 JSString *accum, *str;
8462 uint32 i, j;
8463 JSTempValueRooter tvr;
8465 JS_ASSERT(pn->pn_arity == PN_LIST);
8466 tt = PN_TYPE(pn);
8467 pnp = &pn->pn_head;
8468 pn1 = *pnp;
8469 accum = NULL;
8470 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8471 if (tt == TOK_XMLETAGO)
8472 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8473 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8474 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8478 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8479 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8480 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8481 * Therefore, we have to add additonal protection from GC nesting under
8482 * js_ConcatStrings.
8484 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8485 /* The parser already rejected end-tags with attributes. */
8486 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8487 switch (pn2->pn_type) {
8488 case TOK_XMLATTR:
8489 if (!accum)
8490 goto cantfold;
8491 /* FALL THROUGH */
8492 case TOK_XMLNAME:
8493 case TOK_XMLSPACE:
8494 case TOK_XMLTEXT:
8495 case TOK_STRING:
8496 if (pn2->pn_arity == PN_LIST)
8497 goto cantfold;
8498 str = ATOM_TO_STRING(pn2->pn_atom);
8499 break;
8501 case TOK_XMLCDATA:
8502 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8503 if (!str)
8504 return JS_FALSE;
8505 break;
8507 case TOK_XMLCOMMENT:
8508 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8509 if (!str)
8510 return JS_FALSE;
8511 break;
8513 case TOK_XMLPI:
8514 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8515 ATOM_TO_STRING(pn2->pn_atom2));
8516 if (!str)
8517 return JS_FALSE;
8518 break;
8520 cantfold:
8521 default:
8522 JS_ASSERT(*pnp == pn1);
8523 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8524 (i & 1) ^ (j & 1)) {
8525 #ifdef DEBUG_brendanXXX
8526 printf("1: %d, %d => ", i, j);
8527 if (accum)
8528 js_FileEscapedString(stdout, accum, 0);
8529 else
8530 fputs("NULL", stdout);
8531 fputc('\n', stdout);
8532 #endif
8533 } else if (accum && pn1 != pn2) {
8534 while (pn1->pn_next != pn2) {
8535 pn1 = RecycleTree(pn1, tc);
8536 --pn->pn_count;
8538 pn1->pn_type = TOK_XMLTEXT;
8539 pn1->pn_op = JSOP_STRING;
8540 pn1->pn_arity = PN_NULLARY;
8541 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8542 if (!pn1->pn_atom)
8543 return JS_FALSE;
8544 JS_ASSERT(pnp != &pn1->pn_next);
8545 *pnp = pn1;
8547 pnp = &pn2->pn_next;
8548 pn1 = *pnp;
8549 accum = NULL;
8550 continue;
8553 if (accum) {
8554 JS_PUSH_TEMP_ROOT_STRING(cx, accum, &tvr);
8555 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8556 ? js_AddAttributePart(cx, i & 1, accum, str)
8557 : js_ConcatStrings(cx, accum, str);
8558 JS_POP_TEMP_ROOT(cx, &tvr);
8559 if (!str)
8560 return JS_FALSE;
8561 #ifdef DEBUG_brendanXXX
8562 printf("2: %d, %d => ", i, j);
8563 js_FileEscapedString(stdout, str, 0);
8564 printf(" (%u)\n", str->length());
8565 #endif
8566 ++j;
8568 accum = str;
8571 if (accum) {
8572 str = NULL;
8573 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8574 if (tt == TOK_XMLPTAGC)
8575 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8576 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8577 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8579 if (str) {
8580 accum = js_ConcatStrings(cx, accum, str);
8581 if (!accum)
8582 return JS_FALSE;
8585 JS_ASSERT(*pnp == pn1);
8586 while (pn1->pn_next) {
8587 pn1 = RecycleTree(pn1, tc);
8588 --pn->pn_count;
8590 pn1->pn_type = TOK_XMLTEXT;
8591 pn1->pn_op = JSOP_STRING;
8592 pn1->pn_arity = PN_NULLARY;
8593 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8594 if (!pn1->pn_atom)
8595 return JS_FALSE;
8596 JS_ASSERT(pnp != &pn1->pn_next);
8597 *pnp = pn1;
8600 if (pn1 && pn->pn_count == 1) {
8602 * Only one node under pn, and it has been folded: move pn1 onto pn
8603 * unless pn is an XML root (in which case we need it to tell the code
8604 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8605 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8606 * extra "<" and "/>" bracketing at runtime.
8608 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8609 pn->become(pn1);
8610 } else if (tt == TOK_XMLPTAGC) {
8611 pn->pn_type = TOK_XMLELEM;
8612 pn->pn_op = JSOP_TOXML;
8615 return JS_TRUE;
8618 #endif /* JS_HAS_XML_SUPPORT */
8620 static int
8621 Boolish(JSParseNode *pn)
8623 switch (pn->pn_op) {
8624 case JSOP_DOUBLE:
8625 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8627 case JSOP_STRING:
8628 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8630 #if JS_HAS_GENERATOR_EXPRS
8631 case JSOP_CALL:
8634 * A generator expression as an if or loop condition has no effects, it
8635 * simply results in a truthy object reference. This condition folding
8636 * is needed for the decompiler. See bug 442342 and bug 443074.
8638 if (pn->pn_count != 1)
8639 break;
8640 JSParseNode *pn2 = pn->pn_head;
8641 if (pn2->pn_type != TOK_FUNCTION)
8642 break;
8643 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8644 break;
8645 /* FALL THROUGH */
8647 #endif
8649 case JSOP_DEFFUN:
8650 case JSOP_LAMBDA:
8651 case JSOP_THIS:
8652 case JSOP_TRUE:
8653 return 1;
8655 case JSOP_NULL:
8656 case JSOP_FALSE:
8657 return 0;
8659 default:;
8661 return -1;
8664 JSBool
8665 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8667 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8669 JS_CHECK_RECURSION(cx, return JS_FALSE);
8671 switch (pn->pn_arity) {
8672 case PN_FUNC:
8674 uint16 oldflags = tc->flags;
8675 JSFunctionBox *oldlist = tc->functionList;
8677 tc->flags = (uint16) pn->pn_funbox->tcflags;
8678 tc->functionList = pn->pn_funbox->kids;
8679 if (!js_FoldConstants(cx, pn->pn_body, tc))
8680 return JS_FALSE;
8681 pn->pn_funbox->kids = tc->functionList;
8682 tc->flags = oldflags;
8683 tc->functionList = oldlist;
8684 break;
8687 case PN_LIST:
8689 /* Propagate inCond through logical connectives. */
8690 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8692 /* Save the list head in pn1 for later use. */
8693 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
8694 if (!js_FoldConstants(cx, pn2, tc, cond))
8695 return JS_FALSE;
8697 break;
8700 case PN_TERNARY:
8701 /* Any kid may be null (e.g. for (;;)). */
8702 pn1 = pn->pn_kid1;
8703 pn2 = pn->pn_kid2;
8704 pn3 = pn->pn_kid3;
8705 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8706 return JS_FALSE;
8707 if (pn2) {
8708 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8709 return JS_FALSE;
8710 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8711 RecycleTree(pn2, tc);
8712 pn->pn_kid2 = NULL;
8715 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8716 return JS_FALSE;
8717 break;
8719 case PN_BINARY:
8720 pn1 = pn->pn_left;
8721 pn2 = pn->pn_right;
8723 /* Propagate inCond through logical connectives. */
8724 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8725 if (!js_FoldConstants(cx, pn1, tc, inCond))
8726 return JS_FALSE;
8727 if (!js_FoldConstants(cx, pn2, tc, inCond))
8728 return JS_FALSE;
8729 break;
8732 /* First kid may be null (for default case in switch). */
8733 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8734 return JS_FALSE;
8735 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8736 return JS_FALSE;
8737 break;
8739 case PN_UNARY:
8740 /* Our kid may be null (e.g. return; vs. return e;). */
8741 pn1 = pn->pn_kid;
8742 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
8743 return JS_FALSE;
8744 break;
8746 case PN_NAME:
8748 * Skip pn1 down along a chain of dotted member expressions to avoid
8749 * excessive recursion. Our only goal here is to fold constants (if
8750 * any) in the primary expression operand to the left of the first
8751 * dot in the chain.
8753 if (!pn->pn_used) {
8754 pn1 = pn->pn_expr;
8755 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8756 pn1 = pn1->pn_expr;
8757 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8758 return JS_FALSE;
8760 break;
8762 case PN_NAMESET:
8763 pn1 = pn->pn_tree;
8764 if (!js_FoldConstants(cx, pn1, tc))
8765 return JS_FALSE;
8766 break;
8768 case PN_NULLARY:
8769 break;
8772 switch (pn->pn_type) {
8773 case TOK_IF:
8774 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8775 break;
8776 /* FALL THROUGH */
8778 case TOK_HOOK:
8779 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8780 switch (pn1->pn_type) {
8781 case TOK_NUMBER:
8782 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8783 pn2 = pn3;
8784 break;
8785 case TOK_STRING:
8786 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
8787 pn2 = pn3;
8788 break;
8789 case TOK_PRIMARY:
8790 if (pn1->pn_op == JSOP_TRUE)
8791 break;
8792 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8793 pn2 = pn3;
8794 break;
8796 /* FALL THROUGH */
8797 default:
8798 /* Early return to dodge common code that copies pn2 to pn. */
8799 return JS_TRUE;
8802 #if JS_HAS_GENERATOR_EXPRS
8803 /* Don't fold a trailing |if (0)| in a generator expression. */
8804 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8805 break;
8806 #endif
8808 if (pn2 && !pn2->pn_defn)
8809 pn->become(pn2);
8810 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8812 * False condition and no else, or an empty then-statement was
8813 * moved up over pn. Either way, make pn an empty block (not an
8814 * empty statement, which does not decompile, even when labeled).
8815 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8816 * or an empty statement for a child.
8818 pn->pn_type = TOK_LC;
8819 pn->pn_arity = PN_LIST;
8820 pn->makeEmpty();
8822 RecycleTree(pn2, tc);
8823 if (pn3 && pn3 != pn2)
8824 RecycleTree(pn3, tc);
8825 break;
8827 case TOK_OR:
8828 case TOK_AND:
8829 if (inCond) {
8830 if (pn->pn_arity == PN_LIST) {
8831 JSParseNode **pnp = &pn->pn_head;
8832 JS_ASSERT(*pnp == pn1);
8833 do {
8834 int cond = Boolish(pn1);
8835 if (cond == (pn->pn_type == TOK_OR)) {
8836 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
8837 pn3 = pn2->pn_next;
8838 RecycleTree(pn2, tc);
8839 --pn->pn_count;
8841 pn1->pn_next = NULL;
8842 break;
8844 if (cond != -1) {
8845 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8846 if (pn->pn_count == 1)
8847 break;
8848 *pnp = pn1->pn_next;
8849 RecycleTree(pn1, tc);
8850 --pn->pn_count;
8851 } else {
8852 pnp = &pn1->pn_next;
8854 } while ((pn1 = *pnp) != NULL);
8856 // We may have to change arity from LIST to BINARY.
8857 pn1 = pn->pn_head;
8858 if (pn->pn_count == 2) {
8859 pn2 = pn1->pn_next;
8860 pn1->pn_next = NULL;
8861 JS_ASSERT(!pn2->pn_next);
8862 pn->pn_arity = PN_BINARY;
8863 pn->pn_left = pn1;
8864 pn->pn_right = pn2;
8865 } else if (pn->pn_count == 1) {
8866 pn->become(pn1);
8867 RecycleTree(pn1, tc);
8869 } else {
8870 int cond = Boolish(pn1);
8871 if (cond == (pn->pn_type == TOK_OR)) {
8872 RecycleTree(pn2, tc);
8873 pn->become(pn1);
8874 } else if (cond != -1) {
8875 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8876 RecycleTree(pn1, tc);
8877 pn->become(pn2);
8881 break;
8883 case TOK_ASSIGN:
8885 * Compound operators such as *= should be subject to folding, in case
8886 * the left-hand side is constant, and so that the decompiler produces
8887 * the same string that you get from decompiling a script or function
8888 * compiled from that same string. As with +, += is special.
8890 if (pn->pn_op == JSOP_NOP)
8891 break;
8892 if (pn->pn_op != JSOP_ADD)
8893 goto do_binary_op;
8894 /* FALL THROUGH */
8896 case TOK_PLUS:
8897 if (pn->pn_arity == PN_LIST) {
8898 size_t length, length2;
8899 jschar *chars;
8900 JSString *str, *str2;
8903 * Any string literal term with all others number or string means
8904 * this is a concatenation. If any term is not a string or number
8905 * literal, we can't fold.
8907 JS_ASSERT(pn->pn_count > 2);
8908 if (pn->pn_xflags & PNX_CANTFOLD)
8909 return JS_TRUE;
8910 if (pn->pn_xflags != PNX_STRCAT)
8911 goto do_binary_op;
8913 /* Ok, we're concatenating: convert non-string constant operands. */
8914 length = 0;
8915 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8916 if (!FoldType(cx, pn2, TOK_STRING))
8917 return JS_FALSE;
8918 /* XXX fold only if all operands convert to string */
8919 if (pn2->pn_type != TOK_STRING)
8920 return JS_TRUE;
8921 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
8924 /* Allocate a new buffer and string descriptor for the result. */
8925 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
8926 if (!chars)
8927 return JS_FALSE;
8928 str = js_NewString(cx, chars, length);
8929 if (!str) {
8930 cx->free(chars);
8931 return JS_FALSE;
8934 /* Fill the buffer, advancing chars and recycling kids as we go. */
8935 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
8936 str2 = ATOM_TO_STRING(pn2->pn_atom);
8937 length2 = str2->flatLength();
8938 js_strncpy(chars, str2->flatChars(), length2);
8939 chars += length2;
8941 *chars = 0;
8943 /* Atomize the result string and mutate pn to refer to it. */
8944 pn->pn_atom = js_AtomizeString(cx, str, 0);
8945 if (!pn->pn_atom)
8946 return JS_FALSE;
8947 pn->pn_type = TOK_STRING;
8948 pn->pn_op = JSOP_STRING;
8949 pn->pn_arity = PN_NULLARY;
8950 break;
8953 /* Handle a binary string concatenation. */
8954 JS_ASSERT(pn->pn_arity == PN_BINARY);
8955 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
8956 JSString *left, *right, *str;
8958 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
8959 TOK_STRING)) {
8960 return JS_FALSE;
8962 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
8963 return JS_TRUE;
8964 left = ATOM_TO_STRING(pn1->pn_atom);
8965 right = ATOM_TO_STRING(pn2->pn_atom);
8966 str = js_ConcatStrings(cx, left, right);
8967 if (!str)
8968 return JS_FALSE;
8969 pn->pn_atom = js_AtomizeString(cx, str, 0);
8970 if (!pn->pn_atom)
8971 return JS_FALSE;
8972 pn->pn_type = TOK_STRING;
8973 pn->pn_op = JSOP_STRING;
8974 pn->pn_arity = PN_NULLARY;
8975 RecycleTree(pn1, tc);
8976 RecycleTree(pn2, tc);
8977 break;
8980 /* Can't concatenate string literals, let's try numbers. */
8981 goto do_binary_op;
8983 case TOK_STAR:
8984 case TOK_SHOP:
8985 case TOK_MINUS:
8986 case TOK_DIVOP:
8987 do_binary_op:
8988 if (pn->pn_arity == PN_LIST) {
8989 JS_ASSERT(pn->pn_count > 2);
8990 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8991 if (!FoldType(cx, pn2, TOK_NUMBER))
8992 return JS_FALSE;
8994 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8995 /* XXX fold only if all operands convert to number */
8996 if (pn2->pn_type != TOK_NUMBER)
8997 break;
8999 if (!pn2) {
9000 JSOp op = PN_OP(pn);
9002 pn2 = pn1->pn_next;
9003 pn3 = pn2->pn_next;
9004 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9005 return JS_FALSE;
9006 while ((pn2 = pn3) != NULL) {
9007 pn3 = pn2->pn_next;
9008 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9009 return JS_FALSE;
9012 } else {
9013 JS_ASSERT(pn->pn_arity == PN_BINARY);
9014 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9015 !FoldType(cx, pn2, TOK_NUMBER)) {
9016 return JS_FALSE;
9018 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9019 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9020 return JS_FALSE;
9023 break;
9025 case TOK_UNARYOP:
9026 if (pn1->pn_type == TOK_NUMBER) {
9027 jsdouble d;
9029 /* Operate on one numeric constant. */
9030 d = pn1->pn_dval;
9031 switch (pn->pn_op) {
9032 case JSOP_BITNOT:
9033 d = ~js_DoubleToECMAInt32(d);
9034 break;
9036 case JSOP_NEG:
9037 #ifdef HPUX
9039 * Negation of a zero doesn't produce a negative
9040 * zero on HPUX. Perform the operation by bit
9041 * twiddling.
9043 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
9044 #else
9045 d = -d;
9046 #endif
9047 break;
9049 case JSOP_POS:
9050 break;
9052 case JSOP_NOT:
9053 pn->pn_type = TOK_PRIMARY;
9054 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9055 pn->pn_arity = PN_NULLARY;
9056 /* FALL THROUGH */
9058 default:
9059 /* Return early to dodge the common TOK_NUMBER code. */
9060 return JS_TRUE;
9062 pn->pn_type = TOK_NUMBER;
9063 pn->pn_op = JSOP_DOUBLE;
9064 pn->pn_arity = PN_NULLARY;
9065 pn->pn_dval = d;
9066 RecycleTree(pn1, tc);
9067 } else if (pn1->pn_type == TOK_PRIMARY) {
9068 if (pn->pn_op == JSOP_NOT &&
9069 (pn1->pn_op == JSOP_TRUE ||
9070 pn1->pn_op == JSOP_FALSE)) {
9071 pn->become(pn1);
9072 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9073 RecycleTree(pn1, tc);
9076 break;
9078 #if JS_HAS_XML_SUPPORT
9079 case TOK_XMLELEM:
9080 case TOK_XMLLIST:
9081 case TOK_XMLPTAGC:
9082 case TOK_XMLSTAGO:
9083 case TOK_XMLETAGO:
9084 case TOK_XMLNAME:
9085 if (pn->pn_arity == PN_LIST) {
9086 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9087 if (!FoldXMLConstants(cx, pn, tc))
9088 return JS_FALSE;
9090 break;
9092 case TOK_AT:
9093 if (pn1->pn_type == TOK_XMLNAME) {
9094 jsval v;
9095 JSObjectBox *xmlbox;
9097 v = ATOM_KEY(pn1->pn_atom);
9098 if (!js_ToAttributeName(cx, &v))
9099 return JS_FALSE;
9100 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9102 xmlbox = tc->compiler->newObjectBox(JSVAL_TO_OBJECT(v));
9103 if (!xmlbox)
9104 return JS_FALSE;
9106 pn->pn_type = TOK_XMLNAME;
9107 pn->pn_op = JSOP_OBJECT;
9108 pn->pn_arity = PN_NULLARY;
9109 pn->pn_objbox = xmlbox;
9110 RecycleTree(pn1, tc);
9112 break;
9113 #endif /* JS_HAS_XML_SUPPORT */
9115 default:;
9118 if (inCond) {
9119 int cond = Boolish(pn);
9120 if (cond >= 0) {
9121 switch (pn->pn_arity) {
9122 case PN_LIST:
9123 pn2 = pn->pn_head;
9124 do {
9125 pn3 = pn2->pn_next;
9126 RecycleTree(pn2, tc);
9127 } while ((pn2 = pn3) != NULL);
9128 break;
9129 case PN_FUNC:
9130 RecycleFuncNameKids(pn, tc);
9131 break;
9132 case PN_NULLARY:
9133 break;
9134 default:
9135 JS_NOT_REACHED("unhandled arity");
9137 pn->pn_type = TOK_PRIMARY;
9138 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9139 pn->pn_arity = PN_NULLARY;
9143 return JS_TRUE;