Back out f8c6a272f2dc. It was working on Try a few minutes ago, I swear! (r=red)
[mozilla-central.git] / js / src / jsparse.cpp
blob5ed773ca321fbd4d11eec9375f3954e2d1f56582
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
91 using namespace js;
94 * Asserts to verify assumptions behind pn_ macros.
96 #define pn_offsetof(m) offsetof(JSParseNode, m)
98 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
99 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
101 #undef pn_offsetof
104 * Insist that the next token be of type tt, or report errno and return null.
105 * NB: this macro uses cx and ts from its lexical environment.
107 #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \
108 JS_BEGIN_MACRO \
109 if (tokenStream.getToken((__flags)) != tt) { \
110 reportErrorNumber(NULL, JSREPORT_ERROR, errno); \
111 return NULL; \
113 JS_END_MACRO
114 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
116 #ifdef METER_PARSENODES
117 static uint32 parsenodes = 0;
118 static uint32 maxparsenodes = 0;
119 static uint32 recyclednodes = 0;
120 #endif
122 void
123 JSParseNode::become(JSParseNode *pn2)
125 JS_ASSERT(!pn_defn);
126 JS_ASSERT(!pn2->pn_defn);
128 JS_ASSERT(!pn_used);
129 if (pn2->pn_used) {
130 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
131 while (*pnup != pn2)
132 pnup = &(*pnup)->pn_link;
133 *pnup = this;
134 pn_link = pn2->pn_link;
135 pn_used = true;
136 pn2->pn_link = NULL;
137 pn2->pn_used = false;
140 /* If this is a function node fix up the pn_funbox->node back-pointer. */
141 if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
142 pn2->pn_funbox->node = this;
144 pn_type = pn2->pn_type;
145 pn_op = pn2->pn_op;
146 pn_arity = pn2->pn_arity;
147 pn_parens = pn2->pn_parens;
148 pn_u = pn2->pn_u;
149 pn2->clear();
152 void
153 JSParseNode::clear()
155 pn_type = TOK_EOF;
156 pn_op = JSOP_NOP;
157 pn_used = pn_defn = false;
158 pn_arity = PN_NULLARY;
159 pn_parens = false;
162 bool
163 Parser::init(const jschar *base, size_t length,
164 FILE *fp, const char *filename, uintN lineno)
166 JSContext *cx = context;
168 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
169 if (!tokenStream.init(base, length, fp, filename, lineno)) {
170 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
171 return false;
173 return true;
176 Parser::~Parser()
178 JSContext *cx = context;
180 if (principals)
181 JSPRINCIPALS_DROP(cx, principals);
182 tokenStream.close();
183 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
186 void
187 Parser::setPrincipals(JSPrincipals *prin)
189 JS_ASSERT(!principals);
190 if (prin)
191 JSPRINCIPALS_HOLD(context, prin);
192 principals = prin;
195 JSObjectBox *
196 Parser::newObjectBox(JSObject *obj)
198 JS_ASSERT(obj);
201 * We use JSContext.tempPool to allocate parsed objects and place them on
202 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
203 * containing the entries must be alive until we are done with scanning,
204 * parsing and code generation for the whole script or top-level function.
206 JSObjectBox *objbox;
207 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
208 if (!objbox) {
209 js_ReportOutOfScriptQuota(context);
210 return NULL;
212 objbox->traceLink = traceListHead;
213 traceListHead = objbox;
214 objbox->emitLink = NULL;
215 objbox->object = obj;
216 return objbox;
219 JSFunctionBox *
220 Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
222 JS_ASSERT(obj);
223 JS_ASSERT(obj->isFunction());
226 * We use JSContext.tempPool to allocate parsed objects and place them on
227 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
228 * containing the entries must be alive until we are done with scanning,
229 * parsing and code generation for the whole script or top-level function.
231 JSFunctionBox *funbox;
232 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
233 if (!funbox) {
234 js_ReportOutOfScriptQuota(context);
235 return NULL;
237 funbox->traceLink = traceListHead;
238 traceListHead = funbox;
239 funbox->emitLink = NULL;
240 funbox->object = obj;
241 funbox->node = fn;
242 funbox->siblings = tc->functionList;
243 tc->functionList = funbox;
244 ++tc->parser->functionCount;
245 funbox->kids = NULL;
246 funbox->parent = tc->funbox;
247 funbox->methods = NULL;
248 funbox->queued = false;
249 funbox->inLoop = false;
250 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
251 if (STMT_IS_LOOP(stmt)) {
252 funbox->inLoop = true;
253 break;
256 funbox->level = tc->staticLevel;
257 funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
258 return funbox;
261 bool
262 JSFunctionBox::joinable() const
264 return FUN_NULL_CLOSURE((JSFunction *) object) &&
265 !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
268 bool
269 JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
271 if (slowMethods != 0) {
272 for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
273 if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
274 return true;
275 if (funbox->inLoop)
276 return true;
279 return false;
282 void
283 Parser::trace(JSTracer *trc)
285 JSObjectBox *objbox = traceListHead;
286 while (objbox) {
287 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
288 objbox = objbox->traceLink;
292 static void
293 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
295 static void
296 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
298 JSFunctionBox *funbox = pn->pn_funbox;
299 if (funbox) {
300 JS_ASSERT(funbox->node == pn);
301 funbox->node = NULL;
303 if (funbox->parent && PN_OP(pn) == JSOP_LAMBDA) {
305 * Remove pn from funbox->parent's methods list if it's there. See
306 * the TOK_SEMI case in Statement, near the bottom, the TOK_ASSIGN
307 * sub-case matching a constructor method assignment pattern.
309 JS_ASSERT(!pn->pn_defn);
310 JS_ASSERT(!pn->pn_used);
311 JSParseNode **pnp = &funbox->parent->methods;
312 while (JSParseNode *method = *pnp) {
313 if (method == pn) {
314 *pnp = method->pn_link;
315 break;
317 pnp = &method->pn_link;
321 JSFunctionBox **funboxp = &tc->functionList;
322 while (*funboxp) {
323 if (*funboxp == funbox) {
324 *funboxp = funbox->siblings;
325 break;
327 funboxp = &(*funboxp)->siblings;
330 uint32 oldflags = tc->flags;
331 JSFunctionBox *oldlist = tc->functionList;
333 tc->flags = funbox->tcflags;
334 tc->functionList = funbox->kids;
335 UnlinkFunctionBoxes(pn->pn_body, tc);
336 funbox->kids = tc->functionList;
337 tc->flags = oldflags;
338 tc->functionList = oldlist;
340 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
341 pn->pn_funbox = NULL;
345 static void
346 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
348 if (pn) {
349 switch (pn->pn_arity) {
350 case PN_NULLARY:
351 return;
352 case PN_UNARY:
353 UnlinkFunctionBoxes(pn->pn_kid, tc);
354 return;
355 case PN_BINARY:
356 UnlinkFunctionBoxes(pn->pn_left, tc);
357 UnlinkFunctionBoxes(pn->pn_right, tc);
358 return;
359 case PN_TERNARY:
360 UnlinkFunctionBoxes(pn->pn_kid1, tc);
361 UnlinkFunctionBoxes(pn->pn_kid2, tc);
362 UnlinkFunctionBoxes(pn->pn_kid3, tc);
363 return;
364 case PN_LIST:
365 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
366 UnlinkFunctionBoxes(pn2, tc);
367 return;
368 case PN_FUNC:
369 UnlinkFunctionBox(pn, tc);
370 return;
371 case PN_NAME:
372 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
373 return;
374 case PN_NAMESET:
375 UnlinkFunctionBoxes(pn->pn_tree, tc);
380 static void
381 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
383 static JSParseNode *
384 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
386 JSParseNode *next, **head;
388 if (!pn)
389 return NULL;
391 /* Catch back-to-back dup recycles. */
392 JS_ASSERT(pn != tc->parser->nodeList);
393 next = pn->pn_next;
394 if (pn->pn_used || pn->pn_defn) {
396 * JSAtomLists own definition nodes along with their used-node chains.
397 * Defer recycling such nodes until we unwind to top level to avoid
398 * linkage overhead or (alternatively) unlinking runtime complexity.
399 * Yes, this means dead code can contribute to static analysis results!
401 * Do recycle kids here, since they are no longer needed.
403 pn->pn_next = NULL;
404 RecycleFuncNameKids(pn, tc);
405 } else {
406 UnlinkFunctionBoxes(pn, tc);
407 head = &tc->parser->nodeList;
408 pn->pn_next = *head;
409 *head = pn;
410 #ifdef METER_PARSENODES
411 recyclednodes++;
412 #endif
414 return next;
417 static void
418 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
420 switch (pn->pn_arity) {
421 case PN_FUNC:
422 UnlinkFunctionBox(pn, tc);
423 /* FALL THROUGH */
425 case PN_NAME:
427 * Only a definition node might have a non-null strong pn_expr link
428 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
429 * Every node with the pn_used flag set has a non-null pn_lexdef
430 * weak reference to its definition node.
432 if (!pn->pn_used && pn->pn_expr) {
433 RecycleTree(pn->pn_expr, tc);
434 pn->pn_expr = NULL;
436 break;
438 default:
439 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
444 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
445 * temporary arena.
447 static JSParseNode *
448 NewOrRecycledNode(JSTreeContext *tc)
450 JSParseNode *pn, *pn2;
452 pn = tc->parser->nodeList;
453 if (!pn) {
454 JSContext *cx = tc->parser->context;
456 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
457 if (!pn)
458 js_ReportOutOfScriptQuota(cx);
459 } else {
460 tc->parser->nodeList = pn->pn_next;
462 /* Recycle immediate descendents only, to save work and working set. */
463 switch (pn->pn_arity) {
464 case PN_FUNC:
465 RecycleTree(pn->pn_body, tc);
466 break;
467 case PN_LIST:
468 pn2 = pn->pn_head;
469 if (pn2) {
470 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
471 pn2 = pn2->pn_next;
472 if (pn2) {
473 pn2 = pn->pn_head;
474 do {
475 pn2 = RecycleTree(pn2, tc);
476 } while (pn2);
477 } else {
478 *pn->pn_tail = tc->parser->nodeList;
479 tc->parser->nodeList = pn->pn_head;
480 #ifdef METER_PARSENODES
481 recyclednodes += pn->pn_count;
482 #endif
483 break;
486 break;
487 case PN_TERNARY:
488 RecycleTree(pn->pn_kid1, tc);
489 RecycleTree(pn->pn_kid2, tc);
490 RecycleTree(pn->pn_kid3, tc);
491 break;
492 case PN_BINARY:
493 if (pn->pn_left != pn->pn_right)
494 RecycleTree(pn->pn_left, tc);
495 RecycleTree(pn->pn_right, tc);
496 break;
497 case PN_UNARY:
498 RecycleTree(pn->pn_kid, tc);
499 break;
500 case PN_NAME:
501 if (!pn->pn_used)
502 RecycleTree(pn->pn_expr, tc);
503 break;
504 case PN_NULLARY:
505 break;
508 if (pn) {
509 #ifdef METER_PARSENODES
510 parsenodes++;
511 if (parsenodes - recyclednodes > maxparsenodes)
512 maxparsenodes = parsenodes - recyclednodes;
513 #endif
514 pn->pn_used = pn->pn_defn = false;
515 memset(&pn->pn_u, 0, sizeof pn->pn_u);
516 pn->pn_next = NULL;
518 return pn;
521 /* used only by static create methods of subclasses */
523 JSParseNode *
524 JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc)
526 JSParseNode *pn = NewOrRecycledNode(tc);
527 if (!pn)
528 return NULL;
529 const Token &tok = tc->parser->tokenStream.currentToken();
530 pn->init(tok.type, JSOP_NOP, arity);
531 pn->pn_pos = tok.pos;
532 return pn;
535 JSParseNode *
536 JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParseNode *right,
537 JSTreeContext *tc)
539 JSParseNode *pn, *pn1, *pn2;
541 if (!left || !right)
542 return NULL;
545 * Flatten a left-associative (left-heavy) tree of a given operator into
546 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
548 if (PN_TYPE(left) == tt &&
549 PN_OP(left) == op &&
550 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
551 if (left->pn_arity != PN_LIST) {
552 pn1 = left->pn_left, pn2 = left->pn_right;
553 left->pn_arity = PN_LIST;
554 left->pn_parens = false;
555 left->initList(pn1);
556 left->append(pn2);
557 if (tt == TOK_PLUS) {
558 if (pn1->pn_type == TOK_STRING)
559 left->pn_xflags |= PNX_STRCAT;
560 else if (pn1->pn_type != TOK_NUMBER)
561 left->pn_xflags |= PNX_CANTFOLD;
562 if (pn2->pn_type == TOK_STRING)
563 left->pn_xflags |= PNX_STRCAT;
564 else if (pn2->pn_type != TOK_NUMBER)
565 left->pn_xflags |= PNX_CANTFOLD;
568 left->append(right);
569 left->pn_pos.end = right->pn_pos.end;
570 if (tt == TOK_PLUS) {
571 if (right->pn_type == TOK_STRING)
572 left->pn_xflags |= PNX_STRCAT;
573 else if (right->pn_type != TOK_NUMBER)
574 left->pn_xflags |= PNX_CANTFOLD;
576 return left;
580 * Fold constant addition immediately, to conserve node space and, what's
581 * more, so js_FoldConstants never sees mixed addition and concatenation
582 * operations with more than one leading non-string operand in a PN_LIST
583 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
584 * to "3pt", not "12pt").
586 if (tt == TOK_PLUS &&
587 left->pn_type == TOK_NUMBER &&
588 right->pn_type == TOK_NUMBER) {
589 left->pn_dval += right->pn_dval;
590 left->pn_pos.end = right->pn_pos.end;
591 RecycleTree(right, tc);
592 return left;
595 pn = NewOrRecycledNode(tc);
596 if (!pn)
597 return NULL;
598 pn->init(tt, op, PN_BINARY);
599 pn->pn_pos.begin = left->pn_pos.begin;
600 pn->pn_pos.end = right->pn_pos.end;
601 pn->pn_left = left;
602 pn->pn_right = right;
603 return (BinaryNode *)pn;
606 namespace js {
608 inline void
609 NameNode::initCommon(JSTreeContext *tc)
611 pn_expr = NULL;
612 pn_cookie = FREE_UPVAR_COOKIE;
613 pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
614 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
615 pn_dflags |= PND_BLOCKCHILD;
616 pn_blockid = tc->blockid();
619 NameNode *
620 NameNode::create(JSAtom *atom, JSTreeContext *tc)
622 JSParseNode *pn;
624 pn = JSParseNode::create(PN_NAME, tc);
625 if (pn) {
626 pn->pn_atom = atom;
627 ((NameNode *)pn)->initCommon(tc);
629 return (NameNode *)pn;
632 } /* namespace js */
634 static bool
635 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
637 if (tc->blockidGen == JS_BIT(20)) {
638 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
639 JSMSG_NEED_DIET, "program");
640 return false;
642 blockid = tc->blockidGen++;
643 return true;
646 static bool
647 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
649 JS_ASSERT(tc->topStmt);
650 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
651 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
652 if (!GenerateBlockId(tc, tc->topStmt->blockid))
653 return false;
654 pn->pn_blockid = tc->topStmt->blockid;
655 return true;
659 * Parse a top-level JS script.
661 JSParseNode *
662 Parser::parse(JSObject *chain)
665 * Protect atoms from being collected by a GC activation, which might
666 * - nest on this thread due to out of memory (the so-called "last ditch"
667 * GC attempted within js_NewGCThing), or
668 * - run for any reason on another thread if this thread is suspended on
669 * an object lock before it finishes generating bytecode into a script
670 * protected from the GC by a root or a stack frame reference.
672 JSTreeContext globaltc(this);
673 globaltc.scopeChain = chain;
674 if (!GenerateBlockId(&globaltc, globaltc.bodyid))
675 return NULL;
677 JSParseNode *pn = statements();
678 if (pn) {
679 if (!tokenStream.matchToken(TOK_EOF)) {
680 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
681 pn = NULL;
682 } else {
683 if (!js_FoldConstants(context, pn, &globaltc))
684 pn = NULL;
687 return pn;
690 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
692 static inline bool
693 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
696 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
697 * (0xffffffff) and other cookies with that level.
699 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
700 * practically speaking it leaves more than enough room for upvars. In fact
701 * we might want to split cookie fields giving fewer bits for skip and more
702 * for slot, but only based on evidence.
704 if (staticLevel >= FREE_STATIC_LEVEL) {
705 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
706 JSMSG_TOO_DEEP, js_function_str);
707 return false;
709 tc->staticLevel = staticLevel;
710 return true;
714 * Compile a top-level script.
716 JSScript *
717 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
718 JSPrincipals *principals, uint32 tcflags,
719 const jschar *chars, size_t length,
720 FILE *file, const char *filename, uintN lineno,
721 JSString *source /* = NULL */,
722 unsigned staticLevel /* = 0 */)
724 JSArenaPool codePool, notePool;
725 TokenKind tt;
726 JSParseNode *pn;
727 uint32 scriptGlobals;
728 JSScript *script;
729 bool inDirectivePrologue;
730 #ifdef METER_PARSENODES
731 void *sbrk(ptrdiff_t), *before = sbrk(0);
732 #endif
734 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
737 * The scripted callerFrame can only be given for compile-and-go scripts
738 * and non-zero static level requires callerFrame.
740 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
741 JS_ASSERT_IF(staticLevel != 0, callerFrame);
743 Compiler compiler(cx, principals, callerFrame);
744 if (!compiler.init(chars, length, file, filename, lineno))
745 return NULL;
747 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
748 &cx->scriptStackQuota);
749 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
750 &cx->scriptStackQuota);
752 Parser &parser = compiler.parser;
753 TokenStream &tokenStream = parser.tokenStream;
755 JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
756 if (!cg.init())
757 return NULL;
759 MUST_FLOW_THROUGH("out");
761 /* Null script early in case of error, to reduce our code footprint. */
762 script = NULL;
764 cg.flags |= tcflags;
765 cg.scopeChain = scopeChain;
766 if (!SetStaticLevel(&cg, staticLevel))
767 goto out;
769 /* If this is a direct call to eval, inherit the caller's strictness. */
770 if (callerFrame &&
771 callerFrame->script &&
772 callerFrame->script->strictModeCode) {
773 cg.flags |= TCF_STRICT_MODE_CODE;
774 tokenStream.setStrictMode();
778 * If funbox is non-null after we create the new script, callerFrame->fun
779 * was saved in the 0th object table entry.
781 JSObjectBox *funbox;
782 funbox = NULL;
784 if (tcflags & TCF_COMPILE_N_GO) {
785 if (source) {
787 * Save eval program source in script->atomMap.vector[0] for the
788 * eval cache (see obj_eval in jsobj.cpp).
790 JSAtom *atom = js_AtomizeString(cx, source, 0);
791 if (!atom || !cg.atomList.add(&parser, atom))
792 goto out;
795 if (callerFrame && callerFrame->fun) {
797 * An eval script in a caller frame needs to have its enclosing
798 * function captured in case it refers to an upvar, and someone
799 * wishes to decompile it while it's running.
801 funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun));
802 if (!funbox)
803 goto out;
804 funbox->emitLink = cg.objectList.lastbox;
805 cg.objectList.lastbox = funbox;
806 cg.objectList.length++;
811 * Inline this->statements to emit as we go to save AST space. We must
812 * generate our script-body blockid since we aren't calling Statements.
814 uint32 bodyid;
815 if (!GenerateBlockId(&cg, bodyid))
816 goto out;
817 cg.bodyid = bodyid;
819 #if JS_HAS_XML_SUPPORT
820 pn = NULL;
821 bool onlyXML;
822 onlyXML = true;
823 #endif
825 CG_SWITCH_TO_PROLOG(&cg);
826 if (js_Emit1(cx, &cg, JSOP_TRACE) < 0)
827 goto out;
828 CG_SWITCH_TO_MAIN(&cg);
830 inDirectivePrologue = true;
831 for (;;) {
832 tt = tokenStream.peekToken(TSF_OPERAND);
833 if (tt <= TOK_EOF) {
834 if (tt == TOK_EOF)
835 break;
836 JS_ASSERT(tt == TOK_ERROR);
837 goto out;
840 pn = parser.statement();
841 if (!pn)
842 goto out;
843 JS_ASSERT(!cg.blockNode);
845 if (inDirectivePrologue)
846 inDirectivePrologue = parser.recognizeDirectivePrologue(pn);
848 if (!js_FoldConstants(cx, pn, &cg))
849 goto out;
851 if (cg.functionList) {
852 if (!parser.analyzeFunctions(cg.functionList, cg.flags))
853 goto out;
854 cg.functionList = NULL;
857 if (!js_EmitTree(cx, &cg, pn))
858 goto out;
859 #if JS_HAS_XML_SUPPORT
860 if (PN_TYPE(pn) != TOK_SEMI ||
861 !pn->pn_kid ||
862 !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) {
863 onlyXML = false;
865 #endif
866 RecycleTree(pn, &cg);
869 #if JS_HAS_XML_SUPPORT
871 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
872 * For background, see:
874 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
876 if (pn && onlyXML && !callerFrame) {
877 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
878 goto out;
880 #endif
883 * Global variables (gvars) share the atom index space with locals. Due to
884 * incremental code generation we need to patch the bytecode to adjust the
885 * local references to skip the globals.
887 scriptGlobals = cg.ngvars;
888 if (scriptGlobals != 0 || cg.hasSharps()) {
889 jsbytecode *code, *end;
890 JSOp op;
891 const JSCodeSpec *cs;
892 uintN len, slot;
894 if (scriptGlobals >= SLOTNO_LIMIT)
895 goto too_many_slots;
896 code = CG_BASE(&cg);
897 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
898 JS_ASSERT(code < end);
899 op = (JSOp) *code;
900 cs = &js_CodeSpec[op];
901 len = (cs->length > 0)
902 ? (uintN) cs->length
903 : js_GetVariableBytecodeLength(code);
904 if ((cs->format & JOF_SHARPSLOT) ||
905 JOF_TYPE(cs->format) == JOF_LOCAL ||
906 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
908 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
909 * emitted only for a function.
911 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
912 (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
913 (op == JSOP_GETLOCALPROP));
914 slot = GET_SLOTNO(code);
915 slot += scriptGlobals;
916 if (!(cs->format & JOF_SHARPSLOT))
917 slot += cg.sharpSlots();
918 if (slot >= SLOTNO_LIMIT)
919 goto too_many_slots;
920 SET_SLOTNO(code, slot);
925 #ifdef METER_PARSENODES
926 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
927 (char *)sbrk(0) - (char *)before,
928 parsenodes,
929 maxparsenodes,
930 parsenodes - recyclednodes);
931 before = sbrk(0);
932 #endif
935 * Nowadays the threaded interpreter needs a stop instruction, so we
936 * do have to emit that here.
938 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
939 goto out;
940 #ifdef METER_PARSENODES
941 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
942 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
943 #endif
944 #ifdef JS_ARENAMETER
945 JS_DumpArenaStats(stdout);
946 #endif
947 script = js_NewScriptFromCG(cx, &cg);
948 if (script && funbox && script != script->emptyScript())
949 script->savedCallerFun = true;
951 #ifdef JS_SCOPE_DEPTH_METER
952 if (script) {
953 JSObject *obj = scopeChain;
954 uintN depth = 1;
955 while ((obj = obj->getParent()) != NULL)
956 ++depth;
957 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
959 #endif
961 out:
962 JS_FinishArenaPool(&codePool);
963 JS_FinishArenaPool(&notePool);
964 return script;
966 too_many_slots:
967 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
968 script = NULL;
969 goto out;
973 * Insist on a final return before control flows out of pn. Try to be a bit
974 * smart about loops: do {...; return e2;} while(0) at the end of a function
975 * that contains an early return e1 will get a strict warning. Similarly for
976 * iloops: while (true){...} is treated as though ... returns.
978 #define ENDS_IN_OTHER 0
979 #define ENDS_IN_RETURN 1
980 #define ENDS_IN_BREAK 2
982 static int
983 HasFinalReturn(JSParseNode *pn)
985 JSParseNode *pn2, *pn3;
986 uintN rv, rv2, hasDefault;
988 switch (pn->pn_type) {
989 case TOK_LC:
990 if (!pn->pn_head)
991 return ENDS_IN_OTHER;
992 return HasFinalReturn(pn->last());
994 case TOK_IF:
995 if (!pn->pn_kid3)
996 return ENDS_IN_OTHER;
997 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
999 case TOK_WHILE:
1000 pn2 = pn->pn_left;
1001 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1002 return ENDS_IN_RETURN;
1003 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1004 return ENDS_IN_RETURN;
1005 return ENDS_IN_OTHER;
1007 case TOK_DO:
1008 pn2 = pn->pn_right;
1009 if (pn2->pn_type == TOK_PRIMARY) {
1010 if (pn2->pn_op == JSOP_FALSE)
1011 return HasFinalReturn(pn->pn_left);
1012 if (pn2->pn_op == JSOP_TRUE)
1013 return ENDS_IN_RETURN;
1015 if (pn2->pn_type == TOK_NUMBER) {
1016 if (pn2->pn_dval == 0)
1017 return HasFinalReturn(pn->pn_left);
1018 return ENDS_IN_RETURN;
1020 return ENDS_IN_OTHER;
1022 case TOK_FOR:
1023 pn2 = pn->pn_left;
1024 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1025 return ENDS_IN_RETURN;
1026 return ENDS_IN_OTHER;
1028 case TOK_SWITCH:
1029 rv = ENDS_IN_RETURN;
1030 hasDefault = ENDS_IN_OTHER;
1031 pn2 = pn->pn_right;
1032 if (pn2->pn_type == TOK_LEXICALSCOPE)
1033 pn2 = pn2->expr();
1034 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1035 if (pn2->pn_type == TOK_DEFAULT)
1036 hasDefault = ENDS_IN_RETURN;
1037 pn3 = pn2->pn_right;
1038 JS_ASSERT(pn3->pn_type == TOK_LC);
1039 if (pn3->pn_head) {
1040 rv2 = HasFinalReturn(pn3->last());
1041 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1042 /* Falling through to next case or default. */;
1043 else
1044 rv &= rv2;
1047 /* If a final switch has no default case, we judge it harshly. */
1048 rv &= hasDefault;
1049 return rv;
1051 case TOK_BREAK:
1052 return ENDS_IN_BREAK;
1054 case TOK_WITH:
1055 return HasFinalReturn(pn->pn_right);
1057 case TOK_RETURN:
1058 return ENDS_IN_RETURN;
1060 case TOK_COLON:
1061 case TOK_LEXICALSCOPE:
1062 return HasFinalReturn(pn->expr());
1064 case TOK_THROW:
1065 return ENDS_IN_RETURN;
1067 case TOK_TRY:
1068 /* If we have a finally block that returns, we are done. */
1069 if (pn->pn_kid3) {
1070 rv = HasFinalReturn(pn->pn_kid3);
1071 if (rv == ENDS_IN_RETURN)
1072 return rv;
1075 /* Else check the try block and any and all catch statements. */
1076 rv = HasFinalReturn(pn->pn_kid1);
1077 if (pn->pn_kid2) {
1078 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1079 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1080 rv &= HasFinalReturn(pn2);
1082 return rv;
1084 case TOK_CATCH:
1085 /* Check this catch block's body. */
1086 return HasFinalReturn(pn->pn_kid3);
1088 case TOK_LET:
1089 /* Non-binary let statements are let declarations. */
1090 if (pn->pn_arity != PN_BINARY)
1091 return ENDS_IN_OTHER;
1092 return HasFinalReturn(pn->pn_right);
1094 default:
1095 return ENDS_IN_OTHER;
1099 static JSBool
1100 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1101 uintN anonerrnum)
1103 const char *name;
1105 JS_ASSERT(tc->inFunction());
1106 if (tc->fun->atom) {
1107 name = js_AtomToPrintableString(cx, tc->fun->atom);
1108 } else {
1109 errnum = anonerrnum;
1110 name = NULL;
1112 return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name);
1115 static JSBool
1116 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1118 JS_ASSERT(tc->inFunction());
1119 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1120 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1121 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1125 * Check that it is permitted to assign to lhs. Strict mode code may not
1126 * assign to 'eval' or 'arguments'.
1128 bool
1129 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1131 if (tc->needStrictChecks() &&
1132 lhs->pn_type == TOK_NAME) {
1133 JSAtom *atom = lhs->pn_atom;
1134 JSAtomState *atomState = &cx->runtime->atomState;
1135 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1136 const char *name = js_AtomToPrintableString(cx, atom);
1137 if (!name ||
1138 !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
1139 name)) {
1140 return false;
1144 return true;
1148 * Check that it is permitted to introduce a binding for atom. Strict
1149 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1150 * Use pn for reporting error locations, or use tc's token stream if
1151 * pn is NULL.
1153 bool
1154 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
1156 if (!tc->needStrictChecks())
1157 return true;
1159 JSAtomState *atomState = &cx->runtime->atomState;
1160 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1161 const char *name = js_AtomToPrintableString(cx, atom);
1162 if (name)
1163 ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
1164 return false;
1166 return true;
1170 * In strict mode code, all formal parameter names must be distinct. If fun's
1171 * formals are legit given fun's strictness level, return true. Otherwise,
1172 * report an error and return false. Use pn for error position reporting,
1173 * unless we can find something more accurate in tc's decls.
1175 * In some cases the code to parse the argument list will already have noticed
1176 * the duplication; we could try to use that knowledge instead of re-checking
1177 * here. But since the strictness of the function's body determines what
1178 * constraints to apply to the argument list, we can't report the error until
1179 * after we've parsed the body. And as it turns out, the function's local name
1180 * list makes it reasonably cheap to find duplicates after the fact.
1182 static bool
1183 CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
1184 JSParseNode *pn)
1186 JSAtom *atom;
1188 if (!tc->needStrictChecks())
1189 return true;
1191 atom = fun->findDuplicateFormal();
1192 if (atom) {
1194 * We have found a duplicate parameter name. If we can find the
1195 * JSDefinition for the argument, that will have a more accurate source
1196 * location.
1198 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1199 if (dn->pn_op == JSOP_GETARG)
1200 pn = dn;
1201 const char *name = js_AtomToPrintableString(cx, atom);
1202 if (!name ||
1203 !ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_DUPLICATE_FORMAL, name)) {
1204 return false;
1208 if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
1209 JSAtomState *atoms = &cx->runtime->atomState;
1210 atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS
1211 ? atoms->argumentsAtom : atoms->evalAtom);
1212 /* The definition's source position will be more precise. */
1213 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1214 JS_ASSERT(dn->pn_atom == atom);
1215 const char *name = js_AtomToPrintableString(cx, atom);
1216 if (!name ||
1217 !ReportStrictModeError(cx, TS(tc->parser), tc, dn, JSMSG_BAD_BINDING, name)) {
1218 return false;
1222 return true;
1225 JSParseNode *
1226 Parser::functionBody()
1228 JSStmtInfo stmtInfo;
1229 uintN oldflags, firstLine;
1230 JSParseNode *pn;
1232 JS_ASSERT(tc->inFunction());
1233 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1234 stmtInfo.flags = SIF_BODY_BLOCK;
1236 oldflags = tc->flags;
1237 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1240 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1241 * later, because we may have not peeked in tokenStream yet, so statements
1242 * won't acquire a valid pn->pn_pos.begin from the current token.
1244 firstLine = tokenStream.getLineno();
1245 #if JS_HAS_EXPR_CLOSURES
1246 if (tokenStream.currentToken().type == TOK_LC) {
1247 pn = statements();
1248 } else {
1249 pn = UnaryNode::create(tc);
1250 if (pn) {
1251 pn->pn_kid = assignExpr();
1252 if (!pn->pn_kid) {
1253 pn = NULL;
1254 } else {
1255 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1256 ReportBadReturn(context, tc, JSREPORT_ERROR,
1257 JSMSG_BAD_GENERATOR_RETURN,
1258 JSMSG_BAD_ANON_GENERATOR_RETURN);
1259 pn = NULL;
1260 } else {
1261 pn->pn_type = TOK_RETURN;
1262 pn->pn_op = JSOP_RETURN;
1263 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1268 #else
1269 pn = statements();
1270 #endif
1272 if (pn) {
1273 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1274 js_PopStatement(tc);
1275 pn->pn_pos.begin.lineno = firstLine;
1277 /* Check for falling off the end of a function that returns a value. */
1278 if (JS_HAS_STRICT_OPTION(context) && (tc->flags & TCF_RETURN_EXPR) &&
1279 !CheckFinalReturn(context, tc, pn)) {
1280 pn = NULL;
1284 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1285 return pn;
1288 static JSAtomListElement *
1289 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1291 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, pn->pn_atom);
1292 if (!ale)
1293 return NULL;
1295 JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
1296 if (!dn)
1297 return NULL;
1299 ALE_SET_DEFN(ale, dn);
1300 dn->pn_defn = true;
1301 dn->pn_dflags |= PND_PLACEHOLDER;
1302 return ale;
1305 static bool
1306 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1308 JS_ASSERT(!pn->pn_used);
1309 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1311 JSHashEntry **hep;
1312 JSAtomListElement *ale = NULL;
1313 JSAtomList *list = NULL;
1315 if (let)
1316 ale = (list = &tc->decls)->rawLookup(atom, hep);
1317 if (!ale)
1318 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1320 if (ale) {
1321 JSDefinition *dn = ALE_DEFN(ale);
1322 if (dn != pn) {
1323 JSParseNode **pnup = &dn->dn_uses;
1324 JSParseNode *pnu;
1325 uintN start = let ? pn->pn_blockid : tc->bodyid;
1327 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1328 JS_ASSERT(pnu->pn_used);
1329 pnu->pn_lexdef = (JSDefinition *) pn;
1330 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1331 pnup = &pnu->pn_link;
1334 if (pnu != dn->dn_uses) {
1335 *pnup = pn->dn_uses;
1336 pn->dn_uses = dn->dn_uses;
1337 dn->dn_uses = pnu;
1339 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1340 list->rawRemove(tc->parser, ale, hep);
1345 ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1346 if (!ale)
1347 return false;
1348 ALE_SET_DEFN(ale, pn);
1349 pn->pn_defn = true;
1350 pn->pn_dflags &= ~PND_PLACEHOLDER;
1351 return true;
1354 static void
1355 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1357 JS_ASSERT(!pn->pn_used);
1358 JS_ASSERT(!pn->pn_defn);
1359 JS_ASSERT(pn != dn->dn_uses);
1360 pn->pn_link = dn->dn_uses;
1361 dn->dn_uses = pn;
1362 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1363 pn->pn_used = true;
1364 pn->pn_lexdef = dn;
1367 static void
1368 ForgetUse(JSParseNode *pn)
1370 if (!pn->pn_used) {
1371 JS_ASSERT(!pn->pn_defn);
1372 return;
1375 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1376 JSParseNode *pnu;
1377 while ((pnu = *pnup) != pn)
1378 pnup = &pnu->pn_link;
1379 *pnup = pn->pn_link;
1380 pn->pn_used = false;
1383 static JSParseNode *
1384 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1386 JSParseNode *lhs = NewOrRecycledNode(tc);
1387 if (!lhs)
1388 return NULL;
1389 *lhs = *pn;
1391 if (pn->pn_used) {
1392 JSDefinition *dn = pn->pn_lexdef;
1393 JSParseNode **pnup = &dn->dn_uses;
1395 while (*pnup != pn)
1396 pnup = &(*pnup)->pn_link;
1397 *pnup = lhs;
1398 lhs->pn_link = pn->pn_link;
1399 pn->pn_link = NULL;
1402 pn->pn_type = TOK_ASSIGN;
1403 pn->pn_op = JSOP_NOP;
1404 pn->pn_arity = PN_BINARY;
1405 pn->pn_parens = false;
1406 pn->pn_used = pn->pn_defn = false;
1407 pn->pn_left = lhs;
1408 pn->pn_right = rhs;
1409 return lhs;
1412 static JSParseNode *
1413 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1416 * If dn is var, const, or let, and it has an initializer, then we must
1417 * rewrite it to be an assignment node, whose freshly allocated left-hand
1418 * side becomes a use of pn.
1420 if (dn->isBindingForm()) {
1421 JSParseNode *rhs = dn->expr();
1422 if (rhs) {
1423 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1424 if (!lhs)
1425 return NULL;
1426 //pn->dn_uses = lhs;
1427 dn = (JSDefinition *) lhs;
1430 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1431 } else if (dn->kind() == JSDefinition::FUNCTION) {
1432 JS_ASSERT(dn->isTopLevel());
1433 JS_ASSERT(dn->pn_op == JSOP_NOP);
1434 dn->pn_type = TOK_NAME;
1435 dn->pn_arity = PN_NAME;
1436 dn->pn_atom = atom;
1439 /* Now make dn no longer a definition, rather a use of pn. */
1440 JS_ASSERT(dn->pn_type == TOK_NAME);
1441 JS_ASSERT(dn->pn_arity == PN_NAME);
1442 JS_ASSERT(dn->pn_atom == atom);
1444 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1445 JS_ASSERT(pnu->pn_used);
1446 JS_ASSERT(!pnu->pn_defn);
1447 pnu->pn_lexdef = (JSDefinition *) pn;
1448 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1450 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1451 pn->dn_uses = dn;
1453 dn->pn_defn = false;
1454 dn->pn_used = true;
1455 dn->pn_lexdef = (JSDefinition *) pn;
1456 dn->pn_cookie = FREE_UPVAR_COOKIE;
1457 dn->pn_dflags &= ~PND_BOUND;
1458 return dn;
1461 static bool
1462 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1464 JSParseNode *argpn, *argsbody;
1466 /* Flag tc so we don't have to lookup arguments on every use. */
1467 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1468 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1469 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1470 tc->flags |= TCF_FUN_PARAM_EVAL;
1473 * Make an argument definition node, distinguished by being in tc->decls
1474 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1475 * list node returned via pn->pn_body.
1477 argpn = NameNode::create(atom, tc);
1478 if (!argpn)
1479 return false;
1480 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1482 /* Arguments are initialized by definition. */
1483 argpn->pn_dflags |= PND_INITIALIZED;
1484 if (!Define(argpn, atom, tc))
1485 return false;
1487 argsbody = pn->pn_body;
1488 if (!argsbody) {
1489 argsbody = ListNode::create(tc);
1490 if (!argsbody)
1491 return false;
1492 argsbody->pn_type = TOK_ARGSBODY;
1493 argsbody->pn_op = JSOP_NOP;
1494 argsbody->makeEmpty();
1495 pn->pn_body = argsbody;
1497 argsbody->append(argpn);
1499 argpn->pn_op = JSOP_GETARG;
1500 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1501 argpn->pn_dflags |= PND_BOUND;
1502 return true;
1506 * Compile a JS function body, which might appear as the value of an event
1507 * handler attribute in an HTML <INPUT> tag.
1509 bool
1510 Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1511 const jschar *chars, size_t length,
1512 const char *filename, uintN lineno)
1514 Compiler compiler(cx, principals);
1516 if (!compiler.init(chars, length, NULL, filename, lineno))
1517 return false;
1519 /* No early return from after here until the js_FinishArenaPool calls. */
1520 JSArenaPool codePool, notePool;
1521 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1522 &cx->scriptStackQuota);
1523 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
1524 &cx->scriptStackQuota);
1526 Parser &parser = compiler.parser;
1527 TokenStream &tokenStream = parser.tokenStream;
1529 JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
1530 if (!funcg.init())
1531 return NULL;
1533 funcg.flags |= TCF_IN_FUNCTION;
1534 funcg.fun = fun;
1535 if (!GenerateBlockId(&funcg, funcg.bodyid))
1536 return NULL;
1538 /* FIXME: make Function format the source for a function definition. */
1539 tokenStream.mungeCurrentToken(TOK_NAME);
1540 JSParseNode *fn = FunctionNode::create(&funcg);
1541 if (fn) {
1542 fn->pn_body = NULL;
1543 fn->pn_cookie = FREE_UPVAR_COOKIE;
1545 uintN nargs = fun->nargs;
1546 if (nargs) {
1547 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1548 if (!names) {
1549 fn = NULL;
1550 } else {
1551 for (uintN i = 0; i < nargs; i++) {
1552 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1553 if (!DefineArg(fn, name, i, &funcg)) {
1554 fn = NULL;
1555 break;
1563 * Farble the body so that it looks like a block statement to js_EmitTree,
1564 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1565 * done parsing, we must fold constants, analyze any nested functions, and
1566 * generate code for this function, including a stop opcode at the end.
1568 tokenStream.mungeCurrentToken(TOK_LC);
1569 JSParseNode *pn = fn ? parser.functionBody() : NULL;
1570 if (pn) {
1571 if (!CheckStrictFormals(cx, &funcg, fun, pn)) {
1572 pn = NULL;
1573 } else if (!tokenStream.matchToken(TOK_EOF)) {
1574 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1575 pn = NULL;
1576 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1577 /* js_FoldConstants reported the error already. */
1578 pn = NULL;
1579 } else if (funcg.functionList &&
1580 !parser.analyzeFunctions(funcg.functionList, funcg.flags)) {
1581 pn = NULL;
1582 } else {
1583 if (fn->pn_body) {
1584 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1585 fn->pn_body->append(pn);
1586 fn->pn_body->pn_pos = pn->pn_pos;
1587 pn = fn->pn_body;
1590 if (!js_EmitFunctionScript(cx, &funcg, pn))
1591 pn = NULL;
1595 /* Restore saved state and release code generation arenas. */
1596 JS_FinishArenaPool(&codePool);
1597 JS_FinishArenaPool(&notePool);
1598 return pn != NULL;
1602 * Parameter block types for the several Binder functions. We use a common
1603 * helper function signature in order to share code among destructuring and
1604 * simple variable declaration parsers. In the destructuring case, the binder
1605 * function is called indirectly from the variable declaration parser by way
1606 * of CheckDestructuring and its friends.
1608 typedef JSBool
1609 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1611 struct BindData {
1612 BindData() : fresh(true) {}
1614 JSParseNode *pn; /* name node for definition processing and
1615 error source coordinates */
1616 JSOp op; /* prolog bytecode or nop */
1617 Binder binder; /* binder, discriminates u */
1618 union {
1619 struct {
1620 uintN overflow;
1621 } let;
1623 bool fresh;
1626 static JSBool
1627 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1628 JSLocalKind localKind, bool isArg)
1630 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1633 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1634 * Instead 'var arguments' always restates the predefined property of the
1635 * activation objects whose name is 'arguments'. Assignment to such a
1636 * variable must be handled specially.
1638 * Special case: an argument named 'arguments' *does* shadow the predefined
1639 * arguments property.
1641 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1642 return JS_TRUE;
1644 return js_AddLocal(cx, fun, atom, localKind);
1647 #if JS_HAS_DESTRUCTURING
1648 static JSBool
1649 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1650 JSTreeContext *tc)
1652 JSParseNode *pn;
1654 /* Flag tc so we don't have to lookup arguments on every use. */
1655 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1656 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1657 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1658 tc->flags |= TCF_FUN_PARAM_EVAL;
1660 JS_ASSERT(tc->inFunction());
1662 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1663 if (localKind != JSLOCAL_NONE) {
1664 ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
1665 JSMSG_DESTRUCT_DUP_ARG);
1666 return JS_FALSE;
1668 JS_ASSERT(!tc->decls.lookup(atom));
1670 pn = data->pn;
1671 if (!Define(pn, atom, tc))
1672 return JS_FALSE;
1674 uintN index = tc->fun->u.i.nvars;
1675 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1676 return JS_FALSE;
1677 pn->pn_op = JSOP_SETLOCAL;
1678 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1679 pn->pn_dflags |= PND_BOUND;
1680 return JS_TRUE;
1682 #endif /* JS_HAS_DESTRUCTURING */
1684 JSFunction *
1685 Parser::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1687 JSObject *parent;
1688 JSFunction *fun;
1690 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1693 * Find the global compilation context in order to pre-set the newborn
1694 * function's parent slot to tc->scopeChain. If the global context is a
1695 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1696 * clear parent and proto.
1698 while (tc->parent)
1699 tc = tc->parent;
1700 parent = tc->inFunction() ? NULL : tc->scopeChain;
1702 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1703 parent, atom);
1705 if (fun && !tc->compileAndGo()) {
1706 FUN_OBJECT(fun)->clearParent();
1707 FUN_OBJECT(fun)->clearProto();
1709 return fun;
1712 static JSBool
1713 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
1715 TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
1716 if (tt == TOK_ERROR)
1717 return JS_FALSE;
1718 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1719 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
1720 return JS_FALSE;
1722 (void) ts->matchToken(TOK_SEMI);
1723 return JS_TRUE;
1726 bool
1727 Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
1729 if (!markFunArgs(funbox, tcflags))
1730 return false;
1731 setFunctionKinds(funbox, tcflags);
1732 return true;
1736 * Mark as funargs any functions that reach up to one or more upvars across an
1737 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1739 * function f(o, p) {
1740 * o.m = function o_m(a) {
1741 * function g() { return p; }
1742 * function h() { return a; }
1743 * return g() + h();
1747 * but without this extra marking phase, function g will not be marked as a
1748 * funarg since it is called from within its parent scope. But g reaches up to
1749 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1750 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1751 * nor uses an upvar "above" o_m's level.
1753 * If function g itself contained lambdas that contained non-lambdas that reach
1754 * up above its level, then those non-lambdas would have to be marked too. This
1755 * process is potentially exponential in the number of functions, but generally
1756 * not so complex. But it can't be done during a single recursive traversal of
1757 * the funbox tree, so we must use a work queue.
1759 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1760 * between the static level of the bodies of funbox and its peers (which must
1761 * be funbox->level + 1), and the static level of the nearest upvar among all
1762 * the upvars contained by funbox and its peers. If there are no upvars, return
1763 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1765 static uintN
1766 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1768 uintN allskipmin = FREE_STATIC_LEVEL;
1770 do {
1771 JSParseNode *fn = funbox->node;
1772 JSFunction *fun = (JSFunction *) funbox->object;
1773 int fnlevel = level;
1776 * An eval can leak funbox, functions along its ancestor line, and its
1777 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1778 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1779 * already been marked as funargs by this point. Therefore we have to
1780 * flag only funbox->node and funbox->kids' nodes here.
1782 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1783 fn->setFunArg();
1784 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1785 kid->node->setFunArg();
1789 * Compute in skipmin the least distance from fun's static level up to
1790 * an upvar, whether used directly by fun, or indirectly by a function
1791 * nested in fun.
1793 uintN skipmin = FREE_STATIC_LEVEL;
1794 JSParseNode *pn = fn->pn_body;
1796 if (pn->pn_type == TOK_UPVARS) {
1797 JSAtomList upvars(pn->pn_names);
1798 JS_ASSERT(upvars.count != 0);
1800 JSAtomListIterator iter(&upvars);
1801 JSAtomListElement *ale;
1803 while ((ale = iter()) != NULL) {
1804 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1806 if (!lexdep->isFreeVar()) {
1807 uintN upvarLevel = lexdep->frameLevel();
1809 if (int(upvarLevel) <= fnlevel)
1810 fn->setFunArg();
1812 uintN skip = (funbox->level + 1) - upvarLevel;
1813 if (skip < skipmin)
1814 skipmin = skip;
1820 * If this function escapes, whether directly (the parser detects such
1821 * escapes) or indirectly (because this non-escaping function uses an
1822 * upvar that reaches across an outer function boundary where the outer
1823 * function escapes), enqueue it for further analysis, and bump fnlevel
1824 * to trap any non-escaping children.
1826 if (fn->isFunArg()) {
1827 queue->push(funbox);
1828 fnlevel = int(funbox->level);
1832 * Now process the current function's children, and recalibrate their
1833 * cumulative skipmin to be relative to the current static level.
1835 if (funbox->kids) {
1836 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1838 JS_ASSERT(kidskipmin != 0);
1839 if (kidskipmin != FREE_STATIC_LEVEL) {
1840 --kidskipmin;
1841 if (kidskipmin != 0 && kidskipmin < skipmin)
1842 skipmin = kidskipmin;
1847 * Finally, after we've traversed all of the current function's kids,
1848 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1849 * with allskipmin, but minimize across funbox and all of its siblings,
1850 * to compute our return value.
1852 if (skipmin != FREE_STATIC_LEVEL) {
1853 fun->u.i.skipmin = skipmin;
1854 if (skipmin < allskipmin)
1855 allskipmin = skipmin;
1857 } while ((funbox = funbox->siblings) != NULL);
1859 return allskipmin;
1862 bool
1863 Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1865 JSFunctionBoxQueue queue;
1866 if (!queue.init(functionCount))
1867 return false;
1869 FindFunArgs(funbox, -1, &queue);
1870 while ((funbox = queue.pull()) != NULL) {
1871 JSParseNode *fn = funbox->node;
1872 JS_ASSERT(fn->isFunArg());
1874 JSParseNode *pn = fn->pn_body;
1875 if (pn->pn_type == TOK_UPVARS) {
1876 JSAtomList upvars(pn->pn_names);
1877 JS_ASSERT(upvars.count != 0);
1879 JSAtomListIterator iter(&upvars);
1880 JSAtomListElement *ale;
1882 while ((ale = iter()) != NULL) {
1883 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1885 if (!lexdep->isFreeVar() &&
1886 !lexdep->isFunArg() &&
1887 (lexdep->kind() == JSDefinition::FUNCTION ||
1888 PN_OP(lexdep) == JSOP_CALLEE)) {
1890 * Mark this formerly-Algol-like function as an escaping
1891 * function (i.e., as a funarg), because it is used from a
1892 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1893 * access upvars.
1895 * Progress is guaranteed because we set the funarg flag
1896 * here, which suppresses revisiting this function (thanks
1897 * to the !lexdep->isFunArg() test just above).
1899 lexdep->setFunArg();
1901 JSFunctionBox *afunbox;
1902 if (PN_OP(lexdep) == JSOP_CALLEE) {
1904 * A named function expression will not appear to be a
1905 * funarg if it is immediately applied. However, if its
1906 * name is used in an escaping function nested within
1907 * it, then it must become flagged as a funarg again.
1908 * See bug 545980.
1910 afunbox = funbox;
1911 uintN calleeLevel = UPVAR_FRAME_SKIP(lexdep->pn_cookie);
1912 uintN staticLevel = afunbox->level + 1U;
1913 while (staticLevel != calleeLevel) {
1914 afunbox = afunbox->parent;
1915 --staticLevel;
1917 afunbox->node->setFunArg();
1918 } else {
1919 afunbox = lexdep->pn_funbox;
1921 queue.push(afunbox);
1924 * Walk over nested functions again, now that we have
1925 * changed the level across which it is unsafe to access
1926 * upvars using the runtime dynamic link (frame chain).
1928 if (afunbox->kids)
1929 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1934 return true;
1937 static uint32
1938 MinBlockId(JSParseNode *fn, uint32 id)
1940 if (fn->pn_blockid < id)
1941 return false;
1942 if (fn->pn_defn) {
1943 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1944 if (pn->pn_blockid < id)
1945 return false;
1948 return true;
1951 static inline bool
1952 CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
1955 * Consider the current function (the lambda, innermost below) using a var
1956 * x defined two static levels up:
1958 * function f() {
1959 * // z = g();
1960 * var x = 42;
1961 * function g() {
1962 * return function () { return x; };
1964 * return g();
1967 * So long as (1) the initialization in 'var x = 42' dominates all uses of
1968 * g and (2) x is not reassigned, it is safe to optimize the lambda to a
1969 * flat closure. Uncommenting the early call to g makes this optimization
1970 * unsafe (z could name a global setter that calls its argument).
1972 JSFunctionBox *afunbox = funbox;
1973 uintN dnLevel = dn->frameLevel();
1975 JS_ASSERT(dnLevel <= funbox->level);
1976 while (afunbox->level != dnLevel) {
1977 afunbox = afunbox->parent;
1980 * NB: afunbox can't be null because we are sure to find a function box
1981 * whose level == dnLevel before we would try to walk above the root of
1982 * the funbox tree. See bug 493260 comments 16-18.
1984 * Assert but check anyway, to protect future changes that bind eval
1985 * upvars in the parser.
1987 JS_ASSERT(afunbox);
1990 * If this function is reaching up across an enclosing funarg, then we
1991 * cannot copy dn's value into a flat closure slot (the display stops
1992 * working once the funarg escapes).
1994 if (!afunbox || afunbox->node->isFunArg())
1995 return false;
1998 * Reaching up for dn across a generator also means we can't flatten,
1999 * since the generator iterator does not run until later, in general.
2000 * See bug 563034.
2002 if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
2003 return false;
2007 * If afunbox's function (which is at the same level as dn) is in a loop,
2008 * pessimistically assume the variable initializer may be in the same loop.
2009 * A flat closure would then be unsafe, as the captured variable could be
2010 * assigned after the closure is created. See bug 493232.
2012 if (afunbox->inLoop)
2013 return false;
2016 * |with| and eval used as an operator defeat lexical scoping: they can be
2017 * used to assign to any in-scope variable. Therefore they must disable
2018 * flat closures that use such upvars. The parser detects these as special
2019 * forms and marks the function heavyweight.
2021 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
2022 return false;
2025 * If afunbox's function is not a lambda, it will be hoisted, so it could
2026 * capture the undefined value that by default initializes var, let, and
2027 * const bindings. And if dn is a function that comes at (meaning a
2028 * function refers to its own name) or strictly after afunbox, we also
2029 * defeat the flat closure optimization for this dn.
2031 JSFunction *afun = (JSFunction *) afunbox->object;
2032 if (!(afun->flags & JSFUN_LAMBDA)) {
2033 if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
2034 return false;
2037 if (!dn->isInitialized())
2038 return false;
2040 JSDefinition::Kind dnKind = dn->kind();
2041 if (dnKind != JSDefinition::CONST) {
2042 if (dn->isAssigned())
2043 return false;
2046 * Any formal could be mutated behind our back via the arguments
2047 * object, so deoptimize if the outer function uses arguments.
2049 * In a Function constructor call where the final argument -- the body
2050 * source for the function to create -- contains a nested function
2051 * definition or expression, afunbox->parent will be null. The body
2052 * source might use |arguments| outside of any nested functions it may
2053 * contain, so we have to check the tcflags parameter that was passed
2054 * in from Compiler::compileFunctionBody.
2056 if (dnKind == JSDefinition::ARG &&
2057 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
2058 return false;
2063 * Check quick-and-dirty dominance relation. Function definitions dominate
2064 * their uses thanks to hoisting. Other binding forms hoist as undefined,
2065 * of course, so check forward-reference and blockid relations.
2067 if (dnKind != JSDefinition::FUNCTION) {
2069 * Watch out for code such as
2071 * (function () {
2072 * ...
2073 * var jQuery = ... = function (...) {
2074 * return new jQuery.foo.bar(baz);
2076 * ...
2077 * })();
2079 * where the jQuery variable is not reassigned, but of course is not
2080 * initialized at the time that the would-be-flat closure containing
2081 * the jQuery upvar is formed.
2083 if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
2084 return false;
2085 if (!MinBlockId(afunbox->node, dn->pn_blockid))
2086 return false;
2088 return true;
2091 static void
2092 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2094 uintN dnLevel = dn->frameLevel();
2096 while ((funbox = funbox->parent) != NULL) {
2098 * Notice that funbox->level is the static level of the definition or
2099 * expression of the function parsed into funbox, not the static level
2100 * of its body. Therefore we must add 1 to match dn's level to find the
2101 * funbox whose body contains the dn definition.
2103 if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
2104 funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2105 break;
2107 funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
2110 if (!funbox && (tcflags & TCF_IN_FUNCTION))
2111 tcflags |= TCF_FUN_HEAVYWEIGHT;
2114 static void
2115 DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2117 uintN ndeoptimized = 0;
2118 const TokenPos &pos = funbox->node->pn_body->pn_pos;
2120 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2121 JS_ASSERT(pnu->pn_used);
2122 JS_ASSERT(!pnu->pn_defn);
2123 if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
2124 pnu->pn_dflags |= PND_DEOPTIMIZED;
2125 ++ndeoptimized;
2129 if (ndeoptimized != 0)
2130 FlagHeavyweights(dn, funbox, tcflags);
2133 void
2134 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
2136 #ifdef JS_FUNCTION_METERING
2137 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2138 #else
2139 # define FUN_METER(x) ((void)0)
2140 #endif
2142 for (;;) {
2143 JSParseNode *fn = funbox->node;
2144 JSParseNode *pn = fn->pn_body;
2146 if (funbox->kids) {
2147 setFunctionKinds(funbox->kids, tcflags);
2150 * We've unwound from recursively setting our kids' kinds, which
2151 * also classifies enclosing functions holding upvars referenced in
2152 * those descendants' bodies. So now we can check our "methods".
2154 * Despecialize from branded method-identity-based shape to sprop-
2155 * or slot-based shape if this function smells like a constructor
2156 * and too many of its methods are *not* joinable null closures
2157 * (i.e., they have one or more upvars fetched via the display).
2159 JSParseNode *pn2 = pn;
2160 if (PN_TYPE(pn2) == TOK_UPVARS)
2161 pn2 = pn2->pn_tree;
2162 if (PN_TYPE(pn2) == TOK_ARGSBODY)
2163 pn2 = pn2->last();
2165 #if JS_HAS_EXPR_CLOSURES
2166 if (PN_TYPE(pn2) == TOK_LC)
2167 #endif
2168 if (!(funbox->tcflags & TCF_RETURN_EXPR)) {
2169 uintN methodSets = 0, slowMethodSets = 0;
2171 for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
2172 JS_ASSERT(PN_OP(method) == JSOP_LAMBDA || PN_OP(method) == JSOP_LAMBDA_FC);
2173 ++methodSets;
2174 if (!method->pn_funbox->joinable())
2175 ++slowMethodSets;
2178 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
2179 funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
2183 JSFunction *fun = (JSFunction *) funbox->object;
2185 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2187 FUN_METER(allfun);
2188 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2189 FUN_METER(heavy);
2190 } else if (pn->pn_type != TOK_UPVARS) {
2192 * No lexical dependencies => null closure, for best performance.
2193 * A null closure needs no scope chain, but alas we've coupled
2194 * principals-finding to scope (for good fundamental reasons, but
2195 * the implementation overloads the parent slot and we should fix
2196 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2198 * In more detail: the ES3 spec allows the implementation to create
2199 * "joined function objects", or not, at its discretion. But real-
2200 * world implementations always create unique function objects for
2201 * closures, and this can be detected via mutation. Open question:
2202 * do popular implementations create unique function objects for
2203 * null closures?
2205 * FIXME: bug 476950.
2207 FUN_METER(nofreeupvar);
2208 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2209 } else {
2210 JSAtomList upvars(pn->pn_names);
2211 JS_ASSERT(upvars.count != 0);
2213 JSAtomListIterator iter(&upvars);
2214 JSAtomListElement *ale;
2216 if (!fn->isFunArg()) {
2218 * This function is Algol-like, it never escapes. So long as it
2219 * does not assign to outer variables, it needs only an upvars
2220 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2221 * bytecode to reach up the frame stack at runtime based on
2222 * those upvars' cookies.
2224 * Any assignments to upvars from functions called by this one
2225 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2226 * which load from stack homes when interpreting or from native
2227 * stack slots when executing a trace.
2229 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2230 * nested function to assign to an outer lexical variable, so
2231 * we defer adding yet more code footprint in the absence of
2232 * evidence motivating these opcodes.
2234 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
2235 uintN nupvars = 0;
2238 * Check that at least one outer lexical binding was assigned
2239 * to (global variables don't count). This is conservative: we
2240 * could limit assignments to those in the current function,
2241 * but that's too much work. As with flat closures (handled
2242 * below), we optimize for the case where outer bindings are
2243 * not reassigned anywhere.
2245 while ((ale = iter()) != NULL) {
2246 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2248 if (!lexdep->isFreeVar()) {
2249 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2250 ++nupvars;
2251 if (lexdep->isAssigned())
2252 break;
2255 if (!ale)
2256 mutation = false;
2258 if (nupvars == 0) {
2259 FUN_METER(onlyfreevar);
2260 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2261 } else if (!mutation &&
2262 !(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
2264 * Algol-like functions can read upvars using the dynamic
2265 * link (cx->fp/fp->down), optimized using the cx->display
2266 * lookup table indexed by static level. They do not need
2267 * to entrain and search their environment objects.
2269 FUN_METER(display);
2270 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2271 } else {
2272 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
2273 FUN_METER(setupvar);
2275 } else {
2276 uintN nupvars = 0, nflattened = 0;
2279 * For each lexical dependency from this closure to an outer
2280 * binding, analyze whether it is safe to copy the binding's
2281 * value into a flat closure slot when the closure is formed.
2283 while ((ale = iter()) != NULL) {
2284 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2286 if (!lexdep->isFreeVar()) {
2287 ++nupvars;
2288 if (CanFlattenUpvar(lexdep, funbox, tcflags)) {
2289 ++nflattened;
2290 continue;
2292 DeoptimizeUsesWithin(lexdep, funbox, tcflags);
2296 if (nupvars == 0) {
2297 FUN_METER(onlyfreevar);
2298 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2299 } else if (nflattened == nupvars) {
2300 /* FIXME bug 545759: to test nflattened != 0 */
2302 * We made it all the way through the upvar loop, so it's
2303 * safe to optimize to a flat closure.
2305 FUN_METER(flat);
2306 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2307 switch (PN_OP(fn)) {
2308 case JSOP_DEFFUN:
2309 fn->pn_op = JSOP_DEFFUN_FC;
2310 break;
2311 case JSOP_DEFLOCALFUN:
2312 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2313 break;
2314 case JSOP_LAMBDA:
2315 fn->pn_op = JSOP_LAMBDA_FC;
2316 break;
2317 default:
2318 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2319 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2321 } else {
2322 FUN_METER(badfunarg);
2327 if (FUN_KIND(fun) == JSFUN_INTERPRETED && pn->pn_type == TOK_UPVARS) {
2329 * One or more upvars cannot be safely snapshot into a flat
2330 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2331 * all upvars, and for each non-free upvar, ensure that its
2332 * containing function has been flagged as heavyweight.
2334 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2335 * generating any code for a tree of nested functions.
2337 JSAtomList upvars(pn->pn_names);
2338 JS_ASSERT(upvars.count != 0);
2340 JSAtomListIterator iter(&upvars);
2341 JSAtomListElement *ale;
2343 while ((ale = iter()) != NULL) {
2344 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2345 if (!lexdep->isFreeVar())
2346 FlagHeavyweights(lexdep, funbox, tcflags);
2350 funbox = funbox->siblings;
2351 if (!funbox)
2352 break;
2355 #undef FUN_METER
2358 const char js_argument_str[] = "argument";
2359 const char js_variable_str[] = "variable";
2360 const char js_unknown_str[] = "unknown";
2362 const char *
2363 JSDefinition::kindString(Kind kind)
2365 static const char *table[] = {
2366 js_var_str, js_const_str, js_let_str,
2367 js_function_str, js_argument_str, js_unknown_str
2370 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2371 return table[kind];
2374 static JSFunctionBox *
2375 EnterFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2376 uintN lambda = JSFUN_LAMBDA)
2378 JSTreeContext *tc = funtc->parent;
2379 JSFunction *fun = tc->parser->newFunction(tc, funAtom, lambda);
2380 if (!fun)
2381 return NULL;
2383 /* Create box for fun->object early to protect against last-ditch GC. */
2384 JSFunctionBox *funbox = tc->parser->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2385 if (!funbox)
2386 return NULL;
2388 /* Initialize non-default members of funtc. */
2389 funtc->flags |= funbox->tcflags;
2390 funtc->blockidGen = tc->blockidGen;
2391 if (!GenerateBlockId(funtc, funtc->bodyid))
2392 return NULL;
2393 funtc->fun = fun;
2394 funtc->funbox = funbox;
2395 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2396 return NULL;
2398 return funbox;
2401 static bool
2402 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2403 uintN lambda = JSFUN_LAMBDA)
2405 JSTreeContext *tc = funtc->parent;
2406 tc->blockidGen = funtc->blockidGen;
2408 JSFunctionBox *funbox = fn->pn_funbox;
2409 funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
2411 fn->pn_dflags |= PND_INITIALIZED;
2412 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2413 fn->pn_dflags & PND_TOPLEVEL);
2414 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2415 fn->pn_dflags |= PND_BLOCKCHILD;
2418 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2419 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2420 * params and body. We do this only if there are lexical dependencies not
2421 * satisfied by the function's declarations, to avoid penalizing functions
2422 * that use only their arguments and other local bindings.
2424 if (funtc->lexdeps.count != 0) {
2425 JSAtomListIterator iter(&funtc->lexdeps);
2426 JSAtomListElement *ale;
2427 int foundCallee = 0;
2429 while ((ale = iter()) != NULL) {
2430 JSAtom *atom = ALE_ATOM(ale);
2431 JSDefinition *dn = ALE_DEFN(ale);
2432 JS_ASSERT(dn->isPlaceholder());
2434 if (atom == funAtom && lambda != 0) {
2435 dn->pn_op = JSOP_CALLEE;
2436 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2437 dn->pn_dflags |= PND_BOUND;
2440 * If this named function expression uses its own name other
2441 * than to call itself, flag this function specially.
2443 if (dn->isFunArg())
2444 funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2445 foundCallee = 1;
2446 continue;
2449 if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2450 dn->isAssigned()) {
2452 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2453 * any use of dn in funtc assigns. See NoteLValue for the easy
2454 * backward-reference case; this is the hard forward-reference
2455 * case where we pay a higher price.
2457 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2458 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2459 funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2460 break;
2465 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2466 if (!outer_ale)
2467 outer_ale = tc->lexdeps.lookup(atom);
2468 if (outer_ale) {
2470 * Insert dn's uses list at the front of outer_dn's list.
2472 * Without loss of generality or correctness, we allow a dn to
2473 * be in inner and outer lexdeps, since the purpose of lexdeps
2474 * is one-pass coordination of name use and definition across
2475 * functions, and if different dn's are used we'll merge lists
2476 * when leaving the inner function.
2478 * The dn == outer_dn case arises with generator expressions
2479 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2480 * case), and nowhere else, currently.
2482 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2484 if (dn != outer_dn) {
2485 JSParseNode **pnup = &dn->dn_uses;
2486 JSParseNode *pnu;
2488 while ((pnu = *pnup) != NULL) {
2489 pnu->pn_lexdef = outer_dn;
2490 pnup = &pnu->pn_link;
2494 * Make dn be a use that redirects to outer_dn, because we
2495 * can't replace dn with outer_dn in all the pn_namesets in
2496 * the AST where it may be. Instead we make it forward to
2497 * outer_dn. See JSDefinition::resolve.
2499 *pnup = outer_dn->dn_uses;
2500 outer_dn->dn_uses = dn;
2501 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2502 dn->pn_defn = false;
2503 dn->pn_used = true;
2504 dn->pn_lexdef = outer_dn;
2506 } else {
2507 /* Add an outer lexical dependency for ale's definition. */
2508 outer_ale = tc->lexdeps.add(tc->parser, atom);
2509 if (!outer_ale)
2510 return false;
2511 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2515 if (funtc->lexdeps.count - foundCallee != 0) {
2516 JSParseNode *body = fn->pn_body;
2518 fn->pn_body = NameSetNode::create(tc);
2519 if (!fn->pn_body)
2520 return false;
2522 fn->pn_body->pn_type = TOK_UPVARS;
2523 fn->pn_body->pn_pos = body->pn_pos;
2524 if (foundCallee)
2525 funtc->lexdeps.remove(tc->parser, funAtom);
2526 fn->pn_body->pn_names = funtc->lexdeps;
2527 fn->pn_body->pn_tree = body;
2530 funtc->lexdeps.clear();
2533 return true;
2536 JSParseNode *
2537 Parser::functionDef(uintN lambda, bool namePermitted)
2539 JSParseNode *pn, *body, *result;
2540 TokenKind tt;
2541 JSAtomListElement *ale;
2542 #if JS_HAS_DESTRUCTURING
2543 JSParseNode *item, *list = NULL;
2544 bool destructuringArg = false;
2545 JSAtom *duplicatedArg = NULL;
2546 #endif
2549 * Save the current op for later so we can tag the created function as a
2550 * getter/setter if necessary.
2552 JSOp op = tokenStream.currentToken().t_op;
2554 /* Make a TOK_FUNCTION node. */
2555 pn = FunctionNode::create(tc);
2556 if (!pn)
2557 return NULL;
2558 pn->pn_body = NULL;
2559 pn->pn_cookie = FREE_UPVAR_COOKIE;
2562 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2563 * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
2565 * Also treat function sub-statements (non-lambda, non-top-level functions)
2566 * as escaping funargs, since we can't statically analyze their definitions
2567 * and uses.
2569 bool topLevel = tc->atTopLevel();
2570 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2572 /* Scan the optional function name into funAtom. */
2573 JSAtom *funAtom = NULL;
2574 if (namePermitted) {
2575 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
2576 if (tt == TOK_NAME) {
2577 funAtom = tokenStream.currentToken().t_atom;
2578 } else {
2579 if (lambda == 0 && (context->options & JSOPTION_ANONFUNFIX)) {
2580 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
2581 return NULL;
2583 tokenStream.ungetToken();
2588 * Record names for function statements in tc->decls so we know when to
2589 * avoid optimizing variable references that might name a function.
2591 if (lambda == 0 && funAtom) {
2592 ale = tc->decls.lookup(funAtom);
2593 if (ale) {
2594 JSDefinition *dn = ALE_DEFN(ale);
2595 JSDefinition::Kind dn_kind = dn->kind();
2597 JS_ASSERT(!dn->pn_used);
2598 JS_ASSERT(dn->pn_defn);
2600 if (JS_HAS_STRICT_OPTION(context) || dn_kind == JSDefinition::CONST) {
2601 const char *name = js_AtomToPrintableString(context, funAtom);
2602 if (!name ||
2603 !reportErrorNumber(NULL,
2604 (dn_kind != JSDefinition::CONST)
2605 ? JSREPORT_WARNING | JSREPORT_STRICT
2606 : JSREPORT_ERROR,
2607 JSMSG_REDECLARED_VAR,
2608 JSDefinition::kindString(dn_kind),
2609 name)) {
2610 return NULL;
2614 if (topLevel) {
2615 ALE_SET_DEFN(ale, pn);
2616 pn->pn_defn = true;
2617 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2619 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2620 return NULL;
2622 } else if (topLevel) {
2624 * If this function was used before it was defined, claim the
2625 * pre-created definition node for this function that primaryExpr
2626 * put in tc->lexdeps on first forward reference, and recycle pn.
2628 JSHashEntry **hep;
2630 ale = tc->lexdeps.rawLookup(funAtom, hep);
2631 if (ale) {
2632 JSDefinition *fn = ALE_DEFN(ale);
2634 JS_ASSERT(fn->pn_defn);
2635 fn->pn_type = TOK_FUNCTION;
2636 fn->pn_arity = PN_FUNC;
2637 fn->pn_pos.begin = pn->pn_pos.begin;
2638 fn->pn_body = NULL;
2639 fn->pn_cookie = FREE_UPVAR_COOKIE;
2641 tc->lexdeps.rawRemove(tc->parser, ale, hep);
2642 RecycleTree(pn, tc);
2643 pn = fn;
2646 if (!Define(pn, funAtom, tc))
2647 return NULL;
2651 * A function nested at top level inside another's body needs only a
2652 * local variable to bind its name to its value, and not an activation
2653 * object property (it might also need the activation property, if the
2654 * outer function contains with statements, e.g., but the stack slot
2655 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2656 * JSOP_GETLOCAL bytecode).
2658 if (topLevel) {
2659 pn->pn_dflags |= PND_TOPLEVEL;
2661 if (tc->inFunction()) {
2662 JSLocalKind localKind;
2663 uintN index;
2666 * Define a local in the outer function so that BindNameToSlot
2667 * can properly optimize accesses. Note that we need a local
2668 * variable, not an argument, for the function statement. Thus
2669 * we add a variable even if a parameter with the given name
2670 * already exists.
2672 localKind = js_LookupLocal(context, tc->fun, funAtom, &index);
2673 switch (localKind) {
2674 case JSLOCAL_NONE:
2675 case JSLOCAL_ARG:
2676 index = tc->fun->u.i.nvars;
2677 if (!js_AddLocal(context, tc->fun, funAtom, JSLOCAL_VAR))
2678 return NULL;
2679 /* FALL THROUGH */
2681 case JSLOCAL_VAR:
2682 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2683 pn->pn_dflags |= PND_BOUND;
2684 break;
2686 default:;
2692 JSTreeContext *outertc = tc;
2694 /* Initialize early for possible flags mutation via destructuringExpr. */
2695 JSTreeContext funtc(tc->parser);
2697 JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
2698 if (!funbox)
2699 return NULL;
2701 JSFunction *fun = (JSFunction *) funbox->object;
2703 if (op != JSOP_NOP)
2704 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2706 /* Now parse formal argument list and compute fun->nargs. */
2707 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2708 if (!tokenStream.matchToken(TOK_RP)) {
2709 do {
2710 tt = tokenStream.getToken();
2711 switch (tt) {
2712 #if JS_HAS_DESTRUCTURING
2713 case TOK_LB:
2714 case TOK_LC:
2716 BindData data;
2717 JSParseNode *lhs, *rhs;
2718 jsint slot;
2720 /* See comment below in the TOK_NAME case. */
2721 if (duplicatedArg)
2722 goto report_dup_and_destructuring;
2723 destructuringArg = true;
2726 * A destructuring formal parameter turns into one or more
2727 * local variables initialized from properties of a single
2728 * anonymous positional parameter, so here we must tweak our
2729 * binder and its data.
2731 data.pn = NULL;
2732 data.op = JSOP_DEFVAR;
2733 data.binder = BindDestructuringArg;
2734 lhs = destructuringExpr(&data, tt);
2735 if (!lhs)
2736 return NULL;
2739 * Adjust fun->nargs to count the single anonymous positional
2740 * parameter that is to be destructured.
2742 slot = fun->nargs;
2743 if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
2744 return NULL;
2747 * Synthesize a destructuring assignment from the single
2748 * anonymous positional parameter into the destructuring
2749 * left-hand-side expression and accumulate it in list.
2751 rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
2752 if (!rhs)
2753 return NULL;
2754 rhs->pn_type = TOK_NAME;
2755 rhs->pn_op = JSOP_GETARG;
2756 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2757 rhs->pn_dflags |= PND_BOUND;
2759 item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2760 if (!item)
2761 return NULL;
2762 if (!list) {
2763 list = ListNode::create(&funtc);
2764 if (!list)
2765 return NULL;
2766 list->pn_type = TOK_COMMA;
2767 list->makeEmpty();
2769 list->append(item);
2770 break;
2772 #endif /* JS_HAS_DESTRUCTURING */
2774 case TOK_NAME:
2776 JSAtom *atom = tokenStream.currentToken().t_atom;
2777 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2778 return NULL;
2779 #ifdef JS_HAS_DESTRUCTURING
2781 * ECMA-262 requires us to support duplicate parameter names, but if the
2782 * parameter list includes destructuring, we consider the code to have
2783 * opted in to higher standards, and forbid duplicates. We may see a
2784 * destructuring parameter later, so always note duplicates now.
2786 * Duplicates are warned about (strict option) or cause errors (strict
2787 * mode code), but we do those tests in one place below, after having
2788 * parsed the body.
2790 if (js_LookupLocal(context, fun, atom, NULL) != JSLOCAL_NONE) {
2791 duplicatedArg = atom;
2792 if (destructuringArg)
2793 goto report_dup_and_destructuring;
2795 #endif
2796 if (!js_AddLocal(context, fun, atom, JSLOCAL_ARG))
2797 return NULL;
2798 break;
2801 default:
2802 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
2803 /* FALL THROUGH */
2804 case TOK_ERROR:
2805 return NULL;
2807 #if JS_HAS_DESTRUCTURING
2808 report_dup_and_destructuring:
2809 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
2810 reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
2811 return NULL;
2812 #endif
2814 } while (tokenStream.matchToken(TOK_COMMA));
2816 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2819 #if JS_HAS_EXPR_CLOSURES
2820 tt = tokenStream.getToken(TSF_OPERAND);
2821 if (tt != TOK_LC) {
2822 tokenStream.ungetToken();
2823 fun->flags |= JSFUN_EXPR_CLOSURE;
2825 #else
2826 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2827 #endif
2829 body = functionBody();
2830 if (!body)
2831 return NULL;
2833 if (!CheckStrictBinding(context, &funtc, funAtom, pn))
2834 return NULL;
2836 if (!CheckStrictFormals(context, &funtc, fun, pn))
2837 return NULL;
2839 #if JS_HAS_EXPR_CLOSURES
2840 if (tt == TOK_LC)
2841 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2842 else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
2843 return NULL;
2844 #else
2845 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2846 #endif
2847 pn->pn_pos.end = tokenStream.currentToken().pos.end;
2849 #if JS_HAS_DESTRUCTURING
2851 * If there were destructuring formal parameters, prepend the initializing
2852 * comma expression that we synthesized to body. If the body is a lexical
2853 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2854 * parameter destructuring code without bracing the decompilation of the
2855 * function body's lexical scope.
2857 if (list) {
2858 if (body->pn_arity != PN_LIST) {
2859 JSParseNode *block;
2861 block = ListNode::create(outertc);
2862 if (!block)
2863 return NULL;
2864 block->pn_type = TOK_SEQ;
2865 block->pn_pos = body->pn_pos;
2866 block->initList(body);
2868 body = block;
2871 item = UnaryNode::create(outertc);
2872 if (!item)
2873 return NULL;
2875 item->pn_type = TOK_SEMI;
2876 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2877 item->pn_kid = list;
2878 item->pn_next = body->pn_head;
2879 body->pn_head = item;
2880 if (body->pn_tail == &body->pn_head)
2881 body->pn_tail = &item->pn_next;
2882 ++body->pn_count;
2883 body->pn_xflags |= PNX_DESTRUCT;
2885 #endif
2888 * If we collected flags that indicate nested heavyweight functions, or
2889 * this function contains heavyweight-making statements (with statement,
2890 * visible eval call, or assignment to 'arguments'), flag the function as
2891 * heavyweight (requiring a call object per invocation).
2893 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2894 fun->flags |= JSFUN_HEAVYWEIGHT;
2895 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2896 } else {
2898 * If this function is a named statement function not at top-level
2899 * (i.e. not a top-level function definiton or expression), then our
2900 * enclosing function, if any, must be heavyweight.
2902 if (!topLevel && lambda == 0 && funAtom)
2903 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2906 result = pn;
2907 if (lambda != 0) {
2909 * ECMA ed. 3 standard: function expression, possibly anonymous.
2911 op = JSOP_LAMBDA;
2912 } else if (!funAtom) {
2914 * If this anonymous function definition is *not* embedded within a
2915 * larger expression, we treat it as an expression statement, not as
2916 * a function declaration -- and not as a syntax error (as ECMA-262
2917 * Edition 3 would have it). Backward compatibility must trump all,
2918 * unless JSOPTION_ANONFUNFIX is set.
2920 result = UnaryNode::create(outertc);
2921 if (!result)
2922 return NULL;
2923 result->pn_type = TOK_SEMI;
2924 result->pn_pos = pn->pn_pos;
2925 result->pn_kid = pn;
2926 op = JSOP_LAMBDA;
2927 } else if (!topLevel) {
2929 * ECMA ed. 3 extension: a function expression statement not at the
2930 * top level, e.g., in a compound statement such as the "then" part
2931 * of an "if" statement, binds a closure only if control reaches that
2932 * sub-statement.
2934 op = JSOP_DEFFUN;
2935 } else {
2936 op = JSOP_NOP;
2939 funbox->kids = funtc.functionList;
2941 pn->pn_funbox = funbox;
2942 pn->pn_op = op;
2943 if (pn->pn_body) {
2944 pn->pn_body->append(body);
2945 pn->pn_body->pn_pos = body->pn_pos;
2946 } else {
2947 pn->pn_body = body;
2950 pn->pn_blockid = outertc->blockid();
2952 if (!LeaveFunction(pn, &funtc, funAtom, lambda))
2953 return NULL;
2955 /* If the surrounding function is not strict code, reset the lexer. */
2956 if (!(outertc->flags & TCF_STRICT_MODE_CODE))
2957 tokenStream.setStrictMode(false);
2959 return result;
2962 JSParseNode *
2963 Parser::functionStmt()
2965 return functionDef(0, true);
2968 JSParseNode *
2969 Parser::functionExpr()
2971 return functionDef(JSFUN_LAMBDA, true);
2975 * Recognize Directive Prologue members and directives. Assuming pn
2976 * is a candidate for membership in a directive prologue, return
2977 * true if it is in fact a member. Recognize directives and set
2978 * tc's flags accordingly.
2980 * Note that the following is a strict mode function:
2982 * function foo() {
2983 * "blah" // inserted semi colon
2984 * "blurgh"
2985 * "use\x20loose"
2986 * "use strict"
2989 * That is, a statement can be a Directive Prologue member, even
2990 * if it can't possibly be a directive, now or in the future.
2992 bool
2993 Parser::recognizeDirectivePrologue(JSParseNode *pn)
2995 if (!pn->isDirectivePrologueMember())
2996 return false;
2997 if (pn->isDirective()) {
2998 JSAtom *directive = pn->pn_kid->pn_atom;
2999 if (directive == context->runtime->atomState.useStrictAtom) {
3000 tc->flags |= TCF_STRICT_MODE_CODE;
3001 tokenStream.setStrictMode();
3004 return true;
3008 * Parse the statements in a block, creating a TOK_LC node that lists the
3009 * statements' trees. If called from block-parsing code, the caller must
3010 * match { before and } after.
3012 JSParseNode *
3013 Parser::statements()
3015 JSParseNode *pn, *pn2, *saveBlock;
3016 TokenKind tt;
3017 bool inDirectivePrologue = tc->atTopLevel();
3019 JS_CHECK_RECURSION(context, return NULL);
3021 pn = ListNode::create(tc);
3022 if (!pn)
3023 return NULL;
3024 pn->pn_type = TOK_LC;
3025 pn->makeEmpty();
3026 pn->pn_blockid = tc->blockid();
3027 saveBlock = tc->blockNode;
3028 tc->blockNode = pn;
3030 for (;;) {
3031 tt = tokenStream.peekToken(TSF_OPERAND);
3032 if (tt <= TOK_EOF || tt == TOK_RC) {
3033 if (tt == TOK_ERROR) {
3034 if (tokenStream.isEOF())
3035 tokenStream.setUnexpectedEOF();
3036 return NULL;
3038 break;
3040 pn2 = statement();
3041 if (!pn2) {
3042 if (tokenStream.isEOF())
3043 tokenStream.setUnexpectedEOF();
3044 return NULL;
3047 if (inDirectivePrologue)
3048 inDirectivePrologue = recognizeDirectivePrologue(pn2);
3050 if (pn2->pn_type == TOK_FUNCTION) {
3052 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3053 * level function definitions that should be processed before the
3054 * rest of nodes.
3056 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3057 * is relevant only for function definitions not at top-level,
3058 * which we call function statements.
3060 if (tc->atTopLevel())
3061 pn->pn_xflags |= PNX_FUNCDEFS;
3062 else
3063 tc->flags |= TCF_HAS_FUNCTION_STMT;
3065 pn->append(pn2);
3069 * Handle the case where there was a let declaration under this block. If
3070 * it replaced tc->blockNode with a new block node then we must refresh pn
3071 * and then restore tc->blockNode.
3073 if (tc->blockNode != pn)
3074 pn = tc->blockNode;
3075 tc->blockNode = saveBlock;
3077 pn->pn_pos.end = tokenStream.currentToken().pos.end;
3078 return pn;
3081 JSParseNode *
3082 Parser::condition()
3084 JSParseNode *pn;
3086 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3087 pn = parenExpr(NULL, NULL);
3088 if (!pn)
3089 return NULL;
3090 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3092 /* Check for (a = b) and warn about possible (a == b) mistype. */
3093 if (pn->pn_type == TOK_ASSIGN &&
3094 pn->pn_op == JSOP_NOP &&
3095 !pn->pn_parens &&
3096 !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, "")) {
3097 return NULL;
3099 return pn;
3102 static JSBool
3103 MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
3105 JSAtom *label;
3106 TokenKind tt;
3108 tt = ts->peekTokenSameLine();
3109 if (tt == TOK_ERROR)
3110 return JS_FALSE;
3111 if (tt == TOK_NAME) {
3112 (void) ts->getToken();
3113 label = ts->currentToken().t_atom;
3114 } else {
3115 label = NULL;
3117 pn->pn_atom = label;
3118 return JS_TRUE;
3121 static JSBool
3122 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3124 JSParseNode *pn;
3125 JSObject *blockObj;
3126 JSAtomListElement *ale;
3127 jsint n;
3130 * Top-level 'let' is the same as 'var' currently -- this may change in a
3131 * successor standard to ES3.1 that specifies 'let'.
3133 JS_ASSERT(!tc->atTopLevel());
3135 pn = data->pn;
3136 if (!CheckStrictBinding(cx, tc, atom, pn))
3137 return false;
3139 blockObj = tc->blockChain;
3140 ale = tc->decls.lookup(atom);
3141 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3142 const char *name = js_AtomToPrintableString(cx, atom);
3143 if (name) {
3144 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3145 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3146 (ale && ALE_DEFN(ale)->isConst())
3147 ? js_const_str
3148 : js_variable_str,
3149 name);
3151 return false;
3154 n = OBJ_BLOCK_COUNT(cx, blockObj);
3155 if (n == JS_BIT(16)) {
3156 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3157 JSREPORT_ERROR, data->let.overflow);
3158 return false;
3162 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3163 * This is balanced by PopStatement, defined immediately below.
3165 if (!Define(pn, atom, tc, true))
3166 return false;
3169 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3170 * upvar cookie whose skip tells the current static level. The emitter will
3171 * adjust the node's slot based on its stack depth model -- and, for global
3172 * and eval code, Compiler::compileScript will adjust the slot again to
3173 * include script->nfixed.
3175 pn->pn_op = JSOP_GETLOCAL;
3176 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3177 pn->pn_dflags |= PND_LET | PND_BOUND;
3180 * Define the let binding's property before storing pn in reserved slot at
3181 * reserved slot index (NB: not slot number) n.
3183 if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
3184 return false;
3187 * Store pn temporarily in what would be reserved slots in a cloned block
3188 * object (once the prototype's final population is known, after all 'let'
3189 * bindings for this block have been parsed). We will free these reserved
3190 * slots in jsemit.cpp:EmitEnterBlock.
3192 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3193 if (slot >= blockObj->numSlots() && !blockObj->growSlots(cx, slot + 1))
3194 return false;
3195 blockObj->scope()->freeslot = slot + 1;
3196 blockObj->setSlot(slot, PRIVATE_TO_JSVAL(pn));
3197 return true;
3200 static void
3201 PopStatement(JSTreeContext *tc)
3203 JSStmtInfo *stmt = tc->topStmt;
3205 if (stmt->flags & SIF_SCOPE) {
3206 JSObject *obj = stmt->blockObj;
3207 JSScope *scope = obj->scope();
3208 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3210 for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
3211 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3213 /* Beware the empty destructuring dummy. */
3214 if (atom == tc->parser->context->runtime->atomState.emptyAtom)
3215 continue;
3216 tc->decls.remove(tc->parser, atom);
3219 js_PopStatement(tc);
3222 static inline bool
3223 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3225 while (stmt->downScope) {
3226 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3227 if (!stmt)
3228 return false;
3229 if (stmt->type == STMT_BLOCK)
3230 return true;
3232 return false;
3236 * If we are generating global or eval-called-from-global code, bind a "gvar"
3237 * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3238 * global variable access by memoizing name-to-slot mappings during execution
3239 * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3240 * can't be done due to a pre-existing property of the same name as the var or
3241 * const but incompatible attributes/getter/setter/etc, these ops devolve to
3242 * JSOP_NAME, etc.
3244 * For now, don't try to lookup eval frame variables at compile time. This is
3245 * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3246 * gets its own script and frame. The eval-from-function-code case is harder,
3247 * since functions do not atomize gvars and then reserve their atom indexes as
3248 * stack frame slots.
3250 static bool
3251 BindGvar(JSParseNode *pn, JSTreeContext *tc, bool inWith = false)
3253 JS_ASSERT(pn->pn_op == JSOP_NAME);
3254 JS_ASSERT(!tc->inFunction());
3256 if (tc->compiling() && !tc->parser->callerFrame) {
3257 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3259 /* Index pn->pn_atom so we can map fast global number to name. */
3260 JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
3261 if (!ale)
3262 return false;
3264 /* Defend against cg->ngvars 16-bit overflow. */
3265 uintN slot = ALE_INDEX(ale);
3266 if ((slot + 1) >> 16)
3267 return true;
3269 if ((uint16)(slot + 1) > cg->ngvars)
3270 cg->ngvars = (uint16)(slot + 1);
3272 if (!inWith) {
3273 pn->pn_op = JSOP_GETGVAR;
3274 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3275 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3279 return true;
3282 static JSBool
3283 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3285 JSParseNode *pn = data->pn;
3287 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3288 pn->pn_op = JSOP_NAME;
3290 if (!CheckStrictBinding(cx, tc, atom, pn))
3291 return false;
3293 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3295 if (stmt && stmt->type == STMT_WITH) {
3296 data->fresh = false;
3297 return tc->inFunction() || BindGvar(pn, tc, true);
3300 JSAtomListElement *ale = tc->decls.lookup(atom);
3301 JSOp op = data->op;
3303 if (stmt || ale) {
3304 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3305 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3306 const char *name;
3308 if (dn_kind == JSDefinition::ARG) {
3309 name = js_AtomToPrintableString(cx, atom);
3310 if (!name)
3311 return JS_FALSE;
3313 if (op == JSOP_DEFCONST) {
3314 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3315 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3316 name);
3317 return JS_FALSE;
3319 if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3320 JSREPORT_WARNING | JSREPORT_STRICT,
3321 JSMSG_VAR_HIDES_ARG, name)) {
3322 return JS_FALSE;
3324 } else {
3325 bool error = (op == JSOP_DEFCONST ||
3326 dn_kind == JSDefinition::CONST ||
3327 (dn_kind == JSDefinition::LET &&
3328 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3330 if (JS_HAS_STRICT_OPTION(cx)
3331 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3332 : error) {
3333 name = js_AtomToPrintableString(cx, atom);
3334 if (!name ||
3335 !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3336 !error
3337 ? JSREPORT_WARNING | JSREPORT_STRICT
3338 : JSREPORT_ERROR,
3339 JSMSG_REDECLARED_VAR,
3340 JSDefinition::kindString(dn_kind),
3341 name)) {
3342 return JS_FALSE;
3348 if (!ale) {
3349 if (!Define(pn, atom, tc))
3350 return JS_FALSE;
3351 } else {
3353 * A var declaration never recreates an existing binding, it restates
3354 * it and possibly reinitializes its value. Beware that if pn becomes a
3355 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3356 * const (typically a const would ;-), then pn must be rewritten into a
3357 * TOK_ASSIGN node. See Variables, further below.
3359 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3360 * There the x definition is hoisted but the x = 2 assignment mutates
3361 * the block-local binding of x.
3363 JSDefinition *dn = ALE_DEFN(ale);
3365 data->fresh = false;
3367 if (!pn->pn_used) {
3368 /* Make pnu be a fresh name node that uses dn. */
3369 JSParseNode *pnu = pn;
3371 if (pn->pn_defn) {
3372 pnu = NameNode::create(atom, tc);
3373 if (!pnu)
3374 return JS_FALSE;
3377 LinkUseToDef(pnu, dn, tc);
3378 pnu->pn_op = JSOP_NAME;
3381 while (dn->kind() == JSDefinition::LET) {
3382 do {
3383 ale = ALE_NEXT(ale);
3384 } while (ale && ALE_ATOM(ale) != atom);
3385 if (!ale)
3386 break;
3387 dn = ALE_DEFN(ale);
3390 if (ale) {
3391 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3392 dn->kind() == JSDefinition::CONST);
3393 return JS_TRUE;
3397 * A var or const that is shadowed by one or more let bindings of the
3398 * same name, but that has not been declared until this point, must be
3399 * hoisted above the let bindings.
3401 if (!pn->pn_defn) {
3402 JSHashEntry **hep;
3404 ale = tc->lexdeps.rawLookup(atom, hep);
3405 if (ale) {
3406 pn = ALE_DEFN(ale);
3407 tc->lexdeps.rawRemove(tc->parser, ale, hep);
3408 } else {
3409 JSParseNode *pn2 = NameNode::create(atom, tc);
3410 if (!pn2)
3411 return JS_FALSE;
3413 /* The token stream may be past the location for pn. */
3414 pn2->pn_type = TOK_NAME;
3415 pn2->pn_pos = pn->pn_pos;
3416 pn = pn2;
3418 pn->pn_op = JSOP_NAME;
3421 ale = tc->decls.add(tc->parser, atom, JSAtomList::HOIST);
3422 if (!ale)
3423 return JS_FALSE;
3424 ALE_SET_DEFN(ale, pn);
3425 pn->pn_defn = true;
3426 pn->pn_dflags &= ~PND_PLACEHOLDER;
3429 if (data->op == JSOP_DEFCONST)
3430 pn->pn_dflags |= PND_CONST;
3432 if (!tc->inFunction())
3433 return BindGvar(pn, tc);
3435 if (atom == cx->runtime->atomState.argumentsAtom) {
3436 pn->pn_op = JSOP_ARGUMENTS;
3437 pn->pn_dflags |= PND_BOUND;
3438 return JS_TRUE;
3441 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3442 if (localKind == JSLOCAL_NONE) {
3444 * Property not found in current variable scope: we have not seen this
3445 * variable before. Define a new local variable by adding a property to
3446 * the function's scope and allocating one slot in the function's vars
3447 * frame. Any locals declared in a with statement body are handled at
3448 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3449 * and heavyweight-function-local vars.
3451 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3453 uintN index = tc->fun->u.i.nvars;
3454 if (!BindLocalVariable(cx, tc->fun, atom, localKind, false))
3455 return JS_FALSE;
3456 pn->pn_op = JSOP_GETLOCAL;
3457 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3458 pn->pn_dflags |= PND_BOUND;
3459 return JS_TRUE;
3462 if (localKind == JSLOCAL_ARG) {
3463 /* We checked errors and strict warnings earlier -- see above. */
3464 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3465 } else {
3466 /* Not an argument, must be a redeclared local var. */
3467 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3469 return JS_TRUE;
3472 static JSBool
3473 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3475 JSParseNode *pn2;
3477 JS_ASSERT(pn->pn_arity == PN_LIST);
3478 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3479 pn2 = pn->pn_head;
3480 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3481 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
3482 return JS_FALSE;
3484 pn->pn_op = JSOP_SETCALL;
3485 return JS_TRUE;
3488 static void
3489 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3491 if (pn->pn_used) {
3492 JSDefinition *dn = pn->pn_lexdef;
3495 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3496 * occur as direct kids of the same block with no forward refs to x.
3498 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3499 dn->isBlockChild() &&
3500 pn->isBlockChild() &&
3501 dn->pn_blockid == pn->pn_blockid &&
3502 dn->pn_pos.end <= pn->pn_pos.begin &&
3503 dn->dn_uses == pn) {
3504 dflag = PND_INITIALIZED;
3507 dn->pn_dflags |= dflag;
3509 if (dn->frameLevel() != tc->staticLevel) {
3511 * The above condition takes advantage of the all-ones nature of
3512 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3513 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3515 JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3516 dn->frameLevel() < tc->staticLevel);
3517 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3521 pn->pn_dflags |= dflag;
3523 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3524 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3527 #if JS_HAS_DESTRUCTURING
3529 static JSBool
3530 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3531 JSTreeContext *tc)
3533 JSAtom *atom;
3536 * Destructuring is a form of assignment, so just as for an initialized
3537 * simple variable, we must check for assignment to 'arguments' and flag
3538 * the enclosing function (if any) as heavyweight.
3540 JS_ASSERT(pn->pn_type == TOK_NAME);
3541 atom = pn->pn_atom;
3542 if (atom == cx->runtime->atomState.argumentsAtom)
3543 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3545 data->pn = pn;
3546 if (!data->binder(cx, data, atom, tc))
3547 return JS_FALSE;
3550 * Select the appropriate name-setting opcode, respecting eager selection
3551 * done by the data->binder function.
3553 if (pn->pn_dflags & PND_BOUND) {
3554 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3555 ? JSOP_SETNAME
3556 : (pn->pn_dflags & PND_GVAR)
3557 ? JSOP_SETGVAR
3558 : JSOP_SETLOCAL;
3559 } else {
3560 pn->pn_op = (data->op == JSOP_DEFCONST)
3561 ? JSOP_SETCONST
3562 : JSOP_SETNAME;
3565 if (data->op == JSOP_DEFCONST)
3566 pn->pn_dflags |= PND_CONST;
3568 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3569 return JS_TRUE;
3573 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3574 * LHS expression except a destructuring initialiser, and R is on the stack.
3575 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3576 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3577 * then push its property name QN. At this point the stack looks like
3579 * [... R, R[P], QB, QN]
3581 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3582 * its operands with left-hand side above right-hand side:
3584 * [rval, lval, xval]
3586 * and pops all three values, setting lval[xval] = rval. But we cannot select
3587 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3588 * which can be optimized further. So we select JSOP_SETNAME.
3590 static JSBool
3591 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3593 switch (pn->pn_type) {
3594 case TOK_NAME:
3595 NoteLValue(cx, pn, tc);
3596 /* FALL THROUGH */
3598 case TOK_DOT:
3599 case TOK_LB:
3601 * We may be called on a name node that has already been specialized,
3602 * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
3603 * case. See bug 558633.
3605 if (!(js_CodeSpec[pn->pn_op].format & JOF_SET))
3606 pn->pn_op = JSOP_SETNAME;
3607 break;
3609 case TOK_LP:
3610 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3611 return JS_FALSE;
3612 break;
3614 #if JS_HAS_XML_SUPPORT
3615 case TOK_UNARYOP:
3616 if (pn->pn_op == JSOP_XMLNAME) {
3617 pn->pn_op = JSOP_BINDXMLNAME;
3618 break;
3620 /* FALL THROUGH */
3621 #endif
3623 default:
3624 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3625 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3626 return JS_FALSE;
3629 return JS_TRUE;
3632 typedef struct FindPropValData {
3633 uint32 numvars; /* # of destructuring vars in left side */
3634 uint32 maxstep; /* max # of steps searching right side */
3635 JSDHashTable table; /* hash table for O(1) right side search */
3636 } FindPropValData;
3638 typedef struct FindPropValEntry {
3639 JSDHashEntryHdr hdr;
3640 JSParseNode *pnkey;
3641 JSParseNode *pnval;
3642 } FindPropValEntry;
3644 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3645 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3646 ((pnkey)->pn_type == TOK_NUMBER || \
3647 (pnkey)->pn_type == TOK_STRING || \
3648 (pnkey)->pn_type == TOK_NAME)) || \
3649 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3651 static JSDHashNumber
3652 HashFindPropValKey(JSDHashTable *table, const void *key)
3654 const JSParseNode *pnkey = (const JSParseNode *)key;
3656 ASSERT_VALID_PROPERTY_KEY(pnkey);
3657 return (pnkey->pn_type == TOK_NUMBER)
3658 ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
3659 : ATOM_HASH(pnkey->pn_atom);
3662 static JSBool
3663 MatchFindPropValEntry(JSDHashTable *table,
3664 const JSDHashEntryHdr *entry,
3665 const void *key)
3667 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3668 const JSParseNode *pnkey = (const JSParseNode *)key;
3670 ASSERT_VALID_PROPERTY_KEY(pnkey);
3671 return pnkey->pn_type == fpve->pnkey->pn_type &&
3672 ((pnkey->pn_type == TOK_NUMBER)
3673 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3674 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3677 static const JSDHashTableOps FindPropValOps = {
3678 JS_DHashAllocTable,
3679 JS_DHashFreeTable,
3680 HashFindPropValKey,
3681 MatchFindPropValEntry,
3682 JS_DHashMoveEntryStub,
3683 JS_DHashClearEntryStub,
3684 JS_DHashFinalizeStub,
3685 NULL
3688 #define STEP_HASH_THRESHOLD 10
3689 #define BIG_DESTRUCTURING 5
3690 #define BIG_OBJECT_INIT 20
3692 static JSParseNode *
3693 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3695 FindPropValEntry *entry;
3696 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3697 uint32 step;
3699 /* If we have a hash table, use it as the sole source of truth. */
3700 if (data->table.ops) {
3701 entry = (FindPropValEntry *)
3702 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3703 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3706 /* If pn is not an object initialiser node, we can't do anything here. */
3707 if (pn->pn_type != TOK_RC)
3708 return NULL;
3711 * We must search all the way through pn's list, to handle the case of an
3712 * id duplicated for two or more property initialisers.
3714 pnhit = NULL;
3715 step = 0;
3716 ASSERT_VALID_PROPERTY_KEY(pnid);
3717 pnhead = pn->pn_head;
3718 if (pnid->pn_type == TOK_NUMBER) {
3719 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3720 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3721 if (pnprop->pn_op == JSOP_NOP) {
3722 pnkey = pnprop->pn_left;
3723 ASSERT_VALID_PROPERTY_KEY(pnkey);
3724 if (pnkey->pn_type == TOK_NUMBER &&
3725 pnkey->pn_dval == pnid->pn_dval) {
3726 pnhit = pnprop;
3728 ++step;
3731 } else {
3732 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3733 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3734 if (pnprop->pn_op == JSOP_NOP) {
3735 pnkey = pnprop->pn_left;
3736 ASSERT_VALID_PROPERTY_KEY(pnkey);
3737 if (pnkey->pn_type == pnid->pn_type &&
3738 pnkey->pn_atom == pnid->pn_atom) {
3739 pnhit = pnprop;
3741 ++step;
3745 if (!pnhit)
3746 return NULL;
3748 /* Hit via full search -- see whether it's time to create the hash table. */
3749 JS_ASSERT(!data->table.ops);
3750 if (step > data->maxstep) {
3751 data->maxstep = step;
3752 if (step >= STEP_HASH_THRESHOLD &&
3753 data->numvars >= BIG_DESTRUCTURING &&
3754 pn->pn_count >= BIG_OBJECT_INIT &&
3755 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3756 sizeof(FindPropValEntry),
3757 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3759 for (pn = pnhead; pn; pn = pn->pn_next) {
3760 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3761 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3762 entry = (FindPropValEntry *)
3763 JS_DHashTableOperate(&data->table, pn->pn_left,
3764 JS_DHASH_ADD);
3765 entry->pnval = pn->pn_right;
3769 return pnhit->pn_right;
3773 * Destructuring patterns can appear in two kinds of contexts:
3775 * - assignment-like: assignment expressions and |for| loop heads. In
3776 * these cases, the patterns' property value positions can be
3777 * arbitrary lvalue expressions; the destructuring is just a fancy
3778 * assignment.
3780 * - declaration-like: |var| and |let| declarations, functions' formal
3781 * parameter lists, |catch| clauses, and comprehension tails. In
3782 * these cases, the patterns' property value positions must be
3783 * simple names; the destructuring defines them as new variables.
3785 * In both cases, other code parses the pattern as an arbitrary
3786 * primaryExpr, and then, here in CheckDestructuring, verify that the
3787 * tree is a valid destructuring expression.
3789 * In assignment-like contexts, we parse the pattern with the
3790 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3791 * pattern are parsed normally. primaryExpr links variable references
3792 * into the appropriate use chains; creates placeholder definitions;
3793 * and so on. CheckDestructuring is called with |data| NULL (since we
3794 * won't be binding any new names), and we specialize lvalues as
3795 * appropriate. If right is NULL, we just check for well-formed lvalues.
3797 * In declaration-like contexts, the normal variable reference
3798 * processing would just be an obstruction, because we're going to
3799 * define the names that appear in the property value positions as new
3800 * variables anyway. In this case, we parse the pattern with
3801 * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
3802 * whatever name nodes it creates unconnected. Then, here in
3803 * CheckDestructuring, we require the pattern's property value
3804 * positions to be simple names, and define them as appropriate to the
3805 * context. For these calls, |data| points to the right sort of
3806 * BindData.
3808 * See also UndominateInitializers, immediately below. If you change
3809 * either of these functions, you might have to change the other to
3810 * match.
3812 static JSBool
3813 CheckDestructuring(JSContext *cx, BindData *data,
3814 JSParseNode *left, JSParseNode *right,
3815 JSTreeContext *tc)
3817 JSBool ok;
3818 FindPropValData fpvd;
3819 JSParseNode *lhs, *rhs, *pn, *pn2;
3821 if (left->pn_type == TOK_ARRAYCOMP) {
3822 ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
3823 JSMSG_ARRAY_COMP_LEFTSIDE);
3824 return JS_FALSE;
3827 #if JS_HAS_DESTRUCTURING_SHORTHAND
3828 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3829 ReportCompileErrorNumber(cx, TS(tc->parser), right, JSREPORT_ERROR,
3830 JSMSG_BAD_OBJECT_INIT);
3831 return JS_FALSE;
3833 #endif
3835 fpvd.table.ops = NULL;
3836 lhs = left->pn_head;
3837 if (left->pn_type == TOK_RB) {
3838 rhs = (right && right->pn_type == left->pn_type)
3839 ? right->pn_head
3840 : NULL;
3842 while (lhs) {
3843 pn = lhs, pn2 = rhs;
3845 /* Nullary comma is an elision; binary comma is an expression.*/
3846 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3847 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3848 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3849 } else {
3850 if (data) {
3851 if (pn->pn_type != TOK_NAME)
3852 goto no_var_name;
3854 ok = BindDestructuringVar(cx, data, pn, tc);
3855 } else {
3856 ok = BindDestructuringLHS(cx, pn, tc);
3859 if (!ok)
3860 goto out;
3863 lhs = lhs->pn_next;
3864 if (rhs)
3865 rhs = rhs->pn_next;
3867 } else {
3868 JS_ASSERT(left->pn_type == TOK_RC);
3869 fpvd.numvars = left->pn_count;
3870 fpvd.maxstep = 0;
3871 rhs = NULL;
3873 while (lhs) {
3874 JS_ASSERT(lhs->pn_type == TOK_COLON);
3875 pn = lhs->pn_right;
3877 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3878 if (right)
3879 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3880 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3881 } else if (data) {
3882 if (pn->pn_type != TOK_NAME)
3883 goto no_var_name;
3885 ok = BindDestructuringVar(cx, data, pn, tc);
3886 } else {
3887 ok = BindDestructuringLHS(cx, pn, tc);
3889 if (!ok)
3890 goto out;
3892 lhs = lhs->pn_next;
3897 * The catch/finally handler implementation in the interpreter assumes
3898 * that any operation that introduces a new scope (like a "let" or "with"
3899 * block) increases the stack depth. This way, it is possible to restore
3900 * the scope chain based on stack depth of the handler alone. "let" with
3901 * an empty destructuring pattern like in
3903 * let [] = 1;
3905 * would violate this assumption as the there would be no let locals to
3906 * store on the stack. To satisfy it we add an empty property to such
3907 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3908 * slots, would be always positive.
3910 * Note that we add such a property even if the block has locals due to
3911 * later let declarations in it. We optimize for code simplicity here,
3912 * not the fastest runtime performance with empty [] or {}.
3914 if (data &&
3915 data->binder == BindLet &&
3916 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3917 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3918 ATOM_TO_JSID(cx->runtime->
3919 atomState.emptyAtom),
3920 JSVAL_VOID, NULL, NULL,
3921 JSPROP_ENUMERATE |
3922 JSPROP_PERMANENT |
3923 JSPROP_SHARED,
3924 JSScopeProperty::HAS_SHORTID, 0, NULL);
3925 if (!ok)
3926 goto out;
3929 ok = JS_TRUE;
3931 out:
3932 if (fpvd.table.ops)
3933 JS_DHashTableFinish(&fpvd.table);
3934 return ok;
3936 no_var_name:
3937 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
3938 JSMSG_NO_VARIABLE_NAME);
3939 ok = JS_FALSE;
3940 goto out;
3944 * This is a greatly pared down version of CheckDestructuring that extends the
3945 * pn_pos.end source coordinate of each name in a destructuring binding such as
3947 * var [x, y] = [function () y, 42];
3949 * to cover its corresponding initializer, so that the initialized binding does
3950 * not appear to dominate any closures in its initializer. See bug 496134.
3952 * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
3953 * very precise. With one-pass SSA construction from structured source code
3954 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3955 * Languages", Brandis and Mössenböck), we could do much better.
3957 * See CheckDestructuring, immediately above. If you change either of these
3958 * functions, you might have to change the other to match.
3960 static JSBool
3961 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3963 FindPropValData fpvd;
3964 JSParseNode *lhs, *rhs;
3966 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3967 JS_ASSERT(right);
3969 #if JS_HAS_DESTRUCTURING_SHORTHAND
3970 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3971 ReportCompileErrorNumber(tc->parser->context, TS(tc->parser), right, JSREPORT_ERROR,
3972 JSMSG_BAD_OBJECT_INIT);
3973 return JS_FALSE;
3975 #endif
3977 if (right->pn_type != left->pn_type)
3978 return JS_TRUE;
3980 fpvd.table.ops = NULL;
3981 lhs = left->pn_head;
3982 if (left->pn_type == TOK_RB) {
3983 rhs = right->pn_head;
3985 while (lhs && rhs) {
3986 /* Nullary comma is an elision; binary comma is an expression.*/
3987 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3988 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3989 if (!UndominateInitializers(lhs, rhs, tc))
3990 return JS_FALSE;
3991 } else {
3992 lhs->pn_pos.end = rhs->pn_pos.end;
3996 lhs = lhs->pn_next;
3997 rhs = rhs->pn_next;
3999 } else {
4000 JS_ASSERT(left->pn_type == TOK_RC);
4001 fpvd.numvars = left->pn_count;
4002 fpvd.maxstep = 0;
4004 while (lhs) {
4005 JS_ASSERT(lhs->pn_type == TOK_COLON);
4006 JSParseNode *pn = lhs->pn_right;
4008 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4009 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4010 if (rhs && !UndominateInitializers(pn, rhs, tc))
4011 return JS_FALSE;
4012 } else {
4013 if (rhs)
4014 pn->pn_pos.end = rhs->pn_pos.end;
4017 lhs = lhs->pn_next;
4020 return JS_TRUE;
4023 JSParseNode *
4024 Parser::destructuringExpr(BindData *data, TokenKind tt)
4026 JSParseNode *pn;
4028 tc->flags |= TCF_DECL_DESTRUCTURING;
4029 pn = primaryExpr(tt, JS_FALSE);
4030 tc->flags &= ~TCF_DECL_DESTRUCTURING;
4031 if (!pn)
4032 return NULL;
4033 if (!CheckDestructuring(context, data, pn, NULL, tc))
4034 return NULL;
4035 return pn;
4039 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4040 * This function assumes the cloned tree is for use in the same statement and
4041 * binding context as the original tree.
4043 static JSParseNode *
4044 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4046 JSParseNode *pn, *pn2, *opn2;
4048 pn = NewOrRecycledNode(tc);
4049 if (!pn)
4050 return NULL;
4051 pn->pn_type = opn->pn_type;
4052 pn->pn_pos = opn->pn_pos;
4053 pn->pn_op = opn->pn_op;
4054 pn->pn_used = opn->pn_used;
4055 pn->pn_defn = opn->pn_defn;
4056 pn->pn_arity = opn->pn_arity;
4057 pn->pn_parens = opn->pn_parens;
4059 switch (pn->pn_arity) {
4060 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4062 case PN_FUNC:
4063 NULLCHECK(pn->pn_funbox =
4064 tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
4065 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4066 pn->pn_cookie = opn->pn_cookie;
4067 pn->pn_dflags = opn->pn_dflags;
4068 pn->pn_blockid = opn->pn_blockid;
4069 break;
4071 case PN_LIST:
4072 pn->makeEmpty();
4073 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4074 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4075 pn->append(pn2);
4077 pn->pn_xflags = opn->pn_xflags;
4078 break;
4080 case PN_TERNARY:
4081 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4082 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4083 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4084 break;
4086 case PN_BINARY:
4087 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4088 if (opn->pn_right != opn->pn_left)
4089 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4090 else
4091 pn->pn_right = pn->pn_left;
4092 pn->pn_val = opn->pn_val;
4093 pn->pn_iflags = opn->pn_iflags;
4094 break;
4096 case PN_UNARY:
4097 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4098 pn->pn_num = opn->pn_num;
4099 pn->pn_hidden = opn->pn_hidden;
4100 break;
4102 case PN_NAME:
4103 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4104 pn->pn_u = opn->pn_u;
4105 if (opn->pn_used) {
4107 * The old name is a use of its pn_lexdef. Make the clone also be a
4108 * use of that definition.
4110 JSDefinition *dn = pn->pn_lexdef;
4112 pn->pn_link = dn->dn_uses;
4113 dn->dn_uses = pn;
4114 } else if (opn->pn_expr) {
4115 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4118 * If the old name is a definition, the new one has pn_defn set.
4119 * Make the old name a use of the new node.
4121 if (opn->pn_defn) {
4122 opn->pn_defn = false;
4123 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4126 break;
4128 case PN_NAMESET:
4129 pn->pn_names = opn->pn_names;
4130 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4131 break;
4133 case PN_NULLARY:
4134 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4135 pn->pn_u = opn->pn_u;
4136 break;
4138 #undef NULLCHECK
4140 return pn;
4143 #endif /* JS_HAS_DESTRUCTURING */
4145 extern const char js_with_statement_str[];
4147 static JSParseNode *
4148 ContainsStmt(JSParseNode *pn, TokenKind tt)
4150 JSParseNode *pn2, *pnt;
4152 if (!pn)
4153 return NULL;
4154 if (PN_TYPE(pn) == tt)
4155 return pn;
4156 switch (pn->pn_arity) {
4157 case PN_LIST:
4158 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4159 pnt = ContainsStmt(pn2, tt);
4160 if (pnt)
4161 return pnt;
4163 break;
4164 case PN_TERNARY:
4165 pnt = ContainsStmt(pn->pn_kid1, tt);
4166 if (pnt)
4167 return pnt;
4168 pnt = ContainsStmt(pn->pn_kid2, tt);
4169 if (pnt)
4170 return pnt;
4171 return ContainsStmt(pn->pn_kid3, tt);
4172 case PN_BINARY:
4174 * Limit recursion if pn is a binary expression, which can't contain a
4175 * var statement.
4177 if (pn->pn_op != JSOP_NOP)
4178 return NULL;
4179 pnt = ContainsStmt(pn->pn_left, tt);
4180 if (pnt)
4181 return pnt;
4182 return ContainsStmt(pn->pn_right, tt);
4183 case PN_UNARY:
4184 if (pn->pn_op != JSOP_NOP)
4185 return NULL;
4186 return ContainsStmt(pn->pn_kid, tt);
4187 case PN_NAME:
4188 return ContainsStmt(pn->maybeExpr(), tt);
4189 case PN_NAMESET:
4190 return ContainsStmt(pn->pn_tree, tt);
4191 default:;
4193 return NULL;
4196 JSParseNode *
4197 Parser::returnOrYield(bool useAssignExpr)
4199 TokenKind tt, tt2;
4200 JSParseNode *pn, *pn2;
4202 tt = tokenStream.currentToken().type;
4203 if (tt == TOK_RETURN && !tc->inFunction()) {
4204 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4205 return NULL;
4208 pn = UnaryNode::create(tc);
4209 if (!pn)
4210 return NULL;
4212 #if JS_HAS_GENERATORS
4213 if (tt == TOK_YIELD)
4214 tc->flags |= TCF_FUN_IS_GENERATOR;
4215 #endif
4217 /* This is ugly, but we don't want to require a semicolon. */
4218 tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
4219 if (tt2 == TOK_ERROR)
4220 return NULL;
4222 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4223 #if JS_HAS_GENERATORS
4224 && (tt != TOK_YIELD ||
4225 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4226 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4227 #endif
4229 pn2 = useAssignExpr ? assignExpr() : expr();
4230 if (!pn2)
4231 return NULL;
4232 #if JS_HAS_GENERATORS
4233 if (tt == TOK_RETURN)
4234 #endif
4235 tc->flags |= TCF_RETURN_EXPR;
4236 pn->pn_pos.end = pn2->pn_pos.end;
4237 pn->pn_kid = pn2;
4238 } else {
4239 #if JS_HAS_GENERATORS
4240 if (tt == TOK_RETURN)
4241 #endif
4242 tc->flags |= TCF_RETURN_VOID;
4245 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4246 /* As in Python (see PEP-255), disallow return v; in generators. */
4247 ReportBadReturn(context, tc, JSREPORT_ERROR,
4248 JSMSG_BAD_GENERATOR_RETURN,
4249 JSMSG_BAD_ANON_GENERATOR_RETURN);
4250 return NULL;
4253 if (JS_HAS_STRICT_OPTION(context) &&
4254 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4255 !ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4256 JSMSG_NO_RETURN_VALUE,
4257 JSMSG_ANON_NO_RETURN_VALUE)) {
4258 return NULL;
4261 return pn;
4264 static JSParseNode *
4265 PushLexicalScope(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
4266 JSStmtInfo *stmt)
4268 JSParseNode *pn;
4269 JSObject *obj;
4270 JSObjectBox *blockbox;
4272 pn = LexicalScopeNode::create(tc);
4273 if (!pn)
4274 return NULL;
4276 obj = js_NewBlockObject(cx);
4277 if (!obj)
4278 return NULL;
4280 blockbox = tc->parser->newObjectBox(obj);
4281 if (!blockbox)
4282 return NULL;
4284 js_PushBlockScope(tc, stmt, obj, -1);
4285 pn->pn_type = TOK_LEXICALSCOPE;
4286 pn->pn_op = JSOP_LEAVEBLOCK;
4287 pn->pn_objbox = blockbox;
4288 pn->pn_cookie = FREE_UPVAR_COOKIE;
4289 pn->pn_dflags = 0;
4290 if (!GenerateBlockId(tc, stmt->blockid))
4291 return NULL;
4292 pn->pn_blockid = stmt->blockid;
4293 return pn;
4296 #if JS_HAS_BLOCK_SCOPE
4298 JSParseNode *
4299 Parser::letBlock(JSBool statement)
4301 JSParseNode *pn, *pnblock, *pnlet;
4302 JSStmtInfo stmtInfo;
4304 JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
4306 /* Create the let binary node. */
4307 pnlet = BinaryNode::create(tc);
4308 if (!pnlet)
4309 return NULL;
4311 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4313 /* This is a let block or expression of the form: let (a, b, c) .... */
4314 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
4315 if (!pnblock)
4316 return NULL;
4317 pn = pnblock;
4318 pn->pn_expr = pnlet;
4320 pnlet->pn_left = variables(true);
4321 if (!pnlet->pn_left)
4322 return NULL;
4323 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4325 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4327 if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
4329 * If this is really an expression in let statement guise, then we
4330 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4331 * the return value of the expression.
4333 pn = UnaryNode::create(tc);
4334 if (!pn)
4335 return NULL;
4336 pn->pn_type = TOK_SEMI;
4337 pn->pn_num = -1;
4338 pn->pn_kid = pnblock;
4340 statement = JS_FALSE;
4343 if (statement) {
4344 pnlet->pn_right = statements();
4345 if (!pnlet->pn_right)
4346 return NULL;
4347 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4348 } else {
4350 * Change pnblock's opcode to the variant that propagates the last
4351 * result down after popping the block, and clear statement.
4353 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4354 pnlet->pn_right = assignExpr();
4355 if (!pnlet->pn_right)
4356 return NULL;
4359 PopStatement(tc);
4360 return pn;
4363 #endif /* JS_HAS_BLOCK_SCOPE */
4365 static bool
4366 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4368 js_PushStatement(tc, stmt, type, -1);
4369 return GenerateBlockId(tc, stmt->blockid);
4372 static JSParseNode *
4373 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4375 JSParseNode *pn = NULL;
4377 JSAtomListElement *ale = tc->decls.lookup(atom);
4378 if (ale) {
4379 pn = ALE_DEFN(ale);
4380 JS_ASSERT(!pn->isPlaceholder());
4381 } else {
4382 ale = tc->lexdeps.lookup(atom);
4383 if (ale) {
4384 pn = ALE_DEFN(ale);
4385 JS_ASSERT(pn->isPlaceholder());
4389 if (pn) {
4390 JS_ASSERT(pn->pn_defn);
4393 * A let binding at top level becomes a var before we get here, so if
4394 * pn and tc have the same blockid then that id must not be the bodyid.
4395 * If pn is a forward placeholder definition from the same or a higher
4396 * block then we claim it.
4398 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4399 pn->pn_blockid != tc->bodyid);
4401 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4402 if (let)
4403 pn->pn_blockid = tc->blockid();
4405 tc->lexdeps.remove(tc->parser, atom);
4406 return pn;
4410 /* Make a new node for this declarator name (or destructuring pattern). */
4411 pn = NameNode::create(atom, tc);
4412 if (!pn)
4413 return NULL;
4414 return pn;
4417 #if JS_HAS_BLOCK_SCOPE
4418 static bool
4419 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4421 if (!pn)
4422 return true;
4424 switch (pn->pn_arity) {
4425 case PN_LIST:
4426 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4427 RebindLets(pn2, tc);
4428 break;
4430 case PN_TERNARY:
4431 RebindLets(pn->pn_kid1, tc);
4432 RebindLets(pn->pn_kid2, tc);
4433 RebindLets(pn->pn_kid3, tc);
4434 break;
4436 case PN_BINARY:
4437 RebindLets(pn->pn_left, tc);
4438 RebindLets(pn->pn_right, tc);
4439 break;
4441 case PN_UNARY:
4442 RebindLets(pn->pn_kid, tc);
4443 break;
4445 case PN_FUNC:
4446 RebindLets(pn->pn_body, tc);
4447 break;
4449 case PN_NAME:
4450 RebindLets(pn->maybeExpr(), tc);
4452 if (pn->pn_defn) {
4453 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4454 } else if (pn->pn_used) {
4455 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4456 ForgetUse(pn);
4458 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4459 if (ale) {
4460 while ((ale = ALE_NEXT(ale)) != NULL) {
4461 if (ALE_ATOM(ale) == pn->pn_atom) {
4462 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4463 return true;
4468 ale = tc->lexdeps.lookup(pn->pn_atom);
4469 if (!ale) {
4470 ale = MakePlaceholder(pn, tc);
4471 if (!ale)
4472 return NULL;
4474 JSDefinition *dn = ALE_DEFN(ale);
4475 dn->pn_type = TOK_NAME;
4476 dn->pn_op = JSOP_NOP;
4478 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4481 break;
4483 case PN_NAMESET:
4484 RebindLets(pn->pn_tree, tc);
4485 break;
4488 return true;
4490 #endif /* JS_HAS_BLOCK_SCOPE */
4492 JSParseNode *
4493 Parser::statement()
4495 TokenKind tt;
4496 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4497 JSStmtInfo stmtInfo, *stmt, *stmt2;
4498 JSAtom *label;
4500 JS_CHECK_RECURSION(context, return NULL);
4502 tt = tokenStream.getToken(TSF_OPERAND);
4504 switch (tt) {
4505 case TOK_FUNCTION:
4506 #if JS_HAS_XML_SUPPORT
4507 tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
4508 if (tt == TOK_DBLCOLON)
4509 goto expression;
4510 #endif
4511 return functionStmt();
4513 case TOK_IF:
4514 /* An IF node has three kids: condition, then, and optional else. */
4515 pn = TernaryNode::create(tc);
4516 if (!pn)
4517 return NULL;
4518 pn1 = condition();
4519 if (!pn1)
4520 return NULL;
4521 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4522 pn2 = statement();
4523 if (!pn2)
4524 return NULL;
4525 if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
4526 stmtInfo.type = STMT_ELSE;
4527 pn3 = statement();
4528 if (!pn3)
4529 return NULL;
4530 pn->pn_pos.end = pn3->pn_pos.end;
4531 } else {
4532 pn3 = NULL;
4533 pn->pn_pos.end = pn2->pn_pos.end;
4535 PopStatement(tc);
4536 pn->pn_kid1 = pn1;
4537 pn->pn_kid2 = pn2;
4538 pn->pn_kid3 = pn3;
4539 return pn;
4541 case TOK_SWITCH:
4543 JSParseNode *pn5, *saveBlock;
4544 JSBool seenDefault = JS_FALSE;
4546 pn = BinaryNode::create(tc);
4547 if (!pn)
4548 return NULL;
4549 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4551 /* pn1 points to the switch's discriminant. */
4552 pn1 = parenExpr(NULL, NULL);
4553 if (!pn1)
4554 return NULL;
4556 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4557 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4560 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4561 * because that function states tc->topStmt->blockid.
4563 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4565 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4566 pn2 = ListNode::create(tc);
4567 if (!pn2)
4568 return NULL;
4569 pn2->makeEmpty();
4570 if (!GenerateBlockIdForStmtNode(pn2, tc))
4571 return NULL;
4572 saveBlock = tc->blockNode;
4573 tc->blockNode = pn2;
4575 while ((tt = tokenStream.getToken()) != TOK_RC) {
4576 switch (tt) {
4577 case TOK_DEFAULT:
4578 if (seenDefault) {
4579 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS);
4580 return NULL;
4582 seenDefault = JS_TRUE;
4583 /* FALL THROUGH */
4585 case TOK_CASE:
4586 pn3 = BinaryNode::create(tc);
4587 if (!pn3)
4588 return NULL;
4589 if (tt == TOK_CASE) {
4590 pn3->pn_left = expr();
4591 if (!pn3->pn_left)
4592 return NULL;
4594 pn2->append(pn3);
4595 if (pn2->pn_count == JS_BIT(16)) {
4596 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_CASES);
4597 return NULL;
4599 break;
4601 case TOK_ERROR:
4602 return NULL;
4604 default:
4605 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_SWITCH);
4606 return NULL;
4608 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4610 pn4 = ListNode::create(tc);
4611 if (!pn4)
4612 return NULL;
4613 pn4->pn_type = TOK_LC;
4614 pn4->makeEmpty();
4615 while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
4616 tt != TOK_CASE && tt != TOK_DEFAULT) {
4617 if (tt == TOK_ERROR)
4618 return NULL;
4619 pn5 = statement();
4620 if (!pn5)
4621 return NULL;
4622 pn4->pn_pos.end = pn5->pn_pos.end;
4623 pn4->append(pn5);
4626 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4627 if (pn4->pn_head)
4628 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4629 pn3->pn_pos.end = pn4->pn_pos.end;
4630 pn3->pn_right = pn4;
4634 * Handle the case where there was a let declaration in any case in
4635 * the switch body, but not within an inner block. If it replaced
4636 * tc->blockNode with a new block node then we must refresh pn2 and
4637 * then restore tc->blockNode.
4639 if (tc->blockNode != pn2)
4640 pn2 = tc->blockNode;
4641 tc->blockNode = saveBlock;
4642 PopStatement(tc);
4644 pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
4645 pn->pn_left = pn1;
4646 pn->pn_right = pn2;
4647 return pn;
4650 case TOK_WHILE:
4651 pn = BinaryNode::create(tc);
4652 if (!pn)
4653 return NULL;
4654 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4655 pn2 = condition();
4656 if (!pn2)
4657 return NULL;
4658 pn->pn_left = pn2;
4659 pn2 = statement();
4660 if (!pn2)
4661 return NULL;
4662 PopStatement(tc);
4663 pn->pn_pos.end = pn2->pn_pos.end;
4664 pn->pn_right = pn2;
4665 return pn;
4667 case TOK_DO:
4668 pn = BinaryNode::create(tc);
4669 if (!pn)
4670 return NULL;
4671 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4672 pn2 = statement();
4673 if (!pn2)
4674 return NULL;
4675 pn->pn_left = pn2;
4676 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4677 pn2 = condition();
4678 if (!pn2)
4679 return NULL;
4680 PopStatement(tc);
4681 pn->pn_pos.end = pn2->pn_pos.end;
4682 pn->pn_right = pn2;
4683 if (JSVERSION_NUMBER(context) != JSVERSION_ECMA_3) {
4685 * All legacy and extended versions must do automatic semicolon
4686 * insertion after do-while. See the testcase and discussion in
4687 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4689 (void) tokenStream.matchToken(TOK_SEMI);
4690 return pn;
4692 break;
4694 case TOK_FOR:
4696 JSParseNode *pnseq = NULL;
4697 #if JS_HAS_BLOCK_SCOPE
4698 JSParseNode *pnlet = NULL;
4699 JSStmtInfo blockInfo;
4700 #endif
4702 /* A FOR node is binary, left is loop control and right is the body. */
4703 pn = BinaryNode::create(tc);
4704 if (!pn)
4705 return NULL;
4706 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4708 pn->pn_op = JSOP_ITER;
4709 pn->pn_iflags = 0;
4710 if (tokenStream.matchToken(TOK_NAME)) {
4711 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
4712 pn->pn_iflags = JSITER_FOREACH;
4713 else
4714 tokenStream.ungetToken();
4717 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4718 tt = tokenStream.peekToken(TSF_OPERAND);
4720 #if JS_HAS_BLOCK_SCOPE
4721 bool let = false;
4722 #endif
4724 if (tt == TOK_SEMI) {
4725 if (pn->pn_iflags & JSITER_FOREACH)
4726 goto bad_for_each;
4728 /* No initializer -- set first kid of left sub-node to null. */
4729 pn1 = NULL;
4730 } else {
4732 * Set pn1 to a var list or an initializing expression.
4734 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4735 * of the for statement. This flag will be used by the RelExpr
4736 * production; if it is set, then the 'in' keyword will not be
4737 * recognized as an operator, leaving it available to be parsed as
4738 * part of a for/in loop.
4740 * A side effect of this restriction is that (unparenthesized)
4741 * expressions involving an 'in' operator are illegal in the init
4742 * clause of an ordinary for loop.
4744 tc->flags |= TCF_IN_FOR_INIT;
4745 if (tt == TOK_VAR) {
4746 (void) tokenStream.getToken();
4747 pn1 = variables(false);
4748 #if JS_HAS_BLOCK_SCOPE
4749 } else if (tt == TOK_LET) {
4750 let = true;
4751 (void) tokenStream.getToken();
4752 if (tokenStream.peekToken() == TOK_LP) {
4753 pn1 = letBlock(JS_FALSE);
4754 tt = TOK_LEXICALSCOPE;
4755 } else {
4756 pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
4757 if (!pnlet)
4758 return NULL;
4759 blockInfo.flags |= SIF_FOR_BLOCK;
4760 pn1 = variables(false);
4762 #endif
4763 } else {
4764 pn1 = expr();
4766 tc->flags &= ~TCF_IN_FOR_INIT;
4767 if (!pn1)
4768 return NULL;
4772 * We can be sure that it's a for/in loop if there's still an 'in'
4773 * keyword here, even if JavaScript recognizes 'in' as an operator,
4774 * as we've excluded 'in' from being parsed in RelExpr by setting
4775 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4777 if (pn1 && tokenStream.matchToken(TOK_IN)) {
4778 pn->pn_iflags |= JSITER_ENUMERATE;
4779 stmtInfo.type = STMT_FOR_IN_LOOP;
4781 /* Check that the left side of the 'in' is valid. */
4782 JS_ASSERT(!TokenKindIsDecl(tt) || PN_TYPE(pn1) == tt);
4783 if (TokenKindIsDecl(tt)
4784 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4785 #if JS_HAS_DESTRUCTURING
4786 || (JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4787 pn->pn_op == JSOP_ITER &&
4788 !(pn->pn_iflags & JSITER_FOREACH) &&
4789 (pn1->pn_head->pn_type == TOK_RC ||
4790 (pn1->pn_head->pn_type == TOK_RB &&
4791 pn1->pn_head->pn_count != 2) ||
4792 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4793 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4794 pn1->pn_head->pn_left->pn_count != 2))))
4795 #endif
4797 : (pn1->pn_type != TOK_NAME &&
4798 pn1->pn_type != TOK_DOT &&
4799 #if JS_HAS_DESTRUCTURING
4800 ((JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4801 pn->pn_op == JSOP_ITER &&
4802 !(pn->pn_iflags & JSITER_FOREACH))
4803 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4804 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4805 #endif
4806 pn1->pn_type != TOK_LP &&
4807 #if JS_HAS_XML_SUPPORT
4808 (pn1->pn_type != TOK_UNARYOP ||
4809 pn1->pn_op != JSOP_XMLNAME) &&
4810 #endif
4811 pn1->pn_type != TOK_LB)) {
4812 reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
4813 return NULL;
4816 /* pn2 points to the name or destructuring pattern on in's left. */
4817 pn2 = NULL;
4818 uintN dflag = PND_ASSIGNED;
4820 if (TokenKindIsDecl(tt)) {
4821 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4822 pn1->pn_xflags |= PNX_FORINVAR;
4825 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4826 * 'var', or 'const' to hoist the initializer or the entire
4827 * decl out of the loop head. TOK_VAR is the type for both
4828 * 'var' and 'const'.
4830 pn2 = pn1->pn_head;
4831 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4832 #if JS_HAS_DESTRUCTURING
4833 || pn2->pn_type == TOK_ASSIGN
4834 #endif
4836 pnseq = ListNode::create(tc);
4837 if (!pnseq)
4838 return NULL;
4839 pnseq->pn_type = TOK_SEQ;
4840 pnseq->pn_pos.begin = pn->pn_pos.begin;
4842 #if JS_HAS_BLOCK_SCOPE
4843 if (tt == TOK_LET) {
4845 * Hoist just the 'i' from 'for (let x = i in o)' to
4846 * before the loop, glued together via pnseq.
4848 pn3 = UnaryNode::create(tc);
4849 if (!pn3)
4850 return NULL;
4851 pn3->pn_type = TOK_SEMI;
4852 pn3->pn_op = JSOP_NOP;
4853 #if JS_HAS_DESTRUCTURING
4854 if (pn2->pn_type == TOK_ASSIGN) {
4855 pn4 = pn2->pn_right;
4856 pn2 = pn1->pn_head = pn2->pn_left;
4857 } else
4858 #endif
4860 pn4 = pn2->pn_expr;
4861 pn2->pn_expr = NULL;
4863 if (!RebindLets(pn4, tc))
4864 return NULL;
4865 pn3->pn_pos = pn4->pn_pos;
4866 pn3->pn_kid = pn4;
4867 pnseq->initList(pn3);
4868 } else
4869 #endif /* JS_HAS_BLOCK_SCOPE */
4871 dflag = PND_INITIALIZED;
4874 * All of 'var x = i' is hoisted above 'for (x in o)',
4875 * so clear PNX_FORINVAR.
4877 * Request JSOP_POP here since the var is for a simple
4878 * name (it is not a destructuring binding's left-hand
4879 * side) and it has an initializer.
4881 pn1->pn_xflags &= ~PNX_FORINVAR;
4882 pn1->pn_xflags |= PNX_POPVAR;
4883 pnseq->initList(pn1);
4885 #if JS_HAS_DESTRUCTURING
4886 if (pn2->pn_type == TOK_ASSIGN) {
4887 pn1 = CloneParseTree(pn2->pn_left, tc);
4888 if (!pn1)
4889 return NULL;
4890 } else
4891 #endif
4893 JS_ASSERT(pn2->pn_type == TOK_NAME);
4894 pn1 = NameNode::create(pn2->pn_atom, tc);
4895 if (!pn1)
4896 return NULL;
4897 pn1->pn_type = TOK_NAME;
4898 pn1->pn_op = JSOP_NAME;
4899 pn1->pn_pos = pn2->pn_pos;
4900 if (pn2->pn_defn)
4901 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4903 pn2 = pn1;
4908 if (!pn2) {
4909 pn2 = pn1;
4910 if (pn2->pn_type == TOK_LP &&
4911 !MakeSetCall(context, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4912 return NULL;
4914 #if JS_HAS_XML_SUPPORT
4915 if (pn2->pn_type == TOK_UNARYOP)
4916 pn2->pn_op = JSOP_BINDXMLNAME;
4917 #endif
4920 switch (pn2->pn_type) {
4921 case TOK_NAME:
4922 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4923 NoteLValue(context, pn2, tc, dflag);
4924 break;
4926 #if JS_HAS_DESTRUCTURING
4927 case TOK_ASSIGN:
4928 pn2 = pn2->pn_left;
4929 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4930 /* FALL THROUGH */
4931 case TOK_RB:
4932 case TOK_RC:
4933 /* Check for valid lvalues in var-less destructuring for-in. */
4934 if (pn1 == pn2 && !CheckDestructuring(context, NULL, pn2, NULL, tc))
4935 return NULL;
4937 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
4939 * Destructuring for-in requires [key, value] enumeration
4940 * in JS1.7.
4942 JS_ASSERT(pn->pn_op == JSOP_ITER);
4943 if (!(pn->pn_iflags & JSITER_FOREACH))
4944 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4946 break;
4947 #endif
4949 default:;
4953 * Parse the object expression as the right operand of 'in', first
4954 * removing the top statement from the statement-stack if this is a
4955 * 'for (let x in y)' loop.
4957 #if JS_HAS_BLOCK_SCOPE
4958 JSStmtInfo *save = tc->topStmt;
4959 if (let)
4960 tc->topStmt = save->down;
4961 #endif
4962 pn2 = expr();
4963 #if JS_HAS_BLOCK_SCOPE
4964 if (let)
4965 tc->topStmt = save;
4966 #endif
4968 pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4969 if (!pn2)
4970 return NULL;
4971 pn->pn_left = pn2;
4972 } else {
4973 if (pn->pn_iflags & JSITER_FOREACH)
4974 goto bad_for_each;
4975 pn->pn_op = JSOP_NOP;
4977 /* Parse the loop condition or null into pn2. */
4978 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4979 tt = tokenStream.peekToken(TSF_OPERAND);
4980 if (tt == TOK_SEMI) {
4981 pn2 = NULL;
4982 } else {
4983 pn2 = expr();
4984 if (!pn2)
4985 return NULL;
4988 /* Parse the update expression or null into pn3. */
4989 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4990 tt = tokenStream.peekToken(TSF_OPERAND);
4991 if (tt == TOK_RP) {
4992 pn3 = NULL;
4993 } else {
4994 pn3 = expr();
4995 if (!pn3)
4996 return NULL;
4999 /* Build the FORHEAD node to use as the left kid of pn. */
5000 pn4 = TernaryNode::create(tc);
5001 if (!pn4)
5002 return NULL;
5003 pn4->pn_type = TOK_FORHEAD;
5004 pn4->pn_op = JSOP_NOP;
5005 pn4->pn_kid1 = pn1;
5006 pn4->pn_kid2 = pn2;
5007 pn4->pn_kid3 = pn3;
5008 pn->pn_left = pn4;
5011 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5013 /* Parse the loop body into pn->pn_right. */
5014 pn2 = statement();
5015 if (!pn2)
5016 return NULL;
5017 pn->pn_right = pn2;
5019 /* Record the absolute line number for source note emission. */
5020 pn->pn_pos.end = pn2->pn_pos.end;
5022 #if JS_HAS_BLOCK_SCOPE
5023 if (pnlet) {
5024 PopStatement(tc);
5025 pnlet->pn_expr = pn;
5026 pn = pnlet;
5028 #endif
5029 if (pnseq) {
5030 pnseq->pn_pos.end = pn->pn_pos.end;
5031 pnseq->append(pn);
5032 pn = pnseq;
5034 PopStatement(tc);
5035 return pn;
5037 bad_for_each:
5038 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
5039 return NULL;
5042 case TOK_TRY: {
5043 JSParseNode *catchList, *lastCatch;
5046 * try nodes are ternary.
5047 * kid1 is the try statement
5048 * kid2 is the catch node list or null
5049 * kid3 is the finally statement
5051 * catch nodes are ternary.
5052 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5053 * kid2 is the catch guard or null if no guard
5054 * kid3 is the catch block
5056 * catch lvalue nodes are either:
5057 * TOK_NAME for a single identifier
5058 * TOK_RB or TOK_RC for a destructuring left-hand side
5060 * finally nodes are TOK_LC statement lists.
5062 pn = TernaryNode::create(tc);
5063 if (!pn)
5064 return NULL;
5065 pn->pn_op = JSOP_NOP;
5067 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5068 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5069 return NULL;
5070 pn->pn_kid1 = statements();
5071 if (!pn->pn_kid1)
5072 return NULL;
5073 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5074 PopStatement(tc);
5076 catchList = NULL;
5077 tt = tokenStream.getToken();
5078 if (tt == TOK_CATCH) {
5079 catchList = ListNode::create(tc);
5080 if (!catchList)
5081 return NULL;
5082 catchList->pn_type = TOK_RESERVED;
5083 catchList->makeEmpty();
5084 lastCatch = NULL;
5086 do {
5087 JSParseNode *pnblock;
5088 BindData data;
5090 /* Check for another catch after unconditional catch. */
5091 if (lastCatch && !lastCatch->pn_kid2) {
5092 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
5093 return NULL;
5097 * Create a lexical scope node around the whole catch clause,
5098 * including the head.
5100 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
5101 if (!pnblock)
5102 return NULL;
5103 stmtInfo.type = STMT_CATCH;
5106 * Legal catch forms are:
5107 * catch (lhs)
5108 * catch (lhs if <boolean_expression>)
5109 * where lhs is a name or a destructuring left-hand side.
5110 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5112 pn2 = TernaryNode::create(tc);
5113 if (!pn2)
5114 return NULL;
5115 pnblock->pn_expr = pn2;
5116 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5119 * Contrary to ECMA Ed. 3, the catch variable is lexically
5120 * scoped, not a property of a new Object instance. This is
5121 * an intentional change that anticipates ECMA Ed. 4.
5123 data.pn = NULL;
5124 data.op = JSOP_NOP;
5125 data.binder = BindLet;
5126 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5128 tt = tokenStream.getToken();
5129 switch (tt) {
5130 #if JS_HAS_DESTRUCTURING
5131 case TOK_LB:
5132 case TOK_LC:
5133 pn3 = destructuringExpr(&data, tt);
5134 if (!pn3)
5135 return NULL;
5136 break;
5137 #endif
5139 case TOK_NAME:
5140 label = tokenStream.currentToken().t_atom;
5141 pn3 = NewBindingNode(label, tc, true);
5142 if (!pn3)
5143 return NULL;
5144 data.pn = pn3;
5145 if (!data.binder(context, &data, label, tc))
5146 return NULL;
5147 break;
5149 default:
5150 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER);
5151 return NULL;
5154 pn2->pn_kid1 = pn3;
5155 #if JS_HAS_CATCH_GUARD
5157 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5158 * to avoid conflicting with the JS2/ECMAv4 type annotation
5159 * catchguard syntax.
5161 if (tokenStream.matchToken(TOK_IF)) {
5162 pn2->pn_kid2 = expr();
5163 if (!pn2->pn_kid2)
5164 return NULL;
5166 #endif
5167 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5169 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5170 pn2->pn_kid3 = statements();
5171 if (!pn2->pn_kid3)
5172 return NULL;
5173 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5174 PopStatement(tc);
5176 catchList->append(pnblock);
5177 lastCatch = pn2;
5178 tt = tokenStream.getToken(TSF_OPERAND);
5179 } while (tt == TOK_CATCH);
5181 pn->pn_kid2 = catchList;
5183 if (tt == TOK_FINALLY) {
5184 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5185 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5186 return NULL;
5187 pn->pn_kid3 = statements();
5188 if (!pn->pn_kid3)
5189 return NULL;
5190 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5191 PopStatement(tc);
5192 } else {
5193 tokenStream.ungetToken();
5195 if (!catchList && !pn->pn_kid3) {
5196 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY);
5197 return NULL;
5199 return pn;
5202 case TOK_THROW:
5203 pn = UnaryNode::create(tc);
5204 if (!pn)
5205 return NULL;
5207 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5208 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
5209 if (tt == TOK_ERROR)
5210 return NULL;
5211 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5212 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
5213 return NULL;
5216 pn2 = expr();
5217 if (!pn2)
5218 return NULL;
5219 pn->pn_pos.end = pn2->pn_pos.end;
5220 pn->pn_op = JSOP_THROW;
5221 pn->pn_kid = pn2;
5222 break;
5224 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5225 case TOK_CATCH:
5226 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY);
5227 return NULL;
5229 case TOK_FINALLY:
5230 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY);
5231 return NULL;
5233 case TOK_BREAK:
5234 pn = NullaryNode::create(tc);
5235 if (!pn)
5236 return NULL;
5237 if (!MatchLabel(context, &tokenStream, pn))
5238 return NULL;
5239 stmt = tc->topStmt;
5240 label = pn->pn_atom;
5241 if (label) {
5242 for (; ; stmt = stmt->down) {
5243 if (!stmt) {
5244 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
5245 return NULL;
5247 if (stmt->type == STMT_LABEL && stmt->label == label)
5248 break;
5250 } else {
5251 for (; ; stmt = stmt->down) {
5252 if (!stmt) {
5253 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOUGH_BREAK);
5254 return NULL;
5256 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5257 break;
5260 if (label)
5261 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5262 break;
5264 case TOK_CONTINUE:
5265 pn = NullaryNode::create(tc);
5266 if (!pn)
5267 return NULL;
5268 if (!MatchLabel(context, &tokenStream, pn))
5269 return NULL;
5270 stmt = tc->topStmt;
5271 label = pn->pn_atom;
5272 if (label) {
5273 for (stmt2 = NULL; ; stmt = stmt->down) {
5274 if (!stmt) {
5275 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
5276 return NULL;
5278 if (stmt->type == STMT_LABEL) {
5279 if (stmt->label == label) {
5280 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5281 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
5282 return NULL;
5284 break;
5286 } else {
5287 stmt2 = stmt;
5290 } else {
5291 for (; ; stmt = stmt->down) {
5292 if (!stmt) {
5293 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
5294 return NULL;
5296 if (STMT_IS_LOOP(stmt))
5297 break;
5300 if (label)
5301 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5302 break;
5304 case TOK_WITH:
5306 * In most cases, we want the constructs forbidden in strict mode
5307 * code to be a subset of those that JSOPTION_STRICT warns about, and
5308 * we should use ReportStrictModeError. However, 'with' is the sole
5309 * instance of a construct that is forbidden in strict mode code, but
5310 * doesn't even merit a warning under JSOPTION_STRICT. See
5311 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5313 if (tc->flags & TCF_STRICT_MODE_CODE) {
5314 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
5315 return NULL;
5318 pn = BinaryNode::create(tc);
5319 if (!pn)
5320 return NULL;
5321 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5322 pn2 = parenExpr(NULL, NULL);
5323 if (!pn2)
5324 return NULL;
5325 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5326 pn->pn_left = pn2;
5328 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5329 pn2 = statement();
5330 if (!pn2)
5331 return NULL;
5332 PopStatement(tc);
5334 pn->pn_pos.end = pn2->pn_pos.end;
5335 pn->pn_right = pn2;
5336 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5337 return pn;
5339 case TOK_VAR:
5340 pn = variables(false);
5341 if (!pn)
5342 return NULL;
5344 /* Tell js_EmitTree to generate a final POP. */
5345 pn->pn_xflags |= PNX_POPVAR;
5346 break;
5348 #if JS_HAS_BLOCK_SCOPE
5349 case TOK_LET:
5351 JSObject *obj;
5352 JSObjectBox *blockbox;
5354 /* Check for a let statement or let expression. */
5355 if (tokenStream.peekToken() == TOK_LP) {
5356 pn = letBlock(JS_TRUE);
5357 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5358 return pn;
5360 /* Let expressions require automatic semicolon insertion. */
5361 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5362 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5363 break;
5367 * This is a let declaration. We must be directly under a block per
5368 * the proposed ES4 specs, but not an implicit block created due to
5369 * 'for (let ...)'. If we pass this error test, make the enclosing
5370 * JSStmtInfo be our scope. Further let declarations in this block
5371 * will find this scope statement and use the same block object.
5373 * If we are the first let declaration in this block (i.e., when the
5374 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5375 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5377 stmt = tc->topStmt;
5378 if (stmt &&
5379 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5380 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LET_DECL_NOT_IN_BLOCK);
5381 return NULL;
5384 if (stmt && (stmt->flags & SIF_SCOPE)) {
5385 JS_ASSERT(tc->blockChain == stmt->blockObj);
5386 obj = tc->blockChain;
5387 } else {
5388 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5390 * ES4 specifies that let at top level and at body-block scope
5391 * does not shadow var, so convert back to var.
5393 tokenStream.mungeCurrentToken(TOK_VAR, JSOP_DEFVAR);
5395 pn = variables(false);
5396 if (!pn)
5397 return NULL;
5398 pn->pn_xflags |= PNX_POPVAR;
5399 break;
5403 * Some obvious assertions here, but they may help clarify the
5404 * situation. This stmt is not yet a scope, so it must not be a
5405 * catch block (catch is a lexical scope by definition).
5407 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5408 JS_ASSERT(stmt != tc->topScopeStmt);
5409 JS_ASSERT(stmt->type == STMT_BLOCK ||
5410 stmt->type == STMT_SWITCH ||
5411 stmt->type == STMT_TRY ||
5412 stmt->type == STMT_FINALLY);
5413 JS_ASSERT(!stmt->downScope);
5415 /* Convert the block statement into a scope statement. */
5416 JSObject *obj = js_NewBlockObject(tc->parser->context);
5417 if (!obj)
5418 return NULL;
5420 blockbox = tc->parser->newObjectBox(obj);
5421 if (!blockbox)
5422 return NULL;
5425 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5426 * list stack, if it isn't already there. If it is there, but it
5427 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5428 * block.
5430 stmt->flags |= SIF_SCOPE;
5431 stmt->downScope = tc->topScopeStmt;
5432 tc->topScopeStmt = stmt;
5433 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5434 (tc->maxScopeDepth = tc->scopeDepth));
5436 obj->setParent(tc->blockChain);
5437 tc->blockChain = obj;
5438 stmt->blockObj = obj;
5440 #ifdef DEBUG
5441 pn1 = tc->blockNode;
5442 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5443 #endif
5445 /* Create a new lexical scope node for these statements. */
5446 pn1 = LexicalScopeNode::create(tc);
5447 if (!pn1)
5448 return NULL;
5450 pn1->pn_type = TOK_LEXICALSCOPE;
5451 pn1->pn_op = JSOP_LEAVEBLOCK;
5452 pn1->pn_pos = tc->blockNode->pn_pos;
5453 pn1->pn_objbox = blockbox;
5454 pn1->pn_expr = tc->blockNode;
5455 pn1->pn_blockid = tc->blockNode->pn_blockid;
5456 tc->blockNode = pn1;
5459 pn = variables(false);
5460 if (!pn)
5461 return NULL;
5462 pn->pn_xflags = PNX_POPVAR;
5463 break;
5465 #endif /* JS_HAS_BLOCK_SCOPE */
5467 case TOK_RETURN:
5468 pn = returnOrYield(false);
5469 if (!pn)
5470 return NULL;
5471 break;
5473 case TOK_LC:
5475 uintN oldflags;
5477 oldflags = tc->flags;
5478 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5479 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5480 return NULL;
5481 pn = statements();
5482 if (!pn)
5483 return NULL;
5485 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5486 PopStatement(tc);
5489 * If we contain a function statement and our container is top-level
5490 * or another block, flag pn to preserve braces when decompiling.
5492 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5493 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5494 pn->pn_xflags |= PNX_NEEDBRACES;
5496 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5497 return pn;
5500 case TOK_EOL:
5501 case TOK_SEMI:
5502 pn = UnaryNode::create(tc);
5503 if (!pn)
5504 return NULL;
5505 pn->pn_type = TOK_SEMI;
5506 return pn;
5508 #if JS_HAS_DEBUGGER_KEYWORD
5509 case TOK_DEBUGGER:
5510 pn = NullaryNode::create(tc);
5511 if (!pn)
5512 return NULL;
5513 pn->pn_type = TOK_DEBUGGER;
5514 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5515 break;
5516 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5518 #if JS_HAS_XML_SUPPORT
5519 case TOK_DEFAULT:
5520 pn = UnaryNode::create(tc);
5521 if (!pn)
5522 return NULL;
5523 if (!tokenStream.matchToken(TOK_NAME) ||
5524 tokenStream.currentToken().t_atom != context->runtime->atomState.xmlAtom ||
5525 !tokenStream.matchToken(TOK_NAME) ||
5526 tokenStream.currentToken().t_atom != context->runtime->atomState.namespaceAtom ||
5527 !tokenStream.matchToken(TOK_ASSIGN) ||
5528 tokenStream.currentToken().t_op != JSOP_NOP) {
5529 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5530 return NULL;
5533 /* Is this an E4X dagger I see before me? */
5534 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5535 pn2 = expr();
5536 if (!pn2)
5537 return NULL;
5538 pn->pn_op = JSOP_DEFXMLNS;
5539 pn->pn_pos.end = pn2->pn_pos.end;
5540 pn->pn_kid = pn2;
5541 break;
5542 #endif
5544 case TOK_ERROR:
5545 return NULL;
5547 default:
5548 #if JS_HAS_XML_SUPPORT
5549 expression:
5550 #endif
5551 tokenStream.ungetToken();
5552 pn2 = expr();
5553 if (!pn2)
5554 return NULL;
5556 if (tokenStream.peekToken() == TOK_COLON) {
5557 if (pn2->pn_type != TOK_NAME) {
5558 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LABEL);
5559 return NULL;
5561 label = pn2->pn_atom;
5562 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5563 if (stmt->type == STMT_LABEL && stmt->label == label) {
5564 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL);
5565 return NULL;
5568 ForgetUse(pn2);
5570 (void) tokenStream.getToken();
5572 /* Push a label struct and parse the statement. */
5573 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5574 stmtInfo.label = label;
5575 pn = statement();
5576 if (!pn)
5577 return NULL;
5579 /* Normalize empty statement to empty block for the decompiler. */
5580 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5581 pn->pn_type = TOK_LC;
5582 pn->pn_arity = PN_LIST;
5583 pn->makeEmpty();
5586 /* Pop the label, set pn_expr, and return early. */
5587 PopStatement(tc);
5588 pn2->pn_type = TOK_COLON;
5589 pn2->pn_pos.end = pn->pn_pos.end;
5590 pn2->pn_expr = pn;
5591 return pn2;
5594 pn = UnaryNode::create(tc);
5595 if (!pn)
5596 return NULL;
5597 pn->pn_type = TOK_SEMI;
5598 pn->pn_pos = pn2->pn_pos;
5599 pn->pn_kid = pn2;
5601 switch (PN_TYPE(pn2)) {
5602 case TOK_LP:
5604 * Flag lambdas immediately applied as statements as instances of
5605 * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5607 if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION &&
5608 !pn2->pn_head->pn_funbox->node->isFunArg()) {
5609 pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
5611 break;
5612 case TOK_ASSIGN:
5614 * Keep track of all apparent methods created by assignments such
5615 * as this.foo = function (...) {...} in a function that could end
5616 * up a constructor function. See Parser::setFunctionKinds.
5618 if (tc->funbox &&
5619 PN_OP(pn2) == JSOP_NOP &&
5620 PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5621 PN_OP(pn2->pn_left->pn_expr) == JSOP_THIS &&
5622 PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
5623 JS_ASSERT(!pn2->pn_defn);
5624 JS_ASSERT(!pn2->pn_used);
5625 pn2->pn_right->pn_link = tc->funbox->methods;
5626 tc->funbox->methods = pn2->pn_right;
5628 break;
5629 default:;
5631 break;
5634 /* Check termination of this primitive statement. */
5635 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5638 static void
5639 NoteArgumentsUse(JSTreeContext *tc)
5641 JS_ASSERT(tc->inFunction());
5642 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5643 if (tc->funbox)
5644 tc->funbox->node->pn_dflags |= PND_FUNARG;
5647 JSParseNode *
5648 Parser::variables(bool inLetHead)
5650 TokenKind tt;
5651 bool let;
5652 JSStmtInfo *scopeStmt;
5653 BindData data;
5654 JSParseNode *pn, *pn2;
5655 JSAtom *atom;
5658 * The three options here are:
5659 * - TOK_LET: We are parsing a let declaration.
5660 * - TOK_LP: We are parsing the head of a let block.
5661 * - Otherwise, we're parsing var declarations.
5663 tt = tokenStream.currentToken().type;
5664 let = (tt == TOK_LET || tt == TOK_LP);
5665 JS_ASSERT(let || tt == TOK_VAR);
5667 #if JS_HAS_BLOCK_SCOPE
5668 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5669 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5670 #endif
5672 /* Make sure that statement set up the tree context correctly. */
5673 scopeStmt = tc->topScopeStmt;
5674 if (let) {
5675 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5676 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5677 scopeStmt = scopeStmt->downScope;
5679 JS_ASSERT(scopeStmt);
5682 data.op = let ? JSOP_NOP : tokenStream.currentToken().t_op;
5683 pn = ListNode::create(tc);
5684 if (!pn)
5685 return NULL;
5686 pn->pn_op = data.op;
5687 pn->makeEmpty();
5690 * SpiderMonkey const is really "write once per initialization evaluation"
5691 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5692 * this code will change soon.
5694 if (let) {
5695 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5696 data.binder = BindLet;
5697 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5698 } else {
5699 data.binder = BindVarOrConst;
5702 do {
5703 tt = tokenStream.getToken();
5704 #if JS_HAS_DESTRUCTURING
5705 if (tt == TOK_LB || tt == TOK_LC) {
5706 tc->flags |= TCF_DECL_DESTRUCTURING;
5707 pn2 = primaryExpr(tt, JS_FALSE);
5708 tc->flags &= ~TCF_DECL_DESTRUCTURING;
5709 if (!pn2)
5710 return NULL;
5712 if (!CheckDestructuring(context, &data, pn2, NULL, tc))
5713 return NULL;
5714 if ((tc->flags & TCF_IN_FOR_INIT) &&
5715 tokenStream.peekToken() == TOK_IN) {
5716 pn->append(pn2);
5717 continue;
5720 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5721 if (tokenStream.currentToken().t_op != JSOP_NOP)
5722 goto bad_var_init;
5724 #if JS_HAS_BLOCK_SCOPE
5725 if (popScope) {
5726 tc->topStmt = save->down;
5727 tc->topScopeStmt = saveScope->downScope;
5729 #endif
5730 JSParseNode *init = assignExpr();
5731 #if JS_HAS_BLOCK_SCOPE
5732 if (popScope) {
5733 tc->topStmt = save;
5734 tc->topScopeStmt = saveScope;
5736 #endif
5738 if (!init || !UndominateInitializers(pn2, init, tc))
5739 return NULL;
5741 pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5742 if (!pn2)
5743 return NULL;
5744 pn->append(pn2);
5745 continue;
5747 #endif /* JS_HAS_DESTRUCTURING */
5749 if (tt != TOK_NAME) {
5750 if (tt != TOK_ERROR) {
5751 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
5753 return NULL;
5756 atom = tokenStream.currentToken().t_atom;
5757 pn2 = NewBindingNode(atom, tc, let);
5758 if (!pn2)
5759 return NULL;
5760 if (data.op == JSOP_DEFCONST)
5761 pn2->pn_dflags |= PND_CONST;
5762 data.pn = pn2;
5763 if (!data.binder(context, &data, atom, tc))
5764 return NULL;
5765 pn->append(pn2);
5767 if (tokenStream.matchToken(TOK_ASSIGN)) {
5768 if (tokenStream.currentToken().t_op != JSOP_NOP)
5769 goto bad_var_init;
5771 #if JS_HAS_BLOCK_SCOPE
5772 if (popScope) {
5773 tc->topStmt = save->down;
5774 tc->topScopeStmt = saveScope->downScope;
5776 #endif
5777 JSParseNode *init = assignExpr();
5778 #if JS_HAS_BLOCK_SCOPE
5779 if (popScope) {
5780 tc->topStmt = save;
5781 tc->topScopeStmt = saveScope;
5783 #endif
5784 if (!init)
5785 return NULL;
5787 if (pn2->pn_used) {
5788 pn2 = MakeAssignment(pn2, init, tc);
5789 if (!pn2)
5790 return NULL;
5791 } else {
5792 pn2->pn_expr = init;
5795 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5796 ? JSOP_SETNAME
5797 : (pn2->pn_dflags & PND_GVAR)
5798 ? JSOP_SETGVAR
5799 : (pn2->pn_dflags & PND_BOUND)
5800 ? JSOP_SETLOCAL
5801 : (data.op == JSOP_DEFCONST)
5802 ? JSOP_SETCONST
5803 : JSOP_SETNAME;
5805 NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5807 /* The declarator's position must include the initializer. */
5808 pn2->pn_pos.end = init->pn_pos.end;
5810 if (tc->inFunction() &&
5811 atom == context->runtime->atomState.argumentsAtom) {
5812 NoteArgumentsUse(tc);
5813 if (!let)
5814 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5817 } while (tokenStream.matchToken(TOK_COMMA));
5819 pn->pn_pos.end = pn->last()->pn_pos.end;
5820 return pn;
5822 bad_var_init:
5823 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_VAR_INIT);
5824 return NULL;
5827 JSParseNode *
5828 Parser::expr()
5830 JSParseNode *pn = assignExpr();
5831 if (pn && tokenStream.matchToken(TOK_COMMA)) {
5832 JSParseNode *pn2 = ListNode::create(tc);
5833 if (!pn2)
5834 return NULL;
5835 pn2->pn_pos.begin = pn->pn_pos.begin;
5836 pn2->initList(pn);
5837 pn = pn2;
5838 do {
5839 #if JS_HAS_GENERATORS
5840 pn2 = pn->last();
5841 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
5842 reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
5843 return NULL;
5845 #endif
5846 pn2 = assignExpr();
5847 if (!pn2)
5848 return NULL;
5849 pn->append(pn2);
5850 } while (tokenStream.matchToken(TOK_COMMA));
5851 pn->pn_pos.end = pn->last()->pn_pos.end;
5853 return pn;
5856 JSParseNode *
5857 Parser::assignExpr()
5859 JS_CHECK_RECURSION(context, return NULL);
5861 #if JS_HAS_GENERATORS
5862 if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
5863 return returnOrYield(true);
5864 #endif
5866 JSParseNode *pn = condExpr();
5867 if (!pn)
5868 return NULL;
5870 TokenKind tt = tokenStream.getToken();
5871 if (tt != TOK_ASSIGN) {
5872 tokenStream.ungetToken();
5873 return pn;
5876 JSOp op = tokenStream.currentToken().t_op;
5877 switch (pn->pn_type) {
5878 case TOK_NAME:
5879 if (!CheckStrictAssignment(context, tc, pn))
5880 return NULL;
5881 pn->pn_op = JSOP_SETNAME;
5882 NoteLValue(context, pn, tc);
5883 break;
5884 case TOK_DOT:
5885 pn->pn_op = JSOP_SETPROP;
5886 break;
5887 case TOK_LB:
5888 pn->pn_op = JSOP_SETELEM;
5889 break;
5890 #if JS_HAS_DESTRUCTURING
5891 case TOK_RB:
5892 case TOK_RC:
5894 if (op != JSOP_NOP) {
5895 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS);
5896 return NULL;
5898 JSParseNode *rhs = assignExpr();
5899 if (!rhs || !CheckDestructuring(context, NULL, pn, rhs, tc))
5900 return NULL;
5901 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5903 #endif
5904 case TOK_LP:
5905 if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5906 return NULL;
5907 break;
5908 #if JS_HAS_XML_SUPPORT
5909 case TOK_UNARYOP:
5910 if (pn->pn_op == JSOP_XMLNAME) {
5911 pn->pn_op = JSOP_SETXMLNAME;
5912 break;
5914 /* FALL THROUGH */
5915 #endif
5916 default:
5917 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
5918 return NULL;
5921 JSParseNode *rhs = assignExpr();
5922 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
5923 JSDefinition *dn = pn->pn_lexdef;
5926 * If the definition is not flagged as assigned, we must have imputed
5927 * the initialized flag to it, to optimize for flat closures. But that
5928 * optimization uses source coordinates to check dominance relations,
5929 * so we must extend the end of the definition to cover the right-hand
5930 * side of this assignment, i.e., the initializer.
5932 if (!dn->isAssigned()) {
5933 JS_ASSERT(dn->isInitialized());
5934 dn->pn_pos.end = rhs->pn_pos.end;
5938 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5941 JSParseNode *
5942 Parser::condExpr()
5944 JSParseNode *pn = orExpr();
5945 if (pn && tokenStream.matchToken(TOK_HOOK)) {
5946 JSParseNode *pn1 = pn;
5947 pn = TernaryNode::create(tc);
5948 if (!pn)
5949 return NULL;
5952 * Always accept the 'in' operator in the middle clause of a ternary,
5953 * where it's unambiguous, even if we might be parsing the init of a
5954 * for statement.
5956 uintN oldflags = tc->flags;
5957 tc->flags &= ~TCF_IN_FOR_INIT;
5958 JSParseNode *pn2 = assignExpr();
5959 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5961 if (!pn2)
5962 return NULL;
5963 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
5964 JSParseNode *pn3 = assignExpr();
5965 if (!pn3)
5966 return NULL;
5967 pn->pn_pos.begin = pn1->pn_pos.begin;
5968 pn->pn_pos.end = pn3->pn_pos.end;
5969 pn->pn_kid1 = pn1;
5970 pn->pn_kid2 = pn2;
5971 pn->pn_kid3 = pn3;
5973 return pn;
5976 JSParseNode *
5977 Parser::orExpr()
5979 JSParseNode *pn = andExpr();
5980 while (pn && tokenStream.matchToken(TOK_OR))
5981 pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, andExpr(), tc);
5982 return pn;
5985 JSParseNode *
5986 Parser::andExpr()
5988 JSParseNode *pn = bitOrExpr();
5989 while (pn && tokenStream.matchToken(TOK_AND))
5990 pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, bitOrExpr(), tc);
5991 return pn;
5994 JSParseNode *
5995 Parser::bitOrExpr()
5997 JSParseNode *pn = bitXorExpr();
5998 while (pn && tokenStream.matchToken(TOK_BITOR))
5999 pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, bitXorExpr(), tc);
6000 return pn;
6003 JSParseNode *
6004 Parser::bitXorExpr()
6006 JSParseNode *pn = bitAndExpr();
6007 while (pn && tokenStream.matchToken(TOK_BITXOR)) {
6008 pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, bitAndExpr(), tc);
6010 return pn;
6013 JSParseNode *
6014 Parser::bitAndExpr()
6016 JSParseNode *pn = eqExpr();
6017 while (pn && tokenStream.matchToken(TOK_BITAND))
6018 pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, eqExpr(), tc);
6019 return pn;
6022 JSParseNode *
6023 Parser::eqExpr()
6025 JSParseNode *pn = relExpr();
6026 while (pn && tokenStream.matchToken(TOK_EQOP)) {
6027 JSOp op = tokenStream.currentToken().t_op;
6028 pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, relExpr(), tc);
6030 return pn;
6033 JSParseNode *
6034 Parser::relExpr()
6036 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6039 * Uses of the in operator in shiftExprs are always unambiguous,
6040 * so unset the flag that prohibits recognizing it.
6042 tc->flags &= ~TCF_IN_FOR_INIT;
6044 JSParseNode *pn = shiftExpr();
6045 while (pn &&
6046 (tokenStream.matchToken(TOK_RELOP) ||
6048 * Recognize the 'in' token as an operator only if we're not
6049 * currently in the init expr of a for loop.
6051 (inForInitFlag == 0 && tokenStream.matchToken(TOK_IN)) ||
6052 tokenStream.matchToken(TOK_INSTANCEOF))) {
6053 TokenKind tt = tokenStream.currentToken().type;
6054 JSOp op = tokenStream.currentToken().t_op;
6055 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, shiftExpr(), tc);
6057 /* Restore previous state of inForInit flag. */
6058 tc->flags |= inForInitFlag;
6060 return pn;
6063 JSParseNode *
6064 Parser::shiftExpr()
6066 JSParseNode *pn = addExpr();
6067 while (pn && tokenStream.matchToken(TOK_SHOP)) {
6068 JSOp op = tokenStream.currentToken().t_op;
6069 pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, addExpr(), tc);
6071 return pn;
6074 JSParseNode *
6075 Parser::addExpr()
6077 JSParseNode *pn = mulExpr();
6078 while (pn &&
6079 (tokenStream.matchToken(TOK_PLUS) ||
6080 tokenStream.matchToken(TOK_MINUS))) {
6081 TokenKind tt = tokenStream.currentToken().type;
6082 JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6083 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, mulExpr(), tc);
6085 return pn;
6088 JSParseNode *
6089 Parser::mulExpr()
6091 JSParseNode *pn = unaryExpr();
6092 while (pn && (tokenStream.matchToken(TOK_STAR) || tokenStream.matchToken(TOK_DIVOP))) {
6093 TokenKind tt = tokenStream.currentToken().type;
6094 JSOp op = tokenStream.currentToken().t_op;
6095 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, unaryExpr(), tc);
6097 return pn;
6100 static JSParseNode *
6101 SetLvalKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6102 JSParseNode *pn, JSParseNode *kid, const char *name)
6104 if (kid->pn_type != TOK_NAME &&
6105 kid->pn_type != TOK_DOT &&
6106 (kid->pn_type != TOK_LP ||
6107 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6108 #if JS_HAS_XML_SUPPORT
6109 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6110 #endif
6111 kid->pn_type != TOK_LB) {
6112 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
6113 return NULL;
6115 if (!CheckStrictAssignment(cx, tc, kid))
6116 return NULL;
6117 pn->pn_kid = kid;
6118 return kid;
6121 static const char incop_name_str[][10] = {"increment", "decrement"};
6123 static JSBool
6124 SetIncOpKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6125 JSParseNode *pn, JSParseNode *kid,
6126 TokenKind tt, JSBool preorder)
6128 JSOp op;
6130 kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6131 if (!kid)
6132 return JS_FALSE;
6133 switch (kid->pn_type) {
6134 case TOK_NAME:
6135 op = (tt == TOK_INC)
6136 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6137 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6138 NoteLValue(cx, kid, tc);
6139 break;
6141 case TOK_DOT:
6142 op = (tt == TOK_INC)
6143 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6144 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6145 break;
6147 case TOK_LP:
6148 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6149 return JS_FALSE;
6150 /* FALL THROUGH */
6151 #if JS_HAS_XML_SUPPORT
6152 case TOK_UNARYOP:
6153 if (kid->pn_op == JSOP_XMLNAME)
6154 kid->pn_op = JSOP_SETXMLNAME;
6155 /* FALL THROUGH */
6156 #endif
6157 case TOK_LB:
6158 op = (tt == TOK_INC)
6159 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6160 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6161 break;
6163 default:
6164 JS_ASSERT(0);
6165 op = JSOP_NOP;
6167 pn->pn_op = op;
6168 return JS_TRUE;
6171 JSParseNode *
6172 Parser::unaryExpr()
6174 JSParseNode *pn, *pn2;
6176 JS_CHECK_RECURSION(context, return NULL);
6178 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6179 switch (tt) {
6180 case TOK_UNARYOP:
6181 case TOK_PLUS:
6182 case TOK_MINUS:
6183 pn = UnaryNode::create(tc);
6184 if (!pn)
6185 return NULL;
6186 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6187 pn->pn_op = tokenStream.currentToken().t_op;
6188 pn2 = unaryExpr();
6189 if (!pn2)
6190 return NULL;
6191 pn->pn_pos.end = pn2->pn_pos.end;
6192 pn->pn_kid = pn2;
6193 break;
6195 case TOK_INC:
6196 case TOK_DEC:
6197 pn = UnaryNode::create(tc);
6198 if (!pn)
6199 return NULL;
6200 pn2 = memberExpr(JS_TRUE);
6201 if (!pn2)
6202 return NULL;
6203 if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, JS_TRUE))
6204 return NULL;
6205 pn->pn_pos.end = pn2->pn_pos.end;
6206 break;
6208 case TOK_DELETE:
6210 pn = UnaryNode::create(tc);
6211 if (!pn)
6212 return NULL;
6213 pn2 = unaryExpr();
6214 if (!pn2)
6215 return NULL;
6216 pn->pn_pos.end = pn2->pn_pos.end;
6219 * Under ECMA3, deleting any unary expression is valid -- it simply
6220 * returns true. Here we fold constants before checking for a call
6221 * expression, in order to rule out delete of a generator expression.
6223 if (!js_FoldConstants(context, pn2, tc))
6224 return NULL;
6225 switch (pn2->pn_type) {
6226 case TOK_LP:
6227 if (pn2->pn_op != JSOP_SETCALL &&
6228 !MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6229 return NULL;
6231 break;
6232 case TOK_NAME:
6233 if (!ReportStrictModeError(context, &tokenStream, tc, pn,
6234 JSMSG_DEPRECATED_DELETE_OPERAND))
6235 return NULL;
6236 pn2->pn_op = JSOP_DELNAME;
6237 if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
6238 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6239 break;
6240 default:;
6242 pn->pn_kid = pn2;
6243 break;
6245 case TOK_ERROR:
6246 return NULL;
6248 default:
6249 tokenStream.ungetToken();
6250 pn = memberExpr(JS_TRUE);
6251 if (!pn)
6252 return NULL;
6254 /* Don't look across a newline boundary for a postfix incop. */
6255 if (tokenStream.onCurrentLine(pn->pn_pos)) {
6256 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6257 if (tt == TOK_INC || tt == TOK_DEC) {
6258 (void) tokenStream.getToken();
6259 pn2 = UnaryNode::create(tc);
6260 if (!pn2)
6261 return NULL;
6262 if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, JS_FALSE))
6263 return NULL;
6264 pn2->pn_pos.begin = pn->pn_pos.begin;
6265 pn = pn2;
6268 break;
6270 return pn;
6273 #if JS_HAS_GENERATORS
6276 * A dedicated helper for transplanting the comprehension expression E in
6278 * [E for (V in I)] // array comprehension
6279 * (E for (V in I)) // generator expression
6281 * from its initial location in the AST, on the left of the 'for', to its final
6282 * position on the right. To avoid a separate pass we do this by adjusting the
6283 * blockids and name binding links that were established when E was parsed.
6285 * A generator expression desugars like so:
6287 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6289 * so the transplanter must adjust static level as well as blockid. E's source
6290 * coordinates in root->pn_pos are critical to deciding which binding links to
6291 * preserve and which to cut.
6293 * NB: This is not a general tree transplanter -- it knows in particular that
6294 * the one or more bindings induced by V have not yet been created.
6296 class CompExprTransplanter {
6297 JSParseNode *root;
6298 JSTreeContext *tc;
6299 bool genexp;
6300 uintN adjust;
6301 uintN funcLevel;
6303 public:
6304 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6305 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6309 bool transplant(JSParseNode *pn);
6313 * Any definitions nested within the comprehension expression of a generator
6314 * expression must move "down" one static level, which of course increases the
6315 * upvar-frame-skip count.
6317 static bool
6318 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6320 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6321 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6323 JS_ASSERT(level >= tc->staticLevel);
6324 if (level >= FREE_STATIC_LEVEL) {
6325 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
6326 JSMSG_TOO_DEEP, js_function_str);
6327 return false;
6330 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6332 return true;
6335 static void
6336 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6338 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6339 pn->pn_blockid += adjust;
6340 if (pn->pn_blockid >= tc->blockidGen)
6341 tc->blockidGen = pn->pn_blockid + 1;
6344 bool
6345 CompExprTransplanter::transplant(JSParseNode *pn)
6347 if (!pn)
6348 return true;
6350 switch (pn->pn_arity) {
6351 case PN_LIST:
6352 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6353 transplant(pn2);
6354 if (pn->pn_pos >= root->pn_pos)
6355 AdjustBlockId(pn, adjust, tc);
6356 break;
6358 case PN_TERNARY:
6359 transplant(pn->pn_kid1);
6360 transplant(pn->pn_kid2);
6361 transplant(pn->pn_kid3);
6362 break;
6364 case PN_BINARY:
6365 transplant(pn->pn_left);
6367 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6368 if (pn->pn_right != pn->pn_left)
6369 transplant(pn->pn_right);
6370 break;
6372 case PN_UNARY:
6373 transplant(pn->pn_kid);
6374 break;
6376 case PN_FUNC:
6379 * Only the first level of transplant recursion through functions needs
6380 * to reparent the funbox, since all descendant functions are correctly
6381 * linked under the top-most funbox. But every visit to this case needs
6382 * to update funbox->level.
6384 * Recall that funbox->level is the static level of the code containing
6385 * the definition or expression of the function and not the static level
6386 * of the function's body.
6388 JSFunctionBox *funbox = pn->pn_funbox;
6390 funbox->level = tc->staticLevel + funcLevel;
6391 if (++funcLevel == 1 && genexp) {
6392 JSFunctionBox *parent = tc->funbox;
6394 JSFunctionBox **funboxp = &tc->parent->functionList;
6395 while (*funboxp != funbox)
6396 funboxp = &(*funboxp)->siblings;
6397 *funboxp = funbox->siblings;
6399 funbox->parent = parent;
6400 funbox->siblings = parent->kids;
6401 parent->kids = funbox;
6402 funbox->level = tc->staticLevel;
6404 /* FALL THROUGH */
6407 case PN_NAME:
6408 transplant(pn->maybeExpr());
6409 if (pn->pn_arity == PN_FUNC)
6410 --funcLevel;
6412 if (pn->pn_defn) {
6413 if (genexp && !BumpStaticLevel(pn, tc))
6414 return false;
6415 } else if (pn->pn_used) {
6416 JS_ASSERT(pn->pn_op != JSOP_NOP);
6417 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6419 JSDefinition *dn = pn->pn_lexdef;
6420 JS_ASSERT(dn->pn_defn);
6423 * Adjust the definition's block id only if it is a placeholder not
6424 * to the left of the root node, and if pn is the last use visited
6425 * in the comprehension expression (to avoid adjusting the blockid
6426 * multiple times).
6428 * Non-placeholder definitions within the comprehension expression
6429 * will be visited further below.
6431 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6432 if (genexp && !BumpStaticLevel(dn, tc))
6433 return false;
6434 AdjustBlockId(dn, adjust, tc);
6437 JSAtom *atom = pn->pn_atom;
6438 #ifdef DEBUG
6439 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6440 JS_ASSERT(!stmt || stmt != tc->topStmt);
6441 #endif
6442 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6443 JS_ASSERT(!tc->decls.lookup(atom));
6445 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6446 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, dn->pn_atom);
6447 if (!ale)
6448 return false;
6450 if (dn->pn_pos >= root->pn_pos) {
6451 tc->parent->lexdeps.remove(tc->parser, atom);
6452 } else {
6453 JSDefinition *dn2 = (JSDefinition *)NameNode::create(dn->pn_atom, tc);
6454 if (!dn2)
6455 return false;
6457 dn2->pn_type = dn->pn_type;
6458 dn2->pn_pos = root->pn_pos;
6459 dn2->pn_defn = true;
6460 dn2->pn_dflags |= PND_PLACEHOLDER;
6462 JSParseNode **pnup = &dn->dn_uses;
6463 JSParseNode *pnu;
6464 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6465 pnu->pn_lexdef = dn2;
6466 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6467 pnup = &pnu->pn_link;
6469 dn2->dn_uses = dn->dn_uses;
6470 dn->dn_uses = *pnup;
6471 *pnup = NULL;
6473 dn = dn2;
6476 ALE_SET_DEFN(ale, dn);
6481 if (pn->pn_pos >= root->pn_pos)
6482 AdjustBlockId(pn, adjust, tc);
6483 break;
6485 case PN_NAMESET:
6486 transplant(pn->pn_tree);
6487 break;
6489 return true;
6493 * Starting from a |for| keyword after the first array initialiser element or
6494 * an expression in an open parenthesis, parse the tail of the comprehension
6495 * or generator expression signified by this |for| keyword in context.
6497 * Return null on failure, else return the top-most parse node for the array
6498 * comprehension or generator expression, with a unary node as the body of the
6499 * (possibly nested) for-loop, initialized by |type, op, kid|.
6501 JSParseNode *
6502 Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
6503 TokenKind type, JSOp op)
6505 uintN adjust;
6506 JSParseNode *pn, *pn2, *pn3, **pnp;
6507 JSStmtInfo stmtInfo;
6508 BindData data;
6509 TokenKind tt;
6510 JSAtom *atom;
6512 JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
6514 if (type == TOK_SEMI) {
6516 * Generator expression desugars to an immediately applied lambda that
6517 * yields the next value from a for-in loop (possibly nested, and with
6518 * optional if guard). Make pn be the TOK_LC body node.
6520 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6521 if (!pn)
6522 return NULL;
6523 adjust = pn->pn_blockid - blockid;
6524 } else {
6525 JS_ASSERT(type == TOK_ARRAYPUSH);
6528 * Make a parse-node and literal object representing the block scope of
6529 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6530 * aka the array initialiser case, has passed the blockid to claim for
6531 * the comprehension's block scope. We allocate that id or one above it
6532 * here, by calling js_PushLexicalScope.
6534 * In the case of a comprehension expression that has nested blocks
6535 * (e.g., let expressions), we will allocate a higher blockid but then
6536 * slide all blocks "to the right" to make room for the comprehension's
6537 * block scope.
6539 adjust = tc->blockid();
6540 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6541 if (!pn)
6542 return NULL;
6544 JS_ASSERT(blockid <= pn->pn_blockid);
6545 JS_ASSERT(blockid < tc->blockidGen);
6546 JS_ASSERT(tc->bodyid < blockid);
6547 pn->pn_blockid = stmtInfo.blockid = blockid;
6548 JS_ASSERT(adjust < blockid);
6549 adjust = blockid - adjust;
6552 pnp = &pn->pn_expr;
6554 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6555 transplanter.transplant(kid);
6557 data.pn = NULL;
6558 data.op = JSOP_NOP;
6559 data.binder = BindLet;
6560 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6562 do {
6564 * FOR node is binary, left is loop control and right is body. Use
6565 * index to count each block-local let-variable on the left-hand side
6566 * of the IN.
6568 pn2 = BinaryNode::create(tc);
6569 if (!pn2)
6570 return NULL;
6572 pn2->pn_op = JSOP_ITER;
6573 pn2->pn_iflags = JSITER_ENUMERATE;
6574 if (tokenStream.matchToken(TOK_NAME)) {
6575 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
6576 pn2->pn_iflags |= JSITER_FOREACH;
6577 else
6578 tokenStream.ungetToken();
6580 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6582 atom = NULL;
6583 tt = tokenStream.getToken();
6584 switch (tt) {
6585 #if JS_HAS_DESTRUCTURING
6586 case TOK_LB:
6587 case TOK_LC:
6588 tc->flags |= TCF_DECL_DESTRUCTURING;
6589 pn3 = primaryExpr(tt, JS_FALSE);
6590 tc->flags &= ~TCF_DECL_DESTRUCTURING;
6591 if (!pn3)
6592 return NULL;
6593 break;
6594 #endif
6596 case TOK_NAME:
6597 atom = tokenStream.currentToken().t_atom;
6600 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6601 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6602 * in the operand stack frame. The code generator computes that,
6603 * and it tries to bind all names to slots, so we must let it do
6604 * the deed.
6606 pn3 = NewBindingNode(atom, tc, true);
6607 if (!pn3)
6608 return NULL;
6609 break;
6611 default:
6612 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
6614 case TOK_ERROR:
6615 return NULL;
6618 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6619 JSParseNode *pn4 = expr();
6620 if (!pn4)
6621 return NULL;
6622 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6624 switch (tt) {
6625 #if JS_HAS_DESTRUCTURING
6626 case TOK_LB:
6627 case TOK_LC:
6628 if (!CheckDestructuring(context, &data, pn3, NULL, tc))
6629 return NULL;
6631 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
6632 /* Destructuring requires [key, value] enumeration in JS1.7. */
6633 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6634 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
6635 return NULL;
6638 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6639 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6640 if (!(pn2->pn_iflags & JSITER_FOREACH))
6641 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6643 break;
6644 #endif
6646 case TOK_NAME:
6647 data.pn = pn3;
6648 if (!data.binder(context, &data, atom, tc))
6649 return NULL;
6650 break;
6652 default:;
6655 pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6656 if (!pn2->pn_left)
6657 return NULL;
6658 *pnp = pn2;
6659 pnp = &pn2->pn_right;
6660 } while (tokenStream.matchToken(TOK_FOR));
6662 if (tokenStream.matchToken(TOK_IF)) {
6663 pn2 = TernaryNode::create(tc);
6664 if (!pn2)
6665 return NULL;
6666 pn2->pn_kid1 = condition();
6667 if (!pn2->pn_kid1)
6668 return NULL;
6669 *pnp = pn2;
6670 pnp = &pn2->pn_kid2;
6673 pn2 = UnaryNode::create(tc);
6674 if (!pn2)
6675 return NULL;
6676 pn2->pn_type = type;
6677 pn2->pn_op = op;
6678 pn2->pn_kid = kid;
6679 *pnp = pn2;
6681 PopStatement(tc);
6682 return pn;
6685 #if JS_HAS_GENERATOR_EXPRS
6688 * Starting from a |for| keyword after an expression, parse the comprehension
6689 * tail completing this generator expression. Wrap the expression at kid in a
6690 * generator function that is immediately called to evaluate to the generator
6691 * iterator that is the value of this generator expression.
6693 * Callers pass a blank unary node via pn, which generatorExpr fills in as the
6694 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6695 * expression-statement node that constitutes the body of the |for| loop(s) in
6696 * the generator function.
6698 * Note how unlike Python, we do not evaluate the expression to the right of
6699 * the first |in| in the chain of |for| heads. Instead, a generator expression
6700 * is merely sugar for a generator function expression and its application.
6702 JSParseNode *
6703 Parser::generatorExpr(JSParseNode *pn, JSParseNode *kid)
6705 /* Initialize pn, connecting it to kid. */
6706 JS_ASSERT(pn->pn_arity == PN_UNARY);
6707 pn->pn_type = TOK_YIELD;
6708 pn->pn_op = JSOP_YIELD;
6709 pn->pn_parens = true;
6710 pn->pn_pos = kid->pn_pos;
6711 pn->pn_kid = kid;
6712 pn->pn_hidden = true;
6714 /* Make a new node for the desugared generator function. */
6715 JSParseNode *genfn = FunctionNode::create(tc);
6716 if (!genfn)
6717 return NULL;
6718 genfn->pn_type = TOK_FUNCTION;
6719 genfn->pn_op = JSOP_LAMBDA;
6720 JS_ASSERT(!genfn->pn_body);
6721 genfn->pn_dflags = PND_FUNARG;
6724 JSTreeContext *outertc = tc;
6725 JSTreeContext gentc(tc->parser);
6727 JSFunctionBox *funbox = EnterFunction(genfn, &gentc);
6728 if (!funbox)
6729 return NULL;
6732 * We have to dance around a bit to propagate sharp variables from
6733 * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
6734 * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
6735 * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
6736 * do propagate to gentc.
6738 if (outertc->flags & TCF_HAS_SHARPS) {
6739 gentc.flags |= TCF_IN_FUNCTION;
6740 if (!gentc.ensureSharpSlots())
6741 return NULL;
6745 * We assume conservatively that any deoptimization flag in tc->flags
6746 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6747 * propagate these flags into genfn. For code simplicity we also do
6748 * not detect if the flags were only set in the kid and could be
6749 * removed from tc->flags.
6751 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6752 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6753 funbox->tcflags |= gentc.flags;
6754 genfn->pn_funbox = funbox;
6755 genfn->pn_blockid = gentc.bodyid;
6757 JSParseNode *body = comprehensionTail(pn, outertc->blockid());
6758 if (!body)
6759 return NULL;
6760 JS_ASSERT(!genfn->pn_body);
6761 genfn->pn_body = body;
6762 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6763 genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
6765 if (!LeaveFunction(genfn, &gentc))
6766 return NULL;
6770 * Our result is a call expression that invokes the anonymous generator
6771 * function object.
6773 JSParseNode *result = ListNode::create(tc);
6774 if (!result)
6775 return NULL;
6776 result->pn_type = TOK_LP;
6777 result->pn_op = JSOP_CALL;
6778 result->pn_pos.begin = genfn->pn_pos.begin;
6779 result->initList(genfn);
6780 return result;
6783 static const char js_generator_str[] = "generator";
6785 #endif /* JS_HAS_GENERATOR_EXPRS */
6786 #endif /* JS_HAS_GENERATORS */
6788 JSBool
6789 Parser::argumentList(JSParseNode *listNode)
6791 if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
6792 return JS_TRUE;
6794 do {
6795 JSParseNode *argNode = assignExpr();
6796 if (!argNode)
6797 return JS_FALSE;
6798 #if JS_HAS_GENERATORS
6799 if (argNode->pn_type == TOK_YIELD &&
6800 !argNode->pn_parens &&
6801 tokenStream.peekToken() == TOK_COMMA) {
6802 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
6803 return JS_FALSE;
6805 #endif
6806 #if JS_HAS_GENERATOR_EXPRS
6807 if (tokenStream.matchToken(TOK_FOR)) {
6808 JSParseNode *pn = UnaryNode::create(tc);
6809 if (!pn)
6810 return JS_FALSE;
6811 argNode = generatorExpr(pn, argNode);
6812 if (!argNode)
6813 return JS_FALSE;
6814 if (listNode->pn_count > 1 ||
6815 tokenStream.peekToken() == TOK_COMMA) {
6816 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
6817 js_generator_str);
6818 return JS_FALSE;
6821 #endif
6822 listNode->append(argNode);
6823 } while (tokenStream.matchToken(TOK_COMMA));
6825 if (tokenStream.getToken() != TOK_RP) {
6826 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
6827 return JS_FALSE;
6829 return JS_TRUE;
6832 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6833 static JSParseNode *
6834 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6836 if (pn->pn_type == TOK_FUNCTION) {
6837 JS_ASSERT(pn->pn_arity == PN_FUNC);
6839 JSFunctionBox *funbox = pn->pn_funbox;
6840 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6841 if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
6842 pn->pn_dflags &= ~PND_FUNARG;
6844 return pn;
6847 JSParseNode *
6848 Parser::memberExpr(JSBool allowCallSyntax)
6850 JSParseNode *pn, *pn2, *pn3;
6852 JS_CHECK_RECURSION(context, return NULL);
6854 /* Check for new expression first. */
6855 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6856 if (tt == TOK_NEW) {
6857 pn = ListNode::create(tc);
6858 if (!pn)
6859 return NULL;
6860 pn2 = memberExpr(JS_FALSE);
6861 if (!pn2)
6862 return NULL;
6863 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6864 pn->pn_op = JSOP_NEW;
6865 pn->initList(pn2);
6866 pn->pn_pos.begin = pn2->pn_pos.begin;
6868 if (tokenStream.matchToken(TOK_LP) && !argumentList(pn))
6869 return NULL;
6870 if (pn->pn_count > ARGC_LIMIT) {
6871 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
6872 JSMSG_TOO_MANY_CON_ARGS);
6873 return NULL;
6875 pn->pn_pos.end = pn->last()->pn_pos.end;
6876 } else {
6877 pn = primaryExpr(tt, JS_FALSE);
6878 if (!pn)
6879 return NULL;
6881 if (pn->pn_type == TOK_ANYNAME ||
6882 pn->pn_type == TOK_AT ||
6883 pn->pn_type == TOK_DBLCOLON) {
6884 pn2 = NewOrRecycledNode(tc);
6885 if (!pn2)
6886 return NULL;
6887 pn2->pn_type = TOK_UNARYOP;
6888 pn2->pn_pos = pn->pn_pos;
6889 pn2->pn_op = JSOP_XMLNAME;
6890 pn2->pn_arity = PN_UNARY;
6891 pn2->pn_parens = false;
6892 pn2->pn_kid = pn;
6893 pn = pn2;
6897 while ((tt = tokenStream.getToken()) > TOK_EOF) {
6898 if (tt == TOK_DOT) {
6899 pn2 = NameNode::create(NULL, tc);
6900 if (!pn2)
6901 return NULL;
6902 #if JS_HAS_XML_SUPPORT
6903 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6904 pn3 = primaryExpr(tt, JS_TRUE);
6905 if (!pn3)
6906 return NULL;
6908 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6909 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
6910 pn2->pn_op = JSOP_GETPROP;
6911 pn2->pn_expr = pn;
6912 pn2->pn_atom = pn3->pn_atom;
6913 RecycleTree(pn3, tc);
6914 } else {
6915 if (tt == TOK_LP) {
6916 pn2->pn_type = TOK_FILTER;
6917 pn2->pn_op = JSOP_FILTER;
6919 /* A filtering predicate is like a with statement. */
6920 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6921 } else if (TokenKindIsXML(PN_TYPE(pn3))) {
6922 pn2->pn_type = TOK_LB;
6923 pn2->pn_op = JSOP_GETELEM;
6924 } else {
6925 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
6926 return NULL;
6928 pn2->pn_arity = PN_BINARY;
6929 pn2->pn_left = pn;
6930 pn2->pn_right = pn3;
6932 #else
6933 MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME, JSMSG_NAME_AFTER_DOT, TSF_KEYWORD_IS_NAME);
6934 pn2->pn_op = JSOP_GETPROP;
6935 pn2->pn_expr = pn;
6936 pn2->pn_atom = tokenStream.currentToken().t_atom;
6937 #endif
6938 pn2->pn_pos.begin = pn->pn_pos.begin;
6939 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6940 #if JS_HAS_XML_SUPPORT
6941 } else if (tt == TOK_DBLDOT) {
6942 pn2 = BinaryNode::create(tc);
6943 if (!pn2)
6944 return NULL;
6945 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6946 pn3 = primaryExpr(tt, JS_TRUE);
6947 if (!pn3)
6948 return NULL;
6949 tt = PN_TYPE(pn3);
6950 if (tt == TOK_NAME && !pn3->pn_parens) {
6951 pn3->pn_type = TOK_STRING;
6952 pn3->pn_arity = PN_NULLARY;
6953 pn3->pn_op = JSOP_QNAMEPART;
6954 } else if (!TokenKindIsXML(tt)) {
6955 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
6956 return NULL;
6958 pn2->pn_op = JSOP_DESCENDANTS;
6959 pn2->pn_left = pn;
6960 pn2->pn_right = pn3;
6961 pn2->pn_pos.begin = pn->pn_pos.begin;
6962 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6963 #endif
6964 } else if (tt == TOK_LB) {
6965 pn2 = BinaryNode::create(tc);
6966 if (!pn2)
6967 return NULL;
6968 pn3 = expr();
6969 if (!pn3)
6970 return NULL;
6972 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
6973 pn2->pn_pos.begin = pn->pn_pos.begin;
6974 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6977 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6978 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6979 * the interpreter from fast property access. However, if the
6980 * bracketed string is a uint32, we rewrite pn3 to be a number
6981 * instead of a string.
6983 do {
6984 if (pn3->pn_type == TOK_STRING) {
6985 jsuint index;
6987 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
6988 pn2->pn_type = TOK_DOT;
6989 pn2->pn_op = JSOP_GETPROP;
6990 pn2->pn_arity = PN_NAME;
6991 pn2->pn_expr = pn;
6992 pn2->pn_atom = pn3->pn_atom;
6993 break;
6995 pn3->pn_type = TOK_NUMBER;
6996 pn3->pn_op = JSOP_DOUBLE;
6997 pn3->pn_dval = index;
6999 pn2->pn_op = JSOP_GETELEM;
7000 pn2->pn_left = pn;
7001 pn2->pn_right = pn3;
7002 } while (0);
7003 } else if (allowCallSyntax && tt == TOK_LP) {
7004 pn2 = ListNode::create(tc);
7005 if (!pn2)
7006 return NULL;
7007 pn2->pn_op = JSOP_CALL;
7009 pn = CheckForImmediatelyAppliedLambda(pn);
7010 if (pn->pn_op == JSOP_NAME) {
7011 if (pn->pn_atom == context->runtime->atomState.evalAtom) {
7012 /* Select JSOP_EVAL and flag tc as heavyweight. */
7013 pn2->pn_op = JSOP_EVAL;
7014 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7016 } else if (pn->pn_op == JSOP_GETPROP) {
7017 if (pn->pn_atom == context->runtime->atomState.applyAtom ||
7018 pn->pn_atom == context->runtime->atomState.callAtom) {
7019 /* Select JSOP_APPLY given foo.apply(...). */
7020 pn2->pn_op = JSOP_APPLY;
7024 pn2->initList(pn);
7025 pn2->pn_pos.begin = pn->pn_pos.begin;
7027 if (!argumentList(pn2))
7028 return NULL;
7029 if (pn2->pn_count > ARGC_LIMIT) {
7030 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7031 JSMSG_TOO_MANY_FUN_ARGS);
7032 return NULL;
7034 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7035 } else {
7036 tokenStream.ungetToken();
7037 return pn;
7040 pn = pn2;
7042 if (tt == TOK_ERROR)
7043 return NULL;
7044 return pn;
7047 JSParseNode *
7048 Parser::bracketedExpr()
7050 uintN oldflags;
7051 JSParseNode *pn;
7054 * Always accept the 'in' operator in a parenthesized expression,
7055 * where it's unambiguous, even if we might be parsing the init of a
7056 * for statement.
7058 oldflags = tc->flags;
7059 tc->flags &= ~TCF_IN_FOR_INIT;
7060 pn = expr();
7061 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7062 return pn;
7065 #if JS_HAS_XML_SUPPORT
7067 JSParseNode *
7068 Parser::endBracketedExpr()
7070 JSParseNode *pn;
7072 pn = bracketedExpr();
7073 if (!pn)
7074 return NULL;
7076 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7077 return pn;
7081 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7083 * AttributeIdentifier:
7084 * @ PropertySelector
7085 * @ QualifiedIdentifier
7086 * @ [ Expression ]
7088 * PropertySelector:
7089 * Identifier
7092 * QualifiedIdentifier:
7093 * PropertySelector :: PropertySelector
7094 * PropertySelector :: [ Expression ]
7096 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7098 * AttributeIdentifier:
7099 * @ QualifiedIdentifier
7100 * @ [ Expression ]
7102 * PropertySelector:
7103 * Identifier
7106 * QualifiedIdentifier:
7107 * PropertySelector :: PropertySelector
7108 * PropertySelector :: [ Expression ]
7109 * PropertySelector
7111 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7112 * for that rule to result in a name node, but ECMA-357 extends the grammar
7113 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7115 * QualifiedIdentifier:
7116 * PropertySelector QualifiedSuffix
7118 * QualifiedSuffix:
7119 * :: PropertySelector
7120 * :: [ Expression ]
7121 * /nothing/
7123 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7125 * PrimaryExpression:
7126 * Identifier QualifiedSuffix
7128 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7129 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7131 JSParseNode *
7132 Parser::propertySelector()
7134 JSParseNode *pn;
7136 pn = NullaryNode::create(tc);
7137 if (!pn)
7138 return NULL;
7139 if (pn->pn_type == TOK_STAR) {
7140 pn->pn_type = TOK_ANYNAME;
7141 pn->pn_op = JSOP_ANYNAME;
7142 pn->pn_atom = context->runtime->atomState.starAtom;
7143 } else {
7144 JS_ASSERT(pn->pn_type == TOK_NAME);
7145 pn->pn_op = JSOP_QNAMEPART;
7146 pn->pn_arity = PN_NAME;
7147 pn->pn_atom = tokenStream.currentToken().t_atom;
7148 pn->pn_cookie = FREE_UPVAR_COOKIE;
7150 return pn;
7153 JSParseNode *
7154 Parser::qualifiedSuffix(JSParseNode *pn)
7156 JSParseNode *pn2, *pn3;
7157 TokenKind tt;
7159 JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
7160 pn2 = NameNode::create(NULL, tc);
7161 if (!pn2)
7162 return NULL;
7164 /* Left operand of :: must be evaluated if it is an identifier. */
7165 if (pn->pn_op == JSOP_QNAMEPART)
7166 pn->pn_op = JSOP_NAME;
7168 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7169 if (tt == TOK_STAR || tt == TOK_NAME) {
7170 /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7171 pn2->pn_op = JSOP_QNAMECONST;
7172 pn2->pn_pos.begin = pn->pn_pos.begin;
7173 pn2->pn_atom = (tt == TOK_STAR)
7174 ? context->runtime->atomState.starAtom
7175 : tokenStream.currentToken().t_atom;
7176 pn2->pn_expr = pn;
7177 pn2->pn_cookie = FREE_UPVAR_COOKIE;
7178 return pn2;
7181 if (tt != TOK_LB) {
7182 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7183 return NULL;
7185 pn3 = endBracketedExpr();
7186 if (!pn3)
7187 return NULL;
7189 pn2->pn_op = JSOP_QNAME;
7190 pn2->pn_arity = PN_BINARY;
7191 pn2->pn_pos.begin = pn->pn_pos.begin;
7192 pn2->pn_pos.end = pn3->pn_pos.end;
7193 pn2->pn_left = pn;
7194 pn2->pn_right = pn3;
7195 return pn2;
7198 JSParseNode *
7199 Parser::qualifiedIdentifier()
7201 JSParseNode *pn;
7203 pn = propertySelector();
7204 if (!pn)
7205 return NULL;
7206 if (tokenStream.matchToken(TOK_DBLCOLON)) {
7207 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7208 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7209 pn = qualifiedSuffix(pn);
7211 return pn;
7214 JSParseNode *
7215 Parser::attributeIdentifier()
7217 JSParseNode *pn, *pn2;
7218 TokenKind tt;
7220 JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
7221 pn = UnaryNode::create(tc);
7222 if (!pn)
7223 return NULL;
7224 pn->pn_op = JSOP_TOATTRNAME;
7225 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7226 if (tt == TOK_STAR || tt == TOK_NAME) {
7227 pn2 = qualifiedIdentifier();
7228 } else if (tt == TOK_LB) {
7229 pn2 = endBracketedExpr();
7230 } else {
7231 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7232 return NULL;
7234 if (!pn2)
7235 return NULL;
7236 pn->pn_kid = pn2;
7237 return pn;
7241 * Make a TOK_LC unary node whose pn_kid is an expression.
7243 JSParseNode *
7244 Parser::xmlExpr(JSBool inTag)
7246 JSParseNode *pn, *pn2;
7248 JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
7249 pn = UnaryNode::create(tc);
7250 if (!pn)
7251 return NULL;
7254 * Turn off XML tag mode. We save the old value of the flag because it may
7255 * already be off: XMLExpr is called both from within a tag, and from
7256 * within text contained in an element, but outside of any start, end, or
7257 * point tag.
7259 bool oldflag = tokenStream.isXMLTagMode();
7260 tokenStream.setXMLTagMode(false);
7261 pn2 = expr();
7262 if (!pn2)
7263 return NULL;
7265 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7266 tokenStream.setXMLTagMode(oldflag);
7267 pn->pn_kid = pn2;
7268 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7269 return pn;
7273 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7274 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7275 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7276 * child of a container tag.
7278 JSParseNode *
7279 Parser::xmlAtomNode()
7281 JSParseNode *pn = NullaryNode::create(tc);
7282 if (!pn)
7283 return NULL;
7284 const Token &tok = tokenStream.currentToken();
7285 pn->pn_op = tok.t_op;
7286 pn->pn_atom = tok.t_atom;
7287 if (tok.type == TOK_XMLPI)
7288 pn->pn_atom2 = tok.t_atom2;
7289 return pn;
7293 * Parse the productions:
7295 * XMLNameExpr:
7296 * XMLName XMLNameExpr?
7297 * { Expr } XMLNameExpr?
7299 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7300 * a list of names and/or expressions, a single expression, or a single name.
7301 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7302 * will be TOK_LC.
7304 JSParseNode *
7305 Parser::xmlNameExpr()
7307 JSParseNode *pn, *pn2, *list;
7308 TokenKind tt;
7310 pn = list = NULL;
7311 do {
7312 tt = tokenStream.currentToken().type;
7313 if (tt == TOK_LC) {
7314 pn2 = xmlExpr(JS_TRUE);
7315 if (!pn2)
7316 return NULL;
7317 } else {
7318 JS_ASSERT(tt == TOK_XMLNAME);
7319 pn2 = xmlAtomNode();
7320 if (!pn2)
7321 return NULL;
7324 if (!pn) {
7325 pn = pn2;
7326 } else {
7327 if (!list) {
7328 list = ListNode::create(tc);
7329 if (!list)
7330 return NULL;
7331 list->pn_type = TOK_XMLNAME;
7332 list->pn_pos.begin = pn->pn_pos.begin;
7333 list->initList(pn);
7334 list->pn_xflags = PNX_CANTFOLD;
7335 pn = list;
7337 pn->pn_pos.end = pn2->pn_pos.end;
7338 pn->append(pn2);
7340 } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
7342 tokenStream.ungetToken();
7343 return pn;
7347 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7348 * at compile time into a JSXML tree.
7350 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7351 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7352 : (pn)->pn_type != TOK_LC)
7355 * Parse the productions:
7357 * XMLTagContent:
7358 * XMLNameExpr
7359 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7360 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7362 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7363 * produces a list of name and attribute values and/or braced expressions, a
7364 * single expression, or a single name.
7366 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7367 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7368 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7369 * we parsed exactly one expression.
7371 JSParseNode *
7372 Parser::xmlTagContent(TokenKind tagtype, JSAtom **namep)
7374 JSParseNode *pn, *pn2, *list;
7375 TokenKind tt;
7377 pn = xmlNameExpr();
7378 if (!pn)
7379 return NULL;
7380 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7381 list = NULL;
7383 while (tokenStream.matchToken(TOK_XMLSPACE)) {
7384 tt = tokenStream.getToken();
7385 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7386 tokenStream.ungetToken();
7387 break;
7390 pn2 = xmlNameExpr();
7391 if (!pn2)
7392 return NULL;
7393 if (!list) {
7394 list = ListNode::create(tc);
7395 if (!list)
7396 return NULL;
7397 list->pn_type = tagtype;
7398 list->pn_pos.begin = pn->pn_pos.begin;
7399 list->initList(pn);
7400 pn = list;
7402 pn->append(pn2);
7403 if (!XML_FOLDABLE(pn2))
7404 pn->pn_xflags |= PNX_CANTFOLD;
7406 tokenStream.matchToken(TOK_XMLSPACE);
7407 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7408 tokenStream.matchToken(TOK_XMLSPACE);
7410 tt = tokenStream.getToken();
7411 if (tt == TOK_XMLATTR) {
7412 pn2 = xmlAtomNode();
7413 } else if (tt == TOK_LC) {
7414 pn2 = xmlExpr(JS_TRUE);
7415 pn->pn_xflags |= PNX_CANTFOLD;
7416 } else {
7417 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE);
7418 return NULL;
7420 if (!pn2)
7421 return NULL;
7422 pn->pn_pos.end = pn2->pn_pos.end;
7423 pn->append(pn2);
7426 return pn;
7429 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7430 JS_BEGIN_MACRO \
7431 if ((tt) <= TOK_EOF) { \
7432 if ((tt) == TOK_EOF) { \
7433 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_END_OF_XML_SOURCE); \
7435 return result; \
7437 JS_END_MACRO
7440 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7441 * that opens the end tag for the container.
7443 JSBool
7444 Parser::xmlElementContent(JSParseNode *pn)
7446 tokenStream.setXMLTagMode(false);
7447 for (;;) {
7448 TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
7449 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7451 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7452 JSAtom *textAtom = tokenStream.currentToken().t_atom;
7453 if (textAtom) {
7454 /* Non-zero-length XML text scanned. */
7455 JSParseNode *pn2 = xmlAtomNode();
7456 if (!pn2)
7457 return JS_FALSE;
7458 pn->pn_pos.end = pn2->pn_pos.end;
7459 pn->append(pn2);
7462 tt = tokenStream.getToken(TSF_OPERAND);
7463 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7464 if (tt == TOK_XMLETAGO)
7465 break;
7467 JSParseNode *pn2;
7468 if (tt == TOK_LC) {
7469 pn2 = xmlExpr(JS_FALSE);
7470 pn->pn_xflags |= PNX_CANTFOLD;
7471 } else if (tt == TOK_XMLSTAGO) {
7472 pn2 = xmlElementOrList(JS_FALSE);
7473 if (pn2) {
7474 pn2->pn_xflags &= ~PNX_XMLROOT;
7475 pn->pn_xflags |= pn2->pn_xflags;
7477 } else {
7478 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7479 tt == TOK_XMLPI);
7480 pn2 = xmlAtomNode();
7482 if (!pn2)
7483 return JS_FALSE;
7484 pn->pn_pos.end = pn2->pn_pos.end;
7485 pn->append(pn2);
7487 tokenStream.setXMLTagMode(true);
7489 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
7490 return JS_TRUE;
7494 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7496 JSParseNode *
7497 Parser::xmlElementOrList(JSBool allowList)
7499 JSParseNode *pn, *pn2, *list;
7500 TokenKind tt;
7501 JSAtom *startAtom, *endAtom;
7503 JS_CHECK_RECURSION(context, return NULL);
7505 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
7506 pn = ListNode::create(tc);
7507 if (!pn)
7508 return NULL;
7510 tokenStream.setXMLTagMode(true);
7511 tt = tokenStream.getToken();
7512 if (tt == TOK_ERROR)
7513 return NULL;
7515 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7517 * XMLElement. Append the tag and its contents, if any, to pn.
7519 pn2 = xmlTagContent(TOK_XMLSTAGO, &startAtom);
7520 if (!pn2)
7521 return NULL;
7522 tokenStream.matchToken(TOK_XMLSPACE);
7524 tt = tokenStream.getToken();
7525 if (tt == TOK_XMLPTAGC) {
7526 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7527 if (pn2->pn_type == TOK_XMLSTAGO) {
7528 pn->makeEmpty();
7529 RecycleTree(pn, tc);
7530 pn = pn2;
7531 } else {
7532 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7533 pn2->pn_type == TOK_LC);
7534 pn->initList(pn2);
7535 if (!XML_FOLDABLE(pn2))
7536 pn->pn_xflags |= PNX_CANTFOLD;
7538 pn->pn_type = TOK_XMLPTAGC;
7539 pn->pn_xflags |= PNX_XMLROOT;
7540 } else {
7541 /* We had better have a tag-close (>) at this point. */
7542 if (tt != TOK_XMLTAGC) {
7543 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7544 return NULL;
7546 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7548 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7549 if (pn2->pn_type != TOK_XMLSTAGO) {
7550 pn->initList(pn2);
7551 if (!XML_FOLDABLE(pn2))
7552 pn->pn_xflags |= PNX_CANTFOLD;
7553 pn2 = pn;
7554 pn = ListNode::create(tc);
7555 if (!pn)
7556 return NULL;
7559 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7560 pn->pn_type = TOK_XMLELEM;
7561 pn->pn_pos.begin = pn2->pn_pos.begin;
7562 pn->initList(pn2);
7563 if (!XML_FOLDABLE(pn2))
7564 pn->pn_xflags |= PNX_CANTFOLD;
7565 pn->pn_xflags |= PNX_XMLROOT;
7567 /* Get element contents and delimiting end-tag-open sequence. */
7568 if (!xmlElementContent(pn))
7569 return NULL;
7571 tt = tokenStream.getToken();
7572 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7573 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7574 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7575 return NULL;
7578 /* Parse end tag; check mismatch at compile-time if we can. */
7579 pn2 = xmlTagContent(TOK_XMLETAGO, &endAtom);
7580 if (!pn2)
7581 return NULL;
7582 if (pn2->pn_type == TOK_XMLETAGO) {
7583 /* Oops, end tag has attributes! */
7584 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7585 return NULL;
7587 if (endAtom && startAtom && endAtom != startAtom) {
7588 JSString *str = ATOM_TO_STRING(startAtom);
7590 /* End vs. start tag name mismatch: point to the tag name. */
7591 reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
7592 str->chars());
7593 return NULL;
7596 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7597 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7598 list = ListNode::create(tc);
7599 if (!list)
7600 return NULL;
7601 list->pn_type = TOK_XMLETAGO;
7602 list->initList(pn2);
7603 pn->append(list);
7604 if (!XML_FOLDABLE(pn2)) {
7605 list->pn_xflags |= PNX_CANTFOLD;
7606 pn->pn_xflags |= PNX_CANTFOLD;
7609 tokenStream.matchToken(TOK_XMLSPACE);
7610 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7613 /* Set pn_op now that pn has been updated to its final value. */
7614 pn->pn_op = JSOP_TOXML;
7615 } else if (allowList && tt == TOK_XMLTAGC) {
7616 /* XMLList Initialiser. */
7617 pn->pn_type = TOK_XMLLIST;
7618 pn->pn_op = JSOP_TOXMLLIST;
7619 pn->makeEmpty();
7620 pn->pn_xflags |= PNX_XMLROOT;
7621 if (!xmlElementContent(pn))
7622 return NULL;
7624 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7625 } else {
7626 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX);
7627 return NULL;
7629 tokenStream.setXMLTagMode(false);
7631 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7632 return pn;
7635 JSParseNode *
7636 Parser::xmlElementOrListRoot(JSBool allowList)
7638 uint32 oldopts;
7639 JSParseNode *pn;
7642 * Force XML support to be enabled so that comments and CDATA literals
7643 * are recognized, instead of <! followed by -- starting an HTML comment
7644 * to end of line (used in script tags to hide content from old browsers
7645 * that don't recognize <script>).
7647 oldopts = JS_SetOptions(context, context->options | JSOPTION_XML);
7648 pn = xmlElementOrList(allowList);
7649 JS_SetOptions(context, oldopts);
7650 return pn;
7653 JSParseNode *
7654 Parser::parseXMLText(JSObject *chain, bool allowList)
7657 * Push a compiler frame if we have no frames, or if the top frame is a
7658 * lightweight function activation, or if its scope chain doesn't match
7659 * the one passed to us.
7661 JSTreeContext xmltc(this);
7662 xmltc.scopeChain = chain;
7664 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7665 tokenStream.setXMLOnlyMode();
7666 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
7668 JSParseNode *pn;
7669 if (tt != TOK_XMLSTAGO) {
7670 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
7671 pn = NULL;
7672 } else {
7673 pn = xmlElementOrListRoot(allowList);
7675 tokenStream.setXMLOnlyMode(false);
7677 return pn;
7680 #endif /* JS_HAS_XMLSUPPORT */
7682 #if JS_HAS_BLOCK_SCOPE
7684 * Check whether blockid is an active scoping statement in tc. This code is
7685 * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
7686 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7687 * and let blocks and expressions (not let declarations).
7689 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7690 * due to hoisting, let in a for loop head, let block, or let expression acts
7691 * like Scheme's let: initializers are evaluated without the new let bindings
7692 * being in scope.
7694 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7695 * bindings push on the front of the tc->decls JSAtomList (either the singular
7696 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7697 * scope bindings of the same name.
7699 * This simplifies binding lookup code at the price of a linear search here,
7700 * but only if code uses let (var predominates), and even then this function's
7701 * loop iterates more than once only in crazy cases.
7703 static inline bool
7704 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7706 if (blockid > tc->blockid())
7707 return false;
7708 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7709 if (stmt->blockid == blockid)
7710 return true;
7712 return false;
7714 #endif
7716 JSParseNode *
7717 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
7719 JSParseNode *pn, *pn2, *pn3;
7720 JSOp op;
7722 JS_CHECK_RECURSION(context, return NULL);
7724 switch (tt) {
7725 case TOK_FUNCTION:
7726 #if JS_HAS_XML_SUPPORT
7727 if (tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
7728 pn2 = NullaryNode::create(tc);
7729 if (!pn2)
7730 return NULL;
7731 pn2->pn_type = TOK_FUNCTION;
7732 pn = qualifiedSuffix(pn2);
7733 if (!pn)
7734 return NULL;
7735 break;
7737 #endif
7738 pn = functionExpr();
7739 if (!pn)
7740 return NULL;
7741 break;
7743 case TOK_LB:
7745 JSBool matched;
7746 jsuint index;
7748 pn = ListNode::create(tc);
7749 if (!pn)
7750 return NULL;
7751 pn->pn_type = TOK_RB;
7752 pn->pn_op = JSOP_NEWINIT;
7753 pn->makeEmpty();
7755 #if JS_HAS_GENERATORS
7756 pn->pn_blockid = tc->blockidGen;
7757 #endif
7759 matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
7760 if (!matched) {
7761 for (index = 0; ; index++) {
7762 if (index == JS_ARGS_LENGTH_MAX) {
7763 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
7764 return NULL;
7767 tt = tokenStream.peekToken(TSF_OPERAND);
7768 if (tt == TOK_RB) {
7769 pn->pn_xflags |= PNX_ENDCOMMA;
7770 break;
7773 if (tt == TOK_COMMA) {
7774 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7775 tokenStream.matchToken(TOK_COMMA);
7776 pn2 = NullaryNode::create(tc);
7777 pn->pn_xflags |= PNX_HOLEY;
7778 } else {
7779 pn2 = assignExpr();
7781 if (!pn2)
7782 return NULL;
7783 pn->append(pn2);
7785 if (tt != TOK_COMMA) {
7786 /* If we didn't already match TOK_COMMA in above case. */
7787 if (!tokenStream.matchToken(TOK_COMMA))
7788 break;
7792 #if JS_HAS_GENERATORS
7794 * At this point, (index == 0 && pn->pn_count != 0) implies one
7795 * element initialiser was parsed.
7797 * An array comprehension of the form:
7799 * [i * j for (i in o) for (j in p) if (i != j)]
7801 * translates to roughly the following let expression:
7803 * let (array = new Array, i, j) {
7804 * for (i in o) let {
7805 * for (j in p)
7806 * if (i != j)
7807 * array.push(i * j)
7809 * array
7812 * where array is a nameless block-local variable. The "roughly"
7813 * means that an implementation may optimize away the array.push.
7814 * An array comprehension opens exactly one block scope, no matter
7815 * how many for heads it contains.
7817 * Each let () {...} or for (let ...) ... compiles to:
7819 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7821 * where <o> is a literal object representing the block scope,
7822 * with <n> properties, naming each var declared in the block.
7824 * Each var declaration in a let-block binds a name in <o> at
7825 * compile time, and allocates a slot on the operand stack at
7826 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7827 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7828 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7829 * local slot's stack index from fp->spbase.
7831 * The array comprehension iteration step, array.push(i * j) in
7832 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7833 * where <array> is the index of array's stack slot.
7835 if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
7836 JSParseNode *pnexp, *pntop;
7838 /* Relabel pn as an array comprehension node. */
7839 pn->pn_type = TOK_ARRAYCOMP;
7842 * Remove the comprehension expression from pn's linked list
7843 * and save it via pnexp. We'll re-install it underneath the
7844 * ARRAYPUSH node after we parse the rest of the comprehension.
7846 pnexp = pn->last();
7847 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7848 pn->pn_tail = (--pn->pn_count == 1)
7849 ? &pn->pn_head->pn_next
7850 : &pn->pn_head;
7851 *pn->pn_tail = NULL;
7853 pntop = comprehensionTail(pnexp, pn->pn_blockid,
7854 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7855 if (!pntop)
7856 return NULL;
7857 pn->append(pntop);
7859 #endif /* JS_HAS_GENERATORS */
7861 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7863 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7864 return pn;
7867 case TOK_LC:
7869 JSBool afterComma;
7870 JSParseNode *pnval;
7873 * A map from property names we've seen thus far to bit masks.
7874 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
7875 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
7876 * if we've seen as getter, and both of those if we've just
7877 * seen an ordinary value.
7879 JSAutoAtomList seen(tc->parser);
7881 pn = ListNode::create(tc);
7882 if (!pn)
7883 return NULL;
7884 pn->pn_type = TOK_RC;
7885 pn->pn_op = JSOP_NEWINIT;
7886 pn->makeEmpty();
7888 afterComma = JS_FALSE;
7889 for (;;) {
7890 JSAtom *atom;
7891 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7892 switch (tt) {
7893 case TOK_NUMBER:
7894 pn3 = NullaryNode::create(tc);
7895 if (!pn3)
7896 return NULL;
7897 pn3->pn_dval = tokenStream.currentToken().t_dval;
7898 if (tc->needStrictChecks()) {
7899 atom = js_AtomizeDouble(context, pn3->pn_dval);
7900 if (!atom)
7901 return NULL;
7902 } else {
7903 atom = NULL; /* for the compiler */
7905 break;
7906 case TOK_NAME:
7908 atom = tokenStream.currentToken().t_atom;
7909 if (atom == context->runtime->atomState.getAtom)
7910 op = JSOP_GETTER;
7911 else if (atom == context->runtime->atomState.setAtom)
7912 op = JSOP_SETTER;
7913 else
7914 goto property_name;
7916 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7917 if (tt == TOK_NAME || tt == TOK_STRING) {
7918 atom = tokenStream.currentToken().t_atom;
7919 pn3 = NameNode::create(atom, tc);
7920 if (!pn3)
7921 return NULL;
7922 } else if (tt == TOK_NUMBER) {
7923 pn3 = NullaryNode::create(tc);
7924 if (!pn3)
7925 return NULL;
7926 pn3->pn_dval = tokenStream.currentToken().t_dval;
7927 if (tc->needStrictChecks()) {
7928 atom = js_AtomizeDouble(context, pn3->pn_dval);
7929 if (!atom)
7930 return NULL;
7931 } else {
7932 atom = NULL; /* for the compiler */
7934 } else {
7935 tokenStream.ungetToken();
7936 goto property_name;
7939 /* We have to fake a 'function' token here. */
7940 tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
7941 pn2 = functionDef(JSFUN_LAMBDA, false);
7942 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
7943 goto skip;
7945 property_name:
7946 case TOK_STRING:
7947 atom = tokenStream.currentToken().t_atom;
7948 pn3 = NullaryNode::create(tc);
7949 if (!pn3)
7950 return NULL;
7951 pn3->pn_atom = atom;
7952 break;
7953 case TOK_RC:
7954 goto end_obj_init;
7955 default:
7956 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_PROP_ID);
7957 return NULL;
7960 op = JSOP_INITPROP;
7961 tt = tokenStream.getToken();
7962 if (tt == TOK_COLON) {
7963 pnval = assignExpr();
7964 } else {
7965 #if JS_HAS_DESTRUCTURING_SHORTHAND
7966 if (tt != TOK_COMMA && tt != TOK_RC) {
7967 #endif
7968 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_COLON_AFTER_ID);
7969 return NULL;
7970 #if JS_HAS_DESTRUCTURING_SHORTHAND
7974 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7975 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7977 tokenStream.ungetToken();
7978 pn->pn_xflags |= PNX_DESTRUCT;
7979 pnval = pn3;
7980 if (pnval->pn_type == TOK_NAME) {
7981 pnval->pn_arity = PN_NAME;
7982 ((NameNode *)pnval)->initCommon(tc);
7984 #endif
7987 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
7988 skip:
7989 if (!pn2)
7990 return NULL;
7991 pn->append(pn2);
7994 * In strict mode code, check for duplicate property names. Treat
7995 * getters and setters as distinct attributes of each property. A
7996 * plain old value conflicts with a getter or a setter.
7998 if (tc->needStrictChecks()) {
7999 unsigned attributesMask;
8000 if (op == JSOP_INITPROP) {
8001 attributesMask = JSPROP_GETTER | JSPROP_SETTER;
8002 } else if (op == JSOP_GETTER) {
8003 attributesMask = JSPROP_GETTER;
8004 } else if (op == JSOP_SETTER) {
8005 attributesMask = JSPROP_SETTER;
8006 } else {
8007 JS_NOT_REACHED("bad opcode in object initializer");
8008 attributesMask = 0;
8012 * Use only string-valued atoms for detecting duplicate
8013 * properties so that 1 and "1" properly collide.
8015 if (ATOM_IS_DOUBLE(atom)) {
8016 JSString *str = js_NumberToString(context, pn3->pn_dval);
8017 if (!str)
8018 return JS_FALSE;
8019 atom = js_AtomizeString(context, str, 0);
8020 if (!atom)
8021 return JS_FALSE;
8024 JSAtomListElement *ale = seen.lookup(atom);
8025 if (ale) {
8026 if (ALE_INDEX(ale) & attributesMask) {
8027 const char *name = js_AtomToPrintableString(context, atom);
8028 if (!name ||
8029 !ReportStrictModeError(context, &tokenStream, tc, NULL,
8030 JSMSG_DUPLICATE_PROPERTY, name)) {
8031 return NULL;
8034 ALE_SET_INDEX(ale, attributesMask | ALE_INDEX(ale));
8035 } else {
8036 ale = seen.add(tc->parser, atom);
8037 if (!ale)
8038 return NULL;
8039 ALE_SET_INDEX(ale, attributesMask);
8043 tt = tokenStream.getToken();
8044 if (tt == TOK_RC)
8045 goto end_obj_init;
8046 if (tt != TOK_COMMA) {
8047 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
8048 return NULL;
8050 afterComma = JS_TRUE;
8053 end_obj_init:
8054 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8055 return pn;
8058 #if JS_HAS_BLOCK_SCOPE
8059 case TOK_LET:
8060 pn = letBlock(JS_FALSE);
8061 if (!pn)
8062 return NULL;
8063 break;
8064 #endif
8066 #if JS_HAS_SHARP_VARS
8067 case TOK_DEFSHARP:
8068 pn = UnaryNode::create(tc);
8069 if (!pn)
8070 return NULL;
8071 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8072 tt = tokenStream.getToken(TSF_OPERAND);
8073 pn->pn_kid = primaryExpr(tt, JS_FALSE);
8074 if (!pn->pn_kid)
8075 return NULL;
8076 if (PN_TYPE(pn->pn_kid) == TOK_USESHARP ||
8077 PN_TYPE(pn->pn_kid) == TOK_DEFSHARP ||
8078 PN_TYPE(pn->pn_kid) == TOK_STRING ||
8079 PN_TYPE(pn->pn_kid) == TOK_NUMBER ||
8080 PN_TYPE(pn->pn_kid) == TOK_PRIMARY) {
8081 reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
8082 return NULL;
8084 if (!tc->ensureSharpSlots())
8085 return NULL;
8086 break;
8088 case TOK_USESHARP:
8089 /* Check for forward/dangling references at runtime, to allow eval. */
8090 pn = NullaryNode::create(tc);
8091 if (!pn)
8092 return NULL;
8093 if (!tc->ensureSharpSlots())
8094 return NULL;
8095 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8096 break;
8097 #endif /* JS_HAS_SHARP_VARS */
8099 case TOK_LP:
8101 JSBool genexp;
8103 pn = parenExpr(NULL, &genexp);
8104 if (!pn)
8105 return NULL;
8106 pn->pn_parens = true;
8107 if (!genexp)
8108 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8109 break;
8112 #if JS_HAS_XML_SUPPORT
8113 case TOK_STAR:
8114 pn = qualifiedIdentifier();
8115 if (!pn)
8116 return NULL;
8117 break;
8119 case TOK_AT:
8120 pn = attributeIdentifier();
8121 if (!pn)
8122 return NULL;
8123 break;
8125 case TOK_XMLSTAGO:
8126 pn = xmlElementOrListRoot(JS_TRUE);
8127 if (!pn)
8128 return NULL;
8129 break;
8130 #endif /* JS_HAS_XML_SUPPORT */
8132 case TOK_STRING:
8133 #if JS_HAS_SHARP_VARS
8134 /* FALL THROUGH */
8135 #endif
8137 #if JS_HAS_XML_SUPPORT
8138 case TOK_XMLCDATA:
8139 case TOK_XMLCOMMENT:
8140 case TOK_XMLPI:
8141 #endif
8142 pn = NullaryNode::create(tc);
8143 if (!pn)
8144 return NULL;
8145 pn->pn_atom = tokenStream.currentToken().t_atom;
8146 #if JS_HAS_XML_SUPPORT
8147 if (tt == TOK_XMLPI)
8148 pn->pn_atom2 = tokenStream.currentToken().t_atom2;
8149 else
8150 #endif
8151 pn->pn_op = tokenStream.currentToken().t_op;
8152 break;
8154 case TOK_NAME:
8155 pn = NameNode::create(tokenStream.currentToken().t_atom, tc);
8156 if (!pn)
8157 return NULL;
8158 JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
8159 pn->pn_op = JSOP_NAME;
8161 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8162 pn->pn_atom == context->runtime->atomState.argumentsAtom) {
8164 * Flag arguments usage so we can avoid unsafe optimizations such
8165 * as formal parameter assignment analysis (because of the hated
8166 * feature whereby arguments alias formals). We do this even for
8167 * a reference of the form foo.arguments, which ancient code may
8168 * still use instead of arguments (more hate).
8170 NoteArgumentsUse(tc);
8173 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8174 * to do this work (new rule for the emitter to count on).
8176 if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8177 pn->pn_op = JSOP_ARGUMENTS;
8178 pn->pn_dflags |= PND_BOUND;
8180 } else if ((!afterDot
8181 #if JS_HAS_XML_SUPPORT
8182 || tokenStream.peekToken() == TOK_DBLCOLON
8183 #endif
8184 ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8185 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8186 if (!stmt || stmt->type != STMT_WITH) {
8187 JSDefinition *dn;
8189 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8190 if (ale) {
8191 dn = ALE_DEFN(ale);
8192 #if JS_HAS_BLOCK_SCOPE
8194 * Skip out-of-scope let bindings along an ALE list or hash
8195 * chain. These can happen due to |let (x = x) x| block and
8196 * expression bindings, where the x on the right of = comes
8197 * from an outer scope. See bug 496532.
8199 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8200 do {
8201 ale = ALE_NEXT(ale);
8202 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8203 if (!ale)
8204 break;
8205 dn = ALE_DEFN(ale);
8207 #endif
8210 if (ale) {
8211 dn = ALE_DEFN(ale);
8212 } else {
8213 ale = tc->lexdeps.lookup(pn->pn_atom);
8214 if (ale) {
8215 dn = ALE_DEFN(ale);
8216 } else {
8218 * No definition before this use in any lexical scope.
8219 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8220 * new node for the forward-referenced definition. This
8221 * placeholder definition node will be adopted when we
8222 * parse the real defining declaration form, or left as
8223 * a free variable definition if we never see the real
8224 * definition.
8226 ale = MakePlaceholder(pn, tc);
8227 if (!ale)
8228 return NULL;
8229 dn = ALE_DEFN(ale);
8232 * In case this is a forward reference to a function,
8233 * we pessimistically set PND_FUNARG if the next token
8234 * is not a left parenthesis.
8236 * If the definition eventually parsed into dn is not a
8237 * function, this flag won't hurt, and if we do parse a
8238 * function with pn's name, then the PND_FUNARG flag is
8239 * necessary for safe context->display-based optimiza-
8240 * tion of the closure's static link.
8242 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8243 JS_ASSERT(dn->pn_op == JSOP_NOP);
8244 if (tokenStream.peekToken() != TOK_LP)
8245 dn->pn_dflags |= PND_FUNARG;
8249 JS_ASSERT(dn->pn_defn);
8250 LinkUseToDef(pn, dn, tc);
8252 /* Here we handle the backward function reference case. */
8253 if (tokenStream.peekToken() != TOK_LP)
8254 dn->pn_dflags |= PND_FUNARG;
8256 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8260 #if JS_HAS_XML_SUPPORT
8261 if (tokenStream.matchToken(TOK_DBLCOLON)) {
8262 if (afterDot) {
8263 JSString *str;
8266 * Here primaryExpr is called after . or .. followed by a name
8267 * followed by ::. This is the only case where a keyword after
8268 * . or .. is not treated as a property name.
8270 str = ATOM_TO_STRING(pn->pn_atom);
8271 tt = js_CheckKeyword(str->chars(), str->length());
8272 if (tt == TOK_FUNCTION) {
8273 pn->pn_arity = PN_NULLARY;
8274 pn->pn_type = TOK_FUNCTION;
8275 } else if (tt != TOK_EOF) {
8276 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS);
8277 return NULL;
8280 pn = qualifiedSuffix(pn);
8281 if (!pn)
8282 return NULL;
8284 #endif
8285 break;
8287 case TOK_REGEXP:
8289 JSObject *obj;
8291 pn = NullaryNode::create(tc);
8292 if (!pn)
8293 return NULL;
8295 obj = js_NewRegExpObject(context, &tokenStream,
8296 tokenStream.getTokenbuf().begin(),
8297 tokenStream.getTokenbuf().length(),
8298 tokenStream.currentToken().t_reflags);
8299 if (!obj)
8300 return NULL;
8301 if (!tc->compileAndGo()) {
8302 obj->clearParent();
8303 obj->clearProto();
8306 pn->pn_objbox = tc->parser->newObjectBox(obj);
8307 if (!pn->pn_objbox)
8308 return NULL;
8310 pn->pn_op = JSOP_REGEXP;
8311 break;
8314 case TOK_NUMBER:
8315 pn = NullaryNode::create(tc);
8316 if (!pn)
8317 return NULL;
8318 pn->pn_op = JSOP_DOUBLE;
8319 pn->pn_dval = tokenStream.currentToken().t_dval;
8320 break;
8322 case TOK_PRIMARY:
8323 pn = NullaryNode::create(tc);
8324 if (!pn)
8325 return NULL;
8326 pn->pn_op = tokenStream.currentToken().t_op;
8327 break;
8329 case TOK_ERROR:
8330 /* The scanner or one of its subroutines reported the error. */
8331 return NULL;
8333 default:
8334 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
8335 return NULL;
8337 return pn;
8340 JSParseNode *
8341 Parser::parenExpr(JSParseNode *pn1, JSBool *genexp)
8343 TokenPtr begin;
8344 JSParseNode *pn;
8346 JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
8347 begin = tokenStream.currentToken().pos.begin;
8349 if (genexp)
8350 *genexp = JS_FALSE;
8351 pn = bracketedExpr();
8352 if (!pn)
8353 return NULL;
8355 #if JS_HAS_GENERATOR_EXPRS
8356 if (tokenStream.matchToken(TOK_FOR)) {
8357 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
8358 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
8359 return NULL;
8361 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
8362 reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
8363 js_generator_str);
8364 return NULL;
8366 if (!pn1) {
8367 pn1 = UnaryNode::create(tc);
8368 if (!pn1)
8369 return NULL;
8371 pn = generatorExpr(pn1, pn);
8372 if (!pn)
8373 return NULL;
8374 pn->pn_pos.begin = begin;
8375 if (genexp) {
8376 if (tokenStream.getToken() != TOK_RP) {
8377 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
8378 js_generator_str);
8379 return NULL;
8381 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8382 *genexp = JS_TRUE;
8385 #endif /* JS_HAS_GENERATOR_EXPRS */
8387 return pn;
8391 * Fold from one constant type to another.
8392 * XXX handles only strings and numbers for now
8394 static JSBool
8395 FoldType(JSContext *cx, JSParseNode *pn, TokenKind type)
8397 if (PN_TYPE(pn) != type) {
8398 switch (type) {
8399 case TOK_NUMBER:
8400 if (pn->pn_type == TOK_STRING) {
8401 jsdouble d;
8402 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8403 return JS_FALSE;
8404 pn->pn_dval = d;
8405 pn->pn_type = TOK_NUMBER;
8406 pn->pn_op = JSOP_DOUBLE;
8408 break;
8410 case TOK_STRING:
8411 if (pn->pn_type == TOK_NUMBER) {
8412 JSString *str = js_NumberToString(cx, pn->pn_dval);
8413 if (!str)
8414 return JS_FALSE;
8415 pn->pn_atom = js_AtomizeString(cx, str, 0);
8416 if (!pn->pn_atom)
8417 return JS_FALSE;
8418 pn->pn_type = TOK_STRING;
8419 pn->pn_op = JSOP_STRING;
8421 break;
8423 default:;
8426 return JS_TRUE;
8430 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8431 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8432 * a successful call to this function.
8434 static JSBool
8435 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8436 JSParseNode *pn, JSTreeContext *tc)
8438 jsdouble d, d2;
8439 int32 i, j;
8441 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8442 d = pn1->pn_dval;
8443 d2 = pn2->pn_dval;
8444 switch (op) {
8445 case JSOP_LSH:
8446 case JSOP_RSH:
8447 i = js_DoubleToECMAInt32(d);
8448 j = js_DoubleToECMAInt32(d2);
8449 j &= 31;
8450 d = (op == JSOP_LSH) ? i << j : i >> j;
8451 break;
8453 case JSOP_URSH:
8454 j = js_DoubleToECMAInt32(d2);
8455 j &= 31;
8456 d = js_DoubleToECMAUint32(d) >> j;
8457 break;
8459 case JSOP_ADD:
8460 d += d2;
8461 break;
8463 case JSOP_SUB:
8464 d -= d2;
8465 break;
8467 case JSOP_MUL:
8468 d *= d2;
8469 break;
8471 case JSOP_DIV:
8472 if (d2 == 0) {
8473 #if defined(XP_WIN)
8474 /* XXX MSVC miscompiles such that (NaN == 0) */
8475 if (JSDOUBLE_IS_NaN(d2))
8476 d = js_NaN;
8477 else
8478 #endif
8479 if (d == 0 || JSDOUBLE_IS_NaN(d))
8480 d = js_NaN;
8481 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
8482 d = js_NegativeInfinity;
8483 else
8484 d = js_PositiveInfinity;
8485 } else {
8486 d /= d2;
8488 break;
8490 case JSOP_MOD:
8491 if (d2 == 0) {
8492 d = js_NaN;
8493 } else {
8494 d = js_fmod(d, d2);
8496 break;
8498 default:;
8501 /* Take care to allow pn1 or pn2 to alias pn. */
8502 if (pn1 != pn)
8503 RecycleTree(pn1, tc);
8504 if (pn2 != pn)
8505 RecycleTree(pn2, tc);
8506 pn->pn_type = TOK_NUMBER;
8507 pn->pn_op = JSOP_DOUBLE;
8508 pn->pn_arity = PN_NULLARY;
8509 pn->pn_dval = d;
8510 return JS_TRUE;
8513 #if JS_HAS_XML_SUPPORT
8515 static JSBool
8516 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8518 TokenKind tt;
8519 JSParseNode **pnp, *pn1, *pn2;
8520 JSString *accum, *str;
8521 uint32 i, j;
8523 JS_ASSERT(pn->pn_arity == PN_LIST);
8524 tt = PN_TYPE(pn);
8525 pnp = &pn->pn_head;
8526 pn1 = *pnp;
8527 accum = NULL;
8528 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8529 if (tt == TOK_XMLETAGO)
8530 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8531 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8532 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8536 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8537 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8538 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8539 * Therefore, we have to add additonal protection from GC nesting under
8540 * js_ConcatStrings.
8542 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8543 /* The parser already rejected end-tags with attributes. */
8544 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8545 switch (pn2->pn_type) {
8546 case TOK_XMLATTR:
8547 if (!accum)
8548 goto cantfold;
8549 /* FALL THROUGH */
8550 case TOK_XMLNAME:
8551 case TOK_XMLSPACE:
8552 case TOK_XMLTEXT:
8553 case TOK_STRING:
8554 if (pn2->pn_arity == PN_LIST)
8555 goto cantfold;
8556 str = ATOM_TO_STRING(pn2->pn_atom);
8557 break;
8559 case TOK_XMLCDATA:
8560 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8561 if (!str)
8562 return JS_FALSE;
8563 break;
8565 case TOK_XMLCOMMENT:
8566 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8567 if (!str)
8568 return JS_FALSE;
8569 break;
8571 case TOK_XMLPI:
8572 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8573 ATOM_TO_STRING(pn2->pn_atom2));
8574 if (!str)
8575 return JS_FALSE;
8576 break;
8578 cantfold:
8579 default:
8580 JS_ASSERT(*pnp == pn1);
8581 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8582 (i & 1) ^ (j & 1)) {
8583 #ifdef DEBUG_brendanXXX
8584 printf("1: %d, %d => ", i, j);
8585 if (accum)
8586 js_FileEscapedString(stdout, accum, 0);
8587 else
8588 fputs("NULL", stdout);
8589 fputc('\n', stdout);
8590 #endif
8591 } else if (accum && pn1 != pn2) {
8592 while (pn1->pn_next != pn2) {
8593 pn1 = RecycleTree(pn1, tc);
8594 --pn->pn_count;
8596 pn1->pn_type = TOK_XMLTEXT;
8597 pn1->pn_op = JSOP_STRING;
8598 pn1->pn_arity = PN_NULLARY;
8599 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8600 if (!pn1->pn_atom)
8601 return JS_FALSE;
8602 JS_ASSERT(pnp != &pn1->pn_next);
8603 *pnp = pn1;
8605 pnp = &pn2->pn_next;
8606 pn1 = *pnp;
8607 accum = NULL;
8608 continue;
8611 if (accum) {
8613 AutoValueRooter tvr(cx, accum);
8614 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8615 ? js_AddAttributePart(cx, i & 1, accum, str)
8616 : js_ConcatStrings(cx, accum, str);
8618 if (!str)
8619 return JS_FALSE;
8620 #ifdef DEBUG_brendanXXX
8621 printf("2: %d, %d => ", i, j);
8622 js_FileEscapedString(stdout, str, 0);
8623 printf(" (%u)\n", str->length());
8624 #endif
8625 ++j;
8627 accum = str;
8630 if (accum) {
8631 str = NULL;
8632 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8633 if (tt == TOK_XMLPTAGC)
8634 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8635 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8636 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8638 if (str) {
8639 accum = js_ConcatStrings(cx, accum, str);
8640 if (!accum)
8641 return JS_FALSE;
8644 JS_ASSERT(*pnp == pn1);
8645 while (pn1->pn_next) {
8646 pn1 = RecycleTree(pn1, tc);
8647 --pn->pn_count;
8649 pn1->pn_type = TOK_XMLTEXT;
8650 pn1->pn_op = JSOP_STRING;
8651 pn1->pn_arity = PN_NULLARY;
8652 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8653 if (!pn1->pn_atom)
8654 return JS_FALSE;
8655 JS_ASSERT(pnp != &pn1->pn_next);
8656 *pnp = pn1;
8659 if (pn1 && pn->pn_count == 1) {
8661 * Only one node under pn, and it has been folded: move pn1 onto pn
8662 * unless pn is an XML root (in which case we need it to tell the code
8663 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8664 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8665 * extra "<" and "/>" bracketing at runtime.
8667 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8668 pn->become(pn1);
8669 } else if (tt == TOK_XMLPTAGC) {
8670 pn->pn_type = TOK_XMLELEM;
8671 pn->pn_op = JSOP_TOXML;
8674 return JS_TRUE;
8677 #endif /* JS_HAS_XML_SUPPORT */
8679 static int
8680 Boolish(JSParseNode *pn)
8682 switch (pn->pn_op) {
8683 case JSOP_DOUBLE:
8684 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8686 case JSOP_STRING:
8687 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8689 #if JS_HAS_GENERATOR_EXPRS
8690 case JSOP_CALL:
8693 * A generator expression as an if or loop condition has no effects, it
8694 * simply results in a truthy object reference. This condition folding
8695 * is needed for the decompiler. See bug 442342 and bug 443074.
8697 if (pn->pn_count != 1)
8698 break;
8699 JSParseNode *pn2 = pn->pn_head;
8700 if (pn2->pn_type != TOK_FUNCTION)
8701 break;
8702 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8703 break;
8704 /* FALL THROUGH */
8706 #endif
8708 case JSOP_DEFFUN:
8709 case JSOP_LAMBDA:
8710 case JSOP_THIS:
8711 case JSOP_TRUE:
8712 return 1;
8714 case JSOP_NULL:
8715 case JSOP_FALSE:
8716 return 0;
8718 default:;
8720 return -1;
8723 JSBool
8724 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8726 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8728 JS_CHECK_RECURSION(cx, return JS_FALSE);
8730 switch (pn->pn_arity) {
8731 case PN_FUNC:
8733 uint32 oldflags = tc->flags;
8734 JSFunctionBox *oldlist = tc->functionList;
8736 tc->flags = pn->pn_funbox->tcflags;
8737 tc->functionList = pn->pn_funbox->kids;
8738 if (!js_FoldConstants(cx, pn->pn_body, tc))
8739 return JS_FALSE;
8740 pn->pn_funbox->kids = tc->functionList;
8741 tc->flags = oldflags;
8742 tc->functionList = oldlist;
8743 break;
8746 case PN_LIST:
8748 /* Propagate inCond through logical connectives. */
8749 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8751 /* Don't fold a parenthesized call expression. See bug 537673. */
8752 pn1 = pn2 = pn->pn_head;
8753 if ((pn->pn_type == TOK_LP || pn->pn_type == TOK_NEW) && pn2->pn_parens)
8754 pn2 = pn2->pn_next;
8756 /* Save the list head in pn1 for later use. */
8757 for (; pn2; pn2 = pn2->pn_next) {
8758 if (!js_FoldConstants(cx, pn2, tc, cond))
8759 return JS_FALSE;
8761 break;
8764 case PN_TERNARY:
8765 /* Any kid may be null (e.g. for (;;)). */
8766 pn1 = pn->pn_kid1;
8767 pn2 = pn->pn_kid2;
8768 pn3 = pn->pn_kid3;
8769 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8770 return JS_FALSE;
8771 if (pn2) {
8772 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8773 return JS_FALSE;
8774 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8775 RecycleTree(pn2, tc);
8776 pn->pn_kid2 = NULL;
8779 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8780 return JS_FALSE;
8781 break;
8783 case PN_BINARY:
8784 pn1 = pn->pn_left;
8785 pn2 = pn->pn_right;
8787 /* Propagate inCond through logical connectives. */
8788 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8789 if (!js_FoldConstants(cx, pn1, tc, inCond))
8790 return JS_FALSE;
8791 if (!js_FoldConstants(cx, pn2, tc, inCond))
8792 return JS_FALSE;
8793 break;
8796 /* First kid may be null (for default case in switch). */
8797 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8798 return JS_FALSE;
8799 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8800 return JS_FALSE;
8801 break;
8803 case PN_UNARY:
8804 pn1 = pn->pn_kid;
8807 * Kludge to deal with typeof expressions: because constant folding
8808 * can turn an expression into a name node, we have to check here,
8809 * before folding, to see if we should throw undefined name errors.
8811 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
8812 * null. This assumption does not hold true for other unary
8813 * expressions.
8815 if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
8816 pn->pn_op = JSOP_TYPEOFEXPR;
8818 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
8819 return JS_FALSE;
8820 break;
8822 case PN_NAME:
8824 * Skip pn1 down along a chain of dotted member expressions to avoid
8825 * excessive recursion. Our only goal here is to fold constants (if
8826 * any) in the primary expression operand to the left of the first
8827 * dot in the chain.
8829 if (!pn->pn_used) {
8830 pn1 = pn->pn_expr;
8831 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8832 pn1 = pn1->pn_expr;
8833 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8834 return JS_FALSE;
8836 break;
8838 case PN_NAMESET:
8839 pn1 = pn->pn_tree;
8840 if (!js_FoldConstants(cx, pn1, tc))
8841 return JS_FALSE;
8842 break;
8844 case PN_NULLARY:
8845 break;
8848 switch (pn->pn_type) {
8849 case TOK_IF:
8850 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8851 break;
8852 /* FALL THROUGH */
8854 case TOK_HOOK:
8855 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8856 switch (pn1->pn_type) {
8857 case TOK_NUMBER:
8858 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8859 pn2 = pn3;
8860 break;
8861 case TOK_STRING:
8862 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
8863 pn2 = pn3;
8864 break;
8865 case TOK_PRIMARY:
8866 if (pn1->pn_op == JSOP_TRUE)
8867 break;
8868 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8869 pn2 = pn3;
8870 break;
8872 /* FALL THROUGH */
8873 default:
8874 /* Early return to dodge common code that copies pn2 to pn. */
8875 return JS_TRUE;
8878 #if JS_HAS_GENERATOR_EXPRS
8879 /* Don't fold a trailing |if (0)| in a generator expression. */
8880 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8881 break;
8882 #endif
8884 if (pn2 && !pn2->pn_defn)
8885 pn->become(pn2);
8886 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8888 * False condition and no else, or an empty then-statement was
8889 * moved up over pn. Either way, make pn an empty block (not an
8890 * empty statement, which does not decompile, even when labeled).
8891 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8892 * or an empty statement for a child.
8894 pn->pn_type = TOK_LC;
8895 pn->pn_arity = PN_LIST;
8896 pn->makeEmpty();
8898 RecycleTree(pn2, tc);
8899 if (pn3 && pn3 != pn2)
8900 RecycleTree(pn3, tc);
8901 break;
8903 case TOK_OR:
8904 case TOK_AND:
8905 if (inCond) {
8906 if (pn->pn_arity == PN_LIST) {
8907 JSParseNode **pnp = &pn->pn_head;
8908 JS_ASSERT(*pnp == pn1);
8909 do {
8910 int cond = Boolish(pn1);
8911 if (cond == (pn->pn_type == TOK_OR)) {
8912 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
8913 pn3 = pn2->pn_next;
8914 RecycleTree(pn2, tc);
8915 --pn->pn_count;
8917 pn1->pn_next = NULL;
8918 break;
8920 if (cond != -1) {
8921 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8922 if (pn->pn_count == 1)
8923 break;
8924 *pnp = pn1->pn_next;
8925 RecycleTree(pn1, tc);
8926 --pn->pn_count;
8927 } else {
8928 pnp = &pn1->pn_next;
8930 } while ((pn1 = *pnp) != NULL);
8932 // We may have to change arity from LIST to BINARY.
8933 pn1 = pn->pn_head;
8934 if (pn->pn_count == 2) {
8935 pn2 = pn1->pn_next;
8936 pn1->pn_next = NULL;
8937 JS_ASSERT(!pn2->pn_next);
8938 pn->pn_arity = PN_BINARY;
8939 pn->pn_left = pn1;
8940 pn->pn_right = pn2;
8941 } else if (pn->pn_count == 1) {
8942 pn->become(pn1);
8943 RecycleTree(pn1, tc);
8945 } else {
8946 int cond = Boolish(pn1);
8947 if (cond == (pn->pn_type == TOK_OR)) {
8948 RecycleTree(pn2, tc);
8949 pn->become(pn1);
8950 } else if (cond != -1) {
8951 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8952 RecycleTree(pn1, tc);
8953 pn->become(pn2);
8957 break;
8959 case TOK_ASSIGN:
8961 * Compound operators such as *= should be subject to folding, in case
8962 * the left-hand side is constant, and so that the decompiler produces
8963 * the same string that you get from decompiling a script or function
8964 * compiled from that same string. As with +, += is special.
8966 if (pn->pn_op == JSOP_NOP)
8967 break;
8968 if (pn->pn_op != JSOP_ADD)
8969 goto do_binary_op;
8970 /* FALL THROUGH */
8972 case TOK_PLUS:
8973 if (pn->pn_arity == PN_LIST) {
8974 size_t length, length2;
8975 jschar *chars;
8976 JSString *str, *str2;
8979 * Any string literal term with all others number or string means
8980 * this is a concatenation. If any term is not a string or number
8981 * literal, we can't fold.
8983 JS_ASSERT(pn->pn_count > 2);
8984 if (pn->pn_xflags & PNX_CANTFOLD)
8985 return JS_TRUE;
8986 if (pn->pn_xflags != PNX_STRCAT)
8987 goto do_binary_op;
8989 /* Ok, we're concatenating: convert non-string constant operands. */
8990 length = 0;
8991 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8992 if (!FoldType(cx, pn2, TOK_STRING))
8993 return JS_FALSE;
8994 /* XXX fold only if all operands convert to string */
8995 if (pn2->pn_type != TOK_STRING)
8996 return JS_TRUE;
8997 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9000 /* Allocate a new buffer and string descriptor for the result. */
9001 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9002 if (!chars)
9003 return JS_FALSE;
9004 str = js_NewString(cx, chars, length);
9005 if (!str) {
9006 cx->free(chars);
9007 return JS_FALSE;
9010 /* Fill the buffer, advancing chars and recycling kids as we go. */
9011 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9012 str2 = ATOM_TO_STRING(pn2->pn_atom);
9013 length2 = str2->flatLength();
9014 js_strncpy(chars, str2->flatChars(), length2);
9015 chars += length2;
9017 *chars = 0;
9019 /* Atomize the result string and mutate pn to refer to it. */
9020 pn->pn_atom = js_AtomizeString(cx, str, 0);
9021 if (!pn->pn_atom)
9022 return JS_FALSE;
9023 pn->pn_type = TOK_STRING;
9024 pn->pn_op = JSOP_STRING;
9025 pn->pn_arity = PN_NULLARY;
9026 break;
9029 /* Handle a binary string concatenation. */
9030 JS_ASSERT(pn->pn_arity == PN_BINARY);
9031 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9032 JSString *left, *right, *str;
9034 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9035 TOK_STRING)) {
9036 return JS_FALSE;
9038 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9039 return JS_TRUE;
9040 left = ATOM_TO_STRING(pn1->pn_atom);
9041 right = ATOM_TO_STRING(pn2->pn_atom);
9042 str = js_ConcatStrings(cx, left, right);
9043 if (!str)
9044 return JS_FALSE;
9045 pn->pn_atom = js_AtomizeString(cx, str, 0);
9046 if (!pn->pn_atom)
9047 return JS_FALSE;
9048 pn->pn_type = TOK_STRING;
9049 pn->pn_op = JSOP_STRING;
9050 pn->pn_arity = PN_NULLARY;
9051 RecycleTree(pn1, tc);
9052 RecycleTree(pn2, tc);
9053 break;
9056 /* Can't concatenate string literals, let's try numbers. */
9057 goto do_binary_op;
9059 case TOK_STAR:
9060 case TOK_SHOP:
9061 case TOK_MINUS:
9062 case TOK_DIVOP:
9063 do_binary_op:
9064 if (pn->pn_arity == PN_LIST) {
9065 JS_ASSERT(pn->pn_count > 2);
9066 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9067 if (!FoldType(cx, pn2, TOK_NUMBER))
9068 return JS_FALSE;
9070 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9071 /* XXX fold only if all operands convert to number */
9072 if (pn2->pn_type != TOK_NUMBER)
9073 break;
9075 if (!pn2) {
9076 JSOp op = PN_OP(pn);
9078 pn2 = pn1->pn_next;
9079 pn3 = pn2->pn_next;
9080 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9081 return JS_FALSE;
9082 while ((pn2 = pn3) != NULL) {
9083 pn3 = pn2->pn_next;
9084 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9085 return JS_FALSE;
9088 } else {
9089 JS_ASSERT(pn->pn_arity == PN_BINARY);
9090 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9091 !FoldType(cx, pn2, TOK_NUMBER)) {
9092 return JS_FALSE;
9094 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9095 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9096 return JS_FALSE;
9099 break;
9101 case TOK_UNARYOP:
9102 if (pn1->pn_type == TOK_NUMBER) {
9103 jsdouble d;
9105 /* Operate on one numeric constant. */
9106 d = pn1->pn_dval;
9107 switch (pn->pn_op) {
9108 case JSOP_BITNOT:
9109 d = ~js_DoubleToECMAInt32(d);
9110 break;
9112 case JSOP_NEG:
9113 d = -d;
9114 break;
9116 case JSOP_POS:
9117 break;
9119 case JSOP_NOT:
9120 pn->pn_type = TOK_PRIMARY;
9121 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9122 pn->pn_arity = PN_NULLARY;
9123 /* FALL THROUGH */
9125 default:
9126 /* Return early to dodge the common TOK_NUMBER code. */
9127 return JS_TRUE;
9129 pn->pn_type = TOK_NUMBER;
9130 pn->pn_op = JSOP_DOUBLE;
9131 pn->pn_arity = PN_NULLARY;
9132 pn->pn_dval = d;
9133 RecycleTree(pn1, tc);
9134 } else if (pn1->pn_type == TOK_PRIMARY) {
9135 if (pn->pn_op == JSOP_NOT &&
9136 (pn1->pn_op == JSOP_TRUE ||
9137 pn1->pn_op == JSOP_FALSE)) {
9138 pn->become(pn1);
9139 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9140 RecycleTree(pn1, tc);
9143 break;
9145 #if JS_HAS_XML_SUPPORT
9146 case TOK_XMLELEM:
9147 case TOK_XMLLIST:
9148 case TOK_XMLPTAGC:
9149 case TOK_XMLSTAGO:
9150 case TOK_XMLETAGO:
9151 case TOK_XMLNAME:
9152 if (pn->pn_arity == PN_LIST) {
9153 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9154 if (!FoldXMLConstants(cx, pn, tc))
9155 return JS_FALSE;
9157 break;
9159 case TOK_AT:
9160 if (pn1->pn_type == TOK_XMLNAME) {
9161 jsval v;
9162 JSObjectBox *xmlbox;
9164 v = ATOM_KEY(pn1->pn_atom);
9165 if (!js_ToAttributeName(cx, &v))
9166 return JS_FALSE;
9167 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9169 xmlbox = tc->parser->newObjectBox(JSVAL_TO_OBJECT(v));
9170 if (!xmlbox)
9171 return JS_FALSE;
9173 pn->pn_type = TOK_XMLNAME;
9174 pn->pn_op = JSOP_OBJECT;
9175 pn->pn_arity = PN_NULLARY;
9176 pn->pn_objbox = xmlbox;
9177 RecycleTree(pn1, tc);
9179 break;
9180 #endif /* JS_HAS_XML_SUPPORT */
9182 default:;
9185 if (inCond) {
9186 int cond = Boolish(pn);
9187 if (cond >= 0) {
9188 switch (pn->pn_arity) {
9189 case PN_LIST:
9190 pn2 = pn->pn_head;
9191 do {
9192 pn3 = pn2->pn_next;
9193 RecycleTree(pn2, tc);
9194 } while ((pn2 = pn3) != NULL);
9195 break;
9196 case PN_FUNC:
9197 RecycleFuncNameKids(pn, tc);
9198 break;
9199 case PN_NULLARY:
9200 break;
9201 default:
9202 JS_NOT_REACHED("unhandled arity");
9204 pn->pn_type = TOK_PRIMARY;
9205 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9206 pn->pn_arity = PN_NULLARY;
9210 return JS_TRUE;