Backed out changeset 3862a7e48e79 due to tinderbox failures on js1_5/GC/regress-32427...
[mozilla-central.git] / js / src / jsparse.cpp
blob2e0fbcd4b42b5babe2ce449420a8826fc7c9cf65
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"
81 #include "jsvector.h"
83 #if JS_HAS_XML_SUPPORT
84 #include "jsxml.h"
85 #endif
87 #if JS_HAS_DESTRUCTURING
88 #include "jsdhash.h"
89 #endif
92 * Asserts to verify assumptions behind pn_ macros.
94 #define pn_offsetof(m) offsetof(JSParseNode, m)
96 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
97 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
99 #undef pn_offsetof
102 * JS parsers, from lowest to highest precedence.
104 * Each parser takes a context, a token stream, and a tree context struct.
105 * Each returns a parse node tree or null on error.
108 typedef JSParseNode *
109 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
111 typedef JSParseNode *
112 JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
113 bool inLetHead);
115 typedef JSParseNode *
116 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
117 JSBool allowCallSyntax);
119 typedef JSParseNode *
120 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
121 JSTokenType tt, JSBool afterDot);
123 typedef JSParseNode *
124 JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
125 JSParseNode *pn1, JSBool *genexp);
127 static JSParser FunctionStmt;
128 static JSParser FunctionExpr;
129 static JSParser Statements;
130 static JSParser Statement;
131 static JSVariablesParser Variables;
132 static JSParser Expr;
133 static JSParser AssignExpr;
134 static JSParser CondExpr;
135 static JSParser OrExpr;
136 static JSParser AndExpr;
137 static JSParser BitOrExpr;
138 static JSParser BitXorExpr;
139 static JSParser BitAndExpr;
140 static JSParser EqExpr;
141 static JSParser RelExpr;
142 static JSParser ShiftExpr;
143 static JSParser AddExpr;
144 static JSParser MulExpr;
145 static JSParser UnaryExpr;
146 static JSMemberParser MemberExpr;
147 static JSPrimaryParser PrimaryExpr;
148 static JSParenParser ParenExpr;
150 static bool RecognizeDirectivePrologue(JSContext *cx, JSTokenStream *ts,
151 JSTreeContext *tc, JSParseNode *pn);
154 * Insist that the next token be of type tt, or report errno and return null.
155 * NB: this macro uses cx and ts from its lexical environment.
157 #define MUST_MATCH_TOKEN(tt, errno) \
158 JS_BEGIN_MACRO \
159 if (js_GetToken(cx, ts) != tt) { \
160 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
161 return NULL; \
163 JS_END_MACRO
165 #ifdef METER_PARSENODES
166 static uint32 parsenodes = 0;
167 static uint32 maxparsenodes = 0;
168 static uint32 recyclednodes = 0;
169 #endif
171 void
172 JSParseNode::become(JSParseNode *pn2)
174 JS_ASSERT(!pn_defn);
175 JS_ASSERT(!pn2->pn_defn);
177 JS_ASSERT(!pn_used);
178 if (pn2->pn_used) {
179 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
180 while (*pnup != pn2)
181 pnup = &(*pnup)->pn_link;
182 *pnup = this;
183 pn_link = pn2->pn_link;
184 pn_used = true;
185 pn2->pn_link = NULL;
186 pn2->pn_used = false;
189 /* If this is a function node fix up the pn_funbox->node back-pointer. */
190 if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
191 pn2->pn_funbox->node = this;
193 pn_type = pn2->pn_type;
194 pn_op = pn2->pn_op;
195 pn_arity = pn2->pn_arity;
196 pn_parens = pn2->pn_parens;
197 pn_u = pn2->pn_u;
198 pn2->clear();
201 void
202 JSParseNode::clear()
204 pn_type = TOK_EOF;
205 pn_op = JSOP_NOP;
206 pn_used = pn_defn = false;
207 pn_arity = PN_NULLARY;
208 pn_parens = false;
211 bool
212 JSCompiler::init(const jschar *base, size_t length,
213 FILE *fp, const char *filename, uintN lineno)
215 JSContext *cx = context;
217 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
218 if (!tokenStream.init(cx, base, length, fp, filename, lineno)) {
219 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
220 return false;
223 /* Root atoms and objects allocated for the parsed tree. */
224 JS_KEEP_ATOMS(cx->runtime);
225 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
226 return true;
229 JSCompiler::~JSCompiler()
231 JSContext *cx = context;
233 if (principals)
234 JSPRINCIPALS_DROP(cx, principals);
235 JS_ASSERT(tempRoot.u.compiler == this);
236 JS_POP_TEMP_ROOT(cx, &tempRoot);
237 JS_UNKEEP_ATOMS(cx->runtime);
238 tokenStream.close(cx);
239 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
242 void
243 JSCompiler::setPrincipals(JSPrincipals *prin)
245 JS_ASSERT(!principals);
246 if (prin)
247 JSPRINCIPALS_HOLD(context, prin);
248 principals = prin;
251 JSObjectBox *
252 JSCompiler::newObjectBox(JSObject *obj)
254 JS_ASSERT(obj);
257 * We use JSContext.tempPool to allocate parsed objects and place them on
258 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
259 * containing the entries must be alive until we are done with scanning,
260 * parsing and code generation for the whole script or top-level function.
262 JSObjectBox *objbox;
263 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
264 if (!objbox) {
265 js_ReportOutOfScriptQuota(context);
266 return NULL;
268 objbox->traceLink = traceListHead;
269 traceListHead = objbox;
270 objbox->emitLink = NULL;
271 objbox->object = obj;
272 return objbox;
275 JSFunctionBox *
276 JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
278 JS_ASSERT(obj);
279 JS_ASSERT(HAS_FUNCTION_CLASS(obj));
282 * We use JSContext.tempPool to allocate parsed objects and place them on
283 * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
284 * containing the entries must be alive until we are done with scanning,
285 * parsing and code generation for the whole script or top-level function.
287 JSFunctionBox *funbox;
288 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
289 if (!funbox) {
290 js_ReportOutOfScriptQuota(context);
291 return NULL;
293 funbox->traceLink = traceListHead;
294 traceListHead = funbox;
295 funbox->emitLink = NULL;
296 funbox->object = obj;
297 funbox->node = fn;
298 funbox->siblings = tc->functionList;
299 tc->functionList = funbox;
300 ++tc->compiler->functionCount;
301 funbox->kids = NULL;
302 funbox->parent = tc->funbox;
303 funbox->queued = false;
304 funbox->inLoop = false;
305 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
306 if (STMT_IS_LOOP(stmt)) {
307 funbox->inLoop = true;
308 break;
311 funbox->level = tc->staticLevel;
312 funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
313 return funbox;
316 void
317 JSCompiler::trace(JSTracer *trc)
319 JSObjectBox *objbox;
321 JS_ASSERT(tempRoot.u.compiler == this);
322 objbox = traceListHead;
323 while (objbox) {
324 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
325 objbox = objbox->traceLink;
329 static void
330 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
332 static void
333 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
335 JSFunctionBox *funbox = pn->pn_funbox;
336 if (funbox) {
337 JS_ASSERT(funbox->node == pn);
338 funbox->node = NULL;
340 JSFunctionBox **funboxp = &tc->functionList;
341 while (*funboxp) {
342 if (*funboxp == funbox) {
343 *funboxp = funbox->siblings;
344 break;
346 funboxp = &(*funboxp)->siblings;
349 uint32 oldflags = tc->flags;
350 JSFunctionBox *oldlist = tc->functionList;
352 tc->flags = funbox->tcflags;
353 tc->functionList = funbox->kids;
354 UnlinkFunctionBoxes(pn->pn_body, tc);
355 funbox->kids = tc->functionList;
356 tc->flags = oldflags;
357 tc->functionList = oldlist;
359 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
360 pn->pn_funbox = NULL;
364 static void
365 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
367 if (pn) {
368 switch (pn->pn_arity) {
369 case PN_NULLARY:
370 return;
371 case PN_UNARY:
372 UnlinkFunctionBoxes(pn->pn_kid, tc);
373 return;
374 case PN_BINARY:
375 UnlinkFunctionBoxes(pn->pn_left, tc);
376 UnlinkFunctionBoxes(pn->pn_right, tc);
377 return;
378 case PN_TERNARY:
379 UnlinkFunctionBoxes(pn->pn_kid1, tc);
380 UnlinkFunctionBoxes(pn->pn_kid2, tc);
381 UnlinkFunctionBoxes(pn->pn_kid3, tc);
382 return;
383 case PN_LIST:
384 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
385 UnlinkFunctionBoxes(pn2, tc);
386 return;
387 case PN_FUNC:
388 UnlinkFunctionBox(pn, tc);
389 return;
390 case PN_NAME:
391 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
392 return;
393 case PN_NAMESET:
394 UnlinkFunctionBoxes(pn->pn_tree, tc);
399 static void
400 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
402 static JSParseNode *
403 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
405 JSParseNode *next, **head;
407 if (!pn)
408 return NULL;
410 /* Catch back-to-back dup recycles. */
411 JS_ASSERT(pn != tc->compiler->nodeList);
412 next = pn->pn_next;
413 if (pn->pn_used || pn->pn_defn) {
415 * JSAtomLists own definition nodes along with their used-node chains.
416 * Defer recycling such nodes until we unwind to top level to avoid
417 * linkage overhead or (alternatively) unlinking runtime complexity.
418 * Yes, this means dead code can contribute to static analysis results!
420 * Do recycle kids here, since they are no longer needed.
422 pn->pn_next = NULL;
423 RecycleFuncNameKids(pn, tc);
424 } else {
425 UnlinkFunctionBoxes(pn, tc);
426 head = &tc->compiler->nodeList;
427 pn->pn_next = *head;
428 *head = pn;
429 #ifdef METER_PARSENODES
430 recyclednodes++;
431 #endif
433 return next;
436 static void
437 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
439 switch (pn->pn_arity) {
440 case PN_FUNC:
441 UnlinkFunctionBox(pn, tc);
442 /* FALL THROUGH */
444 case PN_NAME:
446 * Only a definition node might have a non-null strong pn_expr link
447 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
448 * Every node with the pn_used flag set has a non-null pn_lexdef
449 * weak reference to its definition node.
451 if (!pn->pn_used && pn->pn_expr) {
452 RecycleTree(pn->pn_expr, tc);
453 pn->pn_expr = NULL;
455 break;
457 default:
458 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
462 static JSParseNode *
463 NewOrRecycledNode(JSTreeContext *tc)
465 JSParseNode *pn, *pn2;
467 pn = tc->compiler->nodeList;
468 if (!pn) {
469 JSContext *cx = tc->compiler->context;
471 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
472 if (!pn)
473 js_ReportOutOfScriptQuota(cx);
474 } else {
475 tc->compiler->nodeList = pn->pn_next;
477 /* Recycle immediate descendents only, to save work and working set. */
478 switch (pn->pn_arity) {
479 case PN_FUNC:
480 RecycleTree(pn->pn_body, tc);
481 break;
482 case PN_LIST:
483 pn2 = pn->pn_head;
484 if (pn2) {
485 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
486 pn2 = pn2->pn_next;
487 if (pn2) {
488 pn2 = pn->pn_head;
489 do {
490 pn2 = RecycleTree(pn2, tc);
491 } while (pn2);
492 } else {
493 *pn->pn_tail = tc->compiler->nodeList;
494 tc->compiler->nodeList = pn->pn_head;
495 #ifdef METER_PARSENODES
496 recyclednodes += pn->pn_count;
497 #endif
498 break;
501 break;
502 case PN_TERNARY:
503 RecycleTree(pn->pn_kid1, tc);
504 RecycleTree(pn->pn_kid2, tc);
505 RecycleTree(pn->pn_kid3, tc);
506 break;
507 case PN_BINARY:
508 if (pn->pn_left != pn->pn_right)
509 RecycleTree(pn->pn_left, tc);
510 RecycleTree(pn->pn_right, tc);
511 break;
512 case PN_UNARY:
513 RecycleTree(pn->pn_kid, tc);
514 break;
515 case PN_NAME:
516 if (!pn->pn_used)
517 RecycleTree(pn->pn_expr, tc);
518 break;
519 case PN_NULLARY:
520 break;
523 if (pn) {
524 #ifdef METER_PARSENODES
525 parsenodes++;
526 if (parsenodes - recyclednodes > maxparsenodes)
527 maxparsenodes = parsenodes - recyclednodes;
528 #endif
529 pn->pn_used = pn->pn_defn = false;
530 memset(&pn->pn_u, 0, sizeof pn->pn_u);
531 pn->pn_next = NULL;
533 return pn;
536 static inline void
537 InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
539 pn->pn_type = type;
540 pn->pn_op = op;
541 pn->pn_arity = arity;
542 pn->pn_parens = false;
543 JS_ASSERT(!pn->pn_used);
544 JS_ASSERT(!pn->pn_defn);
545 pn->pn_next = pn->pn_link = NULL;
549 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
550 * temporary arena.
552 static JSParseNode *
553 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
555 JSParseNode *pn;
556 JSToken *tp;
558 pn = NewOrRecycledNode(tc);
559 if (!pn)
560 return NULL;
561 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
562 InitParseNode(pn, tp->type, JSOP_NOP, arity);
563 pn->pn_pos = tp->pos;
564 return pn;
567 static inline void
568 InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
570 pn->pn_expr = NULL;
571 pn->pn_cookie = FREE_UPVAR_COOKIE;
572 pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
573 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
574 pn->pn_dflags |= PND_BLOCKCHILD;
575 pn->pn_blockid = tc->blockid();
578 static JSParseNode *
579 NewNameNode(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
581 JSParseNode *pn;
583 pn = NewParseNode(PN_NAME, tc);
584 if (pn) {
585 pn->pn_atom = atom;
586 InitNameNodeCommon(pn, tc);
588 return pn;
591 static JSParseNode *
592 NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
593 JSTreeContext *tc)
595 JSParseNode *pn, *pn1, *pn2;
597 if (!left || !right)
598 return NULL;
601 * Flatten a left-associative (left-heavy) tree of a given operator into
602 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
604 if (PN_TYPE(left) == tt &&
605 PN_OP(left) == op &&
606 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
607 if (left->pn_arity != PN_LIST) {
608 pn1 = left->pn_left, pn2 = left->pn_right;
609 left->pn_arity = PN_LIST;
610 left->pn_parens = false;
611 left->initList(pn1);
612 left->append(pn2);
613 if (tt == TOK_PLUS) {
614 if (pn1->pn_type == TOK_STRING)
615 left->pn_xflags |= PNX_STRCAT;
616 else if (pn1->pn_type != TOK_NUMBER)
617 left->pn_xflags |= PNX_CANTFOLD;
618 if (pn2->pn_type == TOK_STRING)
619 left->pn_xflags |= PNX_STRCAT;
620 else if (pn2->pn_type != TOK_NUMBER)
621 left->pn_xflags |= PNX_CANTFOLD;
624 left->append(right);
625 left->pn_pos.end = right->pn_pos.end;
626 if (tt == TOK_PLUS) {
627 if (right->pn_type == TOK_STRING)
628 left->pn_xflags |= PNX_STRCAT;
629 else if (right->pn_type != TOK_NUMBER)
630 left->pn_xflags |= PNX_CANTFOLD;
632 return left;
636 * Fold constant addition immediately, to conserve node space and, what's
637 * more, so js_FoldConstants never sees mixed addition and concatenation
638 * operations with more than one leading non-string operand in a PN_LIST
639 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
640 * to "3pt", not "12pt").
642 if (tt == TOK_PLUS &&
643 left->pn_type == TOK_NUMBER &&
644 right->pn_type == TOK_NUMBER) {
645 left->pn_dval += right->pn_dval;
646 left->pn_pos.end = right->pn_pos.end;
647 RecycleTree(right, tc);
648 return left;
651 pn = NewOrRecycledNode(tc);
652 if (!pn)
653 return NULL;
654 InitParseNode(pn, tt, op, PN_BINARY);
655 pn->pn_pos.begin = left->pn_pos.begin;
656 pn->pn_pos.end = right->pn_pos.end;
657 pn->pn_left = left;
658 pn->pn_right = right;
659 return pn;
662 #if JS_HAS_GETTER_SETTER
663 static JSTokenType
664 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
666 JSAtom *atom;
667 JSRuntime *rt;
668 JSOp op;
669 const char *name;
671 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
672 atom = CURRENT_TOKEN(ts).t_atom;
673 rt = cx->runtime;
674 if (atom == rt->atomState.getterAtom)
675 op = JSOP_GETTER;
676 else if (atom == rt->atomState.setterAtom)
677 op = JSOP_SETTER;
678 else
679 return TOK_NAME;
680 if (js_PeekTokenSameLine(cx, ts) != tt)
681 return TOK_NAME;
682 (void) js_GetToken(cx, ts);
683 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
684 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
685 JSMSG_BAD_GETTER_OR_SETTER,
686 (op == JSOP_GETTER)
687 ? js_getter_str
688 : js_setter_str);
689 return TOK_ERROR;
691 CURRENT_TOKEN(ts).t_op = op;
692 if (JS_HAS_STRICT_OPTION(cx)) {
693 name = js_AtomToPrintableString(cx, atom);
694 if (!name ||
695 !js_ReportCompileErrorNumber(cx, ts, NULL,
696 JSREPORT_WARNING | JSREPORT_STRICT,
697 JSMSG_DEPRECATED_USAGE,
698 name)) {
699 return TOK_ERROR;
702 return tt;
704 #endif
706 static bool
707 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
709 if (tc->blockidGen == JS_BIT(20)) {
710 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
711 JSMSG_NEED_DIET, "program");
712 return false;
714 blockid = tc->blockidGen++;
715 return true;
718 static bool
719 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
721 JS_ASSERT(tc->topStmt);
722 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
723 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
724 if (!GenerateBlockId(tc, tc->topStmt->blockid))
725 return false;
726 pn->pn_blockid = tc->topStmt->blockid;
727 return true;
731 * Parse a top-level JS script.
733 JSParseNode *
734 JSCompiler::parse(JSObject *chain)
737 * Protect atoms from being collected by a GC activation, which might
738 * - nest on this thread due to out of memory (the so-called "last ditch"
739 * GC attempted within js_NewGCThing), or
740 * - run for any reason on another thread if this thread is suspended on
741 * an object lock before it finishes generating bytecode into a script
742 * protected from the GC by a root or a stack frame reference.
744 JSTreeContext tc(this);
745 tc.scopeChain = chain;
746 if (!GenerateBlockId(&tc, tc.bodyid))
747 return NULL;
749 JSParseNode *pn = Statements(context, TS(this), &tc);
750 if (pn) {
751 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
752 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
753 JSMSG_SYNTAX_ERROR);
754 pn = NULL;
755 } else {
756 if (!js_FoldConstants(context, pn, &tc))
757 pn = NULL;
760 return pn;
763 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
765 static inline bool
766 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
769 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
770 * (0xffffffff) and other cookies with that level.
772 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
773 * practically speaking it leaves more than enough room for upvars. In fact
774 * we might want to split cookie fields giving fewer bits for skip and more
775 * for slot, but only based on evidence.
777 if (staticLevel >= FREE_STATIC_LEVEL) {
778 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
779 JSMSG_TOO_DEEP, js_function_str);
780 return false;
782 tc->staticLevel = staticLevel;
783 return true;
787 * Compile a top-level script.
789 JSScript *
790 JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
791 JSPrincipals *principals, uint32 tcflags,
792 const jschar *chars, size_t length,
793 FILE *file, const char *filename, uintN lineno,
794 JSString *source /* = NULL */,
795 unsigned staticLevel /* = 0 */)
797 JSCompiler jsc(cx, principals, callerFrame);
798 JSArenaPool codePool, notePool;
799 JSTokenType tt;
800 JSParseNode *pn;
801 uint32 scriptGlobals;
802 JSScript *script;
803 bool inDirectivePrologue;
804 #ifdef METER_PARSENODES
805 void *sbrk(ptrdiff_t), *before = sbrk(0);
806 #endif
808 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
811 * The scripted callerFrame can only be given for compile-and-go scripts
812 * and non-zero static level requires callerFrame.
814 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
815 JS_ASSERT_IF(staticLevel != 0, callerFrame);
817 if (!jsc.init(chars, length, file, filename, lineno))
818 return NULL;
820 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
821 &cx->scriptStackQuota);
822 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
823 &cx->scriptStackQuota);
825 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
827 MUST_FLOW_THROUGH("out");
829 /* Null script early in case of error, to reduce our code footprint. */
830 script = NULL;
832 cg.flags |= tcflags;
833 cg.scopeChain = scopeChain;
834 if (!SetStaticLevel(&cg, staticLevel))
835 goto out;
837 /* If this is a direct call to eval, inherit the caller's strictness. */
838 if (callerFrame &&
839 callerFrame->script &&
840 callerFrame->script->strictModeCode) {
841 cg.flags |= TCF_STRICT_MODE_CODE;
842 jsc.tokenStream.flags |= TSF_STRICT_MODE_CODE;
846 * If funbox is non-null after we create the new script, callerFrame->fun
847 * was saved in the 0th object table entry.
849 JSObjectBox *funbox;
850 funbox = NULL;
852 if (tcflags & TCF_COMPILE_N_GO) {
853 if (source) {
855 * Save eval program source in script->atomMap.vector[0] for the
856 * eval cache (see obj_eval in jsobj.cpp).
858 JSAtom *atom = js_AtomizeString(cx, source, 0);
859 if (!atom || !cg.atomList.add(&jsc, atom))
860 goto out;
863 if (callerFrame && callerFrame->fun) {
865 * An eval script in a caller frame needs to have its enclosing
866 * function captured in case it refers to an upvar, and someone
867 * wishes to decompile it while it's running.
869 funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
870 if (!funbox)
871 goto out;
872 funbox->emitLink = cg.objectList.lastbox;
873 cg.objectList.lastbox = funbox;
874 cg.objectList.length++;
879 * Inline Statements to emit as we go to save AST space. We must generate
880 * our script-body blockid since we aren't calling Statements.
882 uint32 bodyid;
883 if (!GenerateBlockId(&cg, bodyid))
884 goto out;
885 cg.bodyid = bodyid;
887 #if JS_HAS_XML_SUPPORT
888 pn = NULL;
889 bool onlyXML;
890 onlyXML = true;
891 #endif
893 CG_SWITCH_TO_PROLOG(&cg);
894 if (js_Emit1(cx, &cg, JSOP_TRACE) < 0)
895 goto out;
896 CG_SWITCH_TO_MAIN(&cg);
898 inDirectivePrologue = true;
899 for (;;) {
900 jsc.tokenStream.flags |= TSF_OPERAND;
901 tt = js_PeekToken(cx, &jsc.tokenStream);
902 jsc.tokenStream.flags &= ~TSF_OPERAND;
903 if (tt <= TOK_EOF) {
904 if (tt == TOK_EOF)
905 break;
906 JS_ASSERT(tt == TOK_ERROR);
907 goto out;
910 pn = Statement(cx, &jsc.tokenStream, &cg);
911 if (!pn)
912 goto out;
913 JS_ASSERT(!cg.blockNode);
915 if (inDirectivePrologue)
916 inDirectivePrologue = RecognizeDirectivePrologue(cx, &jsc.tokenStream, &cg, pn);
918 if (!js_FoldConstants(cx, pn, &cg))
919 goto out;
921 if (cg.functionList) {
922 if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
923 goto out;
924 cg.functionList = NULL;
927 if (!js_EmitTree(cx, &cg, pn))
928 goto out;
929 #if JS_HAS_XML_SUPPORT
930 if (PN_TYPE(pn) != TOK_SEMI ||
931 !pn->pn_kid ||
932 !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
933 onlyXML = false;
935 #endif
936 RecycleTree(pn, &cg);
939 #if JS_HAS_XML_SUPPORT
941 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
942 * For background, see:
944 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
946 if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
947 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
948 JSMSG_XML_WHOLE_PROGRAM);
949 goto out;
951 #endif
954 * Global variables and regexps share the index space with locals. Due to
955 * incremental code generation we need to patch the bytecode to adjust the
956 * local references to skip the globals.
958 scriptGlobals = cg.ngvars + cg.regexpList.length;
959 if (scriptGlobals != 0 || cg.hasSharps()) {
960 jsbytecode *code, *end;
961 JSOp op;
962 const JSCodeSpec *cs;
963 uintN len, slot;
965 if (scriptGlobals >= SLOTNO_LIMIT)
966 goto too_many_slots;
967 code = CG_BASE(&cg);
968 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
969 JS_ASSERT(code < end);
970 op = (JSOp) *code;
971 cs = &js_CodeSpec[op];
972 len = (cs->length > 0)
973 ? (uintN) cs->length
974 : js_GetVariableBytecodeLength(code);
975 if ((cs->format & JOF_SHARPSLOT) ||
976 JOF_TYPE(cs->format) == JOF_LOCAL ||
977 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
979 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
980 * emitted only for a function.
982 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
983 (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
984 (op == JSOP_GETLOCALPROP));
985 slot = GET_SLOTNO(code);
986 slot += scriptGlobals;
987 if (!(cs->format & JOF_SHARPSLOT))
988 slot += cg.sharpSlots();
989 if (slot >= SLOTNO_LIMIT)
990 goto too_many_slots;
991 SET_SLOTNO(code, slot);
996 #ifdef METER_PARSENODES
997 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
998 (char *)sbrk(0) - (char *)before,
999 parsenodes,
1000 maxparsenodes,
1001 parsenodes - recyclednodes);
1002 before = sbrk(0);
1003 #endif
1006 * Nowadays the threaded interpreter needs a stop instruction, so we
1007 * do have to emit that here.
1009 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
1010 goto out;
1011 #ifdef METER_PARSENODES
1012 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
1013 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
1014 #endif
1015 #ifdef JS_ARENAMETER
1016 JS_DumpArenaStats(stdout);
1017 #endif
1018 script = js_NewScriptFromCG(cx, &cg);
1019 if (script && funbox)
1020 script->savedCallerFun = true;
1022 #ifdef JS_SCOPE_DEPTH_METER
1023 if (script) {
1024 JSObject *obj = scopeChain;
1025 uintN depth = 1;
1026 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
1027 ++depth;
1028 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1030 #endif
1032 out:
1033 JS_FinishArenaPool(&codePool);
1034 JS_FinishArenaPool(&notePool);
1035 return script;
1037 too_many_slots:
1038 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1039 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1040 script = NULL;
1041 goto out;
1045 * Insist on a final return before control flows out of pn. Try to be a bit
1046 * smart about loops: do {...; return e2;} while(0) at the end of a function
1047 * that contains an early return e1 will get a strict warning. Similarly for
1048 * iloops: while (true){...} is treated as though ... returns.
1050 #define ENDS_IN_OTHER 0
1051 #define ENDS_IN_RETURN 1
1052 #define ENDS_IN_BREAK 2
1054 static int
1055 HasFinalReturn(JSParseNode *pn)
1057 JSParseNode *pn2, *pn3;
1058 uintN rv, rv2, hasDefault;
1060 switch (pn->pn_type) {
1061 case TOK_LC:
1062 if (!pn->pn_head)
1063 return ENDS_IN_OTHER;
1064 return HasFinalReturn(pn->last());
1066 case TOK_IF:
1067 if (!pn->pn_kid3)
1068 return ENDS_IN_OTHER;
1069 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1071 case TOK_WHILE:
1072 pn2 = pn->pn_left;
1073 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1074 return ENDS_IN_RETURN;
1075 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1076 return ENDS_IN_RETURN;
1077 return ENDS_IN_OTHER;
1079 case TOK_DO:
1080 pn2 = pn->pn_right;
1081 if (pn2->pn_type == TOK_PRIMARY) {
1082 if (pn2->pn_op == JSOP_FALSE)
1083 return HasFinalReturn(pn->pn_left);
1084 if (pn2->pn_op == JSOP_TRUE)
1085 return ENDS_IN_RETURN;
1087 if (pn2->pn_type == TOK_NUMBER) {
1088 if (pn2->pn_dval == 0)
1089 return HasFinalReturn(pn->pn_left);
1090 return ENDS_IN_RETURN;
1092 return ENDS_IN_OTHER;
1094 case TOK_FOR:
1095 pn2 = pn->pn_left;
1096 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1097 return ENDS_IN_RETURN;
1098 return ENDS_IN_OTHER;
1100 case TOK_SWITCH:
1101 rv = ENDS_IN_RETURN;
1102 hasDefault = ENDS_IN_OTHER;
1103 pn2 = pn->pn_right;
1104 if (pn2->pn_type == TOK_LEXICALSCOPE)
1105 pn2 = pn2->expr();
1106 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1107 if (pn2->pn_type == TOK_DEFAULT)
1108 hasDefault = ENDS_IN_RETURN;
1109 pn3 = pn2->pn_right;
1110 JS_ASSERT(pn3->pn_type == TOK_LC);
1111 if (pn3->pn_head) {
1112 rv2 = HasFinalReturn(pn3->last());
1113 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1114 /* Falling through to next case or default. */;
1115 else
1116 rv &= rv2;
1119 /* If a final switch has no default case, we judge it harshly. */
1120 rv &= hasDefault;
1121 return rv;
1123 case TOK_BREAK:
1124 return ENDS_IN_BREAK;
1126 case TOK_WITH:
1127 return HasFinalReturn(pn->pn_right);
1129 case TOK_RETURN:
1130 return ENDS_IN_RETURN;
1132 case TOK_COLON:
1133 case TOK_LEXICALSCOPE:
1134 return HasFinalReturn(pn->expr());
1136 case TOK_THROW:
1137 return ENDS_IN_RETURN;
1139 case TOK_TRY:
1140 /* If we have a finally block that returns, we are done. */
1141 if (pn->pn_kid3) {
1142 rv = HasFinalReturn(pn->pn_kid3);
1143 if (rv == ENDS_IN_RETURN)
1144 return rv;
1147 /* Else check the try block and any and all catch statements. */
1148 rv = HasFinalReturn(pn->pn_kid1);
1149 if (pn->pn_kid2) {
1150 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1151 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1152 rv &= HasFinalReturn(pn2);
1154 return rv;
1156 case TOK_CATCH:
1157 /* Check this catch block's body. */
1158 return HasFinalReturn(pn->pn_kid3);
1160 case TOK_LET:
1161 /* Non-binary let statements are let declarations. */
1162 if (pn->pn_arity != PN_BINARY)
1163 return ENDS_IN_OTHER;
1164 return HasFinalReturn(pn->pn_right);
1166 default:
1167 return ENDS_IN_OTHER;
1171 static JSBool
1172 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1173 uintN anonerrnum)
1175 const char *name;
1177 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1178 if (tc->fun->atom) {
1179 name = js_AtomToPrintableString(cx, tc->fun->atom);
1180 } else {
1181 errnum = anonerrnum;
1182 name = NULL;
1184 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1185 errnum, name);
1188 static JSBool
1189 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1191 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1192 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1193 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1194 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1198 * Check that it is permitted to assign to lhs. Strict mode code may not
1199 * assign to 'eval' or 'arguments'.
1201 bool
1202 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1204 if (tc->needStrictChecks() &&
1205 lhs->pn_type == TOK_NAME) {
1206 JSAtom *atom = lhs->pn_atom;
1207 JSAtomState *atomState = &cx->runtime->atomState;
1208 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1209 const char *name = js_AtomToPrintableString(cx, atom);
1210 if (!name ||
1211 !js_ReportStrictModeError(cx, TS(tc->compiler), tc, lhs,
1212 JSMSG_DEPRECATED_ASSIGN, name)) {
1213 return false;
1217 return true;
1221 * Check that it is permitted to introduce a binding for atom. Strict
1222 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1223 * Use pn for reporting error locations, or use tc's token stream if
1224 * pn is NULL.
1226 bool
1227 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
1228 JSParseNode *pn)
1230 if (!tc->needStrictChecks())
1231 return true;
1233 JSAtomState *atomState = &cx->runtime->atomState;
1234 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1235 const char *name = js_AtomToPrintableString(cx, atom);
1236 if (name)
1237 js_ReportStrictModeError(cx, TS(tc->compiler), tc, pn,
1238 JSMSG_BAD_BINDING, name);
1239 return false;
1241 return true;
1245 * In strict mode code, all formal parameter names must be distinct. If fun's
1246 * formals are legit given fun's strictness level, return true. Otherwise,
1247 * report an error and return false. Use pn for error position reporting,
1248 * unless we can find something more accurate in tc's decls.
1250 * In some cases the code to parse the argument list will already have noticed
1251 * the duplication; we could try to use that knowledge instead of re-checking
1252 * here. But since the strictness of the function's body determines what
1253 * constraints to apply to the argument list, we can't report the error until
1254 * after we've parsed the body. And as it turns out, the function's local name
1255 * list makes it reasonably cheap to find duplicates after the fact.
1257 static bool
1258 CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
1259 JSParseNode *pn)
1261 JSAtom *atom;
1263 if (!tc->needStrictChecks())
1264 return true;
1266 atom = fun->findDuplicateFormal();
1267 if (atom) {
1269 * We have found a duplicate parameter name. If we can find the
1270 * JSDefinition for the argument, that will have a more accurate source
1271 * location.
1273 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1274 if (dn->pn_op == JSOP_GETARG)
1275 pn = dn;
1276 const char *name = js_AtomToPrintableString(cx, atom);
1277 if (!name ||
1278 !js_ReportStrictModeError(cx, TS(tc->compiler), tc, pn,
1279 JSMSG_DUPLICATE_FORMAL, name)) {
1280 return false;
1284 if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
1285 JSAtomState *atoms = &cx->runtime->atomState;
1286 atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS
1287 ? atoms->argumentsAtom : atoms->evalAtom);
1288 /* The definition's source position will be more precise. */
1289 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1290 JS_ASSERT(dn->pn_atom == atom);
1291 const char *name = js_AtomToPrintableString(cx, atom);
1292 if (!name ||
1293 !js_ReportStrictModeError(cx, TS(tc->compiler), tc, dn,
1294 JSMSG_BAD_BINDING, name)) {
1295 return false;
1299 return true;
1302 static JSParseNode *
1303 FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1305 JSStmtInfo stmtInfo;
1306 uintN oldflags, firstLine;
1307 JSParseNode *pn;
1309 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1310 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1311 stmtInfo.flags = SIF_BODY_BLOCK;
1313 oldflags = tc->flags;
1314 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1317 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1318 * later, because we may have not peeked in ts yet, so Statements won't
1319 * acquire a valid pn->pn_pos.begin from the current token.
1321 firstLine = ts->lineno;
1322 #if JS_HAS_EXPR_CLOSURES
1323 if (CURRENT_TOKEN(ts).type == TOK_LC) {
1324 pn = Statements(cx, ts, tc);
1325 } else {
1326 pn = NewParseNode(PN_UNARY, tc);
1327 if (pn) {
1328 pn->pn_kid = AssignExpr(cx, ts, tc);
1329 if (!pn->pn_kid) {
1330 pn = NULL;
1331 } else {
1332 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1333 ReportBadReturn(cx, tc, JSREPORT_ERROR,
1334 JSMSG_BAD_GENERATOR_RETURN,
1335 JSMSG_BAD_ANON_GENERATOR_RETURN);
1336 pn = NULL;
1337 } else {
1338 pn->pn_type = TOK_RETURN;
1339 pn->pn_op = JSOP_RETURN;
1340 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1345 #else
1346 pn = Statements(cx, ts, tc);
1347 #endif
1349 if (pn) {
1350 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1351 js_PopStatement(tc);
1352 pn->pn_pos.begin.lineno = firstLine;
1354 /* Check for falling off the end of a function that returns a value. */
1355 if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
1356 !CheckFinalReturn(cx, tc, pn)) {
1357 pn = NULL;
1361 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1362 return pn;
1365 static JSAtomListElement *
1366 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1368 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1369 if (!ale)
1370 return NULL;
1372 JSDefinition *dn = (JSDefinition *)
1373 NewNameNode(tc->compiler->context, pn->pn_atom, tc);
1374 if (!dn)
1375 return NULL;
1377 ALE_SET_DEFN(ale, dn);
1378 dn->pn_defn = true;
1379 dn->pn_dflags |= PND_PLACEHOLDER;
1380 return ale;
1383 static bool
1384 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1386 JS_ASSERT(!pn->pn_used);
1387 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1389 JSHashEntry **hep;
1390 JSAtomListElement *ale = NULL;
1391 JSAtomList *list = NULL;
1393 if (let)
1394 ale = (list = &tc->decls)->rawLookup(atom, hep);
1395 if (!ale)
1396 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1398 if (ale) {
1399 JSDefinition *dn = ALE_DEFN(ale);
1400 if (dn != pn) {
1401 JSParseNode **pnup = &dn->dn_uses;
1402 JSParseNode *pnu;
1403 uintN start = let ? pn->pn_blockid : tc->bodyid;
1405 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1406 JS_ASSERT(pnu->pn_used);
1407 pnu->pn_lexdef = (JSDefinition *) pn;
1408 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1409 pnup = &pnu->pn_link;
1412 if (pnu != dn->dn_uses) {
1413 *pnup = pn->dn_uses;
1414 pn->dn_uses = dn->dn_uses;
1415 dn->dn_uses = pnu;
1417 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1418 list->rawRemove(tc->compiler, ale, hep);
1423 ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1424 if (!ale)
1425 return false;
1426 ALE_SET_DEFN(ale, pn);
1427 pn->pn_defn = true;
1428 pn->pn_dflags &= ~PND_PLACEHOLDER;
1429 return true;
1432 static void
1433 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1435 JS_ASSERT(!pn->pn_used);
1436 JS_ASSERT(!pn->pn_defn);
1437 JS_ASSERT(pn != dn->dn_uses);
1438 pn->pn_link = dn->dn_uses;
1439 dn->dn_uses = pn;
1440 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1441 pn->pn_used = true;
1442 pn->pn_lexdef = dn;
1445 static void
1446 ForgetUse(JSParseNode *pn)
1448 if (!pn->pn_used) {
1449 JS_ASSERT(!pn->pn_defn);
1450 return;
1453 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1454 JSParseNode *pnu;
1455 while ((pnu = *pnup) != pn)
1456 pnup = &pnu->pn_link;
1457 *pnup = pn->pn_link;
1458 pn->pn_used = false;
1461 static JSParseNode *
1462 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1464 JSParseNode *lhs = NewOrRecycledNode(tc);
1465 if (!lhs)
1466 return NULL;
1467 *lhs = *pn;
1469 if (pn->pn_used) {
1470 JSDefinition *dn = pn->pn_lexdef;
1471 JSParseNode **pnup = &dn->dn_uses;
1473 while (*pnup != pn)
1474 pnup = &(*pnup)->pn_link;
1475 *pnup = lhs;
1476 lhs->pn_link = pn->pn_link;
1477 pn->pn_link = NULL;
1480 pn->pn_type = TOK_ASSIGN;
1481 pn->pn_op = JSOP_NOP;
1482 pn->pn_arity = PN_BINARY;
1483 pn->pn_parens = false;
1484 pn->pn_used = pn->pn_defn = false;
1485 pn->pn_left = lhs;
1486 pn->pn_right = rhs;
1487 return lhs;
1490 static JSParseNode *
1491 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1494 * If dn is var, const, or let, and it has an initializer, then we must
1495 * rewrite it to be an assignment node, whose freshly allocated left-hand
1496 * side becomes a use of pn.
1498 if (dn->isBindingForm()) {
1499 JSParseNode *rhs = dn->expr();
1500 if (rhs) {
1501 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1502 if (!lhs)
1503 return NULL;
1504 //pn->dn_uses = lhs;
1505 dn = (JSDefinition *) lhs;
1508 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1509 } else if (dn->kind() == JSDefinition::FUNCTION) {
1510 JS_ASSERT(dn->isTopLevel());
1511 JS_ASSERT(dn->pn_op == JSOP_NOP);
1512 dn->pn_type = TOK_NAME;
1513 dn->pn_arity = PN_NAME;
1514 dn->pn_atom = atom;
1517 /* Now make dn no longer a definition, rather a use of pn. */
1518 JS_ASSERT(dn->pn_type == TOK_NAME);
1519 JS_ASSERT(dn->pn_arity == PN_NAME);
1520 JS_ASSERT(dn->pn_atom == atom);
1522 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1523 JS_ASSERT(pnu->pn_used);
1524 JS_ASSERT(!pnu->pn_defn);
1525 pnu->pn_lexdef = (JSDefinition *) pn;
1526 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1528 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1529 pn->dn_uses = dn;
1531 dn->pn_defn = false;
1532 dn->pn_used = true;
1533 dn->pn_lexdef = (JSDefinition *) pn;
1534 dn->pn_cookie = FREE_UPVAR_COOKIE;
1535 dn->pn_dflags &= ~PND_BOUND;
1536 return dn;
1539 static bool
1540 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1542 JSParseNode *argpn, *argsbody;
1544 /* Flag tc so we don't have to lookup arguments on every use. */
1545 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1546 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1547 if (atom == tc->compiler->context->runtime->atomState.evalAtom)
1548 tc->flags |= TCF_FUN_PARAM_EVAL;
1551 * Make an argument definition node, distinguished by being in tc->decls
1552 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1553 * list node returned via pn->pn_body.
1555 argpn = NewNameNode(tc->compiler->context, atom, tc);
1556 if (!argpn)
1557 return false;
1558 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1560 /* Arguments are initialized by definition. */
1561 argpn->pn_dflags |= PND_INITIALIZED;
1562 if (!Define(argpn, atom, tc))
1563 return false;
1565 argsbody = pn->pn_body;
1566 if (!argsbody) {
1567 argsbody = NewParseNode(PN_LIST, tc);
1568 if (!argsbody)
1569 return false;
1570 argsbody->pn_type = TOK_ARGSBODY;
1571 argsbody->pn_op = JSOP_NOP;
1572 argsbody->makeEmpty();
1573 pn->pn_body = argsbody;
1575 argsbody->append(argpn);
1577 argpn->pn_op = JSOP_GETARG;
1578 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1579 argpn->pn_dflags |= PND_BOUND;
1580 return true;
1584 * Compile a JS function body, which might appear as the value of an event
1585 * handler attribute in an HTML <INPUT> tag.
1587 bool
1588 JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1589 const jschar *chars, size_t length,
1590 const char *filename, uintN lineno)
1592 JSCompiler jsc(cx, principals);
1594 if (!jsc.init(chars, length, NULL, filename, lineno))
1595 return false;
1597 /* No early return from after here until the js_FinishArenaPool calls. */
1598 JSArenaPool codePool, notePool;
1599 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1600 &cx->scriptStackQuota);
1601 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
1602 &cx->scriptStackQuota);
1604 JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1605 funcg.flags |= TCF_IN_FUNCTION;
1606 funcg.fun = fun;
1607 if (!GenerateBlockId(&funcg, funcg.bodyid))
1608 return NULL;
1610 /* FIXME: make Function format the source for a function definition. */
1611 jsc.tokenStream.tokens[0].type = TOK_NAME;
1612 JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1613 if (fn) {
1614 fn->pn_body = NULL;
1615 fn->pn_cookie = FREE_UPVAR_COOKIE;
1617 uintN nargs = fun->nargs;
1618 if (nargs) {
1619 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1620 if (!names) {
1621 fn = NULL;
1622 } else {
1623 for (uintN i = 0; i < nargs; i++) {
1624 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1625 if (!DefineArg(fn, name, i, &funcg)) {
1626 fn = NULL;
1627 break;
1635 * Farble the body so that it looks like a block statement to js_EmitTree,
1636 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1637 * done parsing, we must fold constants, analyze any nested functions, and
1638 * generate code for this function, including a stop opcode at the end.
1640 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1641 JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1642 if (pn) {
1643 if (!CheckStrictFormals(cx, &funcg, fun, pn)) {
1644 pn = NULL;
1645 } else if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1646 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1647 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1648 pn = NULL;
1649 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1650 /* js_FoldConstants reported the error already. */
1651 pn = NULL;
1652 } else if (funcg.functionList &&
1653 !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1654 pn = NULL;
1655 } else {
1656 if (fn->pn_body) {
1657 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1658 fn->pn_body->append(pn);
1659 fn->pn_body->pn_pos = pn->pn_pos;
1660 pn = fn->pn_body;
1663 if (!js_EmitFunctionScript(cx, &funcg, pn))
1664 pn = NULL;
1668 /* Restore saved state and release code generation arenas. */
1669 JS_FinishArenaPool(&codePool);
1670 JS_FinishArenaPool(&notePool);
1671 return pn != NULL;
1675 * Parameter block types for the several Binder functions. We use a common
1676 * helper function signature in order to share code among destructuring and
1677 * simple variable declaration parsers. In the destructuring case, the binder
1678 * function is called indirectly from the variable declaration parser by way
1679 * of CheckDestructuring and its friends.
1681 typedef struct BindData BindData;
1683 typedef JSBool
1684 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1686 struct BindData {
1687 BindData() : fresh(true) {}
1689 JSParseNode *pn; /* name node for definition processing and
1690 error source coordinates */
1691 JSOp op; /* prolog bytecode or nop */
1692 Binder binder; /* binder, discriminates u */
1693 union {
1694 struct {
1695 uintN overflow;
1696 } let;
1698 bool fresh;
1701 static JSBool
1702 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1703 JSLocalKind localKind, bool isArg)
1705 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1708 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1709 * Instead 'var arguments' always restates the predefined property of the
1710 * activation objects whose name is 'arguments'. Assignment to such a
1711 * variable must be handled specially.
1713 * Special case: an argument named 'arguments' *does* shadow the predefined
1714 * arguments property.
1716 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1717 return JS_TRUE;
1719 return js_AddLocal(cx, fun, atom, localKind);
1722 #if JS_HAS_DESTRUCTURING
1724 * Forward declaration to maintain top-down presentation.
1726 static JSParseNode *
1727 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1728 JSTokenType tt);
1730 static JSBool
1731 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1732 JSTreeContext *tc)
1734 JSParseNode *pn;
1736 /* Flag tc so we don't have to lookup arguments on every use. */
1737 if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1738 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1739 if (atom == tc->compiler->context->runtime->atomState.evalAtom)
1740 tc->flags |= TCF_FUN_PARAM_EVAL;
1742 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1744 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1745 if (localKind != JSLOCAL_NONE) {
1746 js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1747 JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1748 return JS_FALSE;
1750 JS_ASSERT(!tc->decls.lookup(atom));
1752 pn = data->pn;
1753 if (!Define(pn, atom, tc))
1754 return JS_FALSE;
1756 uintN index = tc->fun->u.i.nvars;
1757 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1758 return JS_FALSE;
1759 pn->pn_op = JSOP_SETLOCAL;
1760 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1761 pn->pn_dflags |= PND_BOUND;
1762 return JS_TRUE;
1764 #endif /* JS_HAS_DESTRUCTURING */
1766 JSFunction *
1767 JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1769 JSObject *parent;
1770 JSFunction *fun;
1772 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1775 * Find the global compilation context in order to pre-set the newborn
1776 * function's parent slot to tc->scopeChain. If the global context is a
1777 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1778 * clear parent and proto.
1780 while (tc->parent)
1781 tc = tc->parent;
1782 parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1784 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1785 parent, atom);
1787 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1788 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1789 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1791 return fun;
1794 static JSBool
1795 MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1797 JSTokenType tt;
1799 ts->flags |= TSF_OPERAND;
1800 tt = js_PeekTokenSameLine(cx, ts);
1801 ts->flags &= ~TSF_OPERAND;
1802 if (tt == TOK_ERROR)
1803 return JS_FALSE;
1804 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1805 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1806 JSMSG_SEMI_BEFORE_STMNT);
1807 return JS_FALSE;
1809 (void) js_MatchToken(cx, ts, TOK_SEMI);
1810 return JS_TRUE;
1813 bool
1814 JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
1816 if (!markFunArgs(funbox, tcflags))
1817 return false;
1818 setFunctionKinds(funbox, tcflags);
1819 return true;
1823 * Mark as funargs any functions that reach up to one or more upvars across an
1824 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1826 * function f(o, p) {
1827 * o.m = function o_m(a) {
1828 * function g() { return p; }
1829 * function h() { return a; }
1830 * return g() + h();
1834 * but without this extra marking phase, function g will not be marked as a
1835 * funarg since it is called from within its parent scope. But g reaches up to
1836 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1837 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1838 * nor uses an upvar "above" o_m's level.
1840 * If function g itself contained lambdas that contained non-lambdas that reach
1841 * up above its level, then those non-lambdas would have to be marked too. This
1842 * process is potentially exponential in the number of functions, but generally
1843 * not so complex. But it can't be done during a single recursive traversal of
1844 * the funbox tree, so we must use a work queue.
1846 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1847 * between the static level of the bodies of funbox and its peers (which must
1848 * be funbox->level + 1), and the static level of the nearest upvar among all
1849 * the upvars contained by funbox and its peers. If there are no upvars, return
1850 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1852 static uintN
1853 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1855 uintN allskipmin = FREE_STATIC_LEVEL;
1857 do {
1858 JSParseNode *fn = funbox->node;
1859 JSFunction *fun = (JSFunction *) funbox->object;
1860 int fnlevel = level;
1863 * An eval can leak funbox, functions along its ancestor line, and its
1864 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1865 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1866 * already been marked as funargs by this point. Therefore we have to
1867 * flag only funbox->node and funbox->kids' nodes here.
1869 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1870 fn->setFunArg();
1871 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1872 kid->node->setFunArg();
1876 * Compute in skipmin the least distance from fun's static level up to
1877 * an upvar, whether used directly by fun, or indirectly by a function
1878 * nested in fun.
1880 uintN skipmin = FREE_STATIC_LEVEL;
1881 JSParseNode *pn = fn->pn_body;
1883 if (pn->pn_type == TOK_UPVARS) {
1884 JSAtomList upvars(pn->pn_names);
1885 JS_ASSERT(upvars.count != 0);
1887 JSAtomListIterator iter(&upvars);
1888 JSAtomListElement *ale;
1890 while ((ale = iter()) != NULL) {
1891 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1893 if (!lexdep->isFreeVar()) {
1894 uintN upvarLevel = lexdep->frameLevel();
1896 if (int(upvarLevel) <= fnlevel)
1897 fn->setFunArg();
1899 uintN skip = (funbox->level + 1) - upvarLevel;
1900 if (skip < skipmin)
1901 skipmin = skip;
1907 * If this function escapes, whether directly (the parser detects such
1908 * escapes) or indirectly (because this non-escaping function uses an
1909 * upvar that reaches across an outer function boundary where the outer
1910 * function escapes), enqueue it for further analysis, and bump fnlevel
1911 * to trap any non-escaping children.
1913 if (fn->isFunArg()) {
1914 queue->push(funbox);
1915 fnlevel = int(funbox->level);
1919 * Now process the current function's children, and recalibrate their
1920 * cumulative skipmin to be relative to the current static level.
1922 if (funbox->kids) {
1923 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1925 JS_ASSERT(kidskipmin != 0);
1926 if (kidskipmin != FREE_STATIC_LEVEL) {
1927 --kidskipmin;
1928 if (kidskipmin != 0 && kidskipmin < skipmin)
1929 skipmin = kidskipmin;
1934 * Finally, after we've traversed all of the current function's kids,
1935 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1936 * with allskipmin, but minimize across funbox and all of its siblings,
1937 * to compute our return value.
1939 if (skipmin != FREE_STATIC_LEVEL) {
1940 fun->u.i.skipmin = skipmin;
1941 if (skipmin < allskipmin)
1942 allskipmin = skipmin;
1944 } while ((funbox = funbox->siblings) != NULL);
1946 return allskipmin;
1949 bool
1950 JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1952 JSFunctionBoxQueue queue;
1953 if (!queue.init(functionCount))
1954 return false;
1956 FindFunArgs(funbox, -1, &queue);
1957 while ((funbox = queue.pull()) != NULL) {
1958 JSParseNode *fn = funbox->node;
1959 JS_ASSERT(fn->isFunArg());
1961 JSParseNode *pn = fn->pn_body;
1962 if (pn->pn_type == TOK_UPVARS) {
1963 JSAtomList upvars(pn->pn_names);
1964 JS_ASSERT(upvars.count != 0);
1966 JSAtomListIterator iter(&upvars);
1967 JSAtomListElement *ale;
1969 while ((ale = iter()) != NULL) {
1970 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1972 if (!lexdep->isFreeVar() &&
1973 !lexdep->isFunArg() &&
1974 lexdep->kind() == JSDefinition::FUNCTION) {
1976 * Mark this formerly-Algol-like function as an escaping
1977 * function (i.e., as a funarg), because it is used from a
1978 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1979 * access upvars.
1981 * Progress is guaranteed because we set the funarg flag
1982 * here, which suppresses revisiting this function (thanks
1983 * to the !lexdep->isFunArg() test just above).
1985 lexdep->setFunArg();
1987 JSFunctionBox *afunbox = lexdep->pn_funbox;
1988 queue.push(afunbox);
1991 * Walk over nested functions again, now that we have
1992 * changed the level across which it is unsafe to access
1993 * upvars using the runtime dynamic link (frame chain).
1995 if (afunbox->kids)
1996 FindFunArgs(afunbox->kids, afunbox->level, &queue);
2001 return true;
2004 static uint32
2005 MinBlockId(JSParseNode *fn, uint32 id)
2007 if (fn->pn_blockid < id)
2008 return false;
2009 if (fn->pn_defn) {
2010 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
2011 if (pn->pn_blockid < id)
2012 return false;
2015 return true;
2018 static bool
2019 OneBlockId(JSParseNode *fn, uint32 id)
2021 if (fn->pn_blockid != id)
2022 return false;
2023 if (fn->pn_defn) {
2024 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
2025 if (pn->pn_blockid != id)
2026 return false;
2029 return true;
2032 void
2033 JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
2035 #ifdef JS_FUNCTION_METERING
2036 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2037 #else
2038 # define FUN_METER(x) ((void)0)
2039 #endif
2040 JSFunctionBox *parent = funbox->parent;
2042 for (;;) {
2043 JSParseNode *fn = funbox->node;
2045 if (funbox->kids)
2046 setFunctionKinds(funbox->kids, tcflags);
2048 JSParseNode *pn = fn->pn_body;
2049 JSFunction *fun = (JSFunction *) funbox->object;
2051 FUN_METER(allfun);
2052 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2053 FUN_METER(heavy);
2054 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2055 } else if (pn->pn_type != TOK_UPVARS) {
2057 * No lexical dependencies => null closure, for best performance.
2058 * A null closure needs no scope chain, but alas we've coupled
2059 * principals-finding to scope (for good fundamental reasons, but
2060 * the implementation overloads the parent slot and we should fix
2061 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2063 * In more detail: the ES3 spec allows the implementation to create
2064 * "joined function objects", or not, at its discretion. But real-
2065 * world implementations always create unique function objects for
2066 * closures, and this can be detected via mutation. Open question:
2067 * do popular implementations create unique function objects for
2068 * null closures?
2070 * FIXME: bug 476950.
2072 FUN_METER(nofreeupvar);
2073 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2074 } else {
2075 JSAtomList upvars(pn->pn_names);
2076 JS_ASSERT(upvars.count != 0);
2078 JSAtomListIterator iter(&upvars);
2079 JSAtomListElement *ale;
2081 if (!fn->isFunArg()) {
2083 * This function is Algol-like, it never escapes. So long as it
2084 * does not assign to outer variables, it needs only an upvars
2085 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2086 * bytecode to reach up the frame stack at runtime based on
2087 * those upvars' cookies.
2089 * Any assignments to upvars from functions called by this one
2090 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2091 * which load from stack homes when interpreting or from native
2092 * stack slots when executing a trace.
2094 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2095 * nested function to assign to an outer lexical variable, so
2096 * we defer adding yet more code footprint in the absence of
2097 * evidence motivating these opcodes.
2099 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
2100 uintN nupvars = 0;
2103 * Check that at least one outer lexical binding was assigned
2104 * to (global variables don't count). This is conservative: we
2105 * could limit assignments to those in the current function,
2106 * but that's too much work. As with flat closures (handled
2107 * below), we optimize for the case where outer bindings are
2108 * not reassigned anywhere.
2110 while ((ale = iter()) != NULL) {
2111 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2113 if (!lexdep->isFreeVar()) {
2114 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2115 ++nupvars;
2116 if (lexdep->isAssigned())
2117 break;
2120 if (!ale)
2121 mutation = false;
2123 if (nupvars == 0) {
2124 FUN_METER(onlyfreevar);
2125 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2126 } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
2128 * Algol-like functions can read upvars using the dynamic
2129 * link (cx->fp/fp->down). They do not need to entrain and
2130 * search their environment.
2132 FUN_METER(display);
2133 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2134 } else {
2135 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
2136 FUN_METER(setupvar);
2138 } else {
2139 uintN nupvars = 0;
2142 * For each lexical dependency from this closure to an outer
2143 * binding, analyze whether it is safe to copy the binding's
2144 * value into a flat closure slot when the closure is formed.
2146 while ((ale = iter()) != NULL) {
2147 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2149 if (!lexdep->isFreeVar()) {
2150 ++nupvars;
2153 * Consider the current function (the lambda, innermost
2154 * below) using a var x defined two static levels up:
2156 * function f() {
2157 * // z = g();
2158 * var x = 42;
2159 * function g() {
2160 * return function () { return x; };
2162 * return g();
2165 * So long as (1) the initialization in 'var x = 42'
2166 * dominates all uses of g and (2) x is not reassigned,
2167 * it is safe to optimize the lambda to a flat closure.
2168 * Uncommenting the early call to g makes it unsafe to
2169 * so optimize (z could name a global setter that calls
2170 * its argument).
2172 JSFunctionBox *afunbox = funbox;
2173 uintN lexdepLevel = lexdep->frameLevel();
2175 JS_ASSERT(lexdepLevel <= funbox->level);
2176 while (afunbox->level != lexdepLevel) {
2177 afunbox = afunbox->parent;
2180 * afunbox can't be null because we are sure
2181 * to find a function box whose level == lexdepLevel
2182 * before walking off the top of the funbox tree.
2183 * See bug 493260 comments 16-18.
2185 * Assert but check anyway, to check future changes
2186 * that bind eval upvars in the parser.
2188 JS_ASSERT(afunbox);
2191 * If this function is reaching up across an
2192 * enclosing funarg, we cannot make a flat
2193 * closure. The display stops working once the
2194 * funarg escapes.
2196 if (!afunbox || afunbox->node->isFunArg())
2197 goto break2;
2201 * If afunbox's function (which is at the same level as
2202 * lexdep) is in a loop, pessimistically assume the
2203 * variable initializer may be in the same loop. A flat
2204 * closure would then be unsafe, as the captured
2205 * variable could be assigned after the closure is
2206 * created. See bug 493232.
2208 if (afunbox->inLoop)
2209 break;
2212 * with and eval defeat lexical scoping; eval anywhere
2213 * in a variable's scope can assign to it. Both defeat
2214 * the flat closure optimization. The parser detects
2215 * these cases and flags the function heavyweight.
2217 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags)
2218 & TCF_FUN_HEAVYWEIGHT) {
2219 break;
2223 * If afunbox's function is not a lambda, it will be
2224 * hoisted, so it could capture the undefined value
2225 * that by default initializes var/let/const
2226 * bindings. And if lexdep is a function that comes at
2227 * (meaning a function refers to its own name) or
2228 * strictly after afunbox, we also break to defeat the
2229 * flat closure optimization.
2231 JSFunction *afun = (JSFunction *) afunbox->object;
2232 if (!(afun->flags & JSFUN_LAMBDA)) {
2233 if (lexdep->isBindingForm())
2234 break;
2235 if (lexdep->pn_pos >= afunbox->node->pn_pos)
2236 break;
2239 if (!lexdep->isInitialized())
2240 break;
2242 JSDefinition::Kind lexdepKind = lexdep->kind();
2243 if (lexdepKind != JSDefinition::CONST) {
2244 if (lexdep->isAssigned())
2245 break;
2248 * Any formal could be mutated behind our back via
2249 * the arguments object, so deoptimize if the outer
2250 * function uses arguments.
2252 * In a Function constructor call where the final
2253 * argument -- the body source for the function to
2254 * create -- contains a nested function definition
2255 * or expression, afunbox->parent will be null. The
2256 * body source might use |arguments| outside of any
2257 * nested functions it may contain, so we have to
2258 * check the tcflags parameter that was passed in
2259 * from JSCompiler::compileFunctionBody.
2261 if (lexdepKind == JSDefinition::ARG &&
2262 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2263 TCF_FUN_USES_ARGUMENTS)) {
2264 break;
2269 * Check quick-and-dirty dominance relation. Function
2270 * definitions dominate their uses thanks to hoisting.
2271 * Other binding forms hoist as undefined, of course,
2272 * so check forward-reference and blockid relations.
2274 if (lexdepKind != JSDefinition::FUNCTION) {
2276 * Watch out for code such as
2278 * (function () {
2279 * ...
2280 * var jQuery = ... = function (...) {
2281 * return new jQuery.foo.bar(baz);
2283 * ...
2284 * })();
2286 * where the jQuery var is not reassigned, but of
2287 * course is not initialized at the time that the
2288 * would-be-flat closure containing the jQuery
2289 * upvar is formed.
2291 if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2292 break;
2294 if (lexdep->isTopLevel()
2295 ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2296 : !lexdep->isBlockChild() ||
2297 !afunbox->node->isBlockChild() ||
2298 !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2299 break;
2305 break2:
2306 if (nupvars == 0) {
2307 FUN_METER(onlyfreevar);
2308 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2309 } else if (!ale) {
2311 * We made it all the way through the upvar loop, so it's
2312 * safe to optimize to a flat closure.
2314 FUN_METER(flat);
2315 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2316 switch (PN_OP(fn)) {
2317 case JSOP_DEFFUN:
2318 fn->pn_op = JSOP_DEFFUN_FC;
2319 break;
2320 case JSOP_DEFLOCALFUN:
2321 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2322 break;
2323 case JSOP_LAMBDA:
2324 fn->pn_op = JSOP_LAMBDA_FC;
2325 break;
2326 default:
2327 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2328 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2330 } else {
2331 FUN_METER(badfunarg);
2336 if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2337 if (pn->pn_type != TOK_UPVARS) {
2338 if (parent)
2339 parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2340 } else {
2341 JSAtomList upvars(pn->pn_names);
2342 JS_ASSERT(upvars.count != 0);
2344 JSAtomListIterator iter(&upvars);
2345 JSAtomListElement *ale;
2348 * One or more upvars cannot be safely snapshot into a flat
2349 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2350 * all upvars, and for each non-free upvar, ensure that its
2351 * containing function has been flagged as heavyweight.
2353 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2354 * generating any code for a tree of nested functions.
2356 while ((ale = iter()) != NULL) {
2357 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2359 if (!lexdep->isFreeVar()) {
2360 JSFunctionBox *afunbox = funbox->parent;
2361 uintN lexdepLevel = lexdep->frameLevel();
2363 while (afunbox) {
2365 * NB: afunbox->level is the static level of
2366 * the definition or expression of the function
2367 * parsed into afunbox, not the static level of
2368 * its body. Therefore we must add 1 to match
2369 * lexdep's level to find the afunbox whose
2370 * body contains the lexdep definition.
2372 if (afunbox->level + 1U == lexdepLevel ||
2373 (lexdepLevel == 0 && lexdep->isLet())) {
2374 afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2375 break;
2377 afunbox = afunbox->parent;
2379 if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2380 tcflags |= TCF_FUN_HEAVYWEIGHT;
2386 funbox = funbox->siblings;
2387 if (!funbox)
2388 break;
2389 JS_ASSERT(funbox->parent == parent);
2391 #undef FUN_METER
2394 const char js_argument_str[] = "argument";
2395 const char js_variable_str[] = "variable";
2396 const char js_unknown_str[] = "unknown";
2398 const char *
2399 JSDefinition::kindString(Kind kind)
2401 static const char *table[] = {
2402 js_var_str, js_const_str, js_let_str,
2403 js_function_str, js_argument_str, js_unknown_str
2406 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2407 return table[kind];
2410 static JSFunctionBox *
2411 EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2412 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2414 JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2415 if (!fun)
2416 return NULL;
2418 /* Create box for fun->object early to protect against last-ditch GC. */
2419 JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2420 if (!funbox)
2421 return NULL;
2423 /* Initialize non-default members of funtc. */
2424 funtc->flags |= funbox->tcflags;
2425 funtc->blockidGen = tc->blockidGen;
2426 if (!GenerateBlockId(funtc, funtc->bodyid))
2427 return NULL;
2428 funtc->fun = fun;
2429 funtc->funbox = funbox;
2430 funtc->parent = tc;
2431 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2432 return NULL;
2434 return funbox;
2437 static bool
2438 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2439 JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2441 tc->blockidGen = funtc->blockidGen;
2443 fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2445 fn->pn_dflags |= PND_INITIALIZED;
2446 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2447 fn->pn_dflags & PND_TOPLEVEL);
2448 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2449 fn->pn_dflags |= PND_BLOCKCHILD;
2452 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2453 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2454 * params and body. We do this only if there are lexical dependencies not
2455 * satisfied by the function's declarations, to avoid penalizing functions
2456 * that use only their arguments and other local bindings.
2458 if (funtc->lexdeps.count != 0) {
2459 JSAtomListIterator iter(&funtc->lexdeps);
2460 JSAtomListElement *ale;
2461 int foundCallee = 0;
2463 while ((ale = iter()) != NULL) {
2464 JSAtom *atom = ALE_ATOM(ale);
2465 JSDefinition *dn = ALE_DEFN(ale);
2466 JS_ASSERT(dn->isPlaceholder());
2468 if (atom == funAtom && lambda != 0) {
2469 dn->pn_op = JSOP_CALLEE;
2470 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2471 dn->pn_dflags |= PND_BOUND;
2474 * If this named function expression uses its own name other
2475 * than to call itself, flag this function specially.
2477 if (dn->isFunArg())
2478 fn->pn_funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2479 foundCallee = 1;
2480 continue;
2483 if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2484 dn->isAssigned()) {
2486 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2487 * any use of dn in funtc assigns. See NoteLValue for the easy
2488 * backward-reference case; this is the hard forward-reference
2489 * case where we pay a higher price.
2491 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2492 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2493 fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2494 break;
2499 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2500 if (!outer_ale)
2501 outer_ale = tc->lexdeps.lookup(atom);
2502 if (outer_ale) {
2504 * Insert dn's uses list at the front of outer_dn's list.
2506 * Without loss of generality or correctness, we allow a dn to
2507 * be in inner and outer lexdeps, since the purpose of lexdeps
2508 * is one-pass coordination of name use and definition across
2509 * functions, and if different dn's are used we'll merge lists
2510 * when leaving the inner function.
2512 * The dn == outer_dn case arises with generator expressions
2513 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2514 * case), and nowhere else, currently.
2516 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2518 if (dn != outer_dn) {
2519 JSParseNode **pnup = &dn->dn_uses;
2520 JSParseNode *pnu;
2522 while ((pnu = *pnup) != NULL) {
2523 pnu->pn_lexdef = outer_dn;
2524 pnup = &pnu->pn_link;
2528 * Make dn be a use that redirects to outer_dn, because we
2529 * can't replace dn with outer_dn in all the pn_namesets in
2530 * the AST where it may be. Instead we make it forward to
2531 * outer_dn. See JSDefinition::resolve.
2533 *pnup = outer_dn->dn_uses;
2534 outer_dn->dn_uses = dn;
2535 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2536 dn->pn_defn = false;
2537 dn->pn_used = true;
2538 dn->pn_lexdef = outer_dn;
2540 } else {
2541 /* Add an outer lexical dependency for ale's definition. */
2542 outer_ale = tc->lexdeps.add(tc->compiler, atom);
2543 if (!outer_ale)
2544 return false;
2545 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2549 if (funtc->lexdeps.count - foundCallee != 0) {
2550 JSParseNode *body = fn->pn_body;
2552 fn->pn_body = NewParseNode(PN_NAMESET, tc);
2553 if (!fn->pn_body)
2554 return false;
2556 fn->pn_body->pn_type = TOK_UPVARS;
2557 fn->pn_body->pn_pos = body->pn_pos;
2558 if (foundCallee)
2559 funtc->lexdeps.remove(tc->compiler, funAtom);
2560 fn->pn_body->pn_names = funtc->lexdeps;
2561 fn->pn_body->pn_tree = body;
2564 funtc->lexdeps.clear();
2567 return true;
2570 static JSParseNode *
2571 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2572 uintN lambda)
2574 JSOp op;
2575 JSParseNode *pn, *body, *result;
2576 JSTokenType tt;
2577 JSAtom *funAtom;
2578 JSAtomListElement *ale;
2579 #if JS_HAS_DESTRUCTURING
2580 JSParseNode *item, *list = NULL;
2581 bool destructuringArg = false;
2582 JSAtom *duplicatedArg = NULL;
2583 #endif
2585 /* Make a TOK_FUNCTION node. */
2586 #if JS_HAS_GETTER_SETTER
2587 op = CURRENT_TOKEN(ts).t_op;
2588 #endif
2589 pn = NewParseNode(PN_FUNC, tc);
2590 if (!pn)
2591 return NULL;
2592 pn->pn_body = NULL;
2593 pn->pn_cookie = FREE_UPVAR_COOKIE;
2596 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2597 * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2599 * Also treat function sub-statements (non-lambda, non-top-level functions)
2600 * as escaping funargs, since we can't statically analyze their definitions
2601 * and uses.
2603 bool topLevel = tc->atTopLevel();
2604 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2606 /* Scan the optional function name into funAtom. */
2607 ts->flags |= TSF_KEYWORD_IS_NAME;
2608 tt = js_GetToken(cx, ts);
2609 ts->flags &= ~TSF_KEYWORD_IS_NAME;
2610 if (tt == TOK_NAME) {
2611 funAtom = CURRENT_TOKEN(ts).t_atom;
2612 } else {
2613 if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
2614 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2615 JSMSG_SYNTAX_ERROR);
2616 return NULL;
2618 funAtom = NULL;
2619 js_UngetToken(ts);
2623 * Record names for function statements in tc->decls so we know when to
2624 * avoid optimizing variable references that might name a function.
2626 if (lambda == 0 && funAtom) {
2627 ale = tc->decls.lookup(funAtom);
2628 if (ale) {
2629 JSDefinition *dn = ALE_DEFN(ale);
2630 JSDefinition::Kind dn_kind = dn->kind();
2632 JS_ASSERT(!dn->pn_used);
2633 JS_ASSERT(dn->pn_defn);
2635 if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2636 const char *name = js_AtomToPrintableString(cx, funAtom);
2637 if (!name ||
2638 !js_ReportCompileErrorNumber(cx, ts, NULL,
2639 (dn_kind != JSDefinition::CONST)
2640 ? JSREPORT_WARNING | JSREPORT_STRICT
2641 : JSREPORT_ERROR,
2642 JSMSG_REDECLARED_VAR,
2643 JSDefinition::kindString(dn_kind),
2644 name)) {
2645 return NULL;
2649 if (topLevel) {
2650 ALE_SET_DEFN(ale, pn);
2651 pn->pn_defn = true;
2652 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2654 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2655 return NULL;
2657 } else if (topLevel) {
2659 * If this function was used before it was defined, claim the
2660 * pre-created definition node for this function that PrimaryExpr
2661 * put in tc->lexdeps on first forward reference, and recycle pn.
2663 JSHashEntry **hep;
2665 ale = tc->lexdeps.rawLookup(funAtom, hep);
2666 if (ale) {
2667 JSDefinition *fn = ALE_DEFN(ale);
2669 JS_ASSERT(fn->pn_defn);
2670 fn->pn_type = TOK_FUNCTION;
2671 fn->pn_arity = PN_FUNC;
2672 fn->pn_pos.begin = pn->pn_pos.begin;
2673 fn->pn_body = NULL;
2674 fn->pn_cookie = FREE_UPVAR_COOKIE;
2676 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2677 RecycleTree(pn, tc);
2678 pn = fn;
2681 if (!Define(pn, funAtom, tc))
2682 return NULL;
2686 * A function nested at top level inside another's body needs only a
2687 * local variable to bind its name to its value, and not an activation
2688 * object property (it might also need the activation property, if the
2689 * outer function contains with statements, e.g., but the stack slot
2690 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2691 * JSOP_GETLOCAL bytecode).
2693 if (topLevel) {
2694 pn->pn_dflags |= PND_TOPLEVEL;
2696 if (tc->flags & TCF_IN_FUNCTION) {
2697 JSLocalKind localKind;
2698 uintN index;
2701 * Define a local in the outer function so that BindNameToSlot
2702 * can properly optimize accesses. Note that we need a local
2703 * variable, not an argument, for the function statement. Thus
2704 * we add a variable even if a parameter with the given name
2705 * already exists.
2707 localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2708 switch (localKind) {
2709 case JSLOCAL_NONE:
2710 case JSLOCAL_ARG:
2711 index = tc->fun->u.i.nvars;
2712 if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2713 return NULL;
2714 /* FALL THROUGH */
2716 case JSLOCAL_VAR:
2717 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2718 pn->pn_dflags |= PND_BOUND;
2719 break;
2721 default:;
2727 /* Initialize early for possible flags mutation via DestructuringExpr. */
2728 JSTreeContext funtc(tc->compiler);
2730 JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2731 if (!funbox)
2732 return NULL;
2734 JSFunction *fun = (JSFunction *) funbox->object;
2736 #if JS_HAS_GETTER_SETTER
2737 if (op != JSOP_NOP)
2738 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2739 #endif
2741 /* Now parse formal argument list and compute fun->nargs. */
2742 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2743 if (!js_MatchToken(cx, ts, TOK_RP)) {
2744 do {
2745 tt = js_GetToken(cx, ts);
2746 switch (tt) {
2747 #if JS_HAS_DESTRUCTURING
2748 case TOK_LB:
2749 case TOK_LC:
2751 BindData data;
2752 JSParseNode *lhs, *rhs;
2753 jsint slot;
2755 /* See comment below in the TOK_NAME case. */
2756 if (duplicatedArg)
2757 goto report_dup_and_destructuring;
2758 destructuringArg = true;
2761 * A destructuring formal parameter turns into one or more
2762 * local variables initialized from properties of a single
2763 * anonymous positional parameter, so here we must tweak our
2764 * binder and its data.
2766 data.pn = NULL;
2767 data.op = JSOP_DEFVAR;
2768 data.binder = BindDestructuringArg;
2769 lhs = DestructuringExpr(cx, &data, &funtc, tt);
2770 if (!lhs)
2771 return NULL;
2774 * Adjust fun->nargs to count the single anonymous positional
2775 * parameter that is to be destructured.
2777 slot = fun->nargs;
2778 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
2779 return NULL;
2782 * Synthesize a destructuring assignment from the single
2783 * anonymous positional parameter into the destructuring
2784 * left-hand-side expression and accumulate it in list.
2786 rhs = NewNameNode(cx, cx->runtime->atomState.emptyAtom, &funtc);
2787 if (!rhs)
2788 return NULL;
2789 rhs->pn_type = TOK_NAME;
2790 rhs->pn_op = JSOP_GETARG;
2791 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2792 rhs->pn_dflags |= PND_BOUND;
2794 item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2795 if (!item)
2796 return NULL;
2797 if (!list) {
2798 list = NewParseNode(PN_LIST, &funtc);
2799 if (!list)
2800 return NULL;
2801 list->pn_type = TOK_COMMA;
2802 list->makeEmpty();
2804 list->append(item);
2805 break;
2807 #endif /* JS_HAS_DESTRUCTURING */
2809 case TOK_NAME:
2811 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2812 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2813 return NULL;
2814 #ifdef JS_HAS_DESTRUCTURING
2816 * ECMA-262 requires us to support duplicate parameter names, but if the
2817 * parameter list includes destructuring, we consider the code to have
2818 * opted in to higher standards, and forbid duplicates. We may see a
2819 * destructuring parameter later, so always note duplicates now.
2821 * Duplicates are warned about (strict option) or cause errors (strict
2822 * mode code), but we do those tests in one place below, after having
2823 * parsed the body.
2825 if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2826 duplicatedArg = atom;
2827 if (destructuringArg)
2828 goto report_dup_and_destructuring;
2830 #endif
2831 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2832 return NULL;
2833 break;
2836 default:
2837 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2838 JSMSG_MISSING_FORMAL);
2839 /* FALL THROUGH */
2840 case TOK_ERROR:
2841 return NULL;
2843 #if JS_HAS_DESTRUCTURING
2844 report_dup_and_destructuring:
2845 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
2846 js_ReportCompileErrorNumber(cx, TS(tc->compiler), dn,
2847 JSREPORT_ERROR,
2848 JSMSG_DESTRUCT_DUP_ARG);
2849 return NULL;
2850 #endif
2852 } while (js_MatchToken(cx, ts, TOK_COMMA));
2854 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2857 #if JS_HAS_EXPR_CLOSURES
2858 ts->flags |= TSF_OPERAND;
2859 tt = js_GetToken(cx, ts);
2860 ts->flags &= ~TSF_OPERAND;
2861 if (tt != TOK_LC) {
2862 js_UngetToken(ts);
2863 fun->flags |= JSFUN_EXPR_CLOSURE;
2865 #else
2866 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2867 #endif
2869 body = FunctionBody(cx, ts, &funtc);
2870 if (!body)
2871 return NULL;
2873 if (!CheckStrictBinding(cx, &funtc, funAtom, pn))
2874 return NULL;
2876 if (!CheckStrictFormals(cx, &funtc, fun, pn))
2877 return NULL;
2879 #if JS_HAS_EXPR_CLOSURES
2880 if (tt == TOK_LC)
2881 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2882 else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2883 return NULL;
2884 #else
2885 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2886 #endif
2887 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2889 #if JS_HAS_DESTRUCTURING
2891 * If there were destructuring formal parameters, prepend the initializing
2892 * comma expression that we synthesized to body. If the body is a lexical
2893 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2894 * parameter destructuring code without bracing the decompilation of the
2895 * function body's lexical scope.
2897 if (list) {
2898 if (body->pn_arity != PN_LIST) {
2899 JSParseNode *block;
2901 block = NewParseNode(PN_LIST, tc);
2902 if (!block)
2903 return NULL;
2904 block->pn_type = TOK_SEQ;
2905 block->pn_pos = body->pn_pos;
2906 block->initList(body);
2908 body = block;
2911 item = NewParseNode(PN_UNARY, tc);
2912 if (!item)
2913 return NULL;
2915 item->pn_type = TOK_SEMI;
2916 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2917 item->pn_kid = list;
2918 item->pn_next = body->pn_head;
2919 body->pn_head = item;
2920 if (body->pn_tail == &body->pn_head)
2921 body->pn_tail = &item->pn_next;
2922 ++body->pn_count;
2923 body->pn_xflags |= PNX_DESTRUCT;
2925 #endif
2928 * If we collected flags that indicate nested heavyweight functions, or
2929 * this function contains heavyweight-making statements (with statement,
2930 * visible eval call, or assignment to 'arguments'), flag the function as
2931 * heavyweight (requiring a call object per invocation).
2933 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2934 fun->flags |= JSFUN_HEAVYWEIGHT;
2935 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2936 } else {
2938 * If this function is a named statement function not at top-level
2939 * (i.e. not a top-level function definiton or expression), then our
2940 * enclosing function, if any, must be heavyweight.
2942 if (!topLevel && lambda == 0 && funAtom)
2943 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2946 result = pn;
2947 if (lambda != 0) {
2949 * ECMA ed. 3 standard: function expression, possibly anonymous.
2951 op = JSOP_LAMBDA;
2952 } else if (!funAtom) {
2954 * If this anonymous function definition is *not* embedded within a
2955 * larger expression, we treat it as an expression statement, not as
2956 * a function declaration -- and not as a syntax error (as ECMA-262
2957 * Edition 3 would have it). Backward compatibility must trump all,
2958 * unless JSOPTION_ANONFUNFIX is set.
2960 result = NewParseNode(PN_UNARY, tc);
2961 if (!result)
2962 return NULL;
2963 result->pn_type = TOK_SEMI;
2964 result->pn_pos = pn->pn_pos;
2965 result->pn_kid = pn;
2966 op = JSOP_LAMBDA;
2967 } else if (!topLevel) {
2969 * ECMA ed. 3 extension: a function expression statement not at the
2970 * top level, e.g., in a compound statement such as the "then" part
2971 * of an "if" statement, binds a closure only if control reaches that
2972 * sub-statement.
2974 op = JSOP_DEFFUN;
2975 } else {
2976 op = JSOP_NOP;
2979 funbox->kids = funtc.functionList;
2981 pn->pn_funbox = funbox;
2982 pn->pn_op = op;
2983 if (pn->pn_body) {
2984 pn->pn_body->append(body);
2985 pn->pn_body->pn_pos = body->pn_pos;
2986 } else {
2987 pn->pn_body = body;
2990 pn->pn_blockid = tc->blockid();
2992 if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2993 return NULL;
2995 /* If the surrounding function is not strict code, reset the lexer. */
2996 if (!(tc->flags & TCF_STRICT_MODE_CODE))
2997 ts->flags &= ~TSF_STRICT_MODE_CODE;
2999 return result;
3002 static JSParseNode *
3003 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3005 return FunctionDef(cx, ts, tc, 0);
3008 static JSParseNode *
3009 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3011 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
3015 * Recognize Directive Prologue members and directives. Assuming pn
3016 * is a candidate for membership in a directive prologue, return
3017 * true if it is in fact a member. Recognize directives and set
3018 * tc's flags accordingly.
3020 * Note that the following is a strict mode function:
3022 * function foo() {
3023 * "blah" // inserted semi colon
3024 * "blurgh"
3025 * "use\x20loose"
3026 * "use strict"
3029 * That is, a statement can be a Directive Prologue member, even
3030 * if it can't possibly be a directive, now or in the future.
3032 static bool
3033 RecognizeDirectivePrologue(JSContext *cx, JSTokenStream *ts,
3034 JSTreeContext *tc, JSParseNode *pn)
3036 if (!pn->isDirectivePrologueMember())
3037 return false;
3038 if (pn->isDirective()) {
3039 JSAtom *directive = pn->pn_kid->pn_atom;
3040 if (directive == cx->runtime->atomState.useStrictAtom) {
3041 tc->flags |= TCF_STRICT_MODE_CODE;
3042 ts->flags |= TSF_STRICT_MODE_CODE;
3045 return true;
3049 * Parse the statements in a block, creating a TOK_LC node that lists the
3050 * statements' trees. If called from block-parsing code, the caller must
3051 * match { before and } after.
3053 static JSParseNode *
3054 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3056 JSParseNode *pn, *pn2, *saveBlock;
3057 JSTokenType tt;
3058 bool inDirectivePrologue = tc->atTopLevel();
3060 JS_CHECK_RECURSION(cx, return NULL);
3062 pn = NewParseNode(PN_LIST, tc);
3063 if (!pn)
3064 return NULL;
3065 pn->pn_type = TOK_LC;
3066 pn->makeEmpty();
3067 pn->pn_blockid = tc->blockid();
3068 saveBlock = tc->blockNode;
3069 tc->blockNode = pn;
3071 for (;;) {
3072 ts->flags |= TSF_OPERAND;
3073 tt = js_PeekToken(cx, ts);
3074 ts->flags &= ~TSF_OPERAND;
3075 if (tt <= TOK_EOF || tt == TOK_RC) {
3076 if (tt == TOK_ERROR) {
3077 if (ts->flags & TSF_EOF)
3078 ts->flags |= TSF_UNEXPECTED_EOF;
3079 return NULL;
3081 break;
3083 pn2 = Statement(cx, ts, tc);
3084 if (!pn2) {
3085 if (ts->flags & TSF_EOF)
3086 ts->flags |= TSF_UNEXPECTED_EOF;
3087 return NULL;
3090 if (inDirectivePrologue) {
3091 if (RecognizeDirectivePrologue(cx, ts, tc, pn2)) {
3092 /* A Directive Prologue member is dead code. Omit it from the statement list. */
3093 RecycleTree(pn2, tc);
3094 continue;
3095 } else {
3096 inDirectivePrologue = false;
3100 if (pn2->pn_type == TOK_FUNCTION) {
3102 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3103 * level function definitions that should be processed before the
3104 * rest of nodes.
3106 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3107 * is relevant only for function definitions not at top-level,
3108 * which we call function statements.
3110 if (tc->atTopLevel())
3111 pn->pn_xflags |= PNX_FUNCDEFS;
3112 else
3113 tc->flags |= TCF_HAS_FUNCTION_STMT;
3115 pn->append(pn2);
3119 * Handle the case where there was a let declaration under this block. If
3120 * it replaced tc->blockNode with a new block node then we must refresh pn
3121 * and then restore tc->blockNode.
3123 if (tc->blockNode != pn)
3124 pn = tc->blockNode;
3125 tc->blockNode = saveBlock;
3127 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3128 return pn;
3131 static JSParseNode *
3132 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3134 JSParseNode *pn;
3136 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3137 pn = ParenExpr(cx, ts, tc, NULL, NULL);
3138 if (!pn)
3139 return NULL;
3140 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3142 /* Check for (a = b) and warn about possible (a == b) mistype. */
3143 if (pn->pn_type == TOK_ASSIGN &&
3144 pn->pn_op == JSOP_NOP &&
3145 !pn->pn_parens &&
3146 !js_ReportCompileErrorNumber(cx, ts, NULL,
3147 JSREPORT_WARNING | JSREPORT_STRICT,
3148 JSMSG_EQUAL_AS_ASSIGN,
3149 "")) {
3150 return NULL;
3152 return pn;
3155 static JSBool
3156 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
3158 JSAtom *label;
3159 JSTokenType tt;
3161 tt = js_PeekTokenSameLine(cx, ts);
3162 if (tt == TOK_ERROR)
3163 return JS_FALSE;
3164 if (tt == TOK_NAME) {
3165 (void) js_GetToken(cx, ts);
3166 label = CURRENT_TOKEN(ts).t_atom;
3167 } else {
3168 label = NULL;
3170 pn->pn_atom = label;
3171 return JS_TRUE;
3174 static JSBool
3175 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3177 JSParseNode *pn;
3178 JSObject *blockObj;
3179 JSAtomListElement *ale;
3180 jsint n;
3183 * Top-level 'let' is the same as 'var' currently -- this may change in a
3184 * successor standard to ES3.1 that specifies 'let'.
3186 JS_ASSERT(!tc->atTopLevel());
3188 pn = data->pn;
3189 if (!CheckStrictBinding(cx, tc, atom, pn))
3190 return false;
3192 blockObj = tc->blockChain;
3193 ale = tc->decls.lookup(atom);
3194 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3195 const char *name = js_AtomToPrintableString(cx, atom);
3196 if (name) {
3197 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3198 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3199 (ale && ALE_DEFN(ale)->isConst())
3200 ? js_const_str
3201 : js_variable_str,
3202 name);
3204 return JS_FALSE;
3207 n = OBJ_BLOCK_COUNT(cx, blockObj);
3208 if (n == JS_BIT(16)) {
3209 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3210 JSREPORT_ERROR, data->let.overflow);
3211 return JS_FALSE;
3215 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3216 * This is balanced by PopStatement, defined immediately below.
3218 if (!Define(pn, atom, tc, true))
3219 return JS_FALSE;
3222 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3223 * upvar cookie whose skip tells the current static level. The emitter will
3224 * adjust the node's slot based on its stack depth model -- and, for global
3225 * and eval code, JSCompiler::compileScript will adjust the slot again to
3226 * include script->nfixed.
3228 pn->pn_op = JSOP_GETLOCAL;
3229 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3230 pn->pn_dflags |= PND_LET | PND_BOUND;
3233 * Define the let binding's property before storing pn in a reserved slot,
3234 * since block_reserveSlots depends on OBJ_SCOPE(blockObj)->entryCount.
3236 if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
3237 return JS_FALSE;
3240 * Store pn temporarily in what would be reserved slots in a cloned block
3241 * object (once the prototype's final population is known, after all 'let'
3242 * bindings for this block have been parsed). We will free these reserved
3243 * slots in jsemit.cpp:EmitEnterBlock.
3245 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3246 if (slot >= STOBJ_NSLOTS(blockObj) &&
3247 !js_GrowSlots(cx, blockObj, slot + 1)) {
3248 return JS_FALSE;
3250 OBJ_SCOPE(blockObj)->freeslot = slot + 1;
3251 STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn));
3252 return JS_TRUE;
3255 static void
3256 PopStatement(JSTreeContext *tc)
3258 JSStmtInfo *stmt = tc->topStmt;
3260 if (stmt->flags & SIF_SCOPE) {
3261 JSObject *obj = stmt->blockObj;
3262 JSScope *scope = OBJ_SCOPE(obj);
3263 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3265 for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
3266 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3268 /* Beware the empty destructuring dummy. */
3269 if (atom == tc->compiler->context->runtime->atomState.emptyAtom)
3270 continue;
3271 tc->decls.remove(tc->compiler, atom);
3275 * The block scope will not be modified again. It may be shared. Clear
3276 * scope->object to make scope->owned() false.
3278 scope->object = NULL;
3280 js_PopStatement(tc);
3283 static inline bool
3284 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3286 while (stmt->downScope) {
3287 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3288 if (!stmt)
3289 return false;
3290 if (stmt->type == STMT_BLOCK)
3291 return true;
3293 return false;
3296 static JSBool
3297 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3299 JSParseNode *pn = data->pn;
3301 if (!CheckStrictBinding(cx, tc, atom, pn))
3302 return false;
3304 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3306 if (stmt && stmt->type == STMT_WITH) {
3307 pn->pn_op = JSOP_NAME;
3308 data->fresh = false;
3309 return JS_TRUE;
3312 JSAtomListElement *ale = tc->decls.lookup(atom);
3313 JSOp op = data->op;
3315 if (stmt || ale) {
3316 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3317 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3318 const char *name;
3320 if (dn_kind == JSDefinition::ARG) {
3321 name = js_AtomToPrintableString(cx, atom);
3322 if (!name)
3323 return JS_FALSE;
3325 if (op == JSOP_DEFCONST) {
3326 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3327 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3328 name);
3329 return JS_FALSE;
3331 if (!js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3332 JSREPORT_WARNING | JSREPORT_STRICT,
3333 JSMSG_VAR_HIDES_ARG, name)) {
3334 return JS_FALSE;
3336 } else {
3337 bool error = (op == JSOP_DEFCONST ||
3338 dn_kind == JSDefinition::CONST ||
3339 (dn_kind == JSDefinition::LET &&
3340 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3342 if (JS_HAS_STRICT_OPTION(cx)
3343 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3344 : error) {
3345 name = js_AtomToPrintableString(cx, atom);
3346 if (!name ||
3347 !js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3348 !error
3349 ? JSREPORT_WARNING | JSREPORT_STRICT
3350 : JSREPORT_ERROR,
3351 JSMSG_REDECLARED_VAR,
3352 JSDefinition::kindString(dn_kind),
3353 name)) {
3354 return JS_FALSE;
3360 if (!ale) {
3361 if (!Define(pn, atom, tc))
3362 return JS_FALSE;
3363 } else {
3365 * A var declaration never recreates an existing binding, it restates
3366 * it and possibly reinitializes its value. Beware that if pn becomes a
3367 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3368 * const (typically a const would ;-), then pn must be rewritten into a
3369 * TOK_ASSIGN node. See Variables, further below.
3371 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3372 * There the x definition is hoisted but the x = 2 assignment mutates
3373 * the block-local binding of x.
3375 JSDefinition *dn = ALE_DEFN(ale);
3377 data->fresh = false;
3379 if (!pn->pn_used) {
3380 /* Make pnu be a fresh name node that uses dn. */
3381 JSParseNode *pnu = pn;
3383 if (pn->pn_defn) {
3384 pnu = NewNameNode(cx, atom, tc);
3385 if (!pnu)
3386 return JS_FALSE;
3389 LinkUseToDef(pnu, dn, tc);
3390 pnu->pn_op = JSOP_NAME;
3393 while (dn->kind() == JSDefinition::LET) {
3394 do {
3395 ale = ALE_NEXT(ale);
3396 } while (ale && ALE_ATOM(ale) != atom);
3397 if (!ale)
3398 break;
3399 dn = ALE_DEFN(ale);
3402 if (ale) {
3403 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3404 dn->kind() == JSDefinition::CONST);
3405 return JS_TRUE;
3409 * A var or const that is shadowed by one or more let bindings of the
3410 * same name, but that has not been declared until this point, must be
3411 * hoisted above the let bindings.
3413 if (!pn->pn_defn) {
3414 JSHashEntry **hep;
3416 ale = tc->lexdeps.rawLookup(atom, hep);
3417 if (ale) {
3418 pn = ALE_DEFN(ale);
3419 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
3420 } else {
3421 JSParseNode *pn2 = NewNameNode(cx, atom, tc);
3422 if (!pn2)
3423 return JS_FALSE;
3425 /* The token stream may be past the location for pn. */
3426 pn2->pn_type = TOK_NAME;
3427 pn2->pn_pos = pn->pn_pos;
3428 pn = pn2;
3430 pn->pn_op = JSOP_NAME;
3433 ale = tc->decls.add(tc->compiler, atom, JSAtomList::HOIST);
3434 if (!ale)
3435 return JS_FALSE;
3436 ALE_SET_DEFN(ale, pn);
3437 pn->pn_defn = true;
3438 pn->pn_dflags &= ~PND_PLACEHOLDER;
3441 if (data->op == JSOP_DEFCONST)
3442 pn->pn_dflags |= PND_CONST;
3444 if (!(tc->flags & TCF_IN_FUNCTION)) {
3446 * If we are generating global or eval-called-from-global code, bind a
3447 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3448 * up global variable access by memoizing name-to-slot mappings in the
3449 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3450 * can't be done due to a pre-existing property of the same name as the
3451 * var or const but incompatible attributes/getter/setter/etc, these
3452 * ops devolve to JSOP_NAME, etc.
3454 * For now, don't try to lookup eval frame variables at compile time.
3455 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3456 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3458 pn->pn_op = JSOP_NAME;
3459 if ((tc->flags & TCF_COMPILING) && !tc->compiler->callerFrame) {
3460 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3462 /* Index atom so we can map fast global number to name. */
3463 ale = cg->atomList.add(tc->compiler, atom);
3464 if (!ale)
3465 return JS_FALSE;
3467 /* Defend against cg->ngvars 16-bit overflow. */
3468 uintN slot = ALE_INDEX(ale);
3469 if ((slot + 1) >> 16)
3470 return JS_TRUE;
3472 if ((uint16)(slot + 1) > cg->ngvars)
3473 cg->ngvars = (uint16)(slot + 1);
3475 pn->pn_op = JSOP_GETGVAR;
3476 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3477 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3479 return JS_TRUE;
3482 if (atom == cx->runtime->atomState.argumentsAtom) {
3483 pn->pn_op = JSOP_ARGUMENTS;
3484 pn->pn_dflags |= PND_BOUND;
3485 return JS_TRUE;
3488 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3489 if (localKind == JSLOCAL_NONE) {
3491 * Property not found in current variable scope: we have not seen this
3492 * variable before. Define a new local variable by adding a property to
3493 * the function's scope and allocating one slot in the function's vars
3494 * frame. Any locals declared in a with statement body are handled at
3495 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3496 * and heavyweight-function-local vars.
3498 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3500 uintN index = tc->fun->u.i.nvars;
3501 if (!BindLocalVariable(cx, tc->fun, atom, localKind, false))
3502 return JS_FALSE;
3503 pn->pn_op = JSOP_GETLOCAL;
3504 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3505 pn->pn_dflags |= PND_BOUND;
3506 return JS_TRUE;
3509 if (localKind == JSLOCAL_ARG) {
3510 /* We checked errors and strict warnings earlier -- see above. */
3511 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3512 } else {
3513 /* Not an argument, must be a redeclared local var. */
3514 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3516 pn->pn_op = JSOP_NAME;
3517 return JS_TRUE;
3520 static JSBool
3521 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3523 JSParseNode *pn2;
3525 JS_ASSERT(pn->pn_arity == PN_LIST);
3526 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3527 pn2 = pn->pn_head;
3528 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3529 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR, msg);
3530 return JS_FALSE;
3532 pn->pn_op = JSOP_SETCALL;
3533 return JS_TRUE;
3536 static void
3537 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3539 if (pn->pn_used) {
3540 JSDefinition *dn = pn->pn_lexdef;
3543 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3544 * occur as direct kids of the same block with no forward refs to x.
3546 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3547 dn->isBlockChild() &&
3548 pn->isBlockChild() &&
3549 dn->pn_blockid == pn->pn_blockid &&
3550 dn->pn_pos.end <= pn->pn_pos.begin &&
3551 dn->dn_uses == pn) {
3552 dflag = PND_INITIALIZED;
3555 dn->pn_dflags |= dflag;
3557 if (dn->frameLevel() != tc->staticLevel) {
3559 * The above condition takes advantage of the all-ones nature of
3560 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3561 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3563 JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3564 dn->frameLevel() < tc->staticLevel);
3565 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3569 pn->pn_dflags |= dflag;
3571 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3572 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3575 #if JS_HAS_DESTRUCTURING
3577 static JSBool
3578 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3579 JSTreeContext *tc)
3581 JSAtom *atom;
3584 * Destructuring is a form of assignment, so just as for an initialized
3585 * simple variable, we must check for assignment to 'arguments' and flag
3586 * the enclosing function (if any) as heavyweight.
3588 JS_ASSERT(pn->pn_type == TOK_NAME);
3589 atom = pn->pn_atom;
3590 if (atom == cx->runtime->atomState.argumentsAtom)
3591 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3593 data->pn = pn;
3594 if (!data->binder(cx, data, atom, tc))
3595 return JS_FALSE;
3598 * Select the appropriate name-setting opcode, respecting eager selection
3599 * done by the data->binder function.
3601 if (pn->pn_dflags & PND_BOUND) {
3602 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3603 ? JSOP_SETNAME
3604 : (pn->pn_dflags & PND_GVAR)
3605 ? JSOP_SETGVAR
3606 : JSOP_SETLOCAL;
3607 } else {
3608 pn->pn_op = (data->op == JSOP_DEFCONST)
3609 ? JSOP_SETCONST
3610 : JSOP_SETNAME;
3613 if (data->op == JSOP_DEFCONST)
3614 pn->pn_dflags |= PND_CONST;
3616 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3617 return JS_TRUE;
3621 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3622 * LHS expression except a destructuring initialiser, and R is on the stack.
3623 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3624 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3625 * then push its property name QN. At this point the stack looks like
3627 * [... R, R[P], QB, QN]
3629 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3630 * its operands with left-hand side above right-hand side:
3632 * [rval, lval, xval]
3634 * and pops all three values, setting lval[xval] = rval. But we cannot select
3635 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3636 * which can be optimized further. So we select JSOP_SETNAME.
3638 static JSBool
3639 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3641 switch (pn->pn_type) {
3642 case TOK_NAME:
3643 NoteLValue(cx, pn, tc);
3644 /* FALL THROUGH */
3646 case TOK_DOT:
3647 case TOK_LB:
3648 pn->pn_op = JSOP_SETNAME;
3649 break;
3651 case TOK_LP:
3652 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3653 return JS_FALSE;
3654 break;
3656 #if JS_HAS_XML_SUPPORT
3657 case TOK_UNARYOP:
3658 if (pn->pn_op == JSOP_XMLNAME) {
3659 pn->pn_op = JSOP_BINDXMLNAME;
3660 break;
3662 /* FALL THROUGH */
3663 #endif
3665 default:
3666 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3667 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3668 return JS_FALSE;
3671 return JS_TRUE;
3674 typedef struct FindPropValData {
3675 uint32 numvars; /* # of destructuring vars in left side */
3676 uint32 maxstep; /* max # of steps searching right side */
3677 JSDHashTable table; /* hash table for O(1) right side search */
3678 } FindPropValData;
3680 typedef struct FindPropValEntry {
3681 JSDHashEntryHdr hdr;
3682 JSParseNode *pnkey;
3683 JSParseNode *pnval;
3684 } FindPropValEntry;
3686 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3687 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3688 ((pnkey)->pn_type == TOK_NUMBER || \
3689 (pnkey)->pn_type == TOK_STRING || \
3690 (pnkey)->pn_type == TOK_NAME)) || \
3691 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3693 static JSDHashNumber
3694 HashFindPropValKey(JSDHashTable *table, const void *key)
3696 const JSParseNode *pnkey = (const JSParseNode *)key;
3698 ASSERT_VALID_PROPERTY_KEY(pnkey);
3699 return (pnkey->pn_type == TOK_NUMBER)
3700 ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
3701 : ATOM_HASH(pnkey->pn_atom);
3704 static JSBool
3705 MatchFindPropValEntry(JSDHashTable *table,
3706 const JSDHashEntryHdr *entry,
3707 const void *key)
3709 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3710 const JSParseNode *pnkey = (const JSParseNode *)key;
3712 ASSERT_VALID_PROPERTY_KEY(pnkey);
3713 return pnkey->pn_type == fpve->pnkey->pn_type &&
3714 ((pnkey->pn_type == TOK_NUMBER)
3715 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3716 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3719 static const JSDHashTableOps FindPropValOps = {
3720 JS_DHashAllocTable,
3721 JS_DHashFreeTable,
3722 HashFindPropValKey,
3723 MatchFindPropValEntry,
3724 JS_DHashMoveEntryStub,
3725 JS_DHashClearEntryStub,
3726 JS_DHashFinalizeStub,
3727 NULL
3730 #define STEP_HASH_THRESHOLD 10
3731 #define BIG_DESTRUCTURING 5
3732 #define BIG_OBJECT_INIT 20
3734 static JSParseNode *
3735 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3737 FindPropValEntry *entry;
3738 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3739 uint32 step;
3741 /* If we have a hash table, use it as the sole source of truth. */
3742 if (data->table.ops) {
3743 entry = (FindPropValEntry *)
3744 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3745 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3748 /* If pn is not an object initialiser node, we can't do anything here. */
3749 if (pn->pn_type != TOK_RC)
3750 return NULL;
3753 * We must search all the way through pn's list, to handle the case of an
3754 * id duplicated for two or more property initialisers.
3756 pnhit = NULL;
3757 step = 0;
3758 ASSERT_VALID_PROPERTY_KEY(pnid);
3759 pnhead = pn->pn_head;
3760 if (pnid->pn_type == TOK_NUMBER) {
3761 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3762 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3763 if (pnprop->pn_op == JSOP_NOP) {
3764 pnkey = pnprop->pn_left;
3765 ASSERT_VALID_PROPERTY_KEY(pnkey);
3766 if (pnkey->pn_type == TOK_NUMBER &&
3767 pnkey->pn_dval == pnid->pn_dval) {
3768 pnhit = pnprop;
3770 ++step;
3773 } else {
3774 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3775 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3776 if (pnprop->pn_op == JSOP_NOP) {
3777 pnkey = pnprop->pn_left;
3778 ASSERT_VALID_PROPERTY_KEY(pnkey);
3779 if (pnkey->pn_type == pnid->pn_type &&
3780 pnkey->pn_atom == pnid->pn_atom) {
3781 pnhit = pnprop;
3783 ++step;
3787 if (!pnhit)
3788 return NULL;
3790 /* Hit via full search -- see whether it's time to create the hash table. */
3791 JS_ASSERT(!data->table.ops);
3792 if (step > data->maxstep) {
3793 data->maxstep = step;
3794 if (step >= STEP_HASH_THRESHOLD &&
3795 data->numvars >= BIG_DESTRUCTURING &&
3796 pn->pn_count >= BIG_OBJECT_INIT &&
3797 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3798 sizeof(FindPropValEntry),
3799 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3801 for (pn = pnhead; pn; pn = pn->pn_next) {
3802 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3803 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3804 entry = (FindPropValEntry *)
3805 JS_DHashTableOperate(&data->table, pn->pn_left,
3806 JS_DHASH_ADD);
3807 entry->pnval = pn->pn_right;
3811 return pnhit->pn_right;
3815 * Destructuring patterns can appear in two kinds of contexts:
3817 * - assignment-like: assignment expressions and |for| loop heads. In
3818 * these cases, the patterns' property value positions can be
3819 * arbitrary lvalue expressions; the destructuring is just a fancy
3820 * assignment.
3822 * - declaration-like: |var| and |let| declarations, functions' formal
3823 * parameter lists, |catch| clauses, and comprehension tails. In
3824 * these cases, the patterns' property value positions must be
3825 * simple names; the destructuring defines them as new variables.
3827 * In both cases, other code parses the pattern as an arbitrary
3828 * PrimaryExpr, and then, here in CheckDestructuring, verify that the
3829 * tree is a valid destructuring expression.
3831 * In assignment-like contexts, we parse the pattern with the
3832 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3833 * pattern are parsed normally. PrimaryExpr links variable references
3834 * into the appropriate use chains; creates placeholder definitions;
3835 * and so on. CheckDestructuring is called with |data| NULL (since we
3836 * won't be binding any new names), and we specialize lvalues as
3837 * appropriate. If right is NULL, we just check for well-formed lvalues.
3839 * In declaration-like contexts, the normal variable reference
3840 * processing would just be an obstruction, because we're going to
3841 * define the names that appear in the property value positions as new
3842 * variables anyway. In this case, we parse the pattern with
3843 * TCF_DECL_DESTRUCTURING set, which directs PrimaryExpr to leave
3844 * whatever name nodes it creates unconnected. Then, here in
3845 * CheckDestructuring, we require the pattern's property value
3846 * positions to be simple names, and define them as appropriate to the
3847 * context. For these calls, |data| points to the right sort of
3848 * BindData.
3850 * See also UndominateInitializers, immediately below. If you change
3851 * either of these functions, you might have to change the other to
3852 * match.
3854 static JSBool
3855 CheckDestructuring(JSContext *cx, BindData *data,
3856 JSParseNode *left, JSParseNode *right,
3857 JSTreeContext *tc)
3859 JSBool ok;
3860 FindPropValData fpvd;
3861 JSParseNode *lhs, *rhs, *pn, *pn2;
3863 if (left->pn_type == TOK_ARRAYCOMP) {
3864 js_ReportCompileErrorNumber(cx, TS(tc->compiler), left,
3865 JSREPORT_ERROR, JSMSG_ARRAY_COMP_LEFTSIDE);
3866 return JS_FALSE;
3869 #if JS_HAS_DESTRUCTURING_SHORTHAND
3870 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3871 js_ReportCompileErrorNumber(cx, TS(tc->compiler), right,
3872 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
3873 return JS_FALSE;
3875 #endif
3877 fpvd.table.ops = NULL;
3878 lhs = left->pn_head;
3879 if (left->pn_type == TOK_RB) {
3880 rhs = (right && right->pn_type == left->pn_type)
3881 ? right->pn_head
3882 : NULL;
3884 while (lhs) {
3885 pn = lhs, pn2 = rhs;
3887 /* Nullary comma is an elision; binary comma is an expression.*/
3888 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3889 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3890 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3891 } else {
3892 if (data) {
3893 if (pn->pn_type != TOK_NAME)
3894 goto no_var_name;
3896 ok = BindDestructuringVar(cx, data, pn, tc);
3897 } else {
3898 ok = BindDestructuringLHS(cx, pn, tc);
3901 if (!ok)
3902 goto out;
3905 lhs = lhs->pn_next;
3906 if (rhs)
3907 rhs = rhs->pn_next;
3909 } else {
3910 JS_ASSERT(left->pn_type == TOK_RC);
3911 fpvd.numvars = left->pn_count;
3912 fpvd.maxstep = 0;
3913 rhs = NULL;
3915 while (lhs) {
3916 JS_ASSERT(lhs->pn_type == TOK_COLON);
3917 pn = lhs->pn_right;
3919 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3920 if (right)
3921 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3922 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3923 } else if (data) {
3924 if (pn->pn_type != TOK_NAME)
3925 goto no_var_name;
3927 ok = BindDestructuringVar(cx, data, pn, tc);
3928 } else {
3929 ok = BindDestructuringLHS(cx, pn, tc);
3931 if (!ok)
3932 goto out;
3934 lhs = lhs->pn_next;
3939 * The catch/finally handler implementation in the interpreter assumes
3940 * that any operation that introduces a new scope (like a "let" or "with"
3941 * block) increases the stack depth. This way, it is possible to restore
3942 * the scope chain based on stack depth of the handler alone. "let" with
3943 * an empty destructuring pattern like in
3945 * let [] = 1;
3947 * would violate this assumption as the there would be no let locals to
3948 * store on the stack. To satisfy it we add an empty property to such
3949 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3950 * slots, would be always positive.
3952 * Note that we add such a property even if the block has locals due to
3953 * later let declarations in it. We optimize for code simplicity here,
3954 * not the fastest runtime performance with empty [] or {}.
3956 if (data &&
3957 data->binder == BindLet &&
3958 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3959 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3960 ATOM_TO_JSID(cx->runtime->
3961 atomState.emptyAtom),
3962 JSVAL_VOID, NULL, NULL,
3963 JSPROP_ENUMERATE |
3964 JSPROP_PERMANENT |
3965 JSPROP_SHARED,
3966 SPROP_HAS_SHORTID, 0, NULL);
3967 if (!ok)
3968 goto out;
3971 ok = JS_TRUE;
3973 out:
3974 if (fpvd.table.ops)
3975 JS_DHashTableFinish(&fpvd.table);
3976 return ok;
3978 no_var_name:
3979 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR,
3980 JSMSG_NO_VARIABLE_NAME);
3981 ok = JS_FALSE;
3982 goto out;
3986 * This is a greatly pared down version of CheckDestructuring that extends the
3987 * pn_pos.end source coordinate of each name in a destructuring binding such as
3989 * var [x, y] = [function () y, 42];
3991 * to cover its corresponding initializer, so that the initialized binding does
3992 * not appear to dominate any closures in its initializer. See bug 496134.
3994 * The quick-and-dirty dominance computation in JSCompiler::setFunctionKinds is
3995 * not very precise. With one-pass SSA construction from structured source code
3996 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3997 * Languages", Brandis and Mössenböck), we could do much better.
3999 * See CheckDestructuring, immediately above. If you change either of these
4000 * functions, you might have to change the other to match.
4002 static JSBool
4003 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
4005 FindPropValData fpvd;
4006 JSParseNode *lhs, *rhs;
4008 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
4009 JS_ASSERT(right);
4011 #if JS_HAS_DESTRUCTURING_SHORTHAND
4012 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
4013 js_ReportCompileErrorNumber(tc->compiler->context, TS(tc->compiler), right,
4014 JSREPORT_ERROR, JSMSG_BAD_OBJECT_INIT);
4015 return JS_FALSE;
4017 #endif
4019 if (right->pn_type != left->pn_type)
4020 return JS_TRUE;
4022 fpvd.table.ops = NULL;
4023 lhs = left->pn_head;
4024 if (left->pn_type == TOK_RB) {
4025 rhs = right->pn_head;
4027 while (lhs && rhs) {
4028 /* Nullary comma is an elision; binary comma is an expression.*/
4029 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
4030 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
4031 if (!UndominateInitializers(lhs, rhs, tc))
4032 return JS_FALSE;
4033 } else {
4034 lhs->pn_pos.end = rhs->pn_pos.end;
4038 lhs = lhs->pn_next;
4039 rhs = rhs->pn_next;
4041 } else {
4042 JS_ASSERT(left->pn_type == TOK_RC);
4043 fpvd.numvars = left->pn_count;
4044 fpvd.maxstep = 0;
4046 while (lhs) {
4047 JS_ASSERT(lhs->pn_type == TOK_COLON);
4048 JSParseNode *pn = lhs->pn_right;
4050 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4051 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4052 if (rhs && !UndominateInitializers(pn, rhs, tc))
4053 return JS_FALSE;
4054 } else {
4055 if (rhs)
4056 pn->pn_pos.end = rhs->pn_pos.end;
4059 lhs = lhs->pn_next;
4062 return JS_TRUE;
4065 static JSParseNode *
4066 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
4067 JSTokenType tt)
4069 JSTokenStream *ts;
4070 JSParseNode *pn;
4072 ts = TS(tc->compiler);
4073 tc->flags |= TCF_DECL_DESTRUCTURING;
4074 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
4075 tc->flags &= ~TCF_DECL_DESTRUCTURING;
4076 if (!pn)
4077 return NULL;
4078 if (!CheckDestructuring(cx, data, pn, NULL, tc))
4079 return NULL;
4080 return pn;
4084 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4085 * This function assumes the cloned tree is for use in the same statement and
4086 * binding context as the original tree.
4088 static JSParseNode *
4089 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4091 JSParseNode *pn, *pn2, *opn2;
4093 pn = NewOrRecycledNode(tc);
4094 if (!pn)
4095 return NULL;
4096 pn->pn_type = opn->pn_type;
4097 pn->pn_pos = opn->pn_pos;
4098 pn->pn_op = opn->pn_op;
4099 pn->pn_used = opn->pn_used;
4100 pn->pn_defn = opn->pn_defn;
4101 pn->pn_arity = opn->pn_arity;
4102 pn->pn_parens = opn->pn_parens;
4104 switch (pn->pn_arity) {
4105 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4107 case PN_FUNC:
4108 NULLCHECK(pn->pn_funbox =
4109 tc->compiler->newFunctionBox(opn->pn_funbox->object, pn, tc));
4110 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4111 pn->pn_cookie = opn->pn_cookie;
4112 pn->pn_dflags = opn->pn_dflags;
4113 pn->pn_blockid = opn->pn_blockid;
4114 break;
4116 case PN_LIST:
4117 pn->makeEmpty();
4118 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4119 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4120 pn->append(pn2);
4122 pn->pn_xflags = opn->pn_xflags;
4123 break;
4125 case PN_TERNARY:
4126 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4127 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4128 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4129 break;
4131 case PN_BINARY:
4132 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4133 if (opn->pn_right != opn->pn_left)
4134 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4135 else
4136 pn->pn_right = pn->pn_left;
4137 pn->pn_val = opn->pn_val;
4138 pn->pn_iflags = opn->pn_iflags;
4139 break;
4141 case PN_UNARY:
4142 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4143 pn->pn_num = opn->pn_num;
4144 pn->pn_hidden = opn->pn_hidden;
4145 break;
4147 case PN_NAME:
4148 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4149 pn->pn_u = opn->pn_u;
4150 if (opn->pn_used) {
4152 * The old name is a use of its pn_lexdef. Make the clone also be a
4153 * use of that definition.
4155 JSDefinition *dn = pn->pn_lexdef;
4157 pn->pn_link = dn->dn_uses;
4158 dn->dn_uses = pn;
4159 } else if (opn->pn_expr) {
4160 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4163 * If the old name is a definition, the new one has pn_defn set.
4164 * Make the old name a use of the new node.
4166 if (opn->pn_defn) {
4167 opn->pn_defn = false;
4168 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4171 break;
4173 case PN_NAMESET:
4174 pn->pn_names = opn->pn_names;
4175 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4176 break;
4178 case PN_NULLARY:
4179 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4180 pn->pn_u = opn->pn_u;
4181 break;
4183 #undef NULLCHECK
4185 return pn;
4188 #endif /* JS_HAS_DESTRUCTURING */
4190 extern const char js_with_statement_str[];
4192 static JSParseNode *
4193 ContainsStmt(JSParseNode *pn, JSTokenType tt)
4195 JSParseNode *pn2, *pnt;
4197 if (!pn)
4198 return NULL;
4199 if (PN_TYPE(pn) == tt)
4200 return pn;
4201 switch (pn->pn_arity) {
4202 case PN_LIST:
4203 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4204 pnt = ContainsStmt(pn2, tt);
4205 if (pnt)
4206 return pnt;
4208 break;
4209 case PN_TERNARY:
4210 pnt = ContainsStmt(pn->pn_kid1, tt);
4211 if (pnt)
4212 return pnt;
4213 pnt = ContainsStmt(pn->pn_kid2, tt);
4214 if (pnt)
4215 return pnt;
4216 return ContainsStmt(pn->pn_kid3, tt);
4217 case PN_BINARY:
4219 * Limit recursion if pn is a binary expression, which can't contain a
4220 * var statement.
4222 if (pn->pn_op != JSOP_NOP)
4223 return NULL;
4224 pnt = ContainsStmt(pn->pn_left, tt);
4225 if (pnt)
4226 return pnt;
4227 return ContainsStmt(pn->pn_right, tt);
4228 case PN_UNARY:
4229 if (pn->pn_op != JSOP_NOP)
4230 return NULL;
4231 return ContainsStmt(pn->pn_kid, tt);
4232 case PN_NAME:
4233 return ContainsStmt(pn->maybeExpr(), tt);
4234 case PN_NAMESET:
4235 return ContainsStmt(pn->pn_tree, tt);
4236 default:;
4238 return NULL;
4241 static JSParseNode *
4242 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4243 JSParser operandParser)
4245 JSTokenType tt, tt2;
4246 JSParseNode *pn, *pn2;
4248 tt = CURRENT_TOKEN(ts).type;
4249 if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
4250 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4251 JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4252 return NULL;
4255 pn = NewParseNode(PN_UNARY, tc);
4256 if (!pn)
4257 return NULL;
4259 #if JS_HAS_GENERATORS
4260 if (tt == TOK_YIELD)
4261 tc->flags |= TCF_FUN_IS_GENERATOR;
4262 #endif
4264 /* This is ugly, but we don't want to require a semicolon. */
4265 ts->flags |= TSF_OPERAND;
4266 tt2 = js_PeekTokenSameLine(cx, ts);
4267 ts->flags &= ~TSF_OPERAND;
4268 if (tt2 == TOK_ERROR)
4269 return NULL;
4271 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4272 #if JS_HAS_GENERATORS
4273 && (tt != TOK_YIELD ||
4274 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4275 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4276 #endif
4278 pn2 = operandParser(cx, ts, tc);
4279 if (!pn2)
4280 return NULL;
4281 #if JS_HAS_GENERATORS
4282 if (tt == TOK_RETURN)
4283 #endif
4284 tc->flags |= TCF_RETURN_EXPR;
4285 pn->pn_pos.end = pn2->pn_pos.end;
4286 pn->pn_kid = pn2;
4287 } else {
4288 #if JS_HAS_GENERATORS
4289 if (tt == TOK_RETURN)
4290 #endif
4291 tc->flags |= TCF_RETURN_VOID;
4294 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4295 /* As in Python (see PEP-255), disallow return v; in generators. */
4296 ReportBadReturn(cx, tc, JSREPORT_ERROR,
4297 JSMSG_BAD_GENERATOR_RETURN,
4298 JSMSG_BAD_ANON_GENERATOR_RETURN);
4299 return NULL;
4302 if (JS_HAS_STRICT_OPTION(cx) &&
4303 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4304 !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4305 JSMSG_NO_RETURN_VALUE,
4306 JSMSG_ANON_NO_RETURN_VALUE)) {
4307 return NULL;
4310 return pn;
4313 static JSParseNode *
4314 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4315 JSStmtInfo *stmt)
4317 JSParseNode *pn;
4318 JSObject *obj;
4319 JSObjectBox *blockbox;
4321 pn = NewParseNode(PN_NAME, tc);
4322 if (!pn)
4323 return NULL;
4325 obj = js_NewBlockObject(cx);
4326 if (!obj)
4327 return NULL;
4329 blockbox = tc->compiler->newObjectBox(obj);
4330 if (!blockbox)
4331 return NULL;
4333 js_PushBlockScope(tc, stmt, obj, -1);
4334 pn->pn_type = TOK_LEXICALSCOPE;
4335 pn->pn_op = JSOP_LEAVEBLOCK;
4336 pn->pn_objbox = blockbox;
4337 pn->pn_cookie = FREE_UPVAR_COOKIE;
4338 pn->pn_dflags = 0;
4339 if (!GenerateBlockId(tc, stmt->blockid))
4340 return NULL;
4341 pn->pn_blockid = stmt->blockid;
4342 return pn;
4345 #if JS_HAS_BLOCK_SCOPE
4347 static JSParseNode *
4348 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
4350 JSParseNode *pn, *pnblock, *pnlet;
4351 JSStmtInfo stmtInfo;
4353 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
4355 /* Create the let binary node. */
4356 pnlet = NewParseNode(PN_BINARY, tc);
4357 if (!pnlet)
4358 return NULL;
4360 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4362 /* This is a let block or expression of the form: let (a, b, c) .... */
4363 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
4364 if (!pnblock)
4365 return NULL;
4366 pn = pnblock;
4367 pn->pn_expr = pnlet;
4369 pnlet->pn_left = Variables(cx, ts, tc, true);
4370 if (!pnlet->pn_left)
4371 return NULL;
4372 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4374 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4376 ts->flags |= TSF_OPERAND;
4377 if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
4379 * If this is really an expression in let statement guise, then we
4380 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4381 * the return value of the expression.
4383 pn = NewParseNode(PN_UNARY, tc);
4384 if (!pn)
4385 return NULL;
4386 pn->pn_type = TOK_SEMI;
4387 pn->pn_num = -1;
4388 pn->pn_kid = pnblock;
4390 statement = JS_FALSE;
4392 ts->flags &= ~TSF_OPERAND;
4394 if (statement) {
4395 pnlet->pn_right = Statements(cx, ts, tc);
4396 if (!pnlet->pn_right)
4397 return NULL;
4398 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4399 } else {
4401 * Change pnblock's opcode to the variant that propagates the last
4402 * result down after popping the block, and clear statement.
4404 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4405 pnlet->pn_right = AssignExpr(cx, ts, tc);
4406 if (!pnlet->pn_right)
4407 return NULL;
4410 PopStatement(tc);
4411 return pn;
4414 #endif /* JS_HAS_BLOCK_SCOPE */
4416 static bool
4417 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4419 js_PushStatement(tc, stmt, type, -1);
4420 return GenerateBlockId(tc, stmt->blockid);
4423 static JSParseNode *
4424 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4426 JSParseNode *pn = NULL;
4428 JSAtomListElement *ale = tc->decls.lookup(atom);
4429 if (ale) {
4430 pn = ALE_DEFN(ale);
4431 JS_ASSERT(!pn->isPlaceholder());
4432 } else {
4433 ale = tc->lexdeps.lookup(atom);
4434 if (ale) {
4435 pn = ALE_DEFN(ale);
4436 JS_ASSERT(pn->isPlaceholder());
4440 if (pn) {
4441 JS_ASSERT(pn->pn_defn);
4444 * A let binding at top level becomes a var before we get here, so if
4445 * pn and tc have the same blockid then that id must not be the bodyid.
4446 * If pn is a forward placeholder definition from the same or a higher
4447 * block then we claim it.
4449 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4450 pn->pn_blockid != tc->bodyid);
4452 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4453 if (let)
4454 pn->pn_blockid = tc->blockid();
4456 tc->lexdeps.remove(tc->compiler, atom);
4457 return pn;
4461 /* Make a new node for this declarator name (or destructuring pattern). */
4462 pn = NewNameNode(tc->compiler->context, atom, tc);
4463 if (!pn)
4464 return NULL;
4465 return pn;
4468 #if JS_HAS_BLOCK_SCOPE
4469 static bool
4470 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4472 if (!pn)
4473 return true;
4475 switch (pn->pn_arity) {
4476 case PN_LIST:
4477 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4478 RebindLets(pn2, tc);
4479 break;
4481 case PN_TERNARY:
4482 RebindLets(pn->pn_kid1, tc);
4483 RebindLets(pn->pn_kid2, tc);
4484 RebindLets(pn->pn_kid3, tc);
4485 break;
4487 case PN_BINARY:
4488 RebindLets(pn->pn_left, tc);
4489 RebindLets(pn->pn_right, tc);
4490 break;
4492 case PN_UNARY:
4493 RebindLets(pn->pn_kid, tc);
4494 break;
4496 case PN_FUNC:
4497 RebindLets(pn->pn_body, tc);
4498 break;
4500 case PN_NAME:
4501 RebindLets(pn->maybeExpr(), tc);
4503 if (pn->pn_defn) {
4504 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4505 } else if (pn->pn_used) {
4506 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4507 ForgetUse(pn);
4509 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4510 if (ale) {
4511 while ((ale = ALE_NEXT(ale)) != NULL) {
4512 if (ALE_ATOM(ale) == pn->pn_atom) {
4513 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4514 return true;
4519 ale = tc->lexdeps.lookup(pn->pn_atom);
4520 if (!ale) {
4521 ale = MakePlaceholder(pn, tc);
4522 if (!ale)
4523 return NULL;
4525 JSDefinition *dn = ALE_DEFN(ale);
4526 dn->pn_type = TOK_NAME;
4527 dn->pn_op = JSOP_NOP;
4529 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4532 break;
4534 case PN_NAMESET:
4535 RebindLets(pn->pn_tree, tc);
4536 break;
4539 return true;
4541 #endif /* JS_HAS_BLOCK_SCOPE */
4543 static JSParseNode *
4544 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4546 JSTokenType tt;
4547 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4548 JSStmtInfo stmtInfo, *stmt, *stmt2;
4549 JSAtom *label;
4551 JS_CHECK_RECURSION(cx, return NULL);
4553 ts->flags |= TSF_OPERAND;
4554 tt = js_GetToken(cx, ts);
4555 ts->flags &= ~TSF_OPERAND;
4557 #if JS_HAS_GETTER_SETTER
4558 if (tt == TOK_NAME) {
4559 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
4560 if (tt == TOK_ERROR)
4561 return NULL;
4563 #endif
4565 switch (tt) {
4566 case TOK_FUNCTION:
4567 #if JS_HAS_XML_SUPPORT
4568 ts->flags |= TSF_KEYWORD_IS_NAME;
4569 tt = js_PeekToken(cx, ts);
4570 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4571 if (tt == TOK_DBLCOLON)
4572 goto expression;
4573 #endif
4574 return FunctionStmt(cx, ts, tc);
4576 case TOK_IF:
4577 /* An IF node has three kids: condition, then, and optional else. */
4578 pn = NewParseNode(PN_TERNARY, tc);
4579 if (!pn)
4580 return NULL;
4581 pn1 = Condition(cx, ts, tc);
4582 if (!pn1)
4583 return NULL;
4584 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4585 pn2 = Statement(cx, ts, tc);
4586 if (!pn2)
4587 return NULL;
4588 ts->flags |= TSF_OPERAND;
4589 if (js_MatchToken(cx, ts, TOK_ELSE)) {
4590 ts->flags &= ~TSF_OPERAND;
4591 stmtInfo.type = STMT_ELSE;
4592 pn3 = Statement(cx, ts, tc);
4593 if (!pn3)
4594 return NULL;
4595 pn->pn_pos.end = pn3->pn_pos.end;
4596 } else {
4597 ts->flags &= ~TSF_OPERAND;
4598 pn3 = NULL;
4599 pn->pn_pos.end = pn2->pn_pos.end;
4601 PopStatement(tc);
4602 pn->pn_kid1 = pn1;
4603 pn->pn_kid2 = pn2;
4604 pn->pn_kid3 = pn3;
4605 return pn;
4607 case TOK_SWITCH:
4609 JSParseNode *pn5, *saveBlock;
4610 JSBool seenDefault = JS_FALSE;
4612 pn = NewParseNode(PN_BINARY, tc);
4613 if (!pn)
4614 return NULL;
4615 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4617 /* pn1 points to the switch's discriminant. */
4618 pn1 = ParenExpr(cx, ts, tc, NULL, NULL);
4619 if (!pn1)
4620 return NULL;
4622 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4623 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4626 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4627 * because that function states tc->topStmt->blockid.
4629 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4631 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4632 pn2 = NewParseNode(PN_LIST, tc);
4633 if (!pn2)
4634 return NULL;
4635 pn2->makeEmpty();
4636 if (!GenerateBlockIdForStmtNode(pn2, tc))
4637 return NULL;
4638 saveBlock = tc->blockNode;
4639 tc->blockNode = pn2;
4641 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
4642 switch (tt) {
4643 case TOK_DEFAULT:
4644 if (seenDefault) {
4645 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4646 JSMSG_TOO_MANY_DEFAULTS);
4647 return NULL;
4649 seenDefault = JS_TRUE;
4650 /* FALL THROUGH */
4652 case TOK_CASE:
4653 pn3 = NewParseNode(PN_BINARY, tc);
4654 if (!pn3)
4655 return NULL;
4656 if (tt == TOK_CASE) {
4657 pn3->pn_left = Expr(cx, ts, tc);
4658 if (!pn3->pn_left)
4659 return NULL;
4661 pn2->append(pn3);
4662 if (pn2->pn_count == JS_BIT(16)) {
4663 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4664 JSMSG_TOO_MANY_CASES);
4665 return NULL;
4667 break;
4669 case TOK_ERROR:
4670 return NULL;
4672 default:
4673 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
4674 JSMSG_BAD_SWITCH);
4675 return NULL;
4677 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4679 pn4 = NewParseNode(PN_LIST, tc);
4680 if (!pn4)
4681 return NULL;
4682 pn4->pn_type = TOK_LC;
4683 pn4->makeEmpty();
4684 ts->flags |= TSF_OPERAND;
4685 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
4686 tt != TOK_CASE && tt != TOK_DEFAULT) {
4687 ts->flags &= ~TSF_OPERAND;
4688 if (tt == TOK_ERROR)
4689 return NULL;
4690 pn5 = Statement(cx, ts, tc);
4691 if (!pn5)
4692 return NULL;
4693 pn4->pn_pos.end = pn5->pn_pos.end;
4694 pn4->append(pn5);
4695 ts->flags |= TSF_OPERAND;
4697 ts->flags &= ~TSF_OPERAND;
4699 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4700 if (pn4->pn_head)
4701 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4702 pn3->pn_pos.end = pn4->pn_pos.end;
4703 pn3->pn_right = pn4;
4707 * Handle the case where there was a let declaration in any case in
4708 * the switch body, but not within an inner block. If it replaced
4709 * tc->blockNode with a new block node then we must refresh pn2 and
4710 * then restore tc->blockNode.
4712 if (tc->blockNode != pn2)
4713 pn2 = tc->blockNode;
4714 tc->blockNode = saveBlock;
4715 PopStatement(tc);
4717 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4718 pn->pn_left = pn1;
4719 pn->pn_right = pn2;
4720 return pn;
4723 case TOK_WHILE:
4724 pn = NewParseNode(PN_BINARY, tc);
4725 if (!pn)
4726 return NULL;
4727 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4728 pn2 = Condition(cx, ts, tc);
4729 if (!pn2)
4730 return NULL;
4731 pn->pn_left = pn2;
4732 pn2 = Statement(cx, ts, tc);
4733 if (!pn2)
4734 return NULL;
4735 PopStatement(tc);
4736 pn->pn_pos.end = pn2->pn_pos.end;
4737 pn->pn_right = pn2;
4738 return pn;
4740 case TOK_DO:
4741 pn = NewParseNode(PN_BINARY, tc);
4742 if (!pn)
4743 return NULL;
4744 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4745 pn2 = Statement(cx, ts, tc);
4746 if (!pn2)
4747 return NULL;
4748 pn->pn_left = pn2;
4749 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4750 pn2 = Condition(cx, ts, tc);
4751 if (!pn2)
4752 return NULL;
4753 PopStatement(tc);
4754 pn->pn_pos.end = pn2->pn_pos.end;
4755 pn->pn_right = pn2;
4756 if (JSVERSION_NUMBER(cx) != JSVERSION_ECMA_3) {
4758 * All legacy and extended versions must do automatic semicolon
4759 * insertion after do-while. See the testcase and discussion in
4760 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4762 (void) js_MatchToken(cx, ts, TOK_SEMI);
4763 return pn;
4765 break;
4767 case TOK_FOR:
4769 JSParseNode *pnseq = NULL;
4770 #if JS_HAS_BLOCK_SCOPE
4771 JSParseNode *pnlet = NULL;
4772 JSStmtInfo blockInfo;
4773 #endif
4775 /* A FOR node is binary, left is loop control and right is the body. */
4776 pn = NewParseNode(PN_BINARY, tc);
4777 if (!pn)
4778 return NULL;
4779 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4781 pn->pn_op = JSOP_ITER;
4782 pn->pn_iflags = 0;
4783 if (js_MatchToken(cx, ts, TOK_NAME)) {
4784 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
4785 pn->pn_iflags = JSITER_FOREACH;
4786 else
4787 js_UngetToken(ts);
4790 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4791 ts->flags |= TSF_OPERAND;
4792 tt = js_PeekToken(cx, ts);
4793 ts->flags &= ~TSF_OPERAND;
4795 #if JS_HAS_BLOCK_SCOPE
4796 bool let = false;
4797 #endif
4799 if (tt == TOK_SEMI) {
4800 if (pn->pn_iflags & JSITER_FOREACH)
4801 goto bad_for_each;
4803 /* No initializer -- set first kid of left sub-node to null. */
4804 pn1 = NULL;
4805 } else {
4807 * Set pn1 to a var list or an initializing expression.
4809 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4810 * of the for statement. This flag will be used by the RelExpr
4811 * production; if it is set, then the 'in' keyword will not be
4812 * recognized as an operator, leaving it available to be parsed as
4813 * part of a for/in loop.
4815 * A side effect of this restriction is that (unparenthesized)
4816 * expressions involving an 'in' operator are illegal in the init
4817 * clause of an ordinary for loop.
4819 tc->flags |= TCF_IN_FOR_INIT;
4820 if (tt == TOK_VAR) {
4821 (void) js_GetToken(cx, ts);
4822 pn1 = Variables(cx, ts, tc, false);
4823 #if JS_HAS_BLOCK_SCOPE
4824 } else if (tt == TOK_LET) {
4825 let = true;
4826 (void) js_GetToken(cx, ts);
4827 if (js_PeekToken(cx, ts) == TOK_LP) {
4828 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
4829 tt = TOK_LEXICALSCOPE;
4830 } else {
4831 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
4832 if (!pnlet)
4833 return NULL;
4834 blockInfo.flags |= SIF_FOR_BLOCK;
4835 pn1 = Variables(cx, ts, tc, false);
4837 #endif
4838 } else {
4839 pn1 = Expr(cx, ts, tc);
4841 tc->flags &= ~TCF_IN_FOR_INIT;
4842 if (!pn1)
4843 return NULL;
4847 * We can be sure that it's a for/in loop if there's still an 'in'
4848 * keyword here, even if JavaScript recognizes 'in' as an operator,
4849 * as we've excluded 'in' from being parsed in RelExpr by setting
4850 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4852 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
4853 pn->pn_iflags |= JSITER_ENUMERATE;
4854 stmtInfo.type = STMT_FOR_IN_LOOP;
4856 /* Check that the left side of the 'in' is valid. */
4857 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || PN_TYPE(pn1) == tt);
4858 if (TOKEN_TYPE_IS_DECL(tt)
4859 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4860 #if JS_HAS_DESTRUCTURING
4861 || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4862 pn->pn_op == JSOP_ITER &&
4863 !(pn->pn_iflags & JSITER_FOREACH) &&
4864 (pn1->pn_head->pn_type == TOK_RC ||
4865 (pn1->pn_head->pn_type == TOK_RB &&
4866 pn1->pn_head->pn_count != 2) ||
4867 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4868 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4869 pn1->pn_head->pn_left->pn_count != 2))))
4870 #endif
4872 : (pn1->pn_type != TOK_NAME &&
4873 pn1->pn_type != TOK_DOT &&
4874 #if JS_HAS_DESTRUCTURING
4875 ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
4876 pn->pn_op == JSOP_ITER &&
4877 !(pn->pn_iflags & JSITER_FOREACH))
4878 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4879 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4880 #endif
4881 pn1->pn_type != TOK_LP &&
4882 #if JS_HAS_XML_SUPPORT
4883 (pn1->pn_type != TOK_UNARYOP ||
4884 pn1->pn_op != JSOP_XMLNAME) &&
4885 #endif
4886 pn1->pn_type != TOK_LB)) {
4887 js_ReportCompileErrorNumber(cx, ts, pn1, JSREPORT_ERROR,
4888 JSMSG_BAD_FOR_LEFTSIDE);
4889 return NULL;
4892 /* pn2 points to the name or destructuring pattern on in's left. */
4893 pn2 = NULL;
4894 uintN dflag = PND_ASSIGNED;
4896 if (TOKEN_TYPE_IS_DECL(tt)) {
4897 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4898 pn1->pn_xflags |= PNX_FORINVAR;
4901 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4902 * 'var', or 'const' to hoist the initializer or the entire
4903 * decl out of the loop head. TOK_VAR is the type for both
4904 * 'var' and 'const'.
4906 pn2 = pn1->pn_head;
4907 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4908 #if JS_HAS_DESTRUCTURING
4909 || pn2->pn_type == TOK_ASSIGN
4910 #endif
4912 pnseq = NewParseNode(PN_LIST, tc);
4913 if (!pnseq)
4914 return NULL;
4915 pnseq->pn_type = TOK_SEQ;
4916 pnseq->pn_pos.begin = pn->pn_pos.begin;
4918 #if JS_HAS_BLOCK_SCOPE
4919 if (tt == TOK_LET) {
4921 * Hoist just the 'i' from 'for (let x = i in o)' to
4922 * before the loop, glued together via pnseq.
4924 pn3 = NewParseNode(PN_UNARY, tc);
4925 if (!pn3)
4926 return NULL;
4927 pn3->pn_type = TOK_SEMI;
4928 pn3->pn_op = JSOP_NOP;
4929 #if JS_HAS_DESTRUCTURING
4930 if (pn2->pn_type == TOK_ASSIGN) {
4931 pn4 = pn2->pn_right;
4932 pn2 = pn1->pn_head = pn2->pn_left;
4933 } else
4934 #endif
4936 pn4 = pn2->pn_expr;
4937 pn2->pn_expr = NULL;
4939 if (!RebindLets(pn4, tc))
4940 return NULL;
4941 pn3->pn_pos = pn4->pn_pos;
4942 pn3->pn_kid = pn4;
4943 pnseq->initList(pn3);
4944 } else
4945 #endif /* JS_HAS_BLOCK_SCOPE */
4947 dflag = PND_INITIALIZED;
4950 * All of 'var x = i' is hoisted above 'for (x in o)',
4951 * so clear PNX_FORINVAR.
4953 * Request JSOP_POP here since the var is for a simple
4954 * name (it is not a destructuring binding's left-hand
4955 * side) and it has an initializer.
4957 pn1->pn_xflags &= ~PNX_FORINVAR;
4958 pn1->pn_xflags |= PNX_POPVAR;
4959 pnseq->initList(pn1);
4961 #if JS_HAS_DESTRUCTURING
4962 if (pn2->pn_type == TOK_ASSIGN) {
4963 pn1 = CloneParseTree(pn2->pn_left, tc);
4964 if (!pn1)
4965 return NULL;
4966 } else
4967 #endif
4969 JS_ASSERT(pn2->pn_type == TOK_NAME);
4970 pn1 = NewNameNode(cx, pn2->pn_atom, tc);
4971 if (!pn1)
4972 return NULL;
4973 pn1->pn_type = TOK_NAME;
4974 pn1->pn_op = JSOP_NAME;
4975 pn1->pn_pos = pn2->pn_pos;
4976 if (pn2->pn_defn)
4977 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4979 pn2 = pn1;
4984 if (!pn2) {
4985 pn2 = pn1;
4986 if (pn2->pn_type == TOK_LP &&
4987 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4988 return NULL;
4990 #if JS_HAS_XML_SUPPORT
4991 if (pn2->pn_type == TOK_UNARYOP)
4992 pn2->pn_op = JSOP_BINDXMLNAME;
4993 #endif
4996 switch (pn2->pn_type) {
4997 case TOK_NAME:
4998 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4999 NoteLValue(cx, pn2, tc, dflag);
5000 break;
5002 #if JS_HAS_DESTRUCTURING
5003 case TOK_ASSIGN:
5004 pn2 = pn2->pn_left;
5005 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
5006 /* FALL THROUGH */
5007 case TOK_RB:
5008 case TOK_RC:
5009 /* Check for valid lvalues in var-less destructuring for-in. */
5010 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
5011 return NULL;
5013 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
5015 * Destructuring for-in requires [key, value] enumeration
5016 * in JS1.7.
5018 JS_ASSERT(pn->pn_op == JSOP_ITER);
5019 if (!(pn->pn_iflags & JSITER_FOREACH))
5020 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
5022 break;
5023 #endif
5025 default:;
5029 * Parse the object expression as the right operand of 'in', first
5030 * removing the top statement from the statement-stack if this is a
5031 * 'for (let x in y)' loop.
5033 #if JS_HAS_BLOCK_SCOPE
5034 JSStmtInfo *save = tc->topStmt;
5035 if (let)
5036 tc->topStmt = save->down;
5037 #endif
5038 pn2 = Expr(cx, ts, tc);
5039 #if JS_HAS_BLOCK_SCOPE
5040 if (let)
5041 tc->topStmt = save;
5042 #endif
5044 pn2 = NewBinary(TOK_IN, JSOP_NOP, pn1, pn2, tc);
5045 if (!pn2)
5046 return NULL;
5047 pn->pn_left = pn2;
5048 } else {
5049 if (pn->pn_iflags & JSITER_FOREACH)
5050 goto bad_for_each;
5051 pn->pn_op = JSOP_NOP;
5053 /* Parse the loop condition or null into pn2. */
5054 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
5055 ts->flags |= TSF_OPERAND;
5056 tt = js_PeekToken(cx, ts);
5057 ts->flags &= ~TSF_OPERAND;
5058 if (tt == TOK_SEMI) {
5059 pn2 = NULL;
5060 } else {
5061 pn2 = Expr(cx, ts, tc);
5062 if (!pn2)
5063 return NULL;
5066 /* Parse the update expression or null into pn3. */
5067 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
5068 ts->flags |= TSF_OPERAND;
5069 tt = js_PeekToken(cx, ts);
5070 ts->flags &= ~TSF_OPERAND;
5071 if (tt == TOK_RP) {
5072 pn3 = NULL;
5073 } else {
5074 pn3 = Expr(cx, ts, tc);
5075 if (!pn3)
5076 return NULL;
5079 /* Build the FORHEAD node to use as the left kid of pn. */
5080 pn4 = NewParseNode(PN_TERNARY, tc);
5081 if (!pn4)
5082 return NULL;
5083 pn4->pn_type = TOK_FORHEAD;
5084 pn4->pn_op = JSOP_NOP;
5085 pn4->pn_kid1 = pn1;
5086 pn4->pn_kid2 = pn2;
5087 pn4->pn_kid3 = pn3;
5088 pn->pn_left = pn4;
5091 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5093 /* Parse the loop body into pn->pn_right. */
5094 pn2 = Statement(cx, ts, tc);
5095 if (!pn2)
5096 return NULL;
5097 pn->pn_right = pn2;
5099 /* Record the absolute line number for source note emission. */
5100 pn->pn_pos.end = pn2->pn_pos.end;
5102 #if JS_HAS_BLOCK_SCOPE
5103 if (pnlet) {
5104 PopStatement(tc);
5105 pnlet->pn_expr = pn;
5106 pn = pnlet;
5108 #endif
5109 if (pnseq) {
5110 pnseq->pn_pos.end = pn->pn_pos.end;
5111 pnseq->append(pn);
5112 pn = pnseq;
5114 PopStatement(tc);
5115 return pn;
5117 bad_for_each:
5118 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
5119 JSMSG_BAD_FOR_EACH_LOOP);
5120 return NULL;
5123 case TOK_TRY: {
5124 JSParseNode *catchList, *lastCatch;
5127 * try nodes are ternary.
5128 * kid1 is the try Statement
5129 * kid2 is the catch node list or null
5130 * kid3 is the finally Statement
5132 * catch nodes are ternary.
5133 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5134 * kid2 is the catch guard or null if no guard
5135 * kid3 is the catch block
5137 * catch lvalue nodes are either:
5138 * TOK_NAME for a single identifier
5139 * TOK_RB or TOK_RC for a destructuring left-hand side
5141 * finally nodes are TOK_LC Statement lists.
5143 pn = NewParseNode(PN_TERNARY, tc);
5144 if (!pn)
5145 return NULL;
5146 pn->pn_op = JSOP_NOP;
5148 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5149 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5150 return NULL;
5151 pn->pn_kid1 = Statements(cx, ts, tc);
5152 if (!pn->pn_kid1)
5153 return NULL;
5154 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5155 PopStatement(tc);
5157 catchList = NULL;
5158 tt = js_GetToken(cx, ts);
5159 if (tt == TOK_CATCH) {
5160 catchList = NewParseNode(PN_LIST, tc);
5161 if (!catchList)
5162 return NULL;
5163 catchList->pn_type = TOK_RESERVED;
5164 catchList->makeEmpty();
5165 lastCatch = NULL;
5167 do {
5168 JSParseNode *pnblock;
5169 BindData data;
5171 /* Check for another catch after unconditional catch. */
5172 if (lastCatch && !lastCatch->pn_kid2) {
5173 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5174 JSMSG_CATCH_AFTER_GENERAL);
5175 return NULL;
5179 * Create a lexical scope node around the whole catch clause,
5180 * including the head.
5182 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
5183 if (!pnblock)
5184 return NULL;
5185 stmtInfo.type = STMT_CATCH;
5188 * Legal catch forms are:
5189 * catch (lhs)
5190 * catch (lhs if <boolean_expression>)
5191 * where lhs is a name or a destructuring left-hand side.
5192 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5194 pn2 = NewParseNode(PN_TERNARY, tc);
5195 if (!pn2)
5196 return NULL;
5197 pnblock->pn_expr = pn2;
5198 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5201 * Contrary to ECMA Ed. 3, the catch variable is lexically
5202 * scoped, not a property of a new Object instance. This is
5203 * an intentional change that anticipates ECMA Ed. 4.
5205 data.pn = NULL;
5206 data.op = JSOP_NOP;
5207 data.binder = BindLet;
5208 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5210 tt = js_GetToken(cx, ts);
5211 switch (tt) {
5212 #if JS_HAS_DESTRUCTURING
5213 case TOK_LB:
5214 case TOK_LC:
5215 pn3 = DestructuringExpr(cx, &data, tc, tt);
5216 if (!pn3)
5217 return NULL;
5218 break;
5219 #endif
5221 case TOK_NAME:
5222 label = CURRENT_TOKEN(ts).t_atom;
5223 pn3 = NewBindingNode(label, tc, true);
5224 if (!pn3)
5225 return NULL;
5226 data.pn = pn3;
5227 if (!data.binder(cx, &data, label, tc))
5228 return NULL;
5229 break;
5231 default:
5232 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5233 JSMSG_CATCH_IDENTIFIER);
5234 return NULL;
5237 pn2->pn_kid1 = pn3;
5238 #if JS_HAS_CATCH_GUARD
5240 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5241 * to avoid conflicting with the JS2/ECMAv4 type annotation
5242 * catchguard syntax.
5244 if (js_MatchToken(cx, ts, TOK_IF)) {
5245 pn2->pn_kid2 = Expr(cx, ts, tc);
5246 if (!pn2->pn_kid2)
5247 return NULL;
5249 #endif
5250 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5252 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5253 pn2->pn_kid3 = Statements(cx, ts, tc);
5254 if (!pn2->pn_kid3)
5255 return NULL;
5256 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5257 PopStatement(tc);
5259 catchList->append(pnblock);
5260 lastCatch = pn2;
5261 ts->flags |= TSF_OPERAND;
5262 tt = js_GetToken(cx, ts);
5263 ts->flags &= ~TSF_OPERAND;
5264 } while (tt == TOK_CATCH);
5266 pn->pn_kid2 = catchList;
5268 if (tt == TOK_FINALLY) {
5269 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5270 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5271 return NULL;
5272 pn->pn_kid3 = Statements(cx, ts, tc);
5273 if (!pn->pn_kid3)
5274 return NULL;
5275 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5276 PopStatement(tc);
5277 } else {
5278 js_UngetToken(ts);
5280 if (!catchList && !pn->pn_kid3) {
5281 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5282 JSMSG_CATCH_OR_FINALLY);
5283 return NULL;
5285 return pn;
5288 case TOK_THROW:
5289 pn = NewParseNode(PN_UNARY, tc);
5290 if (!pn)
5291 return NULL;
5293 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5294 ts->flags |= TSF_OPERAND;
5295 tt = js_PeekTokenSameLine(cx, ts);
5296 ts->flags &= ~TSF_OPERAND;
5297 if (tt == TOK_ERROR)
5298 return NULL;
5299 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5300 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5301 JSMSG_SYNTAX_ERROR);
5302 return NULL;
5305 pn2 = Expr(cx, ts, tc);
5306 if (!pn2)
5307 return NULL;
5308 pn->pn_pos.end = pn2->pn_pos.end;
5309 pn->pn_op = JSOP_THROW;
5310 pn->pn_kid = pn2;
5311 break;
5313 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5314 case TOK_CATCH:
5315 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5316 JSMSG_CATCH_WITHOUT_TRY);
5317 return NULL;
5319 case TOK_FINALLY:
5320 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5321 JSMSG_FINALLY_WITHOUT_TRY);
5322 return NULL;
5324 case TOK_BREAK:
5325 pn = NewParseNode(PN_NULLARY, tc);
5326 if (!pn)
5327 return NULL;
5328 if (!MatchLabel(cx, ts, pn))
5329 return NULL;
5330 stmt = tc->topStmt;
5331 label = pn->pn_atom;
5332 if (label) {
5333 for (; ; stmt = stmt->down) {
5334 if (!stmt) {
5335 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5336 JSMSG_LABEL_NOT_FOUND);
5337 return NULL;
5339 if (stmt->type == STMT_LABEL && stmt->label == label)
5340 break;
5342 } else {
5343 for (; ; stmt = stmt->down) {
5344 if (!stmt) {
5345 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5346 JSMSG_TOUGH_BREAK);
5347 return NULL;
5349 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5350 break;
5353 if (label)
5354 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5355 break;
5357 case TOK_CONTINUE:
5358 pn = NewParseNode(PN_NULLARY, tc);
5359 if (!pn)
5360 return NULL;
5361 if (!MatchLabel(cx, ts, pn))
5362 return NULL;
5363 stmt = tc->topStmt;
5364 label = pn->pn_atom;
5365 if (label) {
5366 for (stmt2 = NULL; ; stmt = stmt->down) {
5367 if (!stmt) {
5368 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5369 JSMSG_LABEL_NOT_FOUND);
5370 return NULL;
5372 if (stmt->type == STMT_LABEL) {
5373 if (stmt->label == label) {
5374 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5375 js_ReportCompileErrorNumber(cx, ts, NULL,
5376 JSREPORT_ERROR,
5377 JSMSG_BAD_CONTINUE);
5378 return NULL;
5380 break;
5382 } else {
5383 stmt2 = stmt;
5386 } else {
5387 for (; ; stmt = stmt->down) {
5388 if (!stmt) {
5389 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5390 JSMSG_BAD_CONTINUE);
5391 return NULL;
5393 if (STMT_IS_LOOP(stmt))
5394 break;
5397 if (label)
5398 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5399 break;
5401 case TOK_WITH:
5403 * In most cases, we want the constructs forbidden in strict mode
5404 * code to be a subset of those that JSOPTION_STRICT warns about, and
5405 * we should use js_ReportStrictModeError. However, 'with' is the sole
5406 * instance of a construct that is forbidden in strict mode code, but
5407 * doesn't even merit a warning under JSOPTION_STRICT. See
5408 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5410 if (tc->flags & TCF_STRICT_MODE_CODE) {
5411 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5412 JSMSG_STRICT_CODE_WITH);
5413 return NULL;
5416 pn = NewParseNode(PN_BINARY, tc);
5417 if (!pn)
5418 return NULL;
5419 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5420 pn2 = ParenExpr(cx, ts, tc, NULL, NULL);
5421 if (!pn2)
5422 return NULL;
5423 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5424 pn->pn_left = pn2;
5426 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5427 pn2 = Statement(cx, ts, tc);
5428 if (!pn2)
5429 return NULL;
5430 PopStatement(tc);
5432 pn->pn_pos.end = pn2->pn_pos.end;
5433 pn->pn_right = pn2;
5434 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5435 return pn;
5437 case TOK_VAR:
5438 pn = Variables(cx, ts, tc, false);
5439 if (!pn)
5440 return NULL;
5442 /* Tell js_EmitTree to generate a final POP. */
5443 pn->pn_xflags |= PNX_POPVAR;
5444 break;
5446 #if JS_HAS_BLOCK_SCOPE
5447 case TOK_LET:
5449 JSObject *obj;
5450 JSObjectBox *blockbox;
5452 /* Check for a let statement or let expression. */
5453 if (js_PeekToken(cx, ts) == TOK_LP) {
5454 pn = LetBlock(cx, ts, tc, JS_TRUE);
5455 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5456 return pn;
5458 /* Let expressions require automatic semicolon insertion. */
5459 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5460 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5461 break;
5465 * This is a let declaration. We must be directly under a block per
5466 * the proposed ES4 specs, but not an implicit block created due to
5467 * 'for (let ...)'. If we pass this error test, make the enclosing
5468 * JSStmtInfo be our scope. Further let declarations in this block
5469 * will find this scope statement and use the same block object.
5471 * If we are the first let declaration in this block (i.e., when the
5472 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5473 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5475 stmt = tc->topStmt;
5476 if (stmt &&
5477 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5478 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5479 JSMSG_LET_DECL_NOT_IN_BLOCK);
5480 return NULL;
5483 if (stmt && (stmt->flags & SIF_SCOPE)) {
5484 JS_ASSERT(tc->blockChain == stmt->blockObj);
5485 obj = tc->blockChain;
5486 } else {
5487 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5489 * ES4 specifies that let at top level and at body-block scope
5490 * does not shadow var, so convert back to var.
5492 CURRENT_TOKEN(ts).type = TOK_VAR;
5493 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
5495 pn = Variables(cx, ts, tc, false);
5496 if (!pn)
5497 return NULL;
5498 pn->pn_xflags |= PNX_POPVAR;
5499 break;
5503 * Some obvious assertions here, but they may help clarify the
5504 * situation. This stmt is not yet a scope, so it must not be a
5505 * catch block (catch is a lexical scope by definition).
5507 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5508 JS_ASSERT(stmt != tc->topScopeStmt);
5509 JS_ASSERT(stmt->type == STMT_BLOCK ||
5510 stmt->type == STMT_SWITCH ||
5511 stmt->type == STMT_TRY ||
5512 stmt->type == STMT_FINALLY);
5513 JS_ASSERT(!stmt->downScope);
5515 /* Convert the block statement into a scope statement. */
5516 JSObject *obj = js_NewBlockObject(tc->compiler->context);
5517 if (!obj)
5518 return NULL;
5520 blockbox = tc->compiler->newObjectBox(obj);
5521 if (!blockbox)
5522 return NULL;
5525 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5526 * list stack, if it isn't already there. If it is there, but it
5527 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5528 * block.
5530 stmt->flags |= SIF_SCOPE;
5531 stmt->downScope = tc->topScopeStmt;
5532 tc->topScopeStmt = stmt;
5533 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5534 (tc->maxScopeDepth = tc->scopeDepth));
5536 STOBJ_SET_PARENT(obj, tc->blockChain);
5537 tc->blockChain = obj;
5538 stmt->blockObj = obj;
5540 #ifdef DEBUG
5541 pn1 = tc->blockNode;
5542 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5543 #endif
5545 /* Create a new lexical scope node for these statements. */
5546 pn1 = NewParseNode(PN_NAME, tc);
5547 if (!pn1)
5548 return NULL;
5550 pn1->pn_type = TOK_LEXICALSCOPE;
5551 pn1->pn_op = JSOP_LEAVEBLOCK;
5552 pn1->pn_pos = tc->blockNode->pn_pos;
5553 pn1->pn_objbox = blockbox;
5554 pn1->pn_expr = tc->blockNode;
5555 pn1->pn_blockid = tc->blockNode->pn_blockid;
5556 tc->blockNode = pn1;
5559 pn = Variables(cx, ts, tc, false);
5560 if (!pn)
5561 return NULL;
5562 pn->pn_xflags = PNX_POPVAR;
5563 break;
5565 #endif /* JS_HAS_BLOCK_SCOPE */
5567 case TOK_RETURN:
5568 pn = ReturnOrYield(cx, ts, tc, Expr);
5569 if (!pn)
5570 return NULL;
5571 break;
5573 case TOK_LC:
5575 uintN oldflags;
5577 oldflags = tc->flags;
5578 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5579 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5580 return NULL;
5581 pn = Statements(cx, ts, tc);
5582 if (!pn)
5583 return NULL;
5585 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5586 PopStatement(tc);
5589 * If we contain a function statement and our container is top-level
5590 * or another block, flag pn to preserve braces when decompiling.
5592 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5593 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5594 pn->pn_xflags |= PNX_NEEDBRACES;
5596 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5597 return pn;
5600 case TOK_EOL:
5601 case TOK_SEMI:
5602 pn = NewParseNode(PN_UNARY, tc);
5603 if (!pn)
5604 return NULL;
5605 pn->pn_type = TOK_SEMI;
5606 return pn;
5608 #if JS_HAS_DEBUGGER_KEYWORD
5609 case TOK_DEBUGGER:
5610 pn = NewParseNode(PN_NULLARY, tc);
5611 if (!pn)
5612 return NULL;
5613 pn->pn_type = TOK_DEBUGGER;
5614 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5615 break;
5616 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5618 #if JS_HAS_XML_SUPPORT
5619 case TOK_DEFAULT:
5620 pn = NewParseNode(PN_UNARY, tc);
5621 if (!pn)
5622 return NULL;
5623 if (!js_MatchToken(cx, ts, TOK_NAME) ||
5624 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
5625 !js_MatchToken(cx, ts, TOK_NAME) ||
5626 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
5627 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
5628 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
5629 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5630 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5631 return NULL;
5634 /* Is this an E4X dagger I see before me? */
5635 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5636 pn2 = Expr(cx, ts, tc);
5637 if (!pn2)
5638 return NULL;
5639 pn->pn_op = JSOP_DEFXMLNS;
5640 pn->pn_pos.end = pn2->pn_pos.end;
5641 pn->pn_kid = pn2;
5642 break;
5643 #endif
5645 case TOK_ERROR:
5646 return NULL;
5648 default:
5649 #if JS_HAS_XML_SUPPORT
5650 expression:
5651 #endif
5652 js_UngetToken(ts);
5653 pn2 = Expr(cx, ts, tc);
5654 if (!pn2)
5655 return NULL;
5657 if (js_PeekToken(cx, ts) == TOK_COLON) {
5658 if (pn2->pn_type != TOK_NAME) {
5659 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5660 JSMSG_BAD_LABEL);
5661 return NULL;
5663 label = pn2->pn_atom;
5664 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5665 if (stmt->type == STMT_LABEL && stmt->label == label) {
5666 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5667 JSMSG_DUPLICATE_LABEL);
5668 return NULL;
5671 ForgetUse(pn2);
5673 (void) js_GetToken(cx, ts);
5675 /* Push a label struct and parse the statement. */
5676 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5677 stmtInfo.label = label;
5678 pn = Statement(cx, ts, tc);
5679 if (!pn)
5680 return NULL;
5682 /* Normalize empty statement to empty block for the decompiler. */
5683 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5684 pn->pn_type = TOK_LC;
5685 pn->pn_arity = PN_LIST;
5686 pn->makeEmpty();
5689 /* Pop the label, set pn_expr, and return early. */
5690 PopStatement(tc);
5691 pn2->pn_type = TOK_COLON;
5692 pn2->pn_pos.end = pn->pn_pos.end;
5693 pn2->pn_expr = pn;
5694 return pn2;
5697 pn = NewParseNode(PN_UNARY, tc);
5698 if (!pn)
5699 return NULL;
5700 pn->pn_type = TOK_SEMI;
5701 pn->pn_pos = pn2->pn_pos;
5702 pn->pn_kid = pn2;
5705 * Specialize JSOP_SETPROP into JSOP_SETMETHOD to defer or avoid null
5706 * closure cloning. Do this here rather than in AssignExpr as only now
5707 * do we know that the uncloned (unjoined in ES3 terms) function object
5708 * result of the assignment expression can't escape.
5710 if (PN_TYPE(pn2) == TOK_ASSIGN && PN_OP(pn2) == JSOP_NOP &&
5711 PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5712 PN_OP(pn2->pn_right) == JSOP_LAMBDA &&
5713 !(pn2->pn_right->pn_funbox->tcflags
5714 & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME))) {
5715 pn2->pn_left->pn_op = JSOP_SETMETHOD;
5717 break;
5720 /* Check termination of this primitive statement. */
5721 return MatchOrInsertSemicolon(cx, ts) ? pn : NULL;
5724 static void
5725 NoteArgumentsUse(JSTreeContext *tc)
5727 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
5728 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5729 if (tc->funbox)
5730 tc->funbox->node->pn_dflags |= PND_FUNARG;
5733 static JSParseNode *
5734 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, bool inLetHead)
5736 JSTokenType tt;
5737 bool let;
5738 JSStmtInfo *scopeStmt;
5739 BindData data;
5740 JSParseNode *pn, *pn2;
5741 JSAtom *atom;
5744 * The three options here are:
5745 * - TOK_LET: We are parsing a let declaration.
5746 * - TOK_LP: We are parsing the head of a let block.
5747 * - Otherwise, we're parsing var declarations.
5749 tt = CURRENT_TOKEN(ts).type;
5750 let = (tt == TOK_LET || tt == TOK_LP);
5751 JS_ASSERT(let || tt == TOK_VAR);
5753 #if JS_HAS_BLOCK_SCOPE
5754 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5755 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5756 #endif
5758 /* Make sure that Statement set up the tree context correctly. */
5759 scopeStmt = tc->topScopeStmt;
5760 if (let) {
5761 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5762 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5763 scopeStmt = scopeStmt->downScope;
5765 JS_ASSERT(scopeStmt);
5768 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
5769 pn = NewParseNode(PN_LIST, tc);
5770 if (!pn)
5771 return NULL;
5772 pn->pn_op = data.op;
5773 pn->makeEmpty();
5776 * SpiderMonkey const is really "write once per initialization evaluation"
5777 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5778 * this code will change soon.
5780 if (let) {
5781 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5782 data.binder = BindLet;
5783 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5784 } else {
5785 data.binder = BindVarOrConst;
5788 do {
5789 tt = js_GetToken(cx, ts);
5790 #if JS_HAS_DESTRUCTURING
5791 if (tt == TOK_LB || tt == TOK_LC) {
5792 tc->flags |= TCF_DECL_DESTRUCTURING;
5793 pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
5794 tc->flags &= ~TCF_DECL_DESTRUCTURING;
5795 if (!pn2)
5796 return NULL;
5798 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
5799 return NULL;
5800 if ((tc->flags & TCF_IN_FOR_INIT) &&
5801 js_PeekToken(cx, ts) == TOK_IN) {
5802 pn->append(pn2);
5803 continue;
5806 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5807 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5808 goto bad_var_init;
5810 #if JS_HAS_BLOCK_SCOPE
5811 if (popScope) {
5812 tc->topStmt = save->down;
5813 tc->topScopeStmt = saveScope->downScope;
5815 #endif
5816 JSParseNode *init = AssignExpr(cx, ts, tc);
5817 #if JS_HAS_BLOCK_SCOPE
5818 if (popScope) {
5819 tc->topStmt = save;
5820 tc->topScopeStmt = saveScope;
5822 #endif
5824 if (!init || !UndominateInitializers(pn2, init, tc))
5825 return NULL;
5827 pn2 = NewBinary(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5828 if (!pn2)
5829 return NULL;
5830 pn->append(pn2);
5831 continue;
5833 #endif /* JS_HAS_DESTRUCTURING */
5835 if (tt != TOK_NAME) {
5836 if (tt != TOK_ERROR) {
5837 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5838 JSMSG_NO_VARIABLE_NAME);
5840 return NULL;
5843 atom = CURRENT_TOKEN(ts).t_atom;
5844 pn2 = NewBindingNode(atom, tc, let);
5845 if (!pn2)
5846 return NULL;
5847 if (data.op == JSOP_DEFCONST)
5848 pn2->pn_dflags |= PND_CONST;
5849 data.pn = pn2;
5850 if (!data.binder(cx, &data, atom, tc))
5851 return NULL;
5852 pn->append(pn2);
5854 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
5855 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
5856 goto bad_var_init;
5858 #if JS_HAS_BLOCK_SCOPE
5859 if (popScope) {
5860 tc->topStmt = save->down;
5861 tc->topScopeStmt = saveScope->downScope;
5863 #endif
5864 JSParseNode *init = AssignExpr(cx, ts, tc);
5865 #if JS_HAS_BLOCK_SCOPE
5866 if (popScope) {
5867 tc->topStmt = save;
5868 tc->topScopeStmt = saveScope;
5870 #endif
5871 if (!init)
5872 return NULL;
5874 if (pn2->pn_used) {
5875 pn2 = MakeAssignment(pn2, init, tc);
5876 if (!pn2)
5877 return NULL;
5878 } else {
5879 pn2->pn_expr = init;
5882 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5883 ? JSOP_SETNAME
5884 : (pn2->pn_dflags & PND_GVAR)
5885 ? JSOP_SETGVAR
5886 : (pn2->pn_dflags & PND_BOUND)
5887 ? JSOP_SETLOCAL
5888 : (data.op == JSOP_DEFCONST)
5889 ? JSOP_SETCONST
5890 : JSOP_SETNAME;
5892 NoteLValue(cx, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5894 /* The declarator's position must include the initializer. */
5895 pn2->pn_pos.end = init->pn_pos.end;
5897 if ((tc->flags & TCF_IN_FUNCTION) &&
5898 atom == cx->runtime->atomState.argumentsAtom) {
5899 NoteArgumentsUse(tc);
5900 if (!let)
5901 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5904 } while (js_MatchToken(cx, ts, TOK_COMMA));
5906 pn->pn_pos.end = pn->last()->pn_pos.end;
5907 return pn;
5909 bad_var_init:
5910 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
5911 JSMSG_BAD_VAR_INIT);
5912 return NULL;
5915 static JSParseNode *
5916 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5918 JSParseNode *pn, *pn2;
5920 pn = AssignExpr(cx, ts, tc);
5921 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
5922 pn2 = NewParseNode(PN_LIST, tc);
5923 if (!pn2)
5924 return NULL;
5925 pn2->pn_pos.begin = pn->pn_pos.begin;
5926 pn2->initList(pn);
5927 pn = pn2;
5928 do {
5929 #if JS_HAS_GENERATORS
5930 pn2 = pn->last();
5931 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
5932 js_ReportCompileErrorNumber(cx, ts, pn2, JSREPORT_ERROR,
5933 JSMSG_BAD_GENERATOR_SYNTAX,
5934 js_yield_str);
5935 return NULL;
5937 #endif
5938 pn2 = AssignExpr(cx, ts, tc);
5939 if (!pn2)
5940 return NULL;
5941 pn->append(pn2);
5942 } while (js_MatchToken(cx, ts, TOK_COMMA));
5943 pn->pn_pos.end = pn->last()->pn_pos.end;
5945 return pn;
5948 static JSParseNode *
5949 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
5951 JSParseNode *pn, *rhs;
5952 JSTokenType tt;
5953 JSOp op;
5955 JS_CHECK_RECURSION(cx, return NULL);
5957 #if JS_HAS_GENERATORS
5958 ts->flags |= TSF_OPERAND;
5959 if (js_MatchToken(cx, ts, TOK_YIELD)) {
5960 ts->flags &= ~TSF_OPERAND;
5961 return ReturnOrYield(cx, ts, tc, AssignExpr);
5963 ts->flags &= ~TSF_OPERAND;
5964 #endif
5966 pn = CondExpr(cx, ts, tc);
5967 if (!pn)
5968 return NULL;
5970 tt = js_GetToken(cx, ts);
5971 #if JS_HAS_GETTER_SETTER
5972 if (tt == TOK_NAME) {
5973 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
5974 if (tt == TOK_ERROR)
5975 return NULL;
5977 #endif
5978 if (tt != TOK_ASSIGN) {
5979 js_UngetToken(ts);
5980 return pn;
5983 op = CURRENT_TOKEN(ts).t_op;
5984 switch (pn->pn_type) {
5985 case TOK_NAME:
5986 if (!CheckStrictAssignment(cx, tc, pn))
5987 return NULL;
5988 pn->pn_op = JSOP_SETNAME;
5989 NoteLValue(cx, pn, tc);
5990 break;
5991 case TOK_DOT:
5992 pn->pn_op = JSOP_SETPROP;
5993 break;
5994 case TOK_LB:
5995 pn->pn_op = JSOP_SETELEM;
5996 break;
5997 #if JS_HAS_DESTRUCTURING
5998 case TOK_RB:
5999 case TOK_RC:
6000 if (op != JSOP_NOP) {
6001 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6002 JSMSG_BAD_DESTRUCT_ASS);
6003 return NULL;
6005 rhs = AssignExpr(cx, ts, tc);
6006 if (!rhs || !CheckDestructuring(cx, NULL, pn, rhs, tc))
6007 return NULL;
6008 return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
6009 #endif
6010 case TOK_LP:
6011 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
6012 return NULL;
6013 break;
6014 #if JS_HAS_XML_SUPPORT
6015 case TOK_UNARYOP:
6016 if (pn->pn_op == JSOP_XMLNAME) {
6017 pn->pn_op = JSOP_SETXMLNAME;
6018 break;
6020 /* FALL THROUGH */
6021 #endif
6022 default:
6023 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6024 JSMSG_BAD_LEFTSIDE_OF_ASS);
6025 return NULL;
6028 rhs = AssignExpr(cx, ts, tc);
6029 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
6030 JSDefinition *dn = pn->pn_lexdef;
6033 * If the definition is not flagged as assigned, we must have imputed
6034 * the initialized flag to it, to optimize for flat closures. But that
6035 * optimization uses source coordinates to check dominance relations,
6036 * so we must extend the end of the definition to cover the right-hand
6037 * side of this assignment, i.e., the initializer.
6039 if (!dn->isAssigned()) {
6040 JS_ASSERT(dn->isInitialized());
6041 dn->pn_pos.end = rhs->pn_pos.end;
6045 return NewBinary(TOK_ASSIGN, op, pn, rhs, tc);
6048 static JSParseNode *
6049 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6051 JSParseNode *pn, *pn1, *pn2, *pn3;
6052 uintN oldflags;
6054 pn = OrExpr(cx, ts, tc);
6055 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
6056 pn1 = pn;
6057 pn = NewParseNode(PN_TERNARY, tc);
6058 if (!pn)
6059 return NULL;
6061 * Always accept the 'in' operator in the middle clause of a ternary,
6062 * where it's unambiguous, even if we might be parsing the init of a
6063 * for statement.
6065 oldflags = tc->flags;
6066 tc->flags &= ~TCF_IN_FOR_INIT;
6067 pn2 = AssignExpr(cx, ts, tc);
6068 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
6070 if (!pn2)
6071 return NULL;
6072 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
6073 pn3 = AssignExpr(cx, ts, tc);
6074 if (!pn3)
6075 return NULL;
6076 pn->pn_pos.begin = pn1->pn_pos.begin;
6077 pn->pn_pos.end = pn3->pn_pos.end;
6078 pn->pn_kid1 = pn1;
6079 pn->pn_kid2 = pn2;
6080 pn->pn_kid3 = pn3;
6082 return pn;
6085 static JSParseNode *
6086 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6088 JSParseNode *pn;
6090 pn = AndExpr(cx, ts, tc);
6091 while (pn && js_MatchToken(cx, ts, TOK_OR))
6092 pn = NewBinary(TOK_OR, JSOP_OR, pn, AndExpr(cx, ts, tc), tc);
6093 return pn;
6096 static JSParseNode *
6097 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6099 JSParseNode *pn;
6101 pn = BitOrExpr(cx, ts, tc);
6102 while (pn && js_MatchToken(cx, ts, TOK_AND))
6103 pn = NewBinary(TOK_AND, JSOP_AND, pn, BitOrExpr(cx, ts, tc), tc);
6104 return pn;
6107 static JSParseNode *
6108 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6110 JSParseNode *pn;
6112 pn = BitXorExpr(cx, ts, tc);
6113 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
6114 pn = NewBinary(TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
6115 tc);
6117 return pn;
6120 static JSParseNode *
6121 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6123 JSParseNode *pn;
6125 pn = BitAndExpr(cx, ts, tc);
6126 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
6127 pn = NewBinary(TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
6128 tc);
6130 return pn;
6133 static JSParseNode *
6134 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6136 JSParseNode *pn;
6138 pn = EqExpr(cx, ts, tc);
6139 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
6140 pn = NewBinary(TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
6141 return pn;
6144 static JSParseNode *
6145 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6147 JSParseNode *pn;
6148 JSOp op;
6150 pn = RelExpr(cx, ts, tc);
6151 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
6152 op = CURRENT_TOKEN(ts).t_op;
6153 pn = NewBinary(TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
6155 return pn;
6158 static JSParseNode *
6159 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6161 JSParseNode *pn;
6162 JSTokenType tt;
6163 JSOp op;
6164 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6167 * Uses of the in operator in ShiftExprs are always unambiguous,
6168 * so unset the flag that prohibits recognizing it.
6170 tc->flags &= ~TCF_IN_FOR_INIT;
6172 pn = ShiftExpr(cx, ts, tc);
6173 while (pn &&
6174 (js_MatchToken(cx, ts, TOK_RELOP) ||
6176 * Recognize the 'in' token as an operator only if we're not
6177 * currently in the init expr of a for loop.
6179 (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
6180 js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
6181 tt = CURRENT_TOKEN(ts).type;
6182 op = CURRENT_TOKEN(ts).t_op;
6183 pn = NewBinary(tt, op, pn, ShiftExpr(cx, ts, tc), tc);
6185 /* Restore previous state of inForInit flag. */
6186 tc->flags |= inForInitFlag;
6188 return pn;
6191 static JSParseNode *
6192 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6194 JSParseNode *pn;
6195 JSOp op;
6197 pn = AddExpr(cx, ts, tc);
6198 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
6199 op = CURRENT_TOKEN(ts).t_op;
6200 pn = NewBinary(TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
6202 return pn;
6205 static JSParseNode *
6206 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6208 JSParseNode *pn;
6209 JSTokenType tt;
6210 JSOp op;
6212 pn = MulExpr(cx, ts, tc);
6213 while (pn &&
6214 (js_MatchToken(cx, ts, TOK_PLUS) ||
6215 js_MatchToken(cx, ts, TOK_MINUS))) {
6216 tt = CURRENT_TOKEN(ts).type;
6217 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6218 pn = NewBinary(tt, op, pn, MulExpr(cx, ts, tc), tc);
6220 return pn;
6223 static JSParseNode *
6224 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6226 JSParseNode *pn;
6227 JSTokenType tt;
6228 JSOp op;
6230 pn = UnaryExpr(cx, ts, tc);
6231 while (pn &&
6232 (js_MatchToken(cx, ts, TOK_STAR) ||
6233 js_MatchToken(cx, ts, TOK_DIVOP))) {
6234 tt = CURRENT_TOKEN(ts).type;
6235 op = CURRENT_TOKEN(ts).t_op;
6236 pn = NewBinary(tt, op, pn, UnaryExpr(cx, ts, tc), tc);
6238 return pn;
6241 static JSParseNode *
6242 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6243 JSParseNode *pn, JSParseNode *kid, const char *name)
6245 if (kid->pn_type != TOK_NAME &&
6246 kid->pn_type != TOK_DOT &&
6247 (kid->pn_type != TOK_LP ||
6248 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6249 #if JS_HAS_XML_SUPPORT
6250 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6251 #endif
6252 kid->pn_type != TOK_LB) {
6253 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6254 JSMSG_BAD_OPERAND, name);
6255 return NULL;
6257 if (!CheckStrictAssignment(cx, tc, kid))
6258 return NULL;
6259 pn->pn_kid = kid;
6260 return kid;
6263 static const char incop_name_str[][10] = {"increment", "decrement"};
6265 static JSBool
6266 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6267 JSParseNode *pn, JSParseNode *kid,
6268 JSTokenType tt, JSBool preorder)
6270 JSOp op;
6272 kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6273 if (!kid)
6274 return JS_FALSE;
6275 switch (kid->pn_type) {
6276 case TOK_NAME:
6277 op = (tt == TOK_INC)
6278 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6279 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6280 NoteLValue(cx, kid, tc);
6281 break;
6283 case TOK_DOT:
6284 op = (tt == TOK_INC)
6285 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6286 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6287 break;
6289 case TOK_LP:
6290 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6291 return JS_FALSE;
6292 /* FALL THROUGH */
6293 #if JS_HAS_XML_SUPPORT
6294 case TOK_UNARYOP:
6295 if (kid->pn_op == JSOP_XMLNAME)
6296 kid->pn_op = JSOP_SETXMLNAME;
6297 /* FALL THROUGH */
6298 #endif
6299 case TOK_LB:
6300 op = (tt == TOK_INC)
6301 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6302 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6303 break;
6305 default:
6306 JS_ASSERT(0);
6307 op = JSOP_NOP;
6309 pn->pn_op = op;
6310 return JS_TRUE;
6313 static JSParseNode *
6314 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
6316 JSTokenType tt;
6317 JSParseNode *pn, *pn2;
6319 JS_CHECK_RECURSION(cx, return NULL);
6321 ts->flags |= TSF_OPERAND;
6322 tt = js_GetToken(cx, ts);
6323 ts->flags &= ~TSF_OPERAND;
6325 switch (tt) {
6326 case TOK_UNARYOP:
6327 case TOK_PLUS:
6328 case TOK_MINUS:
6329 pn = NewParseNode(PN_UNARY, tc);
6330 if (!pn)
6331 return NULL;
6332 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6333 pn->pn_op = CURRENT_TOKEN(ts).t_op;
6334 pn2 = UnaryExpr(cx, ts, tc);
6335 if (!pn2)
6336 return NULL;
6337 pn->pn_pos.end = pn2->pn_pos.end;
6338 pn->pn_kid = pn2;
6339 break;
6341 case TOK_INC:
6342 case TOK_DEC:
6343 pn = NewParseNode(PN_UNARY, tc);
6344 if (!pn)
6345 return NULL;
6346 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
6347 if (!pn2)
6348 return NULL;
6349 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
6350 return NULL;
6351 pn->pn_pos.end = pn2->pn_pos.end;
6352 break;
6354 case TOK_DELETE:
6355 pn = NewParseNode(PN_UNARY, tc);
6356 if (!pn)
6357 return NULL;
6358 pn2 = UnaryExpr(cx, ts, tc);
6359 if (!pn2)
6360 return NULL;
6361 pn->pn_pos.end = pn2->pn_pos.end;
6364 * Under ECMA3, deleting any unary expression is valid -- it simply
6365 * returns true. Here we fold constants before checking for a call
6366 * expression, in order to rule out delete of a generator expression.
6368 if (!js_FoldConstants(cx, pn2, tc))
6369 return NULL;
6370 switch (pn2->pn_type) {
6371 case TOK_LP:
6372 if (pn2->pn_op != JSOP_SETCALL &&
6373 !MakeSetCall(cx, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6374 return NULL;
6376 break;
6377 case TOK_NAME:
6378 if (!js_ReportStrictModeError(cx, ts, tc, pn, JSMSG_DEPRECATED_DELETE_OPERAND))
6379 return NULL;
6380 pn2->pn_op = JSOP_DELNAME;
6381 break;
6382 default:;
6384 pn->pn_kid = pn2;
6385 break;
6387 case TOK_ERROR:
6388 return NULL;
6390 default:
6391 js_UngetToken(ts);
6392 pn = MemberExpr(cx, ts, tc, JS_TRUE);
6393 if (!pn)
6394 return NULL;
6396 /* Don't look across a newline boundary for a postfix incop. */
6397 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
6398 ts->flags |= TSF_OPERAND;
6399 tt = js_PeekTokenSameLine(cx, ts);
6400 ts->flags &= ~TSF_OPERAND;
6401 if (tt == TOK_INC || tt == TOK_DEC) {
6402 (void) js_GetToken(cx, ts);
6403 pn2 = NewParseNode(PN_UNARY, tc);
6404 if (!pn2)
6405 return NULL;
6406 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
6407 return NULL;
6408 pn2->pn_pos.begin = pn->pn_pos.begin;
6409 pn = pn2;
6412 break;
6414 return pn;
6417 #if JS_HAS_GENERATORS
6420 * A dedicated helper for transplanting the comprehension expression E in
6422 * [E for (V in I)] // array comprehension
6423 * (E for (V in I)) // generator expression
6425 * from its initial location in the AST, on the left of the 'for', to its final
6426 * position on the right. To avoid a separate pass we do this by adjusting the
6427 * blockids and name binding links that were established when E was parsed.
6429 * A generator expression desugars like so:
6431 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6433 * so the transplanter must adjust static level as well as blockid. E's source
6434 * coordinates in root->pn_pos are critical to deciding which binding links to
6435 * preserve and which to cut.
6437 * NB: This is not a general tree transplanter -- it knows in particular that
6438 * the one or more bindings induced by V have not yet been created.
6440 class CompExprTransplanter {
6441 JSParseNode *root;
6442 JSTreeContext *tc;
6443 bool genexp;
6444 uintN adjust;
6445 uintN funcLevel;
6447 public:
6448 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6449 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6453 bool transplant(JSParseNode *pn);
6457 * Any definitions nested within the comprehension expression of a generator
6458 * expression must move "down" one static level, which of course increases the
6459 * upvar-frame-skip count.
6461 static bool
6462 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6464 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6465 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6467 JS_ASSERT(level >= tc->staticLevel);
6468 if (level >= FREE_STATIC_LEVEL) {
6469 JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
6470 JSMSG_TOO_DEEP, js_function_str);
6471 return false;
6474 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6476 return true;
6479 static void
6480 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6482 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6483 pn->pn_blockid += adjust;
6484 if (pn->pn_blockid >= tc->blockidGen)
6485 tc->blockidGen = pn->pn_blockid + 1;
6488 bool
6489 CompExprTransplanter::transplant(JSParseNode *pn)
6491 if (!pn)
6492 return true;
6494 switch (pn->pn_arity) {
6495 case PN_LIST:
6496 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6497 transplant(pn2);
6498 if (pn->pn_pos >= root->pn_pos)
6499 AdjustBlockId(pn, adjust, tc);
6500 break;
6502 case PN_TERNARY:
6503 transplant(pn->pn_kid1);
6504 transplant(pn->pn_kid2);
6505 transplant(pn->pn_kid3);
6506 break;
6508 case PN_BINARY:
6509 transplant(pn->pn_left);
6511 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6512 if (pn->pn_right != pn->pn_left)
6513 transplant(pn->pn_right);
6514 break;
6516 case PN_UNARY:
6517 transplant(pn->pn_kid);
6518 break;
6520 case PN_FUNC:
6523 * Only the first level of transplant recursion through functions needs
6524 * to reparent the funbox, since all descendant functions are correctly
6525 * linked under the top-most funbox. But every visit to this case needs
6526 * to update funbox->level.
6528 * Recall that funbox->level is the static level of the code containing
6529 * the definition or expression of the function and not the static level
6530 * of the function's body.
6532 JSFunctionBox *funbox = pn->pn_funbox;
6534 funbox->level = tc->staticLevel + funcLevel;
6535 if (++funcLevel == 1 && genexp) {
6536 JSFunctionBox *parent = tc->funbox;
6538 JSFunctionBox **funboxp = &tc->parent->functionList;
6539 while (*funboxp != funbox)
6540 funboxp = &(*funboxp)->siblings;
6541 *funboxp = funbox->siblings;
6543 funbox->parent = parent;
6544 funbox->siblings = parent->kids;
6545 parent->kids = funbox;
6546 funbox->level = tc->staticLevel;
6548 /* FALL THROUGH */
6551 case PN_NAME:
6552 transplant(pn->maybeExpr());
6553 if (pn->pn_arity == PN_FUNC)
6554 --funcLevel;
6556 if (pn->pn_defn) {
6557 if (genexp && !BumpStaticLevel(pn, tc))
6558 return false;
6559 } else if (pn->pn_used) {
6560 JS_ASSERT(pn->pn_op != JSOP_NOP);
6561 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6563 JSDefinition *dn = pn->pn_lexdef;
6564 JS_ASSERT(dn->pn_defn);
6567 * Adjust the definition's block id only if it is a placeholder not
6568 * to the left of the root node, and if pn is the last use visited
6569 * in the comprehension expression (to avoid adjusting the blockid
6570 * multiple times).
6572 * Non-placeholder definitions within the comprehension expression
6573 * will be visited further below.
6575 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6576 if (genexp && !BumpStaticLevel(dn, tc))
6577 return false;
6578 AdjustBlockId(dn, adjust, tc);
6581 JSAtom *atom = pn->pn_atom;
6582 #ifdef DEBUG
6583 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6584 JS_ASSERT(!stmt || stmt != tc->topStmt);
6585 #endif
6586 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6587 JS_ASSERT(!tc->decls.lookup(atom));
6589 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6590 JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
6591 if (!ale)
6592 return false;
6594 if (dn->pn_pos >= root->pn_pos) {
6595 tc->parent->lexdeps.remove(tc->compiler, atom);
6596 } else {
6597 JSDefinition *dn2 = (JSDefinition *)
6598 NewNameNode(tc->compiler->context, dn->pn_atom, tc);
6599 if (!dn2)
6600 return false;
6602 dn2->pn_type = dn->pn_type;
6603 dn2->pn_pos = root->pn_pos;
6604 dn2->pn_defn = true;
6605 dn2->pn_dflags |= PND_PLACEHOLDER;
6607 JSParseNode **pnup = &dn->dn_uses;
6608 JSParseNode *pnu;
6609 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6610 pnu->pn_lexdef = dn2;
6611 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6612 pnup = &pnu->pn_link;
6614 dn2->dn_uses = dn->dn_uses;
6615 dn->dn_uses = *pnup;
6616 *pnup = NULL;
6618 dn = dn2;
6621 ALE_SET_DEFN(ale, dn);
6626 if (pn->pn_pos >= root->pn_pos)
6627 AdjustBlockId(pn, adjust, tc);
6628 break;
6630 case PN_NAMESET:
6631 transplant(pn->pn_tree);
6632 break;
6634 return true;
6638 * Starting from a |for| keyword after the first array initialiser element or
6639 * an expression in an open parenthesis, parse the tail of the comprehension
6640 * or generator expression signified by this |for| keyword in context.
6642 * Return null on failure, else return the top-most parse node for the array
6643 * comprehension or generator expression, with a unary node as the body of the
6644 * (possibly nested) for-loop, initialized by |type, op, kid|.
6646 static JSParseNode *
6647 ComprehensionTail(JSParseNode *kid, uintN blockid, JSTreeContext *tc,
6648 JSTokenType type = TOK_SEMI, JSOp op = JSOP_NOP)
6650 JSContext *cx = tc->compiler->context;
6651 JSTokenStream *ts = TS(tc->compiler);
6653 uintN adjust;
6654 JSParseNode *pn, *pn2, *pn3, **pnp;
6655 JSStmtInfo stmtInfo;
6656 BindData data;
6657 JSTokenType tt;
6658 JSAtom *atom;
6660 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_FOR);
6662 if (type == TOK_SEMI) {
6664 * Generator expression desugars to an immediately applied lambda that
6665 * yields the next value from a for-in loop (possibly nested, and with
6666 * optional if guard). Make pn be the TOK_LC body node.
6668 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6669 if (!pn)
6670 return NULL;
6671 adjust = pn->pn_blockid - blockid;
6672 } else {
6673 JS_ASSERT(type == TOK_ARRAYPUSH);
6676 * Make a parse-node and literal object representing the block scope of
6677 * this array comprehension. Our caller in PrimaryExpr, the TOK_LB case
6678 * aka the array initialiser case, has passed the blockid to claim for
6679 * the comprehension's block scope. We allocate that id or one above it
6680 * here, by calling js_PushLexicalScope.
6682 * In the case of a comprehension expression that has nested blocks
6683 * (e.g., let expressions), we will allocate a higher blockid but then
6684 * slide all blocks "to the right" to make room for the comprehension's
6685 * block scope.
6687 adjust = tc->blockid();
6688 pn = PushLexicalScope(cx, ts, tc, &stmtInfo);
6689 if (!pn)
6690 return NULL;
6692 JS_ASSERT(blockid <= pn->pn_blockid);
6693 JS_ASSERT(blockid < tc->blockidGen);
6694 JS_ASSERT(tc->bodyid < blockid);
6695 pn->pn_blockid = stmtInfo.blockid = blockid;
6696 JS_ASSERT(adjust < blockid);
6697 adjust = blockid - adjust;
6700 pnp = &pn->pn_expr;
6702 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6703 transplanter.transplant(kid);
6705 data.pn = NULL;
6706 data.op = JSOP_NOP;
6707 data.binder = BindLet;
6708 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6710 do {
6712 * FOR node is binary, left is loop control and right is body. Use
6713 * index to count each block-local let-variable on the left-hand side
6714 * of the IN.
6716 pn2 = NewParseNode(PN_BINARY, tc);
6717 if (!pn2)
6718 return NULL;
6720 pn2->pn_op = JSOP_ITER;
6721 pn2->pn_iflags = JSITER_ENUMERATE;
6722 if (js_MatchToken(cx, ts, TOK_NAME)) {
6723 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
6724 pn2->pn_iflags |= JSITER_FOREACH;
6725 else
6726 js_UngetToken(ts);
6728 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6730 atom = NULL;
6731 tt = js_GetToken(cx, ts);
6732 switch (tt) {
6733 #if JS_HAS_DESTRUCTURING
6734 case TOK_LB:
6735 case TOK_LC:
6736 tc->flags |= TCF_DECL_DESTRUCTURING;
6737 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
6738 tc->flags &= ~TCF_DECL_DESTRUCTURING;
6739 if (!pn3)
6740 return NULL;
6741 break;
6742 #endif
6744 case TOK_NAME:
6745 atom = CURRENT_TOKEN(ts).t_atom;
6748 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6749 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6750 * in the operand stack frame. The code generator computes that,
6751 * and it tries to bind all names to slots, so we must let it do
6752 * the deed.
6754 pn3 = NewBindingNode(atom, tc, true);
6755 if (!pn3)
6756 return NULL;
6757 break;
6759 default:
6760 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6761 JSMSG_NO_VARIABLE_NAME);
6763 case TOK_ERROR:
6764 return NULL;
6767 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6768 JSParseNode *pn4 = Expr(cx, ts, tc);
6769 if (!pn4)
6770 return NULL;
6771 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6773 switch (tt) {
6774 #if JS_HAS_DESTRUCTURING
6775 case TOK_LB:
6776 case TOK_LC:
6777 if (!CheckDestructuring(cx, &data, pn3, NULL, tc))
6778 return NULL;
6780 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
6781 /* Destructuring requires [key, value] enumeration in JS1.7. */
6782 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6783 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6784 JSMSG_BAD_FOR_LEFTSIDE);
6785 return NULL;
6788 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6789 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6790 if (!(pn2->pn_iflags & JSITER_FOREACH))
6791 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6793 break;
6794 #endif
6796 case TOK_NAME:
6797 data.pn = pn3;
6798 if (!data.binder(cx, &data, atom, tc))
6799 return NULL;
6800 break;
6802 default:;
6805 pn2->pn_left = NewBinary(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6806 if (!pn2->pn_left)
6807 return NULL;
6808 *pnp = pn2;
6809 pnp = &pn2->pn_right;
6810 } while (js_MatchToken(cx, ts, TOK_FOR));
6812 if (js_MatchToken(cx, ts, TOK_IF)) {
6813 pn2 = NewParseNode(PN_TERNARY, tc);
6814 if (!pn2)
6815 return NULL;
6816 pn2->pn_kid1 = Condition(cx, ts, tc);
6817 if (!pn2->pn_kid1)
6818 return NULL;
6819 *pnp = pn2;
6820 pnp = &pn2->pn_kid2;
6823 pn2 = NewParseNode(PN_UNARY, tc);
6824 if (!pn2)
6825 return NULL;
6826 pn2->pn_type = type;
6827 pn2->pn_op = op;
6828 pn2->pn_kid = kid;
6829 *pnp = pn2;
6831 PopStatement(tc);
6832 return pn;
6835 #if JS_HAS_GENERATOR_EXPRS
6838 * Starting from a |for| keyword after an expression, parse the comprehension
6839 * tail completing this generator expression. Wrap the expression at kid in a
6840 * generator function that is immediately called to evaluate to the generator
6841 * iterator that is the value of this generator expression.
6843 * Callers pass a blank unary node via pn, which GeneratorExpr fills in as the
6844 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6845 * expression-statement node that constitutes the body of the |for| loop(s) in
6846 * the generator function.
6848 * Note how unlike Python, we do not evaluate the expression to the right of
6849 * the first |in| in the chain of |for| heads. Instead, a generator expression
6850 * is merely sugar for a generator function expression and its application.
6852 static JSParseNode *
6853 GeneratorExpr(JSParseNode *pn, JSParseNode *kid, JSTreeContext *tc)
6855 /* Initialize pn, connecting it to kid. */
6856 JS_ASSERT(pn->pn_arity == PN_UNARY);
6857 pn->pn_type = TOK_YIELD;
6858 pn->pn_op = JSOP_YIELD;
6859 pn->pn_parens = true;
6860 pn->pn_pos = kid->pn_pos;
6861 pn->pn_kid = kid;
6862 pn->pn_hidden = true;
6864 /* Make a new node for the desugared generator function. */
6865 JSParseNode *genfn = NewParseNode(PN_FUNC, tc);
6866 if (!genfn)
6867 return NULL;
6868 genfn->pn_type = TOK_FUNCTION;
6869 genfn->pn_op = JSOP_LAMBDA;
6870 JS_ASSERT(!genfn->pn_body);
6871 genfn->pn_dflags = PND_FUNARG;
6874 JSTreeContext gentc(tc->compiler);
6876 JSFunctionBox *funbox = EnterFunction(genfn, tc, &gentc);
6877 if (!funbox)
6878 return NULL;
6881 * We have to dance around a bit to propagate sharp variables from tc
6882 * to gentc before setting TCF_HAS_SHARPS implicitly by propagating all
6883 * of tc's TCF_FUN_FLAGS flags. As below, we have to be conservative by
6884 * leaving TCF_HAS_SHARPS set in tc if we do propagate to gentc.
6886 if (tc->flags & TCF_HAS_SHARPS) {
6887 gentc.flags |= TCF_IN_FUNCTION;
6888 if (!gentc.ensureSharpSlots())
6889 return NULL;
6893 * We assume conservatively that any deoptimization flag in tc->flags
6894 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6895 * propagate these flags into genfn. For code simplicity we also do
6896 * not detect if the flags were only set in the kid and could be
6897 * removed from tc->flags.
6899 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6900 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6901 funbox->tcflags |= gentc.flags;
6902 genfn->pn_funbox = funbox;
6903 genfn->pn_blockid = gentc.bodyid;
6905 JSParseNode *body = ComprehensionTail(pn, tc->blockid(), &gentc);
6906 if (!body)
6907 return NULL;
6908 JS_ASSERT(!genfn->pn_body);
6909 genfn->pn_body = body;
6910 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6911 genfn->pn_pos.end = body->pn_pos.end = CURRENT_TOKEN(TS(tc->compiler)).pos.end;
6913 if (!LeaveFunction(genfn, &gentc, tc))
6914 return NULL;
6918 * Our result is a call expression that invokes the anonymous generator
6919 * function object.
6921 JSParseNode *result = NewParseNode(PN_LIST, tc);
6922 if (!result)
6923 return NULL;
6924 result->pn_type = TOK_LP;
6925 result->pn_op = JSOP_CALL;
6926 result->pn_pos.begin = genfn->pn_pos.begin;
6927 result->initList(genfn);
6928 return result;
6931 static const char js_generator_str[] = "generator";
6933 #endif /* JS_HAS_GENERATOR_EXPRS */
6934 #endif /* JS_HAS_GENERATORS */
6936 static JSBool
6937 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
6938 JSParseNode *listNode)
6940 JSBool matched;
6942 ts->flags |= TSF_OPERAND;
6943 matched = js_MatchToken(cx, ts, TOK_RP);
6944 ts->flags &= ~TSF_OPERAND;
6945 if (!matched) {
6946 do {
6947 JSParseNode *argNode = AssignExpr(cx, ts, tc);
6948 if (!argNode)
6949 return JS_FALSE;
6950 #if JS_HAS_GENERATORS
6951 if (argNode->pn_type == TOK_YIELD &&
6952 !argNode->pn_parens &&
6953 js_PeekToken(cx, ts) == TOK_COMMA) {
6954 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6955 JSMSG_BAD_GENERATOR_SYNTAX,
6956 js_yield_str);
6957 return JS_FALSE;
6959 #endif
6960 #if JS_HAS_GENERATOR_EXPRS
6961 if (js_MatchToken(cx, ts, TOK_FOR)) {
6962 JSParseNode *pn = NewParseNode(PN_UNARY, tc);
6963 if (!pn)
6964 return JS_FALSE;
6965 argNode = GeneratorExpr(pn, argNode, tc);
6966 if (!argNode)
6967 return JS_FALSE;
6968 if (listNode->pn_count > 1 ||
6969 js_PeekToken(cx, ts) == TOK_COMMA) {
6970 js_ReportCompileErrorNumber(cx, ts, argNode, JSREPORT_ERROR,
6971 JSMSG_BAD_GENERATOR_SYNTAX,
6972 js_generator_str);
6973 return JS_FALSE;
6976 #endif
6977 listNode->append(argNode);
6978 } while (js_MatchToken(cx, ts, TOK_COMMA));
6980 if (js_GetToken(cx, ts) != TOK_RP) {
6981 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
6982 JSMSG_PAREN_AFTER_ARGS);
6983 return JS_FALSE;
6986 return JS_TRUE;
6989 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6990 static JSParseNode *
6991 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6993 while (pn->pn_type == TOK_RP)
6994 pn = pn->pn_kid;
6995 if (pn->pn_type == TOK_FUNCTION) {
6996 JS_ASSERT(pn->pn_arity == PN_FUNC);
6998 JSFunctionBox *funbox = pn->pn_funbox;
6999 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
7000 if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
7001 pn->pn_dflags &= ~PND_FUNARG;
7003 return pn;
7006 static JSParseNode *
7007 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7008 JSBool allowCallSyntax)
7010 JSParseNode *pn, *pn2, *pn3;
7011 JSTokenType tt;
7013 JS_CHECK_RECURSION(cx, return NULL);
7015 /* Check for new expression first. */
7016 ts->flags |= TSF_OPERAND;
7017 tt = js_GetToken(cx, ts);
7018 ts->flags &= ~TSF_OPERAND;
7019 if (tt == TOK_NEW) {
7020 pn = NewParseNode(PN_LIST, tc);
7021 if (!pn)
7022 return NULL;
7023 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
7024 if (!pn2)
7025 return NULL;
7026 pn2 = CheckForImmediatelyAppliedLambda(pn2);
7027 pn->pn_op = JSOP_NEW;
7028 pn->initList(pn2);
7029 pn->pn_pos.begin = pn2->pn_pos.begin;
7031 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
7032 return NULL;
7033 if (pn->pn_count > ARGC_LIMIT) {
7034 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7035 JSMSG_TOO_MANY_CON_ARGS);
7036 return NULL;
7038 pn->pn_pos.end = pn->last()->pn_pos.end;
7039 } else {
7040 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
7041 if (!pn)
7042 return NULL;
7044 if (pn->pn_type == TOK_ANYNAME ||
7045 pn->pn_type == TOK_AT ||
7046 pn->pn_type == TOK_DBLCOLON) {
7047 pn2 = NewOrRecycledNode(tc);
7048 if (!pn2)
7049 return NULL;
7050 pn2->pn_type = TOK_UNARYOP;
7051 pn2->pn_pos = pn->pn_pos;
7052 pn2->pn_op = JSOP_XMLNAME;
7053 pn2->pn_arity = PN_UNARY;
7054 pn2->pn_parens = false;
7055 pn2->pn_kid = pn;
7056 pn = pn2;
7060 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
7061 if (tt == TOK_DOT) {
7062 pn2 = NewNameNode(cx, NULL, tc);
7063 if (!pn2)
7064 return NULL;
7065 #if JS_HAS_XML_SUPPORT
7066 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
7067 tt = js_GetToken(cx, ts);
7068 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7069 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
7070 if (!pn3)
7071 return NULL;
7073 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
7074 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
7075 pn2->pn_op = JSOP_GETPROP;
7076 pn2->pn_expr = pn;
7077 pn2->pn_atom = pn3->pn_atom;
7078 RecycleTree(pn3, tc);
7079 } else {
7080 if (tt == TOK_LP) {
7081 pn2->pn_type = TOK_FILTER;
7082 pn2->pn_op = JSOP_FILTER;
7084 /* A filtering predicate is like a with statement. */
7085 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7086 } else if (TOKEN_TYPE_IS_XML(PN_TYPE(pn3))) {
7087 pn2->pn_type = TOK_LB;
7088 pn2->pn_op = JSOP_GETELEM;
7089 } else {
7090 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7091 JSMSG_NAME_AFTER_DOT);
7092 return NULL;
7094 pn2->pn_arity = PN_BINARY;
7095 pn2->pn_left = pn;
7096 pn2->pn_right = pn3;
7098 #else
7099 ts->flags |= TSF_KEYWORD_IS_NAME;
7100 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
7101 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7102 pn2->pn_op = JSOP_GETPROP;
7103 pn2->pn_expr = pn;
7104 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
7105 #endif
7106 pn2->pn_pos.begin = pn->pn_pos.begin;
7107 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7108 #if JS_HAS_XML_SUPPORT
7109 } else if (tt == TOK_DBLDOT) {
7110 pn2 = NewParseNode(PN_BINARY, tc);
7111 if (!pn2)
7112 return NULL;
7113 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
7114 tt = js_GetToken(cx, ts);
7115 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7116 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
7117 if (!pn3)
7118 return NULL;
7119 tt = PN_TYPE(pn3);
7120 if (tt == TOK_NAME && !pn3->pn_parens) {
7121 pn3->pn_type = TOK_STRING;
7122 pn3->pn_arity = PN_NULLARY;
7123 pn3->pn_op = JSOP_QNAMEPART;
7124 } else if (!TOKEN_TYPE_IS_XML(tt)) {
7125 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7126 JSMSG_NAME_AFTER_DOT);
7127 return NULL;
7129 pn2->pn_op = JSOP_DESCENDANTS;
7130 pn2->pn_left = pn;
7131 pn2->pn_right = pn3;
7132 pn2->pn_pos.begin = pn->pn_pos.begin;
7133 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7134 #endif
7135 } else if (tt == TOK_LB) {
7136 pn2 = NewParseNode(PN_BINARY, tc);
7137 if (!pn2)
7138 return NULL;
7139 pn3 = Expr(cx, ts, tc);
7140 if (!pn3)
7141 return NULL;
7143 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
7144 pn2->pn_pos.begin = pn->pn_pos.begin;
7145 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7148 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7149 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7150 * the interpreter from fast property access. However, if the
7151 * bracketed string is a uint32, we rewrite pn3 to be a number
7152 * instead of a string.
7154 do {
7155 if (pn3->pn_type == TOK_STRING) {
7156 jsuint index;
7158 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
7159 pn2->pn_type = TOK_DOT;
7160 pn2->pn_op = JSOP_GETPROP;
7161 pn2->pn_arity = PN_NAME;
7162 pn2->pn_expr = pn;
7163 pn2->pn_atom = pn3->pn_atom;
7164 break;
7166 pn3->pn_type = TOK_NUMBER;
7167 pn3->pn_op = JSOP_DOUBLE;
7168 pn3->pn_dval = index;
7170 pn2->pn_op = JSOP_GETELEM;
7171 pn2->pn_left = pn;
7172 pn2->pn_right = pn3;
7173 } while (0);
7174 } else if (allowCallSyntax && tt == TOK_LP) {
7175 pn2 = NewParseNode(PN_LIST, tc);
7176 if (!pn2)
7177 return NULL;
7178 pn2->pn_op = JSOP_CALL;
7180 /* CheckForImmediatelyAppliedLambda skips useless TOK_RP nodes. */
7181 pn = CheckForImmediatelyAppliedLambda(pn);
7182 if (pn->pn_op == JSOP_NAME) {
7183 if (pn->pn_atom == cx->runtime->atomState.evalAtom) {
7184 /* Select JSOP_EVAL and flag tc as heavyweight. */
7185 pn2->pn_op = JSOP_EVAL;
7186 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7188 } else if (pn->pn_op == JSOP_GETPROP) {
7189 if (pn->pn_atom == cx->runtime->atomState.applyAtom ||
7190 pn->pn_atom == cx->runtime->atomState.callAtom) {
7191 /* Select JSOP_APPLY given foo.apply(...). */
7192 pn2->pn_op = JSOP_APPLY;
7196 pn2->initList(pn);
7197 pn2->pn_pos.begin = pn->pn_pos.begin;
7199 if (!ArgumentList(cx, ts, tc, pn2))
7200 return NULL;
7201 if (pn2->pn_count > ARGC_LIMIT) {
7202 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
7203 JSMSG_TOO_MANY_FUN_ARGS);
7204 return NULL;
7206 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7207 } else {
7208 js_UngetToken(ts);
7209 return pn;
7212 pn = pn2;
7214 if (tt == TOK_ERROR)
7215 return NULL;
7216 return pn;
7219 static JSParseNode *
7220 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7222 uintN oldflags;
7223 JSParseNode *pn;
7226 * Always accept the 'in' operator in a parenthesized expression,
7227 * where it's unambiguous, even if we might be parsing the init of a
7228 * for statement.
7230 oldflags = tc->flags;
7231 tc->flags &= ~TCF_IN_FOR_INIT;
7232 pn = Expr(cx, ts, tc);
7233 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7234 return pn;
7237 #if JS_HAS_XML_SUPPORT
7239 static JSParseNode *
7240 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7242 JSParseNode *pn;
7244 pn = BracketedExpr(cx, ts, tc);
7245 if (!pn)
7246 return NULL;
7248 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7249 return pn;
7253 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7255 * AttributeIdentifier:
7256 * @ PropertySelector
7257 * @ QualifiedIdentifier
7258 * @ [ Expression ]
7260 * PropertySelector:
7261 * Identifier
7264 * QualifiedIdentifier:
7265 * PropertySelector :: PropertySelector
7266 * PropertySelector :: [ Expression ]
7268 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7270 * AttributeIdentifier:
7271 * @ QualifiedIdentifier
7272 * @ [ Expression ]
7274 * PropertySelector:
7275 * Identifier
7278 * QualifiedIdentifier:
7279 * PropertySelector :: PropertySelector
7280 * PropertySelector :: [ Expression ]
7281 * PropertySelector
7283 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7284 * for that rule to result in a name node, but ECMA-357 extends the grammar
7285 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7287 * QualifiedIdentifier:
7288 * PropertySelector QualifiedSuffix
7290 * QualifiedSuffix:
7291 * :: PropertySelector
7292 * :: [ Expression ]
7293 * /nothing/
7295 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7297 * PrimaryExpression:
7298 * Identifier QualifiedSuffix
7300 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7301 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7303 static JSParseNode *
7304 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7306 JSParseNode *pn;
7308 pn = NewParseNode(PN_NULLARY, tc);
7309 if (!pn)
7310 return NULL;
7311 if (pn->pn_type == TOK_STAR) {
7312 pn->pn_type = TOK_ANYNAME;
7313 pn->pn_op = JSOP_ANYNAME;
7314 pn->pn_atom = cx->runtime->atomState.starAtom;
7315 } else {
7316 JS_ASSERT(pn->pn_type == TOK_NAME);
7317 pn->pn_op = JSOP_QNAMEPART;
7318 pn->pn_arity = PN_NAME;
7319 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
7320 pn->pn_cookie = FREE_UPVAR_COOKIE;
7322 return pn;
7325 static JSParseNode *
7326 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7327 JSTreeContext *tc)
7329 JSParseNode *pn2, *pn3;
7330 JSTokenType tt;
7332 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
7333 pn2 = NewNameNode(cx, NULL, tc);
7334 if (!pn2)
7335 return NULL;
7337 /* Left operand of :: must be evaluated if it is an identifier. */
7338 if (pn->pn_op == JSOP_QNAMEPART)
7339 pn->pn_op = JSOP_NAME;
7341 ts->flags |= TSF_KEYWORD_IS_NAME;
7342 tt = js_GetToken(cx, ts);
7343 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7344 if (tt == TOK_STAR || tt == TOK_NAME) {
7345 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
7346 pn2->pn_op = JSOP_QNAMECONST;
7347 pn2->pn_pos.begin = pn->pn_pos.begin;
7348 pn2->pn_atom = (tt == TOK_STAR)
7349 ? cx->runtime->atomState.starAtom
7350 : CURRENT_TOKEN(ts).t_atom;
7351 pn2->pn_expr = pn;
7352 pn2->pn_cookie = FREE_UPVAR_COOKIE;
7353 return pn2;
7356 if (tt != TOK_LB) {
7357 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7358 JSMSG_SYNTAX_ERROR);
7359 return NULL;
7361 pn3 = EndBracketedExpr(cx, ts, tc);
7362 if (!pn3)
7363 return NULL;
7365 pn2->pn_op = JSOP_QNAME;
7366 pn2->pn_arity = PN_BINARY;
7367 pn2->pn_pos.begin = pn->pn_pos.begin;
7368 pn2->pn_pos.end = pn3->pn_pos.end;
7369 pn2->pn_left = pn;
7370 pn2->pn_right = pn3;
7371 return pn2;
7374 static JSParseNode *
7375 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7377 JSParseNode *pn;
7379 pn = PropertySelector(cx, ts, tc);
7380 if (!pn)
7381 return NULL;
7382 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7383 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7384 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7385 pn = QualifiedSuffix(cx, ts, pn, tc);
7387 return pn;
7390 static JSParseNode *
7391 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7393 JSParseNode *pn, *pn2;
7394 JSTokenType tt;
7396 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
7397 pn = NewParseNode(PN_UNARY, tc);
7398 if (!pn)
7399 return NULL;
7400 pn->pn_op = JSOP_TOATTRNAME;
7401 ts->flags |= TSF_KEYWORD_IS_NAME;
7402 tt = js_GetToken(cx, ts);
7403 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7404 if (tt == TOK_STAR || tt == TOK_NAME) {
7405 pn2 = QualifiedIdentifier(cx, ts, tc);
7406 } else if (tt == TOK_LB) {
7407 pn2 = EndBracketedExpr(cx, ts, tc);
7408 } else {
7409 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7410 JSMSG_SYNTAX_ERROR);
7411 return NULL;
7413 if (!pn2)
7414 return NULL;
7415 pn->pn_kid = pn2;
7416 return pn;
7420 * Make a TOK_LC unary node whose pn_kid is an expression.
7422 static JSParseNode *
7423 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
7425 JSParseNode *pn, *pn2;
7426 uintN oldflags;
7428 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
7429 pn = NewParseNode(PN_UNARY, tc);
7430 if (!pn)
7431 return NULL;
7434 * Turn off XML tag mode, but don't restore it after parsing this braced
7435 * expression. Instead, simply restore ts's old flags. This is required
7436 * because XMLExpr is called both from within a tag, and from within text
7437 * contained in an element, but outside of any start, end, or point tag.
7439 oldflags = ts->flags;
7440 ts->flags = oldflags & ~TSF_XMLTAGMODE;
7441 pn2 = Expr(cx, ts, tc);
7442 if (!pn2)
7443 return NULL;
7445 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7446 ts->flags = oldflags;
7447 pn->pn_kid = pn2;
7448 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7449 return pn;
7453 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7454 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7455 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7456 * child of a container tag.
7458 static JSParseNode *
7459 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7461 JSParseNode *pn;
7462 JSToken *tp;
7464 pn = NewParseNode(PN_NULLARY, tc);
7465 if (!pn)
7466 return NULL;
7467 tp = &CURRENT_TOKEN(ts);
7468 pn->pn_op = tp->t_op;
7469 pn->pn_atom = tp->t_atom;
7470 if (tp->type == TOK_XMLPI)
7471 pn->pn_atom2 = tp->t_atom2;
7472 return pn;
7476 * Parse the productions:
7478 * XMLNameExpr:
7479 * XMLName XMLNameExpr?
7480 * { Expr } XMLNameExpr?
7482 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7483 * a list of names and/or expressions, a single expression, or a single name.
7484 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7485 * will be TOK_LC.
7487 static JSParseNode *
7488 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
7490 JSParseNode *pn, *pn2, *list;
7491 JSTokenType tt;
7493 pn = list = NULL;
7494 do {
7495 tt = CURRENT_TOKEN(ts).type;
7496 if (tt == TOK_LC) {
7497 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7498 if (!pn2)
7499 return NULL;
7500 } else {
7501 JS_ASSERT(tt == TOK_XMLNAME);
7502 pn2 = XMLAtomNode(cx, ts, tc);
7503 if (!pn2)
7504 return NULL;
7507 if (!pn) {
7508 pn = pn2;
7509 } else {
7510 if (!list) {
7511 list = NewParseNode(PN_LIST, tc);
7512 if (!list)
7513 return NULL;
7514 list->pn_type = TOK_XMLNAME;
7515 list->pn_pos.begin = pn->pn_pos.begin;
7516 list->initList(pn);
7517 list->pn_xflags = PNX_CANTFOLD;
7518 pn = list;
7520 pn->pn_pos.end = pn2->pn_pos.end;
7521 pn->append(pn2);
7523 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
7525 js_UngetToken(ts);
7526 return pn;
7530 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7531 * at compile time into a JSXML tree.
7533 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7534 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7535 : (pn)->pn_type != TOK_LC)
7538 * Parse the productions:
7540 * XMLTagContent:
7541 * XMLNameExpr
7542 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7543 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7545 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7546 * produces a list of name and attribute values and/or braced expressions, a
7547 * single expression, or a single name.
7549 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7550 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7551 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7552 * we parsed exactly one expression.
7554 static JSParseNode *
7555 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7556 JSTokenType tagtype, JSAtom **namep)
7558 JSParseNode *pn, *pn2, *list;
7559 JSTokenType tt;
7561 pn = XMLNameExpr(cx, ts, tc);
7562 if (!pn)
7563 return NULL;
7564 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7565 list = NULL;
7567 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
7568 tt = js_GetToken(cx, ts);
7569 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7570 js_UngetToken(ts);
7571 break;
7574 pn2 = XMLNameExpr(cx, ts, tc);
7575 if (!pn2)
7576 return NULL;
7577 if (!list) {
7578 list = NewParseNode(PN_LIST, tc);
7579 if (!list)
7580 return NULL;
7581 list->pn_type = tagtype;
7582 list->pn_pos.begin = pn->pn_pos.begin;
7583 list->initList(pn);
7584 pn = list;
7586 pn->append(pn2);
7587 if (!XML_FOLDABLE(pn2))
7588 pn->pn_xflags |= PNX_CANTFOLD;
7590 js_MatchToken(cx, ts, TOK_XMLSPACE);
7591 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7592 js_MatchToken(cx, ts, TOK_XMLSPACE);
7594 tt = js_GetToken(cx, ts);
7595 if (tt == TOK_XMLATTR) {
7596 pn2 = XMLAtomNode(cx, ts, tc);
7597 } else if (tt == TOK_LC) {
7598 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
7599 pn->pn_xflags |= PNX_CANTFOLD;
7600 } else {
7601 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7602 JSMSG_BAD_XML_ATTR_VALUE);
7603 return NULL;
7605 if (!pn2)
7606 return NULL;
7607 pn->pn_pos.end = pn2->pn_pos.end;
7608 pn->append(pn2);
7611 return pn;
7614 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7615 JS_BEGIN_MACRO \
7616 if ((tt) <= TOK_EOF) { \
7617 if ((tt) == TOK_EOF) { \
7618 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
7619 JSMSG_END_OF_XML_SOURCE); \
7621 return result; \
7623 JS_END_MACRO
7625 static JSParseNode *
7626 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7627 JSBool allowList);
7630 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7631 * that opens the end tag for the container.
7633 static JSBool
7634 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
7635 JSTreeContext *tc)
7637 JSTokenType tt;
7638 JSParseNode *pn2;
7639 JSAtom *textAtom;
7641 ts->flags &= ~TSF_XMLTAGMODE;
7642 for (;;) {
7643 ts->flags |= TSF_XMLTEXTMODE;
7644 tt = js_GetToken(cx, ts);
7645 ts->flags &= ~TSF_XMLTEXTMODE;
7646 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7648 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7649 textAtom = CURRENT_TOKEN(ts).t_atom;
7650 if (textAtom) {
7651 /* Non-zero-length XML text scanned. */
7652 pn2 = XMLAtomNode(cx, ts, tc);
7653 if (!pn2)
7654 return JS_FALSE;
7655 pn->pn_pos.end = pn2->pn_pos.end;
7656 pn->append(pn2);
7659 ts->flags |= TSF_OPERAND;
7660 tt = js_GetToken(cx, ts);
7661 ts->flags &= ~TSF_OPERAND;
7662 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7663 if (tt == TOK_XMLETAGO)
7664 break;
7666 if (tt == TOK_LC) {
7667 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
7668 pn->pn_xflags |= PNX_CANTFOLD;
7669 } else if (tt == TOK_XMLSTAGO) {
7670 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
7671 if (pn2) {
7672 pn2->pn_xflags &= ~PNX_XMLROOT;
7673 pn->pn_xflags |= pn2->pn_xflags;
7675 } else {
7676 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7677 tt == TOK_XMLPI);
7678 pn2 = XMLAtomNode(cx, ts, tc);
7680 if (!pn2)
7681 return JS_FALSE;
7682 pn->pn_pos.end = pn2->pn_pos.end;
7683 pn->append(pn2);
7686 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
7687 ts->flags |= TSF_XMLTAGMODE;
7688 return JS_TRUE;
7692 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7694 static JSParseNode *
7695 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7696 JSBool allowList)
7698 JSParseNode *pn, *pn2, *list;
7699 JSTokenType tt;
7700 JSAtom *startAtom, *endAtom;
7702 JS_CHECK_RECURSION(cx, return NULL);
7704 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
7705 pn = NewParseNode(PN_LIST, tc);
7706 if (!pn)
7707 return NULL;
7709 ts->flags |= TSF_XMLTAGMODE;
7710 tt = js_GetToken(cx, ts);
7711 if (tt == TOK_ERROR)
7712 return NULL;
7714 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7716 * XMLElement. Append the tag and its contents, if any, to pn.
7718 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
7719 if (!pn2)
7720 return NULL;
7721 js_MatchToken(cx, ts, TOK_XMLSPACE);
7723 tt = js_GetToken(cx, ts);
7724 if (tt == TOK_XMLPTAGC) {
7725 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7726 if (pn2->pn_type == TOK_XMLSTAGO) {
7727 pn->makeEmpty();
7728 RecycleTree(pn, tc);
7729 pn = pn2;
7730 } else {
7731 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7732 pn2->pn_type == TOK_LC);
7733 pn->initList(pn2);
7734 if (!XML_FOLDABLE(pn2))
7735 pn->pn_xflags |= PNX_CANTFOLD;
7737 pn->pn_type = TOK_XMLPTAGC;
7738 pn->pn_xflags |= PNX_XMLROOT;
7739 } else {
7740 /* We had better have a tag-close (>) at this point. */
7741 if (tt != TOK_XMLTAGC) {
7742 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7743 JSMSG_BAD_XML_TAG_SYNTAX);
7744 return NULL;
7746 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7748 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7749 if (pn2->pn_type != TOK_XMLSTAGO) {
7750 pn->initList(pn2);
7751 if (!XML_FOLDABLE(pn2))
7752 pn->pn_xflags |= PNX_CANTFOLD;
7753 pn2 = pn;
7754 pn = NewParseNode(PN_LIST, tc);
7755 if (!pn)
7756 return NULL;
7759 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7760 pn->pn_type = TOK_XMLELEM;
7761 pn->pn_pos.begin = pn2->pn_pos.begin;
7762 pn->initList(pn2);
7763 if (!XML_FOLDABLE(pn2))
7764 pn->pn_xflags |= PNX_CANTFOLD;
7765 pn->pn_xflags |= PNX_XMLROOT;
7767 /* Get element contents and delimiting end-tag-open sequence. */
7768 if (!XMLElementContent(cx, ts, pn, tc))
7769 return NULL;
7771 tt = js_GetToken(cx, ts);
7772 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7773 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7774 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7775 JSMSG_BAD_XML_TAG_SYNTAX);
7776 return NULL;
7779 /* Parse end tag; check mismatch at compile-time if we can. */
7780 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
7781 if (!pn2)
7782 return NULL;
7783 if (pn2->pn_type == TOK_XMLETAGO) {
7784 /* Oops, end tag has attributes! */
7785 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7786 JSMSG_BAD_XML_TAG_SYNTAX);
7787 return NULL;
7789 if (endAtom && startAtom && endAtom != startAtom) {
7790 JSString *str = ATOM_TO_STRING(startAtom);
7792 /* End vs. start tag name mismatch: point to the tag name. */
7793 js_ReportCompileErrorNumber(cx, ts, pn2,
7794 JSREPORT_UC | JSREPORT_ERROR,
7795 JSMSG_XML_TAG_NAME_MISMATCH,
7796 str->chars());
7797 return NULL;
7800 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7801 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7802 list = NewParseNode(PN_LIST, tc);
7803 if (!list)
7804 return NULL;
7805 list->pn_type = TOK_XMLETAGO;
7806 list->initList(pn2);
7807 pn->append(list);
7808 if (!XML_FOLDABLE(pn2)) {
7809 list->pn_xflags |= PNX_CANTFOLD;
7810 pn->pn_xflags |= PNX_CANTFOLD;
7813 js_MatchToken(cx, ts, TOK_XMLSPACE);
7814 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7817 /* Set pn_op now that pn has been updated to its final value. */
7818 pn->pn_op = JSOP_TOXML;
7819 } else if (allowList && tt == TOK_XMLTAGC) {
7820 /* XMLList Initialiser. */
7821 pn->pn_type = TOK_XMLLIST;
7822 pn->pn_op = JSOP_TOXMLLIST;
7823 pn->makeEmpty();
7824 pn->pn_xflags |= PNX_XMLROOT;
7825 if (!XMLElementContent(cx, ts, pn, tc))
7826 return NULL;
7828 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7829 } else {
7830 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7831 JSMSG_BAD_XML_NAME_SYNTAX);
7832 return NULL;
7835 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
7836 ts->flags &= ~TSF_XMLTAGMODE;
7837 return pn;
7840 static JSParseNode *
7841 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7842 JSBool allowList)
7844 uint32 oldopts;
7845 JSParseNode *pn;
7848 * Force XML support to be enabled so that comments and CDATA literals
7849 * are recognized, instead of <! followed by -- starting an HTML comment
7850 * to end of line (used in script tags to hide content from old browsers
7851 * that don't recognize <script>).
7853 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
7854 pn = XMLElementOrList(cx, ts, tc, allowList);
7855 JS_SetOptions(cx, oldopts);
7856 return pn;
7859 JSParseNode *
7860 JSCompiler::parseXMLText(JSObject *chain, bool allowList)
7863 * Push a compiler frame if we have no frames, or if the top frame is a
7864 * lightweight function activation, or if its scope chain doesn't match
7865 * the one passed to us.
7867 JSTreeContext tc(this);
7868 tc.scopeChain = chain;
7870 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7871 TS(this)->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
7872 JSTokenType tt = js_GetToken(context, TS(this));
7873 TS(this)->flags &= ~TSF_OPERAND;
7875 JSParseNode *pn;
7876 if (tt != TOK_XMLSTAGO) {
7877 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
7878 JSMSG_BAD_XML_MARKUP);
7879 pn = NULL;
7880 } else {
7881 pn = XMLElementOrListRoot(context, TS(this), &tc, allowList);
7884 TS(this)->flags &= ~TSF_XMLONLYMODE;
7885 return pn;
7888 #endif /* JS_HAS_XMLSUPPORT */
7890 #if JS_HAS_BLOCK_SCOPE
7892 * Check whether blockid is an active scoping statement in tc. This code is
7893 * necessary to qualify tc->decls.lookup() hits in PrimaryExpr's TOK_NAME case
7894 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7895 * and let blocks and expressions (not let declarations).
7897 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7898 * due to hoisting, let in a for loop head, let block, or let expression acts
7899 * like Scheme's let: initializers are evaluated without the new let bindings
7900 * being in scope.
7902 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7903 * bindings push on the front of the tc->decls JSAtomList (either the singular
7904 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7905 * scope bindings of the same name.
7907 * This simplifies binding lookup code at the price of a linear search here,
7908 * but only if code uses let (var predominates), and even then this function's
7909 * loop iterates more than once only in crazy cases.
7911 static inline bool
7912 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7914 if (blockid > tc->blockid())
7915 return false;
7916 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7917 if (stmt->blockid == blockid)
7918 return true;
7920 return false;
7922 #endif
7924 static JSParseNode *
7925 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
7926 JSTokenType tt, JSBool afterDot)
7928 JSParseNode *pn, *pn2, *pn3;
7929 JSOp op;
7931 JS_CHECK_RECURSION(cx, return NULL);
7933 #if JS_HAS_GETTER_SETTER
7934 if (tt == TOK_NAME) {
7935 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
7936 if (tt == TOK_ERROR)
7937 return NULL;
7939 #endif
7941 switch (tt) {
7942 case TOK_FUNCTION:
7943 #if JS_HAS_XML_SUPPORT
7944 ts->flags |= TSF_KEYWORD_IS_NAME;
7945 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
7946 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7947 pn2 = NewParseNode(PN_NULLARY, tc);
7948 if (!pn2)
7949 return NULL;
7950 pn2->pn_type = TOK_FUNCTION;
7951 pn = QualifiedSuffix(cx, ts, pn2, tc);
7952 if (!pn)
7953 return NULL;
7954 break;
7956 ts->flags &= ~TSF_KEYWORD_IS_NAME;
7957 #endif
7958 pn = FunctionExpr(cx, ts, tc);
7959 if (!pn)
7960 return NULL;
7961 break;
7963 case TOK_LB:
7965 JSBool matched;
7966 jsuint index;
7968 pn = NewParseNode(PN_LIST, tc);
7969 if (!pn)
7970 return NULL;
7971 pn->pn_type = TOK_RB;
7972 pn->pn_op = JSOP_NEWINIT;
7973 pn->makeEmpty();
7975 #if JS_HAS_GENERATORS
7976 pn->pn_blockid = tc->blockidGen;
7977 #endif
7979 ts->flags |= TSF_OPERAND;
7980 matched = js_MatchToken(cx, ts, TOK_RB);
7981 ts->flags &= ~TSF_OPERAND;
7982 if (!matched) {
7983 for (index = 0; ; index++) {
7984 if (index == JS_ARGS_LENGTH_MAX) {
7985 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
7986 JSMSG_ARRAY_INIT_TOO_BIG);
7987 return NULL;
7990 ts->flags |= TSF_OPERAND;
7991 tt = js_PeekToken(cx, ts);
7992 ts->flags &= ~TSF_OPERAND;
7993 if (tt == TOK_RB) {
7994 pn->pn_xflags |= PNX_ENDCOMMA;
7995 break;
7998 if (tt == TOK_COMMA) {
7999 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
8000 js_MatchToken(cx, ts, TOK_COMMA);
8001 pn2 = NewParseNode(PN_NULLARY, tc);
8002 pn->pn_xflags |= PNX_HOLEY;
8003 } else {
8004 pn2 = AssignExpr(cx, ts, tc);
8006 if (!pn2)
8007 return NULL;
8008 pn->append(pn2);
8010 if (tt != TOK_COMMA) {
8011 /* If we didn't already match TOK_COMMA in above case. */
8012 if (!js_MatchToken(cx, ts, TOK_COMMA))
8013 break;
8017 #if JS_HAS_GENERATORS
8019 * At this point, (index == 0 && pn->pn_count != 0) implies one
8020 * element initialiser was parsed.
8022 * An array comprehension of the form:
8024 * [i * j for (i in o) for (j in p) if (i != j)]
8026 * translates to roughly the following let expression:
8028 * let (array = new Array, i, j) {
8029 * for (i in o) let {
8030 * for (j in p)
8031 * if (i != j)
8032 * array.push(i * j)
8034 * array
8037 * where array is a nameless block-local variable. The "roughly"
8038 * means that an implementation may optimize away the array.push.
8039 * An array comprehension opens exactly one block scope, no matter
8040 * how many for heads it contains.
8042 * Each let () {...} or for (let ...) ... compiles to:
8044 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
8046 * where <o> is a literal object representing the block scope,
8047 * with <n> properties, naming each var declared in the block.
8049 * Each var declaration in a let-block binds a name in <o> at
8050 * compile time, and allocates a slot on the operand stack at
8051 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
8052 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
8053 * JSOP_FORLOCAL. These ops all have an immediate operand, the
8054 * local slot's stack index from fp->spbase.
8056 * The array comprehension iteration step, array.push(i * j) in
8057 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
8058 * where <array> is the index of array's stack slot.
8060 if (index == 0 &&
8061 pn->pn_count != 0 &&
8062 js_MatchToken(cx, ts, TOK_FOR)) {
8063 JSParseNode *pnexp, *pntop;
8065 /* Relabel pn as an array comprehension node. */
8066 pn->pn_type = TOK_ARRAYCOMP;
8069 * Remove the comprehension expression from pn's linked list
8070 * and save it via pnexp. We'll re-install it underneath the
8071 * ARRAYPUSH node after we parse the rest of the comprehension.
8073 pnexp = pn->last();
8074 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
8075 pn->pn_tail = (--pn->pn_count == 1)
8076 ? &pn->pn_head->pn_next
8077 : &pn->pn_head;
8078 *pn->pn_tail = NULL;
8080 pntop = ComprehensionTail(pnexp, pn->pn_blockid, tc,
8081 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
8082 if (!pntop)
8083 return NULL;
8084 pn->append(pntop);
8086 #endif /* JS_HAS_GENERATORS */
8088 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
8090 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8091 return pn;
8094 case TOK_LC:
8096 JSBool afterComma;
8097 JSParseNode *pnval;
8100 * A map from property names we've seen thus far to bit masks.
8101 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
8102 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
8103 * if we've seen as getter, and both of those if we've just
8104 * seen an ordinary value.
8106 JSAutoAtomList seen(tc->compiler);
8108 pn = NewParseNode(PN_LIST, tc);
8109 if (!pn)
8110 return NULL;
8111 pn->pn_type = TOK_RC;
8112 pn->pn_op = JSOP_NEWINIT;
8113 pn->makeEmpty();
8115 afterComma = JS_FALSE;
8116 for (;;) {
8117 JSAtom *atom;
8118 ts->flags |= TSF_KEYWORD_IS_NAME;
8119 tt = js_GetToken(cx, ts);
8120 ts->flags &= ~TSF_KEYWORD_IS_NAME;
8121 switch (tt) {
8122 case TOK_NUMBER:
8123 pn3 = NewParseNode(PN_NULLARY, tc);
8124 if (!pn3)
8125 return NULL;
8126 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
8127 if (tc->needStrictChecks())
8128 atom = js_AtomizeDouble(cx, pn3->pn_dval);
8129 else
8130 atom = NULL; /* for the compiler */
8131 break;
8132 case TOK_NAME:
8133 #if JS_HAS_GETTER_SETTER
8135 atom = CURRENT_TOKEN(ts).t_atom;
8136 if (atom == cx->runtime->atomState.getAtom)
8137 op = JSOP_GETTER;
8138 else if (atom == cx->runtime->atomState.setAtom)
8139 op = JSOP_SETTER;
8140 else
8141 goto property_name;
8143 ts->flags |= TSF_KEYWORD_IS_NAME;
8144 tt = js_GetToken(cx, ts);
8145 ts->flags &= ~TSF_KEYWORD_IS_NAME;
8146 if (tt != TOK_NAME) {
8147 js_UngetToken(ts);
8148 goto property_name;
8150 atom = CURRENT_TOKEN(ts).t_atom;
8151 pn3 = NewNameNode(cx, atom, tc);
8152 if (!pn3)
8153 return NULL;
8155 /* We have to fake a 'function' token here. */
8156 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
8157 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
8158 pn2 = FunctionExpr(cx, ts, tc);
8159 pn2 = NewBinary(TOK_COLON, op, pn3, pn2, tc);
8160 goto skip;
8162 property_name:
8163 #endif
8164 case TOK_STRING:
8165 atom = CURRENT_TOKEN(ts).t_atom;
8166 pn3 = NewParseNode(PN_NULLARY, tc);
8167 if (!pn3)
8168 return NULL;
8169 pn3->pn_atom = atom;
8170 break;
8171 case TOK_RC:
8172 goto end_obj_init;
8173 default:
8174 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8175 JSMSG_BAD_PROP_ID);
8176 return NULL;
8179 tt = js_GetToken(cx, ts);
8180 #if JS_HAS_GETTER_SETTER
8181 if (tt == TOK_NAME) {
8182 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
8183 if (tt == TOK_ERROR)
8184 return NULL;
8186 #endif
8188 if (tt != TOK_COLON) {
8189 #if JS_HAS_DESTRUCTURING_SHORTHAND
8190 if (tt != TOK_COMMA && tt != TOK_RC) {
8191 #endif
8192 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8193 JSMSG_COLON_AFTER_ID);
8194 return NULL;
8195 #if JS_HAS_DESTRUCTURING_SHORTHAND
8199 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8200 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8202 js_UngetToken(ts);
8203 pn->pn_xflags |= PNX_DESTRUCT;
8204 pnval = pn3;
8205 if (pnval->pn_type == TOK_NAME) {
8206 pnval->pn_arity = PN_NAME;
8207 InitNameNodeCommon(pnval, tc);
8209 op = JSOP_NOP;
8210 #endif
8211 } else {
8212 op = CURRENT_TOKEN(ts).t_op;
8213 pnval = AssignExpr(cx, ts, tc);
8216 pn2 = NewBinary(TOK_COLON, op, pn3, pnval, tc);
8217 #if JS_HAS_GETTER_SETTER
8218 skip:
8219 #endif
8220 if (!pn2)
8221 return NULL;
8222 pn->append(pn2);
8225 * In strict mode code, check for duplicate property names. Treat
8226 * getters and setters as distinct attributes of each property. A
8227 * plain old value conflicts with a getter or a setter.
8229 if (tc->needStrictChecks()) {
8230 unsigned attributesMask;
8231 if (op == JSOP_NOP)
8232 attributesMask = JSPROP_GETTER | JSPROP_SETTER;
8233 else if (op == JSOP_GETTER)
8234 attributesMask = JSPROP_GETTER;
8235 else if (op == JSOP_SETTER)
8236 attributesMask = JSPROP_SETTER;
8237 else {
8238 JS_NOT_REACHED("bad opcode in object initializer");
8239 attributesMask = 0;
8242 JSAtomListElement *ale = seen.lookup(atom);
8243 if (ale) {
8244 if (ALE_INDEX(ale) & attributesMask) {
8245 const char *name = js_AtomToPrintableString(cx, atom);
8246 if (!name ||
8247 !js_ReportStrictModeError(cx, ts, tc, NULL,
8248 JSMSG_DUPLICATE_PROPERTY, name)) {
8249 return NULL;
8252 ALE_SET_INDEX(ale, attributesMask | ALE_INDEX(ale));
8253 } else {
8254 ale = seen.add(tc->compiler, atom);
8255 if (!ale)
8256 return NULL;
8257 ALE_SET_INDEX(ale, attributesMask);
8261 tt = js_GetToken(cx, ts);
8262 if (tt == TOK_RC)
8263 goto end_obj_init;
8264 if (tt != TOK_COMMA) {
8265 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8266 JSMSG_CURLY_AFTER_LIST);
8267 return NULL;
8269 afterComma = JS_TRUE;
8272 end_obj_init:
8273 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8274 return pn;
8277 #if JS_HAS_BLOCK_SCOPE
8278 case TOK_LET:
8279 pn = LetBlock(cx, ts, tc, JS_FALSE);
8280 if (!pn)
8281 return NULL;
8282 break;
8283 #endif
8285 #if JS_HAS_SHARP_VARS
8286 case TOK_DEFSHARP:
8287 pn = NewParseNode(PN_UNARY, tc);
8288 if (!pn)
8289 return NULL;
8290 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8291 ts->flags |= TSF_OPERAND;
8292 tt = js_GetToken(cx, ts);
8293 ts->flags &= ~TSF_OPERAND;
8294 if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
8295 #if JS_HAS_XML_SUPPORT
8296 tt == TOK_STAR || tt == TOK_AT ||
8297 tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
8298 #endif
8299 tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
8300 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8301 JSMSG_BAD_SHARP_VAR_DEF);
8302 return NULL;
8304 pn->pn_kid = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
8305 if (!pn->pn_kid)
8306 return NULL;
8307 if (!tc->ensureSharpSlots())
8308 return NULL;
8309 break;
8311 case TOK_USESHARP:
8312 /* Check for forward/dangling references at runtime, to allow eval. */
8313 pn = NewParseNode(PN_NULLARY, tc);
8314 if (!pn)
8315 return NULL;
8316 if (!tc->ensureSharpSlots())
8317 return NULL;
8318 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
8319 break;
8320 #endif /* JS_HAS_SHARP_VARS */
8322 case TOK_LP:
8324 JSBool genexp;
8326 pn = ParenExpr(cx, ts, tc, NULL, &genexp);
8327 if (!pn)
8328 return NULL;
8329 pn->pn_parens = true;
8330 if (!genexp)
8331 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8332 break;
8335 #if JS_HAS_XML_SUPPORT
8336 case TOK_STAR:
8337 pn = QualifiedIdentifier(cx, ts, tc);
8338 if (!pn)
8339 return NULL;
8340 break;
8342 case TOK_AT:
8343 pn = AttributeIdentifier(cx, ts, tc);
8344 if (!pn)
8345 return NULL;
8346 break;
8348 case TOK_XMLSTAGO:
8349 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
8350 if (!pn)
8351 return NULL;
8352 break;
8353 #endif /* JS_HAS_XML_SUPPORT */
8355 case TOK_STRING:
8356 #if JS_HAS_SHARP_VARS
8357 /* FALL THROUGH */
8358 #endif
8360 #if JS_HAS_XML_SUPPORT
8361 case TOK_XMLCDATA:
8362 case TOK_XMLCOMMENT:
8363 case TOK_XMLPI:
8364 #endif
8365 pn = NewParseNode(PN_NULLARY, tc);
8366 if (!pn)
8367 return NULL;
8368 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
8369 #if JS_HAS_XML_SUPPORT
8370 if (tt == TOK_XMLPI)
8371 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
8372 else
8373 #endif
8374 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8375 break;
8377 case TOK_NAME:
8378 pn = NewNameNode(cx, CURRENT_TOKEN(ts).t_atom, tc);
8379 if (!pn)
8380 return NULL;
8381 JS_ASSERT(CURRENT_TOKEN(ts).t_op == JSOP_NAME);
8382 pn->pn_op = JSOP_NAME;
8384 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8385 pn->pn_atom == cx->runtime->atomState.argumentsAtom) {
8387 * Flag arguments usage so we can avoid unsafe optimizations such
8388 * as formal parameter assignment analysis (because of the hated
8389 * feature whereby arguments alias formals). We do this even for
8390 * a reference of the form foo.arguments, which ancient code may
8391 * still use instead of arguments (more hate).
8393 NoteArgumentsUse(tc);
8396 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8397 * to do this work (new rule for the emitter to count on).
8399 if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8400 pn->pn_op = JSOP_ARGUMENTS;
8401 pn->pn_dflags |= PND_BOUND;
8403 } else if ((!afterDot
8404 #if JS_HAS_XML_SUPPORT
8405 || js_PeekToken(cx, ts) == TOK_DBLCOLON
8406 #endif
8407 ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8408 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8409 if (!stmt || stmt->type != STMT_WITH) {
8410 JSDefinition *dn;
8412 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8413 if (ale) {
8414 dn = ALE_DEFN(ale);
8415 #if JS_HAS_BLOCK_SCOPE
8417 * Skip out-of-scope let bindings along an ALE list or hash
8418 * chain. These can happen due to |let (x = x) x| block and
8419 * expression bindings, where the x on the right of = comes
8420 * from an outer scope. See bug 496532.
8422 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8423 do {
8424 ale = ALE_NEXT(ale);
8425 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8426 if (!ale)
8427 break;
8428 dn = ALE_DEFN(ale);
8430 #endif
8433 if (ale) {
8434 dn = ALE_DEFN(ale);
8435 } else {
8436 ale = tc->lexdeps.lookup(pn->pn_atom);
8437 if (ale) {
8438 dn = ALE_DEFN(ale);
8439 } else {
8441 * No definition before this use in any lexical scope.
8442 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8443 * new node for the forward-referenced definition. This
8444 * placeholder definition node will be adopted when we
8445 * parse the real defining declaration form, or left as
8446 * a free variable definition if we never see the real
8447 * definition.
8449 ale = MakePlaceholder(pn, tc);
8450 if (!ale)
8451 return NULL;
8452 dn = ALE_DEFN(ale);
8455 * In case this is a forward reference to a function,
8456 * we pessimistically set PND_FUNARG if the next token
8457 * is not a left parenthesis.
8459 * If the definition eventually parsed into dn is not a
8460 * function, this flag won't hurt, and if we do parse a
8461 * function with pn's name, then the PND_FUNARG flag is
8462 * necessary for safe cx->display-based optimization of
8463 * the closure's static link.
8465 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8466 JS_ASSERT(dn->pn_op == JSOP_NOP);
8467 if (js_PeekToken(cx, ts) != TOK_LP)
8468 dn->pn_dflags |= PND_FUNARG;
8472 JS_ASSERT(dn->pn_defn);
8473 LinkUseToDef(pn, dn, tc);
8475 /* Here we handle the backward function reference case. */
8476 if (js_PeekToken(cx, ts) != TOK_LP)
8477 dn->pn_dflags |= PND_FUNARG;
8479 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8483 #if JS_HAS_XML_SUPPORT
8484 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
8485 if (afterDot) {
8486 JSString *str;
8489 * Here PrimaryExpr is called after . or .. followed by a name
8490 * followed by ::. This is the only case where a keyword after
8491 * . or .. is not treated as a property name.
8493 str = ATOM_TO_STRING(pn->pn_atom);
8494 tt = js_CheckKeyword(str->chars(), str->length());
8495 if (tt == TOK_FUNCTION) {
8496 pn->pn_arity = PN_NULLARY;
8497 pn->pn_type = TOK_FUNCTION;
8498 } else if (tt != TOK_EOF) {
8499 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8500 JSMSG_KEYWORD_NOT_NS);
8501 return NULL;
8504 pn = QualifiedSuffix(cx, ts, pn, tc);
8505 if (!pn)
8506 return NULL;
8508 #endif
8509 break;
8511 case TOK_REGEXP:
8513 JSObject *obj;
8515 pn = NewParseNode(PN_NULLARY, tc);
8516 if (!pn)
8517 return NULL;
8519 obj = js_NewRegExpObject(cx, ts,
8520 ts->tokenbuf.begin(),
8521 ts->tokenbuf.length(),
8522 CURRENT_TOKEN(ts).t_reflags);
8523 if (!obj)
8524 return NULL;
8525 if (!(tc->flags & TCF_COMPILE_N_GO)) {
8526 STOBJ_CLEAR_PARENT(obj);
8527 STOBJ_CLEAR_PROTO(obj);
8530 pn->pn_objbox = tc->compiler->newObjectBox(obj);
8531 if (!pn->pn_objbox)
8532 return NULL;
8534 pn->pn_op = JSOP_REGEXP;
8535 break;
8538 case TOK_NUMBER:
8539 pn = NewParseNode(PN_NULLARY, tc);
8540 if (!pn)
8541 return NULL;
8542 pn->pn_op = JSOP_DOUBLE;
8543 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
8544 break;
8546 case TOK_PRIMARY:
8547 pn = NewParseNode(PN_NULLARY, tc);
8548 if (!pn)
8549 return NULL;
8550 pn->pn_op = CURRENT_TOKEN(ts).t_op;
8551 break;
8553 case TOK_ERROR:
8554 /* The scanner or one of its subroutines reported the error. */
8555 return NULL;
8557 default:
8558 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8559 JSMSG_SYNTAX_ERROR);
8560 return NULL;
8562 return pn;
8565 static JSParseNode *
8566 ParenExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
8567 JSParseNode *pn1, JSBool *genexp)
8569 JSTokenPtr begin;
8570 JSParseNode *pn;
8572 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LP);
8573 begin = CURRENT_TOKEN(ts).pos.begin;
8575 if (genexp)
8576 *genexp = JS_FALSE;
8577 pn = BracketedExpr(cx, ts, tc);
8578 if (!pn)
8579 return NULL;
8581 #if JS_HAS_GENERATOR_EXPRS
8582 if (js_MatchToken(cx, ts, TOK_FOR)) {
8583 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
8584 js_ReportCompileErrorNumber(cx, ts, pn, JSREPORT_ERROR,
8585 JSMSG_BAD_GENERATOR_SYNTAX,
8586 js_yield_str);
8587 return NULL;
8589 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
8590 js_ReportCompileErrorNumber(cx, ts, pn->last(), JSREPORT_ERROR,
8591 JSMSG_BAD_GENERATOR_SYNTAX,
8592 js_generator_str);
8593 return NULL;
8595 if (!pn1) {
8596 pn1 = NewParseNode(PN_UNARY, tc);
8597 if (!pn1)
8598 return NULL;
8600 pn = GeneratorExpr(pn1, pn, tc);
8601 if (!pn)
8602 return NULL;
8603 pn->pn_pos.begin = begin;
8604 if (genexp) {
8605 if (js_GetToken(cx, ts) != TOK_RP) {
8606 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
8607 JSMSG_BAD_GENERATOR_SYNTAX,
8608 js_generator_str);
8609 return NULL;
8611 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
8612 *genexp = JS_TRUE;
8615 #endif /* JS_HAS_GENERATOR_EXPRS */
8617 return pn;
8621 * Fold from one constant type to another.
8622 * XXX handles only strings and numbers for now
8624 static JSBool
8625 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
8627 if (PN_TYPE(pn) != type) {
8628 switch (type) {
8629 case TOK_NUMBER:
8630 if (pn->pn_type == TOK_STRING) {
8631 jsdouble d;
8632 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8633 return JS_FALSE;
8634 pn->pn_dval = d;
8635 pn->pn_type = TOK_NUMBER;
8636 pn->pn_op = JSOP_DOUBLE;
8638 break;
8640 case TOK_STRING:
8641 if (pn->pn_type == TOK_NUMBER) {
8642 JSString *str = js_NumberToString(cx, pn->pn_dval);
8643 if (!str)
8644 return JS_FALSE;
8645 pn->pn_atom = js_AtomizeString(cx, str, 0);
8646 if (!pn->pn_atom)
8647 return JS_FALSE;
8648 pn->pn_type = TOK_STRING;
8649 pn->pn_op = JSOP_STRING;
8651 break;
8653 default:;
8656 return JS_TRUE;
8660 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8661 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8662 * a successful call to this function.
8664 static JSBool
8665 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8666 JSParseNode *pn, JSTreeContext *tc)
8668 jsdouble d, d2;
8669 int32 i, j;
8671 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8672 d = pn1->pn_dval;
8673 d2 = pn2->pn_dval;
8674 switch (op) {
8675 case JSOP_LSH:
8676 case JSOP_RSH:
8677 i = js_DoubleToECMAInt32(d);
8678 j = js_DoubleToECMAInt32(d2);
8679 j &= 31;
8680 d = (op == JSOP_LSH) ? i << j : i >> j;
8681 break;
8683 case JSOP_URSH:
8684 j = js_DoubleToECMAInt32(d2);
8685 j &= 31;
8686 d = js_DoubleToECMAUint32(d) >> j;
8687 break;
8689 case JSOP_ADD:
8690 d += d2;
8691 break;
8693 case JSOP_SUB:
8694 d -= d2;
8695 break;
8697 case JSOP_MUL:
8698 d *= d2;
8699 break;
8701 case JSOP_DIV:
8702 if (d2 == 0) {
8703 #if defined(XP_WIN)
8704 /* XXX MSVC miscompiles such that (NaN == 0) */
8705 if (JSDOUBLE_IS_NaN(d2))
8706 d = js_NaN;
8707 else
8708 #endif
8709 if (d == 0 || JSDOUBLE_IS_NaN(d))
8710 d = js_NaN;
8711 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
8712 d = js_NegativeInfinity;
8713 else
8714 d = js_PositiveInfinity;
8715 } else {
8716 d /= d2;
8718 break;
8720 case JSOP_MOD:
8721 if (d2 == 0) {
8722 d = js_NaN;
8723 } else {
8724 d = js_fmod(d, d2);
8726 break;
8728 default:;
8731 /* Take care to allow pn1 or pn2 to alias pn. */
8732 if (pn1 != pn)
8733 RecycleTree(pn1, tc);
8734 if (pn2 != pn)
8735 RecycleTree(pn2, tc);
8736 pn->pn_type = TOK_NUMBER;
8737 pn->pn_op = JSOP_DOUBLE;
8738 pn->pn_arity = PN_NULLARY;
8739 pn->pn_dval = d;
8740 return JS_TRUE;
8743 #if JS_HAS_XML_SUPPORT
8745 static JSBool
8746 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8748 JSTokenType tt;
8749 JSParseNode **pnp, *pn1, *pn2;
8750 JSString *accum, *str;
8751 uint32 i, j;
8752 JSTempValueRooter tvr;
8754 JS_ASSERT(pn->pn_arity == PN_LIST);
8755 tt = PN_TYPE(pn);
8756 pnp = &pn->pn_head;
8757 pn1 = *pnp;
8758 accum = NULL;
8759 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8760 if (tt == TOK_XMLETAGO)
8761 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8762 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8763 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8767 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8768 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8769 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8770 * Therefore, we have to add additonal protection from GC nesting under
8771 * js_ConcatStrings.
8773 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8774 /* The parser already rejected end-tags with attributes. */
8775 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8776 switch (pn2->pn_type) {
8777 case TOK_XMLATTR:
8778 if (!accum)
8779 goto cantfold;
8780 /* FALL THROUGH */
8781 case TOK_XMLNAME:
8782 case TOK_XMLSPACE:
8783 case TOK_XMLTEXT:
8784 case TOK_STRING:
8785 if (pn2->pn_arity == PN_LIST)
8786 goto cantfold;
8787 str = ATOM_TO_STRING(pn2->pn_atom);
8788 break;
8790 case TOK_XMLCDATA:
8791 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8792 if (!str)
8793 return JS_FALSE;
8794 break;
8796 case TOK_XMLCOMMENT:
8797 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8798 if (!str)
8799 return JS_FALSE;
8800 break;
8802 case TOK_XMLPI:
8803 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8804 ATOM_TO_STRING(pn2->pn_atom2));
8805 if (!str)
8806 return JS_FALSE;
8807 break;
8809 cantfold:
8810 default:
8811 JS_ASSERT(*pnp == pn1);
8812 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8813 (i & 1) ^ (j & 1)) {
8814 #ifdef DEBUG_brendanXXX
8815 printf("1: %d, %d => ", i, j);
8816 if (accum)
8817 js_FileEscapedString(stdout, accum, 0);
8818 else
8819 fputs("NULL", stdout);
8820 fputc('\n', stdout);
8821 #endif
8822 } else if (accum && pn1 != pn2) {
8823 while (pn1->pn_next != pn2) {
8824 pn1 = RecycleTree(pn1, tc);
8825 --pn->pn_count;
8827 pn1->pn_type = TOK_XMLTEXT;
8828 pn1->pn_op = JSOP_STRING;
8829 pn1->pn_arity = PN_NULLARY;
8830 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8831 if (!pn1->pn_atom)
8832 return JS_FALSE;
8833 JS_ASSERT(pnp != &pn1->pn_next);
8834 *pnp = pn1;
8836 pnp = &pn2->pn_next;
8837 pn1 = *pnp;
8838 accum = NULL;
8839 continue;
8842 if (accum) {
8843 JS_PUSH_TEMP_ROOT_STRING(cx, accum, &tvr);
8844 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8845 ? js_AddAttributePart(cx, i & 1, accum, str)
8846 : js_ConcatStrings(cx, accum, str);
8847 JS_POP_TEMP_ROOT(cx, &tvr);
8848 if (!str)
8849 return JS_FALSE;
8850 #ifdef DEBUG_brendanXXX
8851 printf("2: %d, %d => ", i, j);
8852 js_FileEscapedString(stdout, str, 0);
8853 printf(" (%u)\n", str->length());
8854 #endif
8855 ++j;
8857 accum = str;
8860 if (accum) {
8861 str = NULL;
8862 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8863 if (tt == TOK_XMLPTAGC)
8864 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8865 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8866 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8868 if (str) {
8869 accum = js_ConcatStrings(cx, accum, str);
8870 if (!accum)
8871 return JS_FALSE;
8874 JS_ASSERT(*pnp == pn1);
8875 while (pn1->pn_next) {
8876 pn1 = RecycleTree(pn1, tc);
8877 --pn->pn_count;
8879 pn1->pn_type = TOK_XMLTEXT;
8880 pn1->pn_op = JSOP_STRING;
8881 pn1->pn_arity = PN_NULLARY;
8882 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8883 if (!pn1->pn_atom)
8884 return JS_FALSE;
8885 JS_ASSERT(pnp != &pn1->pn_next);
8886 *pnp = pn1;
8889 if (pn1 && pn->pn_count == 1) {
8891 * Only one node under pn, and it has been folded: move pn1 onto pn
8892 * unless pn is an XML root (in which case we need it to tell the code
8893 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8894 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8895 * extra "<" and "/>" bracketing at runtime.
8897 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8898 pn->become(pn1);
8899 } else if (tt == TOK_XMLPTAGC) {
8900 pn->pn_type = TOK_XMLELEM;
8901 pn->pn_op = JSOP_TOXML;
8904 return JS_TRUE;
8907 #endif /* JS_HAS_XML_SUPPORT */
8909 static int
8910 Boolish(JSParseNode *pn)
8912 switch (pn->pn_op) {
8913 case JSOP_DOUBLE:
8914 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8916 case JSOP_STRING:
8917 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8919 #if JS_HAS_GENERATOR_EXPRS
8920 case JSOP_CALL:
8923 * A generator expression as an if or loop condition has no effects, it
8924 * simply results in a truthy object reference. This condition folding
8925 * is needed for the decompiler. See bug 442342 and bug 443074.
8927 if (pn->pn_count != 1)
8928 break;
8929 JSParseNode *pn2 = pn->pn_head;
8930 if (pn2->pn_type != TOK_FUNCTION)
8931 break;
8932 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8933 break;
8934 /* FALL THROUGH */
8936 #endif
8938 case JSOP_DEFFUN:
8939 case JSOP_LAMBDA:
8940 case JSOP_THIS:
8941 case JSOP_TRUE:
8942 return 1;
8944 case JSOP_NULL:
8945 case JSOP_FALSE:
8946 return 0;
8948 default:;
8950 return -1;
8953 JSBool
8954 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8956 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8958 JS_CHECK_RECURSION(cx, return JS_FALSE);
8960 switch (pn->pn_arity) {
8961 case PN_FUNC:
8963 uint32 oldflags = tc->flags;
8964 JSFunctionBox *oldlist = tc->functionList;
8966 tc->flags = pn->pn_funbox->tcflags;
8967 tc->functionList = pn->pn_funbox->kids;
8968 if (!js_FoldConstants(cx, pn->pn_body, tc))
8969 return JS_FALSE;
8970 pn->pn_funbox->kids = tc->functionList;
8971 tc->flags = oldflags;
8972 tc->functionList = oldlist;
8973 break;
8976 case PN_LIST:
8978 /* Propagate inCond through logical connectives. */
8979 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8981 /* Save the list head in pn1 for later use. */
8982 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
8983 if (!js_FoldConstants(cx, pn2, tc, cond))
8984 return JS_FALSE;
8986 break;
8989 case PN_TERNARY:
8990 /* Any kid may be null (e.g. for (;;)). */
8991 pn1 = pn->pn_kid1;
8992 pn2 = pn->pn_kid2;
8993 pn3 = pn->pn_kid3;
8994 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8995 return JS_FALSE;
8996 if (pn2) {
8997 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8998 return JS_FALSE;
8999 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
9000 RecycleTree(pn2, tc);
9001 pn->pn_kid2 = NULL;
9004 if (pn3 && !js_FoldConstants(cx, pn3, tc))
9005 return JS_FALSE;
9006 break;
9008 case PN_BINARY:
9009 pn1 = pn->pn_left;
9010 pn2 = pn->pn_right;
9012 /* Propagate inCond through logical connectives. */
9013 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
9014 if (!js_FoldConstants(cx, pn1, tc, inCond))
9015 return JS_FALSE;
9016 if (!js_FoldConstants(cx, pn2, tc, inCond))
9017 return JS_FALSE;
9018 break;
9021 /* First kid may be null (for default case in switch). */
9022 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
9023 return JS_FALSE;
9024 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
9025 return JS_FALSE;
9026 break;
9028 case PN_UNARY:
9029 pn1 = pn->pn_kid;
9032 * Kludge to deal with typeof expressions: because constant folding
9033 * can turn an expression into a name node, we have to check here,
9034 * before folding, to see if we should throw undefined name errors.
9036 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
9037 * null. This assumption does not hold true for other unary
9038 * expressions.
9040 if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
9041 pn->pn_op = JSOP_TYPEOFEXPR;
9043 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
9044 return JS_FALSE;
9045 break;
9047 case PN_NAME:
9049 * Skip pn1 down along a chain of dotted member expressions to avoid
9050 * excessive recursion. Our only goal here is to fold constants (if
9051 * any) in the primary expression operand to the left of the first
9052 * dot in the chain.
9054 if (!pn->pn_used) {
9055 pn1 = pn->pn_expr;
9056 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
9057 pn1 = pn1->pn_expr;
9058 if (pn1 && !js_FoldConstants(cx, pn1, tc))
9059 return JS_FALSE;
9061 break;
9063 case PN_NAMESET:
9064 pn1 = pn->pn_tree;
9065 if (!js_FoldConstants(cx, pn1, tc))
9066 return JS_FALSE;
9067 break;
9069 case PN_NULLARY:
9070 break;
9073 switch (pn->pn_type) {
9074 case TOK_IF:
9075 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
9076 break;
9077 /* FALL THROUGH */
9079 case TOK_HOOK:
9080 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
9081 switch (pn1->pn_type) {
9082 case TOK_NUMBER:
9083 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
9084 pn2 = pn3;
9085 break;
9086 case TOK_STRING:
9087 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
9088 pn2 = pn3;
9089 break;
9090 case TOK_PRIMARY:
9091 if (pn1->pn_op == JSOP_TRUE)
9092 break;
9093 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
9094 pn2 = pn3;
9095 break;
9097 /* FALL THROUGH */
9098 default:
9099 /* Early return to dodge common code that copies pn2 to pn. */
9100 return JS_TRUE;
9103 #if JS_HAS_GENERATOR_EXPRS
9104 /* Don't fold a trailing |if (0)| in a generator expression. */
9105 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
9106 break;
9107 #endif
9109 if (pn2 && !pn2->pn_defn)
9110 pn->become(pn2);
9111 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
9113 * False condition and no else, or an empty then-statement was
9114 * moved up over pn. Either way, make pn an empty block (not an
9115 * empty statement, which does not decompile, even when labeled).
9116 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
9117 * or an empty statement for a child.
9119 pn->pn_type = TOK_LC;
9120 pn->pn_arity = PN_LIST;
9121 pn->makeEmpty();
9123 RecycleTree(pn2, tc);
9124 if (pn3 && pn3 != pn2)
9125 RecycleTree(pn3, tc);
9126 break;
9128 case TOK_OR:
9129 case TOK_AND:
9130 if (inCond) {
9131 if (pn->pn_arity == PN_LIST) {
9132 JSParseNode **pnp = &pn->pn_head;
9133 JS_ASSERT(*pnp == pn1);
9134 do {
9135 int cond = Boolish(pn1);
9136 if (cond == (pn->pn_type == TOK_OR)) {
9137 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
9138 pn3 = pn2->pn_next;
9139 RecycleTree(pn2, tc);
9140 --pn->pn_count;
9142 pn1->pn_next = NULL;
9143 break;
9145 if (cond != -1) {
9146 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9147 if (pn->pn_count == 1)
9148 break;
9149 *pnp = pn1->pn_next;
9150 RecycleTree(pn1, tc);
9151 --pn->pn_count;
9152 } else {
9153 pnp = &pn1->pn_next;
9155 } while ((pn1 = *pnp) != NULL);
9157 // We may have to change arity from LIST to BINARY.
9158 pn1 = pn->pn_head;
9159 if (pn->pn_count == 2) {
9160 pn2 = pn1->pn_next;
9161 pn1->pn_next = NULL;
9162 JS_ASSERT(!pn2->pn_next);
9163 pn->pn_arity = PN_BINARY;
9164 pn->pn_left = pn1;
9165 pn->pn_right = pn2;
9166 } else if (pn->pn_count == 1) {
9167 pn->become(pn1);
9168 RecycleTree(pn1, tc);
9170 } else {
9171 int cond = Boolish(pn1);
9172 if (cond == (pn->pn_type == TOK_OR)) {
9173 RecycleTree(pn2, tc);
9174 pn->become(pn1);
9175 } else if (cond != -1) {
9176 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9177 RecycleTree(pn1, tc);
9178 pn->become(pn2);
9182 break;
9184 case TOK_ASSIGN:
9186 * Compound operators such as *= should be subject to folding, in case
9187 * the left-hand side is constant, and so that the decompiler produces
9188 * the same string that you get from decompiling a script or function
9189 * compiled from that same string. As with +, += is special.
9191 if (pn->pn_op == JSOP_NOP)
9192 break;
9193 if (pn->pn_op != JSOP_ADD)
9194 goto do_binary_op;
9195 /* FALL THROUGH */
9197 case TOK_PLUS:
9198 if (pn->pn_arity == PN_LIST) {
9199 size_t length, length2;
9200 jschar *chars;
9201 JSString *str, *str2;
9204 * Any string literal term with all others number or string means
9205 * this is a concatenation. If any term is not a string or number
9206 * literal, we can't fold.
9208 JS_ASSERT(pn->pn_count > 2);
9209 if (pn->pn_xflags & PNX_CANTFOLD)
9210 return JS_TRUE;
9211 if (pn->pn_xflags != PNX_STRCAT)
9212 goto do_binary_op;
9214 /* Ok, we're concatenating: convert non-string constant operands. */
9215 length = 0;
9216 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9217 if (!FoldType(cx, pn2, TOK_STRING))
9218 return JS_FALSE;
9219 /* XXX fold only if all operands convert to string */
9220 if (pn2->pn_type != TOK_STRING)
9221 return JS_TRUE;
9222 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9225 /* Allocate a new buffer and string descriptor for the result. */
9226 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9227 if (!chars)
9228 return JS_FALSE;
9229 str = js_NewString(cx, chars, length);
9230 if (!str) {
9231 cx->free(chars);
9232 return JS_FALSE;
9235 /* Fill the buffer, advancing chars and recycling kids as we go. */
9236 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9237 str2 = ATOM_TO_STRING(pn2->pn_atom);
9238 length2 = str2->flatLength();
9239 js_strncpy(chars, str2->flatChars(), length2);
9240 chars += length2;
9242 *chars = 0;
9244 /* Atomize the result string and mutate pn to refer to it. */
9245 pn->pn_atom = js_AtomizeString(cx, str, 0);
9246 if (!pn->pn_atom)
9247 return JS_FALSE;
9248 pn->pn_type = TOK_STRING;
9249 pn->pn_op = JSOP_STRING;
9250 pn->pn_arity = PN_NULLARY;
9251 break;
9254 /* Handle a binary string concatenation. */
9255 JS_ASSERT(pn->pn_arity == PN_BINARY);
9256 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9257 JSString *left, *right, *str;
9259 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9260 TOK_STRING)) {
9261 return JS_FALSE;
9263 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9264 return JS_TRUE;
9265 left = ATOM_TO_STRING(pn1->pn_atom);
9266 right = ATOM_TO_STRING(pn2->pn_atom);
9267 str = js_ConcatStrings(cx, left, right);
9268 if (!str)
9269 return JS_FALSE;
9270 pn->pn_atom = js_AtomizeString(cx, str, 0);
9271 if (!pn->pn_atom)
9272 return JS_FALSE;
9273 pn->pn_type = TOK_STRING;
9274 pn->pn_op = JSOP_STRING;
9275 pn->pn_arity = PN_NULLARY;
9276 RecycleTree(pn1, tc);
9277 RecycleTree(pn2, tc);
9278 break;
9281 /* Can't concatenate string literals, let's try numbers. */
9282 goto do_binary_op;
9284 case TOK_STAR:
9285 case TOK_SHOP:
9286 case TOK_MINUS:
9287 case TOK_DIVOP:
9288 do_binary_op:
9289 if (pn->pn_arity == PN_LIST) {
9290 JS_ASSERT(pn->pn_count > 2);
9291 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9292 if (!FoldType(cx, pn2, TOK_NUMBER))
9293 return JS_FALSE;
9295 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9296 /* XXX fold only if all operands convert to number */
9297 if (pn2->pn_type != TOK_NUMBER)
9298 break;
9300 if (!pn2) {
9301 JSOp op = PN_OP(pn);
9303 pn2 = pn1->pn_next;
9304 pn3 = pn2->pn_next;
9305 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9306 return JS_FALSE;
9307 while ((pn2 = pn3) != NULL) {
9308 pn3 = pn2->pn_next;
9309 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9310 return JS_FALSE;
9313 } else {
9314 JS_ASSERT(pn->pn_arity == PN_BINARY);
9315 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9316 !FoldType(cx, pn2, TOK_NUMBER)) {
9317 return JS_FALSE;
9319 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9320 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9321 return JS_FALSE;
9324 break;
9326 case TOK_UNARYOP:
9327 if (pn1->pn_type == TOK_NUMBER) {
9328 jsdouble d;
9330 /* Operate on one numeric constant. */
9331 d = pn1->pn_dval;
9332 switch (pn->pn_op) {
9333 case JSOP_BITNOT:
9334 d = ~js_DoubleToECMAInt32(d);
9335 break;
9337 case JSOP_NEG:
9338 d = -d;
9339 break;
9341 case JSOP_POS:
9342 break;
9344 case JSOP_NOT:
9345 pn->pn_type = TOK_PRIMARY;
9346 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9347 pn->pn_arity = PN_NULLARY;
9348 /* FALL THROUGH */
9350 default:
9351 /* Return early to dodge the common TOK_NUMBER code. */
9352 return JS_TRUE;
9354 pn->pn_type = TOK_NUMBER;
9355 pn->pn_op = JSOP_DOUBLE;
9356 pn->pn_arity = PN_NULLARY;
9357 pn->pn_dval = d;
9358 RecycleTree(pn1, tc);
9359 } else if (pn1->pn_type == TOK_PRIMARY) {
9360 if (pn->pn_op == JSOP_NOT &&
9361 (pn1->pn_op == JSOP_TRUE ||
9362 pn1->pn_op == JSOP_FALSE)) {
9363 pn->become(pn1);
9364 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9365 RecycleTree(pn1, tc);
9368 break;
9370 #if JS_HAS_XML_SUPPORT
9371 case TOK_XMLELEM:
9372 case TOK_XMLLIST:
9373 case TOK_XMLPTAGC:
9374 case TOK_XMLSTAGO:
9375 case TOK_XMLETAGO:
9376 case TOK_XMLNAME:
9377 if (pn->pn_arity == PN_LIST) {
9378 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9379 if (!FoldXMLConstants(cx, pn, tc))
9380 return JS_FALSE;
9382 break;
9384 case TOK_AT:
9385 if (pn1->pn_type == TOK_XMLNAME) {
9386 jsval v;
9387 JSObjectBox *xmlbox;
9389 v = ATOM_KEY(pn1->pn_atom);
9390 if (!js_ToAttributeName(cx, &v))
9391 return JS_FALSE;
9392 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9394 xmlbox = tc->compiler->newObjectBox(JSVAL_TO_OBJECT(v));
9395 if (!xmlbox)
9396 return JS_FALSE;
9398 pn->pn_type = TOK_XMLNAME;
9399 pn->pn_op = JSOP_OBJECT;
9400 pn->pn_arity = PN_NULLARY;
9401 pn->pn_objbox = xmlbox;
9402 RecycleTree(pn1, tc);
9404 break;
9405 #endif /* JS_HAS_XML_SUPPORT */
9407 default:;
9410 if (inCond) {
9411 int cond = Boolish(pn);
9412 if (cond >= 0) {
9413 switch (pn->pn_arity) {
9414 case PN_LIST:
9415 pn2 = pn->pn_head;
9416 do {
9417 pn3 = pn2->pn_next;
9418 RecycleTree(pn2, tc);
9419 } while ((pn2 = pn3) != NULL);
9420 break;
9421 case PN_FUNC:
9422 RecycleFuncNameKids(pn, tc);
9423 break;
9424 case PN_NULLARY:
9425 break;
9426 default:
9427 JS_NOT_REACHED("unhandled arity");
9429 pn->pn_type = TOK_PRIMARY;
9430 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9431 pn->pn_arity = PN_NULLARY;
9435 return JS_TRUE;