Bug 507665 - Avoid imacros for JSOP_GETELEM and JSOP_CALLELEM. r=gal.
[mozilla-central.git] / js / src / jsparse.cpp
blob35cd45dd9d630fb638ef2600cd28f11d88431194
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_u = pn2->pn_u;
193 pn2->clear();
196 void
197 JSParseNode::clear()
199 pn_type = TOK_EOF;
200 pn_op = JSOP_NOP;
201 pn_used = pn_defn = false;
202 pn_arity = PN_NULLARY;
205 bool
206 JSCompiler::init(const jschar *base, size_t length,
207 FILE *fp, const char *filename, uintN lineno)
209 JSContext *cx = context;
211 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
212 if (!js_InitTokenStream(cx, TS(this), base, length, fp, filename, lineno)) {
213 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
214 return false;
217 /* Root atoms and objects allocated for the parsed tree. */
218 JS_KEEP_ATOMS(cx->runtime);
219 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
220 return true;
223 JSCompiler::~JSCompiler()
225 JSContext *cx = context;
227 if (principals)
228 JSPRINCIPALS_DROP(cx, principals);
229 JS_ASSERT(tempRoot.u.compiler == this);
230 JS_POP_TEMP_ROOT(cx, &tempRoot);
231 JS_UNKEEP_ATOMS(cx->runtime);
232 js_CloseTokenStream(cx, TS(this));
233 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
236 void
237 JSCompiler::setPrincipals(JSPrincipals *prin)
239 JS_ASSERT(!principals);
240 if (prin)
241 JSPRINCIPALS_HOLD(context, prin);
242 principals = prin;
245 JSObjectBox *
246 JSCompiler::newObjectBox(JSObject *obj)
248 JS_ASSERT(obj);
251 * We use JSContext.tempPool to allocate parsed objects and place them on
252 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
253 * containing the entries must be alive until we are done with scanning,
254 * parsing and code generation for the whole script or top-level function.
256 JSObjectBox *objbox;
257 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
258 if (!objbox) {
259 js_ReportOutOfScriptQuota(context);
260 return NULL;
262 objbox->traceLink = traceListHead;
263 traceListHead = objbox;
264 objbox->emitLink = NULL;
265 objbox->object = obj;
266 return objbox;
269 JSFunctionBox *
270 JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
272 JS_ASSERT(obj);
273 JS_ASSERT(HAS_FUNCTION_CLASS(obj));
276 * We use JSContext.tempPool to allocate parsed objects and place them on
277 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
278 * containing the entries must be alive until we are done with scanning,
279 * parsing and code generation for the whole script or top-level function.
281 JSFunctionBox *funbox;
282 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
283 if (!funbox) {
284 js_ReportOutOfScriptQuota(context);
285 return NULL;
287 funbox->traceLink = traceListHead;
288 traceListHead = funbox;
289 funbox->emitLink = NULL;
290 funbox->object = obj;
291 funbox->node = fn;
292 funbox->siblings = tc->functionList;
293 tc->functionList = funbox;
294 ++tc->compiler->functionCount;
295 funbox->kids = NULL;
296 funbox->parent = tc->funbox;
297 funbox->queued = false;
298 funbox->inLoop = false;
299 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
300 if (STMT_IS_LOOP(stmt)) {
301 funbox->inLoop = true;
302 break;
305 funbox->level = tc->staticLevel;
306 funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
307 return funbox;
310 void
311 JSCompiler::trace(JSTracer *trc)
313 JSObjectBox *objbox;
315 JS_ASSERT(tempRoot.u.compiler == this);
316 objbox = traceListHead;
317 while (objbox) {
318 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
319 objbox = objbox->traceLink;
323 static void
324 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
326 static void
327 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
329 JSFunctionBox *funbox = pn->pn_funbox;
330 if (funbox) {
331 JS_ASSERT(funbox->node == pn);
332 funbox->node = NULL;
334 JSFunctionBox **funboxp = &tc->functionList;
335 while (*funboxp) {
336 if (*funboxp == funbox) {
337 *funboxp = funbox->siblings;
338 break;
340 funboxp = &(*funboxp)->siblings;
343 uint16 oldflags = tc->flags;
344 JSFunctionBox *oldlist = tc->functionList;
346 tc->flags = (uint16) funbox->tcflags;
347 tc->functionList = funbox->kids;
348 UnlinkFunctionBoxes(pn->pn_body, tc);
349 funbox->kids = tc->functionList;
350 tc->flags = oldflags;
351 tc->functionList = oldlist;
353 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
354 pn->pn_funbox = NULL;
358 static void
359 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
361 if (pn) {
362 switch (pn->pn_arity) {
363 case PN_NULLARY:
364 return;
365 case PN_UNARY:
366 UnlinkFunctionBoxes(pn->pn_kid, tc);
367 return;
368 case PN_BINARY:
369 UnlinkFunctionBoxes(pn->pn_left, tc);
370 UnlinkFunctionBoxes(pn->pn_right, tc);
371 return;
372 case PN_TERNARY:
373 UnlinkFunctionBoxes(pn->pn_kid1, tc);
374 UnlinkFunctionBoxes(pn->pn_kid2, tc);
375 UnlinkFunctionBoxes(pn->pn_kid3, tc);
376 return;
377 case PN_LIST:
378 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
379 UnlinkFunctionBoxes(pn2, tc);
380 return;
381 case PN_FUNC:
382 UnlinkFunctionBox(pn, tc);
383 return;
384 case PN_NAME:
385 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
386 return;
387 case PN_NAMESET:
388 UnlinkFunctionBoxes(pn->pn_tree, tc);
393 static void
394 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
396 static JSParseNode *
397 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
399 JSParseNode *next, **head;
401 if (!pn)
402 return NULL;
404 /* Catch back-to-back dup recycles. */
405 JS_ASSERT(pn != tc->compiler->nodeList);
406 next = pn->pn_next;
407 if (pn->pn_used || pn->pn_defn) {
409 * JSAtomLists own definition nodes along with their used-node chains.
410 * Defer recycling such nodes until we unwind to top level to avoid
411 * linkage overhead or (alternatively) unlinking runtime complexity.
412 * Yes, this means dead code can contribute to static analysis results!
414 * Do recycle kids here, since they are no longer needed.
416 pn->pn_next = NULL;
417 RecycleFuncNameKids(pn, tc);
418 } else {
419 UnlinkFunctionBoxes(pn, tc);
420 head = &tc->compiler->nodeList;
421 pn->pn_next = *head;
422 *head = pn;
423 #ifdef METER_PARSENODES
424 recyclednodes++;
425 #endif
427 return next;
430 static void
431 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
433 switch (pn->pn_arity) {
434 case PN_FUNC:
435 UnlinkFunctionBox(pn, tc);
436 /* FALL THROUGH */
438 case PN_NAME:
440 * Only a definition node might have a non-null strong pn_expr link
441 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
442 * Every node with the pn_used flag set has a non-null pn_lexdef
443 * weak reference to its definition node.
445 if (!pn->pn_used && pn->pn_expr) {
446 RecycleTree(pn->pn_expr, tc);
447 pn->pn_expr = NULL;
449 break;
451 default:
452 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
456 static JSParseNode *
457 NewOrRecycledNode(JSTreeContext *tc)
459 JSParseNode *pn, *pn2;
461 pn = tc->compiler->nodeList;
462 if (!pn) {
463 JSContext *cx = tc->compiler->context;
465 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
466 if (!pn)
467 js_ReportOutOfScriptQuota(cx);
468 } else {
469 tc->compiler->nodeList = pn->pn_next;
471 /* Recycle immediate descendents only, to save work and working set. */
472 switch (pn->pn_arity) {
473 case PN_FUNC:
474 RecycleTree(pn->pn_body, tc);
475 break;
476 case PN_LIST:
477 pn2 = pn->pn_head;
478 if (pn2) {
479 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
480 pn2 = pn2->pn_next;
481 if (pn2) {
482 pn2 = pn->pn_head;
483 do {
484 pn2 = RecycleTree(pn2, tc);
485 } while (pn2);
486 } else {
487 *pn->pn_tail = tc->compiler->nodeList;
488 tc->compiler->nodeList = pn->pn_head;
489 #ifdef METER_PARSENODES
490 recyclednodes += pn->pn_count;
491 #endif
492 break;
495 break;
496 case PN_TERNARY:
497 RecycleTree(pn->pn_kid1, tc);
498 RecycleTree(pn->pn_kid2, tc);
499 RecycleTree(pn->pn_kid3, tc);
500 break;
501 case PN_BINARY:
502 if (pn->pn_left != pn->pn_right)
503 RecycleTree(pn->pn_left, tc);
504 RecycleTree(pn->pn_right, tc);
505 break;
506 case PN_UNARY:
507 RecycleTree(pn->pn_kid, tc);
508 break;
509 case PN_NAME:
510 if (!pn->pn_used)
511 RecycleTree(pn->pn_expr, tc);
512 break;
513 case PN_NULLARY:
514 break;
517 if (pn) {
518 #ifdef METER_PARSENODES
519 parsenodes++;
520 if (parsenodes - recyclednodes > maxparsenodes)
521 maxparsenodes = parsenodes - recyclednodes;
522 #endif
523 pn->pn_used = pn->pn_defn = false;
524 memset(&pn->pn_u, 0, sizeof pn->pn_u);
525 pn->pn_next = NULL;
527 return pn;
530 static inline void
531 InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
533 pn->pn_type = type;
534 pn->pn_op = op;
535 pn->pn_arity = arity;
536 JS_ASSERT(!pn->pn_used);
537 JS_ASSERT(!pn->pn_defn);
538 pn->pn_next = pn->pn_link = NULL;
542 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
543 * temporary arena.
545 static JSParseNode *
546 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
548 JSParseNode *pn;
549 JSToken *tp;
551 pn = NewOrRecycledNode(tc);
552 if (!pn)
553 return NULL;
554 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
555 InitParseNode(pn, tp->type, JSOP_NOP, arity);
556 pn->pn_pos = tp->pos;
557 return pn;
560 static inline void
561 InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
563 pn->pn_expr = NULL;
564 pn->pn_cookie = FREE_UPVAR_COOKIE;
565 pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
566 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
567 pn->pn_dflags |= PND_BLOCKCHILD;
568 pn->pn_blockid = tc->blockid();
571 static JSParseNode *
572 NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc)
574 JSParseNode *pn;
576 pn = NewParseNode(PN_NAME, tc);
577 if (pn) {
578 pn->pn_atom = atom;
579 InitNameNodeCommon(pn, tc);
581 return pn;
584 static JSParseNode *
585 NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
586 JSTreeContext *tc)
588 JSParseNode *pn, *pn1, *pn2;
590 if (!left || !right)
591 return NULL;
594 * Flatten a left-associative (left-heavy) tree of a given operator into
595 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
597 if (PN_TYPE(left) == tt &&
598 PN_OP(left) == op &&
599 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
600 if (left->pn_arity != PN_LIST) {
601 pn1 = left->pn_left, pn2 = left->pn_right;
602 left->pn_arity = PN_LIST;
603 left->initList(pn1);
604 left->append(pn2);
605 if (tt == TOK_PLUS) {
606 if (pn1->pn_type == TOK_STRING)
607 left->pn_xflags |= PNX_STRCAT;
608 else if (pn1->pn_type != TOK_NUMBER)
609 left->pn_xflags |= PNX_CANTFOLD;
610 if (pn2->pn_type == TOK_STRING)
611 left->pn_xflags |= PNX_STRCAT;
612 else if (pn2->pn_type != TOK_NUMBER)
613 left->pn_xflags |= PNX_CANTFOLD;
616 left->append(right);
617 left->pn_pos.end = right->pn_pos.end;
618 if (tt == TOK_PLUS) {
619 if (right->pn_type == TOK_STRING)
620 left->pn_xflags |= PNX_STRCAT;
621 else if (right->pn_type != TOK_NUMBER)
622 left->pn_xflags |= PNX_CANTFOLD;
624 return left;
628 * Fold constant addition immediately, to conserve node space and, what's
629 * more, so js_FoldConstants never sees mixed addition and concatenation
630 * operations with more than one leading non-string operand in a PN_LIST
631 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
632 * to "3pt", not "12pt").
634 if (tt == TOK_PLUS &&
635 left->pn_type == TOK_NUMBER &&
636 right->pn_type == TOK_NUMBER) {
637 left->pn_dval += right->pn_dval;
638 left->pn_pos.end = right->pn_pos.end;
639 RecycleTree(right, tc);
640 return left;
643 pn = NewOrRecycledNode(tc);
644 if (!pn)
645 return NULL;
646 InitParseNode(pn, tt, op, PN_BINARY);
647 pn->pn_pos.begin = left->pn_pos.begin;
648 pn->pn_pos.end = right->pn_pos.end;
649 pn->pn_left = left;
650 pn->pn_right = right;
651 return pn;
654 #if JS_HAS_GETTER_SETTER
655 static JSTokenType
656 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
658 JSAtom *atom;
659 JSRuntime *rt;
660 JSOp op;
661 const char *name;
663 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
664 atom = CURRENT_TOKEN(ts).t_atom;
665 rt = cx->runtime;
666 if (atom == rt->atomState.getterAtom)
667 op = JSOP_GETTER;
668 else if (atom == rt->atomState.setterAtom)
669 op = JSOP_SETTER;
670 else
671 return TOK_NAME;
672 if (js_PeekTokenSameLine(cx, ts) != tt)
673 return TOK_NAME;
674 (void) js_GetToken(cx, ts);
675 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
676 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
677 JSMSG_BAD_GETTER_OR_SETTER,
678 (op == JSOP_GETTER)
679 ? js_getter_str
680 : js_setter_str);
681 return TOK_ERROR;
683 CURRENT_TOKEN(ts).t_op = op;
684 if (JS_HAS_STRICT_OPTION(cx)) {
685 name = js_AtomToPrintableString(cx, atom);
686 if (!name ||
687 !js_ReportCompileErrorNumber(cx, ts, NULL,
688 JSREPORT_WARNING | JSREPORT_STRICT,
689 JSMSG_DEPRECATED_USAGE,
690 name)) {
691 return TOK_ERROR;
694 return tt;
696 #endif
698 static bool
699 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
701 if (tc->blockidGen == JS_BIT(20)) {
702 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
703 JSMSG_NEED_DIET, "program");
704 return false;
706 blockid = tc->blockidGen++;
707 return true;
710 static bool
711 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
713 JS_ASSERT(tc->topStmt);
714 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
715 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
716 if (!GenerateBlockId(tc, tc->topStmt->blockid))
717 return false;
718 pn->pn_blockid = tc->topStmt->blockid;
719 return true;
723 * Parse a top-level JS script.
725 JSParseNode *
726 JSCompiler::parse(JSObject *chain)
729 * Protect atoms from being collected by a GC activation, which might
730 * - nest on this thread due to out of memory (the so-called "last ditch"
731 * GC attempted within js_NewGCThing), or
732 * - run for any reason on another thread if this thread is suspended on
733 * an object lock before it finishes generating bytecode into a script
734 * protected from the GC by a root or a stack frame reference.
736 JSTreeContext tc(this);
737 tc.scopeChain = chain;
738 if (!GenerateBlockId(&tc, tc.bodyid))
739 return NULL;
741 JSParseNode *pn = Statements(context, TS(this), &tc);
742 if (pn) {
743 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
744 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
745 JSMSG_SYNTAX_ERROR);
746 pn = NULL;
747 } else {
748 if (!js_FoldConstants(context, pn, &tc))
749 pn = NULL;
752 return pn;
755 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
757 static inline bool
758 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
761 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
762 * (0xffffffff) and other cookies with that level.
764 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
765 * practically speaking it leaves more than enough room for upvars. In fact
766 * we might want to split cookie fields giving fewer bits for skip and more
767 * for slot, but only based on evidence.
769 if (staticLevel >= FREE_STATIC_LEVEL) {
770 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
771 JSMSG_TOO_DEEP, js_function_str);
772 return false;
774 tc->staticLevel = staticLevel;
775 return true;
779 * Compile a top-level script.
781 JSScript *
782 JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
783 JSPrincipals *principals, uint32 tcflags,
784 const jschar *chars, size_t length,
785 FILE *file, const char *filename, uintN lineno,
786 JSString *source /* = NULL */)
788 JSCompiler jsc(cx, principals, callerFrame);
789 JSArenaPool codePool, notePool;
790 JSTokenType tt;
791 JSParseNode *pn;
792 uint32 scriptGlobals;
793 JSScript *script;
794 #ifdef METER_PARSENODES
795 void *sbrk(ptrdiff_t), *before = sbrk(0);
796 #endif
798 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
799 TCF_STATIC_LEVEL_MASK)));
802 * The scripted callerFrame can only be given for compile-and-go scripts
803 * and non-zero static level requires callerFrame.
805 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
806 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
808 if (!jsc.init(chars, length, file, filename, lineno))
809 return NULL;
811 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
812 &cx->scriptStackQuota);
813 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
814 &cx->scriptStackQuota);
816 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
818 MUST_FLOW_THROUGH("out");
820 /* Null script early in case of error, to reduce our code footprint. */
821 script = NULL;
823 cg.flags |= uint16(tcflags);
824 cg.scopeChain = scopeChain;
825 if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
826 goto out;
829 * If funbox is non-null after we create the new script, callerFrame->fun
830 * was saved in the 0th object table entry.
832 JSObjectBox *funbox;
833 funbox = NULL;
835 if (tcflags & TCF_COMPILE_N_GO) {
836 if (source) {
838 * Save eval program source in script->atomMap.vector[0] for the
839 * eval cache (see obj_eval in jsobj.cpp).
841 JSAtom *atom = js_AtomizeString(cx, source, 0);
842 if (!atom || !cg.atomList.add(&jsc, atom))
843 goto out;
846 if (callerFrame && callerFrame->fun) {
848 * An eval script in a caller frame needs to have its enclosing
849 * function captured in case it refers to an upvar, and someone
850 * wishes to decompile it while it's running.
852 funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
853 if (!funbox)
854 goto out;
855 funbox->emitLink = cg.objectList.lastbox;
856 cg.objectList.lastbox = funbox;
857 cg.objectList.length++;
862 * Inline Statements to emit as we go to save AST space. We must generate
863 * our script-body blockid since we aren't calling Statements.
865 uint32 bodyid;
866 if (!GenerateBlockId(&cg, bodyid))
867 goto out;
868 cg.bodyid = bodyid;
870 #if JS_HAS_XML_SUPPORT
871 pn = NULL;
872 bool onlyXML;
873 onlyXML = true;
874 #endif
876 for (;;) {
877 jsc.tokenStream.flags |= TSF_OPERAND;
878 tt = js_PeekToken(cx, &jsc.tokenStream);
879 jsc.tokenStream.flags &= ~TSF_OPERAND;
880 if (tt <= TOK_EOF) {
881 if (tt == TOK_EOF)
882 break;
883 JS_ASSERT(tt == TOK_ERROR);
884 goto out;
887 pn = Statement(cx, &jsc.tokenStream, &cg);
888 if (!pn)
889 goto out;
890 JS_ASSERT(!cg.blockNode);
892 if (!js_FoldConstants(cx, pn, &cg))
893 goto out;
895 if (cg.functionList) {
896 if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
897 goto out;
898 cg.functionList = NULL;
901 if (!js_EmitTree(cx, &cg, pn))
902 goto out;
903 #if JS_HAS_XML_SUPPORT
904 if (PN_TYPE(pn) != TOK_SEMI ||
905 !pn->pn_kid ||
906 !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
907 onlyXML = false;
909 #endif
910 RecycleTree(pn, &cg);
913 #if JS_HAS_XML_SUPPORT
915 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
916 * For background, see:
918 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
920 if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
921 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
922 JSMSG_XML_WHOLE_PROGRAM);
923 goto out;
925 #endif
928 * Global variables and regexps share the index space with locals. Due to
929 * incremental code generation we need to patch the bytecode to adjust the
930 * local references to skip the globals.
932 scriptGlobals = cg.ngvars + cg.regexpList.length;
933 if (scriptGlobals != 0) {
934 jsbytecode *code, *end;
935 JSOp op;
936 const JSCodeSpec *cs;
937 uintN len, slot;
939 if (scriptGlobals >= SLOTNO_LIMIT)
940 goto too_many_slots;
941 code = CG_BASE(&cg);
942 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
943 JS_ASSERT(code < end);
944 op = (JSOp) *code;
945 cs = &js_CodeSpec[op];
946 len = (cs->length > 0)
947 ? (uintN) cs->length
948 : js_GetVariableBytecodeLength(code);
949 if (JOF_TYPE(cs->format) == JOF_LOCAL ||
950 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
952 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
953 * emitted only for a function.
955 JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
956 (op == JSOP_GETLOCALPROP));
957 slot = GET_SLOTNO(code);
958 slot += scriptGlobals;
959 if (slot >= SLOTNO_LIMIT)
960 goto too_many_slots;
961 SET_SLOTNO(code, slot);
966 #ifdef METER_PARSENODES
967 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
968 (char *)sbrk(0) - (char *)before,
969 parsenodes,
970 maxparsenodes,
971 parsenodes - recyclednodes);
972 before = sbrk(0);
973 #endif
976 * Nowadays the threaded interpreter needs a stop instruction, so we
977 * do have to emit that here.
979 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
980 goto out;
981 #ifdef METER_PARSENODES
982 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
983 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
984 #endif
985 #ifdef JS_ARENAMETER
986 JS_DumpArenaStats(stdout);
987 #endif
988 script = js_NewScriptFromCG(cx, &cg);
989 if (script && funbox)
990 script->flags |= JSSF_SAVED_CALLER_FUN;
992 #ifdef JS_SCOPE_DEPTH_METER
993 if (script) {
994 JSObject *obj = scopeChain;
995 uintN depth = 1;
996 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
997 ++depth;
998 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1000 #endif
1002 out:
1003 JS_FinishArenaPool(&codePool);
1004 JS_FinishArenaPool(&notePool);
1005 return script;
1007 too_many_slots:
1008 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1009 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1010 script = NULL;
1011 goto out;
1015 * Insist on a final return before control flows out of pn. Try to be a bit
1016 * smart about loops: do {...; return e2;} while(0) at the end of a function
1017 * that contains an early return e1 will get a strict warning. Similarly for
1018 * iloops: while (true){...} is treated as though ... returns.
1020 #define ENDS_IN_OTHER 0
1021 #define ENDS_IN_RETURN 1
1022 #define ENDS_IN_BREAK 2
1024 static int
1025 HasFinalReturn(JSParseNode *pn)
1027 JSParseNode *pn2, *pn3;
1028 uintN rv, rv2, hasDefault;
1030 switch (pn->pn_type) {
1031 case TOK_LC:
1032 if (!pn->pn_head)
1033 return ENDS_IN_OTHER;
1034 return HasFinalReturn(pn->last());
1036 case TOK_IF:
1037 if (!pn->pn_kid3)
1038 return ENDS_IN_OTHER;
1039 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1041 case TOK_WHILE:
1042 pn2 = pn->pn_left;
1043 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1044 return ENDS_IN_RETURN;
1045 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1046 return ENDS_IN_RETURN;
1047 return ENDS_IN_OTHER;
1049 case TOK_DO:
1050 pn2 = pn->pn_right;
1051 if (pn2->pn_type == TOK_PRIMARY) {
1052 if (pn2->pn_op == JSOP_FALSE)
1053 return HasFinalReturn(pn->pn_left);
1054 if (pn2->pn_op == JSOP_TRUE)
1055 return ENDS_IN_RETURN;
1057 if (pn2->pn_type == TOK_NUMBER) {
1058 if (pn2->pn_dval == 0)
1059 return HasFinalReturn(pn->pn_left);
1060 return ENDS_IN_RETURN;
1062 return ENDS_IN_OTHER;
1064 case TOK_FOR:
1065 pn2 = pn->pn_left;
1066 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1067 return ENDS_IN_RETURN;
1068 return ENDS_IN_OTHER;
1070 case TOK_SWITCH:
1071 rv = ENDS_IN_RETURN;
1072 hasDefault = ENDS_IN_OTHER;
1073 pn2 = pn->pn_right;
1074 if (pn2->pn_type == TOK_LEXICALSCOPE)
1075 pn2 = pn2->expr();
1076 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1077 if (pn2->pn_type == TOK_DEFAULT)
1078 hasDefault = ENDS_IN_RETURN;
1079 pn3 = pn2->pn_right;
1080 JS_ASSERT(pn3->pn_type == TOK_LC);
1081 if (pn3->pn_head) {
1082 rv2 = HasFinalReturn(pn3->last());
1083 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1084 /* Falling through to next case or default. */;
1085 else
1086 rv &= rv2;
1089 /* If a final switch has no default case, we judge it harshly. */
1090 rv &= hasDefault;
1091 return rv;
1093 case TOK_BREAK:
1094 return ENDS_IN_BREAK;
1096 case TOK_WITH:
1097 return HasFinalReturn(pn->pn_right);
1099 case TOK_RETURN:
1100 return ENDS_IN_RETURN;
1102 case TOK_COLON:
1103 case TOK_LEXICALSCOPE:
1104 return HasFinalReturn(pn->expr());
1106 case TOK_THROW:
1107 return ENDS_IN_RETURN;
1109 case TOK_TRY:
1110 /* If we have a finally block that returns, we are done. */
1111 if (pn->pn_kid3) {
1112 rv = HasFinalReturn(pn->pn_kid3);
1113 if (rv == ENDS_IN_RETURN)
1114 return rv;
1117 /* Else check the try block and any and all catch statements. */
1118 rv = HasFinalReturn(pn->pn_kid1);
1119 if (pn->pn_kid2) {
1120 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1121 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1122 rv &= HasFinalReturn(pn2);
1124 return rv;
1126 case TOK_CATCH:
1127 /* Check this catch block's body. */
1128 return HasFinalReturn(pn->pn_kid3);
1130 case TOK_LET:
1131 /* Non-binary let statements are let declarations. */
1132 if (pn->pn_arity != PN_BINARY)
1133 return ENDS_IN_OTHER;
1134 return HasFinalReturn(pn->pn_right);
1136 default:
1137 return ENDS_IN_OTHER;
1141 static JSBool
1142 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1143 uintN anonerrnum)
1145 const char *name;
1147 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1148 if (tc->fun->atom) {
1149 name = js_AtomToPrintableString(cx, tc->fun->atom);
1150 } else {
1151 errnum = anonerrnum;
1152 name = NULL;
1154 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1155 errnum, name);
1158 static JSBool
1159 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1161 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1162 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1163 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1164 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1167 static JSParseNode *
1168 FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1170 JSStmtInfo stmtInfo;
1171 uintN oldflags, firstLine;
1172 JSParseNode *pn;
1174 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1175 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1176 stmtInfo.flags = SIF_BODY_BLOCK;
1178 oldflags = tc->flags;
1179 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1182 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1183 * later, because we may have not peeked in ts yet, so Statements won't
1184 * acquire a valid pn->pn_pos.begin from the current token.
1186 firstLine = ts->lineno;
1187 #if JS_HAS_EXPR_CLOSURES
1188 if (CURRENT_TOKEN(ts).type == TOK_LC) {
1189 pn = Statements(cx, ts, tc);
1190 } else {
1191 pn = NewParseNode(PN_UNARY, tc);
1192 if (pn) {
1193 pn->pn_kid = AssignExpr(cx, ts, tc);
1194 if (!pn->pn_kid) {
1195 pn = NULL;
1196 } else {
1197 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1198 ReportBadReturn(cx, tc, JSREPORT_ERROR,
1199 JSMSG_BAD_GENERATOR_RETURN,
1200 JSMSG_BAD_ANON_GENERATOR_RETURN);
1201 pn = NULL;
1202 } else {
1203 pn->pn_type = TOK_RETURN;
1204 pn->pn_op = JSOP_RETURN;
1205 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1210 #else
1211 pn = Statements(cx, ts, tc);
1212 #endif
1214 if (pn) {
1215 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1216 js_PopStatement(tc);
1217 pn->pn_pos.begin.lineno = firstLine;
1219 /* Check for falling off the end of a function that returns a value. */
1220 if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
1221 !CheckFinalReturn(cx, tc, pn)) {
1222 pn = NULL;
1226 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1227 return pn;
1230 static JSAtomListElement *
1231 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1233 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1234 if (!ale)
1235 return NULL;
1237 JSDefinition *dn = (JSDefinition *)
1238 NewNameNode(tc->compiler->context, TS(tc->compiler), pn->pn_atom, tc);
1239 if (!dn)
1240 return NULL;
1242 ALE_SET_DEFN(ale, dn);
1243 dn->pn_defn = true;
1244 dn->pn_dflags |= PND_PLACEHOLDER;
1245 return ale;
1248 static bool
1249 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1251 JS_ASSERT(!pn->pn_used);
1252 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1254 JSHashEntry **hep;
1255 JSAtomListElement *ale = NULL;
1256 JSAtomList *list = NULL;
1258 if (let)
1259 ale = (list = &tc->decls)->rawLookup(atom, hep);
1260 if (!ale)
1261 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1263 if (ale) {
1264 JSDefinition *dn = ALE_DEFN(ale);
1265 if (dn != pn) {
1266 JSParseNode **pnup = &dn->dn_uses;
1267 JSParseNode *pnu;
1268 uintN start = let ? pn->pn_blockid : tc->bodyid;
1270 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1271 JS_ASSERT(pnu->pn_used);
1272 pnu->pn_lexdef = (JSDefinition *) pn;
1273 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1274 pnup = &pnu->pn_link;
1277 if (pnu != dn->dn_uses) {
1278 *pnup = pn->dn_uses;
1279 pn->dn_uses = dn->dn_uses;
1280 dn->dn_uses = pnu;
1282 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1283 list->rawRemove(tc->compiler, ale, hep);
1288 ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1289 if (!ale)
1290 return false;
1291 ALE_SET_DEFN(ale, pn);
1292 pn->pn_defn = true;
1293 pn->pn_dflags &= ~PND_PLACEHOLDER;
1294 return true;
1297 static void
1298 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1300 JS_ASSERT(!pn->pn_used);
1301 JS_ASSERT(!pn->pn_defn);
1302 JS_ASSERT(pn != dn->dn_uses);
1303 pn->pn_link = dn->dn_uses;
1304 dn->dn_uses = pn;
1305 pn->pn_used = true;
1306 pn->pn_lexdef = dn;
1309 static void
1310 ForgetUse(JSParseNode *pn)
1312 if (!pn->pn_used) {
1313 JS_ASSERT(!pn->pn_defn);
1314 return;
1317 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1318 JSParseNode *pnu;
1319 while ((pnu = *pnup) != pn)
1320 pnup = &pnu->pn_link;
1321 *pnup = pn->pn_link;
1322 pn->pn_used = false;
1325 static JSParseNode *
1326 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1328 JSParseNode *lhs = NewOrRecycledNode(tc);
1329 if (!lhs)
1330 return NULL;
1331 *lhs = *pn;
1333 if (pn->pn_used) {
1334 JSDefinition *dn = pn->pn_lexdef;
1335 JSParseNode **pnup = &dn->dn_uses;
1337 while (*pnup != pn)
1338 pnup = &(*pnup)->pn_link;
1339 *pnup = lhs;
1340 lhs->pn_link = pn->pn_link;
1341 pn->pn_link = NULL;
1344 pn->pn_type = TOK_ASSIGN;
1345 pn->pn_op = JSOP_NOP;
1346 pn->pn_arity = PN_BINARY;
1347 pn->pn_used = pn->pn_defn = false;
1348 pn->pn_left = lhs;
1349 pn->pn_right = rhs;
1350 return lhs;
1353 static JSParseNode *
1354 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1357 * If dn is var, const, or let, and it has an initializer, then we must
1358 * rewrite it to be an assignment node, whose freshly allocated left-hand
1359 * side becomes a use of pn.
1361 if (dn->isBindingForm()) {
1362 JSParseNode *rhs = dn->expr();
1363 if (rhs) {
1364 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1365 if (!lhs)
1366 return NULL;
1367 //pn->dn_uses = lhs;
1368 dn = (JSDefinition *) lhs;
1371 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1372 } else if (dn->kind() == JSDefinition::FUNCTION) {
1373 JS_ASSERT(dn->isTopLevel());
1374 JS_ASSERT(dn->pn_op == JSOP_NOP);
1375 dn->pn_type = TOK_NAME;
1376 dn->pn_arity = PN_NAME;
1377 dn->pn_atom = atom;
1380 /* Now make dn no longer a definition, rather a use of pn. */
1381 JS_ASSERT(dn->pn_type == TOK_NAME);
1382 JS_ASSERT(dn->pn_arity == PN_NAME);
1383 JS_ASSERT(dn->pn_atom == atom);
1385 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1386 JS_ASSERT(pnu->pn_used);
1387 JS_ASSERT(!pnu->pn_defn);
1388 pnu->pn_lexdef = (JSDefinition *) pn;
1389 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1391 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1392 pn->dn_uses = dn;
1394 dn->pn_defn = false;
1395 dn->pn_used = true;
1396 dn->pn_lexdef = (JSDefinition *) pn;
1397 dn->pn_cookie = FREE_UPVAR_COOKIE;
1398 dn->pn_dflags &= ~PND_BOUND;
1399 return dn;
1402 static bool
1403 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1405 JSParseNode *argpn, *argsbody;
1407 /* Flag tc so we don't have to lookup arguments on every use. */
1408 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1409 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1412 * Make an argument definition node, distinguished by being in tc->decls
1413 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1414 * list node returned via pn->pn_body.
1416 argpn = NewNameNode(tc->compiler->context, TS(tc->compiler), atom, tc);
1417 if (!argpn)
1418 return false;
1419 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1421 /* Arguments are initialized by definition. */
1422 argpn->pn_dflags |= PND_INITIALIZED;
1423 if (!Define(argpn, atom, tc))
1424 return false;
1426 argsbody = pn->pn_body;
1427 if (!argsbody) {
1428 argsbody = NewParseNode(PN_LIST, tc);
1429 if (!argsbody)
1430 return false;
1431 argsbody->pn_type = TOK_ARGSBODY;
1432 argsbody->pn_op = JSOP_NOP;
1433 argsbody->makeEmpty();
1434 pn->pn_body = argsbody;
1436 argsbody->append(argpn);
1438 argpn->pn_op = JSOP_GETARG;
1439 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1440 argpn->pn_dflags |= PND_BOUND;
1441 return true;
1445 * Compile a JS function body, which might appear as the value of an event
1446 * handler attribute in an HTML <INPUT> tag.
1448 bool
1449 JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1450 const jschar *chars, size_t length,
1451 const char *filename, uintN lineno)
1453 JSCompiler jsc(cx, principals);
1455 if (!jsc.init(chars, length, NULL, filename, lineno))
1456 return false;
1458 /* No early return from after here until the js_FinishArenaPool calls. */
1459 JSArenaPool codePool, notePool;
1460 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
1461 &cx->scriptStackQuota);
1462 JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
1463 &cx->scriptStackQuota);
1465 JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1466 funcg.flags |= TCF_IN_FUNCTION;
1467 funcg.fun = fun;
1468 if (!GenerateBlockId(&funcg, funcg.bodyid))
1469 return NULL;
1471 /* FIXME: make Function format the source for a function definition. */
1472 jsc.tokenStream.tokens[0].type = TOK_NAME;
1473 JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1474 if (fn) {
1475 fn->pn_body = NULL;
1476 fn->pn_cookie = FREE_UPVAR_COOKIE;
1478 uintN nargs = fun->nargs;
1479 if (nargs) {
1480 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1481 if (!names) {
1482 fn = NULL;
1483 } else {
1484 for (uintN i = 0; i < nargs; i++) {
1485 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1486 if (!DefineArg(fn, name, i, &funcg)) {
1487 fn = NULL;
1488 break;
1496 * Farble the body so that it looks like a block statement to js_EmitTree,
1497 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1498 * done parsing, we must fold constants, analyze any nested functions, and
1499 * generate code for this function, including a stop opcode at the end.
1501 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1502 JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1503 if (pn) {
1504 if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1505 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1506 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1507 pn = NULL;
1508 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1509 /* js_FoldConstants reported the error already. */
1510 pn = NULL;
1511 } else if (funcg.functionList &&
1512 !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1513 pn = NULL;
1514 } else {
1515 if (fn->pn_body) {
1516 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1517 fn->pn_body->append(pn);
1518 fn->pn_body->pn_pos = pn->pn_pos;
1519 pn = fn->pn_body;
1522 if (!js_EmitFunctionScript(cx, &funcg, pn))
1523 pn = NULL;
1527 /* Restore saved state and release code generation arenas. */
1528 JS_FinishArenaPool(&codePool);
1529 JS_FinishArenaPool(&notePool);
1530 return pn != NULL;
1534 * Parameter block types for the several Binder functions. We use a common
1535 * helper function signature in order to share code among destructuring and
1536 * simple variable declaration parsers. In the destructuring case, the binder
1537 * function is called indirectly from the variable declaration parser by way
1538 * of CheckDestructuring and its friends.
1540 typedef struct BindData BindData;
1542 typedef JSBool
1543 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1545 struct BindData {
1546 BindData() : fresh(true) {}
1548 JSParseNode *pn; /* name node for definition processing and
1549 error source coordinates */
1550 JSOp op; /* prolog bytecode or nop */
1551 Binder binder; /* binder, discriminates u */
1552 union {
1553 struct {
1554 uintN overflow;
1555 } let;
1557 bool fresh;
1560 static JSBool
1561 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1562 JSLocalKind localKind)
1564 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1567 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1568 * Instead 'var arguments' always restates the predefined property of the
1569 * activation objects whose name is 'arguments'. Assignment to such a
1570 * variable must be handled specially.
1572 if (atom == cx->runtime->atomState.argumentsAtom)
1573 return JS_TRUE;
1575 return js_AddLocal(cx, fun, atom, localKind);
1578 #if JS_HAS_DESTRUCTURING
1580 * Forward declaration to maintain top-down presentation.
1582 static JSParseNode *
1583 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1584 JSTokenType tt);
1586 static JSBool
1587 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1588 JSTreeContext *tc)
1590 JSAtomListElement *ale;
1591 JSParseNode *pn;
1593 /* Flag tc so we don't have to lookup arguments on every use. */
1594 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1595 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1597 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1598 ale = tc->decls.lookup(atom);
1599 pn = data->pn;
1600 if (!ale && !Define(pn, atom, tc))
1601 return JS_FALSE;
1603 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1604 if (localKind != JSLOCAL_NONE) {
1605 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1606 JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1607 return JS_FALSE;
1610 uintN index = tc->fun->u.i.nvars;
1611 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR))
1612 return JS_FALSE;
1613 pn->pn_op = JSOP_SETLOCAL;
1614 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1615 pn->pn_dflags |= PND_BOUND;
1616 return JS_TRUE;
1618 #endif /* JS_HAS_DESTRUCTURING */
1620 JSFunction *
1621 JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1623 JSObject *parent;
1624 JSFunction *fun;
1626 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1629 * Find the global compilation context in order to pre-set the newborn
1630 * function's parent slot to tc->scopeChain. If the global context is a
1631 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1632 * clear parent and proto.
1634 while (tc->parent)
1635 tc = tc->parent;
1636 parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1638 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1639 parent, atom);
1641 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1642 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1643 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1645 return fun;
1648 static JSBool
1649 MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1651 JSTokenType tt;
1653 ts->flags |= TSF_OPERAND;
1654 tt = js_PeekTokenSameLine(cx, ts);
1655 ts->flags &= ~TSF_OPERAND;
1656 if (tt == TOK_ERROR)
1657 return JS_FALSE;
1658 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1659 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1660 JSMSG_SEMI_BEFORE_STMNT);
1661 return JS_FALSE;
1663 (void) js_MatchToken(cx, ts, TOK_SEMI);
1664 return JS_TRUE;
1667 bool
1668 JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
1670 if (!markFunArgs(funbox, tcflags))
1671 return false;
1672 setFunctionKinds(funbox, tcflags);
1673 return true;
1677 * Mark as funargs any functions that reach up to one or more upvars across an
1678 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1680 * function f(o, p) {
1681 * o.m = function o_m(a) {
1682 * function g() { return p; }
1683 * function h() { return a; }
1684 * return g() + h();
1688 * but without this extra marking phase, function g will not be marked as a
1689 * funarg since it is called from within its parent scope. But g reaches up to
1690 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1691 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1692 * nor uses an upvar "above" o_m's level.
1694 * If function g itself contained lambdas that contained non-lambdas that reach
1695 * up above its level, then those non-lambdas would have to be marked too. This
1696 * process is potentially exponential in the number of functions, but generally
1697 * not so complex. But it can't be done during a single recursive traversal of
1698 * the funbox tree, so we must use a work queue.
1700 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1701 * between the static level of the bodies of funbox and its peers (which must
1702 * be funbox->level + 1), and the static level of the nearest upvar among all
1703 * the upvars contained by funbox and its peers. If there are no upvars, return
1704 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1706 static uintN
1707 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1709 uintN allskipmin = FREE_STATIC_LEVEL;
1711 do {
1712 JSParseNode *fn = funbox->node;
1713 JSFunction *fun = (JSFunction *) funbox->object;
1714 int fnlevel = level;
1717 * An eval can leak funbox, functions along its ancestor line, and its
1718 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1719 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1720 * already been marked as funargs by this point. Therefore we have to
1721 * flag only funbox->node and funbox->kids' nodes here.
1723 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1724 fn->setFunArg();
1725 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1726 kid->node->setFunArg();
1730 * Compute in skipmin the least distance from fun's static level up to
1731 * an upvar, whether used directly by fun, or indirectly by a function
1732 * nested in fun.
1734 uintN skipmin = FREE_STATIC_LEVEL;
1735 JSParseNode *pn = fn->pn_body;
1737 if (pn->pn_type == TOK_UPVARS) {
1738 JSAtomList upvars(pn->pn_names);
1739 JS_ASSERT(upvars.count != 0);
1741 JSAtomListIterator iter(&upvars);
1742 JSAtomListElement *ale;
1744 while ((ale = iter()) != NULL) {
1745 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1747 if (!lexdep->isFreeVar()) {
1748 uintN upvarLevel = lexdep->frameLevel();
1750 if (int(upvarLevel) <= fnlevel)
1751 fn->setFunArg();
1753 uintN skip = (funbox->level + 1) - upvarLevel;
1754 if (skip < skipmin)
1755 skipmin = skip;
1761 * If this function escapes, whether directly (the parser detects such
1762 * escapes) or indirectly (because this non-escaping function uses an
1763 * upvar that reaches across an outer function boundary where the outer
1764 * function escapes), enqueue it for further analysis, and bump fnlevel
1765 * to trap any non-escaping children.
1767 if (fn->isFunArg()) {
1768 queue->push(funbox);
1769 fnlevel = int(funbox->level);
1773 * Now process the current function's children, and recalibrate their
1774 * cumulative skipmin to be relative to the current static level.
1776 if (funbox->kids) {
1777 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1779 JS_ASSERT(kidskipmin != 0);
1780 if (kidskipmin != FREE_STATIC_LEVEL) {
1781 --kidskipmin;
1782 if (kidskipmin != 0 && kidskipmin < skipmin)
1783 skipmin = kidskipmin;
1788 * Finally, after we've traversed all of the current function's kids,
1789 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1790 * with allskipmin, but minimize across funbox and all of its siblings,
1791 * to compute our return value.
1793 if (skipmin != FREE_STATIC_LEVEL) {
1794 fun->u.i.skipmin = skipmin;
1795 if (skipmin < allskipmin)
1796 allskipmin = skipmin;
1798 } while ((funbox = funbox->siblings) != NULL);
1800 return allskipmin;
1803 bool
1804 JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1806 JSFunctionBoxQueue queue;
1807 if (!queue.init(functionCount))
1808 return false;
1810 FindFunArgs(funbox, -1, &queue);
1811 while ((funbox = queue.pull()) != NULL) {
1812 JSParseNode *fn = funbox->node;
1813 JS_ASSERT(fn->isFunArg());
1815 JSParseNode *pn = fn->pn_body;
1816 if (pn->pn_type == TOK_UPVARS) {
1817 JSAtomList upvars(pn->pn_names);
1818 JS_ASSERT(upvars.count != 0);
1820 JSAtomListIterator iter(&upvars);
1821 JSAtomListElement *ale;
1823 while ((ale = iter()) != NULL) {
1824 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1826 if (!lexdep->isFreeVar() &&
1827 !lexdep->isFunArg() &&
1828 lexdep->kind() == JSDefinition::FUNCTION) {
1830 * Mark this formerly-Algol-like function as an escaping
1831 * function (i.e., as a funarg), because it is used from a
1832 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1833 * access upvars.
1835 * Progress is guaranteed because we set the funarg flag
1836 * here, which suppresses revisiting this function (thanks
1837 * to the !lexdep->isFunArg() test just above).
1839 lexdep->setFunArg();
1841 JSFunctionBox *afunbox = lexdep->pn_funbox;
1842 queue.push(afunbox);
1845 * Walk over nested functions again, now that we have
1846 * changed the level across which it is unsafe to access
1847 * upvars using the runtime dynamic link (frame chain).
1849 if (afunbox->kids)
1850 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1855 return true;
1858 static uint32
1859 MinBlockId(JSParseNode *fn, uint32 id)
1861 if (fn->pn_blockid < id)
1862 return false;
1863 if (fn->pn_defn) {
1864 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1865 if (pn->pn_blockid < id)
1866 return false;
1869 return true;
1872 static bool
1873 OneBlockId(JSParseNode *fn, uint32 id)
1875 if (fn->pn_blockid != id)
1876 return false;
1877 if (fn->pn_defn) {
1878 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1879 if (pn->pn_blockid != id)
1880 return false;
1883 return true;
1886 void
1887 JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
1889 #ifdef JS_FUNCTION_METERING
1890 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1891 #else
1892 # define FUN_METER(x) ((void)0)
1893 #endif
1894 JSFunctionBox *parent = funbox->parent;
1896 for (;;) {
1897 JSParseNode *fn = funbox->node;
1899 if (funbox->kids)
1900 setFunctionKinds(funbox->kids, tcflags);
1902 JSParseNode *pn = fn->pn_body;
1903 JSFunction *fun = (JSFunction *) funbox->object;
1905 FUN_METER(allfun);
1906 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1907 FUN_METER(heavy);
1908 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
1909 } else if (pn->pn_type != TOK_UPVARS) {
1911 * No lexical dependencies => null closure, for best performance.
1912 * A null closure needs no scope chain, but alas we've coupled
1913 * principals-finding to scope (for good fundamental reasons, but
1914 * the implementation overloads the parent slot and we should fix
1915 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1917 * In more detail: the ES3 spec allows the implementation to create
1918 * "joined function objects", or not, at its discretion. But real-
1919 * world implementations always create unique function objects for
1920 * closures, and this can be detected via mutation. Open question:
1921 * do popular implementations create unique function objects for
1922 * null closures?
1924 * FIXME: bug 476950.
1926 FUN_METER(nofreeupvar);
1927 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1928 } else {
1929 JSAtomList upvars(pn->pn_names);
1930 JS_ASSERT(upvars.count != 0);
1932 JSAtomListIterator iter(&upvars);
1933 JSAtomListElement *ale;
1935 if (!fn->isFunArg()) {
1937 * This function is Algol-like, it never escapes. So long as it
1938 * does not assign to outer variables, it needs only an upvars
1939 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1940 * bytecode to reach up the frame stack at runtime based on
1941 * those upvars' cookies.
1943 * Any assignments to upvars from functions called by this one
1944 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1945 * which load from stack homes when interpreting or from native
1946 * stack slots when executing a trace.
1948 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1949 * nested function to assign to an outer lexical variable, so
1950 * we defer adding yet more code footprint in the absence of
1951 * evidence motivating these opcodes.
1953 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
1954 uintN nupvars = 0;
1957 * Check that at least one outer lexical binding was assigned
1958 * to (global variables don't count). This is conservative: we
1959 * could limit assignments to those in the current function,
1960 * but that's too much work. As with flat closures (handled
1961 * below), we optimize for the case where outer bindings are
1962 * not reassigned anywhere.
1964 while ((ale = iter()) != NULL) {
1965 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1967 if (!lexdep->isFreeVar()) {
1968 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
1969 ++nupvars;
1970 if (lexdep->isAssigned())
1971 break;
1974 if (!ale)
1975 mutation = false;
1977 if (nupvars == 0) {
1978 FUN_METER(onlyfreevar);
1979 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1980 } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
1982 * Algol-like functions can read upvars using the dynamic
1983 * link (cx->fp/fp->down). They do not need to entrain and
1984 * search their environment.
1986 FUN_METER(display);
1987 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1988 } else {
1989 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
1990 FUN_METER(setupvar);
1992 } else {
1993 uintN nupvars = 0;
1996 * For each lexical dependency from this closure to an outer
1997 * binding, analyze whether it is safe to copy the binding's
1998 * value into a flat closure slot when the closure is formed.
2000 while ((ale = iter()) != NULL) {
2001 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2003 if (!lexdep->isFreeVar()) {
2004 ++nupvars;
2007 * Consider the current function (the lambda, innermost
2008 * below) using a var x defined two static levels up:
2010 * function f() {
2011 * // z = g();
2012 * var x = 42;
2013 * function g() {
2014 * return function () { return x; };
2016 * return g();
2019 * So long as (1) the initialization in 'var x = 42'
2020 * dominates all uses of g and (2) x is not reassigned,
2021 * it is safe to optimize the lambda to a flat closure.
2022 * Uncommenting the early call to g makes it unsafe to
2023 * so optimize (z could name a global setter that calls
2024 * its argument).
2026 JSFunctionBox *afunbox = funbox;
2027 uintN lexdepLevel = lexdep->frameLevel();
2029 JS_ASSERT(lexdepLevel <= funbox->level);
2030 while (afunbox->level != lexdepLevel) {
2031 afunbox = afunbox->parent;
2034 * afunbox can't be null because we are sure
2035 * to find a function box whose level == lexdepLevel
2036 * before walking off the top of the funbox tree.
2037 * See bug 493260 comments 16-18.
2039 * Assert but check anyway, to check future changes
2040 * that bind eval upvars in the parser.
2042 JS_ASSERT(afunbox);
2045 * If this function is reaching up across an
2046 * enclosing funarg, we cannot make a flat
2047 * closure. The display stops working once the
2048 * funarg escapes.
2050 if (!afunbox || afunbox->node->isFunArg())
2051 goto break2;
2055 * If afunbox's function (which is at the same level as
2056 * lexdep) is in a loop, pessimistically assume the
2057 * variable initializer may be in the same loop. A flat
2058 * closure would then be unsafe, as the captured
2059 * variable could be assigned after the closure is
2060 * created. See bug 493232.
2062 if (afunbox->inLoop)
2063 break;
2066 * with and eval defeat lexical scoping; eval anywhere
2067 * in a variable's scope can assign to it. Both defeat
2068 * the flat closure optimization. The parser detects
2069 * these cases and flags the function heavyweight.
2071 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags)
2072 & TCF_FUN_HEAVYWEIGHT) {
2073 break;
2077 * If afunbox's function is not a lambda, it will be
2078 * hoisted, so it could capture the undefined value
2079 * that by default initializes var/let/const
2080 * bindings. And if lexdep is a function that comes at
2081 * (meaning a function refers to its own name) or
2082 * strictly after afunbox, we also break to defeat the
2083 * flat closure optimization.
2085 JSFunction *afun = (JSFunction *) afunbox->object;
2086 if (!(afun->flags & JSFUN_LAMBDA)) {
2087 if (lexdep->isBindingForm())
2088 break;
2089 if (lexdep->pn_pos >= afunbox->node->pn_pos)
2090 break;
2093 if (!lexdep->isInitialized())
2094 break;
2096 JSDefinition::Kind lexdepKind = lexdep->kind();
2097 if (lexdepKind != JSDefinition::CONST) {
2098 if (lexdep->isAssigned())
2099 break;
2102 * Any formal could be mutated behind our back via
2103 * the arguments object, so deoptimize if the outer
2104 * function uses arguments.
2106 * In a Function constructor call where the final
2107 * argument -- the body source for the function to
2108 * create -- contains a nested function definition
2109 * or expression, afunbox->parent will be null. The
2110 * body source might use |arguments| outside of any
2111 * nested functions it may contain, so we have to
2112 * check the tcflags parameter that was passed in
2113 * from JSCompiler::compileFunctionBody.
2115 if (lexdepKind == JSDefinition::ARG &&
2116 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2117 TCF_FUN_USES_ARGUMENTS)) {
2118 break;
2123 * Check quick-and-dirty dominance relation. Function
2124 * definitions dominate their uses thanks to hoisting.
2125 * Other binding forms hoist as undefined, of course,
2126 * so check forward-reference and blockid relations.
2128 if (lexdepKind != JSDefinition::FUNCTION) {
2130 * Watch out for code such as
2132 * (function () {
2133 * ...
2134 * var jQuery = ... = function (...) {
2135 * return new jQuery.foo.bar(baz);
2137 * ...
2138 * })();
2140 * where the jQuery var is not reassigned, but of
2141 * course is not initialized at the time that the
2142 * would-be-flat closure containing the jQuery
2143 * upvar is formed.
2145 if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2146 break;
2148 if (lexdep->isTopLevel()
2149 ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2150 : !lexdep->isBlockChild() ||
2151 !afunbox->node->isBlockChild() ||
2152 !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2153 break;
2159 break2:
2160 if (nupvars == 0) {
2161 FUN_METER(onlyfreevar);
2162 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2163 } else if (!ale) {
2165 * We made it all the way through the upvar loop, so it's
2166 * safe to optimize to a flat closure.
2168 FUN_METER(flat);
2169 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2170 switch (PN_OP(fn)) {
2171 case JSOP_DEFFUN:
2172 fn->pn_op = JSOP_DEFFUN_FC;
2173 break;
2174 case JSOP_DEFLOCALFUN:
2175 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2176 break;
2177 case JSOP_LAMBDA:
2178 fn->pn_op = JSOP_LAMBDA_FC;
2179 break;
2180 default:
2181 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2182 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2184 } else {
2185 FUN_METER(badfunarg);
2190 if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2191 if (pn->pn_type != TOK_UPVARS) {
2192 if (parent)
2193 parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2194 } else {
2195 JSAtomList upvars(pn->pn_names);
2196 JS_ASSERT(upvars.count != 0);
2198 JSAtomListIterator iter(&upvars);
2199 JSAtomListElement *ale;
2202 * One or more upvars cannot be safely snapshot into a flat
2203 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2204 * all upvars, and for each non-free upvar, ensure that its
2205 * containing function has been flagged as heavyweight.
2207 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2208 * generating any code for a tree of nested functions.
2210 while ((ale = iter()) != NULL) {
2211 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2213 if (!lexdep->isFreeVar()) {
2214 JSFunctionBox *afunbox = funbox->parent;
2215 uintN lexdepLevel = lexdep->frameLevel();
2217 while (afunbox) {
2219 * NB: afunbox->level is the static level of
2220 * the definition or expression of the function
2221 * parsed into afunbox, not the static level of
2222 * its body. Therefore we must add 1 to match
2223 * lexdep's level to find the afunbox whose
2224 * body contains the lexdep definition.
2226 if (afunbox->level + 1U == lexdepLevel ||
2227 (lexdepLevel == 0 && lexdep->isLet())) {
2228 afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2229 break;
2231 afunbox = afunbox->parent;
2233 if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2234 tcflags |= TCF_FUN_HEAVYWEIGHT;
2240 funbox = funbox->siblings;
2241 if (!funbox)
2242 break;
2243 JS_ASSERT(funbox->parent == parent);
2245 #undef FUN_METER
2248 const char js_argument_str[] = "argument";
2249 const char js_variable_str[] = "variable";
2250 const char js_unknown_str[] = "unknown";
2252 const char *
2253 JSDefinition::kindString(Kind kind)
2255 static const char *table[] = {
2256 js_var_str, js_const_str, js_let_str,
2257 js_function_str, js_argument_str, js_unknown_str
2260 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2261 return table[kind];
2264 static JSFunctionBox *
2265 EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2266 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2268 JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2269 if (!fun)
2270 return NULL;
2272 /* Create box for fun->object early to protect against last-ditch GC. */
2273 JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2274 if (!funbox)
2275 return NULL;
2277 /* Initialize non-default members of funtc. */
2278 funtc->flags |= funbox->tcflags;
2279 funtc->blockidGen = tc->blockidGen;
2280 if (!GenerateBlockId(funtc, funtc->bodyid))
2281 return NULL;
2282 funtc->fun = fun;
2283 funtc->funbox = funbox;
2284 funtc->parent = tc;
2285 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2286 return NULL;
2288 return funbox;
2291 static bool
2292 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2293 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2295 tc->blockidGen = funtc->blockidGen;
2297 fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2299 fn->pn_dflags |= PND_INITIALIZED;
2300 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2301 fn->pn_dflags & PND_TOPLEVEL);
2302 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2303 fn->pn_dflags |= PND_BLOCKCHILD;
2306 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2307 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2308 * params and body. We do this only if there are lexical dependencies not
2309 * satisfied by the function's declarations, to avoid penalizing functions
2310 * that use only their arguments and other local bindings.
2312 if (funtc->lexdeps.count != 0) {
2313 JSAtomListIterator iter(&funtc->lexdeps);
2314 JSAtomListElement *ale;
2315 int foundCallee = 0;
2317 while ((ale = iter()) != NULL) {
2318 JSAtom *atom = ALE_ATOM(ale);
2319 JSDefinition *dn = ALE_DEFN(ale);
2320 JS_ASSERT(dn->isPlaceholder());
2322 if (atom == funAtom && lambda != 0) {
2323 dn->pn_op = JSOP_CALLEE;
2324 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2325 dn->pn_dflags |= PND_BOUND;
2328 * If this named function expression uses its own name other
2329 * than to call itself, flag this function as using arguments,
2330 * as if it had used arguments.callee instead of its own name.
2332 * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2333 * we are out of tcflags bits at the moment. If it deoptimizes
2334 * code unfairly (see JSCompiler::setFunctionKinds, where this
2335 * flag is interpreted in its broader sense, not only to mean
2336 * "this function might leak arguments.callee"), we can perhaps
2337 * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2338 * use that more precisely, both here and for unnamed function
2339 * expressions.
2341 if (dn->isFunArg())
2342 fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
2343 foundCallee = 1;
2344 continue;
2347 if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2348 dn->isAssigned()) {
2350 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2351 * any use of dn in funtc assigns. See NoteLValue for the easy
2352 * backward-reference case; this is the hard forward-reference
2353 * case where we pay a higher price.
2355 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2356 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2357 fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2358 break;
2363 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2364 if (!outer_ale)
2365 outer_ale = tc->lexdeps.lookup(atom);
2366 if (outer_ale) {
2368 * Insert dn's uses list at the front of outer_dn's list.
2370 * Without loss of generality or correctness, we allow a dn to
2371 * be in inner and outer lexdeps, since the purpose of lexdeps
2372 * is one-pass coordination of name use and definition across
2373 * functions, and if different dn's are used we'll merge lists
2374 * when leaving the inner function.
2376 * The dn == outer_dn case arises with generator expressions
2377 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2378 * case), and nowhere else, currently.
2380 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2382 if (dn != outer_dn) {
2383 JSParseNode **pnup = &dn->dn_uses;
2384 JSParseNode *pnu;
2386 while ((pnu = *pnup) != NULL) {
2387 pnu->pn_lexdef = outer_dn;
2388 pnup = &pnu->pn_link;
2392 * Make dn be a use that redirects to outer_dn, because we
2393 * can't replace dn with outer_dn in all the pn_namesets in
2394 * the AST where it may be. Instead we make it forward to
2395 * outer_dn. See JSDefinition::resolve.
2397 *pnup = outer_dn->dn_uses;
2398 outer_dn->dn_uses = dn;
2399 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2400 dn->pn_defn = false;
2401 dn->pn_used = true;
2402 dn->pn_lexdef = outer_dn;
2404 } else {
2405 /* Add an outer lexical dependency for ale's definition. */
2406 outer_ale = tc->lexdeps.add(tc->compiler, atom);
2407 if (!outer_ale)
2408 return false;
2409 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2413 if (funtc->lexdeps.count - foundCallee != 0) {
2414 JSParseNode *body = fn->pn_body;
2416 fn->pn_body = NewParseNode(PN_NAMESET, tc);
2417 if (!fn->pn_body)
2418 return false;
2420 fn->pn_body->pn_type = TOK_UPVARS;
2421 fn->pn_body->pn_pos = body->pn_pos;
2422 if (foundCallee)
2423 funtc->lexdeps.remove(tc->compiler, funAtom);
2424 fn->pn_body->pn_names = funtc->lexdeps;
2425 fn->pn_body->pn_tree = body;
2428 funtc->lexdeps.clear();
2431 return true;
2434 static JSParseNode *
2435 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2436 uintN lambda)
2438 JSOp op;
2439 JSParseNode *pn, *body, *result;
2440 JSTokenType tt;
2441 JSAtom *funAtom;
2442 JSAtomListElement *ale;
2443 #if JS_HAS_DESTRUCTURING
2444 JSParseNode *item, *list = NULL;
2445 bool destructuringArg = false, duplicatedArg = false;
2446 #endif
2448 /* Make a TOK_FUNCTION node. */
2449 #if JS_HAS_GETTER_SETTER
2450 op = CURRENT_TOKEN(ts).t_op;
2451 #endif
2452 pn = NewParseNode(PN_FUNC, tc);
2453 if (!pn)
2454 return NULL;
2455 pn->pn_body = NULL;
2456 pn->pn_cookie = FREE_UPVAR_COOKIE;
2459 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2460 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2462 * Also treat function sub-statements (non-lambda, non-top-level functions)
2463 * as escaping funargs, since we can't statically analyze their definitions
2464 * and uses.
2466 bool topLevel = tc->atTopLevel();
2467 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2469 /* Scan the optional function name into funAtom. */
2470 ts->flags |= TSF_KEYWORD_IS_NAME;
2471 tt = js_GetToken(cx, ts);
2472 ts->flags &= ~TSF_KEYWORD_IS_NAME;
2473 if (tt == TOK_NAME) {
2474 funAtom = CURRENT_TOKEN(ts).t_atom;
2475 } else {
2476 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
2477 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2478 JSMSG_SYNTAX_ERROR);
2479 return NULL;
2481 funAtom = NULL;
2482 js_UngetToken(ts);
2486 * Record names for function statements in tc->decls so we know when to
2487 * avoid optimizing variable references that might name a function.
2489 if (lambda == 0 && funAtom) {
2490 ale = tc->decls.lookup(funAtom);
2491 if (ale) {
2492 JSDefinition *dn = ALE_DEFN(ale);
2493 JSDefinition::Kind dn_kind = dn->kind();
2495 JS_ASSERT(!dn->pn_used);
2496 JS_ASSERT(dn->pn_defn);
2498 if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2499 const char *name = js_AtomToPrintableString(cx, funAtom);
2500 if (!name ||
2501 !js_ReportCompileErrorNumber(cx, ts, NULL,
2502 (dn_kind != JSDefinition::CONST)
2503 ? JSREPORT_WARNING | JSREPORT_STRICT
2504 : JSREPORT_ERROR,
2505 JSMSG_REDECLARED_VAR,
2506 JSDefinition::kindString(dn_kind),
2507 name)) {
2508 return NULL;
2512 if (topLevel) {
2513 ALE_SET_DEFN(ale, pn);
2514 pn->pn_defn = true;
2515 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2517 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2518 return NULL;
2520 } else if (topLevel) {
2522 * If this function was used before it was defined, claim the
2523 * pre-created definition node for this function that PrimaryExpr
2524 * put in tc->lexdeps on first forward reference, and recycle pn.
2526 JSHashEntry **hep;
2528 ale = tc->lexdeps.rawLookup(funAtom, hep);
2529 if (ale) {
2530 JSDefinition *fn = ALE_DEFN(ale);
2532 JS_ASSERT(fn->pn_defn);
2533 fn->pn_type = TOK_FUNCTION;
2534 fn->pn_arity = PN_FUNC;
2535 fn->pn_pos.begin = pn->pn_pos.begin;
2536 fn->pn_body = NULL;
2537 fn->pn_cookie = FREE_UPVAR_COOKIE;
2539 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2540 RecycleTree(pn, tc);
2541 pn = fn;
2544 if (!Define(pn, funAtom, tc))
2545 return NULL;
2549 * A function nested at top level inside another's body needs only a
2550 * local variable to bind its name to its value, and not an activation
2551 * object property (it might also need the activation property, if the
2552 * outer function contains with statements, e.g., but the stack slot
2553 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2554 * JSOP_GETLOCAL bytecode).
2556 if (topLevel) {
2557 pn->pn_dflags |= PND_TOPLEVEL;
2559 if (tc->flags & TCF_IN_FUNCTION) {
2560 JSLocalKind localKind;
2561 uintN index;
2564 * Define a local in the outer function so that BindNameToSlot
2565 * can properly optimize accesses. Note that we need a local
2566 * variable, not an argument, for the function statement. Thus
2567 * we add a variable even if a parameter with the given name
2568 * already exists.
2570 localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2571 switch (localKind) {
2572 case JSLOCAL_NONE:
2573 case JSLOCAL_ARG:
2574 index = tc->fun->u.i.nvars;
2575 if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2576 return NULL;
2577 /* FALL THROUGH */
2579 case JSLOCAL_VAR:
2580 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2581 pn->pn_dflags |= PND_BOUND;
2582 break;
2584 default:;
2590 /* Initialize early for possible flags mutation via DestructuringExpr. */
2591 JSTreeContext funtc(tc->compiler);
2593 JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2594 if (!funbox)
2595 return NULL;
2597 JSFunction *fun = (JSFunction *) funbox->object;
2599 #if JS_HAS_GETTER_SETTER
2600 if (op != JSOP_NOP)
2601 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2602 #endif
2604 /* Now parse formal argument list and compute fun->nargs. */
2605 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2606 if (!js_MatchToken(cx, ts, TOK_RP)) {
2607 do {
2608 tt = js_GetToken(cx, ts);
2609 switch (tt) {
2610 #if JS_HAS_DESTRUCTURING
2611 case TOK_LB:
2612 case TOK_LC:
2614 BindData data;
2615 JSParseNode *lhs, *rhs;
2616 jsint slot;
2618 /* See comment below in the TOK_NAME case. */
2619 if (duplicatedArg)
2620 goto report_dup_and_destructuring;
2621 destructuringArg = true;
2624 * A destructuring formal parameter turns into one or more
2625 * local variables initialized from properties of a single
2626 * anonymous positional parameter, so here we must tweak our
2627 * binder and its data.
2629 data.pn = NULL;
2630 data.op = JSOP_DEFVAR;
2631 data.binder = BindDestructuringArg;
2632 lhs = DestructuringExpr(cx, &data, &funtc, tt);
2633 if (!lhs)
2634 return NULL;
2637 * Adjust fun->nargs to count the single anonymous positional
2638 * parameter that is to be destructured.
2640 slot = fun->nargs;
2641 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
2642 return NULL;
2645 * Synthesize a destructuring assignment from the single
2646 * anonymous positional parameter into the destructuring
2647 * left-hand-side expression and accumulate it in list.
2649 rhs = NewNameNode(cx, ts, cx->runtime->atomState.emptyAtom, &funtc);
2650 if (!rhs)
2651 return NULL;
2652 rhs->pn_type = TOK_NAME;
2653 rhs->pn_op = JSOP_GETARG;
2654 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2655 rhs->pn_dflags |= PND_BOUND;
2657 item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2658 if (!item)
2659 return NULL;
2660 if (!list) {
2661 list = NewParseNode(PN_LIST, &funtc);
2662 if (!list)
2663 return NULL;
2664 list->pn_type = TOK_COMMA;
2665 list->makeEmpty();
2667 list->append(item);
2668 break;
2670 #endif /* JS_HAS_DESTRUCTURING */
2672 case TOK_NAME:
2675 * Check for a duplicate parameter name, a "feature" that
2676 * ECMA-262 requires. This is a SpiderMonkey strict warning,
2677 * soon to be an ES3.1 strict error.
2679 * Further, if any argument is a destructuring pattern, forbid
2680 * duplicates. We will report the error either now if we have
2681 * seen a destructuring pattern already, or later when we find
2682 * the first pattern.
2684 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2685 if (JS_HAS_STRICT_OPTION(cx) &&
2686 js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2687 #if JS_HAS_DESTRUCTURING
2688 if (destructuringArg)
2689 goto report_dup_and_destructuring;
2690 duplicatedArg = true;
2691 #endif
2692 const char *name = js_AtomToPrintableString(cx, atom);
2693 if (!name ||
2694 !js_ReportCompileErrorNumber(cx, TS(funtc.compiler),
2695 NULL,
2696 JSREPORT_WARNING |
2697 JSREPORT_STRICT,
2698 JSMSG_DUPLICATE_FORMAL,
2699 name)) {
2700 return NULL;
2703 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2704 return NULL;
2705 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2706 return NULL;
2707 break;
2710 default:
2711 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2712 JSMSG_MISSING_FORMAL);
2713 /* FALL THROUGH */
2714 case TOK_ERROR:
2715 return NULL;
2717 #if JS_HAS_DESTRUCTURING
2718 report_dup_and_destructuring:
2719 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
2720 JSREPORT_ERROR,
2721 JSMSG_DESTRUCT_DUP_ARG);
2722 return NULL;
2723 #endif
2725 } while (js_MatchToken(cx, ts, TOK_COMMA));
2727 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2730 #if JS_HAS_EXPR_CLOSURES
2731 ts->flags |= TSF_OPERAND;
2732 tt = js_GetToken(cx, ts);
2733 ts->flags &= ~TSF_OPERAND;
2734 if (tt != TOK_LC) {
2735 js_UngetToken(ts);
2736 fun->flags |= JSFUN_EXPR_CLOSURE;
2738 #else
2739 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2740 #endif
2742 body = FunctionBody(cx, ts, &funtc);
2743 if (!body)
2744 return NULL;
2746 #if JS_HAS_EXPR_CLOSURES
2747 if (tt == TOK_LC)
2748 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2749 else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2750 return NULL;
2751 #else
2752 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2753 #endif
2754 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2756 #if JS_HAS_DESTRUCTURING
2758 * If there were destructuring formal parameters, prepend the initializing
2759 * comma expression that we synthesized to body. If the body is a lexical
2760 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2761 * parameter destructuring code without bracing the decompilation of the
2762 * function body's lexical scope.
2764 if (list) {
2765 if (body->pn_arity != PN_LIST) {
2766 JSParseNode *block;
2768 block = NewParseNode(PN_LIST, tc);
2769 if (!block)
2770 return NULL;
2771 block->pn_type = TOK_SEQ;
2772 block->pn_pos = body->pn_pos;
2773 block->initList(body);
2775 body = block;
2778 item = NewParseNode(PN_UNARY, tc);
2779 if (!item)
2780 return NULL;
2782 item->pn_type = TOK_SEMI;
2783 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2784 item->pn_kid = list;
2785 item->pn_next = body->pn_head;
2786 body->pn_head = item;
2787 if (body->pn_tail == &body->pn_head)
2788 body->pn_tail = &item->pn_next;
2789 ++body->pn_count;
2790 body->pn_xflags |= PNX_DESTRUCT;
2792 #endif
2795 * If we collected flags that indicate nested heavyweight functions, or
2796 * this function contains heavyweight-making statements (with statement,
2797 * visible eval call, or assignment to 'arguments'), flag the function as
2798 * heavyweight (requiring a call object per invocation).
2800 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2801 fun->flags |= JSFUN_HEAVYWEIGHT;
2802 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2803 } else {
2805 * If this function is a named statement function not at top-level
2806 * (i.e. not a top-level function definiton or expression), then our
2807 * enclosing function, if any, must be heavyweight.
2809 if (!topLevel && lambda == 0 && funAtom)
2810 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2813 result = pn;
2814 if (lambda != 0) {
2816 * ECMA ed. 3 standard: function expression, possibly anonymous.
2818 op = JSOP_LAMBDA;
2819 } else if (!funAtom) {
2821 * If this anonymous function definition is *not* embedded within a
2822 * larger expression, we treat it as an expression statement, not as
2823 * a function declaration -- and not as a syntax error (as ECMA-262
2824 * Edition 3 would have it). Backward compatibility must trump all,
2825 * unless JSOPTION_ANONFUNFIX is set.
2827 result = NewParseNode(PN_UNARY, tc);
2828 if (!result)
2829 return NULL;
2830 result->pn_type = TOK_SEMI;
2831 result->pn_pos = pn->pn_pos;
2832 result->pn_kid = pn;
2833 op = JSOP_LAMBDA;
2834 } else if (!topLevel) {
2836 * ECMA ed. 3 extension: a function expression statement not at the
2837 * top level, e.g., in a compound statement such as the "then" part
2838 * of an "if" statement, binds a closure only if control reaches that
2839 * sub-statement.
2841 op = JSOP_DEFFUN;
2842 } else {
2843 op = JSOP_NOP;
2846 funbox->kids = funtc.functionList;
2848 pn->pn_funbox = funbox;
2849 pn->pn_op = op;
2850 if (pn->pn_body) {
2851 pn->pn_body->append(body);
2852 pn->pn_body->pn_pos = body->pn_pos;
2853 } else {
2854 pn->pn_body = body;
2857 pn->pn_blockid = tc->blockid();
2859 if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2860 return NULL;
2862 return result;
2865 static JSParseNode *
2866 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2868 return FunctionDef(cx, ts, tc, 0);
2871 static JSParseNode *
2872 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2874 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
2878 * Parse the statements in a block, creating a TOK_LC node that lists the
2879 * statements' trees. If called from block-parsing code, the caller must
2880 * match { before and } after.
2882 static JSParseNode *
2883 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2885 JSParseNode *pn, *pn2, *saveBlock;
2886 JSTokenType tt;
2888 JS_CHECK_RECURSION(cx, return NULL);
2890 pn = NewParseNode(PN_LIST, tc);
2891 if (!pn)
2892 return NULL;
2893 pn->pn_type = TOK_LC;
2894 pn->makeEmpty();
2895 pn->pn_blockid = tc->blockid();
2896 saveBlock = tc->blockNode;
2897 tc->blockNode = pn;
2899 for (;;) {
2900 ts->flags |= TSF_OPERAND;
2901 tt = js_PeekToken(cx, ts);
2902 ts->flags &= ~TSF_OPERAND;
2903 if (tt <= TOK_EOF || tt == TOK_RC) {
2904 if (tt == TOK_ERROR) {
2905 if (ts->flags & TSF_EOF)
2906 ts->flags |= TSF_UNEXPECTED_EOF;
2907 return NULL;
2909 break;
2911 pn2 = Statement(cx, ts, tc);
2912 if (!pn2) {
2913 if (ts->flags & TSF_EOF)
2914 ts->flags |= TSF_UNEXPECTED_EOF;
2915 return NULL;
2918 if (pn2->pn_type == TOK_FUNCTION) {
2920 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2921 * level function definitions that should be processed before the
2922 * rest of nodes.
2924 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2925 * is relevant only for function definitions not at top-level,
2926 * which we call function statements.
2928 if (tc->atTopLevel())
2929 pn->pn_xflags |= PNX_FUNCDEFS;
2930 else
2931 tc->flags |= TCF_HAS_FUNCTION_STMT;
2933 pn->append(pn2);
2937 * Handle the case where there was a let declaration under this block. If
2938 * it replaced tc->blockNode with a new block node then we must refresh pn
2939 * and then restore tc->blockNode.
2941 if (tc->blockNode != pn)
2942 pn = tc->blockNode;
2943 tc->blockNode = saveBlock;
2945 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2946 return pn;
2949 static JSParseNode *
2950 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2952 JSParseNode *pn;
2954 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
2955 pn = ParenExpr(cx, ts, tc, NULL, NULL);
2956 if (!pn)
2957 return NULL;
2958 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
2961 * Check for (a = b) and warn about possible (a == b) mistype iff b's
2962 * operator has greater precedence than ==.
2964 if (pn->pn_type == TOK_ASSIGN &&
2965 pn->pn_op == JSOP_NOP &&
2966 pn->pn_right->pn_type > TOK_EQOP)
2968 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2969 JSREPORT_WARNING | JSREPORT_STRICT,
2970 JSMSG_EQUAL_AS_ASSIGN,
2971 "")) {
2972 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 while (pn->pn_type == TOK_RP)
3465 pn = pn->pn_kid;
3467 switch (pn->pn_type) {
3468 case TOK_NAME:
3469 NoteLValue(cx, pn, tc);
3470 /* FALL THROUGH */
3472 case TOK_DOT:
3473 case TOK_LB:
3474 pn->pn_op = JSOP_SETNAME;
3475 break;
3477 #if JS_HAS_LVALUE_RETURN
3478 case TOK_LP:
3479 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3480 return JS_FALSE;
3481 break;
3482 #endif
3484 #if JS_HAS_XML_SUPPORT
3485 case TOK_UNARYOP:
3486 if (pn->pn_op == JSOP_XMLNAME) {
3487 pn->pn_op = JSOP_BINDXMLNAME;
3488 break;
3490 /* FALL THROUGH */
3491 #endif
3493 default:
3494 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3495 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3496 return JS_FALSE;
3499 return JS_TRUE;
3502 typedef struct FindPropValData {
3503 uint32 numvars; /* # of destructuring vars in left side */
3504 uint32 maxstep; /* max # of steps searching right side */
3505 JSDHashTable table; /* hash table for O(1) right side search */
3506 } FindPropValData;
3508 typedef struct FindPropValEntry {
3509 JSDHashEntryHdr hdr;
3510 JSParseNode *pnkey;
3511 JSParseNode *pnval;
3512 } FindPropValEntry;
3514 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3515 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3516 ((pnkey)->pn_type == TOK_NUMBER || \
3517 (pnkey)->pn_type == TOK_STRING || \
3518 (pnkey)->pn_type == TOK_NAME)) || \
3519 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3521 static JSDHashNumber
3522 HashFindPropValKey(JSDHashTable *table, const void *key)
3524 const JSParseNode *pnkey = (const JSParseNode *)key;
3526 ASSERT_VALID_PROPERTY_KEY(pnkey);
3527 return (pnkey->pn_type == TOK_NUMBER)
3528 ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
3529 JSDOUBLE_LO32(pnkey->pn_dval))
3530 : ATOM_HASH(pnkey->pn_atom);
3533 static JSBool
3534 MatchFindPropValEntry(JSDHashTable *table,
3535 const JSDHashEntryHdr *entry,
3536 const void *key)
3538 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3539 const JSParseNode *pnkey = (const JSParseNode *)key;
3541 ASSERT_VALID_PROPERTY_KEY(pnkey);
3542 return pnkey->pn_type == fpve->pnkey->pn_type &&
3543 ((pnkey->pn_type == TOK_NUMBER)
3544 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3545 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3548 static const JSDHashTableOps FindPropValOps = {
3549 JS_DHashAllocTable,
3550 JS_DHashFreeTable,
3551 HashFindPropValKey,
3552 MatchFindPropValEntry,
3553 JS_DHashMoveEntryStub,
3554 JS_DHashClearEntryStub,
3555 JS_DHashFinalizeStub,
3556 NULL
3559 #define STEP_HASH_THRESHOLD 10
3560 #define BIG_DESTRUCTURING 5
3561 #define BIG_OBJECT_INIT 20
3563 static JSParseNode *
3564 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3566 FindPropValEntry *entry;
3567 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3568 uint32 step;
3570 /* If we have a hash table, use it as the sole source of truth. */
3571 if (data->table.ops) {
3572 entry = (FindPropValEntry *)
3573 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3574 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3577 /* If pn is not an object initialiser node, we can't do anything here. */
3578 if (pn->pn_type != TOK_RC)
3579 return NULL;
3582 * We must search all the way through pn's list, to handle the case of an
3583 * id duplicated for two or more property initialisers.
3585 pnhit = NULL;
3586 step = 0;
3587 ASSERT_VALID_PROPERTY_KEY(pnid);
3588 pnhead = pn->pn_head;
3589 if (pnid->pn_type == TOK_NUMBER) {
3590 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3591 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3592 if (pnprop->pn_op == JSOP_NOP) {
3593 pnkey = pnprop->pn_left;
3594 ASSERT_VALID_PROPERTY_KEY(pnkey);
3595 if (pnkey->pn_type == TOK_NUMBER &&
3596 pnkey->pn_dval == pnid->pn_dval) {
3597 pnhit = pnprop;
3599 ++step;
3602 } else {
3603 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3604 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3605 if (pnprop->pn_op == JSOP_NOP) {
3606 pnkey = pnprop->pn_left;
3607 ASSERT_VALID_PROPERTY_KEY(pnkey);
3608 if (pnkey->pn_type == pnid->pn_type &&
3609 pnkey->pn_atom == pnid->pn_atom) {
3610 pnhit = pnprop;
3612 ++step;
3616 if (!pnhit)
3617 return NULL;
3619 /* Hit via full search -- see whether it's time to create the hash table. */
3620 JS_ASSERT(!data->table.ops);
3621 if (step > data->maxstep) {
3622 data->maxstep = step;
3623 if (step >= STEP_HASH_THRESHOLD &&
3624 data->numvars >= BIG_DESTRUCTURING &&
3625 pn->pn_count >= BIG_OBJECT_INIT &&
3626 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3627 sizeof(FindPropValEntry),
3628 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3630 for (pn = pnhead; pn; pn = pn->pn_next) {
3631 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3632 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3633 entry = (FindPropValEntry *)
3634 JS_DHashTableOperate(&data->table, pn->pn_left,
3635 JS_DHASH_ADD);
3636 entry->pnval = pn->pn_right;
3640 return pnhit->pn_right;
3644 * If data is null, the caller is AssignExpr and instead of binding variables,
3645 * we specialize lvalues in the propery value positions of the left-hand side.
3646 * If right is null, just check for well-formed lvalues.
3648 * See also UndominateInitializers, immediately below. If you change either of
3649 * these functions, you might have to change the other to match.
3651 static JSBool
3652 CheckDestructuring(JSContext *cx, BindData *data,
3653 JSParseNode *left, JSParseNode *right,
3654 JSTreeContext *tc)
3656 JSBool ok;
3657 FindPropValData fpvd;
3658 JSParseNode *lhs, *rhs, *pn, *pn2;
3660 if (left->pn_type == TOK_ARRAYCOMP) {
3661 js_ReportCompileErrorNumber(cx, TS(tc->compiler), left,
3662 JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
3663 return JS_FALSE;
3666 #if JS_HAS_DESTRUCTURING_SHORTHAND
3667 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3668 js_ReportCompileErrorNumber(cx, TS(tc->compiler), right,
3669 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3670 return JS_FALSE;
3672 #endif
3674 fpvd.table.ops = NULL;
3675 lhs = left->pn_head;
3676 if (left->pn_type == TOK_RB) {
3677 rhs = (right && right->pn_type == left->pn_type)
3678 ? right->pn_head
3679 : NULL;
3681 while (lhs) {
3682 pn = lhs, pn2 = rhs;
3683 if (!data) {
3684 /* Skip parenthesization if not in a variable declaration. */
3685 while (pn->pn_type == TOK_RP)
3686 pn = pn->pn_kid;
3687 if (pn2) {
3688 while (pn2->pn_type == TOK_RP)
3689 pn2 = pn2->pn_kid;
3693 /* Nullary comma is an elision; binary comma is an expression.*/
3694 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3695 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3696 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3697 } else {
3698 if (data) {
3699 if (pn->pn_type != TOK_NAME)
3700 goto no_var_name;
3702 ok = BindDestructuringVar(cx, data, pn, tc);
3703 } else {
3704 ok = BindDestructuringLHS(cx, pn, tc);
3707 if (!ok)
3708 goto out;
3711 lhs = lhs->pn_next;
3712 if (rhs)
3713 rhs = rhs->pn_next;
3715 } else {
3716 JS_ASSERT(left->pn_type == TOK_RC);
3717 fpvd.numvars = left->pn_count;
3718 fpvd.maxstep = 0;
3719 rhs = NULL;
3721 while (lhs) {
3722 JS_ASSERT(lhs->pn_type == TOK_COLON);
3723 pn = lhs->pn_right;
3724 if (!data) {
3725 /* Skip parenthesization if not in a variable declaration. */
3726 while (pn->pn_type == TOK_RP)
3727 pn = pn->pn_kid;
3730 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3731 if (right) {
3732 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3733 if (rhs && !data) {
3734 while (rhs->pn_type == TOK_RP)
3735 rhs = rhs->pn_kid;
3739 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3740 } else if (data) {
3741 if (pn->pn_type != TOK_NAME)
3742 goto no_var_name;
3744 ok = BindDestructuringVar(cx, data, pn, tc);
3745 } else {
3746 ok = BindDestructuringLHS(cx, pn, tc);
3748 if (!ok)
3749 goto out;
3751 lhs = lhs->pn_next;
3756 * The catch/finally handler implementation in the interpreter assumes
3757 * that any operation that introduces a new scope (like a "let" or "with"
3758 * block) increases the stack depth. This way, it is possible to restore
3759 * the scope chain based on stack depth of the handler alone. "let" with
3760 * an empty destructuring pattern like in
3762 * let [] = 1;
3764 * would violate this assumption as the there would be no let locals to
3765 * store on the stack. To satisfy it we add an empty property to such
3766 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3767 * slots, would be always positive.
3769 * Note that we add such a property even if the block has locals due to
3770 * later let declarations in it. We optimize for code simplicity here,
3771 * not the fastest runtime performance with empty [] or {}.
3773 if (data &&
3774 data->binder == BindLet &&
3775 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3776 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3777 ATOM_TO_JSID(cx->runtime->
3778 atomState.emptyAtom),
3779 JSVAL_VOID, NULL, NULL,
3780 JSPROP_ENUMERATE |
3781 JSPROP_PERMANENT |
3782 JSPROP_SHARED,
3783 SPROP_HAS_SHORTID, 0, NULL);
3784 if (!ok)
3785 goto out;
3788 ok = JS_TRUE;
3790 out:
3791 if (fpvd.table.ops)
3792 JS_DHashTableFinish(&fpvd.table);
3793 return ok;
3795 no_var_name:
3796 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
3797 JSMSG_NO_VARIABLE_NAME);
3798 ok = JS_FALSE;
3799 goto out;
3803 * This is a greatly pared down version of CheckDestructuring that extends the
3804 * pn_pos.end source coordinate of each name in a destructuring binding such as
3806 * var [x, y] = [function () y, 42];
3808 * to cover its corresponding initializer, so that the initialized binding does
3809 * not appear to dominate any closures in its initializer. See bug 496134.
3811 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3812 * not very precise. With one-pass SSA construction from structured source code
3813 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3814 * Languages", Brandis and Mössenböck), we could do much better.
3816 * See CheckDestructuring, immediately above. If you change either of these
3817 * functions, you might have to change the other to match.
3819 static JSBool
3820 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3822 FindPropValData fpvd;
3823 JSParseNode *lhs, *rhs;
3825 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3826 JS_ASSERT(right);
3828 #if JS_HAS_DESTRUCTURING_SHORTHAND
3829 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3830 js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
3831 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3832 return JS_FALSE;
3834 #endif
3836 if (right->pn_type != left->pn_type)
3837 return JS_TRUE;
3839 fpvd.table.ops = NULL;
3840 lhs = left->pn_head;
3841 if (left->pn_type == TOK_RB) {
3842 rhs = right->pn_head;
3844 while (lhs && rhs) {
3845 /* Nullary comma is an elision; binary comma is an expression.*/
3846 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3847 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3848 if (!UndominateInitializers(lhs, rhs, tc))
3849 return JS_FALSE;
3850 } else {
3851 lhs->pn_pos.end = rhs->pn_pos.end;
3855 lhs = lhs->pn_next;
3856 rhs = rhs->pn_next;
3858 } else {
3859 JS_ASSERT(left->pn_type == TOK_RC);
3860 fpvd.numvars = left->pn_count;
3861 fpvd.maxstep = 0;
3863 while (lhs) {
3864 JS_ASSERT(lhs->pn_type == TOK_COLON);
3865 JSParseNode *pn = lhs->pn_right;
3867 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3868 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3869 if (rhs && !UndominateInitializers(pn, rhs, tc))
3870 return JS_FALSE;
3871 } else {
3872 if (rhs)
3873 pn->pn_pos.end = rhs->pn_pos.end;
3876 lhs = lhs->pn_next;
3879 return JS_TRUE;
3882 static JSParseNode *
3883 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
3884 JSTokenType tt)
3886 JSTokenStream *ts;
3887 JSParseNode *pn;
3889 ts = TS(tc->compiler);
3890 ts->flags |= TSF_DESTRUCTURING;
3891 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3892 ts->flags &= ~TSF_DESTRUCTURING;
3893 if (!pn)
3894 return NULL;
3895 if (!CheckDestructuring(cx, data, pn, NULL, tc))
3896 return NULL;
3897 return pn;
3901 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
3902 * This function assumes the cloned tree is for use in the same statement and
3903 * binding context as the original tree.
3905 static JSParseNode *
3906 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
3908 JSParseNode *pn, *pn2, *opn2;
3910 pn = NewOrRecycledNode(tc);
3911 if (!pn)
3912 return NULL;
3913 pn->pn_type = opn->pn_type;
3914 pn->pn_pos = opn->pn_pos;
3915 pn->pn_op = opn->pn_op;
3916 pn->pn_used = opn->pn_used;
3917 pn->pn_defn = opn->pn_defn;
3918 pn->pn_arity = opn->pn_arity;
3920 switch (pn->pn_arity) {
3921 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
3923 case PN_FUNC:
3924 NULLCHECK(pn->pn_funbox =
3925 tc->compiler->newFunctionBox(opn->pn_funbox->object, pn, tc));
3926 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
3927 pn->pn_cookie = opn->pn_cookie;
3928 pn->pn_dflags = opn->pn_dflags;
3929 pn->pn_blockid = opn->pn_blockid;
3930 break;
3932 case PN_LIST:
3933 pn->makeEmpty();
3934 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
3935 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
3936 pn->append(pn2);
3938 pn->pn_xflags = opn->pn_xflags;
3939 break;
3941 case PN_TERNARY:
3942 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
3943 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
3944 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
3945 break;
3947 case PN_BINARY:
3948 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
3949 if (opn->pn_right != opn->pn_left)
3950 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
3951 else
3952 pn->pn_right = pn->pn_left;
3953 pn->pn_val = opn->pn_val;
3954 pn->pn_iflags = opn->pn_iflags;
3955 break;
3957 case PN_UNARY:
3958 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
3959 pn->pn_num = opn->pn_num;
3960 pn->pn_hidden = opn->pn_hidden;
3961 break;
3963 case PN_NAME:
3964 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
3965 pn->pn_u = opn->pn_u;
3966 if (opn->pn_used) {
3968 * The old name is a use of its pn_lexdef. Make the clone also be a
3969 * use of that definition.
3971 JSDefinition *dn = pn->pn_lexdef;
3973 pn->pn_link = dn->dn_uses;
3974 dn->dn_uses = pn;
3975 } else if (opn->pn_expr) {
3976 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
3979 * If the old name is a definition, the new one has pn_defn set.
3980 * Make the old name a use of the new node.
3982 if (opn->pn_defn) {
3983 opn->pn_defn = false;
3984 LinkUseToDef(opn, (JSDefinition *) pn, tc);
3987 break;
3989 case PN_NAMESET:
3990 pn->pn_names = opn->pn_names;
3991 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
3992 break;
3994 case PN_NULLARY:
3995 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
3996 pn->pn_u = opn->pn_u;
3997 break;
3999 #undef NULLCHECK
4001 return pn;
4004 #endif /* JS_HAS_DESTRUCTURING */
4006 extern const char js_with_statement_str[];
4008 static JSParseNode *
4009 ContainsStmt(JSParseNode *pn, JSTokenType tt)
4011 JSParseNode *pn2, *pnt;
4013 if (!pn)
4014 return NULL;
4015 if (PN_TYPE(pn) == tt)
4016 return pn;
4017 switch (pn->pn_arity) {
4018 case PN_LIST:
4019 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4020 pnt = ContainsStmt(pn2, tt);
4021 if (pnt)
4022 return pnt;
4024 break;
4025 case PN_TERNARY:
4026 pnt = ContainsStmt(pn->pn_kid1, tt);
4027 if (pnt)
4028 return pnt;
4029 pnt = ContainsStmt(pn->pn_kid2, tt);
4030 if (pnt)
4031 return pnt;
4032 return ContainsStmt(pn->pn_kid3, tt);
4033 case PN_BINARY:
4035 * Limit recursion if pn is a binary expression, which can't contain a
4036 * var statement.
4038 if (pn->pn_op != JSOP_NOP)
4039 return NULL;
4040 pnt = ContainsStmt(pn->pn_left, tt);
4041 if (pnt)
4042 return pnt;
4043 return ContainsStmt(pn->pn_right, tt);
4044 case PN_UNARY:
4045 if (pn->pn_op != JSOP_NOP)
4046 return NULL;
4047 return ContainsStmt(pn->pn_kid, tt);
4048 case PN_NAME:
4049 return ContainsStmt(pn->maybeExpr(), tt);
4050 case PN_NAMESET:
4051 return ContainsStmt(pn->pn_tree, tt);
4052 default:;
4054 return NULL;
4057 static JSParseNode *
4058 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4059 JSParser operandParser)
4061 JSTokenType tt, tt2;
4062 JSParseNode *pn, *pn2;
4064 tt = CURRENT_TOKEN(ts).type;
4065 if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
4066 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4067 JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4068 return NULL;
4071 pn = NewParseNode(PN_UNARY, tc);
4072 if (!pn)
4073 return NULL;
4075 #if JS_HAS_GENERATORS
4076 if (tt == TOK_YIELD)
4077 tc->flags |= TCF_FUN_IS_GENERATOR;
4078 #endif
4080 /* This is ugly, but we don't want to require a semicolon. */
4081 ts->flags |= TSF_OPERAND;
4082 tt2 = js_PeekTokenSameLine(cx, ts);
4083 ts->flags &= ~TSF_OPERAND;
4084 if (tt2 == TOK_ERROR)
4085 return NULL;
4087 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4088 #if JS_HAS_GENERATORS
4089 && (tt != TOK_YIELD ||
4090 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4091 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4092 #endif
4094 pn2 = operandParser(cx, ts, tc);
4095 if (!pn2)
4096 return NULL;
4097 #if JS_HAS_GENERATORS
4098 if (tt == TOK_RETURN)
4099 #endif
4100 tc->flags |= TCF_RETURN_EXPR;
4101 pn->pn_pos.end = pn2->pn_pos.end;
4102 pn->pn_kid = pn2;
4103 } else {
4104 #if JS_HAS_GENERATORS
4105 if (tt == TOK_RETURN)
4106 #endif
4107 tc->flags |= TCF_RETURN_VOID;
4110 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4111 /* As in Python (see PEP-255), disallow return v; in generators. */
4112 ReportBadReturn(cx, tc, JSREPORT_ERROR,
4113 JSMSG_BAD_GENERATOR_RETURN,
4114 JSMSG_BAD_ANON_GENERATOR_RETURN);
4115 return NULL;
4118 if (JS_HAS_STRICT_OPTION(cx) &&
4119 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4120 !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4121 JSMSG_NO_RETURN_VALUE,
4122 JSMSG_ANON_NO_RETURN_VALUE)) {
4123 return NULL;
4126 return pn;
4129 static JSParseNode *
4130 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4131 JSStmtInfo *stmt)
4133 JSParseNode *pn;
4134 JSObject *obj;
4135 JSObjectBox *blockbox;
4137 pn = NewParseNode(PN_NAME, tc);
4138 if (!pn)
4139 return NULL;
4141 obj = js_NewBlockObject(cx);
4142 if (!obj)
4143 return NULL;
4145 blockbox = tc->compiler->newObjectBox(obj);
4146 if (!blockbox)
4147 return NULL;
4149 js_PushBlockScope(tc, stmt, obj, -1);
4150 pn->pn_type = TOK_LEXICALSCOPE;
4151 pn->pn_op = JSOP_LEAVEBLOCK;
4152 pn->pn_objbox = blockbox;
4153 pn->pn_cookie = FREE_UPVAR_COOKIE;
4154 pn->pn_dflags = 0;
4155 if (!GenerateBlockId(tc, stmt->blockid))
4156 return NULL;
4157 pn->pn_blockid = stmt->blockid;
4158 return pn;
4161 #if JS_HAS_BLOCK_SCOPE
4163 static JSParseNode *
4164 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
4166 JSParseNode *pn, *pnblock, *pnlet;
4167 JSStmtInfo stmtInfo;
4169 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
4171 /* Create the let binary node. */
4172 pnlet = NewParseNode(PN_BINARY, tc);
4173 if (!pnlet)
4174 return NULL;
4176 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4178 /* This is a let block or expression of the form: let (a, b, c) .... */
4179 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
4180 if (!pnblock)
4181 return NULL;
4182 pn = pnblock;
4183 pn->pn_expr = pnlet;
4185 pnlet->pn_left = Variables(cx, ts, tc, true);
4186 if (!pnlet->pn_left)
4187 return NULL;
4188 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4190 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4192 ts->flags |= TSF_OPERAND;
4193 if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
4195 * If this is really an expression in let statement guise, then we
4196 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4197 * the return value of the expression.
4199 pn = NewParseNode(PN_UNARY, tc);
4200 if (!pn)
4201 return NULL;
4202 pn->pn_type = TOK_SEMI;
4203 pn->pn_num = -1;
4204 pn->pn_kid = pnblock;
4206 statement = JS_FALSE;
4208 ts->flags &= ~TSF_OPERAND;
4210 if (statement) {
4211 pnlet->pn_right = Statements(cx, ts, tc);
4212 if (!pnlet->pn_right)
4213 return NULL;
4214 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4215 } else {
4217 * Change pnblock's opcode to the variant that propagates the last
4218 * result down after popping the block, and clear statement.
4220 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4221 pnlet->pn_right = AssignExpr(cx, ts, tc);
4222 if (!pnlet->pn_right)
4223 return NULL;
4226 PopStatement(tc);
4227 return pn;
4230 #endif /* JS_HAS_BLOCK_SCOPE */
4232 static bool
4233 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4235 js_PushStatement(tc, stmt, type, -1);
4236 return GenerateBlockId(tc, stmt->blockid);
4239 static JSParseNode *
4240 NewBindingNode(JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc, bool let = false)
4242 JSParseNode *pn = NULL;
4244 JSAtomListElement *ale = tc->decls.lookup(atom);
4245 if (ale) {
4246 pn = ALE_DEFN(ale);
4247 JS_ASSERT(!pn->isPlaceholder());
4248 } else {
4249 ale = tc->lexdeps.lookup(atom);
4250 if (ale) {
4251 pn = ALE_DEFN(ale);
4252 JS_ASSERT(pn->isPlaceholder());
4256 if (pn) {
4257 JS_ASSERT(pn->pn_defn);
4260 * A let binding at top level becomes a var before we get here, so if
4261 * pn and tc have the same blockid then that id must not be the bodyid.
4262 * If pn is a forward placeholder definition from the same or a higher
4263 * block then we claim it.
4265 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4266 pn->pn_blockid != tc->bodyid);
4268 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4269 if (let)
4270 pn->pn_blockid = tc->blockid();
4272 tc->lexdeps.remove(tc->compiler, atom);
4273 return pn;
4277 /* Make a new node for this declarator name (or destructuring pattern). */
4278 pn = NewNameNode(tc->compiler->context, ts, atom, tc);
4279 if (!pn)
4280 return NULL;
4281 return pn;
4284 #if JS_HAS_BLOCK_SCOPE
4285 static bool
4286 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4288 if (!pn)
4289 return true;
4291 switch (pn->pn_arity) {
4292 case PN_LIST:
4293 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4294 RebindLets(pn2, tc);
4295 break;
4297 case PN_TERNARY:
4298 RebindLets(pn->pn_kid1, tc);
4299 RebindLets(pn->pn_kid2, tc);
4300 RebindLets(pn->pn_kid3, tc);
4301 break;
4303 case PN_BINARY:
4304 RebindLets(pn->pn_left, tc);
4305 RebindLets(pn->pn_right, tc);
4306 break;
4308 case PN_UNARY:
4309 RebindLets(pn->pn_kid, tc);
4310 break;
4312 case PN_FUNC:
4313 RebindLets(pn->pn_body, tc);
4314 break;
4316 case PN_NAME:
4317 RebindLets(pn->maybeExpr(), tc);
4319 if (pn->pn_defn) {
4320 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4321 } else if (pn->pn_used) {
4322 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4323 ForgetUse(pn);
4325 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4326 if (ale) {
4327 while ((ale = ALE_NEXT(ale)) != NULL) {
4328 if (ALE_ATOM(ale) == pn->pn_atom) {
4329 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4330 return true;
4335 ale = tc->lexdeps.lookup(pn->pn_atom);
4336 if (!ale) {
4337 ale = MakePlaceholder(pn, tc);
4338 if (!ale)
4339 return NULL;
4341 JSDefinition *dn = ALE_DEFN(ale);
4342 dn->pn_type = TOK_NAME;
4343 dn->pn_op = JSOP_NOP;
4344 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
4346 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4349 break;
4351 case PN_NAMESET:
4352 RebindLets(pn->pn_tree, tc);
4353 break;
4356 return true;
4358 #endif /* JS_HAS_BLOCK_SCOPE */
4360 static JSParseNode *
4361 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4363 JSTokenType tt;
4364 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4365 JSStmtInfo stmtInfo, *stmt, *stmt2;
4366 JSAtom *label;
4368 JS_CHECK_RECURSION(cx, return NULL);
4370 ts->flags |= TSF_OPERAND;
4371 tt = js_GetToken(cx, ts);
4372 ts->flags &= ~TSF_OPERAND;
4374 #if JS_HAS_GETTER_SETTER
4375 if (tt == TOK_NAME) {
4376 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
4377 if (tt == TOK_ERROR)
4378 return NULL;
4380 #endif
4382 switch (tt) {
4383 case TOK_FUNCTION:
4384 #if JS_HAS_XML_SUPPORT
4385 ts->flags |= TSF_KEYWORD_IS_NAME;
4386 tt = js_PeekToken(cx, ts);
4387 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4388 if (tt == TOK_DBLCOLON)
4389 goto expression;
4390 #endif
4391 return FunctionStmt(cx, ts, tc);
4393 case TOK_IF:
4394 /* An IF node has three kids: condition, then, and optional else. */
4395 pn = NewParseNode(PN_TERNARY, tc);
4396 if (!pn)
4397 return NULL;
4398 pn1 = Condition(cx, ts, tc);
4399 if (!pn1)
4400 return NULL;
4401 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4402 pn2 = Statement(cx, ts, tc);
4403 if (!pn2)
4404 return NULL;
4405 ts->flags |= TSF_OPERAND;
4406 if (js_MatchToken(cx, ts, TOK_ELSE)) {
4407 ts->flags &= ~TSF_OPERAND;
4408 stmtInfo.type = STMT_ELSE;
4409 pn3 = Statement(cx, ts, tc);
4410 if (!pn3)
4411 return NULL;
4412 pn->pn_pos.end = pn3->pn_pos.end;
4413 } else {
4414 ts->flags &= ~TSF_OPERAND;
4415 pn3 = NULL;
4416 pn->pn_pos.end = pn2->pn_pos.end;
4418 PopStatement(tc);
4419 pn->pn_kid1 = pn1;
4420 pn->pn_kid2 = pn2;
4421 pn->pn_kid3 = pn3;
4422 return pn;
4424 case TOK_SWITCH:
4426 JSParseNode *pn5, *saveBlock;
4427 JSBool seenDefault = JS_FALSE;
4429 pn = NewParseNode(PN_BINARY, tc);
4430 if (!pn)
4431 return NULL;
4432 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4434 /* pn1 points to the switch's discriminant. */
4435 pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
4436 if (!pn1)
4437 return NULL;
4439 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4440 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4443 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4444 * because that function states tc->topStmt->blockid.
4446 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4448 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4449 pn2 = NewParseNode(PN_LIST, tc);
4450 if (!pn2)
4451 return NULL;
4452 pn2->makeEmpty();
4453 if (!GenerateBlockIdForStmtNode(pn2, tc))
4454 return NULL;
4455 saveBlock = tc->blockNode;
4456 tc->blockNode = pn2;
4458 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
4459 switch (tt) {
4460 case TOK_DEFAULT:
4461 if (seenDefault) {
4462 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4463 JSMSG_TOO_MANY_DEFAULTS);
4464 return NULL;
4466 seenDefault = JS_TRUE;
4467 /* FALL THROUGH */
4469 case TOK_CASE:
4470 pn3 = NewParseNode(PN_BINARY, tc);
4471 if (!pn3)
4472 return NULL;
4473 if (tt == TOK_CASE) {
4474 pn3->pn_left = Expr(cx, ts, tc);
4475 if (!pn3->pn_left)
4476 return NULL;
4478 pn2->append(pn3);
4479 if (pn2->pn_count == JS_BIT(16)) {
4480 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4481 JSMSG_TOO_MANY_CASES);
4482 return NULL;
4484 break;
4486 case TOK_ERROR:
4487 return NULL;
4489 default:
4490 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4491 JSMSG_BAD_SWITCH);
4492 return NULL;
4494 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4496 pn4 = NewParseNode(PN_LIST, tc);
4497 if (!pn4)
4498 return NULL;
4499 pn4->pn_type = TOK_LC;
4500 pn4->makeEmpty();
4501 ts->flags |= TSF_OPERAND;
4502 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
4503 tt != TOK_CASE && tt != TOK_DEFAULT) {
4504 ts->flags &= ~TSF_OPERAND;
4505 if (tt == TOK_ERROR)
4506 return NULL;
4507 pn5 = Statement(cx, ts, tc);
4508 if (!pn5)
4509 return NULL;
4510 pn4->pn_pos.end = pn5->pn_pos.end;
4511 pn4->append(pn5);
4512 ts->flags |= TSF_OPERAND;
4514 ts->flags &= ~TSF_OPERAND;
4516 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4517 if (pn4->pn_head)
4518 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4519 pn3->pn_pos.end = pn4->pn_pos.end;
4520 pn3->pn_right = pn4;
4524 * Handle the case where there was a let declaration in any case in
4525 * the switch body, but not within an inner block. If it replaced
4526 * tc->blockNode with a new block node then we must refresh pn2 and
4527 * then restore tc->blockNode.
4529 if (tc->blockNode != pn2)
4530 pn2 = tc->blockNode;
4531 tc->blockNode = saveBlock;
4532 PopStatement(tc);
4534 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4535 pn->pn_left = pn1;
4536 pn->pn_right = pn2;
4537 return pn;
4540 case TOK_WHILE:
4541 pn = NewParseNode(PN_BINARY, tc);
4542 if (!pn)
4543 return NULL;
4544 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4545 pn2 = Condition(cx, ts, tc);
4546 if (!pn2)
4547 return NULL;
4548 pn->pn_left = pn2;
4549 pn2 = Statement(cx, ts, tc);
4550 if (!pn2)
4551 return NULL;
4552 PopStatement(tc);
4553 pn->pn_pos.end = pn2->pn_pos.end;
4554 pn->pn_right = pn2;
4555 return pn;
4557 case TOK_DO:
4558 pn = NewParseNode(PN_BINARY, tc);
4559 if (!pn)
4560 return NULL;
4561 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4562 pn2 = Statement(cx, ts, tc);
4563 if (!pn2)
4564 return NULL;
4565 pn->pn_left = pn2;
4566 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4567 pn2 = Condition(cx, ts, tc);
4568 if (!pn2)
4569 return NULL;
4570 PopStatement(tc);
4571 pn->pn_pos.end = pn2->pn_pos.end;
4572 pn->pn_right = pn2;
4573 if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
4575 * All legacy and extended versions must do automatic semicolon
4576 * insertion after do-while. See the testcase and discussion in
4577 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4579 (void) js_MatchToken(cx, ts, TOK_SEMI);
4580 return pn;
4582 break;
4584 case TOK_FOR:
4586 JSParseNode *pnseq = NULL;
4587 #if JS_HAS_BLOCK_SCOPE
4588 JSParseNode *pnlet = NULL;
4589 JSStmtInfo blockInfo;
4590 #endif
4592 /* A FOR node is binary, left is loop control and right is the body. */
4593 pn = NewParseNode(PN_BINARY, tc);
4594 if (!pn)
4595 return NULL;
4596 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4598 pn->pn_op = JSOP_ITER;
4599 pn->pn_iflags = 0;
4600 if (js_MatchToken(cx, ts, TOK_NAME)) {
4601 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
4602 pn->pn_iflags = JSITER_FOREACH;
4603 else
4604 js_UngetToken(ts);
4607 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4608 ts->flags |= TSF_OPERAND;
4609 tt = js_PeekToken(cx, ts);
4610 ts->flags &= ~TSF_OPERAND;
4612 #if JS_HAS_BLOCK_SCOPE
4613 bool let = false;
4614 #endif
4616 if (tt == TOK_SEMI) {
4617 if (pn->pn_iflags & JSITER_FOREACH)
4618 goto bad_for_each;
4620 /* No initializer -- set first kid of left sub-node to null. */
4621 pn1 = NULL;
4622 } else {
4624 * Set pn1 to a var list or an initializing expression.
4626 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4627 * of the for statement. This flag will be used by the RelExpr
4628 * production; if it is set, then the 'in' keyword will not be
4629 * recognized as an operator, leaving it available to be parsed as
4630 * part of a for/in loop.
4632 * A side effect of this restriction is that (unparenthesized)
4633 * expressions involving an 'in' operator are illegal in the init
4634 * clause of an ordinary for loop.
4636 tc->flags |= TCF_IN_FOR_INIT;
4637 if (tt == TOK_VAR) {
4638 (void) js_GetToken(cx, ts);
4639 pn1 = Variables(cx, ts, tc, false);
4640 #if JS_HAS_BLOCK_SCOPE
4641 } else if (tt == TOK_LET) {
4642 let = true;
4643 (void) js_GetToken(cx, ts);
4644 if (js_PeekToken(cx, ts) == TOK_LP) {
4645 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
4646 tt = TOK_LEXICALSCOPE;
4647 } else {
4648 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
4649 if (!pnlet)
4650 return NULL;
4651 blockInfo.flags |= SIF_FOR_BLOCK;
4652 pn1 = Variables(cx, ts, tc, false);
4654 #endif
4655 } else {
4656 pn1 = Expr(cx, ts, tc);
4657 if (pn1) {
4658 while (pn1->pn_type == TOK_RP)
4659 pn1 = pn1->pn_kid;
4662 tc->flags &= ~TCF_IN_FOR_INIT;
4663 if (!pn1)
4664 return NULL;
4668 * We can be sure that it's a for/in loop if there's still an 'in'
4669 * keyword here, even if JavaScript recognizes 'in' as an operator,
4670 * as we've excluded 'in' from being parsed in RelExpr by setting
4671 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4673 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
4674 pn->pn_iflags |= JSITER_ENUMERATE;
4675 stmtInfo.type = STMT_FOR_IN_LOOP;
4677 /* Check that the left side of the 'in' is valid. */
4678 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || PN_TYPE(pn1) == tt);
4679 if (TOKEN_TYPE_IS_DECL(tt)
4680 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4681 #if JS_HAS_DESTRUCTURING
4682 || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4683 pn->pn_op == JSOP_ITER &&
4684 !(pn->pn_iflags & JSITER_FOREACH) &&
4685 (pn1->pn_head->pn_type == TOK_RC ||
4686 (pn1->pn_head->pn_type == TOK_RB &&
4687 pn1->pn_head->pn_count != 2) ||
4688 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4689 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4690 pn1->pn_head->pn_left->pn_count != 2))))
4691 #endif
4693 : (pn1->pn_type != TOK_NAME &&
4694 pn1->pn_type != TOK_DOT &&
4695 #if JS_HAS_DESTRUCTURING
4696 ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4697 pn->pn_op == JSOP_ITER &&
4698 !(pn->pn_iflags & JSITER_FOREACH))
4699 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4700 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4701 #endif
4702 #if JS_HAS_LVALUE_RETURN
4703 pn1->pn_type != TOK_LP &&
4704 #endif
4705 #if JS_HAS_XML_SUPPORT
4706 (pn1->pn_type != TOK_UNARYOP ||
4707 pn1->pn_op != JSOP_XMLNAME) &&
4708 #endif
4709 pn1->pn_type != TOK_LB)) {
4710 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
4711 JSMSG_BAD_FOR_LEFTSIDE);
4712 return NULL;
4715 /* pn2 points to the name or destructuring pattern on in's left. */
4716 pn2 = NULL;
4717 uintN dflag = PND_ASSIGNED;
4719 if (TOKEN_TYPE_IS_DECL(tt)) {
4720 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4721 pn1->pn_xflags |= PNX_FORINVAR;
4724 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4725 * 'var', or 'const' to hoist the initializer or the entire
4726 * decl out of the loop head. TOK_VAR is the type for both
4727 * 'var' and 'const'.
4729 pn2 = pn1->pn_head;
4730 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4731 #if JS_HAS_DESTRUCTURING
4732 || pn2->pn_type == TOK_ASSIGN
4733 #endif
4735 pnseq = NewParseNode(PN_LIST, tc);
4736 if (!pnseq)
4737 return NULL;
4738 pnseq->pn_type = TOK_SEQ;
4739 pnseq->pn_pos.begin = pn->pn_pos.begin;
4741 #if JS_HAS_BLOCK_SCOPE
4742 if (tt == TOK_LET) {
4744 * Hoist just the 'i' from 'for (let x = i in o)' to
4745 * before the loop, glued together via pnseq.
4747 pn3 = NewParseNode(PN_UNARY, tc);
4748 if (!pn3)
4749 return NULL;
4750 pn3->pn_type = TOK_SEMI;
4751 pn3->pn_op = JSOP_NOP;
4752 #if JS_HAS_DESTRUCTURING
4753 if (pn2->pn_type == TOK_ASSIGN) {
4754 pn4 = pn2->pn_right;
4755 pn2 = pn1->pn_head = pn2->pn_left;
4756 } else
4757 #endif
4759 pn4 = pn2->pn_expr;
4760 pn2->pn_expr = NULL;
4762 if (!RebindLets(pn4, tc))
4763 return NULL;
4764 pn3->pn_pos = pn4->pn_pos;
4765 pn3->pn_kid = pn4;
4766 pnseq->initList(pn3);
4767 } else
4768 #endif /* JS_HAS_BLOCK_SCOPE */
4770 dflag = PND_INITIALIZED;
4773 * All of 'var x = i' is hoisted above 'for (x in o)',
4774 * so clear PNX_FORINVAR.
4776 * Request JSOP_POP here since the var is for a simple
4777 * name (it is not a destructuring binding's left-hand
4778 * side) and it has an initializer.
4780 pn1->pn_xflags &= ~PNX_FORINVAR;
4781 pn1->pn_xflags |= PNX_POPVAR;
4782 pnseq->initList(pn1);
4784 #if JS_HAS_DESTRUCTURING
4785 if (pn2->pn_type == TOK_ASSIGN) {
4786 pn1 = CloneParseTree(pn2->pn_left, tc);
4787 if (!pn1)
4788 return NULL;
4789 } else
4790 #endif
4792 JS_ASSERT(pn2->pn_type == TOK_NAME);
4793 pn1 = NewNameNode(cx, ts, pn2->pn_atom, tc);
4794 if (!pn1)
4795 return NULL;
4796 pn1->pn_type = TOK_NAME;
4797 pn1->pn_op = JSOP_NAME;
4798 pn1->pn_pos = pn2->pn_pos;
4799 if (pn2->pn_defn)
4800 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4802 pn2 = pn1;
4807 if (!pn2) {
4808 pn2 = pn1;
4809 #if JS_HAS_LVALUE_RETURN
4810 if (pn2->pn_type == TOK_LP &&
4811 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4812 return NULL;
4814 #endif
4815 #if JS_HAS_XML_SUPPORT
4816 if (pn2->pn_type == TOK_UNARYOP)
4817 pn2->pn_op = JSOP_BINDXMLNAME;
4818 #endif
4821 switch (pn2->pn_type) {
4822 case TOK_NAME:
4823 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4824 NoteLValue(cx, pn2, tc, dflag);
4825 break;
4827 #if JS_HAS_DESTRUCTURING
4828 case TOK_ASSIGN:
4829 pn2 = pn2->pn_left;
4830 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4831 /* FALL THROUGH */
4832 case TOK_RB:
4833 case TOK_RC:
4834 /* Check for valid lvalues in var-less destructuring for-in. */
4835 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
4836 return NULL;
4838 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
4840 * Destructuring for-in requires [key, value] enumeration
4841 * in JS1.7.
4843 JS_ASSERT(pn->pn_op == JSOP_ITER);
4844 if (!(pn->pn_iflags & JSITER_FOREACH))
4845 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4847 break;
4848 #endif
4850 default:;
4854 * Parse the object expression as the right operand of 'in', first
4855 * removing the top statement from the statement-stack if this is a
4856 * 'for (let x in y)' loop.
4858 #if JS_HAS_BLOCK_SCOPE
4859 JSStmtInfo *save = tc->topStmt;
4860 if (let)
4861 tc->topStmt = save->down;
4862 #endif
4863 pn2 = Expr(cx, ts, tc);
4864 #if JS_HAS_BLOCK_SCOPE
4865 if (let)
4866 tc->topStmt = save;
4867 #endif
4869 pn2 = NewBinary(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4870 if (!pn2)
4871 return NULL;
4872 pn->pn_left = pn2;
4873 } else {
4874 if (pn->pn_iflags & JSITER_FOREACH)
4875 goto bad_for_each;
4876 pn->pn_op = JSOP_NOP;
4878 /* Parse the loop condition or null into pn2. */
4879 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4880 ts->flags |= TSF_OPERAND;
4881 tt = js_PeekToken(cx, ts);
4882 ts->flags &= ~TSF_OPERAND;
4883 if (tt == TOK_SEMI) {
4884 pn2 = NULL;
4885 } else {
4886 pn2 = Expr(cx, ts, tc);
4887 if (!pn2)
4888 return NULL;
4891 /* Parse the update expression or null into pn3. */
4892 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4893 ts->flags |= TSF_OPERAND;
4894 tt = js_PeekToken(cx, ts);
4895 ts->flags &= ~TSF_OPERAND;
4896 if (tt == TOK_RP) {
4897 pn3 = NULL;
4898 } else {
4899 pn3 = Expr(cx, ts, tc);
4900 if (!pn3)
4901 return NULL;
4904 /* Build the FORHEAD node to use as the left kid of pn. */
4905 pn4 = NewParseNode(PN_TERNARY, tc);
4906 if (!pn4)
4907 return NULL;
4908 pn4->pn_type = TOK_FORHEAD;
4909 pn4->pn_op = JSOP_NOP;
4910 pn4->pn_kid1 = pn1;
4911 pn4->pn_kid2 = pn2;
4912 pn4->pn_kid3 = pn3;
4913 pn->pn_left = pn4;
4916 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
4918 /* Parse the loop body into pn->pn_right. */
4919 pn2 = Statement(cx, ts, tc);
4920 if (!pn2)
4921 return NULL;
4922 pn->pn_right = pn2;
4924 /* Record the absolute line number for source note emission. */
4925 pn->pn_pos.end = pn2->pn_pos.end;
4927 #if JS_HAS_BLOCK_SCOPE
4928 if (pnlet) {
4929 PopStatement(tc);
4930 pnlet->pn_expr = pn;
4931 pn = pnlet;
4933 #endif
4934 if (pnseq) {
4935 pnseq->pn_pos.end = pn->pn_pos.end;
4936 pnseq->append(pn);
4937 pn = pnseq;
4939 PopStatement(tc);
4940 return pn;
4942 bad_for_each:
4943 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
4944 JSMSG_BAD_FOR_EACH_LOOP);
4945 return NULL;
4948 case TOK_TRY: {
4949 JSParseNode *catchList, *lastCatch;
4952 * try nodes are ternary.
4953 * kid1 is the try Statement
4954 * kid2 is the catch node list or null
4955 * kid3 is the finally Statement
4957 * catch nodes are ternary.
4958 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
4959 * kid2 is the catch guard or null if no guard
4960 * kid3 is the catch block
4962 * catch lvalue nodes are either:
4963 * TOK_NAME for a single identifier
4964 * TOK_RB or TOK_RC for a destructuring left-hand side
4966 * finally nodes are TOK_LC Statement lists.
4968 pn = NewParseNode(PN_TERNARY, tc);
4969 if (!pn)
4970 return NULL;
4971 pn->pn_op = JSOP_NOP;
4973 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
4974 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
4975 return NULL;
4976 pn->pn_kid1 = Statements(cx, ts, tc);
4977 if (!pn->pn_kid1)
4978 return NULL;
4979 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
4980 PopStatement(tc);
4982 catchList = NULL;
4983 tt = js_GetToken(cx, ts);
4984 if (tt == TOK_CATCH) {
4985 catchList = NewParseNode(PN_LIST, tc);
4986 if (!catchList)
4987 return NULL;
4988 catchList->pn_type = TOK_RESERVED;
4989 catchList->makeEmpty();
4990 lastCatch = NULL;
4992 do {
4993 JSParseNode *pnblock;
4994 BindData data;
4996 /* Check for another catch after unconditional catch. */
4997 if (lastCatch && !lastCatch->pn_kid2) {
4998 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4999 JSMSG_CATCH_AFTER_GENERAL);
5000 return NULL;
5004 * Create a lexical scope node around the whole catch clause,
5005 * including the head.
5007 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
5008 if (!pnblock)
5009 return NULL;
5010 stmtInfo.type = STMT_CATCH;
5013 * Legal catch forms are:
5014 * catch (lhs)
5015 * catch (lhs if <boolean_expression>)
5016 * where lhs is a name or a destructuring left-hand side.
5017 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5019 pn2 = NewParseNode(PN_TERNARY, tc);
5020 if (!pn2)
5021 return NULL;
5022 pnblock->pn_expr = pn2;
5023 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5026 * Contrary to ECMA Ed. 3, the catch variable is lexically
5027 * scoped, not a property of a new Object instance. This is
5028 * an intentional change that anticipates ECMA Ed. 4.
5030 data.pn = NULL;
5031 data.op = JSOP_NOP;
5032 data.binder = BindLet;
5033 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5035 tt = js_GetToken(cx, ts);
5036 switch (tt) {
5037 #if JS_HAS_DESTRUCTURING
5038 case TOK_LB:
5039 case TOK_LC:
5040 pn3 = DestructuringExpr(cx, &data, tc, tt);
5041 if (!pn3)
5042 return NULL;
5043 break;
5044 #endif
5046 case TOK_NAME:
5047 label = CURRENT_TOKEN(ts).t_atom;
5048 pn3 = NewBindingNode(ts, label, tc, true);
5049 if (!pn3)
5050 return NULL;
5051 data.pn = pn3;
5052 if (!data.binder(cx, &data, label, tc))
5053 return NULL;
5054 break;
5056 default:
5057 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5058 JSMSG_CATCH_IDENTIFIER);
5059 return NULL;
5062 pn2->pn_kid1 = pn3;
5063 #if JS_HAS_CATCH_GUARD
5065 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5066 * to avoid conflicting with the JS2/ECMAv4 type annotation
5067 * catchguard syntax.
5069 if (js_MatchToken(cx, ts, TOK_IF)) {
5070 pn2->pn_kid2 = Expr(cx, ts, tc);
5071 if (!pn2->pn_kid2)
5072 return NULL;
5074 #endif
5075 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5077 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5078 pn2->pn_kid3 = Statements(cx, ts, tc);
5079 if (!pn2->pn_kid3)
5080 return NULL;
5081 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5082 PopStatement(tc);
5084 catchList->append(pnblock);
5085 lastCatch = pn2;
5086 ts->flags |= TSF_OPERAND;
5087 tt = js_GetToken(cx, ts);
5088 ts->flags &= ~TSF_OPERAND;
5089 } while (tt == TOK_CATCH);
5091 pn->pn_kid2 = catchList;
5093 if (tt == TOK_FINALLY) {
5094 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5095 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5096 return NULL;
5097 pn->pn_kid3 = Statements(cx, ts, tc);
5098 if (!pn->pn_kid3)
5099 return NULL;
5100 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5101 PopStatement(tc);
5102 } else {
5103 js_UngetToken(ts);
5105 if (!catchList && !pn->pn_kid3) {
5106 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5107 JSMSG_CATCH_OR_FINALLY);
5108 return NULL;
5110 return pn;
5113 case TOK_THROW:
5114 pn = NewParseNode(PN_UNARY, tc);
5115 if (!pn)
5116 return NULL;
5118 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5119 ts->flags |= TSF_OPERAND;
5120 tt = js_PeekTokenSameLine(cx, ts);
5121 ts->flags &= ~TSF_OPERAND;
5122 if (tt == TOK_ERROR)
5123 return NULL;
5124 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5125 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5126 JSMSG_SYNTAX_ERROR);
5127 return NULL;
5130 pn2 = Expr(cx, ts, tc);
5131 if (!pn2)
5132 return NULL;
5133 pn->pn_pos.end = pn2->pn_pos.end;
5134 pn->pn_op = JSOP_THROW;
5135 pn->pn_kid = pn2;
5136 break;
5138 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5139 case TOK_CATCH:
5140 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5141 JSMSG_CATCH_WITHOUT_TRY);
5142 return NULL;
5144 case TOK_FINALLY:
5145 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5146 JSMSG_FINALLY_WITHOUT_TRY);
5147 return NULL;
5149 case TOK_BREAK:
5150 pn = NewParseNode(PN_NULLARY, tc);
5151 if (!pn)
5152 return NULL;
5153 if (!MatchLabel(cx, ts, pn))
5154 return NULL;
5155 stmt = tc->topStmt;
5156 label = pn->pn_atom;
5157 if (label) {
5158 for (; ; stmt = stmt->down) {
5159 if (!stmt) {
5160 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5161 JSMSG_LABEL_NOT_FOUND);
5162 return NULL;
5164 if (stmt->type == STMT_LABEL && stmt->label == label)
5165 break;
5167 } else {
5168 for (; ; stmt = stmt->down) {
5169 if (!stmt) {
5170 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5171 JSMSG_TOUGH_BREAK);
5172 return NULL;
5174 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5175 break;
5178 if (label)
5179 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5180 break;
5182 case TOK_CONTINUE:
5183 pn = NewParseNode(PN_NULLARY, tc);
5184 if (!pn)
5185 return NULL;
5186 if (!MatchLabel(cx, ts, pn))
5187 return NULL;
5188 stmt = tc->topStmt;
5189 label = pn->pn_atom;
5190 if (label) {
5191 for (stmt2 = NULL; ; stmt = stmt->down) {
5192 if (!stmt) {
5193 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5194 JSMSG_LABEL_NOT_FOUND);
5195 return NULL;
5197 if (stmt->type == STMT_LABEL) {
5198 if (stmt->label == label) {
5199 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5200 js_ReportCompileErrorNumber(cx, ts, NULL,
5201 JSREPORT_ERROR,
5202 JSMSG_BAD_CONTINUE);
5203 return NULL;
5205 break;
5207 } else {
5208 stmt2 = stmt;
5211 } else {
5212 for (; ; stmt = stmt->down) {
5213 if (!stmt) {
5214 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5215 JSMSG_BAD_CONTINUE);
5216 return NULL;
5218 if (STMT_IS_LOOP(stmt))
5219 break;
5222 if (label)
5223 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5224 break;
5226 case TOK_WITH:
5227 pn = NewParseNode(PN_BINARY, tc);
5228 if (!pn)
5229 return NULL;
5230 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5231 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
5232 if (!pn2)
5233 return NULL;
5234 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5235 pn->pn_left = pn2;
5237 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5238 pn2 = Statement(cx, ts, tc);
5239 if (!pn2)
5240 return NULL;
5241 PopStatement(tc);
5243 pn->pn_pos.end = pn2->pn_pos.end;
5244 pn->pn_right = pn2;
5245 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5246 return pn;
5248 case TOK_VAR:
5249 pn = Variables(cx, ts, tc, false);
5250 if (!pn)
5251 return NULL;
5253 /* Tell js_EmitTree to generate a final POP. */
5254 pn->pn_xflags |= PNX_POPVAR;
5255 break;
5257 #if JS_HAS_BLOCK_SCOPE
5258 case TOK_LET:
5260 JSObject *obj;
5261 JSObjectBox *blockbox;
5263 /* Check for a let statement or let expression. */
5264 if (js_PeekToken(cx, ts) == TOK_LP) {
5265 pn = LetBlock(cx, ts, tc, JS_TRUE);
5266 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5267 return pn;
5269 /* Let expressions require automatic semicolon insertion. */
5270 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5271 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5272 break;
5276 * This is a let declaration. We must be directly under a block per
5277 * the proposed ES4 specs, but not an implicit block created due to
5278 * 'for (let ...)'. If we pass this error test, make the enclosing
5279 * JSStmtInfo be our scope. Further let declarations in this block
5280 * will find this scope statement and use the same block object.
5282 * If we are the first let declaration in this block (i.e., when the
5283 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5284 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5286 stmt = tc->topStmt;
5287 if (stmt &&
5288 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5289 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5290 JSMSG_LET_DECL_NOT_IN_BLOCK);
5291 return NULL;
5294 if (stmt && (stmt->flags & SIF_SCOPE)) {
5295 JS_ASSERT(tc->blockChain == stmt->blockObj);
5296 obj = tc->blockChain;
5297 } else {
5298 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5300 * ES4 specifies that let at top level and at body-block scope
5301 * does not shadow var, so convert back to var.
5303 CURRENT_TOKEN(ts).type = TOK_VAR;
5304 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
5306 pn = Variables(cx, ts, tc, false);
5307 if (!pn)
5308 return NULL;
5309 pn->pn_xflags |= PNX_POPVAR;
5310 break;
5314 * Some obvious assertions here, but they may help clarify the
5315 * situation. This stmt is not yet a scope, so it must not be a
5316 * catch block (catch is a lexical scope by definition).
5318 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5319 JS_ASSERT(stmt != tc->topScopeStmt);
5320 JS_ASSERT(stmt->type == STMT_BLOCK ||
5321 stmt->type == STMT_SWITCH ||
5322 stmt->type == STMT_TRY ||
5323 stmt->type == STMT_FINALLY);
5324 JS_ASSERT(!stmt->downScope);
5326 /* Convert the block statement into a scope statement. */
5327 JSObject *obj = js_NewBlockObject(tc->compiler->context);
5328 if (!obj)
5329 return NULL;
5331 blockbox = tc->compiler->newObjectBox(obj);
5332 if (!blockbox)
5333 return NULL;
5336 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5337 * list stack, if it isn't already there. If it is there, but it
5338 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5339 * block.
5341 stmt->flags |= SIF_SCOPE;
5342 stmt->downScope = tc->topScopeStmt;
5343 tc->topScopeStmt = stmt;
5344 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5345 (tc->maxScopeDepth = tc->scopeDepth));
5347 STOBJ_SET_PARENT(obj, tc->blockChain);
5348 tc->blockChain = obj;
5349 stmt->blockObj = obj;
5351 #ifdef DEBUG
5352 pn1 = tc->blockNode;
5353 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5354 #endif
5356 /* Create a new lexical scope node for these statements. */
5357 pn1 = NewParseNode(PN_NAME, tc);
5358 if (!pn1)
5359 return NULL;
5361 pn1->pn_type = TOK_LEXICALSCOPE;
5362 pn1->pn_op = JSOP_LEAVEBLOCK;
5363 pn1->pn_pos = tc->blockNode->pn_pos;
5364 pn1->pn_objbox = blockbox;
5365 pn1->pn_expr = tc->blockNode;
5366 pn1->pn_blockid = tc->blockNode->pn_blockid;
5367 tc->blockNode = pn1;
5370 pn = Variables(cx, ts, tc, false);
5371 if (!pn)
5372 return NULL;
5373 pn->pn_xflags = PNX_POPVAR;
5374 break;
5376 #endif /* JS_HAS_BLOCK_SCOPE */
5378 case TOK_RETURN:
5379 pn = ReturnOrYield(cx, ts, tc, Expr);
5380 if (!pn)
5381 return NULL;
5382 break;
5384 case TOK_LC:
5386 uintN oldflags;
5388 oldflags = tc->flags;
5389 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5390 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5391 return NULL;
5392 pn = Statements(cx, ts, tc);
5393 if (!pn)
5394 return NULL;
5396 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5397 PopStatement(tc);
5400 * If we contain a function statement and our container is top-level
5401 * or another block, flag pn to preserve braces when decompiling.
5403 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5404 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5405 pn->pn_xflags |= PNX_NEEDBRACES;
5407 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5408 return pn;
5411 case TOK_EOL:
5412 case TOK_SEMI:
5413 pn = NewParseNode(PN_UNARY, tc);
5414 if (!pn)
5415 return NULL;
5416 pn->pn_type = TOK_SEMI;
5417 return pn;
5419 #if JS_HAS_DEBUGGER_KEYWORD
5420 case TOK_DEBUGGER:
5421 pn = NewParseNode(PN_NULLARY, tc);
5422 if (!pn)
5423 return NULL;
5424 pn->pn_type = TOK_DEBUGGER;
5425 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5426 break;
5427 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5429 #if JS_HAS_XML_SUPPORT
5430 case TOK_DEFAULT:
5431 pn = NewParseNode(PN_UNARY, tc);
5432 if (!pn)
5433 return NULL;
5434 if (!js_MatchToken(cx, ts, TOK_NAME) ||
5435 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
5436 !js_MatchToken(cx, ts, TOK_NAME) ||
5437 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
5438 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
5439 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
5440 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5441 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5442 return NULL;
5444 pn2 = Expr(cx, ts, tc);
5445 if (!pn2)
5446 return NULL;
5447 pn->pn_op = JSOP_DEFXMLNS;
5448 pn->pn_pos.end = pn2->pn_pos.end;
5449 pn->pn_kid = pn2;
5450 break;
5451 #endif
5453 case TOK_ERROR:
5454 return NULL;
5456 default:
5457 #if JS_HAS_XML_SUPPORT
5458 expression:
5459 #endif
5460 js_UngetToken(ts);
5461 pn2 = Expr(cx, ts, tc);
5462 if (!pn2)
5463 return NULL;
5465 if (js_PeekToken(cx, ts) == TOK_COLON) {
5466 if (pn2->pn_type != TOK_NAME) {
5467 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5468 JSMSG_BAD_LABEL);
5469 return NULL;
5471 label = pn2->pn_atom;
5472 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5473 if (stmt->type == STMT_LABEL && stmt->label == label) {
5474 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5475 JSMSG_DUPLICATE_LABEL);
5476 return NULL;
5479 ForgetUse(pn2);
5481 (void) js_GetToken(cx, ts);
5483 /* Push a label struct and parse the statement. */
5484 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5485 stmtInfo.label = label;
5486 pn = Statement(cx, ts, tc);
5487 if (!pn)
5488 return NULL;
5490 /* Normalize empty statement to empty block for the decompiler. */
5491 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5492 pn->pn_type = TOK_LC;
5493 pn->pn_arity = PN_LIST;
5494 pn->makeEmpty();
5497 /* Pop the label, set pn_expr, and return early. */
5498 PopStatement(tc);
5499 pn2->pn_type = TOK_COLON;
5500 pn2->pn_pos.end = pn->pn_pos.end;
5501 pn2->pn_expr = pn;
5502 return pn2;
5505 pn = NewParseNode(PN_UNARY, tc);
5506 if (!pn)
5507 return NULL;
5508 pn->pn_type = TOK_SEMI;
5509 pn->pn_pos = pn2->pn_pos;
5510 pn->pn_kid = pn2;
5511 break;
5514 /* Check termination of this primitive statement. */
5515 return MatchOrInsertSemicolon(cx, ts) ? pn : NULL;
5518 static void
5519 NoteArgumentsUse(JSTreeContext *tc)
5521 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
5522 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5523 if (tc->funbox)
5524 tc->funbox->node->pn_dflags |= PND_FUNARG;
5527 static JSParseNode *
5528 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, bool inLetHead)
5530 JSTokenType tt;
5531 bool let;
5532 JSStmtInfo *scopeStmt;
5533 BindData data;
5534 JSParseNode *pn, *pn2;
5535 JSAtom *atom;
5538 * The three options here are:
5539 * - TOK_LET: We are parsing a let declaration.
5540 * - TOK_LP: We are parsing the head of a let block.
5541 * - Otherwise, we're parsing var declarations.
5543 tt = CURRENT_TOKEN(ts).type;
5544 let = (tt == TOK_LET || tt == TOK_LP);
5545 JS_ASSERT(let || tt == TOK_VAR);
5547 #if JS_HAS_BLOCK_SCOPE
5548 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5549 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5550 #endif
5552 /* Make sure that Statement set up the tree context correctly. */
5553 scopeStmt = tc->topScopeStmt;
5554 if (let) {
5555 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5556 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5557 scopeStmt = scopeStmt->downScope;
5559 JS_ASSERT(scopeStmt);
5562 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
5563 pn = NewParseNode(PN_LIST, tc);
5564 if (!pn)
5565 return NULL;
5566 pn->pn_op = data.op;
5567 pn->makeEmpty();
5570 * SpiderMonkey const is really "write once per initialization evaluation"
5571 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5572 * this code will change soon.
5574 if (let) {
5575 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5576 data.binder = BindLet;
5577 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5578 } else {
5579 data.binder = BindVarOrConst;
5582 do {
5583 tt = js_GetToken(cx, ts);
5584 #if JS_HAS_DESTRUCTURING
5585 if (tt == TOK_LB || tt == TOK_LC) {
5586 ts->flags |= TSF_DESTRUCTURING;
5587 pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
5588 ts->flags &= ~TSF_DESTRUCTURING;
5589 if (!pn2)
5590 return NULL;
5592 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
5593 return NULL;
5594 if ((tc->flags & TCF_IN_FOR_INIT) &&
5595 js_PeekToken(cx, ts) == TOK_IN) {
5596 pn->append(pn2);
5597 continue;
5600 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5601 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5602 goto bad_var_init;
5604 #if JS_HAS_BLOCK_SCOPE
5605 if (popScope) {
5606 tc->topStmt = save->down;
5607 tc->topScopeStmt = saveScope->downScope;
5609 #endif
5610 JSParseNode *init = AssignExpr(cx, ts, tc);
5611 #if JS_HAS_BLOCK_SCOPE
5612 if (popScope) {
5613 tc->topStmt = save;
5614 tc->topScopeStmt = saveScope;
5616 #endif
5618 if (!init || !UndominateInitializers(pn2, init, tc))
5619 return NULL;
5621 pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5622 if (!pn2)
5623 return NULL;
5624 pn->append(pn2);
5625 continue;
5627 #endif /* JS_HAS_DESTRUCTURING */
5629 if (tt != TOK_NAME) {
5630 if (tt != TOK_ERROR) {
5631 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5632 JSMSG_NO_VARIABLE_NAME);
5634 return NULL;
5637 atom = CURRENT_TOKEN(ts).t_atom;
5638 pn2 = NewBindingNode(ts, atom, tc, let);
5639 if (!pn2)
5640 return NULL;
5641 if (data.op == JSOP_DEFCONST)
5642 pn2->pn_dflags |= PND_CONST;
5643 data.pn = pn2;
5644 if (!data.binder(cx, &data, atom, tc))
5645 return NULL;
5646 pn->append(pn2);
5648 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
5649 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5650 goto bad_var_init;
5652 #if JS_HAS_BLOCK_SCOPE
5653 if (popScope) {
5654 tc->topStmt = save->down;
5655 tc->topScopeStmt = saveScope->downScope;
5657 #endif
5658 JSParseNode *init = AssignExpr(cx, ts, tc);
5659 #if JS_HAS_BLOCK_SCOPE
5660 if (popScope) {
5661 tc->topStmt = save;
5662 tc->topScopeStmt = saveScope;
5664 #endif
5665 if (!init)
5666 return NULL;
5668 if (pn2->pn_used) {
5669 pn2 = MakeAssignment(pn2, init, tc);
5670 if (!pn2)
5671 return NULL;
5672 } else {
5673 pn2->pn_expr = init;
5676 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5677 ? JSOP_SETNAME
5678 : (pn2->pn_dflags & PND_GVAR)
5679 ? JSOP_SETGVAR
5680 : (pn2->pn_dflags & PND_BOUND)
5681 ? JSOP_SETLOCAL
5682 : (data.op == JSOP_DEFCONST)
5683 ? JSOP_SETCONST
5684 : JSOP_SETNAME;
5686 NoteLValue(cx, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5688 /* The declarator's position must include the initializer. */
5689 pn2->pn_pos.end = init->pn_pos.end;
5691 if ((tc->flags & TCF_IN_FUNCTION) &&
5692 atom == cx->runtime->atomState.argumentsAtom) {
5693 NoteArgumentsUse(tc);
5694 if (!let)
5695 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5698 } while (js_MatchToken(cx, ts, TOK_COMMA));
5700 pn->pn_pos.end = pn->last()->pn_pos.end;
5701 return pn;
5703 bad_var_init:
5704 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5705 JSMSG_BAD_VAR_INIT);
5706 return NULL;
5709 static JSParseNode *
5710 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5712 JSParseNode *pn, *pn2;
5714 pn = AssignExpr(cx, ts, tc);
5715 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
5716 pn2 = NewParseNode(PN_LIST, tc);
5717 if (!pn2)
5718 return NULL;
5719 pn2->pn_pos.begin = pn->pn_pos.begin;
5720 pn2->initList(pn);
5721 pn = pn2;
5722 do {
5723 #if JS_HAS_GENERATORS
5724 pn2 = pn->last();
5725 if (pn2->pn_type == TOK_YIELD) {
5726 js_ReportCompileErrorNumber(cx, ts, pn2, JSREPORT_ERROR,
5727 JSMSG_BAD_GENERATOR_SYNTAX,
5728 js_yield_str);
5729 return NULL;
5731 #endif
5732 pn2 = AssignExpr(cx, ts, tc);
5733 if (!pn2)
5734 return NULL;
5735 pn->append(pn2);
5736 } while (js_MatchToken(cx, ts, TOK_COMMA));
5737 pn->pn_pos.end = pn->last()->pn_pos.end;
5739 return pn;
5742 static JSParseNode *
5743 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5745 JSParseNode *pn, *pn2;
5746 JSTokenType tt;
5747 JSOp op;
5749 JS_CHECK_RECURSION(cx, return NULL);
5751 #if JS_HAS_GENERATORS
5752 ts->flags |= TSF_OPERAND;
5753 if (js_MatchToken(cx, ts, TOK_YIELD)) {
5754 ts->flags &= ~TSF_OPERAND;
5755 return ReturnOrYield(cx, ts, tc, AssignExpr);
5757 ts->flags &= ~TSF_OPERAND;
5758 #endif
5760 pn = CondExpr(cx, ts, tc);
5761 if (!pn)
5762 return NULL;
5764 tt = js_GetToken(cx, ts);
5765 #if JS_HAS_GETTER_SETTER
5766 if (tt == TOK_NAME) {
5767 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
5768 if (tt == TOK_ERROR)
5769 return NULL;
5771 #endif
5772 if (tt != TOK_ASSIGN) {
5773 js_UngetToken(ts);
5774 return pn;
5777 op = CURRENT_TOKEN(ts).t_op;
5778 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
5779 continue;
5780 switch (pn2->pn_type) {
5781 case TOK_NAME:
5782 pn2->pn_op = JSOP_SETNAME;
5783 NoteLValue(cx, pn2, tc);
5784 break;
5785 case TOK_DOT:
5786 pn2->pn_op = JSOP_SETPROP;
5787 break;
5788 case TOK_LB:
5789 pn2->pn_op = JSOP_SETELEM;
5790 break;
5791 #if JS_HAS_DESTRUCTURING
5792 case TOK_RB:
5793 case TOK_RC:
5794 if (op != JSOP_NOP) {
5795 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5796 JSMSG_BAD_DESTRUCT_ASS);
5797 return NULL;
5799 pn = AssignExpr(cx, ts, tc);
5800 if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
5801 return NULL;
5802 return NewBinary(TOK_ASSIGN, op, pn2, pn, tc);
5803 #endif
5804 #if JS_HAS_LVALUE_RETURN
5805 case TOK_LP:
5806 if (!MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5807 return NULL;
5808 break;
5809 #endif
5810 #if JS_HAS_XML_SUPPORT
5811 case TOK_UNARYOP:
5812 if (pn2->pn_op == JSOP_XMLNAME) {
5813 pn2->pn_op = JSOP_SETXMLNAME;
5814 break;
5816 /* FALL THROUGH */
5817 #endif
5818 default:
5819 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5820 JSMSG_BAD_LEFTSIDE_OF_ASS);
5821 return NULL;
5824 JSParseNode *pn3 = AssignExpr(cx, ts, tc);
5825 if (pn3 && PN_TYPE(pn2) == TOK_NAME && pn2->pn_used) {
5826 JSDefinition *dn = pn2->pn_lexdef;
5829 * If the definition is not flagged as assigned, we must have imputed
5830 * the initialized flag to it, to optimize for flat closures. But that
5831 * optimization uses source coordinates to check dominance relations,
5832 * so we must extend the end of the definition to cover the right-hand
5833 * side of this assignment, i.e., the initializer.
5835 if (!dn->isAssigned()) {
5836 JS_ASSERT(dn->isInitialized());
5837 dn->pn_pos.end = pn3->pn_pos.end;
5841 return NewBinary(TOK_ASSIGN, op, pn2, pn3, tc);
5844 static JSParseNode *
5845 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5847 JSParseNode *pn, *pn1, *pn2, *pn3;
5848 uintN oldflags;
5850 pn = OrExpr(cx, ts, tc);
5851 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
5852 pn1 = pn;
5853 pn = NewParseNode(PN_TERNARY, tc);
5854 if (!pn)
5855 return NULL;
5857 * Always accept the 'in' operator in the middle clause of a ternary,
5858 * where it's unambiguous, even if we might be parsing the init of a
5859 * for statement.
5861 oldflags = tc->flags;
5862 tc->flags &= ~TCF_IN_FOR_INIT;
5863 pn2 = AssignExpr(cx, ts, tc);
5864 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5866 if (!pn2)
5867 return NULL;
5868 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
5869 pn3 = AssignExpr(cx, ts, tc);
5870 if (!pn3)
5871 return NULL;
5872 pn->pn_pos.begin = pn1->pn_pos.begin;
5873 pn->pn_pos.end = pn3->pn_pos.end;
5874 pn->pn_kid1 = pn1;
5875 pn->pn_kid2 = pn2;
5876 pn->pn_kid3 = pn3;
5878 return pn;
5881 static JSParseNode *
5882 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5884 JSParseNode *pn;
5886 pn = AndExpr(cx, ts, tc);
5887 while (pn && js_MatchToken(cx, ts, TOK_OR))
5888 pn = NewBinary(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
5889 return pn;
5892 static JSParseNode *
5893 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5895 JSParseNode *pn;
5897 pn = BitOrExpr(cx, ts, tc);
5898 while (pn && js_MatchToken(cx, ts, TOK_AND))
5899 pn = NewBinary(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
5900 return pn;
5903 static JSParseNode *
5904 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5906 JSParseNode *pn;
5908 pn = BitXorExpr(cx, ts, tc);
5909 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
5910 pn = NewBinary(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
5911 tc);
5913 return pn;
5916 static JSParseNode *
5917 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5919 JSParseNode *pn;
5921 pn = BitAndExpr(cx, ts, tc);
5922 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
5923 pn = NewBinary(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
5924 tc);
5926 return pn;
5929 static JSParseNode *
5930 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5932 JSParseNode *pn;
5934 pn = EqExpr(cx, ts, tc);
5935 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
5936 pn = NewBinary(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
5937 return pn;
5940 static JSParseNode *
5941 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5943 JSParseNode *pn;
5944 JSOp op;
5946 pn = RelExpr(cx, ts, tc);
5947 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
5948 op = CURRENT_TOKEN(ts).t_op;
5949 pn = NewBinary(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
5951 return pn;
5954 static JSParseNode *
5955 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5957 JSParseNode *pn;
5958 JSTokenType tt;
5959 JSOp op;
5960 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
5963 * Uses of the in operator in ShiftExprs are always unambiguous,
5964 * so unset the flag that prohibits recognizing it.
5966 tc->flags &= ~TCF_IN_FOR_INIT;
5968 pn = ShiftExpr(cx, ts, tc);
5969 while (pn &&
5970 (js_MatchToken(cx, ts, TOK_RELOP) ||
5972 * Recognize the 'in' token as an operator only if we're not
5973 * currently in the init expr of a for loop.
5975 (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
5976 js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
5977 tt = CURRENT_TOKEN(ts).type;
5978 op = CURRENT_TOKEN(ts).t_op;
5979 pn = NewBinary(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
5981 /* Restore previous state of inForInit flag. */
5982 tc->flags |= inForInitFlag;
5984 return pn;
5987 static JSParseNode *
5988 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5990 JSParseNode *pn;
5991 JSOp op;
5993 pn = AddExpr(cx, ts, tc);
5994 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
5995 op = CURRENT_TOKEN(ts).t_op;
5996 pn = NewBinary(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
5998 return pn;
6001 static JSParseNode *
6002 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6004 JSParseNode *pn;
6005 JSTokenType tt;
6006 JSOp op;
6008 pn = MulExpr(cx, ts, tc);
6009 while (pn &&
6010 (js_MatchToken(cx, ts, TOK_PLUS) ||
6011 js_MatchToken(cx, ts, TOK_MINUS))) {
6012 tt = CURRENT_TOKEN(ts).type;
6013 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6014 pn = NewBinary(tt, op, pn, MulExpr(cx, ts, tc), tc);
6016 return pn;
6019 static JSParseNode *
6020 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6022 JSParseNode *pn;
6023 JSTokenType tt;
6024 JSOp op;
6026 pn = UnaryExpr(cx, ts, tc);
6027 while (pn &&
6028 (js_MatchToken(cx, ts, TOK_STAR) ||
6029 js_MatchToken(cx, ts, TOK_DIVOP))) {
6030 tt = CURRENT_TOKEN(ts).type;
6031 op = CURRENT_TOKEN(ts).t_op;
6032 pn = NewBinary(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
6034 return pn;
6037 static JSParseNode *
6038 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
6039 const char *name)
6041 while (kid->pn_type == TOK_RP)
6042 kid = kid->pn_kid;
6043 if (kid->pn_type != TOK_NAME &&
6044 kid->pn_type != TOK_DOT &&
6045 #if JS_HAS_LVALUE_RETURN
6046 (kid->pn_type != TOK_LP ||
6047 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6048 #endif
6049 #if JS_HAS_XML_SUPPORT
6050 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6051 #endif
6052 kid->pn_type != TOK_LB) {
6053 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6054 JSMSG_BAD_OPERAND, name);
6055 return NULL;
6057 pn->pn_kid = kid;
6058 return kid;
6061 static const char incop_name_str[][10] = {"increment", "decrement"};
6063 static JSBool
6064 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6065 JSParseNode *pn, JSParseNode *kid,
6066 JSTokenType tt, JSBool preorder)
6068 JSOp op;
6070 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
6071 if (!kid)
6072 return JS_FALSE;
6073 switch (kid->pn_type) {
6074 case TOK_NAME:
6075 op = (tt == TOK_INC)
6076 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6077 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6078 NoteLValue(cx, kid, tc);
6079 break;
6081 case TOK_DOT:
6082 op = (tt == TOK_INC)
6083 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6084 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6085 break;
6087 #if JS_HAS_LVALUE_RETURN
6088 case TOK_LP:
6089 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6090 return JS_FALSE;
6091 /* FALL THROUGH */
6092 #endif
6093 #if JS_HAS_XML_SUPPORT
6094 case TOK_UNARYOP:
6095 if (kid->pn_op == JSOP_XMLNAME)
6096 kid->pn_op = JSOP_SETXMLNAME;
6097 /* FALL THROUGH */
6098 #endif
6099 case TOK_LB:
6100 op = (tt == TOK_INC)
6101 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6102 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6103 break;
6105 default:
6106 JS_ASSERT(0);
6107 op = JSOP_NOP;
6109 pn->pn_op = op;
6110 return JS_TRUE;
6113 static JSParseNode *
6114 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6116 JSTokenType tt;
6117 JSParseNode *pn, *pn2;
6119 JS_CHECK_RECURSION(cx, return NULL);
6121 ts->flags |= TSF_OPERAND;
6122 tt = js_GetToken(cx, ts);
6123 ts->flags &= ~TSF_OPERAND;
6125 switch (tt) {
6126 case TOK_UNARYOP:
6127 case TOK_PLUS:
6128 case TOK_MINUS:
6129 pn = NewParseNode(PN_UNARY, tc);
6130 if (!pn)
6131 return NULL;
6132 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6133 pn->pn_op = CURRENT_TOKEN(ts).t_op;
6134 pn2 = UnaryExpr(cx, ts, tc);
6135 if (!pn2)
6136 return NULL;
6137 pn->pn_pos.end = pn2->pn_pos.end;
6138 pn->pn_kid = pn2;
6139 break;
6141 case TOK_INC:
6142 case TOK_DEC:
6143 pn = NewParseNode(PN_UNARY, tc);
6144 if (!pn)
6145 return NULL;
6146 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
6147 if (!pn2)
6148 return NULL;
6149 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
6150 return NULL;
6151 pn->pn_pos.end = pn2->pn_pos.end;
6152 break;
6154 case TOK_DELETE:
6155 pn = NewParseNode(PN_UNARY, tc);
6156 if (!pn)
6157 return NULL;
6158 pn2 = UnaryExpr(cx, ts, tc);
6159 if (!pn2)
6160 return NULL;
6161 pn->pn_pos.end = pn2->pn_pos.end;
6164 * Under ECMA3, deleting any unary expression is valid -- it simply
6165 * returns true. Here we strip off any parentheses and fold constants
6166 * before checking for a call expression, in order to rule out delete
6167 * of a generator expression.
6169 while (pn2->pn_type == TOK_RP)
6170 pn2 = pn2->pn_kid;
6171 if (!js_FoldConstants(cx, pn2, tc))
6172 return NULL;
6173 switch (pn2->pn_type) {
6174 case TOK_LP:
6175 if (pn2->pn_op != JSOP_SETCALL &&
6176 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6177 return NULL;
6179 break;
6180 case TOK_NAME:
6181 pn2->pn_op = JSOP_DELNAME;
6182 break;
6183 default:;
6185 pn->pn_kid = pn2;
6186 break;
6188 case TOK_ERROR:
6189 return NULL;
6191 default:
6192 js_UngetToken(ts);
6193 pn = MemberExpr(cx, ts, tc, JS_TRUE);
6194 if (!pn)
6195 return NULL;
6197 /* Don't look across a newline boundary for a postfix incop. */
6198 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
6199 ts->flags |= TSF_OPERAND;
6200 tt = js_PeekTokenSameLine(cx, ts);
6201 ts->flags &= ~TSF_OPERAND;
6202 if (tt == TOK_INC || tt == TOK_DEC) {
6203 (void) js_GetToken(cx, ts);
6204 pn2 = NewParseNode(PN_UNARY, tc);
6205 if (!pn2)
6206 return NULL;
6207 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
6208 return NULL;
6209 pn2->pn_pos.begin = pn->pn_pos.begin;
6210 pn = pn2;
6213 break;
6215 return pn;
6218 #if JS_HAS_GENERATORS
6221 * A dedicated helper for transplanting the comprehension expression E in
6223 * [E for (V in I)] // array comprehension
6224 * (E for (V in I)) // generator expression
6226 * from its initial location in the AST, on the left of the 'for', to its final
6227 * position on the right. To avoid a separate pass we do this by adjusting the
6228 * blockids and name binding links that were established when E was parsed.
6230 * A generator expression desugars like so:
6232 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6234 * so the transplanter must adjust static level as well as blockid. E's source
6235 * coordinates in root->pn_pos are critical to deciding which binding links to
6236 * preserve and which to cut.
6238 * NB: This is not a general tree transplanter -- it knows in particular that
6239 * the one or more bindings induced by V have not yet been created.
6241 class CompExprTransplanter {
6242 JSParseNode *root;
6243 JSTreeContext *tc;
6244 bool genexp;
6245 uintN adjust;
6246 uintN funcLevel;
6248 public:
6249 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6250 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6254 bool transplant(JSParseNode *pn);
6258 * Any definitions nested within the comprehension expression of a generator
6259 * expression must move "down" one static level, which of course increases the
6260 * upvar-frame-skip count.
6262 static bool
6263 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6265 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6266 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6268 JS_ASSERT(level >= tc->staticLevel);
6269 if (level >= FREE_STATIC_LEVEL) {
6270 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
6271 JSMSG_TOO_DEEP, js_function_str);
6272 return false;
6275 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6277 return true;
6280 static void
6281 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6283 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6284 pn->pn_blockid += adjust;
6285 if (pn->pn_blockid >= tc->blockidGen)
6286 tc->blockidGen = pn->pn_blockid + 1;
6289 bool
6290 CompExprTransplanter::transplant(JSParseNode *pn)
6292 if (!pn)
6293 return true;
6295 switch (pn->pn_arity) {
6296 case PN_LIST:
6297 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6298 transplant(pn2);
6299 if (pn->pn_pos >= root->pn_pos)
6300 AdjustBlockId(pn, adjust, tc);
6301 break;
6303 case PN_TERNARY:
6304 transplant(pn->pn_kid1);
6305 transplant(pn->pn_kid2);
6306 transplant(pn->pn_kid3);
6307 break;
6309 case PN_BINARY:
6310 transplant(pn->pn_left);
6312 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6313 if (pn->pn_right != pn->pn_left)
6314 transplant(pn->pn_right);
6315 break;
6317 case PN_UNARY:
6318 transplant(pn->pn_kid);
6319 break;
6321 case PN_FUNC:
6324 * Only the first level of transplant recursion through functions needs
6325 * to reparent the funbox, since all descendant functions are correctly
6326 * linked under the top-most funbox. But every visit to this case needs
6327 * to update funbox->level.
6329 * Recall that funbox->level is the static level of the code containing
6330 * the definition or expression of the function and not the static level
6331 * of the function's body.
6333 JSFunctionBox *funbox = pn->pn_funbox;
6335 funbox->level = tc->staticLevel + funcLevel;
6336 if (++funcLevel == 1 && genexp) {
6337 JSFunctionBox *parent = tc->funbox;
6339 JSFunctionBox **funboxp = &tc->parent->functionList;
6340 while (*funboxp != funbox)
6341 funboxp = &(*funboxp)->siblings;
6342 *funboxp = funbox->siblings;
6344 funbox->parent = parent;
6345 funbox->siblings = parent->kids;
6346 parent->kids = funbox;
6347 funbox->level = tc->staticLevel;
6349 /* FALL THROUGH */
6352 case PN_NAME:
6353 transplant(pn->maybeExpr());
6354 if (pn->pn_arity == PN_FUNC)
6355 --funcLevel;
6357 if (pn->pn_defn) {
6358 if (genexp && !BumpStaticLevel(pn, tc))
6359 return false;
6360 } else if (pn->pn_used) {
6361 JS_ASSERT(pn->pn_op != JSOP_NOP);
6362 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6364 JSDefinition *dn = pn->pn_lexdef;
6365 JS_ASSERT(dn->pn_defn);
6368 * Adjust the definition's block id only if it is a placeholder not
6369 * to the left of the root node, and if pn is the last use visited
6370 * in the comprehension expression (to avoid adjusting the blockid
6371 * multiple times).
6373 * Non-placeholder definitions within the comprehension expression
6374 * will be visited further below.
6376 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6377 if (genexp && !BumpStaticLevel(dn, tc))
6378 return false;
6379 AdjustBlockId(dn, adjust, tc);
6382 JSAtom *atom = pn->pn_atom;
6383 #ifdef DEBUG
6384 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6385 JS_ASSERT(!stmt || stmt != tc->topStmt);
6386 #endif
6387 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6388 JS_ASSERT(!tc->decls.lookup(atom));
6390 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6391 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
6392 if (!ale)
6393 return false;
6395 if (dn->pn_pos >= root->pn_pos) {
6396 tc->parent->lexdeps.remove(tc->compiler, atom);
6397 } else {
6398 JSDefinition *dn2 = (JSDefinition *)
6399 NewNameNode(tc->compiler->context, TS(tc->compiler), dn->pn_atom, tc);
6400 if (!dn2)
6401 return false;
6403 dn2->pn_type = dn->pn_type;
6404 dn2->pn_pos = root->pn_pos;
6405 dn2->pn_defn = true;
6406 dn2->pn_dflags |= PND_PLACEHOLDER;
6408 JSParseNode **pnup = &dn->dn_uses;
6409 JSParseNode *pnu;
6410 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6411 pnu->pn_lexdef = dn2;
6412 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6413 pnup = &pnu->pn_link;
6415 dn2->dn_uses = dn->dn_uses;
6416 dn->dn_uses = *pnup;
6417 *pnup = NULL;
6419 dn = dn2;
6422 ALE_SET_DEFN(ale, dn);
6427 if (pn->pn_pos >= root->pn_pos)
6428 AdjustBlockId(pn, adjust, tc);
6429 break;
6431 case PN_NAMESET:
6432 transplant(pn->pn_tree);
6433 break;
6435 return true;
6439 * Starting from a |for| keyword after the first array initialiser element or
6440 * an expression in an open parenthesis, parse the tail of the comprehension
6441 * or generator expression signified by this |for| keyword in context.
6443 * Return null on failure, else return the top-most parse node for the array
6444 * comprehension or generator expression, with a unary node as the body of the
6445 * (possibly nested) for-loop, initialized by |type, op, kid|.
6447 static JSParseNode *
6448 ComprehensionTail(JSParseNode *kid, uintN blockid, JSTreeContext *tc,
6449 JSTokenType type = TOK_SEMI, JSOp op = JSOP_NOP)
6451 JSContext *cx = tc->compiler->context;
6452 JSTokenStream *ts = TS(tc->compiler);
6454 uintN adjust;
6455 JSParseNode *pn, *pn2, *pn3, **pnp;
6456 JSStmtInfo stmtInfo;
6457 BindData data;
6458 JSTokenType tt;
6459 JSAtom *atom;
6461 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
6463 if (type == TOK_SEMI) {
6465 * Generator expression desugars to an immediately applied lambda that
6466 * yields the next value from a for-in loop (possibly nested, and with
6467 * optional if guard). Make pn be the TOK_LC body node.
6469 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6470 if (!pn)
6471 return NULL;
6472 adjust = pn->pn_blockid - blockid;
6473 } else {
6474 JS_ASSERT(type == TOK_ARRAYPUSH);
6477 * Make a parse-node and literal object representing the block scope of
6478 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6479 * aka the array initialiser case, has passed the blockid to claim for
6480 * the comprehension's block scope. We allocate that id or one above it
6481 * here, by calling js_PushLexicalScope.
6483 * In the case of a comprehension expression that has nested blocks
6484 * (e.g., let expressions), we will allocate a higher blockid but then
6485 * slide all blocks "to the right" to make room for the comprehension's
6486 * block scope.
6488 adjust = tc->blockid();
6489 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6490 if (!pn)
6491 return NULL;
6493 JS_ASSERT(blockid <= pn->pn_blockid);
6494 JS_ASSERT(blockid < tc->blockidGen);
6495 JS_ASSERT(tc->bodyid < blockid);
6496 pn->pn_blockid = stmtInfo.blockid = blockid;
6497 JS_ASSERT(adjust < blockid);
6498 adjust = blockid - adjust;
6501 pnp = &pn->pn_expr;
6503 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6504 transplanter.transplant(kid);
6506 data.pn = NULL;
6507 data.op = JSOP_NOP;
6508 data.binder = BindLet;
6509 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6511 do {
6513 * FOR node is binary, left is loop control and right is body. Use
6514 * index to count each block-local let-variable on the left-hand side
6515 * of the IN.
6517 pn2 = NewParseNode(PN_BINARY, tc);
6518 if (!pn2)
6519 return NULL;
6521 pn2->pn_op = JSOP_ITER;
6522 pn2->pn_iflags = JSITER_ENUMERATE;
6523 if (js_MatchToken(cx, ts, TOK_NAME)) {
6524 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
6525 pn2->pn_iflags |= JSITER_FOREACH;
6526 else
6527 js_UngetToken(ts);
6529 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6531 atom = NULL;
6532 tt = js_GetToken(cx, ts);
6533 switch (tt) {
6534 #if JS_HAS_DESTRUCTURING
6535 case TOK_LB:
6536 case TOK_LC:
6537 ts->flags |= TSF_DESTRUCTURING;
6538 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6539 ts->flags &= ~TSF_DESTRUCTURING;
6540 if (!pn3)
6541 return NULL;
6542 break;
6543 #endif
6545 case TOK_NAME:
6546 atom = CURRENT_TOKEN(ts).t_atom;
6549 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6550 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6551 * in the operand stack frame. The code generator computes that,
6552 * and it tries to bind all names to slots, so we must let it do
6553 * the deed.
6555 pn3 = NewBindingNode(ts, atom, tc, true);
6556 if (!pn3)
6557 return NULL;
6558 break;
6560 default:
6561 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6562 JSMSG_NO_VARIABLE_NAME);
6564 case TOK_ERROR:
6565 return NULL;
6568 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6569 JSParseNode *pn4 = Expr(cx, ts, tc);
6570 if (!pn4)
6571 return NULL;
6572 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6574 switch (tt) {
6575 #if JS_HAS_DESTRUCTURING
6576 case TOK_LB:
6577 case TOK_LC:
6578 if (!CheckDestructuring(cx, &data, pn3, NULL, tc))
6579 return NULL;
6581 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
6582 /* Destructuring requires [key, value] enumeration in JS1.7. */
6583 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6584 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6585 JSMSG_BAD_FOR_LEFTSIDE);
6586 return NULL;
6589 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6590 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6591 if (!(pn2->pn_iflags & JSITER_FOREACH))
6592 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6594 break;
6595 #endif
6597 case TOK_NAME:
6598 data.pn = pn3;
6599 if (!data.binder(cx, &data, atom, tc))
6600 return NULL;
6601 break;
6603 default:;
6606 pn2->pn_left = NewBinary(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6607 if (!pn2->pn_left)
6608 return NULL;
6609 *pnp = pn2;
6610 pnp = &pn2->pn_right;
6611 } while (js_MatchToken(cx, ts, TOK_FOR));
6613 if (js_MatchToken(cx, ts, TOK_IF)) {
6614 pn2 = NewParseNode(PN_TERNARY, tc);
6615 if (!pn2)
6616 return NULL;
6617 pn2->pn_kid1 = Condition(cx, ts, tc);
6618 if (!pn2->pn_kid1)
6619 return NULL;
6620 *pnp = pn2;
6621 pnp = &pn2->pn_kid2;
6624 pn2 = NewParseNode(PN_UNARY, tc);
6625 if (!pn2)
6626 return NULL;
6627 pn2->pn_type = type;
6628 pn2->pn_op = op;
6629 pn2->pn_kid = kid;
6630 *pnp = pn2;
6632 if (type == TOK_ARRAYPUSH)
6633 PopStatement(tc);
6634 return pn;
6637 #if JS_HAS_GENERATOR_EXPRS
6640 * Starting from a |for| keyword after an expression, parse the comprehension
6641 * tail completing this generator expression. Wrap the expression at kid in a
6642 * generator function that is immediately called to evaluate to the generator
6643 * iterator that is the value of this generator expression.
6645 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6646 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6647 * expression-statement node that constitutes the body of the |for| loop(s) in
6648 * the generator function.
6650 * Note how unlike Python, we do not evaluate the expression to the right of
6651 * the first |in| in the chain of |for| heads. Instead, a generator expression
6652 * is merely sugar for a generator function expression and its application.
6654 static JSParseNode *
6655 GeneratorExpr(JSParseNode *pn, JSParseNode *kid, JSTreeContext *tc)
6657 /* Initialize pn, connecting it to kid. */
6658 JS_ASSERT(pn->pn_arity == PN_UNARY);
6659 pn->pn_type = TOK_YIELD;
6660 pn->pn_op = JSOP_YIELD;
6661 pn->pn_pos = kid->pn_pos;
6662 pn->pn_kid = kid;
6663 pn->pn_hidden = JS_TRUE;
6665 /* Make a new node for the desugared generator function. */
6666 JSParseNode *genfn = NewParseNode(PN_FUNC, tc);
6667 if (!genfn)
6668 return NULL;
6669 genfn->pn_type = TOK_FUNCTION;
6670 genfn->pn_op = JSOP_LAMBDA;
6671 JS_ASSERT(!genfn->pn_body);
6672 genfn->pn_dflags = PND_FUNARG;
6675 JSTreeContext gentc(tc->compiler);
6677 JSFunctionBox *funbox = EnterFunction(genfn, tc, &gentc);
6678 if (!funbox)
6679 return NULL;
6682 * We assume conservatively that any deoptimization flag in tc->flags
6683 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6684 * propagate these flags into genfn. For code simplicity we also do
6685 * not detect if the flags were only set in the kid and could be
6686 * removed from tc->flags.
6688 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6689 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6690 funbox->tcflags |= gentc.flags;
6691 genfn->pn_funbox = funbox;
6692 genfn->pn_blockid = gentc.bodyid;
6694 JSParseNode *body = ComprehensionTail(pn, tc->blockid(), &gentc);
6695 if (!body)
6696 return NULL;
6697 JS_ASSERT(!genfn->pn_body);
6698 genfn->pn_body = body;
6699 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6700 genfn->pn_pos.end = body->pn_pos.end = CURRENT_TOKEN(TS(tc->compiler)).pos.end;
6702 if (!LeaveFunction(genfn, &gentc, tc))
6703 return NULL;
6707 * Our result is a call expression that invokes the anonymous generator
6708 * function object.
6710 JSParseNode *result = NewParseNode(PN_LIST, tc);
6711 if (!result)
6712 return NULL;
6713 result->pn_type = TOK_LP;
6714 result->pn_op = JSOP_CALL;
6715 result->pn_pos.begin = genfn->pn_pos.begin;
6716 result->initList(genfn);
6717 return result;
6720 static const char js_generator_str[] = "generator";
6722 #endif /* JS_HAS_GENERATOR_EXPRS */
6723 #endif /* JS_HAS_GENERATORS */
6725 static JSBool
6726 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6727 JSParseNode *listNode)
6729 JSBool matched;
6731 ts->flags |= TSF_OPERAND;
6732 matched = js_MatchToken(cx, ts, TOK_RP);
6733 ts->flags &= ~TSF_OPERAND;
6734 if (!matched) {
6735 do {
6736 JSParseNode *argNode = AssignExpr(cx, ts, tc);
6737 if (!argNode)
6738 return JS_FALSE;
6739 #if JS_HAS_GENERATORS
6740 if (argNode->pn_type == TOK_YIELD &&
6741 js_PeekToken(cx, ts) == TOK_COMMA) {
6742 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6743 JSMSG_BAD_GENERATOR_SYNTAX,
6744 js_yield_str);
6745 return JS_FALSE;
6747 #endif
6748 #if JS_HAS_GENERATOR_EXPRS
6749 if (js_MatchToken(cx, ts, TOK_FOR)) {
6750 JSParseNode *pn = NewParseNode(PN_UNARY, tc);
6751 if (!pn)
6752 return JS_FALSE;
6753 argNode = GeneratorExpr(pn, argNode, tc);
6754 if (!argNode)
6755 return JS_FALSE;
6756 if (listNode->pn_count > 1 ||
6757 js_PeekToken(cx, ts) == TOK_COMMA) {
6758 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6759 JSMSG_BAD_GENERATOR_SYNTAX,
6760 js_generator_str);
6761 return JS_FALSE;
6764 #endif
6765 listNode->append(argNode);
6766 } while (js_MatchToken(cx, ts, TOK_COMMA));
6768 if (js_GetToken(cx, ts) != TOK_RP) {
6769 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6770 JSMSG_PAREN_AFTER_ARGS);
6771 return JS_FALSE;
6774 return JS_TRUE;
6777 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6778 static JSParseNode *
6779 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6781 while (pn->pn_type == TOK_RP)
6782 pn = pn->pn_kid;
6783 if (pn->pn_type == TOK_FUNCTION) {
6784 JS_ASSERT(pn->pn_arity == PN_FUNC);
6786 JSFunctionBox *funbox = pn->pn_funbox;
6787 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6788 if (!(funbox->tcflags & TCF_FUN_USES_ARGUMENTS))
6789 pn->pn_dflags &= ~PND_FUNARG;
6791 return pn;
6794 static JSParseNode *
6795 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6796 JSBool allowCallSyntax)
6798 JSParseNode *pn, *pn2, *pn3;
6799 JSTokenType tt;
6801 JS_CHECK_RECURSION(cx, return NULL);
6803 /* Check for new expression first. */
6804 ts->flags |= TSF_OPERAND;
6805 tt = js_GetToken(cx, ts);
6806 ts->flags &= ~TSF_OPERAND;
6807 if (tt == TOK_NEW) {
6808 pn = NewParseNode(PN_LIST, tc);
6809 if (!pn)
6810 return NULL;
6811 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
6812 if (!pn2)
6813 return NULL;
6814 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6815 pn->pn_op = JSOP_NEW;
6816 pn->initList(pn2);
6817 pn->pn_pos.begin = pn2->pn_pos.begin;
6819 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
6820 return NULL;
6821 if (pn->pn_count > ARGC_LIMIT) {
6822 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6823 JSMSG_TOO_MANY_CON_ARGS);
6824 return NULL;
6826 pn->pn_pos.end = pn->last()->pn_pos.end;
6827 } else {
6828 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6829 if (!pn)
6830 return NULL;
6832 if (pn->pn_type == TOK_ANYNAME ||
6833 pn->pn_type == TOK_AT ||
6834 pn->pn_type == TOK_DBLCOLON) {
6835 pn2 = NewOrRecycledNode(tc);
6836 if (!pn2)
6837 return NULL;
6838 pn2->pn_type = TOK_UNARYOP;
6839 pn2->pn_pos = pn->pn_pos;
6840 pn2->pn_op = JSOP_XMLNAME;
6841 pn2->pn_arity = PN_UNARY;
6842 pn2->pn_kid = pn;
6843 pn = pn2;
6847 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
6848 if (tt == TOK_DOT) {
6849 pn2 = NewNameNode(cx, ts, NULL, tc);
6850 if (!pn2)
6851 return NULL;
6852 #if JS_HAS_XML_SUPPORT
6853 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6854 tt = js_GetToken(cx, ts);
6855 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6856 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6857 if (!pn3)
6858 return NULL;
6859 tt = PN_TYPE(pn3);
6860 if (tt == TOK_NAME) {
6861 pn2->pn_op = JSOP_GETPROP;
6862 pn2->pn_expr = pn;
6863 pn2->pn_atom = pn3->pn_atom;
6864 RecycleTree(pn3, tc);
6865 } else {
6866 if (TOKEN_TYPE_IS_XML(tt)) {
6867 pn2->pn_type = TOK_LB;
6868 pn2->pn_op = JSOP_GETELEM;
6869 } else if (tt == TOK_RP) {
6870 JSParseNode *group = pn3;
6872 /* Recycle the useless TOK_RP node. */
6873 pn3 = group->pn_kid;
6874 group->pn_kid = NULL;
6875 RecycleTree(group, tc);
6876 pn2->pn_type = TOK_FILTER;
6877 pn2->pn_op = JSOP_FILTER;
6879 /* A filtering predicate is like a with statement. */
6880 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6881 } else {
6882 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6883 JSMSG_NAME_AFTER_DOT);
6884 return NULL;
6886 pn2->pn_arity = PN_BINARY;
6887 pn2->pn_left = pn;
6888 pn2->pn_right = pn3;
6890 #else
6891 ts->flags |= TSF_KEYWORD_IS_NAME;
6892 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
6893 ts->flags &= ~TSF_KEYWORD_IS_NAME;
6894 pn2->pn_op = JSOP_GETPROP;
6895 pn2->pn_expr = pn;
6896 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
6897 #endif
6898 pn2->pn_pos.begin = pn->pn_pos.begin;
6899 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6900 #if JS_HAS_XML_SUPPORT
6901 } else if (tt == TOK_DBLDOT) {
6902 pn2 = NewParseNode(PN_BINARY, tc);
6903 if (!pn2)
6904 return NULL;
6905 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
6906 tt = js_GetToken(cx, ts);
6907 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6908 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
6909 if (!pn3)
6910 return NULL;
6911 tt = PN_TYPE(pn3);
6912 if (tt == TOK_NAME) {
6913 pn3->pn_type = TOK_STRING;
6914 pn3->pn_arity = PN_NULLARY;
6915 pn3->pn_op = JSOP_QNAMEPART;
6916 } else if (!TOKEN_TYPE_IS_XML(tt)) {
6917 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6918 JSMSG_NAME_AFTER_DOT);
6919 return NULL;
6921 pn2->pn_op = JSOP_DESCENDANTS;
6922 pn2->pn_left = pn;
6923 pn2->pn_right = pn3;
6924 pn2->pn_pos.begin = pn->pn_pos.begin;
6925 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6926 #endif
6927 } else if (tt == TOK_LB) {
6928 pn2 = NewParseNode(PN_BINARY, tc);
6929 if (!pn2)
6930 return NULL;
6931 pn3 = Expr(cx, ts, tc);
6932 if (!pn3)
6933 return NULL;
6935 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
6936 pn2->pn_pos.begin = pn->pn_pos.begin;
6937 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6940 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6941 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6942 * the interpreter from fast property access. However, if the
6943 * bracketed string is a uint32, we rewrite pn3 to be a number
6944 * instead of a string.
6946 do {
6947 if (pn3->pn_type == TOK_STRING) {
6948 jsuint index;
6950 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
6951 pn2->pn_type = TOK_DOT;
6952 pn2->pn_op = JSOP_GETPROP;
6953 pn2->pn_arity = PN_NAME;
6954 pn2->pn_expr = pn;
6955 pn2->pn_atom = pn3->pn_atom;
6956 break;
6958 pn3->pn_type = TOK_NUMBER;
6959 pn3->pn_op = JSOP_DOUBLE;
6960 pn3->pn_dval = index;
6962 pn2->pn_op = JSOP_GETELEM;
6963 pn2->pn_left = pn;
6964 pn2->pn_right = pn3;
6965 } while (0);
6966 } else if (allowCallSyntax && tt == TOK_LP) {
6967 pn2 = NewParseNode(PN_LIST, tc);
6968 if (!pn2)
6969 return NULL;
6970 pn2->pn_op = JSOP_CALL;
6972 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
6973 pn = CheckForImmediatelyAppliedLambda(pn);
6974 if (pn->pn_op == JSOP_NAME) {
6975 if (pn->pn_atom == cx->runtime->atomState.evalAtom) {
6976 /* Select JSOP_EVAL and flag tc as heavyweight. */
6977 pn2->pn_op = JSOP_EVAL;
6978 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6980 } else if (pn->pn_op == JSOP_GETPROP) {
6981 if (pn->pn_atom == cx->runtime->atomState.applyAtom ||
6982 pn->pn_atom == cx->runtime->atomState.callAtom) {
6983 /* Select JSOP_APPLY given foo.apply(...). */
6984 pn2->pn_op = JSOP_APPLY;
6988 pn2->initList(pn);
6989 pn2->pn_pos.begin = pn->pn_pos.begin;
6991 if (!ArgumentList(cx, ts, tc, pn2))
6992 return NULL;
6993 if (pn2->pn_count > ARGC_LIMIT) {
6994 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6995 JSMSG_TOO_MANY_FUN_ARGS);
6996 return NULL;
6998 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
6999 } else {
7000 js_UngetToken(ts);
7001 return pn;
7004 pn = pn2;
7006 if (tt == TOK_ERROR)
7007 return NULL;
7008 return pn;
7011 static JSParseNode *
7012 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7014 uintN oldflags;
7015 JSParseNode *pn;
7018 * Always accept the 'in' operator in a parenthesized expression,
7019 * where it's unambiguous, even if we might be parsing the init of a
7020 * for statement.
7022 oldflags = tc->flags;
7023 tc->flags &= ~TCF_IN_FOR_INIT;
7024 pn = Expr(cx, ts, tc);
7025 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7026 return pn;
7029 #if JS_HAS_XML_SUPPORT
7031 static JSParseNode *
7032 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7034 JSParseNode *pn;
7036 pn = BracketedExpr(cx, ts, tc);
7037 if (!pn)
7038 return NULL;
7040 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7041 return pn;
7045 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7047 * AttributeIdentifier:
7048 * @ PropertySelector
7049 * @ QualifiedIdentifier
7050 * @ [ Expression ]
7052 * PropertySelector:
7053 * Identifier
7056 * QualifiedIdentifier:
7057 * PropertySelector :: PropertySelector
7058 * PropertySelector :: [ Expression ]
7060 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7062 * AttributeIdentifier:
7063 * @ QualifiedIdentifier
7064 * @ [ Expression ]
7066 * PropertySelector:
7067 * Identifier
7070 * QualifiedIdentifier:
7071 * PropertySelector :: PropertySelector
7072 * PropertySelector :: [ Expression ]
7073 * PropertySelector
7075 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7076 * for that rule to result in a name node, but ECMA-357 extends the grammar
7077 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7079 * QualifiedIdentifier:
7080 * PropertySelector QualifiedSuffix
7082 * QualifiedSuffix:
7083 * :: PropertySelector
7084 * :: [ Expression ]
7085 * /nothing/
7087 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7089 * PrimaryExpression:
7090 * Identifier QualifiedSuffix
7092 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7093 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7095 static JSParseNode *
7096 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7098 JSParseNode *pn;
7100 pn = NewParseNode(PN_NULLARY, tc);
7101 if (!pn)
7102 return NULL;
7103 if (pn->pn_type == TOK_STAR) {
7104 pn->pn_type = TOK_ANYNAME;
7105 pn->pn_op = JSOP_ANYNAME;
7106 pn->pn_atom = cx->runtime->atomState.starAtom;
7107 } else {
7108 JS_ASSERT(pn->pn_type == TOK_NAME);
7109 pn->pn_op = JSOP_QNAMEPART;
7110 pn->pn_arity = PN_NAME;
7111 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
7112 pn->pn_cookie = FREE_UPVAR_COOKIE;
7114 return pn;
7117 static JSParseNode *
7118 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7119 JSTreeContext *tc)
7121 JSParseNode *pn2, *pn3;
7122 JSTokenType tt;
7124 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
7125 pn2 = NewNameNode(cx, ts, NULL, tc);
7126 if (!pn2)
7127 return NULL;
7129 /* Left operand of :: must be evaluated if it is an identifier. */
7130 if (pn->pn_op == JSOP_QNAMEPART)
7131 pn->pn_op = JSOP_NAME;
7133 ts->flags |= TSF_KEYWORD_IS_NAME;
7134 tt = js_GetToken(cx, ts);
7135 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7136 if (tt == TOK_STAR || tt == TOK_NAME) {
7137 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7138 pn2->pn_op = JSOP_QNAMECONST;
7139 pn2->pn_pos.begin = pn->pn_pos.begin;
7140 pn2->pn_atom = (tt == TOK_STAR)
7141 ? cx->runtime->atomState.starAtom
7142 : CURRENT_TOKEN(ts).t_atom;
7143 pn2->pn_expr = pn;
7144 pn2->pn_cookie = FREE_UPVAR_COOKIE;
7145 return pn2;
7148 if (tt != TOK_LB) {
7149 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7150 JSMSG_SYNTAX_ERROR);
7151 return NULL;
7153 pn3 = EndBracketedExpr(cx, ts, tc);
7154 if (!pn3)
7155 return NULL;
7157 pn2->pn_op = JSOP_QNAME;
7158 pn2->pn_arity = PN_BINARY;
7159 pn2->pn_pos.begin = pn->pn_pos.begin;
7160 pn2->pn_pos.end = pn3->pn_pos.end;
7161 pn2->pn_left = pn;
7162 pn2->pn_right = pn3;
7163 return pn2;
7166 static JSParseNode *
7167 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7169 JSParseNode *pn;
7171 pn = PropertySelector(cx, ts, tc);
7172 if (!pn)
7173 return NULL;
7174 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7175 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7176 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7177 pn = QualifiedSuffix(cx, ts, pn, tc);
7179 return pn;
7182 static JSParseNode *
7183 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7185 JSParseNode *pn, *pn2;
7186 JSTokenType tt;
7188 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
7189 pn = NewParseNode(PN_UNARY, tc);
7190 if (!pn)
7191 return NULL;
7192 pn->pn_op = JSOP_TOATTRNAME;
7193 ts->flags |= TSF_KEYWORD_IS_NAME;
7194 tt = js_GetToken(cx, ts);
7195 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7196 if (tt == TOK_STAR || tt == TOK_NAME) {
7197 pn2 = QualifiedIdentifier(cx, ts, tc);
7198 } else if (tt == TOK_LB) {
7199 pn2 = EndBracketedExpr(cx, ts, tc);
7200 } else {
7201 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7202 JSMSG_SYNTAX_ERROR);
7203 return NULL;
7205 if (!pn2)
7206 return NULL;
7207 pn->pn_kid = pn2;
7208 return pn;
7212 * Make a TOK_LC unary node whose pn_kid is an expression.
7214 static JSParseNode *
7215 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
7217 JSParseNode *pn, *pn2;
7218 uintN oldflags;
7220 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
7221 pn = NewParseNode(PN_UNARY, tc);
7222 if (!pn)
7223 return NULL;
7226 * Turn off XML tag mode, but don't restore it after parsing this braced
7227 * expression. Instead, simply restore ts's old flags. This is required
7228 * because XMLExpr is called both from within a tag, and from within text
7229 * contained in an element, but outside of any start, end, or point tag.
7231 oldflags = ts->flags;
7232 ts->flags = oldflags & ~TSF_XMLTAGMODE;
7233 pn2 = Expr(cx, ts, tc);
7234 if (!pn2)
7235 return NULL;
7237 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7238 ts->flags = oldflags;
7239 pn->pn_kid = pn2;
7240 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7241 return pn;
7245 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7246 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7247 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7248 * child of a container tag.
7250 static JSParseNode *
7251 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7253 JSParseNode *pn;
7254 JSToken *tp;
7256 pn = NewParseNode(PN_NULLARY, tc);
7257 if (!pn)
7258 return NULL;
7259 tp = &CURRENT_TOKEN(ts);
7260 pn->pn_op = tp->t_op;
7261 pn->pn_atom = tp->t_atom;
7262 if (tp->type == TOK_XMLPI)
7263 pn->pn_atom2 = tp->t_atom2;
7264 return pn;
7268 * Parse the productions:
7270 * XMLNameExpr:
7271 * XMLName XMLNameExpr?
7272 * { Expr } XMLNameExpr?
7274 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7275 * a list of names and/or expressions, a single expression, or a single name.
7276 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7277 * will be TOK_LC.
7279 static JSParseNode *
7280 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7282 JSParseNode *pn, *pn2, *list;
7283 JSTokenType tt;
7285 pn = list = NULL;
7286 do {
7287 tt = CURRENT_TOKEN(ts).type;
7288 if (tt == TOK_LC) {
7289 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7290 if (!pn2)
7291 return NULL;
7292 } else {
7293 JS_ASSERT(tt == TOK_XMLNAME);
7294 pn2 = XMLAtomNode(cx, ts, tc);
7295 if (!pn2)
7296 return NULL;
7299 if (!pn) {
7300 pn = pn2;
7301 } else {
7302 if (!list) {
7303 list = NewParseNode(PN_LIST, tc);
7304 if (!list)
7305 return NULL;
7306 list->pn_type = TOK_XMLNAME;
7307 list->pn_pos.begin = pn->pn_pos.begin;
7308 list->initList(pn);
7309 list->pn_xflags = PNX_CANTFOLD;
7310 pn = list;
7312 pn->pn_pos.end = pn2->pn_pos.end;
7313 pn->append(pn2);
7315 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
7317 js_UngetToken(ts);
7318 return pn;
7322 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7323 * at compile time into a JSXML tree.
7325 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7326 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7327 : (pn)->pn_type != TOK_LC)
7330 * Parse the productions:
7332 * XMLTagContent:
7333 * XMLNameExpr
7334 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7335 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7337 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7338 * produces a list of name and attribute values and/or braced expressions, a
7339 * single expression, or a single name.
7341 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7342 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7343 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7344 * we parsed exactly one expression.
7346 static JSParseNode *
7347 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7348 JSTokenType tagtype, JSAtom **namep)
7350 JSParseNode *pn, *pn2, *list;
7351 JSTokenType tt;
7353 pn = XMLNameExpr(cx, ts, tc);
7354 if (!pn)
7355 return NULL;
7356 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7357 list = NULL;
7359 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
7360 tt = js_GetToken(cx, ts);
7361 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7362 js_UngetToken(ts);
7363 break;
7366 pn2 = XMLNameExpr(cx, ts, tc);
7367 if (!pn2)
7368 return NULL;
7369 if (!list) {
7370 list = NewParseNode(PN_LIST, tc);
7371 if (!list)
7372 return NULL;
7373 list->pn_type = tagtype;
7374 list->pn_pos.begin = pn->pn_pos.begin;
7375 list->initList(pn);
7376 pn = list;
7378 pn->append(pn2);
7379 if (!XML_FOLDABLE(pn2))
7380 pn->pn_xflags |= PNX_CANTFOLD;
7382 js_MatchToken(cx, ts, TOK_XMLSPACE);
7383 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7384 js_MatchToken(cx, ts, TOK_XMLSPACE);
7386 tt = js_GetToken(cx, ts);
7387 if (tt == TOK_XMLATTR) {
7388 pn2 = XMLAtomNode(cx, ts, tc);
7389 } else if (tt == TOK_LC) {
7390 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7391 pn->pn_xflags |= PNX_CANTFOLD;
7392 } else {
7393 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7394 JSMSG_BAD_XML_ATTR_VALUE);
7395 return NULL;
7397 if (!pn2)
7398 return NULL;
7399 pn->pn_pos.end = pn2->pn_pos.end;
7400 pn->append(pn2);
7403 return pn;
7406 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7407 JS_BEGIN_MACRO \
7408 if ((tt) <= TOK_EOF) { \
7409 if ((tt) == TOK_EOF) { \
7410 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7411 JSMSG_END_OF_XML_SOURCE); \
7413 return result; \
7415 JS_END_MACRO
7417 static JSParseNode *
7418 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7419 JSBool allowList);
7422 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7423 * that opens the end tag for the container.
7425 static JSBool
7426 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7427 JSTreeContext *tc)
7429 JSTokenType tt;
7430 JSParseNode *pn2;
7431 JSAtom *textAtom;
7433 ts->flags &= ~TSF_XMLTAGMODE;
7434 for (;;) {
7435 ts->flags |= TSF_XMLTEXTMODE;
7436 tt = js_GetToken(cx, ts);
7437 ts->flags &= ~TSF_XMLTEXTMODE;
7438 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7440 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7441 textAtom = CURRENT_TOKEN(ts).t_atom;
7442 if (textAtom) {
7443 /* Non-zero-length XML text scanned. */
7444 pn2 = XMLAtomNode(cx, ts, tc);
7445 if (!pn2)
7446 return JS_FALSE;
7447 pn->pn_pos.end = pn2->pn_pos.end;
7448 pn->append(pn2);
7451 ts->flags |= TSF_OPERAND;
7452 tt = js_GetToken(cx, ts);
7453 ts->flags &= ~TSF_OPERAND;
7454 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7455 if (tt == TOK_XMLETAGO)
7456 break;
7458 if (tt == TOK_LC) {
7459 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
7460 pn->pn_xflags |= PNX_CANTFOLD;
7461 } else if (tt == TOK_XMLSTAGO) {
7462 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
7463 if (pn2) {
7464 pn2->pn_xflags &= ~PNX_XMLROOT;
7465 pn->pn_xflags |= pn2->pn_xflags;
7467 } else {
7468 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7469 tt == TOK_XMLPI);
7470 pn2 = XMLAtomNode(cx, ts, tc);
7472 if (!pn2)
7473 return JS_FALSE;
7474 pn->pn_pos.end = pn2->pn_pos.end;
7475 pn->append(pn2);
7478 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
7479 ts->flags |= TSF_XMLTAGMODE;
7480 return JS_TRUE;
7484 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7486 static JSParseNode *
7487 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7488 JSBool allowList)
7490 JSParseNode *pn, *pn2, *list;
7491 JSTokenType tt;
7492 JSAtom *startAtom, *endAtom;
7494 JS_CHECK_RECURSION(cx, return NULL);
7496 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
7497 pn = NewParseNode(PN_LIST, tc);
7498 if (!pn)
7499 return NULL;
7501 ts->flags |= TSF_XMLTAGMODE;
7502 tt = js_GetToken(cx, ts);
7503 if (tt == TOK_ERROR)
7504 return NULL;
7506 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7508 * XMLElement. Append the tag and its contents, if any, to pn.
7510 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
7511 if (!pn2)
7512 return NULL;
7513 js_MatchToken(cx, ts, TOK_XMLSPACE);
7515 tt = js_GetToken(cx, ts);
7516 if (tt == TOK_XMLPTAGC) {
7517 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7518 if (pn2->pn_type == TOK_XMLSTAGO) {
7519 pn->makeEmpty();
7520 RecycleTree(pn, tc);
7521 pn = pn2;
7522 } else {
7523 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7524 pn2->pn_type == TOK_LC);
7525 pn->initList(pn2);
7526 if (!XML_FOLDABLE(pn2))
7527 pn->pn_xflags |= PNX_CANTFOLD;
7529 pn->pn_type = TOK_XMLPTAGC;
7530 pn->pn_xflags |= PNX_XMLROOT;
7531 } else {
7532 /* We had better have a tag-close (>) at this point. */
7533 if (tt != TOK_XMLTAGC) {
7534 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7535 JSMSG_BAD_XML_TAG_SYNTAX);
7536 return NULL;
7538 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7540 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7541 if (pn2->pn_type != TOK_XMLSTAGO) {
7542 pn->initList(pn2);
7543 if (!XML_FOLDABLE(pn2))
7544 pn->pn_xflags |= PNX_CANTFOLD;
7545 pn2 = pn;
7546 pn = NewParseNode(PN_LIST, tc);
7547 if (!pn)
7548 return NULL;
7551 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7552 pn->pn_type = TOK_XMLELEM;
7553 pn->pn_pos.begin = pn2->pn_pos.begin;
7554 pn->initList(pn2);
7555 if (!XML_FOLDABLE(pn2))
7556 pn->pn_xflags |= PNX_CANTFOLD;
7557 pn->pn_xflags |= PNX_XMLROOT;
7559 /* Get element contents and delimiting end-tag-open sequence. */
7560 if (!XMLElementContent(cx, ts, pn, tc))
7561 return NULL;
7563 tt = js_GetToken(cx, ts);
7564 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7565 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7566 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7567 JSMSG_BAD_XML_TAG_SYNTAX);
7568 return NULL;
7571 /* Parse end tag; check mismatch at compile-time if we can. */
7572 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
7573 if (!pn2)
7574 return NULL;
7575 if (pn2->pn_type == TOK_XMLETAGO) {
7576 /* Oops, end tag has attributes! */
7577 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7578 JSMSG_BAD_XML_TAG_SYNTAX);
7579 return NULL;
7581 if (endAtom && startAtom && endAtom != startAtom) {
7582 JSString *str = ATOM_TO_STRING(startAtom);
7584 /* End vs. start tag name mismatch: point to the tag name. */
7585 js_ReportCompileErrorNumber(cx, ts, pn2,
7586 JSREPORT_UC | JSREPORT_ERROR,
7587 JSMSG_XML_TAG_NAME_MISMATCH,
7588 str->chars());
7589 return NULL;
7592 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7593 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7594 list = NewParseNode(PN_LIST, tc);
7595 if (!list)
7596 return NULL;
7597 list->pn_type = TOK_XMLETAGO;
7598 list->initList(pn2);
7599 pn->append(list);
7600 if (!XML_FOLDABLE(pn2)) {
7601 list->pn_xflags |= PNX_CANTFOLD;
7602 pn->pn_xflags |= PNX_CANTFOLD;
7605 js_MatchToken(cx, ts, TOK_XMLSPACE);
7606 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7609 /* Set pn_op now that pn has been updated to its final value. */
7610 pn->pn_op = JSOP_TOXML;
7611 } else if (allowList && tt == TOK_XMLTAGC) {
7612 /* XMLList Initialiser. */
7613 pn->pn_type = TOK_XMLLIST;
7614 pn->pn_op = JSOP_TOXMLLIST;
7615 pn->makeEmpty();
7616 pn->pn_xflags |= PNX_XMLROOT;
7617 if (!XMLElementContent(cx, ts, pn, tc))
7618 return NULL;
7620 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7621 } else {
7622 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7623 JSMSG_BAD_XML_NAME_SYNTAX);
7624 return NULL;
7627 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7628 ts->flags &= ~TSF_XMLTAGMODE;
7629 return pn;
7632 static JSParseNode *
7633 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7634 JSBool allowList)
7636 uint32 oldopts;
7637 JSParseNode *pn;
7640 * Force XML support to be enabled so that comments and CDATA literals
7641 * are recognized, instead of <! followed by -- starting an HTML comment
7642 * to end of line (used in script tags to hide content from old browsers
7643 * that don't recognize <script>).
7645 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
7646 pn = XMLElementOrList(cx, ts, tc, allowList);
7647 JS_SetOptions(cx, oldopts);
7648 return pn;
7651 JSParseNode *
7652 JSCompiler::parseXMLText(JSObject *chain, bool allowList)
7655 * Push a compiler frame if we have no frames, or if the top frame is a
7656 * lightweight function activation, or if its scope chain doesn't match
7657 * the one passed to us.
7659 JSTreeContext tc(this);
7660 tc.scopeChain = chain;
7662 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7663 TS(this)->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
7664 JSTokenType tt = js_GetToken(context, TS(this));
7665 TS(this)->flags &= ~TSF_OPERAND;
7667 JSParseNode *pn;
7668 if (tt != TOK_XMLSTAGO) {
7669 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
7670 JSMSG_BAD_XML_MARKUP);
7671 pn = NULL;
7672 } else {
7673 pn = XMLElementOrListRoot(context, TS(this), &tc, allowList);
7676 TS(this)->flags &= ~TSF_XMLONLYMODE;
7677 return pn;
7680 #endif /* JS_HAS_XMLSUPPORT */
7682 #if JS_HAS_BLOCK_SCOPE
7684 * Check whether blockid is an active scoping statement in tc. This code is
7685 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7686 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7687 * and let blocks and expressions (not let declarations).
7689 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7690 * due to hoisting, let in a for loop head, let block, or let expression acts
7691 * like Scheme's let: initializers are evaluated without the new let bindings
7692 * being in scope.
7694 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7695 * bindings push on the front of the tc->decls JSAtomList (either the singular
7696 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7697 * scope bindings of the same name.
7699 * This simplifies binding lookup code at the price of a linear search here,
7700 * but only if code uses let (var predominates), and even then this function's
7701 * loop iterates more than once only in crazy cases.
7703 static inline bool
7704 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7706 if (blockid > tc->blockid())
7707 return false;
7708 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7709 if (stmt->blockid == blockid)
7710 return true;
7712 return false;
7714 #endif
7716 static JSParseNode *
7717 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7718 JSTokenType tt, JSBool afterDot)
7720 JSParseNode *pn, *pn2, *pn3;
7721 JSOp op;
7723 JS_CHECK_RECURSION(cx, return NULL);
7725 #if JS_HAS_GETTER_SETTER
7726 if (tt == TOK_NAME) {
7727 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
7728 if (tt == TOK_ERROR)
7729 return NULL;
7731 #endif
7733 switch (tt) {
7734 case TOK_FUNCTION:
7735 #if JS_HAS_XML_SUPPORT
7736 ts->flags |= TSF_KEYWORD_IS_NAME;
7737 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7738 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7739 pn2 = NewParseNode(PN_NULLARY, tc);
7740 if (!pn2)
7741 return NULL;
7742 pn2->pn_type = TOK_FUNCTION;
7743 pn = QualifiedSuffix(cx, ts, pn2, tc);
7744 if (!pn)
7745 return NULL;
7746 break;
7748 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7749 #endif
7750 pn = FunctionExpr(cx, ts, tc);
7751 if (!pn)
7752 return NULL;
7753 break;
7755 case TOK_LB:
7757 JSBool matched;
7758 jsuint index;
7760 pn = NewParseNode(PN_LIST, tc);
7761 if (!pn)
7762 return NULL;
7763 pn->pn_type = TOK_RB;
7764 pn->pn_op = JSOP_NEWINIT;
7765 pn->makeEmpty();
7767 #if JS_HAS_GENERATORS
7768 pn->pn_blockid = tc->blockidGen;
7769 #endif
7771 ts->flags |= TSF_OPERAND;
7772 matched = js_MatchToken(cx, ts, TOK_RB);
7773 ts->flags &= ~TSF_OPERAND;
7774 if (!matched) {
7775 for (index = 0; ; index++) {
7776 if (index == ARRAY_INIT_LIMIT) {
7777 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7778 JSMSG_ARRAY_INIT_TOO_BIG);
7779 return NULL;
7782 ts->flags |= TSF_OPERAND;
7783 tt = js_PeekToken(cx, ts);
7784 ts->flags &= ~TSF_OPERAND;
7785 if (tt == TOK_RB) {
7786 pn->pn_xflags |= PNX_ENDCOMMA;
7787 break;
7790 if (tt == TOK_COMMA) {
7791 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7792 js_MatchToken(cx, ts, TOK_COMMA);
7793 pn2 = NewParseNode(PN_NULLARY, tc);
7794 pn->pn_xflags |= PNX_HOLEY;
7795 } else {
7796 pn2 = AssignExpr(cx, ts, tc);
7798 if (!pn2)
7799 return NULL;
7800 pn->append(pn2);
7802 if (tt != TOK_COMMA) {
7803 /* If we didn't already match TOK_COMMA in above case. */
7804 if (!js_MatchToken(cx, ts, TOK_COMMA))
7805 break;
7809 #if JS_HAS_GENERATORS
7811 * At this point, (index == 0 && pn->pn_count != 0) implies one
7812 * element initialiser was parsed.
7814 * An array comprehension of the form:
7816 * [i * j for (i in o) for (j in p) if (i != j)]
7818 * translates to roughly the following let expression:
7820 * let (array = new Array, i, j) {
7821 * for (i in o) let {
7822 * for (j in p)
7823 * if (i != j)
7824 * array.push(i * j)
7826 * array
7829 * where array is a nameless block-local variable. The "roughly"
7830 * means that an implementation may optimize away the array.push.
7831 * An array comprehension opens exactly one block scope, no matter
7832 * how many for heads it contains.
7834 * Each let () {...} or for (let ...) ... compiles to:
7836 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7838 * where <o> is a literal object representing the block scope,
7839 * with <n> properties, naming each var declared in the block.
7841 * Each var declaration in a let-block binds a name in <o> at
7842 * compile time, and allocates a slot on the operand stack at
7843 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7844 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7845 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7846 * local slot's stack index from fp->spbase.
7848 * The array comprehension iteration step, array.push(i * j) in
7849 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7850 * where <array> is the index of array's stack slot.
7852 if (index == 0 &&
7853 pn->pn_count != 0 &&
7854 js_MatchToken(cx, ts, TOK_FOR)) {
7855 JSParseNode *pnexp, *pntop;
7857 /* Relabel pn as an array comprehension node. */
7858 pn->pn_type = TOK_ARRAYCOMP;
7861 * Remove the comprehension expression from pn's linked list
7862 * and save it via pnexp. We'll re-install it underneath the
7863 * ARRAYPUSH node after we parse the rest of the comprehension.
7865 pnexp = pn->last();
7866 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7867 pn->pn_tail = (--pn->pn_count == 1)
7868 ? &pn->pn_head->pn_next
7869 : &pn->pn_head;
7870 *pn->pn_tail = NULL;
7872 pntop = ComprehensionTail(pnexp, pn->pn_blockid, tc,
7873 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7874 if (!pntop)
7875 return NULL;
7876 pn->append(pntop);
7878 #endif /* JS_HAS_GENERATORS */
7880 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7882 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7883 return pn;
7886 case TOK_LC:
7888 JSBool afterComma;
7889 JSParseNode *pnval;
7891 pn = NewParseNode(PN_LIST, tc);
7892 if (!pn)
7893 return NULL;
7894 pn->pn_type = TOK_RC;
7895 pn->pn_op = JSOP_NEWINIT;
7896 pn->makeEmpty();
7898 afterComma = JS_FALSE;
7899 for (;;) {
7900 ts->flags |= TSF_KEYWORD_IS_NAME;
7901 tt = js_GetToken(cx, ts);
7902 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7903 switch (tt) {
7904 case TOK_NUMBER:
7905 pn3 = NewParseNode(PN_NULLARY, tc);
7906 if (pn3)
7907 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
7908 break;
7909 case TOK_NAME:
7910 #if JS_HAS_GETTER_SETTER
7912 JSAtom *atom;
7914 atom = CURRENT_TOKEN(ts).t_atom;
7915 if (atom == cx->runtime->atomState.getAtom)
7916 op = JSOP_GETTER;
7917 else if (atom == cx->runtime->atomState.setAtom)
7918 op = JSOP_SETTER;
7919 else
7920 goto property_name;
7922 ts->flags |= TSF_KEYWORD_IS_NAME;
7923 tt = js_GetToken(cx, ts);
7924 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7925 if (tt != TOK_NAME) {
7926 js_UngetToken(ts);
7927 goto property_name;
7929 pn3 = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
7930 if (!pn3)
7931 return NULL;
7933 /* We have to fake a 'function' token here. */
7934 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
7935 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
7936 pn2 = FunctionExpr(cx, ts, tc);
7937 pn2 = NewBinary(TOK_COLON, op, pn3, pn2, tc);
7938 goto skip;
7940 property_name:
7941 #endif
7942 case TOK_STRING:
7943 pn3 = NewParseNode(PN_NULLARY, tc);
7944 if (pn3)
7945 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
7946 break;
7947 case TOK_RC:
7948 if (afterComma &&
7949 !js_ReportCompileErrorNumber(cx, ts, NULL,
7950 JSREPORT_WARNING |
7951 JSREPORT_STRICT,
7952 JSMSG_TRAILING_COMMA)) {
7953 return NULL;
7955 goto end_obj_init;
7956 default:
7957 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7958 JSMSG_BAD_PROP_ID);
7959 return NULL;
7962 tt = js_GetToken(cx, ts);
7963 #if JS_HAS_GETTER_SETTER
7964 if (tt == TOK_NAME) {
7965 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
7966 if (tt == TOK_ERROR)
7967 return NULL;
7969 #endif
7971 if (tt != TOK_COLON) {
7972 #if JS_HAS_DESTRUCTURING_SHORTHAND
7973 if (tt != TOK_COMMA && tt != TOK_RC) {
7974 #endif
7975 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7976 JSMSG_COLON_AFTER_ID);
7977 return NULL;
7978 #if JS_HAS_DESTRUCTURING_SHORTHAND
7982 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7983 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7985 js_UngetToken(ts);
7986 pn->pn_xflags |= PNX_DESTRUCT;
7987 pnval = pn3;
7988 if (pnval->pn_type == TOK_NAME) {
7989 pnval->pn_arity = PN_NAME;
7990 InitNameNodeCommon(pnval, tc);
7992 op = JSOP_NOP;
7993 #endif
7994 } else {
7995 op = CURRENT_TOKEN(ts).t_op;
7996 pnval = AssignExpr(cx, ts, tc);
7999 pn2 = NewBinary(TOK_COLON, op, pn3, pnval, tc);
8000 #if JS_HAS_GETTER_SETTER
8001 skip:
8002 #endif
8003 if (!pn2)
8004 return NULL;
8005 pn->append(pn2);
8007 tt = js_GetToken(cx, ts);
8008 if (tt == TOK_RC)
8009 goto end_obj_init;
8010 if (tt != TOK_COMMA) {
8011 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8012 JSMSG_CURLY_AFTER_LIST);
8013 return NULL;
8015 afterComma = JS_TRUE;
8018 end_obj_init:
8019 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8020 return pn;
8023 #if JS_HAS_BLOCK_SCOPE
8024 case TOK_LET:
8025 pn = LetBlock(cx, ts, tc, JS_FALSE);
8026 if (!pn)
8027 return NULL;
8028 break;
8029 #endif
8031 #if JS_HAS_SHARP_VARS
8032 case TOK_DEFSHARP:
8033 pn = NewParseNode(PN_UNARY, tc);
8034 if (!pn)
8035 return NULL;
8036 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8037 ts->flags |= TSF_OPERAND;
8038 tt = js_GetToken(cx, ts);
8039 ts->flags &= ~TSF_OPERAND;
8040 if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
8041 #if JS_HAS_XML_SUPPORT
8042 tt == TOK_STAR || tt == TOK_AT ||
8043 tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
8044 #endif
8045 tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
8046 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8047 JSMSG_BAD_SHARP_VAR_DEF);
8048 return NULL;
8050 pn->pn_kid = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
8051 if (!pn->pn_kid)
8052 return NULL;
8053 tc->flags |= TCF_HAS_SHARPS;
8054 break;
8056 case TOK_USESHARP:
8057 /* Check for forward/dangling references at runtime, to allow eval. */
8058 pn = NewParseNode(PN_NULLARY, tc);
8059 if (!pn)
8060 return NULL;
8061 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8062 tc->flags |= TCF_HAS_SHARPS;
8063 break;
8064 #endif /* JS_HAS_SHARP_VARS */
8066 case TOK_LP:
8068 JSBool genexp;
8070 pn = NewParseNode(PN_UNARY, tc);
8071 if (!pn)
8072 return NULL;
8073 pn2 = ParenExpr(cx, ts, tc, pn, &genexp);
8074 if (!pn2)
8075 return NULL;
8076 if (genexp)
8077 return pn2;
8078 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8080 /* Check if parentheses were unnecessary. */
8081 if (pn2->pn_type == TOK_RP ||
8082 (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
8083 !afterDot)) {
8084 RecycleTree(pn, tc);
8085 pn = pn2;
8086 } else {
8087 pn->pn_type = TOK_RP;
8088 pn->pn_kid = pn2;
8090 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8091 break;
8094 #if JS_HAS_XML_SUPPORT
8095 case TOK_STAR:
8096 pn = QualifiedIdentifier(cx, ts, tc);
8097 if (!pn)
8098 return NULL;
8099 break;
8101 case TOK_AT:
8102 pn = AttributeIdentifier(cx, ts, tc);
8103 if (!pn)
8104 return NULL;
8105 break;
8107 case TOK_XMLSTAGO:
8108 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
8109 if (!pn)
8110 return NULL;
8111 break;
8112 #endif /* JS_HAS_XML_SUPPORT */
8114 case TOK_STRING:
8115 #if JS_HAS_SHARP_VARS
8116 /* FALL THROUGH */
8117 #endif
8119 #if JS_HAS_XML_SUPPORT
8120 case TOK_XMLCDATA:
8121 case TOK_XMLCOMMENT:
8122 case TOK_XMLPI:
8123 #endif
8124 pn = NewParseNode(PN_NULLARY, tc);
8125 if (!pn)
8126 return NULL;
8127 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
8128 #if JS_HAS_XML_SUPPORT
8129 if (tt == TOK_XMLPI)
8130 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
8131 else
8132 #endif
8133 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8134 break;
8136 case TOK_NAME:
8137 pn = NewNameNode(cx, ts, CURRENT_TOKEN(ts).t_atom, tc);
8138 if (!pn)
8139 return NULL;
8140 JS_ASSERT(CURRENT_TOKEN(ts).t_op == JSOP_NAME);
8141 pn->pn_op = JSOP_NAME;
8143 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8144 pn->pn_atom == cx->runtime->atomState.argumentsAtom) {
8146 * Flag arguments usage so we can avoid unsafe optimizations such
8147 * as formal parameter assignment analysis (because of the hated
8148 * feature whereby arguments alias formals). We do this even for
8149 * a reference of the form foo.arguments, which ancient code may
8150 * still use instead of arguments (more hate).
8152 NoteArgumentsUse(tc);
8155 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8156 * to do this work (new rule for the emitter to count on).
8158 if (!afterDot && !(ts->flags & TSF_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8159 pn->pn_op = JSOP_ARGUMENTS;
8160 pn->pn_dflags |= PND_BOUND;
8162 } else if ((!afterDot
8163 #if JS_HAS_XML_SUPPORT
8164 || js_PeekToken(cx, ts) == TOK_DBLCOLON
8165 #endif
8166 ) && !(ts->flags & TSF_DESTRUCTURING)) {
8167 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8168 if (!stmt || stmt->type != STMT_WITH) {
8169 JSDefinition *dn;
8171 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8172 if (ale) {
8173 dn = ALE_DEFN(ale);
8174 #if JS_HAS_BLOCK_SCOPE
8176 * Skip out-of-scope let bindings along an ALE list or hash
8177 * chain. These can happen due to |let (x = x) x| block and
8178 * expression bindings, where the x on the right of = comes
8179 * from an outer scope. See bug 496532.
8181 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8182 do {
8183 ale = ALE_NEXT(ale);
8184 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8185 if (!ale)
8186 break;
8187 dn = ALE_DEFN(ale);
8189 #endif
8192 if (ale) {
8193 dn = ALE_DEFN(ale);
8194 } else {
8195 ale = tc->lexdeps.lookup(pn->pn_atom);
8196 if (ale) {
8197 dn = ALE_DEFN(ale);
8198 } else {
8200 * No definition before this use in any lexical scope.
8201 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8202 * new node for the forward-referenced definition. This
8203 * placeholder definition node will be adopted when we
8204 * parse the real defining declaration form, or left as
8205 * a free variable definition if we never see the real
8206 * definition.
8208 ale = MakePlaceholder(pn, tc);
8209 if (!ale)
8210 return NULL;
8211 dn = ALE_DEFN(ale);
8214 * In case this is a forward reference to a function,
8215 * we pessimistically set PND_FUNARG if the next token
8216 * is not a left parenthesis.
8218 * If the definition eventually parsed into dn is not a
8219 * function, this flag won't hurt, and if we do parse a
8220 * function with pn's name, then the PND_FUNARG flag is
8221 * necessary for safe cx->display-based optimization of
8222 * the closure's static link.
8224 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8225 JS_ASSERT(dn->pn_op == JSOP_NOP);
8226 if (js_PeekToken(cx, ts) != TOK_LP)
8227 dn->pn_dflags |= PND_FUNARG;
8231 JS_ASSERT(dn->pn_defn);
8232 LinkUseToDef(pn, dn, tc);
8234 /* Here we handle the backward function reference case. */
8235 if (js_PeekToken(cx, ts) != TOK_LP)
8236 dn->pn_dflags |= PND_FUNARG;
8238 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8242 #if JS_HAS_XML_SUPPORT
8243 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
8244 if (afterDot) {
8245 JSString *str;
8248 * Here PrimaryExpr is called after . or .. followed by a name
8249 * followed by ::. This is the only case where a keyword after
8250 * . or .. is not treated as a property name.
8252 str = ATOM_TO_STRING(pn->pn_atom);
8253 tt = js_CheckKeyword(str->chars(), str->length());
8254 if (tt == TOK_FUNCTION) {
8255 pn->pn_arity = PN_NULLARY;
8256 pn->pn_type = TOK_FUNCTION;
8257 } else if (tt != TOK_EOF) {
8258 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8259 JSMSG_KEYWORD_NOT_NS);
8260 return NULL;
8263 pn = QualifiedSuffix(cx, ts, pn, tc);
8264 if (!pn)
8265 return NULL;
8267 #endif
8268 break;
8270 case TOK_REGEXP:
8272 JSObject *obj;
8274 pn = NewParseNode(PN_NULLARY, tc);
8275 if (!pn)
8276 return NULL;
8278 /* Token stream ensures that tokenbuf is NUL-terminated. */
8279 JS_ASSERT(*ts->tokenbuf.ptr == (jschar) 0);
8280 obj = js_NewRegExpObject(cx, ts,
8281 ts->tokenbuf.base,
8282 ts->tokenbuf.ptr - ts->tokenbuf.base,
8283 CURRENT_TOKEN(ts).t_reflags);
8284 if (!obj)
8285 return NULL;
8286 if (!(tc->flags & TCF_COMPILE_N_GO)) {
8287 STOBJ_CLEAR_PARENT(obj);
8288 STOBJ_CLEAR_PROTO(obj);
8291 pn->pn_objbox = tc->compiler->newObjectBox(obj);
8292 if (!pn->pn_objbox)
8293 return NULL;
8295 pn->pn_op = JSOP_REGEXP;
8296 break;
8299 case TOK_NUMBER:
8300 pn = NewParseNode(PN_NULLARY, tc);
8301 if (!pn)
8302 return NULL;
8303 pn->pn_op = JSOP_DOUBLE;
8304 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
8305 break;
8307 case TOK_PRIMARY:
8308 pn = NewParseNode(PN_NULLARY, tc);
8309 if (!pn)
8310 return NULL;
8311 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8312 break;
8314 case TOK_ERROR:
8315 /* The scanner or one of its subroutines reported the error. */
8316 return NULL;
8318 default:
8319 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8320 JSMSG_SYNTAX_ERROR);
8321 return NULL;
8323 return pn;
8326 static JSParseNode *
8327 ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
8328 JSParseNode *pn1, JSBool *genexp)
8330 JSTokenPtr begin;
8331 JSParseNode *pn;
8333 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
8334 begin = CURRENT_TOKEN(ts).pos.begin;
8336 if (genexp)
8337 *genexp = JS_FALSE;
8338 pn = BracketedExpr(cx, ts, tc);
8339 if (!pn)
8340 return NULL;
8342 #if JS_HAS_GENERATOR_EXPRS
8343 if (js_MatchToken(cx, ts, TOK_FOR)) {
8344 if (pn->pn_type == TOK_YIELD) {
8345 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
8346 JSMSG_BAD_GENERATOR_SYNTAX,
8347 js_yield_str);
8348 return NULL;
8350 if (pn->pn_type == TOK_COMMA) {
8351 js_ReportCompileErrorNumber(cx, ts, pn->last(), JSREPORT_ERROR,
8352 JSMSG_BAD_GENERATOR_SYNTAX,
8353 js_generator_str);
8354 return NULL;
8356 if (!pn1) {
8357 pn1 = NewParseNode(PN_UNARY, tc);
8358 if (!pn1)
8359 return NULL;
8361 pn = GeneratorExpr(pn1, pn, tc);
8362 if (!pn)
8363 return NULL;
8364 pn->pn_pos.begin = begin;
8365 if (genexp) {
8366 if (js_GetToken(cx, ts) != TOK_RP) {
8367 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8368 JSMSG_BAD_GENERATOR_SYNTAX,
8369 js_generator_str);
8370 return NULL;
8372 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8373 *genexp = JS_TRUE;
8376 #endif /* JS_HAS_GENERATOR_EXPRS */
8378 return pn;
8382 * Fold from one constant type to another.
8383 * XXX handles only strings and numbers for now
8385 static JSBool
8386 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
8388 if (PN_TYPE(pn) != type) {
8389 switch (type) {
8390 case TOK_NUMBER:
8391 if (pn->pn_type == TOK_STRING) {
8392 jsdouble d;
8393 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8394 return JS_FALSE;
8395 pn->pn_dval = d;
8396 pn->pn_type = TOK_NUMBER;
8397 pn->pn_op = JSOP_DOUBLE;
8399 break;
8401 case TOK_STRING:
8402 if (pn->pn_type == TOK_NUMBER) {
8403 JSString *str = js_NumberToString(cx, pn->pn_dval);
8404 if (!str)
8405 return JS_FALSE;
8406 pn->pn_atom = js_AtomizeString(cx, str, 0);
8407 if (!pn->pn_atom)
8408 return JS_FALSE;
8409 pn->pn_type = TOK_STRING;
8410 pn->pn_op = JSOP_STRING;
8412 break;
8414 default:;
8417 return JS_TRUE;
8421 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8422 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8423 * a successful call to this function.
8425 static JSBool
8426 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8427 JSParseNode *pn, JSTreeContext *tc)
8429 jsdouble d, d2;
8430 int32 i, j;
8432 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8433 d = pn1->pn_dval;
8434 d2 = pn2->pn_dval;
8435 switch (op) {
8436 case JSOP_LSH:
8437 case JSOP_RSH:
8438 i = js_DoubleToECMAInt32(d);
8439 j = js_DoubleToECMAInt32(d2);
8440 j &= 31;
8441 d = (op == JSOP_LSH) ? i << j : i >> j;
8442 break;
8444 case JSOP_URSH:
8445 j = js_DoubleToECMAInt32(d2);
8446 j &= 31;
8447 d = js_DoubleToECMAUint32(d) >> j;
8448 break;
8450 case JSOP_ADD:
8451 d += d2;
8452 break;
8454 case JSOP_SUB:
8455 d -= d2;
8456 break;
8458 case JSOP_MUL:
8459 d *= d2;
8460 break;
8462 case JSOP_DIV:
8463 if (d2 == 0) {
8464 #if defined(XP_WIN)
8465 /* XXX MSVC miscompiles such that (NaN == 0) */
8466 if (JSDOUBLE_IS_NaN(d2))
8467 d = *cx->runtime->jsNaN;
8468 else
8469 #endif
8470 if (d == 0 || JSDOUBLE_IS_NaN(d))
8471 d = *cx->runtime->jsNaN;
8472 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
8473 d = *cx->runtime->jsNegativeInfinity;
8474 else
8475 d = *cx->runtime->jsPositiveInfinity;
8476 } else {
8477 d /= d2;
8479 break;
8481 case JSOP_MOD:
8482 if (d2 == 0) {
8483 d = *cx->runtime->jsNaN;
8484 } else {
8485 d = js_fmod(d, d2);
8487 break;
8489 default:;
8492 /* Take care to allow pn1 or pn2 to alias pn. */
8493 if (pn1 != pn)
8494 RecycleTree(pn1, tc);
8495 if (pn2 != pn)
8496 RecycleTree(pn2, tc);
8497 pn->pn_type = TOK_NUMBER;
8498 pn->pn_op = JSOP_DOUBLE;
8499 pn->pn_arity = PN_NULLARY;
8500 pn->pn_dval = d;
8501 return JS_TRUE;
8504 #if JS_HAS_XML_SUPPORT
8506 static JSBool
8507 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8509 JSTokenType tt;
8510 JSParseNode **pnp, *pn1, *pn2;
8511 JSString *accum, *str;
8512 uint32 i, j;
8513 JSTempValueRooter tvr;
8515 JS_ASSERT(pn->pn_arity == PN_LIST);
8516 tt = PN_TYPE(pn);
8517 pnp = &pn->pn_head;
8518 pn1 = *pnp;
8519 accum = NULL;
8520 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8521 if (tt == TOK_XMLETAGO)
8522 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8523 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8524 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8528 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8529 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8530 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8531 * Therefore, we have to add additonal protection from GC nesting under
8532 * js_ConcatStrings.
8534 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8535 /* The parser already rejected end-tags with attributes. */
8536 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8537 switch (pn2->pn_type) {
8538 case TOK_XMLATTR:
8539 if (!accum)
8540 goto cantfold;
8541 /* FALL THROUGH */
8542 case TOK_XMLNAME:
8543 case TOK_XMLSPACE:
8544 case TOK_XMLTEXT:
8545 case TOK_STRING:
8546 if (pn2->pn_arity == PN_LIST)
8547 goto cantfold;
8548 str = ATOM_TO_STRING(pn2->pn_atom);
8549 break;
8551 case TOK_XMLCDATA:
8552 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8553 if (!str)
8554 return JS_FALSE;
8555 break;
8557 case TOK_XMLCOMMENT:
8558 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8559 if (!str)
8560 return JS_FALSE;
8561 break;
8563 case TOK_XMLPI:
8564 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8565 ATOM_TO_STRING(pn2->pn_atom2));
8566 if (!str)
8567 return JS_FALSE;
8568 break;
8570 cantfold:
8571 default:
8572 JS_ASSERT(*pnp == pn1);
8573 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8574 (i & 1) ^ (j & 1)) {
8575 #ifdef DEBUG_brendanXXX
8576 printf("1: %d, %d => ", i, j);
8577 if (accum)
8578 js_FileEscapedString(stdout, accum, 0);
8579 else
8580 fputs("NULL", stdout);
8581 fputc('\n', stdout);
8582 #endif
8583 } else if (accum && pn1 != pn2) {
8584 while (pn1->pn_next != pn2) {
8585 pn1 = RecycleTree(pn1, tc);
8586 --pn->pn_count;
8588 pn1->pn_type = TOK_XMLTEXT;
8589 pn1->pn_op = JSOP_STRING;
8590 pn1->pn_arity = PN_NULLARY;
8591 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8592 if (!pn1->pn_atom)
8593 return JS_FALSE;
8594 JS_ASSERT(pnp != &pn1->pn_next);
8595 *pnp = pn1;
8597 pnp = &pn2->pn_next;
8598 pn1 = *pnp;
8599 accum = NULL;
8600 continue;
8603 if (accum) {
8604 JS_PUSH_TEMP_ROOT_STRING(cx, accum, &tvr);
8605 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8606 ? js_AddAttributePart(cx, i & 1, accum, str)
8607 : js_ConcatStrings(cx, accum, str);
8608 JS_POP_TEMP_ROOT(cx, &tvr);
8609 if (!str)
8610 return JS_FALSE;
8611 #ifdef DEBUG_brendanXXX
8612 printf("2: %d, %d => ", i, j);
8613 js_FileEscapedString(stdout, str, 0);
8614 printf(" (%u)\n", str->length());
8615 #endif
8616 ++j;
8618 accum = str;
8621 if (accum) {
8622 str = NULL;
8623 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8624 if (tt == TOK_XMLPTAGC)
8625 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8626 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8627 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8629 if (str) {
8630 accum = js_ConcatStrings(cx, accum, str);
8631 if (!accum)
8632 return JS_FALSE;
8635 JS_ASSERT(*pnp == pn1);
8636 while (pn1->pn_next) {
8637 pn1 = RecycleTree(pn1, tc);
8638 --pn->pn_count;
8640 pn1->pn_type = TOK_XMLTEXT;
8641 pn1->pn_op = JSOP_STRING;
8642 pn1->pn_arity = PN_NULLARY;
8643 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8644 if (!pn1->pn_atom)
8645 return JS_FALSE;
8646 JS_ASSERT(pnp != &pn1->pn_next);
8647 *pnp = pn1;
8650 if (pn1 && pn->pn_count == 1) {
8652 * Only one node under pn, and it has been folded: move pn1 onto pn
8653 * unless pn is an XML root (in which case we need it to tell the code
8654 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8655 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8656 * extra "<" and "/>" bracketing at runtime.
8658 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8659 pn->become(pn1);
8660 } else if (tt == TOK_XMLPTAGC) {
8661 pn->pn_type = TOK_XMLELEM;
8662 pn->pn_op = JSOP_TOXML;
8665 return JS_TRUE;
8668 #endif /* JS_HAS_XML_SUPPORT */
8670 static JSBool
8671 StartsWith(JSParseNode *pn, JSTokenType tt)
8673 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
8675 recur:
8676 if (PN_TYPE(pn) == tt)
8677 return JS_TRUE;
8678 switch (pn->pn_arity) {
8679 case PN_FUNC:
8680 return tt == TOK_FUNCTION;
8681 case PN_LIST:
8682 if (pn->pn_head)
8683 TAIL_RECURSE(pn->pn_head);
8684 break;
8685 case PN_TERNARY:
8686 if (pn->pn_kid1)
8687 TAIL_RECURSE(pn->pn_kid1);
8688 break;
8689 case PN_BINARY:
8690 if (pn->pn_left)
8691 TAIL_RECURSE(pn->pn_left);
8692 break;
8693 case PN_UNARY:
8694 /* A parenthesized expression starts with a left parenthesis. */
8695 if (pn->pn_type == TOK_RP)
8696 return tt == TOK_LP;
8697 if (pn->pn_kid)
8698 TAIL_RECURSE(pn->pn_kid);
8699 break;
8700 case PN_NAME:
8701 if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
8702 TAIL_RECURSE(pn->expr());
8703 break;
8704 case PN_NAMESET:
8705 TAIL_RECURSE(pn->pn_tree);
8707 return JS_FALSE;
8709 #undef TAIL_RECURSE
8712 static int
8713 Boolish(JSParseNode *pn)
8715 switch (pn->pn_op) {
8716 case JSOP_DOUBLE:
8717 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8719 case JSOP_STRING:
8720 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8722 #if JS_HAS_GENERATOR_EXPRS
8723 case JSOP_CALL:
8726 * A generator expression as an if or loop condition has no effects, it
8727 * simply results in a truthy object reference. This condition folding
8728 * is needed for the decompiler. See bug 442342 and bug 443074.
8730 if (pn->pn_count != 1)
8731 break;
8732 JSParseNode *pn2 = pn->pn_head;
8733 if (pn2->pn_type != TOK_FUNCTION)
8734 break;
8735 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8736 break;
8737 /* FALL THROUGH */
8739 #endif
8741 case JSOP_DEFFUN:
8742 case JSOP_LAMBDA:
8743 case JSOP_THIS:
8744 case JSOP_TRUE:
8745 return 1;
8747 case JSOP_NULL:
8748 case JSOP_FALSE:
8749 return 0;
8751 default:;
8753 return -1;
8756 JSBool
8757 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8759 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8761 JS_CHECK_RECURSION(cx, return JS_FALSE);
8763 switch (pn->pn_arity) {
8764 case PN_FUNC:
8766 uint16 oldflags = tc->flags;
8767 JSFunctionBox *oldlist = tc->functionList;
8769 tc->flags = (uint16) pn->pn_funbox->tcflags;
8770 tc->functionList = pn->pn_funbox->kids;
8771 if (!js_FoldConstants(cx, pn->pn_body, tc))
8772 return JS_FALSE;
8773 pn->pn_funbox->kids = tc->functionList;
8774 tc->flags = oldflags;
8775 tc->functionList = oldlist;
8776 break;
8779 case PN_LIST:
8781 /* Propagate inCond through logical connectives. */
8782 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8784 /* Save the list head in pn1 for later use. */
8785 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
8786 if (!js_FoldConstants(cx, pn2, tc, cond))
8787 return JS_FALSE;
8789 break;
8792 case PN_TERNARY:
8793 /* Any kid may be null (e.g. for (;;)). */
8794 pn1 = pn->pn_kid1;
8795 pn2 = pn->pn_kid2;
8796 pn3 = pn->pn_kid3;
8797 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8798 return JS_FALSE;
8799 if (pn2) {
8800 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8801 return JS_FALSE;
8802 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8803 RecycleTree(pn2, tc);
8804 pn->pn_kid2 = NULL;
8807 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8808 return JS_FALSE;
8809 break;
8811 case PN_BINARY:
8812 pn1 = pn->pn_left;
8813 pn2 = pn->pn_right;
8815 /* Propagate inCond through logical connectives. */
8816 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8817 if (!js_FoldConstants(cx, pn1, tc, inCond))
8818 return JS_FALSE;
8819 if (!js_FoldConstants(cx, pn2, tc, inCond))
8820 return JS_FALSE;
8821 break;
8824 /* First kid may be null (for default case in switch). */
8825 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8826 return JS_FALSE;
8827 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8828 return JS_FALSE;
8829 break;
8831 case PN_UNARY:
8832 /* Our kid may be null (e.g. return; vs. return e;). */
8833 pn1 = pn->pn_kid;
8834 if (pn1 &&
8835 !js_FoldConstants(cx, pn1, tc,
8836 (inCond && pn->pn_type == TOK_RP) ||
8837 pn->pn_op == JSOP_NOT)) {
8838 return JS_FALSE;
8840 break;
8842 case PN_NAME:
8844 * Skip pn1 down along a chain of dotted member expressions to avoid
8845 * excessive recursion. Our only goal here is to fold constants (if
8846 * any) in the primary expression operand to the left of the first
8847 * dot in the chain.
8849 if (!pn->pn_used) {
8850 pn1 = pn->pn_expr;
8851 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8852 pn1 = pn1->pn_expr;
8853 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8854 return JS_FALSE;
8856 break;
8858 case PN_NAMESET:
8859 pn1 = pn->pn_tree;
8860 if (!js_FoldConstants(cx, pn1, tc))
8861 return JS_FALSE;
8862 break;
8864 case PN_NULLARY:
8865 break;
8868 switch (pn->pn_type) {
8869 case TOK_IF:
8870 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8871 break;
8872 /* FALL THROUGH */
8874 case TOK_HOOK:
8875 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8876 while (pn1->pn_type == TOK_RP)
8877 pn1 = pn1->pn_kid;
8878 switch (pn1->pn_type) {
8879 case TOK_NUMBER:
8880 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8881 pn2 = pn3;
8882 break;
8883 case TOK_STRING:
8884 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
8885 pn2 = pn3;
8886 break;
8887 case TOK_PRIMARY:
8888 if (pn1->pn_op == JSOP_TRUE)
8889 break;
8890 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8891 pn2 = pn3;
8892 break;
8894 /* FALL THROUGH */
8895 default:
8896 /* Early return to dodge common code that copies pn2 to pn. */
8897 return JS_TRUE;
8900 #if JS_HAS_GENERATOR_EXPRS
8901 /* Don't fold a trailing |if (0)| in a generator expression. */
8902 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8903 break;
8904 #endif
8906 if (pn2) {
8908 * pn2 is the then- or else-statement subtree to compile. Take
8909 * care not to expose an object initialiser, which would be parsed
8910 * as a block, to the Statement parser via eval(uneval(e)) where e
8911 * is '1 ? {p:2, q:3}[i] : r;' or the like.
8913 if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) {
8914 pn->pn_type = TOK_RP;
8915 pn->pn_arity = PN_UNARY;
8916 pn->pn_kid = pn2;
8917 if (pn3 && pn3 != pn2)
8918 RecycleTree(pn3, tc);
8919 break;
8921 if (!pn2->pn_defn)
8922 pn->become(pn2);
8924 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8926 * False condition and no else, or an empty then-statement was
8927 * moved up over pn. Either way, make pn an empty block (not an
8928 * empty statement, which does not decompile, even when labeled).
8929 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8930 * or an empty statement for a child.
8932 pn->pn_type = TOK_LC;
8933 pn->pn_arity = PN_LIST;
8934 pn->makeEmpty();
8936 RecycleTree(pn2, tc);
8937 if (pn3 && pn3 != pn2)
8938 RecycleTree(pn3, tc);
8939 break;
8941 case TOK_OR:
8942 case TOK_AND:
8943 if (inCond) {
8944 if (pn->pn_arity == PN_LIST) {
8945 JSParseNode **pnp = &pn->pn_head;
8946 JS_ASSERT(*pnp == pn1);
8947 do {
8948 int cond = Boolish(pn1);
8949 if (cond == (pn->pn_type == TOK_OR)) {
8950 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
8951 pn3 = pn2->pn_next;
8952 RecycleTree(pn2, tc);
8953 --pn->pn_count;
8955 pn1->pn_next = NULL;
8956 break;
8958 if (cond != -1) {
8959 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8960 if (pn->pn_count == 1)
8961 break;
8962 *pnp = pn1->pn_next;
8963 RecycleTree(pn1, tc);
8964 --pn->pn_count;
8965 } else {
8966 pnp = &pn1->pn_next;
8968 } while ((pn1 = *pnp) != NULL);
8970 // We may have to change arity from LIST to BINARY.
8971 pn1 = pn->pn_head;
8972 if (pn->pn_count == 2) {
8973 pn2 = pn1->pn_next;
8974 pn1->pn_next = NULL;
8975 JS_ASSERT(!pn2->pn_next);
8976 pn->pn_arity = PN_BINARY;
8977 pn->pn_left = pn1;
8978 pn->pn_right = pn2;
8979 } else if (pn->pn_count == 1) {
8980 pn->become(pn1);
8981 RecycleTree(pn1, tc);
8983 } else {
8984 int cond = Boolish(pn1);
8985 if (cond == (pn->pn_type == TOK_OR)) {
8986 RecycleTree(pn2, tc);
8987 pn->become(pn1);
8988 } else if (cond != -1) {
8989 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8990 RecycleTree(pn1, tc);
8991 pn->become(pn2);
8995 break;
8997 case TOK_ASSIGN:
8999 * Compound operators such as *= should be subject to folding, in case
9000 * the left-hand side is constant, and so that the decompiler produces
9001 * the same string that you get from decompiling a script or function
9002 * compiled from that same string. As with +, += is special.
9004 if (pn->pn_op == JSOP_NOP)
9005 break;
9006 if (pn->pn_op != JSOP_ADD)
9007 goto do_binary_op;
9008 /* FALL THROUGH */
9010 case TOK_PLUS:
9011 if (pn->pn_arity == PN_LIST) {
9012 size_t length, length2;
9013 jschar *chars;
9014 JSString *str, *str2;
9017 * Any string literal term with all others number or string means
9018 * this is a concatenation. If any term is not a string or number
9019 * literal, we can't fold.
9021 JS_ASSERT(pn->pn_count > 2);
9022 if (pn->pn_xflags & PNX_CANTFOLD)
9023 return JS_TRUE;
9024 if (pn->pn_xflags != PNX_STRCAT)
9025 goto do_binary_op;
9027 /* Ok, we're concatenating: convert non-string constant operands. */
9028 length = 0;
9029 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9030 if (!FoldType(cx, pn2, TOK_STRING))
9031 return JS_FALSE;
9032 /* XXX fold only if all operands convert to string */
9033 if (pn2->pn_type != TOK_STRING)
9034 return JS_TRUE;
9035 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9038 /* Allocate a new buffer and string descriptor for the result. */
9039 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9040 if (!chars)
9041 return JS_FALSE;
9042 str = js_NewString(cx, chars, length);
9043 if (!str) {
9044 cx->free(chars);
9045 return JS_FALSE;
9048 /* Fill the buffer, advancing chars and recycling kids as we go. */
9049 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9050 str2 = ATOM_TO_STRING(pn2->pn_atom);
9051 length2 = str2->flatLength();
9052 js_strncpy(chars, str2->flatChars(), length2);
9053 chars += length2;
9055 *chars = 0;
9057 /* Atomize the result string and mutate pn to refer to it. */
9058 pn->pn_atom = js_AtomizeString(cx, str, 0);
9059 if (!pn->pn_atom)
9060 return JS_FALSE;
9061 pn->pn_type = TOK_STRING;
9062 pn->pn_op = JSOP_STRING;
9063 pn->pn_arity = PN_NULLARY;
9064 break;
9067 /* Handle a binary string concatenation. */
9068 JS_ASSERT(pn->pn_arity == PN_BINARY);
9069 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9070 JSString *left, *right, *str;
9072 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9073 TOK_STRING)) {
9074 return JS_FALSE;
9076 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9077 return JS_TRUE;
9078 left = ATOM_TO_STRING(pn1->pn_atom);
9079 right = ATOM_TO_STRING(pn2->pn_atom);
9080 str = js_ConcatStrings(cx, left, right);
9081 if (!str)
9082 return JS_FALSE;
9083 pn->pn_atom = js_AtomizeString(cx, str, 0);
9084 if (!pn->pn_atom)
9085 return JS_FALSE;
9086 pn->pn_type = TOK_STRING;
9087 pn->pn_op = JSOP_STRING;
9088 pn->pn_arity = PN_NULLARY;
9089 RecycleTree(pn1, tc);
9090 RecycleTree(pn2, tc);
9091 break;
9094 /* Can't concatenate string literals, let's try numbers. */
9095 goto do_binary_op;
9097 case TOK_STAR:
9098 case TOK_SHOP:
9099 case TOK_MINUS:
9100 case TOK_DIVOP:
9101 do_binary_op:
9102 if (pn->pn_arity == PN_LIST) {
9103 JS_ASSERT(pn->pn_count > 2);
9104 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9105 if (!FoldType(cx, pn2, TOK_NUMBER))
9106 return JS_FALSE;
9108 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9109 /* XXX fold only if all operands convert to number */
9110 if (pn2->pn_type != TOK_NUMBER)
9111 break;
9113 if (!pn2) {
9114 JSOp op = PN_OP(pn);
9116 pn2 = pn1->pn_next;
9117 pn3 = pn2->pn_next;
9118 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9119 return JS_FALSE;
9120 while ((pn2 = pn3) != NULL) {
9121 pn3 = pn2->pn_next;
9122 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9123 return JS_FALSE;
9126 } else {
9127 JS_ASSERT(pn->pn_arity == PN_BINARY);
9128 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9129 !FoldType(cx, pn2, TOK_NUMBER)) {
9130 return JS_FALSE;
9132 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9133 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9134 return JS_FALSE;
9137 break;
9139 case TOK_UNARYOP:
9140 while (pn1->pn_type == TOK_RP)
9141 pn1 = pn1->pn_kid;
9142 if (pn1->pn_type == TOK_NUMBER) {
9143 jsdouble d;
9145 /* Operate on one numeric constant. */
9146 d = pn1->pn_dval;
9147 switch (pn->pn_op) {
9148 case JSOP_BITNOT:
9149 d = ~js_DoubleToECMAInt32(d);
9150 break;
9152 case JSOP_NEG:
9153 #ifdef HPUX
9155 * Negation of a zero doesn't produce a negative
9156 * zero on HPUX. Perform the operation by bit
9157 * twiddling.
9159 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
9160 #else
9161 d = -d;
9162 #endif
9163 break;
9165 case JSOP_POS:
9166 break;
9168 case JSOP_NOT:
9169 pn->pn_type = TOK_PRIMARY;
9170 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9171 pn->pn_arity = PN_NULLARY;
9172 /* FALL THROUGH */
9174 default:
9175 /* Return early to dodge the common TOK_NUMBER code. */
9176 return JS_TRUE;
9178 pn->pn_type = TOK_NUMBER;
9179 pn->pn_op = JSOP_DOUBLE;
9180 pn->pn_arity = PN_NULLARY;
9181 pn->pn_dval = d;
9182 RecycleTree(pn1, tc);
9183 } else if (pn1->pn_type == TOK_PRIMARY) {
9184 if (pn->pn_op == JSOP_NOT &&
9185 (pn1->pn_op == JSOP_TRUE ||
9186 pn1->pn_op == JSOP_FALSE)) {
9187 pn->become(pn1);
9188 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9189 RecycleTree(pn1, tc);
9192 break;
9194 #if JS_HAS_XML_SUPPORT
9195 case TOK_XMLELEM:
9196 case TOK_XMLLIST:
9197 case TOK_XMLPTAGC:
9198 case TOK_XMLSTAGO:
9199 case TOK_XMLETAGO:
9200 case TOK_XMLNAME:
9201 if (pn->pn_arity == PN_LIST) {
9202 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9203 if (!FoldXMLConstants(cx, pn, tc))
9204 return JS_FALSE;
9206 break;
9208 case TOK_AT:
9209 if (pn1->pn_type == TOK_XMLNAME) {
9210 jsval v;
9211 JSObjectBox *xmlbox;
9213 v = ATOM_KEY(pn1->pn_atom);
9214 if (!js_ToAttributeName(cx, &v))
9215 return JS_FALSE;
9216 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9218 xmlbox = tc->compiler->newObjectBox(JSVAL_TO_OBJECT(v));
9219 if (!xmlbox)
9220 return JS_FALSE;
9222 pn->pn_type = TOK_XMLNAME;
9223 pn->pn_op = JSOP_OBJECT;
9224 pn->pn_arity = PN_NULLARY;
9225 pn->pn_objbox = xmlbox;
9226 RecycleTree(pn1, tc);
9228 break;
9229 #endif /* JS_HAS_XML_SUPPORT */
9231 default:;
9234 if (inCond) {
9235 int cond = Boolish(pn);
9236 if (cond >= 0) {
9237 switch (pn->pn_arity) {
9238 case PN_LIST:
9239 pn2 = pn->pn_head;
9240 do {
9241 pn3 = pn2->pn_next;
9242 RecycleTree(pn2, tc);
9243 } while ((pn2 = pn3) != NULL);
9244 break;
9245 case PN_FUNC:
9246 RecycleFuncNameKids(pn, tc);
9247 break;
9248 case PN_NULLARY:
9249 break;
9250 default:
9251 JS_NOT_REACHED("unhandled arity");
9253 pn->pn_type = TOK_PRIMARY;
9254 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9255 pn->pn_arity = PN_NULLARY;
9259 return JS_TRUE;