Bug 576148: Factor out js::UpvarCookie. (r=mrbkap, dvander)
[mozilla-central.git] / js / src / jsparse.cpp
blobe75984a61afe1ab549b5beaee4c67401c99dc080
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.makeFree();
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(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
692 static inline bool
693 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
696 * This is a lot simpler than error-checking every UpvarCookie::set, and
697 * practically speaking it leaves more than enough room for upvars.
699 if (UpvarCookie::isLevelReserved(staticLevel)) {
700 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
701 JSMSG_TOO_DEEP, js_function_str);
702 return false;
704 tc->staticLevel = staticLevel;
705 return true;
709 * Compile a top-level script.
711 JSScript *
712 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
713 JSPrincipals *principals, uint32 tcflags,
714 const jschar *chars, size_t length,
715 FILE *file, const char *filename, uintN lineno,
716 JSString *source /* = NULL */,
717 unsigned staticLevel /* = 0 */)
719 JSArenaPool codePool, notePool;
720 TokenKind tt;
721 JSParseNode *pn;
722 uint32 scriptGlobals;
723 JSScript *script;
724 bool inDirectivePrologue;
725 #ifdef METER_PARSENODES
726 void *sbrk(ptrdiff_t), *before = sbrk(0);
727 #endif
729 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
732 * The scripted callerFrame can only be given for compile-and-go scripts
733 * and non-zero static level requires callerFrame.
735 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
736 JS_ASSERT_IF(staticLevel != 0, callerFrame);
738 Compiler compiler(cx, principals, callerFrame);
739 if (!compiler.init(chars, length, file, filename, lineno))
740 return NULL;
742 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
743 &cx->scriptStackQuota);
744 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
745 &cx->scriptStackQuota);
747 Parser &parser = compiler.parser;
748 TokenStream &tokenStream = parser.tokenStream;
750 JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
751 if (!cg.init())
752 return NULL;
754 MUST_FLOW_THROUGH("out");
756 /* Null script early in case of error, to reduce our code footprint. */
757 script = NULL;
759 cg.flags |= tcflags;
760 cg.scopeChain = scopeChain;
761 if (!SetStaticLevel(&cg, staticLevel))
762 goto out;
764 /* If this is a direct call to eval, inherit the caller's strictness. */
765 if (callerFrame &&
766 callerFrame->script &&
767 callerFrame->script->strictModeCode) {
768 cg.flags |= TCF_STRICT_MODE_CODE;
769 tokenStream.setStrictMode();
773 * If funbox is non-null after we create the new script, callerFrame->fun
774 * was saved in the 0th object table entry.
776 JSObjectBox *funbox;
777 funbox = NULL;
779 if (tcflags & TCF_COMPILE_N_GO) {
780 if (source) {
782 * Save eval program source in script->atomMap.vector[0] for the
783 * eval cache (see obj_eval in jsobj.cpp).
785 JSAtom *atom = js_AtomizeString(cx, source, 0);
786 if (!atom || !cg.atomList.add(&parser, atom))
787 goto out;
790 if (callerFrame && callerFrame->fun) {
792 * An eval script in a caller frame needs to have its enclosing
793 * function captured in case it refers to an upvar, and someone
794 * wishes to decompile it while it's running.
796 funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun));
797 if (!funbox)
798 goto out;
799 funbox->emitLink = cg.objectList.lastbox;
800 cg.objectList.lastbox = funbox;
801 cg.objectList.length++;
806 * Inline this->statements to emit as we go to save AST space. We must
807 * generate our script-body blockid since we aren't calling Statements.
809 uint32 bodyid;
810 if (!GenerateBlockId(&cg, bodyid))
811 goto out;
812 cg.bodyid = bodyid;
814 #if JS_HAS_XML_SUPPORT
815 pn = NULL;
816 bool onlyXML;
817 onlyXML = true;
818 #endif
820 CG_SWITCH_TO_PROLOG(&cg);
821 if (js_Emit1(cx, &cg, JSOP_TRACE) < 0)
822 goto out;
823 CG_SWITCH_TO_MAIN(&cg);
825 inDirectivePrologue = true;
826 for (;;) {
827 tt = tokenStream.peekToken(TSF_OPERAND);
828 if (tt <= TOK_EOF) {
829 if (tt == TOK_EOF)
830 break;
831 JS_ASSERT(tt == TOK_ERROR);
832 goto out;
835 pn = parser.statement();
836 if (!pn)
837 goto out;
838 JS_ASSERT(!cg.blockNode);
840 if (inDirectivePrologue)
841 inDirectivePrologue = parser.recognizeDirectivePrologue(pn);
843 if (!js_FoldConstants(cx, pn, &cg))
844 goto out;
846 if (cg.functionList) {
847 if (!parser.analyzeFunctions(cg.functionList, cg.flags))
848 goto out;
849 cg.functionList = NULL;
852 if (!js_EmitTree(cx, &cg, pn))
853 goto out;
854 #if JS_HAS_XML_SUPPORT
855 if (PN_TYPE(pn) != TOK_SEMI ||
856 !pn->pn_kid ||
857 !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) {
858 onlyXML = false;
860 #endif
861 RecycleTree(pn, &cg);
864 #if JS_HAS_XML_SUPPORT
866 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
867 * For background, see:
869 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
871 if (pn && onlyXML && !callerFrame) {
872 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
873 goto out;
875 #endif
878 * Global variables (gvars) share the atom index space with locals. Due to
879 * incremental code generation we need to patch the bytecode to adjust the
880 * local references to skip the globals.
882 scriptGlobals = cg.ngvars;
883 if (scriptGlobals != 0 || cg.hasSharps()) {
884 jsbytecode *code, *end;
885 JSOp op;
886 const JSCodeSpec *cs;
887 uintN len, slot;
889 if (scriptGlobals >= SLOTNO_LIMIT)
890 goto too_many_slots;
891 code = CG_BASE(&cg);
892 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
893 JS_ASSERT(code < end);
894 op = (JSOp) *code;
895 cs = &js_CodeSpec[op];
896 len = (cs->length > 0)
897 ? (uintN) cs->length
898 : js_GetVariableBytecodeLength(code);
899 if ((cs->format & JOF_SHARPSLOT) ||
900 JOF_TYPE(cs->format) == JOF_LOCAL ||
901 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
903 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
904 * emitted only for a function.
906 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
907 (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
908 (op == JSOP_GETLOCALPROP));
909 slot = GET_SLOTNO(code);
910 slot += scriptGlobals;
911 if (!(cs->format & JOF_SHARPSLOT))
912 slot += cg.sharpSlots();
913 if (slot >= SLOTNO_LIMIT)
914 goto too_many_slots;
915 SET_SLOTNO(code, slot);
920 #ifdef METER_PARSENODES
921 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
922 (char *)sbrk(0) - (char *)before,
923 parsenodes,
924 maxparsenodes,
925 parsenodes - recyclednodes);
926 before = sbrk(0);
927 #endif
930 * Nowadays the threaded interpreter needs a stop instruction, so we
931 * do have to emit that here.
933 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
934 goto out;
935 #ifdef METER_PARSENODES
936 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
937 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
938 #endif
939 #ifdef JS_ARENAMETER
940 JS_DumpArenaStats(stdout);
941 #endif
942 script = js_NewScriptFromCG(cx, &cg);
943 if (script && funbox && script != script->emptyScript())
944 script->savedCallerFun = true;
946 #ifdef JS_SCOPE_DEPTH_METER
947 if (script) {
948 JSObject *obj = scopeChain;
949 uintN depth = 1;
950 while ((obj = obj->getParent()) != NULL)
951 ++depth;
952 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
954 #endif
956 out:
957 JS_FinishArenaPool(&codePool);
958 JS_FinishArenaPool(&notePool);
959 return script;
961 too_many_slots:
962 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
963 script = NULL;
964 goto out;
968 * Insist on a final return before control flows out of pn. Try to be a bit
969 * smart about loops: do {...; return e2;} while(0) at the end of a function
970 * that contains an early return e1 will get a strict warning. Similarly for
971 * iloops: while (true){...} is treated as though ... returns.
973 #define ENDS_IN_OTHER 0
974 #define ENDS_IN_RETURN 1
975 #define ENDS_IN_BREAK 2
977 static int
978 HasFinalReturn(JSParseNode *pn)
980 JSParseNode *pn2, *pn3;
981 uintN rv, rv2, hasDefault;
983 switch (pn->pn_type) {
984 case TOK_LC:
985 if (!pn->pn_head)
986 return ENDS_IN_OTHER;
987 return HasFinalReturn(pn->last());
989 case TOK_IF:
990 if (!pn->pn_kid3)
991 return ENDS_IN_OTHER;
992 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
994 case TOK_WHILE:
995 pn2 = pn->pn_left;
996 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
997 return ENDS_IN_RETURN;
998 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
999 return ENDS_IN_RETURN;
1000 return ENDS_IN_OTHER;
1002 case TOK_DO:
1003 pn2 = pn->pn_right;
1004 if (pn2->pn_type == TOK_PRIMARY) {
1005 if (pn2->pn_op == JSOP_FALSE)
1006 return HasFinalReturn(pn->pn_left);
1007 if (pn2->pn_op == JSOP_TRUE)
1008 return ENDS_IN_RETURN;
1010 if (pn2->pn_type == TOK_NUMBER) {
1011 if (pn2->pn_dval == 0)
1012 return HasFinalReturn(pn->pn_left);
1013 return ENDS_IN_RETURN;
1015 return ENDS_IN_OTHER;
1017 case TOK_FOR:
1018 pn2 = pn->pn_left;
1019 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1020 return ENDS_IN_RETURN;
1021 return ENDS_IN_OTHER;
1023 case TOK_SWITCH:
1024 rv = ENDS_IN_RETURN;
1025 hasDefault = ENDS_IN_OTHER;
1026 pn2 = pn->pn_right;
1027 if (pn2->pn_type == TOK_LEXICALSCOPE)
1028 pn2 = pn2->expr();
1029 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1030 if (pn2->pn_type == TOK_DEFAULT)
1031 hasDefault = ENDS_IN_RETURN;
1032 pn3 = pn2->pn_right;
1033 JS_ASSERT(pn3->pn_type == TOK_LC);
1034 if (pn3->pn_head) {
1035 rv2 = HasFinalReturn(pn3->last());
1036 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1037 /* Falling through to next case or default. */;
1038 else
1039 rv &= rv2;
1042 /* If a final switch has no default case, we judge it harshly. */
1043 rv &= hasDefault;
1044 return rv;
1046 case TOK_BREAK:
1047 return ENDS_IN_BREAK;
1049 case TOK_WITH:
1050 return HasFinalReturn(pn->pn_right);
1052 case TOK_RETURN:
1053 return ENDS_IN_RETURN;
1055 case TOK_COLON:
1056 case TOK_LEXICALSCOPE:
1057 return HasFinalReturn(pn->expr());
1059 case TOK_THROW:
1060 return ENDS_IN_RETURN;
1062 case TOK_TRY:
1063 /* If we have a finally block that returns, we are done. */
1064 if (pn->pn_kid3) {
1065 rv = HasFinalReturn(pn->pn_kid3);
1066 if (rv == ENDS_IN_RETURN)
1067 return rv;
1070 /* Else check the try block and any and all catch statements. */
1071 rv = HasFinalReturn(pn->pn_kid1);
1072 if (pn->pn_kid2) {
1073 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1074 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1075 rv &= HasFinalReturn(pn2);
1077 return rv;
1079 case TOK_CATCH:
1080 /* Check this catch block's body. */
1081 return HasFinalReturn(pn->pn_kid3);
1083 case TOK_LET:
1084 /* Non-binary let statements are let declarations. */
1085 if (pn->pn_arity != PN_BINARY)
1086 return ENDS_IN_OTHER;
1087 return HasFinalReturn(pn->pn_right);
1089 default:
1090 return ENDS_IN_OTHER;
1094 static JSBool
1095 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1096 uintN anonerrnum)
1098 const char *name;
1100 JS_ASSERT(tc->inFunction());
1101 if (tc->fun->atom) {
1102 name = js_AtomToPrintableString(cx, tc->fun->atom);
1103 } else {
1104 errnum = anonerrnum;
1105 name = NULL;
1107 return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name);
1110 static JSBool
1111 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1113 JS_ASSERT(tc->inFunction());
1114 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1115 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1116 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1120 * Check that it is permitted to assign to lhs. Strict mode code may not
1121 * assign to 'eval' or 'arguments'.
1123 bool
1124 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1126 if (tc->needStrictChecks() &&
1127 lhs->pn_type == TOK_NAME) {
1128 JSAtom *atom = lhs->pn_atom;
1129 JSAtomState *atomState = &cx->runtime->atomState;
1130 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1131 const char *name = js_AtomToPrintableString(cx, atom);
1132 if (!name ||
1133 !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
1134 name)) {
1135 return false;
1139 return true;
1143 * Check that it is permitted to introduce a binding for atom. Strict
1144 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1145 * Use pn for reporting error locations, or use tc's token stream if
1146 * pn is NULL.
1148 bool
1149 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
1151 if (!tc->needStrictChecks())
1152 return true;
1154 JSAtomState *atomState = &cx->runtime->atomState;
1155 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1156 const char *name = js_AtomToPrintableString(cx, atom);
1157 if (name)
1158 ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
1159 return false;
1161 return true;
1165 * In strict mode code, all formal parameter names must be distinct. If fun's
1166 * formals are legit given fun's strictness level, return true. Otherwise,
1167 * report an error and return false. Use pn for error position reporting,
1168 * unless we can find something more accurate in tc's decls.
1170 * In some cases the code to parse the argument list will already have noticed
1171 * the duplication; we could try to use that knowledge instead of re-checking
1172 * here. But since the strictness of the function's body determines what
1173 * constraints to apply to the argument list, we can't report the error until
1174 * after we've parsed the body. And as it turns out, the function's local name
1175 * list makes it reasonably cheap to find duplicates after the fact.
1177 static bool
1178 CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
1179 JSParseNode *pn)
1181 JSAtom *atom;
1183 if (!tc->needStrictChecks())
1184 return true;
1186 atom = fun->findDuplicateFormal();
1187 if (atom) {
1189 * We have found a duplicate parameter name. If we can find the
1190 * JSDefinition for the argument, that will have a more accurate source
1191 * location.
1193 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1194 if (dn->pn_op == JSOP_GETARG)
1195 pn = dn;
1196 const char *name = js_AtomToPrintableString(cx, atom);
1197 if (!name ||
1198 !ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_DUPLICATE_FORMAL, name)) {
1199 return false;
1203 if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
1204 JSAtomState *atoms = &cx->runtime->atomState;
1205 atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS
1206 ? atoms->argumentsAtom : atoms->evalAtom);
1207 /* The definition's source position will be more precise. */
1208 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1209 JS_ASSERT(dn->pn_atom == atom);
1210 const char *name = js_AtomToPrintableString(cx, atom);
1211 if (!name ||
1212 !ReportStrictModeError(cx, TS(tc->parser), tc, dn, JSMSG_BAD_BINDING, name)) {
1213 return false;
1217 return true;
1220 JSParseNode *
1221 Parser::functionBody()
1223 JSStmtInfo stmtInfo;
1224 uintN oldflags, firstLine;
1225 JSParseNode *pn;
1227 JS_ASSERT(tc->inFunction());
1228 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1229 stmtInfo.flags = SIF_BODY_BLOCK;
1231 oldflags = tc->flags;
1232 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1235 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1236 * later, because we may have not peeked in tokenStream yet, so statements
1237 * won't acquire a valid pn->pn_pos.begin from the current token.
1239 firstLine = tokenStream.getLineno();
1240 #if JS_HAS_EXPR_CLOSURES
1241 if (tokenStream.currentToken().type == TOK_LC) {
1242 pn = statements();
1243 } else {
1244 pn = UnaryNode::create(tc);
1245 if (pn) {
1246 pn->pn_kid = assignExpr();
1247 if (!pn->pn_kid) {
1248 pn = NULL;
1249 } else {
1250 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1251 ReportBadReturn(context, tc, JSREPORT_ERROR,
1252 JSMSG_BAD_GENERATOR_RETURN,
1253 JSMSG_BAD_ANON_GENERATOR_RETURN);
1254 pn = NULL;
1255 } else {
1256 pn->pn_type = TOK_RETURN;
1257 pn->pn_op = JSOP_RETURN;
1258 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1263 #else
1264 pn = statements();
1265 #endif
1267 if (pn) {
1268 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1269 js_PopStatement(tc);
1270 pn->pn_pos.begin.lineno = firstLine;
1272 /* Check for falling off the end of a function that returns a value. */
1273 if (JS_HAS_STRICT_OPTION(context) && (tc->flags & TCF_RETURN_EXPR) &&
1274 !CheckFinalReturn(context, tc, pn)) {
1275 pn = NULL;
1279 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1280 return pn;
1283 static JSAtomListElement *
1284 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1286 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, pn->pn_atom);
1287 if (!ale)
1288 return NULL;
1290 JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
1291 if (!dn)
1292 return NULL;
1294 ALE_SET_DEFN(ale, dn);
1295 dn->pn_defn = true;
1296 dn->pn_dflags |= PND_PLACEHOLDER;
1297 return ale;
1300 static bool
1301 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1303 JS_ASSERT(!pn->pn_used);
1304 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1306 JSHashEntry **hep;
1307 JSAtomListElement *ale = NULL;
1308 JSAtomList *list = NULL;
1310 if (let)
1311 ale = (list = &tc->decls)->rawLookup(atom, hep);
1312 if (!ale)
1313 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1315 if (ale) {
1316 JSDefinition *dn = ALE_DEFN(ale);
1317 if (dn != pn) {
1318 JSParseNode **pnup = &dn->dn_uses;
1319 JSParseNode *pnu;
1320 uintN start = let ? pn->pn_blockid : tc->bodyid;
1322 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1323 JS_ASSERT(pnu->pn_used);
1324 pnu->pn_lexdef = (JSDefinition *) pn;
1325 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1326 pnup = &pnu->pn_link;
1329 if (pnu != dn->dn_uses) {
1330 *pnup = pn->dn_uses;
1331 pn->dn_uses = dn->dn_uses;
1332 dn->dn_uses = pnu;
1334 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1335 list->rawRemove(tc->parser, ale, hep);
1340 ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1341 if (!ale)
1342 return false;
1343 ALE_SET_DEFN(ale, pn);
1344 pn->pn_defn = true;
1345 pn->pn_dflags &= ~PND_PLACEHOLDER;
1346 return true;
1349 static void
1350 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1352 JS_ASSERT(!pn->pn_used);
1353 JS_ASSERT(!pn->pn_defn);
1354 JS_ASSERT(pn != dn->dn_uses);
1355 pn->pn_link = dn->dn_uses;
1356 dn->dn_uses = pn;
1357 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1358 pn->pn_used = true;
1359 pn->pn_lexdef = dn;
1362 static void
1363 ForgetUse(JSParseNode *pn)
1365 if (!pn->pn_used) {
1366 JS_ASSERT(!pn->pn_defn);
1367 return;
1370 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1371 JSParseNode *pnu;
1372 while ((pnu = *pnup) != pn)
1373 pnup = &pnu->pn_link;
1374 *pnup = pn->pn_link;
1375 pn->pn_used = false;
1378 static JSParseNode *
1379 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1381 JSParseNode *lhs = NewOrRecycledNode(tc);
1382 if (!lhs)
1383 return NULL;
1384 *lhs = *pn;
1386 if (pn->pn_used) {
1387 JSDefinition *dn = pn->pn_lexdef;
1388 JSParseNode **pnup = &dn->dn_uses;
1390 while (*pnup != pn)
1391 pnup = &(*pnup)->pn_link;
1392 *pnup = lhs;
1393 lhs->pn_link = pn->pn_link;
1394 pn->pn_link = NULL;
1397 pn->pn_type = TOK_ASSIGN;
1398 pn->pn_op = JSOP_NOP;
1399 pn->pn_arity = PN_BINARY;
1400 pn->pn_parens = false;
1401 pn->pn_used = pn->pn_defn = false;
1402 pn->pn_left = lhs;
1403 pn->pn_right = rhs;
1404 return lhs;
1407 static JSParseNode *
1408 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1411 * If dn is var, const, or let, and it has an initializer, then we must
1412 * rewrite it to be an assignment node, whose freshly allocated left-hand
1413 * side becomes a use of pn.
1415 if (dn->isBindingForm()) {
1416 JSParseNode *rhs = dn->expr();
1417 if (rhs) {
1418 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1419 if (!lhs)
1420 return NULL;
1421 //pn->dn_uses = lhs;
1422 dn = (JSDefinition *) lhs;
1425 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1426 } else if (dn->kind() == JSDefinition::FUNCTION) {
1427 JS_ASSERT(dn->isTopLevel());
1428 JS_ASSERT(dn->pn_op == JSOP_NOP);
1429 dn->pn_type = TOK_NAME;
1430 dn->pn_arity = PN_NAME;
1431 dn->pn_atom = atom;
1434 /* Now make dn no longer a definition, rather a use of pn. */
1435 JS_ASSERT(dn->pn_type == TOK_NAME);
1436 JS_ASSERT(dn->pn_arity == PN_NAME);
1437 JS_ASSERT(dn->pn_atom == atom);
1439 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1440 JS_ASSERT(pnu->pn_used);
1441 JS_ASSERT(!pnu->pn_defn);
1442 pnu->pn_lexdef = (JSDefinition *) pn;
1443 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1445 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1446 pn->dn_uses = dn;
1448 dn->pn_defn = false;
1449 dn->pn_used = true;
1450 dn->pn_lexdef = (JSDefinition *) pn;
1451 dn->pn_cookie.makeFree();
1452 dn->pn_dflags &= ~PND_BOUND;
1453 return dn;
1456 static bool
1457 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1459 JSParseNode *argpn, *argsbody;
1461 /* Flag tc so we don't have to lookup arguments on every use. */
1462 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1463 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1464 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1465 tc->flags |= TCF_FUN_PARAM_EVAL;
1468 * Make an argument definition node, distinguished by being in tc->decls
1469 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1470 * list node returned via pn->pn_body.
1472 argpn = NameNode::create(atom, tc);
1473 if (!argpn)
1474 return false;
1475 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1477 /* Arguments are initialized by definition. */
1478 argpn->pn_dflags |= PND_INITIALIZED;
1479 if (!Define(argpn, atom, tc))
1480 return false;
1482 argsbody = pn->pn_body;
1483 if (!argsbody) {
1484 argsbody = ListNode::create(tc);
1485 if (!argsbody)
1486 return false;
1487 argsbody->pn_type = TOK_ARGSBODY;
1488 argsbody->pn_op = JSOP_NOP;
1489 argsbody->makeEmpty();
1490 pn->pn_body = argsbody;
1492 argsbody->append(argpn);
1494 argpn->pn_op = JSOP_GETARG;
1495 argpn->pn_cookie.set(tc->staticLevel, i);
1496 argpn->pn_dflags |= PND_BOUND;
1497 return true;
1501 * Compile a JS function body, which might appear as the value of an event
1502 * handler attribute in an HTML <INPUT> tag.
1504 bool
1505 Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1506 const jschar *chars, size_t length,
1507 const char *filename, uintN lineno)
1509 Compiler compiler(cx, principals);
1511 if (!compiler.init(chars, length, NULL, filename, lineno))
1512 return false;
1514 /* No early return from after here until the js_FinishArenaPool calls. */
1515 JSArenaPool codePool, notePool;
1516 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1517 &cx->scriptStackQuota);
1518 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
1519 &cx->scriptStackQuota);
1521 Parser &parser = compiler.parser;
1522 TokenStream &tokenStream = parser.tokenStream;
1524 JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
1525 if (!funcg.init())
1526 return NULL;
1528 funcg.flags |= TCF_IN_FUNCTION;
1529 funcg.fun = fun;
1530 if (!GenerateBlockId(&funcg, funcg.bodyid))
1531 return NULL;
1533 /* FIXME: make Function format the source for a function definition. */
1534 tokenStream.mungeCurrentToken(TOK_NAME);
1535 JSParseNode *fn = FunctionNode::create(&funcg);
1536 if (fn) {
1537 fn->pn_body = NULL;
1538 fn->pn_cookie.makeFree();
1540 uintN nargs = fun->nargs;
1541 if (nargs) {
1542 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1543 if (!names) {
1544 fn = NULL;
1545 } else {
1546 for (uintN i = 0; i < nargs; i++) {
1547 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1548 if (!DefineArg(fn, name, i, &funcg)) {
1549 fn = NULL;
1550 break;
1558 * Farble the body so that it looks like a block statement to js_EmitTree,
1559 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1560 * done parsing, we must fold constants, analyze any nested functions, and
1561 * generate code for this function, including a stop opcode at the end.
1563 tokenStream.mungeCurrentToken(TOK_LC);
1564 JSParseNode *pn = fn ? parser.functionBody() : NULL;
1565 if (pn) {
1566 if (!CheckStrictFormals(cx, &funcg, fun, pn)) {
1567 pn = NULL;
1568 } else if (!tokenStream.matchToken(TOK_EOF)) {
1569 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1570 pn = NULL;
1571 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1572 /* js_FoldConstants reported the error already. */
1573 pn = NULL;
1574 } else if (funcg.functionList &&
1575 !parser.analyzeFunctions(funcg.functionList, funcg.flags)) {
1576 pn = NULL;
1577 } else {
1578 if (fn->pn_body) {
1579 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1580 fn->pn_body->append(pn);
1581 fn->pn_body->pn_pos = pn->pn_pos;
1582 pn = fn->pn_body;
1585 if (!js_EmitFunctionScript(cx, &funcg, pn))
1586 pn = NULL;
1590 /* Restore saved state and release code generation arenas. */
1591 JS_FinishArenaPool(&codePool);
1592 JS_FinishArenaPool(&notePool);
1593 return pn != NULL;
1597 * Parameter block types for the several Binder functions. We use a common
1598 * helper function signature in order to share code among destructuring and
1599 * simple variable declaration parsers. In the destructuring case, the binder
1600 * function is called indirectly from the variable declaration parser by way
1601 * of CheckDestructuring and its friends.
1603 typedef JSBool
1604 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1606 struct BindData {
1607 BindData() : fresh(true) {}
1609 JSParseNode *pn; /* name node for definition processing and
1610 error source coordinates */
1611 JSOp op; /* prolog bytecode or nop */
1612 Binder binder; /* binder, discriminates u */
1613 union {
1614 struct {
1615 uintN overflow;
1616 } let;
1618 bool fresh;
1621 static JSBool
1622 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1623 JSLocalKind localKind, bool isArg)
1625 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1628 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1629 * Instead 'var arguments' always restates the predefined property of the
1630 * activation objects whose name is 'arguments'. Assignment to such a
1631 * variable must be handled specially.
1633 * Special case: an argument named 'arguments' *does* shadow the predefined
1634 * arguments property.
1636 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1637 return JS_TRUE;
1639 return js_AddLocal(cx, fun, atom, localKind);
1642 #if JS_HAS_DESTRUCTURING
1643 static JSBool
1644 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1645 JSTreeContext *tc)
1647 JSParseNode *pn;
1649 /* Flag tc so we don't have to lookup arguments on every use. */
1650 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1651 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1652 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1653 tc->flags |= TCF_FUN_PARAM_EVAL;
1655 JS_ASSERT(tc->inFunction());
1657 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1658 if (localKind != JSLOCAL_NONE) {
1659 ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
1660 JSMSG_DESTRUCT_DUP_ARG);
1661 return JS_FALSE;
1663 JS_ASSERT(!tc->decls.lookup(atom));
1665 pn = data->pn;
1666 if (!Define(pn, atom, tc))
1667 return JS_FALSE;
1669 uintN index = tc->fun->u.i.nvars;
1670 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1671 return JS_FALSE;
1672 pn->pn_op = JSOP_SETLOCAL;
1673 pn->pn_cookie.set(tc->staticLevel, index);
1674 pn->pn_dflags |= PND_BOUND;
1675 return JS_TRUE;
1677 #endif /* JS_HAS_DESTRUCTURING */
1679 JSFunction *
1680 Parser::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1682 JSObject *parent;
1683 JSFunction *fun;
1685 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1688 * Find the global compilation context in order to pre-set the newborn
1689 * function's parent slot to tc->scopeChain. If the global context is a
1690 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1691 * clear parent and proto.
1693 while (tc->parent)
1694 tc = tc->parent;
1695 parent = tc->inFunction() ? NULL : tc->scopeChain;
1697 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1698 parent, atom);
1700 if (fun && !tc->compileAndGo()) {
1701 FUN_OBJECT(fun)->clearParent();
1702 FUN_OBJECT(fun)->clearProto();
1704 return fun;
1707 static JSBool
1708 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
1710 TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
1711 if (tt == TOK_ERROR)
1712 return JS_FALSE;
1713 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1714 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
1715 return JS_FALSE;
1717 (void) ts->matchToken(TOK_SEMI);
1718 return JS_TRUE;
1721 bool
1722 Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
1724 if (!markFunArgs(funbox, tcflags))
1725 return false;
1726 setFunctionKinds(funbox, tcflags);
1727 return true;
1731 * Mark as funargs any functions that reach up to one or more upvars across an
1732 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1734 * function f(o, p) {
1735 * o.m = function o_m(a) {
1736 * function g() { return p; }
1737 * function h() { return a; }
1738 * return g() + h();
1742 * but without this extra marking phase, function g will not be marked as a
1743 * funarg since it is called from within its parent scope. But g reaches up to
1744 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1745 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1746 * nor uses an upvar "above" o_m's level.
1748 * If function g itself contained lambdas that contained non-lambdas that reach
1749 * up above its level, then those non-lambdas would have to be marked too. This
1750 * process is potentially exponential in the number of functions, but generally
1751 * not so complex. But it can't be done during a single recursive traversal of
1752 * the funbox tree, so we must use a work queue.
1754 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1755 * between the static level of the bodies of funbox and its peers (which must
1756 * be funbox->level + 1), and the static level of the nearest upvar among all
1757 * the upvars contained by funbox and its peers. If there are no upvars, return
1758 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1760 static uintN
1761 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1763 uintN allskipmin = UpvarCookie::FREE_LEVEL;
1765 do {
1766 JSParseNode *fn = funbox->node;
1767 JSFunction *fun = (JSFunction *) funbox->object;
1768 int fnlevel = level;
1771 * An eval can leak funbox, functions along its ancestor line, and its
1772 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1773 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1774 * already been marked as funargs by this point. Therefore we have to
1775 * flag only funbox->node and funbox->kids' nodes here.
1777 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1778 fn->setFunArg();
1779 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1780 kid->node->setFunArg();
1784 * Compute in skipmin the least distance from fun's static level up to
1785 * an upvar, whether used directly by fun, or indirectly by a function
1786 * nested in fun.
1788 uintN skipmin = UpvarCookie::FREE_LEVEL;
1789 JSParseNode *pn = fn->pn_body;
1791 if (pn->pn_type == TOK_UPVARS) {
1792 JSAtomList upvars(pn->pn_names);
1793 JS_ASSERT(upvars.count != 0);
1795 JSAtomListIterator iter(&upvars);
1796 JSAtomListElement *ale;
1798 while ((ale = iter()) != NULL) {
1799 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1801 if (!lexdep->isFreeVar()) {
1802 uintN upvarLevel = lexdep->frameLevel();
1804 if (int(upvarLevel) <= fnlevel)
1805 fn->setFunArg();
1807 uintN skip = (funbox->level + 1) - upvarLevel;
1808 if (skip < skipmin)
1809 skipmin = skip;
1815 * If this function escapes, whether directly (the parser detects such
1816 * escapes) or indirectly (because this non-escaping function uses an
1817 * upvar that reaches across an outer function boundary where the outer
1818 * function escapes), enqueue it for further analysis, and bump fnlevel
1819 * to trap any non-escaping children.
1821 if (fn->isFunArg()) {
1822 queue->push(funbox);
1823 fnlevel = int(funbox->level);
1827 * Now process the current function's children, and recalibrate their
1828 * cumulative skipmin to be relative to the current static level.
1830 if (funbox->kids) {
1831 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1833 JS_ASSERT(kidskipmin != 0);
1834 if (kidskipmin != UpvarCookie::FREE_LEVEL) {
1835 --kidskipmin;
1836 if (kidskipmin != 0 && kidskipmin < skipmin)
1837 skipmin = kidskipmin;
1842 * Finally, after we've traversed all of the current function's kids,
1843 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1844 * with allskipmin, but minimize across funbox and all of its siblings,
1845 * to compute our return value.
1847 if (skipmin != UpvarCookie::FREE_LEVEL) {
1848 fun->u.i.skipmin = skipmin;
1849 if (skipmin < allskipmin)
1850 allskipmin = skipmin;
1852 } while ((funbox = funbox->siblings) != NULL);
1854 return allskipmin;
1857 bool
1858 Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1860 JSFunctionBoxQueue queue;
1861 if (!queue.init(functionCount))
1862 return false;
1864 FindFunArgs(funbox, -1, &queue);
1865 while ((funbox = queue.pull()) != NULL) {
1866 JSParseNode *fn = funbox->node;
1867 JS_ASSERT(fn->isFunArg());
1869 JSParseNode *pn = fn->pn_body;
1870 if (pn->pn_type == TOK_UPVARS) {
1871 JSAtomList upvars(pn->pn_names);
1872 JS_ASSERT(upvars.count != 0);
1874 JSAtomListIterator iter(&upvars);
1875 JSAtomListElement *ale;
1877 while ((ale = iter()) != NULL) {
1878 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1880 if (!lexdep->isFreeVar() &&
1881 !lexdep->isFunArg() &&
1882 (lexdep->kind() == JSDefinition::FUNCTION ||
1883 PN_OP(lexdep) == JSOP_CALLEE)) {
1885 * Mark this formerly-Algol-like function as an escaping
1886 * function (i.e., as a funarg), because it is used from a
1887 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1888 * access upvars.
1890 * Progress is guaranteed because we set the funarg flag
1891 * here, which suppresses revisiting this function (thanks
1892 * to the !lexdep->isFunArg() test just above).
1894 lexdep->setFunArg();
1896 JSFunctionBox *afunbox;
1897 if (PN_OP(lexdep) == JSOP_CALLEE) {
1899 * A named function expression will not appear to be a
1900 * funarg if it is immediately applied. However, if its
1901 * name is used in an escaping function nested within
1902 * it, then it must become flagged as a funarg again.
1903 * See bug 545980.
1905 afunbox = funbox;
1906 uintN calleeLevel = lexdep->pn_cookie.level();
1907 uintN staticLevel = afunbox->level + 1U;
1908 while (staticLevel != calleeLevel) {
1909 afunbox = afunbox->parent;
1910 --staticLevel;
1912 afunbox->node->setFunArg();
1913 } else {
1914 afunbox = lexdep->pn_funbox;
1916 queue.push(afunbox);
1919 * Walk over nested functions again, now that we have
1920 * changed the level across which it is unsafe to access
1921 * upvars using the runtime dynamic link (frame chain).
1923 if (afunbox->kids)
1924 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1929 return true;
1932 static uint32
1933 MinBlockId(JSParseNode *fn, uint32 id)
1935 if (fn->pn_blockid < id)
1936 return false;
1937 if (fn->pn_defn) {
1938 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1939 if (pn->pn_blockid < id)
1940 return false;
1943 return true;
1946 static inline bool
1947 CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
1950 * Consider the current function (the lambda, innermost below) using a var
1951 * x defined two static levels up:
1953 * function f() {
1954 * // z = g();
1955 * var x = 42;
1956 * function g() {
1957 * return function () { return x; };
1959 * return g();
1962 * So long as (1) the initialization in 'var x = 42' dominates all uses of
1963 * g and (2) x is not reassigned, it is safe to optimize the lambda to a
1964 * flat closure. Uncommenting the early call to g makes this optimization
1965 * unsafe (z could name a global setter that calls its argument).
1967 JSFunctionBox *afunbox = funbox;
1968 uintN dnLevel = dn->frameLevel();
1970 JS_ASSERT(dnLevel <= funbox->level);
1971 while (afunbox->level != dnLevel) {
1972 afunbox = afunbox->parent;
1975 * NB: afunbox can't be null because we are sure to find a function box
1976 * whose level == dnLevel before we would try to walk above the root of
1977 * the funbox tree. See bug 493260 comments 16-18.
1979 * Assert but check anyway, to protect future changes that bind eval
1980 * upvars in the parser.
1982 JS_ASSERT(afunbox);
1985 * If this function is reaching up across an enclosing funarg, then we
1986 * cannot copy dn's value into a flat closure slot (the display stops
1987 * working once the funarg escapes).
1989 if (!afunbox || afunbox->node->isFunArg())
1990 return false;
1993 * Reaching up for dn across a generator also means we can't flatten,
1994 * since the generator iterator does not run until later, in general.
1995 * See bug 563034.
1997 if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
1998 return false;
2002 * If afunbox's function (which is at the same level as dn) is in a loop,
2003 * pessimistically assume the variable initializer may be in the same loop.
2004 * A flat closure would then be unsafe, as the captured variable could be
2005 * assigned after the closure is created. See bug 493232.
2007 if (afunbox->inLoop)
2008 return false;
2011 * |with| and eval used as an operator defeat lexical scoping: they can be
2012 * used to assign to any in-scope variable. Therefore they must disable
2013 * flat closures that use such upvars. The parser detects these as special
2014 * forms and marks the function heavyweight.
2016 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
2017 return false;
2020 * If afunbox's function is not a lambda, it will be hoisted, so it could
2021 * capture the undefined value that by default initializes var, let, and
2022 * const bindings. And if dn is a function that comes at (meaning a
2023 * function refers to its own name) or strictly after afunbox, we also
2024 * defeat the flat closure optimization for this dn.
2026 JSFunction *afun = (JSFunction *) afunbox->object;
2027 if (!(afun->flags & JSFUN_LAMBDA)) {
2028 if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
2029 return false;
2032 if (!dn->isInitialized())
2033 return false;
2035 JSDefinition::Kind dnKind = dn->kind();
2036 if (dnKind != JSDefinition::CONST) {
2037 if (dn->isAssigned())
2038 return false;
2041 * Any formal could be mutated behind our back via the arguments
2042 * object, so deoptimize if the outer function uses arguments.
2044 * In a Function constructor call where the final argument -- the body
2045 * source for the function to create -- contains a nested function
2046 * definition or expression, afunbox->parent will be null. The body
2047 * source might use |arguments| outside of any nested functions it may
2048 * contain, so we have to check the tcflags parameter that was passed
2049 * in from Compiler::compileFunctionBody.
2051 if (dnKind == JSDefinition::ARG &&
2052 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
2053 return false;
2058 * Check quick-and-dirty dominance relation. Function definitions dominate
2059 * their uses thanks to hoisting. Other binding forms hoist as undefined,
2060 * of course, so check forward-reference and blockid relations.
2062 if (dnKind != JSDefinition::FUNCTION) {
2064 * Watch out for code such as
2066 * (function () {
2067 * ...
2068 * var jQuery = ... = function (...) {
2069 * return new jQuery.foo.bar(baz);
2071 * ...
2072 * })();
2074 * where the jQuery variable is not reassigned, but of course is not
2075 * initialized at the time that the would-be-flat closure containing
2076 * the jQuery upvar is formed.
2078 if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
2079 return false;
2080 if (!MinBlockId(afunbox->node, dn->pn_blockid))
2081 return false;
2083 return true;
2086 static void
2087 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2089 uintN dnLevel = dn->frameLevel();
2091 while ((funbox = funbox->parent) != NULL) {
2093 * Notice that funbox->level is the static level of the definition or
2094 * expression of the function parsed into funbox, not the static level
2095 * of its body. Therefore we must add 1 to match dn's level to find the
2096 * funbox whose body contains the dn definition.
2098 if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
2099 funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2100 break;
2102 funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
2105 if (!funbox && (tcflags & TCF_IN_FUNCTION))
2106 tcflags |= TCF_FUN_HEAVYWEIGHT;
2109 static void
2110 DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2112 uintN ndeoptimized = 0;
2113 const TokenPos &pos = funbox->node->pn_body->pn_pos;
2115 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2116 JS_ASSERT(pnu->pn_used);
2117 JS_ASSERT(!pnu->pn_defn);
2118 if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
2119 pnu->pn_dflags |= PND_DEOPTIMIZED;
2120 ++ndeoptimized;
2124 if (ndeoptimized != 0)
2125 FlagHeavyweights(dn, funbox, tcflags);
2128 void
2129 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
2131 #ifdef JS_FUNCTION_METERING
2132 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2133 #else
2134 # define FUN_METER(x) ((void)0)
2135 #endif
2137 for (;;) {
2138 JSParseNode *fn = funbox->node;
2139 JSParseNode *pn = fn->pn_body;
2141 if (funbox->kids) {
2142 setFunctionKinds(funbox->kids, tcflags);
2145 * We've unwound from recursively setting our kids' kinds, which
2146 * also classifies enclosing functions holding upvars referenced in
2147 * those descendants' bodies. So now we can check our "methods".
2149 * Despecialize from branded method-identity-based shape to sprop-
2150 * or slot-based shape if this function smells like a constructor
2151 * and too many of its methods are *not* joinable null closures
2152 * (i.e., they have one or more upvars fetched via the display).
2154 JSParseNode *pn2 = pn;
2155 if (PN_TYPE(pn2) == TOK_UPVARS)
2156 pn2 = pn2->pn_tree;
2157 if (PN_TYPE(pn2) == TOK_ARGSBODY)
2158 pn2 = pn2->last();
2160 #if JS_HAS_EXPR_CLOSURES
2161 if (PN_TYPE(pn2) == TOK_LC)
2162 #endif
2163 if (!(funbox->tcflags & TCF_RETURN_EXPR)) {
2164 uintN methodSets = 0, slowMethodSets = 0;
2166 for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
2167 JS_ASSERT(PN_OP(method) == JSOP_LAMBDA || PN_OP(method) == JSOP_LAMBDA_FC);
2168 ++methodSets;
2169 if (!method->pn_funbox->joinable())
2170 ++slowMethodSets;
2173 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
2174 funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
2178 JSFunction *fun = (JSFunction *) funbox->object;
2180 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2182 FUN_METER(allfun);
2183 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2184 FUN_METER(heavy);
2185 } else if (pn->pn_type != TOK_UPVARS) {
2187 * No lexical dependencies => null closure, for best performance.
2188 * A null closure needs no scope chain, but alas we've coupled
2189 * principals-finding to scope (for good fundamental reasons, but
2190 * the implementation overloads the parent slot and we should fix
2191 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2193 * In more detail: the ES3 spec allows the implementation to create
2194 * "joined function objects", or not, at its discretion. But real-
2195 * world implementations always create unique function objects for
2196 * closures, and this can be detected via mutation. Open question:
2197 * do popular implementations create unique function objects for
2198 * null closures?
2200 * FIXME: bug 476950.
2202 FUN_METER(nofreeupvar);
2203 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2204 } else {
2205 JSAtomList upvars(pn->pn_names);
2206 JS_ASSERT(upvars.count != 0);
2208 JSAtomListIterator iter(&upvars);
2209 JSAtomListElement *ale;
2211 if (!fn->isFunArg()) {
2213 * This function is Algol-like, it never escapes. So long as it
2214 * does not assign to outer variables, it needs only an upvars
2215 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2216 * bytecode to reach up the frame stack at runtime based on
2217 * those upvars' cookies.
2219 * Any assignments to upvars from functions called by this one
2220 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2221 * which load from stack homes when interpreting or from native
2222 * stack slots when executing a trace.
2224 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2225 * nested function to assign to an outer lexical variable, so
2226 * we defer adding yet more code footprint in the absence of
2227 * evidence motivating these opcodes.
2229 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
2230 uintN nupvars = 0;
2233 * Check that at least one outer lexical binding was assigned
2234 * to (global variables don't count). This is conservative: we
2235 * could limit assignments to those in the current function,
2236 * but that's too much work. As with flat closures (handled
2237 * below), we optimize for the case where outer bindings are
2238 * not reassigned anywhere.
2240 while ((ale = iter()) != NULL) {
2241 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2243 if (!lexdep->isFreeVar()) {
2244 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2245 ++nupvars;
2246 if (lexdep->isAssigned())
2247 break;
2250 if (!ale)
2251 mutation = false;
2253 if (nupvars == 0) {
2254 FUN_METER(onlyfreevar);
2255 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2256 } else if (!mutation &&
2257 !(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
2259 * Algol-like functions can read upvars using the dynamic
2260 * link (cx->fp/fp->down), optimized using the cx->display
2261 * lookup table indexed by static level. They do not need
2262 * to entrain and search their environment objects.
2264 FUN_METER(display);
2265 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2266 } else {
2267 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
2268 FUN_METER(setupvar);
2270 } else {
2271 uintN nupvars = 0, nflattened = 0;
2274 * For each lexical dependency from this closure to an outer
2275 * binding, analyze whether it is safe to copy the binding's
2276 * value into a flat closure slot when the closure is formed.
2278 while ((ale = iter()) != NULL) {
2279 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2281 if (!lexdep->isFreeVar()) {
2282 ++nupvars;
2283 if (CanFlattenUpvar(lexdep, funbox, tcflags)) {
2284 ++nflattened;
2285 continue;
2287 DeoptimizeUsesWithin(lexdep, funbox, tcflags);
2291 if (nupvars == 0) {
2292 FUN_METER(onlyfreevar);
2293 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2294 } else if (nflattened == nupvars) {
2295 /* FIXME bug 545759: to test nflattened != 0 */
2297 * We made it all the way through the upvar loop, so it's
2298 * safe to optimize to a flat closure.
2300 FUN_METER(flat);
2301 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2302 switch (PN_OP(fn)) {
2303 case JSOP_DEFFUN:
2304 fn->pn_op = JSOP_DEFFUN_FC;
2305 break;
2306 case JSOP_DEFLOCALFUN:
2307 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2308 break;
2309 case JSOP_LAMBDA:
2310 fn->pn_op = JSOP_LAMBDA_FC;
2311 break;
2312 default:
2313 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2314 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2316 } else {
2317 FUN_METER(badfunarg);
2322 if (FUN_KIND(fun) == JSFUN_INTERPRETED && pn->pn_type == TOK_UPVARS) {
2324 * One or more upvars cannot be safely snapshot into a flat
2325 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2326 * all upvars, and for each non-free upvar, ensure that its
2327 * containing function has been flagged as heavyweight.
2329 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2330 * generating any code for a tree of nested functions.
2332 JSAtomList upvars(pn->pn_names);
2333 JS_ASSERT(upvars.count != 0);
2335 JSAtomListIterator iter(&upvars);
2336 JSAtomListElement *ale;
2338 while ((ale = iter()) != NULL) {
2339 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2340 if (!lexdep->isFreeVar())
2341 FlagHeavyweights(lexdep, funbox, tcflags);
2345 funbox = funbox->siblings;
2346 if (!funbox)
2347 break;
2350 #undef FUN_METER
2353 const char js_argument_str[] = "argument";
2354 const char js_variable_str[] = "variable";
2355 const char js_unknown_str[] = "unknown";
2357 const char *
2358 JSDefinition::kindString(Kind kind)
2360 static const char *table[] = {
2361 js_var_str, js_const_str, js_let_str,
2362 js_function_str, js_argument_str, js_unknown_str
2365 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2366 return table[kind];
2369 static JSFunctionBox *
2370 EnterFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2371 uintN lambda = JSFUN_LAMBDA)
2373 JSTreeContext *tc = funtc->parent;
2374 JSFunction *fun = tc->parser->newFunction(tc, funAtom, lambda);
2375 if (!fun)
2376 return NULL;
2378 /* Create box for fun->object early to protect against last-ditch GC. */
2379 JSFunctionBox *funbox = tc->parser->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2380 if (!funbox)
2381 return NULL;
2383 /* Initialize non-default members of funtc. */
2384 funtc->flags |= funbox->tcflags;
2385 funtc->blockidGen = tc->blockidGen;
2386 if (!GenerateBlockId(funtc, funtc->bodyid))
2387 return NULL;
2388 funtc->fun = fun;
2389 funtc->funbox = funbox;
2390 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2391 return NULL;
2393 return funbox;
2396 static bool
2397 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2398 uintN lambda = JSFUN_LAMBDA)
2400 JSTreeContext *tc = funtc->parent;
2401 tc->blockidGen = funtc->blockidGen;
2403 JSFunctionBox *funbox = fn->pn_funbox;
2404 funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
2406 fn->pn_dflags |= PND_INITIALIZED;
2407 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2408 fn->pn_dflags & PND_TOPLEVEL);
2409 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2410 fn->pn_dflags |= PND_BLOCKCHILD;
2413 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2414 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2415 * params and body. We do this only if there are lexical dependencies not
2416 * satisfied by the function's declarations, to avoid penalizing functions
2417 * that use only their arguments and other local bindings.
2419 if (funtc->lexdeps.count != 0) {
2420 JSAtomListIterator iter(&funtc->lexdeps);
2421 JSAtomListElement *ale;
2422 int foundCallee = 0;
2424 while ((ale = iter()) != NULL) {
2425 JSAtom *atom = ALE_ATOM(ale);
2426 JSDefinition *dn = ALE_DEFN(ale);
2427 JS_ASSERT(dn->isPlaceholder());
2429 if (atom == funAtom && lambda != 0) {
2430 dn->pn_op = JSOP_CALLEE;
2431 dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
2432 dn->pn_dflags |= PND_BOUND;
2435 * If this named function expression uses its own name other
2436 * than to call itself, flag this function specially.
2438 if (dn->isFunArg())
2439 funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2440 foundCallee = 1;
2441 continue;
2444 if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2445 dn->isAssigned()) {
2447 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2448 * any use of dn in funtc assigns. See NoteLValue for the easy
2449 * backward-reference case; this is the hard forward-reference
2450 * case where we pay a higher price.
2452 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2453 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2454 funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2455 break;
2460 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2461 if (!outer_ale)
2462 outer_ale = tc->lexdeps.lookup(atom);
2463 if (outer_ale) {
2465 * Insert dn's uses list at the front of outer_dn's list.
2467 * Without loss of generality or correctness, we allow a dn to
2468 * be in inner and outer lexdeps, since the purpose of lexdeps
2469 * is one-pass coordination of name use and definition across
2470 * functions, and if different dn's are used we'll merge lists
2471 * when leaving the inner function.
2473 * The dn == outer_dn case arises with generator expressions
2474 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2475 * case), and nowhere else, currently.
2477 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2479 if (dn != outer_dn) {
2480 JSParseNode **pnup = &dn->dn_uses;
2481 JSParseNode *pnu;
2483 while ((pnu = *pnup) != NULL) {
2484 pnu->pn_lexdef = outer_dn;
2485 pnup = &pnu->pn_link;
2489 * Make dn be a use that redirects to outer_dn, because we
2490 * can't replace dn with outer_dn in all the pn_namesets in
2491 * the AST where it may be. Instead we make it forward to
2492 * outer_dn. See JSDefinition::resolve.
2494 *pnup = outer_dn->dn_uses;
2495 outer_dn->dn_uses = dn;
2496 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2497 dn->pn_defn = false;
2498 dn->pn_used = true;
2499 dn->pn_lexdef = outer_dn;
2501 } else {
2502 /* Add an outer lexical dependency for ale's definition. */
2503 outer_ale = tc->lexdeps.add(tc->parser, atom);
2504 if (!outer_ale)
2505 return false;
2506 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2510 if (funtc->lexdeps.count - foundCallee != 0) {
2511 JSParseNode *body = fn->pn_body;
2513 fn->pn_body = NameSetNode::create(tc);
2514 if (!fn->pn_body)
2515 return false;
2517 fn->pn_body->pn_type = TOK_UPVARS;
2518 fn->pn_body->pn_pos = body->pn_pos;
2519 if (foundCallee)
2520 funtc->lexdeps.remove(tc->parser, funAtom);
2521 fn->pn_body->pn_names = funtc->lexdeps;
2522 fn->pn_body->pn_tree = body;
2525 funtc->lexdeps.clear();
2528 return true;
2531 JSParseNode *
2532 Parser::functionDef(uintN lambda, bool namePermitted)
2534 JSParseNode *pn, *body, *result;
2535 TokenKind tt;
2536 JSAtomListElement *ale;
2537 #if JS_HAS_DESTRUCTURING
2538 JSParseNode *item, *list = NULL;
2539 bool destructuringArg = false;
2540 JSAtom *duplicatedArg = NULL;
2541 #endif
2544 * Save the current op for later so we can tag the created function as a
2545 * getter/setter if necessary.
2547 JSOp op = tokenStream.currentToken().t_op;
2549 /* Make a TOK_FUNCTION node. */
2550 pn = FunctionNode::create(tc);
2551 if (!pn)
2552 return NULL;
2553 pn->pn_body = NULL;
2554 pn->pn_cookie.makeFree();
2557 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2558 * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
2560 * Also treat function sub-statements (non-lambda, non-top-level functions)
2561 * as escaping funargs, since we can't statically analyze their definitions
2562 * and uses.
2564 bool topLevel = tc->atTopLevel();
2565 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2567 /* Scan the optional function name into funAtom. */
2568 JSAtom *funAtom = NULL;
2569 if (namePermitted) {
2570 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
2571 if (tt == TOK_NAME) {
2572 funAtom = tokenStream.currentToken().t_atom;
2573 } else {
2574 if (lambda == 0 && (context->options & JSOPTION_ANONFUNFIX)) {
2575 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
2576 return NULL;
2578 tokenStream.ungetToken();
2583 * Record names for function statements in tc->decls so we know when to
2584 * avoid optimizing variable references that might name a function.
2586 if (lambda == 0 && funAtom) {
2587 ale = tc->decls.lookup(funAtom);
2588 if (ale) {
2589 JSDefinition *dn = ALE_DEFN(ale);
2590 JSDefinition::Kind dn_kind = dn->kind();
2592 JS_ASSERT(!dn->pn_used);
2593 JS_ASSERT(dn->pn_defn);
2595 if (JS_HAS_STRICT_OPTION(context) || dn_kind == JSDefinition::CONST) {
2596 const char *name = js_AtomToPrintableString(context, funAtom);
2597 if (!name ||
2598 !reportErrorNumber(NULL,
2599 (dn_kind != JSDefinition::CONST)
2600 ? JSREPORT_WARNING | JSREPORT_STRICT
2601 : JSREPORT_ERROR,
2602 JSMSG_REDECLARED_VAR,
2603 JSDefinition::kindString(dn_kind),
2604 name)) {
2605 return NULL;
2609 if (topLevel) {
2610 ALE_SET_DEFN(ale, pn);
2611 pn->pn_defn = true;
2612 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2614 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2615 return NULL;
2617 } else if (topLevel) {
2619 * If this function was used before it was defined, claim the
2620 * pre-created definition node for this function that primaryExpr
2621 * put in tc->lexdeps on first forward reference, and recycle pn.
2623 JSHashEntry **hep;
2625 ale = tc->lexdeps.rawLookup(funAtom, hep);
2626 if (ale) {
2627 JSDefinition *fn = ALE_DEFN(ale);
2629 JS_ASSERT(fn->pn_defn);
2630 fn->pn_type = TOK_FUNCTION;
2631 fn->pn_arity = PN_FUNC;
2632 fn->pn_pos.begin = pn->pn_pos.begin;
2633 fn->pn_body = NULL;
2634 fn->pn_cookie.makeFree();
2636 tc->lexdeps.rawRemove(tc->parser, ale, hep);
2637 RecycleTree(pn, tc);
2638 pn = fn;
2641 if (!Define(pn, funAtom, tc))
2642 return NULL;
2646 * A function nested at top level inside another's body needs only a
2647 * local variable to bind its name to its value, and not an activation
2648 * object property (it might also need the activation property, if the
2649 * outer function contains with statements, e.g., but the stack slot
2650 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2651 * JSOP_GETLOCAL bytecode).
2653 if (topLevel) {
2654 pn->pn_dflags |= PND_TOPLEVEL;
2656 if (tc->inFunction()) {
2657 JSLocalKind localKind;
2658 uintN index;
2661 * Define a local in the outer function so that BindNameToSlot
2662 * can properly optimize accesses. Note that we need a local
2663 * variable, not an argument, for the function statement. Thus
2664 * we add a variable even if a parameter with the given name
2665 * already exists.
2667 localKind = js_LookupLocal(context, tc->fun, funAtom, &index);
2668 switch (localKind) {
2669 case JSLOCAL_NONE:
2670 case JSLOCAL_ARG:
2671 index = tc->fun->u.i.nvars;
2672 if (!js_AddLocal(context, tc->fun, funAtom, JSLOCAL_VAR))
2673 return NULL;
2674 /* FALL THROUGH */
2676 case JSLOCAL_VAR:
2677 pn->pn_cookie.set(tc->staticLevel, index);
2678 pn->pn_dflags |= PND_BOUND;
2679 break;
2681 default:;
2687 JSTreeContext *outertc = tc;
2689 /* Initialize early for possible flags mutation via destructuringExpr. */
2690 JSTreeContext funtc(tc->parser);
2692 JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
2693 if (!funbox)
2694 return NULL;
2696 JSFunction *fun = (JSFunction *) funbox->object;
2698 if (op != JSOP_NOP)
2699 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2701 /* Now parse formal argument list and compute fun->nargs. */
2702 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2703 if (!tokenStream.matchToken(TOK_RP)) {
2704 do {
2705 tt = tokenStream.getToken();
2706 switch (tt) {
2707 #if JS_HAS_DESTRUCTURING
2708 case TOK_LB:
2709 case TOK_LC:
2711 BindData data;
2712 JSParseNode *lhs, *rhs;
2713 jsint slot;
2715 /* See comment below in the TOK_NAME case. */
2716 if (duplicatedArg)
2717 goto report_dup_and_destructuring;
2718 destructuringArg = true;
2721 * A destructuring formal parameter turns into one or more
2722 * local variables initialized from properties of a single
2723 * anonymous positional parameter, so here we must tweak our
2724 * binder and its data.
2726 data.pn = NULL;
2727 data.op = JSOP_DEFVAR;
2728 data.binder = BindDestructuringArg;
2729 lhs = destructuringExpr(&data, tt);
2730 if (!lhs)
2731 return NULL;
2734 * Adjust fun->nargs to count the single anonymous positional
2735 * parameter that is to be destructured.
2737 slot = fun->nargs;
2738 if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
2739 return NULL;
2742 * Synthesize a destructuring assignment from the single
2743 * anonymous positional parameter into the destructuring
2744 * left-hand-side expression and accumulate it in list.
2746 rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
2747 if (!rhs)
2748 return NULL;
2749 rhs->pn_type = TOK_NAME;
2750 rhs->pn_op = JSOP_GETARG;
2751 rhs->pn_cookie.set(funtc.staticLevel, slot);
2752 rhs->pn_dflags |= PND_BOUND;
2754 item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2755 if (!item)
2756 return NULL;
2757 if (!list) {
2758 list = ListNode::create(&funtc);
2759 if (!list)
2760 return NULL;
2761 list->pn_type = TOK_COMMA;
2762 list->makeEmpty();
2764 list->append(item);
2765 break;
2767 #endif /* JS_HAS_DESTRUCTURING */
2769 case TOK_NAME:
2771 JSAtom *atom = tokenStream.currentToken().t_atom;
2772 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2773 return NULL;
2774 #ifdef JS_HAS_DESTRUCTURING
2776 * ECMA-262 requires us to support duplicate parameter names, but if the
2777 * parameter list includes destructuring, we consider the code to have
2778 * opted in to higher standards, and forbid duplicates. We may see a
2779 * destructuring parameter later, so always note duplicates now.
2781 * Duplicates are warned about (strict option) or cause errors (strict
2782 * mode code), but we do those tests in one place below, after having
2783 * parsed the body.
2785 if (js_LookupLocal(context, fun, atom, NULL) != JSLOCAL_NONE) {
2786 duplicatedArg = atom;
2787 if (destructuringArg)
2788 goto report_dup_and_destructuring;
2790 #endif
2791 if (!js_AddLocal(context, fun, atom, JSLOCAL_ARG))
2792 return NULL;
2793 break;
2796 default:
2797 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
2798 /* FALL THROUGH */
2799 case TOK_ERROR:
2800 return NULL;
2802 #if JS_HAS_DESTRUCTURING
2803 report_dup_and_destructuring:
2804 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
2805 reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
2806 return NULL;
2807 #endif
2809 } while (tokenStream.matchToken(TOK_COMMA));
2811 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2814 #if JS_HAS_EXPR_CLOSURES
2815 tt = tokenStream.getToken(TSF_OPERAND);
2816 if (tt != TOK_LC) {
2817 tokenStream.ungetToken();
2818 fun->flags |= JSFUN_EXPR_CLOSURE;
2820 #else
2821 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2822 #endif
2824 body = functionBody();
2825 if (!body)
2826 return NULL;
2828 if (!CheckStrictBinding(context, &funtc, funAtom, pn))
2829 return NULL;
2831 if (!CheckStrictFormals(context, &funtc, fun, pn))
2832 return NULL;
2834 #if JS_HAS_EXPR_CLOSURES
2835 if (tt == TOK_LC)
2836 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2837 else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
2838 return NULL;
2839 #else
2840 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2841 #endif
2842 pn->pn_pos.end = tokenStream.currentToken().pos.end;
2844 #if JS_HAS_DESTRUCTURING
2846 * If there were destructuring formal parameters, prepend the initializing
2847 * comma expression that we synthesized to body. If the body is a lexical
2848 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2849 * parameter destructuring code without bracing the decompilation of the
2850 * function body's lexical scope.
2852 if (list) {
2853 if (body->pn_arity != PN_LIST) {
2854 JSParseNode *block;
2856 block = ListNode::create(outertc);
2857 if (!block)
2858 return NULL;
2859 block->pn_type = TOK_SEQ;
2860 block->pn_pos = body->pn_pos;
2861 block->initList(body);
2863 body = block;
2866 item = UnaryNode::create(outertc);
2867 if (!item)
2868 return NULL;
2870 item->pn_type = TOK_SEMI;
2871 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2872 item->pn_kid = list;
2873 item->pn_next = body->pn_head;
2874 body->pn_head = item;
2875 if (body->pn_tail == &body->pn_head)
2876 body->pn_tail = &item->pn_next;
2877 ++body->pn_count;
2878 body->pn_xflags |= PNX_DESTRUCT;
2880 #endif
2883 * If we collected flags that indicate nested heavyweight functions, or
2884 * this function contains heavyweight-making statements (with statement,
2885 * visible eval call, or assignment to 'arguments'), flag the function as
2886 * heavyweight (requiring a call object per invocation).
2888 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2889 fun->flags |= JSFUN_HEAVYWEIGHT;
2890 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2891 } else {
2893 * If this function is a named statement function not at top-level
2894 * (i.e. not a top-level function definiton or expression), then our
2895 * enclosing function, if any, must be heavyweight.
2897 if (!topLevel && lambda == 0 && funAtom)
2898 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2901 result = pn;
2902 if (lambda != 0) {
2904 * ECMA ed. 3 standard: function expression, possibly anonymous.
2906 op = JSOP_LAMBDA;
2907 } else if (!funAtom) {
2909 * If this anonymous function definition is *not* embedded within a
2910 * larger expression, we treat it as an expression statement, not as
2911 * a function declaration -- and not as a syntax error (as ECMA-262
2912 * Edition 3 would have it). Backward compatibility must trump all,
2913 * unless JSOPTION_ANONFUNFIX is set.
2915 result = UnaryNode::create(outertc);
2916 if (!result)
2917 return NULL;
2918 result->pn_type = TOK_SEMI;
2919 result->pn_pos = pn->pn_pos;
2920 result->pn_kid = pn;
2921 op = JSOP_LAMBDA;
2922 } else if (!topLevel) {
2924 * ECMA ed. 3 extension: a function expression statement not at the
2925 * top level, e.g., in a compound statement such as the "then" part
2926 * of an "if" statement, binds a closure only if control reaches that
2927 * sub-statement.
2929 op = JSOP_DEFFUN;
2930 } else {
2931 op = JSOP_NOP;
2934 funbox->kids = funtc.functionList;
2936 pn->pn_funbox = funbox;
2937 pn->pn_op = op;
2938 if (pn->pn_body) {
2939 pn->pn_body->append(body);
2940 pn->pn_body->pn_pos = body->pn_pos;
2941 } else {
2942 pn->pn_body = body;
2945 pn->pn_blockid = outertc->blockid();
2947 if (!LeaveFunction(pn, &funtc, funAtom, lambda))
2948 return NULL;
2950 /* If the surrounding function is not strict code, reset the lexer. */
2951 if (!(outertc->flags & TCF_STRICT_MODE_CODE))
2952 tokenStream.setStrictMode(false);
2954 return result;
2957 JSParseNode *
2958 Parser::functionStmt()
2960 return functionDef(0, true);
2963 JSParseNode *
2964 Parser::functionExpr()
2966 return functionDef(JSFUN_LAMBDA, true);
2970 * Recognize Directive Prologue members and directives. Assuming pn
2971 * is a candidate for membership in a directive prologue, return
2972 * true if it is in fact a member. Recognize directives and set
2973 * tc's flags accordingly.
2975 * Note that the following is a strict mode function:
2977 * function foo() {
2978 * "blah" // inserted semi colon
2979 * "blurgh"
2980 * "use\x20loose"
2981 * "use strict"
2984 * That is, a statement can be a Directive Prologue member, even
2985 * if it can't possibly be a directive, now or in the future.
2987 bool
2988 Parser::recognizeDirectivePrologue(JSParseNode *pn)
2990 if (!pn->isDirectivePrologueMember())
2991 return false;
2992 if (pn->isDirective()) {
2993 JSAtom *directive = pn->pn_kid->pn_atom;
2994 if (directive == context->runtime->atomState.useStrictAtom) {
2995 tc->flags |= TCF_STRICT_MODE_CODE;
2996 tokenStream.setStrictMode();
2999 return true;
3003 * Parse the statements in a block, creating a TOK_LC node that lists the
3004 * statements' trees. If called from block-parsing code, the caller must
3005 * match { before and } after.
3007 JSParseNode *
3008 Parser::statements()
3010 JSParseNode *pn, *pn2, *saveBlock;
3011 TokenKind tt;
3012 bool inDirectivePrologue = tc->atTopLevel();
3014 JS_CHECK_RECURSION(context, return NULL);
3016 pn = ListNode::create(tc);
3017 if (!pn)
3018 return NULL;
3019 pn->pn_type = TOK_LC;
3020 pn->makeEmpty();
3021 pn->pn_blockid = tc->blockid();
3022 saveBlock = tc->blockNode;
3023 tc->blockNode = pn;
3025 for (;;) {
3026 tt = tokenStream.peekToken(TSF_OPERAND);
3027 if (tt <= TOK_EOF || tt == TOK_RC) {
3028 if (tt == TOK_ERROR) {
3029 if (tokenStream.isEOF())
3030 tokenStream.setUnexpectedEOF();
3031 return NULL;
3033 break;
3035 pn2 = statement();
3036 if (!pn2) {
3037 if (tokenStream.isEOF())
3038 tokenStream.setUnexpectedEOF();
3039 return NULL;
3042 if (inDirectivePrologue)
3043 inDirectivePrologue = recognizeDirectivePrologue(pn2);
3045 if (pn2->pn_type == TOK_FUNCTION) {
3047 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3048 * level function definitions that should be processed before the
3049 * rest of nodes.
3051 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3052 * is relevant only for function definitions not at top-level,
3053 * which we call function statements.
3055 if (tc->atTopLevel())
3056 pn->pn_xflags |= PNX_FUNCDEFS;
3057 else
3058 tc->flags |= TCF_HAS_FUNCTION_STMT;
3060 pn->append(pn2);
3064 * Handle the case where there was a let declaration under this block. If
3065 * it replaced tc->blockNode with a new block node then we must refresh pn
3066 * and then restore tc->blockNode.
3068 if (tc->blockNode != pn)
3069 pn = tc->blockNode;
3070 tc->blockNode = saveBlock;
3072 pn->pn_pos.end = tokenStream.currentToken().pos.end;
3073 return pn;
3076 JSParseNode *
3077 Parser::condition()
3079 JSParseNode *pn;
3081 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3082 pn = parenExpr(NULL, NULL);
3083 if (!pn)
3084 return NULL;
3085 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3087 /* Check for (a = b) and warn about possible (a == b) mistype. */
3088 if (pn->pn_type == TOK_ASSIGN &&
3089 pn->pn_op == JSOP_NOP &&
3090 !pn->pn_parens &&
3091 !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, "")) {
3092 return NULL;
3094 return pn;
3097 static JSBool
3098 MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
3100 JSAtom *label;
3101 TokenKind tt;
3103 tt = ts->peekTokenSameLine();
3104 if (tt == TOK_ERROR)
3105 return JS_FALSE;
3106 if (tt == TOK_NAME) {
3107 (void) ts->getToken();
3108 label = ts->currentToken().t_atom;
3109 } else {
3110 label = NULL;
3112 pn->pn_atom = label;
3113 return JS_TRUE;
3116 static JSBool
3117 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3119 JSParseNode *pn;
3120 JSObject *blockObj;
3121 JSAtomListElement *ale;
3122 jsint n;
3125 * Top-level 'let' is the same as 'var' currently -- this may change in a
3126 * successor standard to ES3.1 that specifies 'let'.
3128 JS_ASSERT(!tc->atTopLevel());
3130 pn = data->pn;
3131 if (!CheckStrictBinding(cx, tc, atom, pn))
3132 return false;
3134 blockObj = tc->blockChain;
3135 ale = tc->decls.lookup(atom);
3136 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3137 const char *name = js_AtomToPrintableString(cx, atom);
3138 if (name) {
3139 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3140 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3141 (ale && ALE_DEFN(ale)->isConst())
3142 ? js_const_str
3143 : js_variable_str,
3144 name);
3146 return false;
3149 n = OBJ_BLOCK_COUNT(cx, blockObj);
3150 if (n == JS_BIT(16)) {
3151 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3152 JSREPORT_ERROR, data->let.overflow);
3153 return false;
3157 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3158 * This is balanced by PopStatement, defined immediately below.
3160 if (!Define(pn, atom, tc, true))
3161 return false;
3164 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3165 * upvar cookie whose skip tells the current static level. The emitter will
3166 * adjust the node's slot based on its stack depth model -- and, for global
3167 * and eval code, Compiler::compileScript will adjust the slot again to
3168 * include script->nfixed.
3170 pn->pn_op = JSOP_GETLOCAL;
3171 pn->pn_cookie.set(tc->staticLevel, n);
3172 pn->pn_dflags |= PND_LET | PND_BOUND;
3175 * Define the let binding's property before storing pn in reserved slot at
3176 * reserved slot index (NB: not slot number) n.
3178 if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
3179 return false;
3182 * Store pn temporarily in what would be reserved slots in a cloned block
3183 * object (once the prototype's final population is known, after all 'let'
3184 * bindings for this block have been parsed). We will free these reserved
3185 * slots in jsemit.cpp:EmitEnterBlock.
3187 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3188 if (slot >= blockObj->numSlots() && !blockObj->growSlots(cx, slot + 1))
3189 return false;
3190 blockObj->scope()->freeslot = slot + 1;
3191 blockObj->setSlot(slot, PRIVATE_TO_JSVAL(pn));
3192 return true;
3195 static void
3196 PopStatement(JSTreeContext *tc)
3198 JSStmtInfo *stmt = tc->topStmt;
3200 if (stmt->flags & SIF_SCOPE) {
3201 JSObject *obj = stmt->blockObj;
3202 JSScope *scope = obj->scope();
3203 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3205 for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
3206 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3208 /* Beware the empty destructuring dummy. */
3209 if (atom == tc->parser->context->runtime->atomState.emptyAtom)
3210 continue;
3211 tc->decls.remove(tc->parser, atom);
3214 js_PopStatement(tc);
3217 static inline bool
3218 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3220 while (stmt->downScope) {
3221 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3222 if (!stmt)
3223 return false;
3224 if (stmt->type == STMT_BLOCK)
3225 return true;
3227 return false;
3231 * If we are generating global or eval-called-from-global code, bind a "gvar"
3232 * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3233 * global variable access by memoizing name-to-slot mappings during execution
3234 * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3235 * can't be done due to a pre-existing property of the same name as the var or
3236 * const but incompatible attributes/getter/setter/etc, these ops devolve to
3237 * JSOP_NAME, etc.
3239 * For now, don't try to lookup eval frame variables at compile time. This is
3240 * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3241 * gets its own script and frame. The eval-from-function-code case is harder,
3242 * since functions do not atomize gvars and then reserve their atom indexes as
3243 * stack frame slots.
3245 static bool
3246 BindGvar(JSParseNode *pn, JSTreeContext *tc, bool inWith = false)
3248 JS_ASSERT(pn->pn_op == JSOP_NAME);
3249 JS_ASSERT(!tc->inFunction());
3251 if (tc->compiling() && !tc->parser->callerFrame) {
3252 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3254 /* Index pn->pn_atom so we can map fast global number to name. */
3255 JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
3256 if (!ale)
3257 return false;
3259 /* Defend against cg->ngvars 16-bit overflow. */
3260 uintN slot = ALE_INDEX(ale);
3261 if ((slot + 1) >> 16)
3262 return true;
3264 if ((uint16)(slot + 1) > cg->ngvars)
3265 cg->ngvars = (uint16)(slot + 1);
3267 if (!inWith) {
3268 pn->pn_op = JSOP_GETGVAR;
3269 pn->pn_cookie.set(tc->staticLevel, slot);
3270 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3274 return true;
3277 static JSBool
3278 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3280 JSParseNode *pn = data->pn;
3282 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3283 pn->pn_op = JSOP_NAME;
3285 if (!CheckStrictBinding(cx, tc, atom, pn))
3286 return false;
3288 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3290 if (stmt && stmt->type == STMT_WITH) {
3291 data->fresh = false;
3292 return tc->inFunction() || BindGvar(pn, tc, true);
3295 JSAtomListElement *ale = tc->decls.lookup(atom);
3296 JSOp op = data->op;
3298 if (stmt || ale) {
3299 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3300 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3301 const char *name;
3303 if (dn_kind == JSDefinition::ARG) {
3304 name = js_AtomToPrintableString(cx, atom);
3305 if (!name)
3306 return JS_FALSE;
3308 if (op == JSOP_DEFCONST) {
3309 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3310 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3311 name);
3312 return JS_FALSE;
3314 if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3315 JSREPORT_WARNING | JSREPORT_STRICT,
3316 JSMSG_VAR_HIDES_ARG, name)) {
3317 return JS_FALSE;
3319 } else {
3320 bool error = (op == JSOP_DEFCONST ||
3321 dn_kind == JSDefinition::CONST ||
3322 (dn_kind == JSDefinition::LET &&
3323 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3325 if (JS_HAS_STRICT_OPTION(cx)
3326 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3327 : error) {
3328 name = js_AtomToPrintableString(cx, atom);
3329 if (!name ||
3330 !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3331 !error
3332 ? JSREPORT_WARNING | JSREPORT_STRICT
3333 : JSREPORT_ERROR,
3334 JSMSG_REDECLARED_VAR,
3335 JSDefinition::kindString(dn_kind),
3336 name)) {
3337 return JS_FALSE;
3343 if (!ale) {
3344 if (!Define(pn, atom, tc))
3345 return JS_FALSE;
3346 } else {
3348 * A var declaration never recreates an existing binding, it restates
3349 * it and possibly reinitializes its value. Beware that if pn becomes a
3350 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3351 * const (typically a const would ;-), then pn must be rewritten into a
3352 * TOK_ASSIGN node. See Variables, further below.
3354 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3355 * There the x definition is hoisted but the x = 2 assignment mutates
3356 * the block-local binding of x.
3358 JSDefinition *dn = ALE_DEFN(ale);
3360 data->fresh = false;
3362 if (!pn->pn_used) {
3363 /* Make pnu be a fresh name node that uses dn. */
3364 JSParseNode *pnu = pn;
3366 if (pn->pn_defn) {
3367 pnu = NameNode::create(atom, tc);
3368 if (!pnu)
3369 return JS_FALSE;
3372 LinkUseToDef(pnu, dn, tc);
3373 pnu->pn_op = JSOP_NAME;
3376 while (dn->kind() == JSDefinition::LET) {
3377 do {
3378 ale = ALE_NEXT(ale);
3379 } while (ale && ALE_ATOM(ale) != atom);
3380 if (!ale)
3381 break;
3382 dn = ALE_DEFN(ale);
3385 if (ale) {
3386 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3387 dn->kind() == JSDefinition::CONST);
3388 return JS_TRUE;
3392 * A var or const that is shadowed by one or more let bindings of the
3393 * same name, but that has not been declared until this point, must be
3394 * hoisted above the let bindings.
3396 if (!pn->pn_defn) {
3397 JSHashEntry **hep;
3399 ale = tc->lexdeps.rawLookup(atom, hep);
3400 if (ale) {
3401 pn = ALE_DEFN(ale);
3402 tc->lexdeps.rawRemove(tc->parser, ale, hep);
3403 } else {
3404 JSParseNode *pn2 = NameNode::create(atom, tc);
3405 if (!pn2)
3406 return JS_FALSE;
3408 /* The token stream may be past the location for pn. */
3409 pn2->pn_type = TOK_NAME;
3410 pn2->pn_pos = pn->pn_pos;
3411 pn = pn2;
3413 pn->pn_op = JSOP_NAME;
3416 ale = tc->decls.add(tc->parser, atom, JSAtomList::HOIST);
3417 if (!ale)
3418 return JS_FALSE;
3419 ALE_SET_DEFN(ale, pn);
3420 pn->pn_defn = true;
3421 pn->pn_dflags &= ~PND_PLACEHOLDER;
3424 if (data->op == JSOP_DEFCONST)
3425 pn->pn_dflags |= PND_CONST;
3427 if (!tc->inFunction())
3428 return BindGvar(pn, tc);
3430 if (atom == cx->runtime->atomState.argumentsAtom) {
3431 pn->pn_op = JSOP_ARGUMENTS;
3432 pn->pn_dflags |= PND_BOUND;
3433 return JS_TRUE;
3436 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3437 if (localKind == JSLOCAL_NONE) {
3439 * Property not found in current variable scope: we have not seen this
3440 * variable before. Define a new local variable by adding a property to
3441 * the function's scope and allocating one slot in the function's vars
3442 * frame. Any locals declared in a with statement body are handled at
3443 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3444 * and heavyweight-function-local vars.
3446 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3448 uintN index = tc->fun->u.i.nvars;
3449 if (!BindLocalVariable(cx, tc->fun, atom, localKind, false))
3450 return JS_FALSE;
3451 pn->pn_op = JSOP_GETLOCAL;
3452 pn->pn_cookie.set(tc->staticLevel, index);
3453 pn->pn_dflags |= PND_BOUND;
3454 return JS_TRUE;
3457 if (localKind == JSLOCAL_ARG) {
3458 /* We checked errors and strict warnings earlier -- see above. */
3459 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3460 } else {
3461 /* Not an argument, must be a redeclared local var. */
3462 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3464 return JS_TRUE;
3467 static JSBool
3468 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3470 JSParseNode *pn2;
3472 JS_ASSERT(pn->pn_arity == PN_LIST);
3473 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3474 pn2 = pn->pn_head;
3475 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3476 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
3477 return JS_FALSE;
3479 pn->pn_op = JSOP_SETCALL;
3480 return JS_TRUE;
3483 static void
3484 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3486 if (pn->pn_used) {
3487 JSDefinition *dn = pn->pn_lexdef;
3490 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3491 * occur as direct kids of the same block with no forward refs to x.
3493 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3494 dn->isBlockChild() &&
3495 pn->isBlockChild() &&
3496 dn->pn_blockid == pn->pn_blockid &&
3497 dn->pn_pos.end <= pn->pn_pos.begin &&
3498 dn->dn_uses == pn) {
3499 dflag = PND_INITIALIZED;
3502 dn->pn_dflags |= dflag;
3504 if (dn->pn_cookie.isFree() || dn->frameLevel() < tc->staticLevel)
3505 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3508 pn->pn_dflags |= dflag;
3510 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3511 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3514 #if JS_HAS_DESTRUCTURING
3516 static JSBool
3517 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3518 JSTreeContext *tc)
3520 JSAtom *atom;
3523 * Destructuring is a form of assignment, so just as for an initialized
3524 * simple variable, we must check for assignment to 'arguments' and flag
3525 * the enclosing function (if any) as heavyweight.
3527 JS_ASSERT(pn->pn_type == TOK_NAME);
3528 atom = pn->pn_atom;
3529 if (atom == cx->runtime->atomState.argumentsAtom)
3530 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3532 data->pn = pn;
3533 if (!data->binder(cx, data, atom, tc))
3534 return JS_FALSE;
3537 * Select the appropriate name-setting opcode, respecting eager selection
3538 * done by the data->binder function.
3540 if (pn->pn_dflags & PND_BOUND) {
3541 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3542 ? JSOP_SETNAME
3543 : (pn->pn_dflags & PND_GVAR)
3544 ? JSOP_SETGVAR
3545 : JSOP_SETLOCAL;
3546 } else {
3547 pn->pn_op = (data->op == JSOP_DEFCONST)
3548 ? JSOP_SETCONST
3549 : JSOP_SETNAME;
3552 if (data->op == JSOP_DEFCONST)
3553 pn->pn_dflags |= PND_CONST;
3555 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3556 return JS_TRUE;
3560 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3561 * LHS expression except a destructuring initialiser, and R is on the stack.
3562 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3563 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3564 * then push its property name QN. At this point the stack looks like
3566 * [... R, R[P], QB, QN]
3568 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3569 * its operands with left-hand side above right-hand side:
3571 * [rval, lval, xval]
3573 * and pops all three values, setting lval[xval] = rval. But we cannot select
3574 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3575 * which can be optimized further. So we select JSOP_SETNAME.
3577 static JSBool
3578 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3580 switch (pn->pn_type) {
3581 case TOK_NAME:
3582 NoteLValue(cx, pn, tc);
3583 /* FALL THROUGH */
3585 case TOK_DOT:
3586 case TOK_LB:
3588 * We may be called on a name node that has already been specialized,
3589 * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
3590 * case. See bug 558633.
3592 if (!(js_CodeSpec[pn->pn_op].format & JOF_SET))
3593 pn->pn_op = JSOP_SETNAME;
3594 break;
3596 case TOK_LP:
3597 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3598 return JS_FALSE;
3599 break;
3601 #if JS_HAS_XML_SUPPORT
3602 case TOK_UNARYOP:
3603 if (pn->pn_op == JSOP_XMLNAME) {
3604 pn->pn_op = JSOP_BINDXMLNAME;
3605 break;
3607 /* FALL THROUGH */
3608 #endif
3610 default:
3611 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3612 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3613 return JS_FALSE;
3616 return JS_TRUE;
3619 typedef struct FindPropValData {
3620 uint32 numvars; /* # of destructuring vars in left side */
3621 uint32 maxstep; /* max # of steps searching right side */
3622 JSDHashTable table; /* hash table for O(1) right side search */
3623 } FindPropValData;
3625 typedef struct FindPropValEntry {
3626 JSDHashEntryHdr hdr;
3627 JSParseNode *pnkey;
3628 JSParseNode *pnval;
3629 } FindPropValEntry;
3631 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3632 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3633 ((pnkey)->pn_type == TOK_NUMBER || \
3634 (pnkey)->pn_type == TOK_STRING || \
3635 (pnkey)->pn_type == TOK_NAME)) || \
3636 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3638 static JSDHashNumber
3639 HashFindPropValKey(JSDHashTable *table, const void *key)
3641 const JSParseNode *pnkey = (const JSParseNode *)key;
3643 ASSERT_VALID_PROPERTY_KEY(pnkey);
3644 return (pnkey->pn_type == TOK_NUMBER)
3645 ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
3646 : ATOM_HASH(pnkey->pn_atom);
3649 static JSBool
3650 MatchFindPropValEntry(JSDHashTable *table,
3651 const JSDHashEntryHdr *entry,
3652 const void *key)
3654 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3655 const JSParseNode *pnkey = (const JSParseNode *)key;
3657 ASSERT_VALID_PROPERTY_KEY(pnkey);
3658 return pnkey->pn_type == fpve->pnkey->pn_type &&
3659 ((pnkey->pn_type == TOK_NUMBER)
3660 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3661 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3664 static const JSDHashTableOps FindPropValOps = {
3665 JS_DHashAllocTable,
3666 JS_DHashFreeTable,
3667 HashFindPropValKey,
3668 MatchFindPropValEntry,
3669 JS_DHashMoveEntryStub,
3670 JS_DHashClearEntryStub,
3671 JS_DHashFinalizeStub,
3672 NULL
3675 #define STEP_HASH_THRESHOLD 10
3676 #define BIG_DESTRUCTURING 5
3677 #define BIG_OBJECT_INIT 20
3679 static JSParseNode *
3680 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3682 FindPropValEntry *entry;
3683 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3684 uint32 step;
3686 /* If we have a hash table, use it as the sole source of truth. */
3687 if (data->table.ops) {
3688 entry = (FindPropValEntry *)
3689 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3690 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3693 /* If pn is not an object initialiser node, we can't do anything here. */
3694 if (pn->pn_type != TOK_RC)
3695 return NULL;
3698 * We must search all the way through pn's list, to handle the case of an
3699 * id duplicated for two or more property initialisers.
3701 pnhit = NULL;
3702 step = 0;
3703 ASSERT_VALID_PROPERTY_KEY(pnid);
3704 pnhead = pn->pn_head;
3705 if (pnid->pn_type == TOK_NUMBER) {
3706 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3707 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3708 if (pnprop->pn_op == JSOP_NOP) {
3709 pnkey = pnprop->pn_left;
3710 ASSERT_VALID_PROPERTY_KEY(pnkey);
3711 if (pnkey->pn_type == TOK_NUMBER &&
3712 pnkey->pn_dval == pnid->pn_dval) {
3713 pnhit = pnprop;
3715 ++step;
3718 } else {
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 == pnid->pn_type &&
3725 pnkey->pn_atom == pnid->pn_atom) {
3726 pnhit = pnprop;
3728 ++step;
3732 if (!pnhit)
3733 return NULL;
3735 /* Hit via full search -- see whether it's time to create the hash table. */
3736 JS_ASSERT(!data->table.ops);
3737 if (step > data->maxstep) {
3738 data->maxstep = step;
3739 if (step >= STEP_HASH_THRESHOLD &&
3740 data->numvars >= BIG_DESTRUCTURING &&
3741 pn->pn_count >= BIG_OBJECT_INIT &&
3742 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3743 sizeof(FindPropValEntry),
3744 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3746 for (pn = pnhead; pn; pn = pn->pn_next) {
3747 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3748 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3749 entry = (FindPropValEntry *)
3750 JS_DHashTableOperate(&data->table, pn->pn_left,
3751 JS_DHASH_ADD);
3752 entry->pnval = pn->pn_right;
3756 return pnhit->pn_right;
3760 * Destructuring patterns can appear in two kinds of contexts:
3762 * - assignment-like: assignment expressions and |for| loop heads. In
3763 * these cases, the patterns' property value positions can be
3764 * arbitrary lvalue expressions; the destructuring is just a fancy
3765 * assignment.
3767 * - declaration-like: |var| and |let| declarations, functions' formal
3768 * parameter lists, |catch| clauses, and comprehension tails. In
3769 * these cases, the patterns' property value positions must be
3770 * simple names; the destructuring defines them as new variables.
3772 * In both cases, other code parses the pattern as an arbitrary
3773 * primaryExpr, and then, here in CheckDestructuring, verify that the
3774 * tree is a valid destructuring expression.
3776 * In assignment-like contexts, we parse the pattern with the
3777 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3778 * pattern are parsed normally. primaryExpr links variable references
3779 * into the appropriate use chains; creates placeholder definitions;
3780 * and so on. CheckDestructuring is called with |data| NULL (since we
3781 * won't be binding any new names), and we specialize lvalues as
3782 * appropriate. If right is NULL, we just check for well-formed lvalues.
3784 * In declaration-like contexts, the normal variable reference
3785 * processing would just be an obstruction, because we're going to
3786 * define the names that appear in the property value positions as new
3787 * variables anyway. In this case, we parse the pattern with
3788 * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
3789 * whatever name nodes it creates unconnected. Then, here in
3790 * CheckDestructuring, we require the pattern's property value
3791 * positions to be simple names, and define them as appropriate to the
3792 * context. For these calls, |data| points to the right sort of
3793 * BindData.
3795 * See also UndominateInitializers, immediately below. If you change
3796 * either of these functions, you might have to change the other to
3797 * match.
3799 static JSBool
3800 CheckDestructuring(JSContext *cx, BindData *data,
3801 JSParseNode *left, JSParseNode *right,
3802 JSTreeContext *tc)
3804 JSBool ok;
3805 FindPropValData fpvd;
3806 JSParseNode *lhs, *rhs, *pn, *pn2;
3808 if (left->pn_type == TOK_ARRAYCOMP) {
3809 ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
3810 JSMSG_ARRAY_COMP_LEFTSIDE);
3811 return JS_FALSE;
3814 #if JS_HAS_DESTRUCTURING_SHORTHAND
3815 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3816 ReportCompileErrorNumber(cx, TS(tc->parser), right, JSREPORT_ERROR,
3817 JSMSG_BAD_OBJECT_INIT);
3818 return JS_FALSE;
3820 #endif
3822 fpvd.table.ops = NULL;
3823 lhs = left->pn_head;
3824 if (left->pn_type == TOK_RB) {
3825 rhs = (right && right->pn_type == left->pn_type)
3826 ? right->pn_head
3827 : NULL;
3829 while (lhs) {
3830 pn = lhs, pn2 = rhs;
3832 /* Nullary comma is an elision; binary comma is an expression.*/
3833 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3834 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3835 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3836 } else {
3837 if (data) {
3838 if (pn->pn_type != TOK_NAME)
3839 goto no_var_name;
3841 ok = BindDestructuringVar(cx, data, pn, tc);
3842 } else {
3843 ok = BindDestructuringLHS(cx, pn, tc);
3846 if (!ok)
3847 goto out;
3850 lhs = lhs->pn_next;
3851 if (rhs)
3852 rhs = rhs->pn_next;
3854 } else {
3855 JS_ASSERT(left->pn_type == TOK_RC);
3856 fpvd.numvars = left->pn_count;
3857 fpvd.maxstep = 0;
3858 rhs = NULL;
3860 while (lhs) {
3861 JS_ASSERT(lhs->pn_type == TOK_COLON);
3862 pn = lhs->pn_right;
3864 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3865 if (right)
3866 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3867 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3868 } else if (data) {
3869 if (pn->pn_type != TOK_NAME)
3870 goto no_var_name;
3872 ok = BindDestructuringVar(cx, data, pn, tc);
3873 } else {
3874 ok = BindDestructuringLHS(cx, pn, tc);
3876 if (!ok)
3877 goto out;
3879 lhs = lhs->pn_next;
3884 * The catch/finally handler implementation in the interpreter assumes
3885 * that any operation that introduces a new scope (like a "let" or "with"
3886 * block) increases the stack depth. This way, it is possible to restore
3887 * the scope chain based on stack depth of the handler alone. "let" with
3888 * an empty destructuring pattern like in
3890 * let [] = 1;
3892 * would violate this assumption as the there would be no let locals to
3893 * store on the stack. To satisfy it we add an empty property to such
3894 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3895 * slots, would be always positive.
3897 * Note that we add such a property even if the block has locals due to
3898 * later let declarations in it. We optimize for code simplicity here,
3899 * not the fastest runtime performance with empty [] or {}.
3901 if (data &&
3902 data->binder == BindLet &&
3903 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3904 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3905 ATOM_TO_JSID(cx->runtime->
3906 atomState.emptyAtom),
3907 JSVAL_VOID, NULL, NULL,
3908 JSPROP_ENUMERATE |
3909 JSPROP_PERMANENT |
3910 JSPROP_SHARED,
3911 JSScopeProperty::HAS_SHORTID, 0, NULL);
3912 if (!ok)
3913 goto out;
3916 ok = JS_TRUE;
3918 out:
3919 if (fpvd.table.ops)
3920 JS_DHashTableFinish(&fpvd.table);
3921 return ok;
3923 no_var_name:
3924 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
3925 JSMSG_NO_VARIABLE_NAME);
3926 ok = JS_FALSE;
3927 goto out;
3931 * This is a greatly pared down version of CheckDestructuring that extends the
3932 * pn_pos.end source coordinate of each name in a destructuring binding such as
3934 * var [x, y] = [function () y, 42];
3936 * to cover its corresponding initializer, so that the initialized binding does
3937 * not appear to dominate any closures in its initializer. See bug 496134.
3939 * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
3940 * very precise. With one-pass SSA construction from structured source code
3941 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3942 * Languages", Brandis and Mössenböck), we could do much better.
3944 * See CheckDestructuring, immediately above. If you change either of these
3945 * functions, you might have to change the other to match.
3947 static JSBool
3948 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3950 FindPropValData fpvd;
3951 JSParseNode *lhs, *rhs;
3953 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3954 JS_ASSERT(right);
3956 #if JS_HAS_DESTRUCTURING_SHORTHAND
3957 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3958 ReportCompileErrorNumber(tc->parser->context, TS(tc->parser), right, JSREPORT_ERROR,
3959 JSMSG_BAD_OBJECT_INIT);
3960 return JS_FALSE;
3962 #endif
3964 if (right->pn_type != left->pn_type)
3965 return JS_TRUE;
3967 fpvd.table.ops = NULL;
3968 lhs = left->pn_head;
3969 if (left->pn_type == TOK_RB) {
3970 rhs = right->pn_head;
3972 while (lhs && rhs) {
3973 /* Nullary comma is an elision; binary comma is an expression.*/
3974 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3975 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3976 if (!UndominateInitializers(lhs, rhs, tc))
3977 return JS_FALSE;
3978 } else {
3979 lhs->pn_pos.end = rhs->pn_pos.end;
3983 lhs = lhs->pn_next;
3984 rhs = rhs->pn_next;
3986 } else {
3987 JS_ASSERT(left->pn_type == TOK_RC);
3988 fpvd.numvars = left->pn_count;
3989 fpvd.maxstep = 0;
3991 while (lhs) {
3992 JS_ASSERT(lhs->pn_type == TOK_COLON);
3993 JSParseNode *pn = lhs->pn_right;
3995 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3996 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3997 if (rhs && !UndominateInitializers(pn, rhs, tc))
3998 return JS_FALSE;
3999 } else {
4000 if (rhs)
4001 pn->pn_pos.end = rhs->pn_pos.end;
4004 lhs = lhs->pn_next;
4007 return JS_TRUE;
4010 JSParseNode *
4011 Parser::destructuringExpr(BindData *data, TokenKind tt)
4013 JSParseNode *pn;
4015 tc->flags |= TCF_DECL_DESTRUCTURING;
4016 pn = primaryExpr(tt, JS_FALSE);
4017 tc->flags &= ~TCF_DECL_DESTRUCTURING;
4018 if (!pn)
4019 return NULL;
4020 if (!CheckDestructuring(context, data, pn, NULL, tc))
4021 return NULL;
4022 return pn;
4026 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4027 * This function assumes the cloned tree is for use in the same statement and
4028 * binding context as the original tree.
4030 static JSParseNode *
4031 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4033 JSParseNode *pn, *pn2, *opn2;
4035 pn = NewOrRecycledNode(tc);
4036 if (!pn)
4037 return NULL;
4038 pn->pn_type = opn->pn_type;
4039 pn->pn_pos = opn->pn_pos;
4040 pn->pn_op = opn->pn_op;
4041 pn->pn_used = opn->pn_used;
4042 pn->pn_defn = opn->pn_defn;
4043 pn->pn_arity = opn->pn_arity;
4044 pn->pn_parens = opn->pn_parens;
4046 switch (pn->pn_arity) {
4047 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4049 case PN_FUNC:
4050 NULLCHECK(pn->pn_funbox =
4051 tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
4052 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4053 pn->pn_cookie = opn->pn_cookie;
4054 pn->pn_dflags = opn->pn_dflags;
4055 pn->pn_blockid = opn->pn_blockid;
4056 break;
4058 case PN_LIST:
4059 pn->makeEmpty();
4060 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4061 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4062 pn->append(pn2);
4064 pn->pn_xflags = opn->pn_xflags;
4065 break;
4067 case PN_TERNARY:
4068 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4069 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4070 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4071 break;
4073 case PN_BINARY:
4074 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4075 if (opn->pn_right != opn->pn_left)
4076 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4077 else
4078 pn->pn_right = pn->pn_left;
4079 pn->pn_val = opn->pn_val;
4080 pn->pn_iflags = opn->pn_iflags;
4081 break;
4083 case PN_UNARY:
4084 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4085 pn->pn_num = opn->pn_num;
4086 pn->pn_hidden = opn->pn_hidden;
4087 break;
4089 case PN_NAME:
4090 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4091 pn->pn_u = opn->pn_u;
4092 if (opn->pn_used) {
4094 * The old name is a use of its pn_lexdef. Make the clone also be a
4095 * use of that definition.
4097 JSDefinition *dn = pn->pn_lexdef;
4099 pn->pn_link = dn->dn_uses;
4100 dn->dn_uses = pn;
4101 } else if (opn->pn_expr) {
4102 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4105 * If the old name is a definition, the new one has pn_defn set.
4106 * Make the old name a use of the new node.
4108 if (opn->pn_defn) {
4109 opn->pn_defn = false;
4110 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4113 break;
4115 case PN_NAMESET:
4116 pn->pn_names = opn->pn_names;
4117 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4118 break;
4120 case PN_NULLARY:
4121 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4122 pn->pn_u = opn->pn_u;
4123 break;
4125 #undef NULLCHECK
4127 return pn;
4130 #endif /* JS_HAS_DESTRUCTURING */
4132 extern const char js_with_statement_str[];
4134 static JSParseNode *
4135 ContainsStmt(JSParseNode *pn, TokenKind tt)
4137 JSParseNode *pn2, *pnt;
4139 if (!pn)
4140 return NULL;
4141 if (PN_TYPE(pn) == tt)
4142 return pn;
4143 switch (pn->pn_arity) {
4144 case PN_LIST:
4145 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4146 pnt = ContainsStmt(pn2, tt);
4147 if (pnt)
4148 return pnt;
4150 break;
4151 case PN_TERNARY:
4152 pnt = ContainsStmt(pn->pn_kid1, tt);
4153 if (pnt)
4154 return pnt;
4155 pnt = ContainsStmt(pn->pn_kid2, tt);
4156 if (pnt)
4157 return pnt;
4158 return ContainsStmt(pn->pn_kid3, tt);
4159 case PN_BINARY:
4161 * Limit recursion if pn is a binary expression, which can't contain a
4162 * var statement.
4164 if (pn->pn_op != JSOP_NOP)
4165 return NULL;
4166 pnt = ContainsStmt(pn->pn_left, tt);
4167 if (pnt)
4168 return pnt;
4169 return ContainsStmt(pn->pn_right, tt);
4170 case PN_UNARY:
4171 if (pn->pn_op != JSOP_NOP)
4172 return NULL;
4173 return ContainsStmt(pn->pn_kid, tt);
4174 case PN_NAME:
4175 return ContainsStmt(pn->maybeExpr(), tt);
4176 case PN_NAMESET:
4177 return ContainsStmt(pn->pn_tree, tt);
4178 default:;
4180 return NULL;
4183 JSParseNode *
4184 Parser::returnOrYield(bool useAssignExpr)
4186 TokenKind tt, tt2;
4187 JSParseNode *pn, *pn2;
4189 tt = tokenStream.currentToken().type;
4190 if (tt == TOK_RETURN && !tc->inFunction()) {
4191 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4192 return NULL;
4195 pn = UnaryNode::create(tc);
4196 if (!pn)
4197 return NULL;
4199 #if JS_HAS_GENERATORS
4200 if (tt == TOK_YIELD)
4201 tc->flags |= TCF_FUN_IS_GENERATOR;
4202 #endif
4204 /* This is ugly, but we don't want to require a semicolon. */
4205 tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
4206 if (tt2 == TOK_ERROR)
4207 return NULL;
4209 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4210 #if JS_HAS_GENERATORS
4211 && (tt != TOK_YIELD ||
4212 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4213 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4214 #endif
4216 pn2 = useAssignExpr ? assignExpr() : expr();
4217 if (!pn2)
4218 return NULL;
4219 #if JS_HAS_GENERATORS
4220 if (tt == TOK_RETURN)
4221 #endif
4222 tc->flags |= TCF_RETURN_EXPR;
4223 pn->pn_pos.end = pn2->pn_pos.end;
4224 pn->pn_kid = pn2;
4225 } else {
4226 #if JS_HAS_GENERATORS
4227 if (tt == TOK_RETURN)
4228 #endif
4229 tc->flags |= TCF_RETURN_VOID;
4232 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4233 /* As in Python (see PEP-255), disallow return v; in generators. */
4234 ReportBadReturn(context, tc, JSREPORT_ERROR,
4235 JSMSG_BAD_GENERATOR_RETURN,
4236 JSMSG_BAD_ANON_GENERATOR_RETURN);
4237 return NULL;
4240 if (JS_HAS_STRICT_OPTION(context) &&
4241 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4242 !ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4243 JSMSG_NO_RETURN_VALUE,
4244 JSMSG_ANON_NO_RETURN_VALUE)) {
4245 return NULL;
4248 return pn;
4251 static JSParseNode *
4252 PushLexicalScope(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
4253 JSStmtInfo *stmt)
4255 JSParseNode *pn;
4256 JSObject *obj;
4257 JSObjectBox *blockbox;
4259 pn = LexicalScopeNode::create(tc);
4260 if (!pn)
4261 return NULL;
4263 obj = js_NewBlockObject(cx);
4264 if (!obj)
4265 return NULL;
4267 blockbox = tc->parser->newObjectBox(obj);
4268 if (!blockbox)
4269 return NULL;
4271 js_PushBlockScope(tc, stmt, obj, -1);
4272 pn->pn_type = TOK_LEXICALSCOPE;
4273 pn->pn_op = JSOP_LEAVEBLOCK;
4274 pn->pn_objbox = blockbox;
4275 pn->pn_cookie.makeFree();
4276 pn->pn_dflags = 0;
4277 if (!GenerateBlockId(tc, stmt->blockid))
4278 return NULL;
4279 pn->pn_blockid = stmt->blockid;
4280 return pn;
4283 #if JS_HAS_BLOCK_SCOPE
4285 JSParseNode *
4286 Parser::letBlock(JSBool statement)
4288 JSParseNode *pn, *pnblock, *pnlet;
4289 JSStmtInfo stmtInfo;
4291 JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
4293 /* Create the let binary node. */
4294 pnlet = BinaryNode::create(tc);
4295 if (!pnlet)
4296 return NULL;
4298 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4300 /* This is a let block or expression of the form: let (a, b, c) .... */
4301 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
4302 if (!pnblock)
4303 return NULL;
4304 pn = pnblock;
4305 pn->pn_expr = pnlet;
4307 pnlet->pn_left = variables(true);
4308 if (!pnlet->pn_left)
4309 return NULL;
4310 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4312 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4314 if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
4316 * If this is really an expression in let statement guise, then we
4317 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4318 * the return value of the expression.
4320 pn = UnaryNode::create(tc);
4321 if (!pn)
4322 return NULL;
4323 pn->pn_type = TOK_SEMI;
4324 pn->pn_num = -1;
4325 pn->pn_kid = pnblock;
4327 statement = JS_FALSE;
4330 if (statement) {
4331 pnlet->pn_right = statements();
4332 if (!pnlet->pn_right)
4333 return NULL;
4334 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4335 } else {
4337 * Change pnblock's opcode to the variant that propagates the last
4338 * result down after popping the block, and clear statement.
4340 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4341 pnlet->pn_right = assignExpr();
4342 if (!pnlet->pn_right)
4343 return NULL;
4346 PopStatement(tc);
4347 return pn;
4350 #endif /* JS_HAS_BLOCK_SCOPE */
4352 static bool
4353 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4355 js_PushStatement(tc, stmt, type, -1);
4356 return GenerateBlockId(tc, stmt->blockid);
4359 static JSParseNode *
4360 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4362 JSParseNode *pn = NULL;
4364 JSAtomListElement *ale = tc->decls.lookup(atom);
4365 if (ale) {
4366 pn = ALE_DEFN(ale);
4367 JS_ASSERT(!pn->isPlaceholder());
4368 } else {
4369 ale = tc->lexdeps.lookup(atom);
4370 if (ale) {
4371 pn = ALE_DEFN(ale);
4372 JS_ASSERT(pn->isPlaceholder());
4376 if (pn) {
4377 JS_ASSERT(pn->pn_defn);
4380 * A let binding at top level becomes a var before we get here, so if
4381 * pn and tc have the same blockid then that id must not be the bodyid.
4382 * If pn is a forward placeholder definition from the same or a higher
4383 * block then we claim it.
4385 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4386 pn->pn_blockid != tc->bodyid);
4388 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4389 if (let)
4390 pn->pn_blockid = tc->blockid();
4392 tc->lexdeps.remove(tc->parser, atom);
4393 return pn;
4397 /* Make a new node for this declarator name (or destructuring pattern). */
4398 pn = NameNode::create(atom, tc);
4399 if (!pn)
4400 return NULL;
4401 return pn;
4404 #if JS_HAS_BLOCK_SCOPE
4405 static bool
4406 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4408 if (!pn)
4409 return true;
4411 switch (pn->pn_arity) {
4412 case PN_LIST:
4413 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4414 RebindLets(pn2, tc);
4415 break;
4417 case PN_TERNARY:
4418 RebindLets(pn->pn_kid1, tc);
4419 RebindLets(pn->pn_kid2, tc);
4420 RebindLets(pn->pn_kid3, tc);
4421 break;
4423 case PN_BINARY:
4424 RebindLets(pn->pn_left, tc);
4425 RebindLets(pn->pn_right, tc);
4426 break;
4428 case PN_UNARY:
4429 RebindLets(pn->pn_kid, tc);
4430 break;
4432 case PN_FUNC:
4433 RebindLets(pn->pn_body, tc);
4434 break;
4436 case PN_NAME:
4437 RebindLets(pn->maybeExpr(), tc);
4439 if (pn->pn_defn) {
4440 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4441 } else if (pn->pn_used) {
4442 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4443 ForgetUse(pn);
4445 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4446 if (ale) {
4447 while ((ale = ALE_NEXT(ale)) != NULL) {
4448 if (ALE_ATOM(ale) == pn->pn_atom) {
4449 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4450 return true;
4455 ale = tc->lexdeps.lookup(pn->pn_atom);
4456 if (!ale) {
4457 ale = MakePlaceholder(pn, tc);
4458 if (!ale)
4459 return NULL;
4461 JSDefinition *dn = ALE_DEFN(ale);
4462 dn->pn_type = TOK_NAME;
4463 dn->pn_op = JSOP_NOP;
4465 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4468 break;
4470 case PN_NAMESET:
4471 RebindLets(pn->pn_tree, tc);
4472 break;
4475 return true;
4477 #endif /* JS_HAS_BLOCK_SCOPE */
4479 JSParseNode *
4480 Parser::statement()
4482 TokenKind tt;
4483 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4484 JSStmtInfo stmtInfo, *stmt, *stmt2;
4485 JSAtom *label;
4487 JS_CHECK_RECURSION(context, return NULL);
4489 tt = tokenStream.getToken(TSF_OPERAND);
4491 switch (tt) {
4492 case TOK_FUNCTION:
4493 #if JS_HAS_XML_SUPPORT
4494 tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
4495 if (tt == TOK_DBLCOLON)
4496 goto expression;
4497 #endif
4498 return functionStmt();
4500 case TOK_IF:
4501 /* An IF node has three kids: condition, then, and optional else. */
4502 pn = TernaryNode::create(tc);
4503 if (!pn)
4504 return NULL;
4505 pn1 = condition();
4506 if (!pn1)
4507 return NULL;
4508 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4509 pn2 = statement();
4510 if (!pn2)
4511 return NULL;
4512 if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
4513 stmtInfo.type = STMT_ELSE;
4514 pn3 = statement();
4515 if (!pn3)
4516 return NULL;
4517 pn->pn_pos.end = pn3->pn_pos.end;
4518 } else {
4519 pn3 = NULL;
4520 pn->pn_pos.end = pn2->pn_pos.end;
4522 PopStatement(tc);
4523 pn->pn_kid1 = pn1;
4524 pn->pn_kid2 = pn2;
4525 pn->pn_kid3 = pn3;
4526 return pn;
4528 case TOK_SWITCH:
4530 JSParseNode *pn5, *saveBlock;
4531 JSBool seenDefault = JS_FALSE;
4533 pn = BinaryNode::create(tc);
4534 if (!pn)
4535 return NULL;
4536 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4538 /* pn1 points to the switch's discriminant. */
4539 pn1 = parenExpr(NULL, NULL);
4540 if (!pn1)
4541 return NULL;
4543 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4544 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4547 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4548 * because that function states tc->topStmt->blockid.
4550 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4552 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4553 pn2 = ListNode::create(tc);
4554 if (!pn2)
4555 return NULL;
4556 pn2->makeEmpty();
4557 if (!GenerateBlockIdForStmtNode(pn2, tc))
4558 return NULL;
4559 saveBlock = tc->blockNode;
4560 tc->blockNode = pn2;
4562 while ((tt = tokenStream.getToken()) != TOK_RC) {
4563 switch (tt) {
4564 case TOK_DEFAULT:
4565 if (seenDefault) {
4566 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS);
4567 return NULL;
4569 seenDefault = JS_TRUE;
4570 /* FALL THROUGH */
4572 case TOK_CASE:
4573 pn3 = BinaryNode::create(tc);
4574 if (!pn3)
4575 return NULL;
4576 if (tt == TOK_CASE) {
4577 pn3->pn_left = expr();
4578 if (!pn3->pn_left)
4579 return NULL;
4581 pn2->append(pn3);
4582 if (pn2->pn_count == JS_BIT(16)) {
4583 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_CASES);
4584 return NULL;
4586 break;
4588 case TOK_ERROR:
4589 return NULL;
4591 default:
4592 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_SWITCH);
4593 return NULL;
4595 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4597 pn4 = ListNode::create(tc);
4598 if (!pn4)
4599 return NULL;
4600 pn4->pn_type = TOK_LC;
4601 pn4->makeEmpty();
4602 while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
4603 tt != TOK_CASE && tt != TOK_DEFAULT) {
4604 if (tt == TOK_ERROR)
4605 return NULL;
4606 pn5 = statement();
4607 if (!pn5)
4608 return NULL;
4609 pn4->pn_pos.end = pn5->pn_pos.end;
4610 pn4->append(pn5);
4613 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4614 if (pn4->pn_head)
4615 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4616 pn3->pn_pos.end = pn4->pn_pos.end;
4617 pn3->pn_right = pn4;
4621 * Handle the case where there was a let declaration in any case in
4622 * the switch body, but not within an inner block. If it replaced
4623 * tc->blockNode with a new block node then we must refresh pn2 and
4624 * then restore tc->blockNode.
4626 if (tc->blockNode != pn2)
4627 pn2 = tc->blockNode;
4628 tc->blockNode = saveBlock;
4629 PopStatement(tc);
4631 pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
4632 pn->pn_left = pn1;
4633 pn->pn_right = pn2;
4634 return pn;
4637 case TOK_WHILE:
4638 pn = BinaryNode::create(tc);
4639 if (!pn)
4640 return NULL;
4641 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4642 pn2 = condition();
4643 if (!pn2)
4644 return NULL;
4645 pn->pn_left = pn2;
4646 pn2 = statement();
4647 if (!pn2)
4648 return NULL;
4649 PopStatement(tc);
4650 pn->pn_pos.end = pn2->pn_pos.end;
4651 pn->pn_right = pn2;
4652 return pn;
4654 case TOK_DO:
4655 pn = BinaryNode::create(tc);
4656 if (!pn)
4657 return NULL;
4658 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4659 pn2 = statement();
4660 if (!pn2)
4661 return NULL;
4662 pn->pn_left = pn2;
4663 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4664 pn2 = condition();
4665 if (!pn2)
4666 return NULL;
4667 PopStatement(tc);
4668 pn->pn_pos.end = pn2->pn_pos.end;
4669 pn->pn_right = pn2;
4670 if (JSVERSION_NUMBER(context) != JSVERSION_ECMA_3) {
4672 * All legacy and extended versions must do automatic semicolon
4673 * insertion after do-while. See the testcase and discussion in
4674 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4676 (void) tokenStream.matchToken(TOK_SEMI);
4677 return pn;
4679 break;
4681 case TOK_FOR:
4683 JSParseNode *pnseq = NULL;
4684 #if JS_HAS_BLOCK_SCOPE
4685 JSParseNode *pnlet = NULL;
4686 JSStmtInfo blockInfo;
4687 #endif
4689 /* A FOR node is binary, left is loop control and right is the body. */
4690 pn = BinaryNode::create(tc);
4691 if (!pn)
4692 return NULL;
4693 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4695 pn->pn_op = JSOP_ITER;
4696 pn->pn_iflags = 0;
4697 if (tokenStream.matchToken(TOK_NAME)) {
4698 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
4699 pn->pn_iflags = JSITER_FOREACH;
4700 else
4701 tokenStream.ungetToken();
4704 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4705 tt = tokenStream.peekToken(TSF_OPERAND);
4707 #if JS_HAS_BLOCK_SCOPE
4708 bool let = false;
4709 #endif
4711 if (tt == TOK_SEMI) {
4712 if (pn->pn_iflags & JSITER_FOREACH)
4713 goto bad_for_each;
4715 /* No initializer -- set first kid of left sub-node to null. */
4716 pn1 = NULL;
4717 } else {
4719 * Set pn1 to a var list or an initializing expression.
4721 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4722 * of the for statement. This flag will be used by the RelExpr
4723 * production; if it is set, then the 'in' keyword will not be
4724 * recognized as an operator, leaving it available to be parsed as
4725 * part of a for/in loop.
4727 * A side effect of this restriction is that (unparenthesized)
4728 * expressions involving an 'in' operator are illegal in the init
4729 * clause of an ordinary for loop.
4731 tc->flags |= TCF_IN_FOR_INIT;
4732 if (tt == TOK_VAR) {
4733 (void) tokenStream.getToken();
4734 pn1 = variables(false);
4735 #if JS_HAS_BLOCK_SCOPE
4736 } else if (tt == TOK_LET) {
4737 let = true;
4738 (void) tokenStream.getToken();
4739 if (tokenStream.peekToken() == TOK_LP) {
4740 pn1 = letBlock(JS_FALSE);
4741 tt = TOK_LEXICALSCOPE;
4742 } else {
4743 pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
4744 if (!pnlet)
4745 return NULL;
4746 blockInfo.flags |= SIF_FOR_BLOCK;
4747 pn1 = variables(false);
4749 #endif
4750 } else {
4751 pn1 = expr();
4753 tc->flags &= ~TCF_IN_FOR_INIT;
4754 if (!pn1)
4755 return NULL;
4759 * We can be sure that it's a for/in loop if there's still an 'in'
4760 * keyword here, even if JavaScript recognizes 'in' as an operator,
4761 * as we've excluded 'in' from being parsed in RelExpr by setting
4762 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4764 if (pn1 && tokenStream.matchToken(TOK_IN)) {
4765 pn->pn_iflags |= JSITER_ENUMERATE;
4766 stmtInfo.type = STMT_FOR_IN_LOOP;
4768 /* Check that the left side of the 'in' is valid. */
4769 JS_ASSERT(!TokenKindIsDecl(tt) || PN_TYPE(pn1) == tt);
4770 if (TokenKindIsDecl(tt)
4771 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4772 #if JS_HAS_DESTRUCTURING
4773 || (JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4774 pn->pn_op == JSOP_ITER &&
4775 !(pn->pn_iflags & JSITER_FOREACH) &&
4776 (pn1->pn_head->pn_type == TOK_RC ||
4777 (pn1->pn_head->pn_type == TOK_RB &&
4778 pn1->pn_head->pn_count != 2) ||
4779 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4780 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4781 pn1->pn_head->pn_left->pn_count != 2))))
4782 #endif
4784 : (pn1->pn_type != TOK_NAME &&
4785 pn1->pn_type != TOK_DOT &&
4786 #if JS_HAS_DESTRUCTURING
4787 ((JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4788 pn->pn_op == JSOP_ITER &&
4789 !(pn->pn_iflags & JSITER_FOREACH))
4790 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4791 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4792 #endif
4793 pn1->pn_type != TOK_LP &&
4794 #if JS_HAS_XML_SUPPORT
4795 (pn1->pn_type != TOK_UNARYOP ||
4796 pn1->pn_op != JSOP_XMLNAME) &&
4797 #endif
4798 pn1->pn_type != TOK_LB)) {
4799 reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
4800 return NULL;
4803 /* pn2 points to the name or destructuring pattern on in's left. */
4804 pn2 = NULL;
4805 uintN dflag = PND_ASSIGNED;
4807 if (TokenKindIsDecl(tt)) {
4808 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4809 pn1->pn_xflags |= PNX_FORINVAR;
4812 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4813 * 'var', or 'const' to hoist the initializer or the entire
4814 * decl out of the loop head. TOK_VAR is the type for both
4815 * 'var' and 'const'.
4817 pn2 = pn1->pn_head;
4818 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4819 #if JS_HAS_DESTRUCTURING
4820 || pn2->pn_type == TOK_ASSIGN
4821 #endif
4823 pnseq = ListNode::create(tc);
4824 if (!pnseq)
4825 return NULL;
4826 pnseq->pn_type = TOK_SEQ;
4827 pnseq->pn_pos.begin = pn->pn_pos.begin;
4829 #if JS_HAS_BLOCK_SCOPE
4830 if (tt == TOK_LET) {
4832 * Hoist just the 'i' from 'for (let x = i in o)' to
4833 * before the loop, glued together via pnseq.
4835 pn3 = UnaryNode::create(tc);
4836 if (!pn3)
4837 return NULL;
4838 pn3->pn_type = TOK_SEMI;
4839 pn3->pn_op = JSOP_NOP;
4840 #if JS_HAS_DESTRUCTURING
4841 if (pn2->pn_type == TOK_ASSIGN) {
4842 pn4 = pn2->pn_right;
4843 pn2 = pn1->pn_head = pn2->pn_left;
4844 } else
4845 #endif
4847 pn4 = pn2->pn_expr;
4848 pn2->pn_expr = NULL;
4850 if (!RebindLets(pn4, tc))
4851 return NULL;
4852 pn3->pn_pos = pn4->pn_pos;
4853 pn3->pn_kid = pn4;
4854 pnseq->initList(pn3);
4855 } else
4856 #endif /* JS_HAS_BLOCK_SCOPE */
4858 dflag = PND_INITIALIZED;
4861 * All of 'var x = i' is hoisted above 'for (x in o)',
4862 * so clear PNX_FORINVAR.
4864 * Request JSOP_POP here since the var is for a simple
4865 * name (it is not a destructuring binding's left-hand
4866 * side) and it has an initializer.
4868 pn1->pn_xflags &= ~PNX_FORINVAR;
4869 pn1->pn_xflags |= PNX_POPVAR;
4870 pnseq->initList(pn1);
4872 #if JS_HAS_DESTRUCTURING
4873 if (pn2->pn_type == TOK_ASSIGN) {
4874 pn1 = CloneParseTree(pn2->pn_left, tc);
4875 if (!pn1)
4876 return NULL;
4877 } else
4878 #endif
4880 JS_ASSERT(pn2->pn_type == TOK_NAME);
4881 pn1 = NameNode::create(pn2->pn_atom, tc);
4882 if (!pn1)
4883 return NULL;
4884 pn1->pn_type = TOK_NAME;
4885 pn1->pn_op = JSOP_NAME;
4886 pn1->pn_pos = pn2->pn_pos;
4887 if (pn2->pn_defn)
4888 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4890 pn2 = pn1;
4895 if (!pn2) {
4896 pn2 = pn1;
4897 if (pn2->pn_type == TOK_LP &&
4898 !MakeSetCall(context, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4899 return NULL;
4901 #if JS_HAS_XML_SUPPORT
4902 if (pn2->pn_type == TOK_UNARYOP)
4903 pn2->pn_op = JSOP_BINDXMLNAME;
4904 #endif
4907 switch (pn2->pn_type) {
4908 case TOK_NAME:
4909 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4910 NoteLValue(context, pn2, tc, dflag);
4911 break;
4913 #if JS_HAS_DESTRUCTURING
4914 case TOK_ASSIGN:
4915 pn2 = pn2->pn_left;
4916 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4917 /* FALL THROUGH */
4918 case TOK_RB:
4919 case TOK_RC:
4920 /* Check for valid lvalues in var-less destructuring for-in. */
4921 if (pn1 == pn2 && !CheckDestructuring(context, NULL, pn2, NULL, tc))
4922 return NULL;
4924 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
4926 * Destructuring for-in requires [key, value] enumeration
4927 * in JS1.7.
4929 JS_ASSERT(pn->pn_op == JSOP_ITER);
4930 if (!(pn->pn_iflags & JSITER_FOREACH))
4931 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4933 break;
4934 #endif
4936 default:;
4940 * Parse the object expression as the right operand of 'in', first
4941 * removing the top statement from the statement-stack if this is a
4942 * 'for (let x in y)' loop.
4944 #if JS_HAS_BLOCK_SCOPE
4945 JSStmtInfo *save = tc->topStmt;
4946 if (let)
4947 tc->topStmt = save->down;
4948 #endif
4949 pn2 = expr();
4950 #if JS_HAS_BLOCK_SCOPE
4951 if (let)
4952 tc->topStmt = save;
4953 #endif
4955 pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4956 if (!pn2)
4957 return NULL;
4958 pn->pn_left = pn2;
4959 } else {
4960 if (pn->pn_iflags & JSITER_FOREACH)
4961 goto bad_for_each;
4962 pn->pn_op = JSOP_NOP;
4964 /* Parse the loop condition or null into pn2. */
4965 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4966 tt = tokenStream.peekToken(TSF_OPERAND);
4967 if (tt == TOK_SEMI) {
4968 pn2 = NULL;
4969 } else {
4970 pn2 = expr();
4971 if (!pn2)
4972 return NULL;
4975 /* Parse the update expression or null into pn3. */
4976 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4977 tt = tokenStream.peekToken(TSF_OPERAND);
4978 if (tt == TOK_RP) {
4979 pn3 = NULL;
4980 } else {
4981 pn3 = expr();
4982 if (!pn3)
4983 return NULL;
4986 /* Build the FORHEAD node to use as the left kid of pn. */
4987 pn4 = TernaryNode::create(tc);
4988 if (!pn4)
4989 return NULL;
4990 pn4->pn_type = TOK_FORHEAD;
4991 pn4->pn_op = JSOP_NOP;
4992 pn4->pn_kid1 = pn1;
4993 pn4->pn_kid2 = pn2;
4994 pn4->pn_kid3 = pn3;
4995 pn->pn_left = pn4;
4998 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5000 /* Parse the loop body into pn->pn_right. */
5001 pn2 = statement();
5002 if (!pn2)
5003 return NULL;
5004 pn->pn_right = pn2;
5006 /* Record the absolute line number for source note emission. */
5007 pn->pn_pos.end = pn2->pn_pos.end;
5009 #if JS_HAS_BLOCK_SCOPE
5010 if (pnlet) {
5011 PopStatement(tc);
5012 pnlet->pn_expr = pn;
5013 pn = pnlet;
5015 #endif
5016 if (pnseq) {
5017 pnseq->pn_pos.end = pn->pn_pos.end;
5018 pnseq->append(pn);
5019 pn = pnseq;
5021 PopStatement(tc);
5022 return pn;
5024 bad_for_each:
5025 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
5026 return NULL;
5029 case TOK_TRY: {
5030 JSParseNode *catchList, *lastCatch;
5033 * try nodes are ternary.
5034 * kid1 is the try statement
5035 * kid2 is the catch node list or null
5036 * kid3 is the finally statement
5038 * catch nodes are ternary.
5039 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5040 * kid2 is the catch guard or null if no guard
5041 * kid3 is the catch block
5043 * catch lvalue nodes are either:
5044 * TOK_NAME for a single identifier
5045 * TOK_RB or TOK_RC for a destructuring left-hand side
5047 * finally nodes are TOK_LC statement lists.
5049 pn = TernaryNode::create(tc);
5050 if (!pn)
5051 return NULL;
5052 pn->pn_op = JSOP_NOP;
5054 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5055 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5056 return NULL;
5057 pn->pn_kid1 = statements();
5058 if (!pn->pn_kid1)
5059 return NULL;
5060 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5061 PopStatement(tc);
5063 catchList = NULL;
5064 tt = tokenStream.getToken();
5065 if (tt == TOK_CATCH) {
5066 catchList = ListNode::create(tc);
5067 if (!catchList)
5068 return NULL;
5069 catchList->pn_type = TOK_RESERVED;
5070 catchList->makeEmpty();
5071 lastCatch = NULL;
5073 do {
5074 JSParseNode *pnblock;
5075 BindData data;
5077 /* Check for another catch after unconditional catch. */
5078 if (lastCatch && !lastCatch->pn_kid2) {
5079 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
5080 return NULL;
5084 * Create a lexical scope node around the whole catch clause,
5085 * including the head.
5087 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
5088 if (!pnblock)
5089 return NULL;
5090 stmtInfo.type = STMT_CATCH;
5093 * Legal catch forms are:
5094 * catch (lhs)
5095 * catch (lhs if <boolean_expression>)
5096 * where lhs is a name or a destructuring left-hand side.
5097 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5099 pn2 = TernaryNode::create(tc);
5100 if (!pn2)
5101 return NULL;
5102 pnblock->pn_expr = pn2;
5103 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5106 * Contrary to ECMA Ed. 3, the catch variable is lexically
5107 * scoped, not a property of a new Object instance. This is
5108 * an intentional change that anticipates ECMA Ed. 4.
5110 data.pn = NULL;
5111 data.op = JSOP_NOP;
5112 data.binder = BindLet;
5113 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5115 tt = tokenStream.getToken();
5116 switch (tt) {
5117 #if JS_HAS_DESTRUCTURING
5118 case TOK_LB:
5119 case TOK_LC:
5120 pn3 = destructuringExpr(&data, tt);
5121 if (!pn3)
5122 return NULL;
5123 break;
5124 #endif
5126 case TOK_NAME:
5127 label = tokenStream.currentToken().t_atom;
5128 pn3 = NewBindingNode(label, tc, true);
5129 if (!pn3)
5130 return NULL;
5131 data.pn = pn3;
5132 if (!data.binder(context, &data, label, tc))
5133 return NULL;
5134 break;
5136 default:
5137 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER);
5138 return NULL;
5141 pn2->pn_kid1 = pn3;
5142 #if JS_HAS_CATCH_GUARD
5144 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5145 * to avoid conflicting with the JS2/ECMAv4 type annotation
5146 * catchguard syntax.
5148 if (tokenStream.matchToken(TOK_IF)) {
5149 pn2->pn_kid2 = expr();
5150 if (!pn2->pn_kid2)
5151 return NULL;
5153 #endif
5154 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5156 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5157 pn2->pn_kid3 = statements();
5158 if (!pn2->pn_kid3)
5159 return NULL;
5160 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5161 PopStatement(tc);
5163 catchList->append(pnblock);
5164 lastCatch = pn2;
5165 tt = tokenStream.getToken(TSF_OPERAND);
5166 } while (tt == TOK_CATCH);
5168 pn->pn_kid2 = catchList;
5170 if (tt == TOK_FINALLY) {
5171 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5172 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5173 return NULL;
5174 pn->pn_kid3 = statements();
5175 if (!pn->pn_kid3)
5176 return NULL;
5177 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5178 PopStatement(tc);
5179 } else {
5180 tokenStream.ungetToken();
5182 if (!catchList && !pn->pn_kid3) {
5183 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY);
5184 return NULL;
5186 return pn;
5189 case TOK_THROW:
5190 pn = UnaryNode::create(tc);
5191 if (!pn)
5192 return NULL;
5194 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5195 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
5196 if (tt == TOK_ERROR)
5197 return NULL;
5198 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5199 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
5200 return NULL;
5203 pn2 = expr();
5204 if (!pn2)
5205 return NULL;
5206 pn->pn_pos.end = pn2->pn_pos.end;
5207 pn->pn_op = JSOP_THROW;
5208 pn->pn_kid = pn2;
5209 break;
5211 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5212 case TOK_CATCH:
5213 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY);
5214 return NULL;
5216 case TOK_FINALLY:
5217 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY);
5218 return NULL;
5220 case TOK_BREAK:
5221 pn = NullaryNode::create(tc);
5222 if (!pn)
5223 return NULL;
5224 if (!MatchLabel(context, &tokenStream, pn))
5225 return NULL;
5226 stmt = tc->topStmt;
5227 label = pn->pn_atom;
5228 if (label) {
5229 for (; ; stmt = stmt->down) {
5230 if (!stmt) {
5231 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
5232 return NULL;
5234 if (stmt->type == STMT_LABEL && stmt->label == label)
5235 break;
5237 } else {
5238 for (; ; stmt = stmt->down) {
5239 if (!stmt) {
5240 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOUGH_BREAK);
5241 return NULL;
5243 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5244 break;
5247 if (label)
5248 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5249 break;
5251 case TOK_CONTINUE:
5252 pn = NullaryNode::create(tc);
5253 if (!pn)
5254 return NULL;
5255 if (!MatchLabel(context, &tokenStream, pn))
5256 return NULL;
5257 stmt = tc->topStmt;
5258 label = pn->pn_atom;
5259 if (label) {
5260 for (stmt2 = NULL; ; stmt = stmt->down) {
5261 if (!stmt) {
5262 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
5263 return NULL;
5265 if (stmt->type == STMT_LABEL) {
5266 if (stmt->label == label) {
5267 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5268 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
5269 return NULL;
5271 break;
5273 } else {
5274 stmt2 = stmt;
5277 } else {
5278 for (; ; stmt = stmt->down) {
5279 if (!stmt) {
5280 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
5281 return NULL;
5283 if (STMT_IS_LOOP(stmt))
5284 break;
5287 if (label)
5288 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5289 break;
5291 case TOK_WITH:
5293 * In most cases, we want the constructs forbidden in strict mode
5294 * code to be a subset of those that JSOPTION_STRICT warns about, and
5295 * we should use ReportStrictModeError. However, 'with' is the sole
5296 * instance of a construct that is forbidden in strict mode code, but
5297 * doesn't even merit a warning under JSOPTION_STRICT. See
5298 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5300 if (tc->flags & TCF_STRICT_MODE_CODE) {
5301 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
5302 return NULL;
5305 pn = BinaryNode::create(tc);
5306 if (!pn)
5307 return NULL;
5308 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5309 pn2 = parenExpr(NULL, NULL);
5310 if (!pn2)
5311 return NULL;
5312 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5313 pn->pn_left = pn2;
5315 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5316 pn2 = statement();
5317 if (!pn2)
5318 return NULL;
5319 PopStatement(tc);
5321 pn->pn_pos.end = pn2->pn_pos.end;
5322 pn->pn_right = pn2;
5323 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5324 return pn;
5326 case TOK_VAR:
5327 pn = variables(false);
5328 if (!pn)
5329 return NULL;
5331 /* Tell js_EmitTree to generate a final POP. */
5332 pn->pn_xflags |= PNX_POPVAR;
5333 break;
5335 #if JS_HAS_BLOCK_SCOPE
5336 case TOK_LET:
5338 JSObject *obj;
5339 JSObjectBox *blockbox;
5341 /* Check for a let statement or let expression. */
5342 if (tokenStream.peekToken() == TOK_LP) {
5343 pn = letBlock(JS_TRUE);
5344 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5345 return pn;
5347 /* Let expressions require automatic semicolon insertion. */
5348 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5349 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5350 break;
5354 * This is a let declaration. We must be directly under a block per
5355 * the proposed ES4 specs, but not an implicit block created due to
5356 * 'for (let ...)'. If we pass this error test, make the enclosing
5357 * JSStmtInfo be our scope. Further let declarations in this block
5358 * will find this scope statement and use the same block object.
5360 * If we are the first let declaration in this block (i.e., when the
5361 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5362 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5364 stmt = tc->topStmt;
5365 if (stmt &&
5366 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5367 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LET_DECL_NOT_IN_BLOCK);
5368 return NULL;
5371 if (stmt && (stmt->flags & SIF_SCOPE)) {
5372 JS_ASSERT(tc->blockChain == stmt->blockObj);
5373 obj = tc->blockChain;
5374 } else {
5375 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5377 * ES4 specifies that let at top level and at body-block scope
5378 * does not shadow var, so convert back to var.
5380 tokenStream.mungeCurrentToken(TOK_VAR, JSOP_DEFVAR);
5382 pn = variables(false);
5383 if (!pn)
5384 return NULL;
5385 pn->pn_xflags |= PNX_POPVAR;
5386 break;
5390 * Some obvious assertions here, but they may help clarify the
5391 * situation. This stmt is not yet a scope, so it must not be a
5392 * catch block (catch is a lexical scope by definition).
5394 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5395 JS_ASSERT(stmt != tc->topScopeStmt);
5396 JS_ASSERT(stmt->type == STMT_BLOCK ||
5397 stmt->type == STMT_SWITCH ||
5398 stmt->type == STMT_TRY ||
5399 stmt->type == STMT_FINALLY);
5400 JS_ASSERT(!stmt->downScope);
5402 /* Convert the block statement into a scope statement. */
5403 JSObject *obj = js_NewBlockObject(tc->parser->context);
5404 if (!obj)
5405 return NULL;
5407 blockbox = tc->parser->newObjectBox(obj);
5408 if (!blockbox)
5409 return NULL;
5412 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5413 * list stack, if it isn't already there. If it is there, but it
5414 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5415 * block.
5417 stmt->flags |= SIF_SCOPE;
5418 stmt->downScope = tc->topScopeStmt;
5419 tc->topScopeStmt = stmt;
5420 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5421 (tc->maxScopeDepth = tc->scopeDepth));
5423 obj->setParent(tc->blockChain);
5424 tc->blockChain = obj;
5425 stmt->blockObj = obj;
5427 #ifdef DEBUG
5428 pn1 = tc->blockNode;
5429 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5430 #endif
5432 /* Create a new lexical scope node for these statements. */
5433 pn1 = LexicalScopeNode::create(tc);
5434 if (!pn1)
5435 return NULL;
5437 pn1->pn_type = TOK_LEXICALSCOPE;
5438 pn1->pn_op = JSOP_LEAVEBLOCK;
5439 pn1->pn_pos = tc->blockNode->pn_pos;
5440 pn1->pn_objbox = blockbox;
5441 pn1->pn_expr = tc->blockNode;
5442 pn1->pn_blockid = tc->blockNode->pn_blockid;
5443 tc->blockNode = pn1;
5446 pn = variables(false);
5447 if (!pn)
5448 return NULL;
5449 pn->pn_xflags = PNX_POPVAR;
5450 break;
5452 #endif /* JS_HAS_BLOCK_SCOPE */
5454 case TOK_RETURN:
5455 pn = returnOrYield(false);
5456 if (!pn)
5457 return NULL;
5458 break;
5460 case TOK_LC:
5462 uintN oldflags;
5464 oldflags = tc->flags;
5465 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5466 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5467 return NULL;
5468 pn = statements();
5469 if (!pn)
5470 return NULL;
5472 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5473 PopStatement(tc);
5476 * If we contain a function statement and our container is top-level
5477 * or another block, flag pn to preserve braces when decompiling.
5479 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5480 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5481 pn->pn_xflags |= PNX_NEEDBRACES;
5483 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5484 return pn;
5487 case TOK_EOL:
5488 case TOK_SEMI:
5489 pn = UnaryNode::create(tc);
5490 if (!pn)
5491 return NULL;
5492 pn->pn_type = TOK_SEMI;
5493 return pn;
5495 #if JS_HAS_DEBUGGER_KEYWORD
5496 case TOK_DEBUGGER:
5497 pn = NullaryNode::create(tc);
5498 if (!pn)
5499 return NULL;
5500 pn->pn_type = TOK_DEBUGGER;
5501 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5502 break;
5503 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5505 #if JS_HAS_XML_SUPPORT
5506 case TOK_DEFAULT:
5507 pn = UnaryNode::create(tc);
5508 if (!pn)
5509 return NULL;
5510 if (!tokenStream.matchToken(TOK_NAME) ||
5511 tokenStream.currentToken().t_atom != context->runtime->atomState.xmlAtom ||
5512 !tokenStream.matchToken(TOK_NAME) ||
5513 tokenStream.currentToken().t_atom != context->runtime->atomState.namespaceAtom ||
5514 !tokenStream.matchToken(TOK_ASSIGN) ||
5515 tokenStream.currentToken().t_op != JSOP_NOP) {
5516 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5517 return NULL;
5520 /* Is this an E4X dagger I see before me? */
5521 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5522 pn2 = expr();
5523 if (!pn2)
5524 return NULL;
5525 pn->pn_op = JSOP_DEFXMLNS;
5526 pn->pn_pos.end = pn2->pn_pos.end;
5527 pn->pn_kid = pn2;
5528 break;
5529 #endif
5531 case TOK_ERROR:
5532 return NULL;
5534 default:
5535 #if JS_HAS_XML_SUPPORT
5536 expression:
5537 #endif
5538 tokenStream.ungetToken();
5539 pn2 = expr();
5540 if (!pn2)
5541 return NULL;
5543 if (tokenStream.peekToken() == TOK_COLON) {
5544 if (pn2->pn_type != TOK_NAME) {
5545 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LABEL);
5546 return NULL;
5548 label = pn2->pn_atom;
5549 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5550 if (stmt->type == STMT_LABEL && stmt->label == label) {
5551 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL);
5552 return NULL;
5555 ForgetUse(pn2);
5557 (void) tokenStream.getToken();
5559 /* Push a label struct and parse the statement. */
5560 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5561 stmtInfo.label = label;
5562 pn = statement();
5563 if (!pn)
5564 return NULL;
5566 /* Normalize empty statement to empty block for the decompiler. */
5567 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5568 pn->pn_type = TOK_LC;
5569 pn->pn_arity = PN_LIST;
5570 pn->makeEmpty();
5573 /* Pop the label, set pn_expr, and return early. */
5574 PopStatement(tc);
5575 pn2->pn_type = TOK_COLON;
5576 pn2->pn_pos.end = pn->pn_pos.end;
5577 pn2->pn_expr = pn;
5578 return pn2;
5581 pn = UnaryNode::create(tc);
5582 if (!pn)
5583 return NULL;
5584 pn->pn_type = TOK_SEMI;
5585 pn->pn_pos = pn2->pn_pos;
5586 pn->pn_kid = pn2;
5588 switch (PN_TYPE(pn2)) {
5589 case TOK_LP:
5591 * Flag lambdas immediately applied as statements as instances of
5592 * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5594 if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION &&
5595 !pn2->pn_head->pn_funbox->node->isFunArg()) {
5596 pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
5598 break;
5599 case TOK_ASSIGN:
5601 * Keep track of all apparent methods created by assignments such
5602 * as this.foo = function (...) {...} in a function that could end
5603 * up a constructor function. See Parser::setFunctionKinds.
5605 if (tc->funbox &&
5606 PN_OP(pn2) == JSOP_NOP &&
5607 PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5608 PN_OP(pn2->pn_left->pn_expr) == JSOP_THIS &&
5609 PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
5610 JS_ASSERT(!pn2->pn_defn);
5611 JS_ASSERT(!pn2->pn_used);
5612 pn2->pn_right->pn_link = tc->funbox->methods;
5613 tc->funbox->methods = pn2->pn_right;
5615 break;
5616 default:;
5618 break;
5621 /* Check termination of this primitive statement. */
5622 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5625 static void
5626 NoteArgumentsUse(JSTreeContext *tc)
5628 JS_ASSERT(tc->inFunction());
5629 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5630 if (tc->funbox)
5631 tc->funbox->node->pn_dflags |= PND_FUNARG;
5634 JSParseNode *
5635 Parser::variables(bool inLetHead)
5637 TokenKind tt;
5638 bool let;
5639 JSStmtInfo *scopeStmt;
5640 BindData data;
5641 JSParseNode *pn, *pn2;
5642 JSAtom *atom;
5645 * The three options here are:
5646 * - TOK_LET: We are parsing a let declaration.
5647 * - TOK_LP: We are parsing the head of a let block.
5648 * - Otherwise, we're parsing var declarations.
5650 tt = tokenStream.currentToken().type;
5651 let = (tt == TOK_LET || tt == TOK_LP);
5652 JS_ASSERT(let || tt == TOK_VAR);
5654 #if JS_HAS_BLOCK_SCOPE
5655 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5656 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5657 #endif
5659 /* Make sure that statement set up the tree context correctly. */
5660 scopeStmt = tc->topScopeStmt;
5661 if (let) {
5662 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5663 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5664 scopeStmt = scopeStmt->downScope;
5666 JS_ASSERT(scopeStmt);
5669 data.op = let ? JSOP_NOP : tokenStream.currentToken().t_op;
5670 pn = ListNode::create(tc);
5671 if (!pn)
5672 return NULL;
5673 pn->pn_op = data.op;
5674 pn->makeEmpty();
5677 * SpiderMonkey const is really "write once per initialization evaluation"
5678 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5679 * this code will change soon.
5681 if (let) {
5682 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5683 data.binder = BindLet;
5684 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5685 } else {
5686 data.binder = BindVarOrConst;
5689 do {
5690 tt = tokenStream.getToken();
5691 #if JS_HAS_DESTRUCTURING
5692 if (tt == TOK_LB || tt == TOK_LC) {
5693 tc->flags |= TCF_DECL_DESTRUCTURING;
5694 pn2 = primaryExpr(tt, JS_FALSE);
5695 tc->flags &= ~TCF_DECL_DESTRUCTURING;
5696 if (!pn2)
5697 return NULL;
5699 if (!CheckDestructuring(context, &data, pn2, NULL, tc))
5700 return NULL;
5701 if ((tc->flags & TCF_IN_FOR_INIT) &&
5702 tokenStream.peekToken() == TOK_IN) {
5703 pn->append(pn2);
5704 continue;
5707 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5708 if (tokenStream.currentToken().t_op != JSOP_NOP)
5709 goto bad_var_init;
5711 #if JS_HAS_BLOCK_SCOPE
5712 if (popScope) {
5713 tc->topStmt = save->down;
5714 tc->topScopeStmt = saveScope->downScope;
5716 #endif
5717 JSParseNode *init = assignExpr();
5718 #if JS_HAS_BLOCK_SCOPE
5719 if (popScope) {
5720 tc->topStmt = save;
5721 tc->topScopeStmt = saveScope;
5723 #endif
5725 if (!init || !UndominateInitializers(pn2, init, tc))
5726 return NULL;
5728 pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5729 if (!pn2)
5730 return NULL;
5731 pn->append(pn2);
5732 continue;
5734 #endif /* JS_HAS_DESTRUCTURING */
5736 if (tt != TOK_NAME) {
5737 if (tt != TOK_ERROR) {
5738 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
5740 return NULL;
5743 atom = tokenStream.currentToken().t_atom;
5744 pn2 = NewBindingNode(atom, tc, let);
5745 if (!pn2)
5746 return NULL;
5747 if (data.op == JSOP_DEFCONST)
5748 pn2->pn_dflags |= PND_CONST;
5749 data.pn = pn2;
5750 if (!data.binder(context, &data, atom, tc))
5751 return NULL;
5752 pn->append(pn2);
5754 if (tokenStream.matchToken(TOK_ASSIGN)) {
5755 if (tokenStream.currentToken().t_op != JSOP_NOP)
5756 goto bad_var_init;
5758 #if JS_HAS_BLOCK_SCOPE
5759 if (popScope) {
5760 tc->topStmt = save->down;
5761 tc->topScopeStmt = saveScope->downScope;
5763 #endif
5764 JSParseNode *init = assignExpr();
5765 #if JS_HAS_BLOCK_SCOPE
5766 if (popScope) {
5767 tc->topStmt = save;
5768 tc->topScopeStmt = saveScope;
5770 #endif
5771 if (!init)
5772 return NULL;
5774 if (pn2->pn_used) {
5775 pn2 = MakeAssignment(pn2, init, tc);
5776 if (!pn2)
5777 return NULL;
5778 } else {
5779 pn2->pn_expr = init;
5782 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5783 ? JSOP_SETNAME
5784 : (pn2->pn_dflags & PND_GVAR)
5785 ? JSOP_SETGVAR
5786 : (pn2->pn_dflags & PND_BOUND)
5787 ? JSOP_SETLOCAL
5788 : (data.op == JSOP_DEFCONST)
5789 ? JSOP_SETCONST
5790 : JSOP_SETNAME;
5792 NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5794 /* The declarator's position must include the initializer. */
5795 pn2->pn_pos.end = init->pn_pos.end;
5797 if (tc->inFunction() &&
5798 atom == context->runtime->atomState.argumentsAtom) {
5799 NoteArgumentsUse(tc);
5800 if (!let)
5801 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5804 } while (tokenStream.matchToken(TOK_COMMA));
5806 pn->pn_pos.end = pn->last()->pn_pos.end;
5807 return pn;
5809 bad_var_init:
5810 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_VAR_INIT);
5811 return NULL;
5814 JSParseNode *
5815 Parser::expr()
5817 JSParseNode *pn = assignExpr();
5818 if (pn && tokenStream.matchToken(TOK_COMMA)) {
5819 JSParseNode *pn2 = ListNode::create(tc);
5820 if (!pn2)
5821 return NULL;
5822 pn2->pn_pos.begin = pn->pn_pos.begin;
5823 pn2->initList(pn);
5824 pn = pn2;
5825 do {
5826 #if JS_HAS_GENERATORS
5827 pn2 = pn->last();
5828 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
5829 reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
5830 return NULL;
5832 #endif
5833 pn2 = assignExpr();
5834 if (!pn2)
5835 return NULL;
5836 pn->append(pn2);
5837 } while (tokenStream.matchToken(TOK_COMMA));
5838 pn->pn_pos.end = pn->last()->pn_pos.end;
5840 return pn;
5843 JSParseNode *
5844 Parser::assignExpr()
5846 JS_CHECK_RECURSION(context, return NULL);
5848 #if JS_HAS_GENERATORS
5849 if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
5850 return returnOrYield(true);
5851 #endif
5853 JSParseNode *pn = condExpr();
5854 if (!pn)
5855 return NULL;
5857 TokenKind tt = tokenStream.getToken();
5858 if (tt != TOK_ASSIGN) {
5859 tokenStream.ungetToken();
5860 return pn;
5863 JSOp op = tokenStream.currentToken().t_op;
5864 switch (pn->pn_type) {
5865 case TOK_NAME:
5866 if (!CheckStrictAssignment(context, tc, pn))
5867 return NULL;
5868 pn->pn_op = JSOP_SETNAME;
5869 NoteLValue(context, pn, tc);
5870 break;
5871 case TOK_DOT:
5872 pn->pn_op = JSOP_SETPROP;
5873 break;
5874 case TOK_LB:
5875 pn->pn_op = JSOP_SETELEM;
5876 break;
5877 #if JS_HAS_DESTRUCTURING
5878 case TOK_RB:
5879 case TOK_RC:
5881 if (op != JSOP_NOP) {
5882 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS);
5883 return NULL;
5885 JSParseNode *rhs = assignExpr();
5886 if (!rhs || !CheckDestructuring(context, NULL, pn, rhs, tc))
5887 return NULL;
5888 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5890 #endif
5891 case TOK_LP:
5892 if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5893 return NULL;
5894 break;
5895 #if JS_HAS_XML_SUPPORT
5896 case TOK_UNARYOP:
5897 if (pn->pn_op == JSOP_XMLNAME) {
5898 pn->pn_op = JSOP_SETXMLNAME;
5899 break;
5901 /* FALL THROUGH */
5902 #endif
5903 default:
5904 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
5905 return NULL;
5908 JSParseNode *rhs = assignExpr();
5909 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
5910 JSDefinition *dn = pn->pn_lexdef;
5913 * If the definition is not flagged as assigned, we must have imputed
5914 * the initialized flag to it, to optimize for flat closures. But that
5915 * optimization uses source coordinates to check dominance relations,
5916 * so we must extend the end of the definition to cover the right-hand
5917 * side of this assignment, i.e., the initializer.
5919 if (!dn->isAssigned()) {
5920 JS_ASSERT(dn->isInitialized());
5921 dn->pn_pos.end = rhs->pn_pos.end;
5925 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5928 JSParseNode *
5929 Parser::condExpr()
5931 JSParseNode *pn = orExpr();
5932 if (pn && tokenStream.matchToken(TOK_HOOK)) {
5933 JSParseNode *pn1 = pn;
5934 pn = TernaryNode::create(tc);
5935 if (!pn)
5936 return NULL;
5939 * Always accept the 'in' operator in the middle clause of a ternary,
5940 * where it's unambiguous, even if we might be parsing the init of a
5941 * for statement.
5943 uintN oldflags = tc->flags;
5944 tc->flags &= ~TCF_IN_FOR_INIT;
5945 JSParseNode *pn2 = assignExpr();
5946 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5948 if (!pn2)
5949 return NULL;
5950 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
5951 JSParseNode *pn3 = assignExpr();
5952 if (!pn3)
5953 return NULL;
5954 pn->pn_pos.begin = pn1->pn_pos.begin;
5955 pn->pn_pos.end = pn3->pn_pos.end;
5956 pn->pn_kid1 = pn1;
5957 pn->pn_kid2 = pn2;
5958 pn->pn_kid3 = pn3;
5960 return pn;
5963 JSParseNode *
5964 Parser::orExpr()
5966 JSParseNode *pn = andExpr();
5967 while (pn && tokenStream.matchToken(TOK_OR))
5968 pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, andExpr(), tc);
5969 return pn;
5972 JSParseNode *
5973 Parser::andExpr()
5975 JSParseNode *pn = bitOrExpr();
5976 while (pn && tokenStream.matchToken(TOK_AND))
5977 pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, bitOrExpr(), tc);
5978 return pn;
5981 JSParseNode *
5982 Parser::bitOrExpr()
5984 JSParseNode *pn = bitXorExpr();
5985 while (pn && tokenStream.matchToken(TOK_BITOR))
5986 pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, bitXorExpr(), tc);
5987 return pn;
5990 JSParseNode *
5991 Parser::bitXorExpr()
5993 JSParseNode *pn = bitAndExpr();
5994 while (pn && tokenStream.matchToken(TOK_BITXOR)) {
5995 pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, bitAndExpr(), tc);
5997 return pn;
6000 JSParseNode *
6001 Parser::bitAndExpr()
6003 JSParseNode *pn = eqExpr();
6004 while (pn && tokenStream.matchToken(TOK_BITAND))
6005 pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, eqExpr(), tc);
6006 return pn;
6009 JSParseNode *
6010 Parser::eqExpr()
6012 JSParseNode *pn = relExpr();
6013 while (pn && tokenStream.matchToken(TOK_EQOP)) {
6014 JSOp op = tokenStream.currentToken().t_op;
6015 pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, relExpr(), tc);
6017 return pn;
6020 JSParseNode *
6021 Parser::relExpr()
6023 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6026 * Uses of the in operator in shiftExprs are always unambiguous,
6027 * so unset the flag that prohibits recognizing it.
6029 tc->flags &= ~TCF_IN_FOR_INIT;
6031 JSParseNode *pn = shiftExpr();
6032 while (pn &&
6033 (tokenStream.matchToken(TOK_RELOP) ||
6035 * Recognize the 'in' token as an operator only if we're not
6036 * currently in the init expr of a for loop.
6038 (inForInitFlag == 0 && tokenStream.matchToken(TOK_IN)) ||
6039 tokenStream.matchToken(TOK_INSTANCEOF))) {
6040 TokenKind tt = tokenStream.currentToken().type;
6041 JSOp op = tokenStream.currentToken().t_op;
6042 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, shiftExpr(), tc);
6044 /* Restore previous state of inForInit flag. */
6045 tc->flags |= inForInitFlag;
6047 return pn;
6050 JSParseNode *
6051 Parser::shiftExpr()
6053 JSParseNode *pn = addExpr();
6054 while (pn && tokenStream.matchToken(TOK_SHOP)) {
6055 JSOp op = tokenStream.currentToken().t_op;
6056 pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, addExpr(), tc);
6058 return pn;
6061 JSParseNode *
6062 Parser::addExpr()
6064 JSParseNode *pn = mulExpr();
6065 while (pn &&
6066 (tokenStream.matchToken(TOK_PLUS) ||
6067 tokenStream.matchToken(TOK_MINUS))) {
6068 TokenKind tt = tokenStream.currentToken().type;
6069 JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6070 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, mulExpr(), tc);
6072 return pn;
6075 JSParseNode *
6076 Parser::mulExpr()
6078 JSParseNode *pn = unaryExpr();
6079 while (pn && (tokenStream.matchToken(TOK_STAR) || tokenStream.matchToken(TOK_DIVOP))) {
6080 TokenKind tt = tokenStream.currentToken().type;
6081 JSOp op = tokenStream.currentToken().t_op;
6082 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, unaryExpr(), tc);
6084 return pn;
6087 static JSParseNode *
6088 SetLvalKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6089 JSParseNode *pn, JSParseNode *kid, const char *name)
6091 if (kid->pn_type != TOK_NAME &&
6092 kid->pn_type != TOK_DOT &&
6093 (kid->pn_type != TOK_LP ||
6094 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6095 #if JS_HAS_XML_SUPPORT
6096 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6097 #endif
6098 kid->pn_type != TOK_LB) {
6099 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
6100 return NULL;
6102 if (!CheckStrictAssignment(cx, tc, kid))
6103 return NULL;
6104 pn->pn_kid = kid;
6105 return kid;
6108 static const char incop_name_str[][10] = {"increment", "decrement"};
6110 static JSBool
6111 SetIncOpKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6112 JSParseNode *pn, JSParseNode *kid,
6113 TokenKind tt, JSBool preorder)
6115 JSOp op;
6117 kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6118 if (!kid)
6119 return JS_FALSE;
6120 switch (kid->pn_type) {
6121 case TOK_NAME:
6122 op = (tt == TOK_INC)
6123 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6124 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6125 NoteLValue(cx, kid, tc);
6126 break;
6128 case TOK_DOT:
6129 op = (tt == TOK_INC)
6130 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6131 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6132 break;
6134 case TOK_LP:
6135 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6136 return JS_FALSE;
6137 /* FALL THROUGH */
6138 #if JS_HAS_XML_SUPPORT
6139 case TOK_UNARYOP:
6140 if (kid->pn_op == JSOP_XMLNAME)
6141 kid->pn_op = JSOP_SETXMLNAME;
6142 /* FALL THROUGH */
6143 #endif
6144 case TOK_LB:
6145 op = (tt == TOK_INC)
6146 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6147 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6148 break;
6150 default:
6151 JS_ASSERT(0);
6152 op = JSOP_NOP;
6154 pn->pn_op = op;
6155 return JS_TRUE;
6158 JSParseNode *
6159 Parser::unaryExpr()
6161 JSParseNode *pn, *pn2;
6163 JS_CHECK_RECURSION(context, return NULL);
6165 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6166 switch (tt) {
6167 case TOK_UNARYOP:
6168 case TOK_PLUS:
6169 case TOK_MINUS:
6170 pn = UnaryNode::create(tc);
6171 if (!pn)
6172 return NULL;
6173 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6174 pn->pn_op = tokenStream.currentToken().t_op;
6175 pn2 = unaryExpr();
6176 if (!pn2)
6177 return NULL;
6178 pn->pn_pos.end = pn2->pn_pos.end;
6179 pn->pn_kid = pn2;
6180 break;
6182 case TOK_INC:
6183 case TOK_DEC:
6184 pn = UnaryNode::create(tc);
6185 if (!pn)
6186 return NULL;
6187 pn2 = memberExpr(JS_TRUE);
6188 if (!pn2)
6189 return NULL;
6190 if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, JS_TRUE))
6191 return NULL;
6192 pn->pn_pos.end = pn2->pn_pos.end;
6193 break;
6195 case TOK_DELETE:
6197 pn = UnaryNode::create(tc);
6198 if (!pn)
6199 return NULL;
6200 pn2 = unaryExpr();
6201 if (!pn2)
6202 return NULL;
6203 pn->pn_pos.end = pn2->pn_pos.end;
6206 * Under ECMA3, deleting any unary expression is valid -- it simply
6207 * returns true. Here we fold constants before checking for a call
6208 * expression, in order to rule out delete of a generator expression.
6210 if (!js_FoldConstants(context, pn2, tc))
6211 return NULL;
6212 switch (pn2->pn_type) {
6213 case TOK_LP:
6214 if (pn2->pn_op != JSOP_SETCALL &&
6215 !MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6216 return NULL;
6218 break;
6219 case TOK_NAME:
6220 if (!ReportStrictModeError(context, &tokenStream, tc, pn,
6221 JSMSG_DEPRECATED_DELETE_OPERAND))
6222 return NULL;
6223 pn2->pn_op = JSOP_DELNAME;
6224 if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
6225 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6226 break;
6227 default:;
6229 pn->pn_kid = pn2;
6230 break;
6232 case TOK_ERROR:
6233 return NULL;
6235 default:
6236 tokenStream.ungetToken();
6237 pn = memberExpr(JS_TRUE);
6238 if (!pn)
6239 return NULL;
6241 /* Don't look across a newline boundary for a postfix incop. */
6242 if (tokenStream.onCurrentLine(pn->pn_pos)) {
6243 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6244 if (tt == TOK_INC || tt == TOK_DEC) {
6245 (void) tokenStream.getToken();
6246 pn2 = UnaryNode::create(tc);
6247 if (!pn2)
6248 return NULL;
6249 if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, JS_FALSE))
6250 return NULL;
6251 pn2->pn_pos.begin = pn->pn_pos.begin;
6252 pn = pn2;
6255 break;
6257 return pn;
6260 #if JS_HAS_GENERATORS
6263 * A dedicated helper for transplanting the comprehension expression E in
6265 * [E for (V in I)] // array comprehension
6266 * (E for (V in I)) // generator expression
6268 * from its initial location in the AST, on the left of the 'for', to its final
6269 * position on the right. To avoid a separate pass we do this by adjusting the
6270 * blockids and name binding links that were established when E was parsed.
6272 * A generator expression desugars like so:
6274 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6276 * so the transplanter must adjust static level as well as blockid. E's source
6277 * coordinates in root->pn_pos are critical to deciding which binding links to
6278 * preserve and which to cut.
6280 * NB: This is not a general tree transplanter -- it knows in particular that
6281 * the one or more bindings induced by V have not yet been created.
6283 class CompExprTransplanter {
6284 JSParseNode *root;
6285 JSTreeContext *tc;
6286 bool genexp;
6287 uintN adjust;
6288 uintN funcLevel;
6290 public:
6291 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6292 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6296 bool transplant(JSParseNode *pn);
6300 * Any definitions nested within the comprehension expression of a generator
6301 * expression must move "down" one static level, which of course increases the
6302 * upvar-frame-skip count.
6304 static bool
6305 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6307 if (!pn->pn_cookie.isFree()) {
6308 uintN level = pn->pn_cookie.level() + 1;
6310 JS_ASSERT(level >= tc->staticLevel);
6311 if (level >= UpvarCookie::FREE_LEVEL) {
6312 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
6313 JSMSG_TOO_DEEP, js_function_str);
6314 return false;
6317 pn->pn_cookie.set(level, pn->pn_cookie.slot());
6319 return true;
6322 static void
6323 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6325 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6326 pn->pn_blockid += adjust;
6327 if (pn->pn_blockid >= tc->blockidGen)
6328 tc->blockidGen = pn->pn_blockid + 1;
6331 bool
6332 CompExprTransplanter::transplant(JSParseNode *pn)
6334 if (!pn)
6335 return true;
6337 switch (pn->pn_arity) {
6338 case PN_LIST:
6339 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6340 transplant(pn2);
6341 if (pn->pn_pos >= root->pn_pos)
6342 AdjustBlockId(pn, adjust, tc);
6343 break;
6345 case PN_TERNARY:
6346 transplant(pn->pn_kid1);
6347 transplant(pn->pn_kid2);
6348 transplant(pn->pn_kid3);
6349 break;
6351 case PN_BINARY:
6352 transplant(pn->pn_left);
6354 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6355 if (pn->pn_right != pn->pn_left)
6356 transplant(pn->pn_right);
6357 break;
6359 case PN_UNARY:
6360 transplant(pn->pn_kid);
6361 break;
6363 case PN_FUNC:
6366 * Only the first level of transplant recursion through functions needs
6367 * to reparent the funbox, since all descendant functions are correctly
6368 * linked under the top-most funbox. But every visit to this case needs
6369 * to update funbox->level.
6371 * Recall that funbox->level is the static level of the code containing
6372 * the definition or expression of the function and not the static level
6373 * of the function's body.
6375 JSFunctionBox *funbox = pn->pn_funbox;
6377 funbox->level = tc->staticLevel + funcLevel;
6378 if (++funcLevel == 1 && genexp) {
6379 JSFunctionBox *parent = tc->funbox;
6381 JSFunctionBox **funboxp = &tc->parent->functionList;
6382 while (*funboxp != funbox)
6383 funboxp = &(*funboxp)->siblings;
6384 *funboxp = funbox->siblings;
6386 funbox->parent = parent;
6387 funbox->siblings = parent->kids;
6388 parent->kids = funbox;
6389 funbox->level = tc->staticLevel;
6391 /* FALL THROUGH */
6394 case PN_NAME:
6395 transplant(pn->maybeExpr());
6396 if (pn->pn_arity == PN_FUNC)
6397 --funcLevel;
6399 if (pn->pn_defn) {
6400 if (genexp && !BumpStaticLevel(pn, tc))
6401 return false;
6402 } else if (pn->pn_used) {
6403 JS_ASSERT(pn->pn_op != JSOP_NOP);
6404 JS_ASSERT(pn->pn_cookie.isFree());
6406 JSDefinition *dn = pn->pn_lexdef;
6407 JS_ASSERT(dn->pn_defn);
6410 * Adjust the definition's block id only if it is a placeholder not
6411 * to the left of the root node, and if pn is the last use visited
6412 * in the comprehension expression (to avoid adjusting the blockid
6413 * multiple times).
6415 * Non-placeholder definitions within the comprehension expression
6416 * will be visited further below.
6418 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6419 if (genexp && !BumpStaticLevel(dn, tc))
6420 return false;
6421 AdjustBlockId(dn, adjust, tc);
6424 JSAtom *atom = pn->pn_atom;
6425 #ifdef DEBUG
6426 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6427 JS_ASSERT(!stmt || stmt != tc->topStmt);
6428 #endif
6429 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6430 JS_ASSERT(!tc->decls.lookup(atom));
6432 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6433 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, dn->pn_atom);
6434 if (!ale)
6435 return false;
6437 if (dn->pn_pos >= root->pn_pos) {
6438 tc->parent->lexdeps.remove(tc->parser, atom);
6439 } else {
6440 JSDefinition *dn2 = (JSDefinition *)NameNode::create(dn->pn_atom, tc);
6441 if (!dn2)
6442 return false;
6444 dn2->pn_type = dn->pn_type;
6445 dn2->pn_pos = root->pn_pos;
6446 dn2->pn_defn = true;
6447 dn2->pn_dflags |= PND_PLACEHOLDER;
6449 JSParseNode **pnup = &dn->dn_uses;
6450 JSParseNode *pnu;
6451 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6452 pnu->pn_lexdef = dn2;
6453 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6454 pnup = &pnu->pn_link;
6456 dn2->dn_uses = dn->dn_uses;
6457 dn->dn_uses = *pnup;
6458 *pnup = NULL;
6460 dn = dn2;
6463 ALE_SET_DEFN(ale, dn);
6468 if (pn->pn_pos >= root->pn_pos)
6469 AdjustBlockId(pn, adjust, tc);
6470 break;
6472 case PN_NAMESET:
6473 transplant(pn->pn_tree);
6474 break;
6476 return true;
6480 * Starting from a |for| keyword after the first array initialiser element or
6481 * an expression in an open parenthesis, parse the tail of the comprehension
6482 * or generator expression signified by this |for| keyword in context.
6484 * Return null on failure, else return the top-most parse node for the array
6485 * comprehension or generator expression, with a unary node as the body of the
6486 * (possibly nested) for-loop, initialized by |type, op, kid|.
6488 JSParseNode *
6489 Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
6490 TokenKind type, JSOp op)
6492 uintN adjust;
6493 JSParseNode *pn, *pn2, *pn3, **pnp;
6494 JSStmtInfo stmtInfo;
6495 BindData data;
6496 TokenKind tt;
6497 JSAtom *atom;
6499 JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
6501 if (type == TOK_SEMI) {
6503 * Generator expression desugars to an immediately applied lambda that
6504 * yields the next value from a for-in loop (possibly nested, and with
6505 * optional if guard). Make pn be the TOK_LC body node.
6507 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6508 if (!pn)
6509 return NULL;
6510 adjust = pn->pn_blockid - blockid;
6511 } else {
6512 JS_ASSERT(type == TOK_ARRAYPUSH);
6515 * Make a parse-node and literal object representing the block scope of
6516 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6517 * aka the array initialiser case, has passed the blockid to claim for
6518 * the comprehension's block scope. We allocate that id or one above it
6519 * here, by calling js_PushLexicalScope.
6521 * In the case of a comprehension expression that has nested blocks
6522 * (e.g., let expressions), we will allocate a higher blockid but then
6523 * slide all blocks "to the right" to make room for the comprehension's
6524 * block scope.
6526 adjust = tc->blockid();
6527 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6528 if (!pn)
6529 return NULL;
6531 JS_ASSERT(blockid <= pn->pn_blockid);
6532 JS_ASSERT(blockid < tc->blockidGen);
6533 JS_ASSERT(tc->bodyid < blockid);
6534 pn->pn_blockid = stmtInfo.blockid = blockid;
6535 JS_ASSERT(adjust < blockid);
6536 adjust = blockid - adjust;
6539 pnp = &pn->pn_expr;
6541 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6542 transplanter.transplant(kid);
6544 data.pn = NULL;
6545 data.op = JSOP_NOP;
6546 data.binder = BindLet;
6547 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6549 do {
6551 * FOR node is binary, left is loop control and right is body. Use
6552 * index to count each block-local let-variable on the left-hand side
6553 * of the IN.
6555 pn2 = BinaryNode::create(tc);
6556 if (!pn2)
6557 return NULL;
6559 pn2->pn_op = JSOP_ITER;
6560 pn2->pn_iflags = JSITER_ENUMERATE;
6561 if (tokenStream.matchToken(TOK_NAME)) {
6562 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
6563 pn2->pn_iflags |= JSITER_FOREACH;
6564 else
6565 tokenStream.ungetToken();
6567 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6569 atom = NULL;
6570 tt = tokenStream.getToken();
6571 switch (tt) {
6572 #if JS_HAS_DESTRUCTURING
6573 case TOK_LB:
6574 case TOK_LC:
6575 tc->flags |= TCF_DECL_DESTRUCTURING;
6576 pn3 = primaryExpr(tt, JS_FALSE);
6577 tc->flags &= ~TCF_DECL_DESTRUCTURING;
6578 if (!pn3)
6579 return NULL;
6580 break;
6581 #endif
6583 case TOK_NAME:
6584 atom = tokenStream.currentToken().t_atom;
6587 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6588 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6589 * in the operand stack frame. The code generator computes that,
6590 * and it tries to bind all names to slots, so we must let it do
6591 * the deed.
6593 pn3 = NewBindingNode(atom, tc, true);
6594 if (!pn3)
6595 return NULL;
6596 break;
6598 default:
6599 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
6601 case TOK_ERROR:
6602 return NULL;
6605 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6606 JSParseNode *pn4 = expr();
6607 if (!pn4)
6608 return NULL;
6609 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6611 switch (tt) {
6612 #if JS_HAS_DESTRUCTURING
6613 case TOK_LB:
6614 case TOK_LC:
6615 if (!CheckDestructuring(context, &data, pn3, NULL, tc))
6616 return NULL;
6618 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
6619 /* Destructuring requires [key, value] enumeration in JS1.7. */
6620 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6621 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
6622 return NULL;
6625 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6626 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6627 if (!(pn2->pn_iflags & JSITER_FOREACH))
6628 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6630 break;
6631 #endif
6633 case TOK_NAME:
6634 data.pn = pn3;
6635 if (!data.binder(context, &data, atom, tc))
6636 return NULL;
6637 break;
6639 default:;
6642 pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6643 if (!pn2->pn_left)
6644 return NULL;
6645 *pnp = pn2;
6646 pnp = &pn2->pn_right;
6647 } while (tokenStream.matchToken(TOK_FOR));
6649 if (tokenStream.matchToken(TOK_IF)) {
6650 pn2 = TernaryNode::create(tc);
6651 if (!pn2)
6652 return NULL;
6653 pn2->pn_kid1 = condition();
6654 if (!pn2->pn_kid1)
6655 return NULL;
6656 *pnp = pn2;
6657 pnp = &pn2->pn_kid2;
6660 pn2 = UnaryNode::create(tc);
6661 if (!pn2)
6662 return NULL;
6663 pn2->pn_type = type;
6664 pn2->pn_op = op;
6665 pn2->pn_kid = kid;
6666 *pnp = pn2;
6668 PopStatement(tc);
6669 return pn;
6672 #if JS_HAS_GENERATOR_EXPRS
6675 * Starting from a |for| keyword after an expression, parse the comprehension
6676 * tail completing this generator expression. Wrap the expression at kid in a
6677 * generator function that is immediately called to evaluate to the generator
6678 * iterator that is the value of this generator expression.
6680 * Callers pass a blank unary node via pn, which generatorExpr fills in as the
6681 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6682 * expression-statement node that constitutes the body of the |for| loop(s) in
6683 * the generator function.
6685 * Note how unlike Python, we do not evaluate the expression to the right of
6686 * the first |in| in the chain of |for| heads. Instead, a generator expression
6687 * is merely sugar for a generator function expression and its application.
6689 JSParseNode *
6690 Parser::generatorExpr(JSParseNode *pn, JSParseNode *kid)
6692 /* Initialize pn, connecting it to kid. */
6693 JS_ASSERT(pn->pn_arity == PN_UNARY);
6694 pn->pn_type = TOK_YIELD;
6695 pn->pn_op = JSOP_YIELD;
6696 pn->pn_parens = true;
6697 pn->pn_pos = kid->pn_pos;
6698 pn->pn_kid = kid;
6699 pn->pn_hidden = true;
6701 /* Make a new node for the desugared generator function. */
6702 JSParseNode *genfn = FunctionNode::create(tc);
6703 if (!genfn)
6704 return NULL;
6705 genfn->pn_type = TOK_FUNCTION;
6706 genfn->pn_op = JSOP_LAMBDA;
6707 JS_ASSERT(!genfn->pn_body);
6708 genfn->pn_dflags = PND_FUNARG;
6711 JSTreeContext *outertc = tc;
6712 JSTreeContext gentc(tc->parser);
6714 JSFunctionBox *funbox = EnterFunction(genfn, &gentc);
6715 if (!funbox)
6716 return NULL;
6719 * We have to dance around a bit to propagate sharp variables from
6720 * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
6721 * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
6722 * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
6723 * do propagate to gentc.
6725 if (outertc->flags & TCF_HAS_SHARPS) {
6726 gentc.flags |= TCF_IN_FUNCTION;
6727 if (!gentc.ensureSharpSlots())
6728 return NULL;
6732 * We assume conservatively that any deoptimization flag in tc->flags
6733 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6734 * propagate these flags into genfn. For code simplicity we also do
6735 * not detect if the flags were only set in the kid and could be
6736 * removed from tc->flags.
6738 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6739 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6740 funbox->tcflags |= gentc.flags;
6741 genfn->pn_funbox = funbox;
6742 genfn->pn_blockid = gentc.bodyid;
6744 JSParseNode *body = comprehensionTail(pn, outertc->blockid());
6745 if (!body)
6746 return NULL;
6747 JS_ASSERT(!genfn->pn_body);
6748 genfn->pn_body = body;
6749 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6750 genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
6752 if (!LeaveFunction(genfn, &gentc))
6753 return NULL;
6757 * Our result is a call expression that invokes the anonymous generator
6758 * function object.
6760 JSParseNode *result = ListNode::create(tc);
6761 if (!result)
6762 return NULL;
6763 result->pn_type = TOK_LP;
6764 result->pn_op = JSOP_CALL;
6765 result->pn_pos.begin = genfn->pn_pos.begin;
6766 result->initList(genfn);
6767 return result;
6770 static const char js_generator_str[] = "generator";
6772 #endif /* JS_HAS_GENERATOR_EXPRS */
6773 #endif /* JS_HAS_GENERATORS */
6775 JSBool
6776 Parser::argumentList(JSParseNode *listNode)
6778 if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
6779 return JS_TRUE;
6781 do {
6782 JSParseNode *argNode = assignExpr();
6783 if (!argNode)
6784 return JS_FALSE;
6785 #if JS_HAS_GENERATORS
6786 if (argNode->pn_type == TOK_YIELD &&
6787 !argNode->pn_parens &&
6788 tokenStream.peekToken() == TOK_COMMA) {
6789 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
6790 return JS_FALSE;
6792 #endif
6793 #if JS_HAS_GENERATOR_EXPRS
6794 if (tokenStream.matchToken(TOK_FOR)) {
6795 JSParseNode *pn = UnaryNode::create(tc);
6796 if (!pn)
6797 return JS_FALSE;
6798 argNode = generatorExpr(pn, argNode);
6799 if (!argNode)
6800 return JS_FALSE;
6801 if (listNode->pn_count > 1 ||
6802 tokenStream.peekToken() == TOK_COMMA) {
6803 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
6804 js_generator_str);
6805 return JS_FALSE;
6808 #endif
6809 listNode->append(argNode);
6810 } while (tokenStream.matchToken(TOK_COMMA));
6812 if (tokenStream.getToken() != TOK_RP) {
6813 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
6814 return JS_FALSE;
6816 return JS_TRUE;
6819 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6820 static JSParseNode *
6821 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6823 if (pn->pn_type == TOK_FUNCTION) {
6824 JS_ASSERT(pn->pn_arity == PN_FUNC);
6826 JSFunctionBox *funbox = pn->pn_funbox;
6827 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6828 if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
6829 pn->pn_dflags &= ~PND_FUNARG;
6831 return pn;
6834 JSParseNode *
6835 Parser::memberExpr(JSBool allowCallSyntax)
6837 JSParseNode *pn, *pn2, *pn3;
6839 JS_CHECK_RECURSION(context, return NULL);
6841 /* Check for new expression first. */
6842 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6843 if (tt == TOK_NEW) {
6844 pn = ListNode::create(tc);
6845 if (!pn)
6846 return NULL;
6847 pn2 = memberExpr(JS_FALSE);
6848 if (!pn2)
6849 return NULL;
6850 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6851 pn->pn_op = JSOP_NEW;
6852 pn->initList(pn2);
6853 pn->pn_pos.begin = pn2->pn_pos.begin;
6855 if (tokenStream.matchToken(TOK_LP) && !argumentList(pn))
6856 return NULL;
6857 if (pn->pn_count > ARGC_LIMIT) {
6858 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
6859 JSMSG_TOO_MANY_CON_ARGS);
6860 return NULL;
6862 pn->pn_pos.end = pn->last()->pn_pos.end;
6863 } else {
6864 pn = primaryExpr(tt, JS_FALSE);
6865 if (!pn)
6866 return NULL;
6868 if (pn->pn_type == TOK_ANYNAME ||
6869 pn->pn_type == TOK_AT ||
6870 pn->pn_type == TOK_DBLCOLON) {
6871 pn2 = NewOrRecycledNode(tc);
6872 if (!pn2)
6873 return NULL;
6874 pn2->pn_type = TOK_UNARYOP;
6875 pn2->pn_pos = pn->pn_pos;
6876 pn2->pn_op = JSOP_XMLNAME;
6877 pn2->pn_arity = PN_UNARY;
6878 pn2->pn_parens = false;
6879 pn2->pn_kid = pn;
6880 pn = pn2;
6884 while ((tt = tokenStream.getToken()) > TOK_EOF) {
6885 if (tt == TOK_DOT) {
6886 pn2 = NameNode::create(NULL, tc);
6887 if (!pn2)
6888 return NULL;
6889 #if JS_HAS_XML_SUPPORT
6890 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6891 pn3 = primaryExpr(tt, JS_TRUE);
6892 if (!pn3)
6893 return NULL;
6895 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6896 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
6897 pn2->pn_op = JSOP_GETPROP;
6898 pn2->pn_expr = pn;
6899 pn2->pn_atom = pn3->pn_atom;
6900 RecycleTree(pn3, tc);
6901 } else {
6902 if (tt == TOK_LP) {
6903 pn2->pn_type = TOK_FILTER;
6904 pn2->pn_op = JSOP_FILTER;
6906 /* A filtering predicate is like a with statement. */
6907 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6908 } else if (TokenKindIsXML(PN_TYPE(pn3))) {
6909 pn2->pn_type = TOK_LB;
6910 pn2->pn_op = JSOP_GETELEM;
6911 } else {
6912 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
6913 return NULL;
6915 pn2->pn_arity = PN_BINARY;
6916 pn2->pn_left = pn;
6917 pn2->pn_right = pn3;
6919 #else
6920 MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME, JSMSG_NAME_AFTER_DOT, TSF_KEYWORD_IS_NAME);
6921 pn2->pn_op = JSOP_GETPROP;
6922 pn2->pn_expr = pn;
6923 pn2->pn_atom = tokenStream.currentToken().t_atom;
6924 #endif
6925 pn2->pn_pos.begin = pn->pn_pos.begin;
6926 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6927 #if JS_HAS_XML_SUPPORT
6928 } else if (tt == TOK_DBLDOT) {
6929 pn2 = BinaryNode::create(tc);
6930 if (!pn2)
6931 return NULL;
6932 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6933 pn3 = primaryExpr(tt, JS_TRUE);
6934 if (!pn3)
6935 return NULL;
6936 tt = PN_TYPE(pn3);
6937 if (tt == TOK_NAME && !pn3->pn_parens) {
6938 pn3->pn_type = TOK_STRING;
6939 pn3->pn_arity = PN_NULLARY;
6940 pn3->pn_op = JSOP_QNAMEPART;
6941 } else if (!TokenKindIsXML(tt)) {
6942 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
6943 return NULL;
6945 pn2->pn_op = JSOP_DESCENDANTS;
6946 pn2->pn_left = pn;
6947 pn2->pn_right = pn3;
6948 pn2->pn_pos.begin = pn->pn_pos.begin;
6949 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6950 #endif
6951 } else if (tt == TOK_LB) {
6952 pn2 = BinaryNode::create(tc);
6953 if (!pn2)
6954 return NULL;
6955 pn3 = expr();
6956 if (!pn3)
6957 return NULL;
6959 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
6960 pn2->pn_pos.begin = pn->pn_pos.begin;
6961 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
6964 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
6965 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
6966 * the interpreter from fast property access. However, if the
6967 * bracketed string is a uint32, we rewrite pn3 to be a number
6968 * instead of a string.
6970 do {
6971 if (pn3->pn_type == TOK_STRING) {
6972 jsuint index;
6974 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
6975 pn2->pn_type = TOK_DOT;
6976 pn2->pn_op = JSOP_GETPROP;
6977 pn2->pn_arity = PN_NAME;
6978 pn2->pn_expr = pn;
6979 pn2->pn_atom = pn3->pn_atom;
6980 break;
6982 pn3->pn_type = TOK_NUMBER;
6983 pn3->pn_op = JSOP_DOUBLE;
6984 pn3->pn_dval = index;
6986 pn2->pn_op = JSOP_GETELEM;
6987 pn2->pn_left = pn;
6988 pn2->pn_right = pn3;
6989 } while (0);
6990 } else if (allowCallSyntax && tt == TOK_LP) {
6991 pn2 = ListNode::create(tc);
6992 if (!pn2)
6993 return NULL;
6994 pn2->pn_op = JSOP_CALL;
6996 pn = CheckForImmediatelyAppliedLambda(pn);
6997 if (pn->pn_op == JSOP_NAME) {
6998 if (pn->pn_atom == context->runtime->atomState.evalAtom) {
6999 /* Select JSOP_EVAL and flag tc as heavyweight. */
7000 pn2->pn_op = JSOP_EVAL;
7001 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7003 } else if (pn->pn_op == JSOP_GETPROP) {
7004 if (pn->pn_atom == context->runtime->atomState.applyAtom ||
7005 pn->pn_atom == context->runtime->atomState.callAtom) {
7006 /* Select JSOP_APPLY given foo.apply(...). */
7007 pn2->pn_op = JSOP_APPLY;
7011 pn2->initList(pn);
7012 pn2->pn_pos.begin = pn->pn_pos.begin;
7014 if (!argumentList(pn2))
7015 return NULL;
7016 if (pn2->pn_count > ARGC_LIMIT) {
7017 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7018 JSMSG_TOO_MANY_FUN_ARGS);
7019 return NULL;
7021 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7022 } else {
7023 tokenStream.ungetToken();
7024 return pn;
7027 pn = pn2;
7029 if (tt == TOK_ERROR)
7030 return NULL;
7031 return pn;
7034 JSParseNode *
7035 Parser::bracketedExpr()
7037 uintN oldflags;
7038 JSParseNode *pn;
7041 * Always accept the 'in' operator in a parenthesized expression,
7042 * where it's unambiguous, even if we might be parsing the init of a
7043 * for statement.
7045 oldflags = tc->flags;
7046 tc->flags &= ~TCF_IN_FOR_INIT;
7047 pn = expr();
7048 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7049 return pn;
7052 #if JS_HAS_XML_SUPPORT
7054 JSParseNode *
7055 Parser::endBracketedExpr()
7057 JSParseNode *pn;
7059 pn = bracketedExpr();
7060 if (!pn)
7061 return NULL;
7063 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7064 return pn;
7068 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7070 * AttributeIdentifier:
7071 * @ PropertySelector
7072 * @ QualifiedIdentifier
7073 * @ [ Expression ]
7075 * PropertySelector:
7076 * Identifier
7079 * QualifiedIdentifier:
7080 * PropertySelector :: PropertySelector
7081 * PropertySelector :: [ Expression ]
7083 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7085 * AttributeIdentifier:
7086 * @ QualifiedIdentifier
7087 * @ [ Expression ]
7089 * PropertySelector:
7090 * Identifier
7093 * QualifiedIdentifier:
7094 * PropertySelector :: PropertySelector
7095 * PropertySelector :: [ Expression ]
7096 * PropertySelector
7098 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7099 * for that rule to result in a name node, but ECMA-357 extends the grammar
7100 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7102 * QualifiedIdentifier:
7103 * PropertySelector QualifiedSuffix
7105 * QualifiedSuffix:
7106 * :: PropertySelector
7107 * :: [ Expression ]
7108 * /nothing/
7110 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7112 * PrimaryExpression:
7113 * Identifier QualifiedSuffix
7115 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7116 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7118 JSParseNode *
7119 Parser::propertySelector()
7121 JSParseNode *pn;
7123 pn = NullaryNode::create(tc);
7124 if (!pn)
7125 return NULL;
7126 if (pn->pn_type == TOK_STAR) {
7127 pn->pn_type = TOK_ANYNAME;
7128 pn->pn_op = JSOP_ANYNAME;
7129 pn->pn_atom = context->runtime->atomState.starAtom;
7130 } else {
7131 JS_ASSERT(pn->pn_type == TOK_NAME);
7132 pn->pn_op = JSOP_QNAMEPART;
7133 pn->pn_arity = PN_NAME;
7134 pn->pn_atom = tokenStream.currentToken().t_atom;
7135 pn->pn_cookie.makeFree();
7137 return pn;
7140 JSParseNode *
7141 Parser::qualifiedSuffix(JSParseNode *pn)
7143 JSParseNode *pn2, *pn3;
7144 TokenKind tt;
7146 JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
7147 pn2 = NameNode::create(NULL, tc);
7148 if (!pn2)
7149 return NULL;
7151 /* Left operand of :: must be evaluated if it is an identifier. */
7152 if (pn->pn_op == JSOP_QNAMEPART)
7153 pn->pn_op = JSOP_NAME;
7155 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7156 if (tt == TOK_STAR || tt == TOK_NAME) {
7157 /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7158 pn2->pn_op = JSOP_QNAMECONST;
7159 pn2->pn_pos.begin = pn->pn_pos.begin;
7160 pn2->pn_atom = (tt == TOK_STAR)
7161 ? context->runtime->atomState.starAtom
7162 : tokenStream.currentToken().t_atom;
7163 pn2->pn_expr = pn;
7164 pn2->pn_cookie.makeFree();
7165 return pn2;
7168 if (tt != TOK_LB) {
7169 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7170 return NULL;
7172 pn3 = endBracketedExpr();
7173 if (!pn3)
7174 return NULL;
7176 pn2->pn_op = JSOP_QNAME;
7177 pn2->pn_arity = PN_BINARY;
7178 pn2->pn_pos.begin = pn->pn_pos.begin;
7179 pn2->pn_pos.end = pn3->pn_pos.end;
7180 pn2->pn_left = pn;
7181 pn2->pn_right = pn3;
7182 return pn2;
7185 JSParseNode *
7186 Parser::qualifiedIdentifier()
7188 JSParseNode *pn;
7190 pn = propertySelector();
7191 if (!pn)
7192 return NULL;
7193 if (tokenStream.matchToken(TOK_DBLCOLON)) {
7194 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7195 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7196 pn = qualifiedSuffix(pn);
7198 return pn;
7201 JSParseNode *
7202 Parser::attributeIdentifier()
7204 JSParseNode *pn, *pn2;
7205 TokenKind tt;
7207 JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
7208 pn = UnaryNode::create(tc);
7209 if (!pn)
7210 return NULL;
7211 pn->pn_op = JSOP_TOATTRNAME;
7212 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7213 if (tt == TOK_STAR || tt == TOK_NAME) {
7214 pn2 = qualifiedIdentifier();
7215 } else if (tt == TOK_LB) {
7216 pn2 = endBracketedExpr();
7217 } else {
7218 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7219 return NULL;
7221 if (!pn2)
7222 return NULL;
7223 pn->pn_kid = pn2;
7224 return pn;
7228 * Make a TOK_LC unary node whose pn_kid is an expression.
7230 JSParseNode *
7231 Parser::xmlExpr(JSBool inTag)
7233 JSParseNode *pn, *pn2;
7235 JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
7236 pn = UnaryNode::create(tc);
7237 if (!pn)
7238 return NULL;
7241 * Turn off XML tag mode. We save the old value of the flag because it may
7242 * already be off: XMLExpr is called both from within a tag, and from
7243 * within text contained in an element, but outside of any start, end, or
7244 * point tag.
7246 bool oldflag = tokenStream.isXMLTagMode();
7247 tokenStream.setXMLTagMode(false);
7248 pn2 = expr();
7249 if (!pn2)
7250 return NULL;
7252 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7253 tokenStream.setXMLTagMode(oldflag);
7254 pn->pn_kid = pn2;
7255 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7256 return pn;
7260 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7261 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7262 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7263 * child of a container tag.
7265 JSParseNode *
7266 Parser::xmlAtomNode()
7268 JSParseNode *pn = NullaryNode::create(tc);
7269 if (!pn)
7270 return NULL;
7271 const Token &tok = tokenStream.currentToken();
7272 pn->pn_op = tok.t_op;
7273 pn->pn_atom = tok.t_atom;
7274 if (tok.type == TOK_XMLPI)
7275 pn->pn_atom2 = tok.t_atom2;
7276 return pn;
7280 * Parse the productions:
7282 * XMLNameExpr:
7283 * XMLName XMLNameExpr?
7284 * { Expr } XMLNameExpr?
7286 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7287 * a list of names and/or expressions, a single expression, or a single name.
7288 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7289 * will be TOK_LC.
7291 JSParseNode *
7292 Parser::xmlNameExpr()
7294 JSParseNode *pn, *pn2, *list;
7295 TokenKind tt;
7297 pn = list = NULL;
7298 do {
7299 tt = tokenStream.currentToken().type;
7300 if (tt == TOK_LC) {
7301 pn2 = xmlExpr(JS_TRUE);
7302 if (!pn2)
7303 return NULL;
7304 } else {
7305 JS_ASSERT(tt == TOK_XMLNAME);
7306 pn2 = xmlAtomNode();
7307 if (!pn2)
7308 return NULL;
7311 if (!pn) {
7312 pn = pn2;
7313 } else {
7314 if (!list) {
7315 list = ListNode::create(tc);
7316 if (!list)
7317 return NULL;
7318 list->pn_type = TOK_XMLNAME;
7319 list->pn_pos.begin = pn->pn_pos.begin;
7320 list->initList(pn);
7321 list->pn_xflags = PNX_CANTFOLD;
7322 pn = list;
7324 pn->pn_pos.end = pn2->pn_pos.end;
7325 pn->append(pn2);
7327 } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
7329 tokenStream.ungetToken();
7330 return pn;
7334 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7335 * at compile time into a JSXML tree.
7337 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7338 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7339 : (pn)->pn_type != TOK_LC)
7342 * Parse the productions:
7344 * XMLTagContent:
7345 * XMLNameExpr
7346 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7347 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7349 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7350 * produces a list of name and attribute values and/or braced expressions, a
7351 * single expression, or a single name.
7353 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7354 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7355 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7356 * we parsed exactly one expression.
7358 JSParseNode *
7359 Parser::xmlTagContent(TokenKind tagtype, JSAtom **namep)
7361 JSParseNode *pn, *pn2, *list;
7362 TokenKind tt;
7364 pn = xmlNameExpr();
7365 if (!pn)
7366 return NULL;
7367 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7368 list = NULL;
7370 while (tokenStream.matchToken(TOK_XMLSPACE)) {
7371 tt = tokenStream.getToken();
7372 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7373 tokenStream.ungetToken();
7374 break;
7377 pn2 = xmlNameExpr();
7378 if (!pn2)
7379 return NULL;
7380 if (!list) {
7381 list = ListNode::create(tc);
7382 if (!list)
7383 return NULL;
7384 list->pn_type = tagtype;
7385 list->pn_pos.begin = pn->pn_pos.begin;
7386 list->initList(pn);
7387 pn = list;
7389 pn->append(pn2);
7390 if (!XML_FOLDABLE(pn2))
7391 pn->pn_xflags |= PNX_CANTFOLD;
7393 tokenStream.matchToken(TOK_XMLSPACE);
7394 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7395 tokenStream.matchToken(TOK_XMLSPACE);
7397 tt = tokenStream.getToken();
7398 if (tt == TOK_XMLATTR) {
7399 pn2 = xmlAtomNode();
7400 } else if (tt == TOK_LC) {
7401 pn2 = xmlExpr(JS_TRUE);
7402 pn->pn_xflags |= PNX_CANTFOLD;
7403 } else {
7404 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE);
7405 return NULL;
7407 if (!pn2)
7408 return NULL;
7409 pn->pn_pos.end = pn2->pn_pos.end;
7410 pn->append(pn2);
7413 return pn;
7416 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7417 JS_BEGIN_MACRO \
7418 if ((tt) <= TOK_EOF) { \
7419 if ((tt) == TOK_EOF) { \
7420 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_END_OF_XML_SOURCE); \
7422 return result; \
7424 JS_END_MACRO
7427 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7428 * that opens the end tag for the container.
7430 JSBool
7431 Parser::xmlElementContent(JSParseNode *pn)
7433 tokenStream.setXMLTagMode(false);
7434 for (;;) {
7435 TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
7436 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7438 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7439 JSAtom *textAtom = tokenStream.currentToken().t_atom;
7440 if (textAtom) {
7441 /* Non-zero-length XML text scanned. */
7442 JSParseNode *pn2 = xmlAtomNode();
7443 if (!pn2)
7444 return JS_FALSE;
7445 pn->pn_pos.end = pn2->pn_pos.end;
7446 pn->append(pn2);
7449 tt = tokenStream.getToken(TSF_OPERAND);
7450 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7451 if (tt == TOK_XMLETAGO)
7452 break;
7454 JSParseNode *pn2;
7455 if (tt == TOK_LC) {
7456 pn2 = xmlExpr(JS_FALSE);
7457 pn->pn_xflags |= PNX_CANTFOLD;
7458 } else if (tt == TOK_XMLSTAGO) {
7459 pn2 = xmlElementOrList(JS_FALSE);
7460 if (pn2) {
7461 pn2->pn_xflags &= ~PNX_XMLROOT;
7462 pn->pn_xflags |= pn2->pn_xflags;
7464 } else {
7465 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7466 tt == TOK_XMLPI);
7467 pn2 = xmlAtomNode();
7469 if (!pn2)
7470 return JS_FALSE;
7471 pn->pn_pos.end = pn2->pn_pos.end;
7472 pn->append(pn2);
7474 tokenStream.setXMLTagMode(true);
7476 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
7477 return JS_TRUE;
7481 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7483 JSParseNode *
7484 Parser::xmlElementOrList(JSBool allowList)
7486 JSParseNode *pn, *pn2, *list;
7487 TokenKind tt;
7488 JSAtom *startAtom, *endAtom;
7490 JS_CHECK_RECURSION(context, return NULL);
7492 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
7493 pn = ListNode::create(tc);
7494 if (!pn)
7495 return NULL;
7497 tokenStream.setXMLTagMode(true);
7498 tt = tokenStream.getToken();
7499 if (tt == TOK_ERROR)
7500 return NULL;
7502 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7504 * XMLElement. Append the tag and its contents, if any, to pn.
7506 pn2 = xmlTagContent(TOK_XMLSTAGO, &startAtom);
7507 if (!pn2)
7508 return NULL;
7509 tokenStream.matchToken(TOK_XMLSPACE);
7511 tt = tokenStream.getToken();
7512 if (tt == TOK_XMLPTAGC) {
7513 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7514 if (pn2->pn_type == TOK_XMLSTAGO) {
7515 pn->makeEmpty();
7516 RecycleTree(pn, tc);
7517 pn = pn2;
7518 } else {
7519 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7520 pn2->pn_type == TOK_LC);
7521 pn->initList(pn2);
7522 if (!XML_FOLDABLE(pn2))
7523 pn->pn_xflags |= PNX_CANTFOLD;
7525 pn->pn_type = TOK_XMLPTAGC;
7526 pn->pn_xflags |= PNX_XMLROOT;
7527 } else {
7528 /* We had better have a tag-close (>) at this point. */
7529 if (tt != TOK_XMLTAGC) {
7530 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7531 return NULL;
7533 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7535 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7536 if (pn2->pn_type != TOK_XMLSTAGO) {
7537 pn->initList(pn2);
7538 if (!XML_FOLDABLE(pn2))
7539 pn->pn_xflags |= PNX_CANTFOLD;
7540 pn2 = pn;
7541 pn = ListNode::create(tc);
7542 if (!pn)
7543 return NULL;
7546 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7547 pn->pn_type = TOK_XMLELEM;
7548 pn->pn_pos.begin = pn2->pn_pos.begin;
7549 pn->initList(pn2);
7550 if (!XML_FOLDABLE(pn2))
7551 pn->pn_xflags |= PNX_CANTFOLD;
7552 pn->pn_xflags |= PNX_XMLROOT;
7554 /* Get element contents and delimiting end-tag-open sequence. */
7555 if (!xmlElementContent(pn))
7556 return NULL;
7558 tt = tokenStream.getToken();
7559 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7560 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7561 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7562 return NULL;
7565 /* Parse end tag; check mismatch at compile-time if we can. */
7566 pn2 = xmlTagContent(TOK_XMLETAGO, &endAtom);
7567 if (!pn2)
7568 return NULL;
7569 if (pn2->pn_type == TOK_XMLETAGO) {
7570 /* Oops, end tag has attributes! */
7571 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
7572 return NULL;
7574 if (endAtom && startAtom && endAtom != startAtom) {
7575 JSString *str = ATOM_TO_STRING(startAtom);
7577 /* End vs. start tag name mismatch: point to the tag name. */
7578 reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
7579 str->chars());
7580 return NULL;
7583 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7584 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7585 list = ListNode::create(tc);
7586 if (!list)
7587 return NULL;
7588 list->pn_type = TOK_XMLETAGO;
7589 list->initList(pn2);
7590 pn->append(list);
7591 if (!XML_FOLDABLE(pn2)) {
7592 list->pn_xflags |= PNX_CANTFOLD;
7593 pn->pn_xflags |= PNX_CANTFOLD;
7596 tokenStream.matchToken(TOK_XMLSPACE);
7597 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7600 /* Set pn_op now that pn has been updated to its final value. */
7601 pn->pn_op = JSOP_TOXML;
7602 } else if (allowList && tt == TOK_XMLTAGC) {
7603 /* XMLList Initialiser. */
7604 pn->pn_type = TOK_XMLLIST;
7605 pn->pn_op = JSOP_TOXMLLIST;
7606 pn->makeEmpty();
7607 pn->pn_xflags |= PNX_XMLROOT;
7608 if (!xmlElementContent(pn))
7609 return NULL;
7611 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7612 } else {
7613 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX);
7614 return NULL;
7616 tokenStream.setXMLTagMode(false);
7618 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7619 return pn;
7622 JSParseNode *
7623 Parser::xmlElementOrListRoot(JSBool allowList)
7625 uint32 oldopts;
7626 JSParseNode *pn;
7629 * Force XML support to be enabled so that comments and CDATA literals
7630 * are recognized, instead of <! followed by -- starting an HTML comment
7631 * to end of line (used in script tags to hide content from old browsers
7632 * that don't recognize <script>).
7634 oldopts = JS_SetOptions(context, context->options | JSOPTION_XML);
7635 pn = xmlElementOrList(allowList);
7636 JS_SetOptions(context, oldopts);
7637 return pn;
7640 JSParseNode *
7641 Parser::parseXMLText(JSObject *chain, bool allowList)
7644 * Push a compiler frame if we have no frames, or if the top frame is a
7645 * lightweight function activation, or if its scope chain doesn't match
7646 * the one passed to us.
7648 JSTreeContext xmltc(this);
7649 xmltc.scopeChain = chain;
7651 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7652 tokenStream.setXMLOnlyMode();
7653 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
7655 JSParseNode *pn;
7656 if (tt != TOK_XMLSTAGO) {
7657 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
7658 pn = NULL;
7659 } else {
7660 pn = xmlElementOrListRoot(allowList);
7662 tokenStream.setXMLOnlyMode(false);
7664 return pn;
7667 #endif /* JS_HAS_XMLSUPPORT */
7669 #if JS_HAS_BLOCK_SCOPE
7671 * Check whether blockid is an active scoping statement in tc. This code is
7672 * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
7673 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7674 * and let blocks and expressions (not let declarations).
7676 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7677 * due to hoisting, let in a for loop head, let block, or let expression acts
7678 * like Scheme's let: initializers are evaluated without the new let bindings
7679 * being in scope.
7681 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7682 * bindings push on the front of the tc->decls JSAtomList (either the singular
7683 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7684 * scope bindings of the same name.
7686 * This simplifies binding lookup code at the price of a linear search here,
7687 * but only if code uses let (var predominates), and even then this function's
7688 * loop iterates more than once only in crazy cases.
7690 static inline bool
7691 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7693 if (blockid > tc->blockid())
7694 return false;
7695 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7696 if (stmt->blockid == blockid)
7697 return true;
7699 return false;
7701 #endif
7703 JSParseNode *
7704 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
7706 JSParseNode *pn, *pn2, *pn3;
7707 JSOp op;
7709 JS_CHECK_RECURSION(context, return NULL);
7711 switch (tt) {
7712 case TOK_FUNCTION:
7713 #if JS_HAS_XML_SUPPORT
7714 if (tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
7715 pn2 = NullaryNode::create(tc);
7716 if (!pn2)
7717 return NULL;
7718 pn2->pn_type = TOK_FUNCTION;
7719 pn = qualifiedSuffix(pn2);
7720 if (!pn)
7721 return NULL;
7722 break;
7724 #endif
7725 pn = functionExpr();
7726 if (!pn)
7727 return NULL;
7728 break;
7730 case TOK_LB:
7732 JSBool matched;
7733 jsuint index;
7735 pn = ListNode::create(tc);
7736 if (!pn)
7737 return NULL;
7738 pn->pn_type = TOK_RB;
7739 pn->pn_op = JSOP_NEWINIT;
7740 pn->makeEmpty();
7742 #if JS_HAS_GENERATORS
7743 pn->pn_blockid = tc->blockidGen;
7744 #endif
7746 matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
7747 if (!matched) {
7748 for (index = 0; ; index++) {
7749 if (index == JS_ARGS_LENGTH_MAX) {
7750 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
7751 return NULL;
7754 tt = tokenStream.peekToken(TSF_OPERAND);
7755 if (tt == TOK_RB) {
7756 pn->pn_xflags |= PNX_ENDCOMMA;
7757 break;
7760 if (tt == TOK_COMMA) {
7761 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7762 tokenStream.matchToken(TOK_COMMA);
7763 pn2 = NullaryNode::create(tc);
7764 pn->pn_xflags |= PNX_HOLEY;
7765 } else {
7766 pn2 = assignExpr();
7768 if (!pn2)
7769 return NULL;
7770 pn->append(pn2);
7772 if (tt != TOK_COMMA) {
7773 /* If we didn't already match TOK_COMMA in above case. */
7774 if (!tokenStream.matchToken(TOK_COMMA))
7775 break;
7779 #if JS_HAS_GENERATORS
7781 * At this point, (index == 0 && pn->pn_count != 0) implies one
7782 * element initialiser was parsed.
7784 * An array comprehension of the form:
7786 * [i * j for (i in o) for (j in p) if (i != j)]
7788 * translates to roughly the following let expression:
7790 * let (array = new Array, i, j) {
7791 * for (i in o) let {
7792 * for (j in p)
7793 * if (i != j)
7794 * array.push(i * j)
7796 * array
7799 * where array is a nameless block-local variable. The "roughly"
7800 * means that an implementation may optimize away the array.push.
7801 * An array comprehension opens exactly one block scope, no matter
7802 * how many for heads it contains.
7804 * Each let () {...} or for (let ...) ... compiles to:
7806 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7808 * where <o> is a literal object representing the block scope,
7809 * with <n> properties, naming each var declared in the block.
7811 * Each var declaration in a let-block binds a name in <o> at
7812 * compile time, and allocates a slot on the operand stack at
7813 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7814 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7815 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7816 * local slot's stack index from fp->spbase.
7818 * The array comprehension iteration step, array.push(i * j) in
7819 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7820 * where <array> is the index of array's stack slot.
7822 if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
7823 JSParseNode *pnexp, *pntop;
7825 /* Relabel pn as an array comprehension node. */
7826 pn->pn_type = TOK_ARRAYCOMP;
7829 * Remove the comprehension expression from pn's linked list
7830 * and save it via pnexp. We'll re-install it underneath the
7831 * ARRAYPUSH node after we parse the rest of the comprehension.
7833 pnexp = pn->last();
7834 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7835 pn->pn_tail = (--pn->pn_count == 1)
7836 ? &pn->pn_head->pn_next
7837 : &pn->pn_head;
7838 *pn->pn_tail = NULL;
7840 pntop = comprehensionTail(pnexp, pn->pn_blockid,
7841 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7842 if (!pntop)
7843 return NULL;
7844 pn->append(pntop);
7846 #endif /* JS_HAS_GENERATORS */
7848 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7850 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7851 return pn;
7854 case TOK_LC:
7856 JSBool afterComma;
7857 JSParseNode *pnval;
7860 * A map from property names we've seen thus far to bit masks.
7861 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
7862 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
7863 * if we've seen as getter, and both of those if we've just
7864 * seen an ordinary value.
7866 JSAutoAtomList seen(tc->parser);
7868 pn = ListNode::create(tc);
7869 if (!pn)
7870 return NULL;
7871 pn->pn_type = TOK_RC;
7872 pn->pn_op = JSOP_NEWINIT;
7873 pn->makeEmpty();
7875 afterComma = JS_FALSE;
7876 for (;;) {
7877 JSAtom *atom;
7878 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7879 switch (tt) {
7880 case TOK_NUMBER:
7881 pn3 = NullaryNode::create(tc);
7882 if (!pn3)
7883 return NULL;
7884 pn3->pn_dval = tokenStream.currentToken().t_dval;
7885 if (tc->needStrictChecks()) {
7886 atom = js_AtomizeDouble(context, pn3->pn_dval);
7887 if (!atom)
7888 return NULL;
7889 } else {
7890 atom = NULL; /* for the compiler */
7892 break;
7893 case TOK_NAME:
7895 atom = tokenStream.currentToken().t_atom;
7896 if (atom == context->runtime->atomState.getAtom)
7897 op = JSOP_GETTER;
7898 else if (atom == context->runtime->atomState.setAtom)
7899 op = JSOP_SETTER;
7900 else
7901 goto property_name;
7903 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7904 if (tt == TOK_NAME || tt == TOK_STRING) {
7905 atom = tokenStream.currentToken().t_atom;
7906 pn3 = NameNode::create(atom, tc);
7907 if (!pn3)
7908 return NULL;
7909 } else if (tt == TOK_NUMBER) {
7910 pn3 = NullaryNode::create(tc);
7911 if (!pn3)
7912 return NULL;
7913 pn3->pn_dval = tokenStream.currentToken().t_dval;
7914 if (tc->needStrictChecks()) {
7915 atom = js_AtomizeDouble(context, pn3->pn_dval);
7916 if (!atom)
7917 return NULL;
7918 } else {
7919 atom = NULL; /* for the compiler */
7921 } else {
7922 tokenStream.ungetToken();
7923 goto property_name;
7926 /* We have to fake a 'function' token here. */
7927 tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
7928 pn2 = functionDef(JSFUN_LAMBDA, false);
7929 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
7930 goto skip;
7932 property_name:
7933 case TOK_STRING:
7934 atom = tokenStream.currentToken().t_atom;
7935 pn3 = NullaryNode::create(tc);
7936 if (!pn3)
7937 return NULL;
7938 pn3->pn_atom = atom;
7939 break;
7940 case TOK_RC:
7941 goto end_obj_init;
7942 default:
7943 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_PROP_ID);
7944 return NULL;
7947 op = JSOP_INITPROP;
7948 tt = tokenStream.getToken();
7949 if (tt == TOK_COLON) {
7950 pnval = assignExpr();
7951 } else {
7952 #if JS_HAS_DESTRUCTURING_SHORTHAND
7953 if (tt != TOK_COMMA && tt != TOK_RC) {
7954 #endif
7955 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_COLON_AFTER_ID);
7956 return NULL;
7957 #if JS_HAS_DESTRUCTURING_SHORTHAND
7961 * Support, e.g., |var {x, y} = o| as destructuring shorthand
7962 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
7964 tokenStream.ungetToken();
7965 pn->pn_xflags |= PNX_DESTRUCT;
7966 pnval = pn3;
7967 if (pnval->pn_type == TOK_NAME) {
7968 pnval->pn_arity = PN_NAME;
7969 ((NameNode *)pnval)->initCommon(tc);
7971 #endif
7974 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
7975 skip:
7976 if (!pn2)
7977 return NULL;
7978 pn->append(pn2);
7981 * In strict mode code, check for duplicate property names. Treat
7982 * getters and setters as distinct attributes of each property. A
7983 * plain old value conflicts with a getter or a setter.
7985 if (tc->needStrictChecks()) {
7986 unsigned attributesMask;
7987 if (op == JSOP_INITPROP) {
7988 attributesMask = JSPROP_GETTER | JSPROP_SETTER;
7989 } else if (op == JSOP_GETTER) {
7990 attributesMask = JSPROP_GETTER;
7991 } else if (op == JSOP_SETTER) {
7992 attributesMask = JSPROP_SETTER;
7993 } else {
7994 JS_NOT_REACHED("bad opcode in object initializer");
7995 attributesMask = 0;
7999 * Use only string-valued atoms for detecting duplicate
8000 * properties so that 1 and "1" properly collide.
8002 if (ATOM_IS_DOUBLE(atom)) {
8003 JSString *str = js_NumberToString(context, pn3->pn_dval);
8004 if (!str)
8005 return JS_FALSE;
8006 atom = js_AtomizeString(context, str, 0);
8007 if (!atom)
8008 return JS_FALSE;
8011 JSAtomListElement *ale = seen.lookup(atom);
8012 if (ale) {
8013 if (ALE_INDEX(ale) & attributesMask) {
8014 const char *name = js_AtomToPrintableString(context, atom);
8015 if (!name ||
8016 !ReportStrictModeError(context, &tokenStream, tc, NULL,
8017 JSMSG_DUPLICATE_PROPERTY, name)) {
8018 return NULL;
8021 ALE_SET_INDEX(ale, attributesMask | ALE_INDEX(ale));
8022 } else {
8023 ale = seen.add(tc->parser, atom);
8024 if (!ale)
8025 return NULL;
8026 ALE_SET_INDEX(ale, attributesMask);
8030 tt = tokenStream.getToken();
8031 if (tt == TOK_RC)
8032 goto end_obj_init;
8033 if (tt != TOK_COMMA) {
8034 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
8035 return NULL;
8037 afterComma = JS_TRUE;
8040 end_obj_init:
8041 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8042 return pn;
8045 #if JS_HAS_BLOCK_SCOPE
8046 case TOK_LET:
8047 pn = letBlock(JS_FALSE);
8048 if (!pn)
8049 return NULL;
8050 break;
8051 #endif
8053 #if JS_HAS_SHARP_VARS
8054 case TOK_DEFSHARP:
8055 pn = UnaryNode::create(tc);
8056 if (!pn)
8057 return NULL;
8058 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8059 tt = tokenStream.getToken(TSF_OPERAND);
8060 pn->pn_kid = primaryExpr(tt, JS_FALSE);
8061 if (!pn->pn_kid)
8062 return NULL;
8063 if (PN_TYPE(pn->pn_kid) == TOK_USESHARP ||
8064 PN_TYPE(pn->pn_kid) == TOK_DEFSHARP ||
8065 PN_TYPE(pn->pn_kid) == TOK_STRING ||
8066 PN_TYPE(pn->pn_kid) == TOK_NUMBER ||
8067 PN_TYPE(pn->pn_kid) == TOK_PRIMARY) {
8068 reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
8069 return NULL;
8071 if (!tc->ensureSharpSlots())
8072 return NULL;
8073 break;
8075 case TOK_USESHARP:
8076 /* Check for forward/dangling references at runtime, to allow eval. */
8077 pn = NullaryNode::create(tc);
8078 if (!pn)
8079 return NULL;
8080 if (!tc->ensureSharpSlots())
8081 return NULL;
8082 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8083 break;
8084 #endif /* JS_HAS_SHARP_VARS */
8086 case TOK_LP:
8088 JSBool genexp;
8090 pn = parenExpr(NULL, &genexp);
8091 if (!pn)
8092 return NULL;
8093 pn->pn_parens = true;
8094 if (!genexp)
8095 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8096 break;
8099 #if JS_HAS_XML_SUPPORT
8100 case TOK_STAR:
8101 pn = qualifiedIdentifier();
8102 if (!pn)
8103 return NULL;
8104 break;
8106 case TOK_AT:
8107 pn = attributeIdentifier();
8108 if (!pn)
8109 return NULL;
8110 break;
8112 case TOK_XMLSTAGO:
8113 pn = xmlElementOrListRoot(JS_TRUE);
8114 if (!pn)
8115 return NULL;
8116 break;
8117 #endif /* JS_HAS_XML_SUPPORT */
8119 case TOK_STRING:
8120 #if JS_HAS_SHARP_VARS
8121 /* FALL THROUGH */
8122 #endif
8124 #if JS_HAS_XML_SUPPORT
8125 case TOK_XMLCDATA:
8126 case TOK_XMLCOMMENT:
8127 case TOK_XMLPI:
8128 #endif
8129 pn = NullaryNode::create(tc);
8130 if (!pn)
8131 return NULL;
8132 pn->pn_atom = tokenStream.currentToken().t_atom;
8133 #if JS_HAS_XML_SUPPORT
8134 if (tt == TOK_XMLPI)
8135 pn->pn_atom2 = tokenStream.currentToken().t_atom2;
8136 else
8137 #endif
8138 pn->pn_op = tokenStream.currentToken().t_op;
8139 break;
8141 case TOK_NAME:
8142 pn = NameNode::create(tokenStream.currentToken().t_atom, tc);
8143 if (!pn)
8144 return NULL;
8145 JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
8146 pn->pn_op = JSOP_NAME;
8148 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8149 pn->pn_atom == context->runtime->atomState.argumentsAtom) {
8151 * Flag arguments usage so we can avoid unsafe optimizations such
8152 * as formal parameter assignment analysis (because of the hated
8153 * feature whereby arguments alias formals). We do this even for
8154 * a reference of the form foo.arguments, which ancient code may
8155 * still use instead of arguments (more hate).
8157 NoteArgumentsUse(tc);
8160 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8161 * to do this work (new rule for the emitter to count on).
8163 if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8164 pn->pn_op = JSOP_ARGUMENTS;
8165 pn->pn_dflags |= PND_BOUND;
8167 } else if ((!afterDot
8168 #if JS_HAS_XML_SUPPORT
8169 || tokenStream.peekToken() == TOK_DBLCOLON
8170 #endif
8171 ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8172 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8173 if (!stmt || stmt->type != STMT_WITH) {
8174 JSDefinition *dn;
8176 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8177 if (ale) {
8178 dn = ALE_DEFN(ale);
8179 #if JS_HAS_BLOCK_SCOPE
8181 * Skip out-of-scope let bindings along an ALE list or hash
8182 * chain. These can happen due to |let (x = x) x| block and
8183 * expression bindings, where the x on the right of = comes
8184 * from an outer scope. See bug 496532.
8186 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8187 do {
8188 ale = ALE_NEXT(ale);
8189 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8190 if (!ale)
8191 break;
8192 dn = ALE_DEFN(ale);
8194 #endif
8197 if (ale) {
8198 dn = ALE_DEFN(ale);
8199 } else {
8200 ale = tc->lexdeps.lookup(pn->pn_atom);
8201 if (ale) {
8202 dn = ALE_DEFN(ale);
8203 } else {
8205 * No definition before this use in any lexical scope.
8206 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8207 * new node for the forward-referenced definition. This
8208 * placeholder definition node will be adopted when we
8209 * parse the real defining declaration form, or left as
8210 * a free variable definition if we never see the real
8211 * definition.
8213 ale = MakePlaceholder(pn, tc);
8214 if (!ale)
8215 return NULL;
8216 dn = ALE_DEFN(ale);
8219 * In case this is a forward reference to a function,
8220 * we pessimistically set PND_FUNARG if the next token
8221 * is not a left parenthesis.
8223 * If the definition eventually parsed into dn is not a
8224 * function, this flag won't hurt, and if we do parse a
8225 * function with pn's name, then the PND_FUNARG flag is
8226 * necessary for safe context->display-based optimiza-
8227 * tion of the closure's static link.
8229 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8230 JS_ASSERT(dn->pn_op == JSOP_NOP);
8231 if (tokenStream.peekToken() != TOK_LP)
8232 dn->pn_dflags |= PND_FUNARG;
8236 JS_ASSERT(dn->pn_defn);
8237 LinkUseToDef(pn, dn, tc);
8239 /* Here we handle the backward function reference case. */
8240 if (tokenStream.peekToken() != TOK_LP)
8241 dn->pn_dflags |= PND_FUNARG;
8243 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8247 #if JS_HAS_XML_SUPPORT
8248 if (tokenStream.matchToken(TOK_DBLCOLON)) {
8249 if (afterDot) {
8250 JSString *str;
8253 * Here primaryExpr is called after . or .. followed by a name
8254 * followed by ::. This is the only case where a keyword after
8255 * . or .. is not treated as a property name.
8257 str = ATOM_TO_STRING(pn->pn_atom);
8258 tt = js_CheckKeyword(str->chars(), str->length());
8259 if (tt == TOK_FUNCTION) {
8260 pn->pn_arity = PN_NULLARY;
8261 pn->pn_type = TOK_FUNCTION;
8262 } else if (tt != TOK_EOF) {
8263 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS);
8264 return NULL;
8267 pn = qualifiedSuffix(pn);
8268 if (!pn)
8269 return NULL;
8271 #endif
8272 break;
8274 case TOK_REGEXP:
8276 JSObject *obj;
8278 pn = NullaryNode::create(tc);
8279 if (!pn)
8280 return NULL;
8282 obj = js_NewRegExpObject(context, &tokenStream,
8283 tokenStream.getTokenbuf().begin(),
8284 tokenStream.getTokenbuf().length(),
8285 tokenStream.currentToken().t_reflags);
8286 if (!obj)
8287 return NULL;
8288 if (!tc->compileAndGo()) {
8289 obj->clearParent();
8290 obj->clearProto();
8293 pn->pn_objbox = tc->parser->newObjectBox(obj);
8294 if (!pn->pn_objbox)
8295 return NULL;
8297 pn->pn_op = JSOP_REGEXP;
8298 break;
8301 case TOK_NUMBER:
8302 pn = NullaryNode::create(tc);
8303 if (!pn)
8304 return NULL;
8305 pn->pn_op = JSOP_DOUBLE;
8306 pn->pn_dval = tokenStream.currentToken().t_dval;
8307 break;
8309 case TOK_PRIMARY:
8310 pn = NullaryNode::create(tc);
8311 if (!pn)
8312 return NULL;
8313 pn->pn_op = tokenStream.currentToken().t_op;
8314 break;
8316 case TOK_ERROR:
8317 /* The scanner or one of its subroutines reported the error. */
8318 return NULL;
8320 default:
8321 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
8322 return NULL;
8324 return pn;
8327 JSParseNode *
8328 Parser::parenExpr(JSParseNode *pn1, JSBool *genexp)
8330 TokenPtr begin;
8331 JSParseNode *pn;
8333 JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
8334 begin = tokenStream.currentToken().pos.begin;
8336 if (genexp)
8337 *genexp = JS_FALSE;
8338 pn = bracketedExpr();
8339 if (!pn)
8340 return NULL;
8342 #if JS_HAS_GENERATOR_EXPRS
8343 if (tokenStream.matchToken(TOK_FOR)) {
8344 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
8345 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
8346 return NULL;
8348 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
8349 reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
8350 js_generator_str);
8351 return NULL;
8353 if (!pn1) {
8354 pn1 = UnaryNode::create(tc);
8355 if (!pn1)
8356 return NULL;
8358 pn = generatorExpr(pn1, pn);
8359 if (!pn)
8360 return NULL;
8361 pn->pn_pos.begin = begin;
8362 if (genexp) {
8363 if (tokenStream.getToken() != TOK_RP) {
8364 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
8365 js_generator_str);
8366 return NULL;
8368 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8369 *genexp = JS_TRUE;
8372 #endif /* JS_HAS_GENERATOR_EXPRS */
8374 return pn;
8378 * Fold from one constant type to another.
8379 * XXX handles only strings and numbers for now
8381 static JSBool
8382 FoldType(JSContext *cx, JSParseNode *pn, TokenKind type)
8384 if (PN_TYPE(pn) != type) {
8385 switch (type) {
8386 case TOK_NUMBER:
8387 if (pn->pn_type == TOK_STRING) {
8388 jsdouble d;
8389 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8390 return JS_FALSE;
8391 pn->pn_dval = d;
8392 pn->pn_type = TOK_NUMBER;
8393 pn->pn_op = JSOP_DOUBLE;
8395 break;
8397 case TOK_STRING:
8398 if (pn->pn_type == TOK_NUMBER) {
8399 JSString *str = js_NumberToString(cx, pn->pn_dval);
8400 if (!str)
8401 return JS_FALSE;
8402 pn->pn_atom = js_AtomizeString(cx, str, 0);
8403 if (!pn->pn_atom)
8404 return JS_FALSE;
8405 pn->pn_type = TOK_STRING;
8406 pn->pn_op = JSOP_STRING;
8408 break;
8410 default:;
8413 return JS_TRUE;
8417 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8418 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8419 * a successful call to this function.
8421 static JSBool
8422 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8423 JSParseNode *pn, JSTreeContext *tc)
8425 jsdouble d, d2;
8426 int32 i, j;
8428 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8429 d = pn1->pn_dval;
8430 d2 = pn2->pn_dval;
8431 switch (op) {
8432 case JSOP_LSH:
8433 case JSOP_RSH:
8434 i = js_DoubleToECMAInt32(d);
8435 j = js_DoubleToECMAInt32(d2);
8436 j &= 31;
8437 d = (op == JSOP_LSH) ? i << j : i >> j;
8438 break;
8440 case JSOP_URSH:
8441 j = js_DoubleToECMAInt32(d2);
8442 j &= 31;
8443 d = js_DoubleToECMAUint32(d) >> j;
8444 break;
8446 case JSOP_ADD:
8447 d += d2;
8448 break;
8450 case JSOP_SUB:
8451 d -= d2;
8452 break;
8454 case JSOP_MUL:
8455 d *= d2;
8456 break;
8458 case JSOP_DIV:
8459 if (d2 == 0) {
8460 #if defined(XP_WIN)
8461 /* XXX MSVC miscompiles such that (NaN == 0) */
8462 if (JSDOUBLE_IS_NaN(d2))
8463 d = js_NaN;
8464 else
8465 #endif
8466 if (d == 0 || JSDOUBLE_IS_NaN(d))
8467 d = js_NaN;
8468 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
8469 d = js_NegativeInfinity;
8470 else
8471 d = js_PositiveInfinity;
8472 } else {
8473 d /= d2;
8475 break;
8477 case JSOP_MOD:
8478 if (d2 == 0) {
8479 d = js_NaN;
8480 } else {
8481 d = js_fmod(d, d2);
8483 break;
8485 default:;
8488 /* Take care to allow pn1 or pn2 to alias pn. */
8489 if (pn1 != pn)
8490 RecycleTree(pn1, tc);
8491 if (pn2 != pn)
8492 RecycleTree(pn2, tc);
8493 pn->pn_type = TOK_NUMBER;
8494 pn->pn_op = JSOP_DOUBLE;
8495 pn->pn_arity = PN_NULLARY;
8496 pn->pn_dval = d;
8497 return JS_TRUE;
8500 #if JS_HAS_XML_SUPPORT
8502 static JSBool
8503 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8505 TokenKind tt;
8506 JSParseNode **pnp, *pn1, *pn2;
8507 JSString *accum, *str;
8508 uint32 i, j;
8510 JS_ASSERT(pn->pn_arity == PN_LIST);
8511 tt = PN_TYPE(pn);
8512 pnp = &pn->pn_head;
8513 pn1 = *pnp;
8514 accum = NULL;
8515 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8516 if (tt == TOK_XMLETAGO)
8517 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8518 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8519 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8523 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8524 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8525 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8526 * Therefore, we have to add additonal protection from GC nesting under
8527 * js_ConcatStrings.
8529 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8530 /* The parser already rejected end-tags with attributes. */
8531 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8532 switch (pn2->pn_type) {
8533 case TOK_XMLATTR:
8534 if (!accum)
8535 goto cantfold;
8536 /* FALL THROUGH */
8537 case TOK_XMLNAME:
8538 case TOK_XMLSPACE:
8539 case TOK_XMLTEXT:
8540 case TOK_STRING:
8541 if (pn2->pn_arity == PN_LIST)
8542 goto cantfold;
8543 str = ATOM_TO_STRING(pn2->pn_atom);
8544 break;
8546 case TOK_XMLCDATA:
8547 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8548 if (!str)
8549 return JS_FALSE;
8550 break;
8552 case TOK_XMLCOMMENT:
8553 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8554 if (!str)
8555 return JS_FALSE;
8556 break;
8558 case TOK_XMLPI:
8559 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8560 ATOM_TO_STRING(pn2->pn_atom2));
8561 if (!str)
8562 return JS_FALSE;
8563 break;
8565 cantfold:
8566 default:
8567 JS_ASSERT(*pnp == pn1);
8568 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8569 (i & 1) ^ (j & 1)) {
8570 #ifdef DEBUG_brendanXXX
8571 printf("1: %d, %d => ", i, j);
8572 if (accum)
8573 js_FileEscapedString(stdout, accum, 0);
8574 else
8575 fputs("NULL", stdout);
8576 fputc('\n', stdout);
8577 #endif
8578 } else if (accum && pn1 != pn2) {
8579 while (pn1->pn_next != pn2) {
8580 pn1 = RecycleTree(pn1, tc);
8581 --pn->pn_count;
8583 pn1->pn_type = TOK_XMLTEXT;
8584 pn1->pn_op = JSOP_STRING;
8585 pn1->pn_arity = PN_NULLARY;
8586 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8587 if (!pn1->pn_atom)
8588 return JS_FALSE;
8589 JS_ASSERT(pnp != &pn1->pn_next);
8590 *pnp = pn1;
8592 pnp = &pn2->pn_next;
8593 pn1 = *pnp;
8594 accum = NULL;
8595 continue;
8598 if (accum) {
8600 AutoValueRooter tvr(cx, accum);
8601 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8602 ? js_AddAttributePart(cx, i & 1, accum, str)
8603 : js_ConcatStrings(cx, accum, str);
8605 if (!str)
8606 return JS_FALSE;
8607 #ifdef DEBUG_brendanXXX
8608 printf("2: %d, %d => ", i, j);
8609 js_FileEscapedString(stdout, str, 0);
8610 printf(" (%u)\n", str->length());
8611 #endif
8612 ++j;
8614 accum = str;
8617 if (accum) {
8618 str = NULL;
8619 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8620 if (tt == TOK_XMLPTAGC)
8621 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8622 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8623 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8625 if (str) {
8626 accum = js_ConcatStrings(cx, accum, str);
8627 if (!accum)
8628 return JS_FALSE;
8631 JS_ASSERT(*pnp == pn1);
8632 while (pn1->pn_next) {
8633 pn1 = RecycleTree(pn1, tc);
8634 --pn->pn_count;
8636 pn1->pn_type = TOK_XMLTEXT;
8637 pn1->pn_op = JSOP_STRING;
8638 pn1->pn_arity = PN_NULLARY;
8639 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8640 if (!pn1->pn_atom)
8641 return JS_FALSE;
8642 JS_ASSERT(pnp != &pn1->pn_next);
8643 *pnp = pn1;
8646 if (pn1 && pn->pn_count == 1) {
8648 * Only one node under pn, and it has been folded: move pn1 onto pn
8649 * unless pn is an XML root (in which case we need it to tell the code
8650 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8651 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8652 * extra "<" and "/>" bracketing at runtime.
8654 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8655 pn->become(pn1);
8656 } else if (tt == TOK_XMLPTAGC) {
8657 pn->pn_type = TOK_XMLELEM;
8658 pn->pn_op = JSOP_TOXML;
8661 return JS_TRUE;
8664 #endif /* JS_HAS_XML_SUPPORT */
8666 static int
8667 Boolish(JSParseNode *pn)
8669 switch (pn->pn_op) {
8670 case JSOP_DOUBLE:
8671 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8673 case JSOP_STRING:
8674 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8676 #if JS_HAS_GENERATOR_EXPRS
8677 case JSOP_CALL:
8680 * A generator expression as an if or loop condition has no effects, it
8681 * simply results in a truthy object reference. This condition folding
8682 * is needed for the decompiler. See bug 442342 and bug 443074.
8684 if (pn->pn_count != 1)
8685 break;
8686 JSParseNode *pn2 = pn->pn_head;
8687 if (pn2->pn_type != TOK_FUNCTION)
8688 break;
8689 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8690 break;
8691 /* FALL THROUGH */
8693 #endif
8695 case JSOP_DEFFUN:
8696 case JSOP_LAMBDA:
8697 case JSOP_THIS:
8698 case JSOP_TRUE:
8699 return 1;
8701 case JSOP_NULL:
8702 case JSOP_FALSE:
8703 return 0;
8705 default:;
8707 return -1;
8710 JSBool
8711 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8713 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8715 JS_CHECK_RECURSION(cx, return JS_FALSE);
8717 switch (pn->pn_arity) {
8718 case PN_FUNC:
8720 uint32 oldflags = tc->flags;
8721 JSFunctionBox *oldlist = tc->functionList;
8723 tc->flags = pn->pn_funbox->tcflags;
8724 tc->functionList = pn->pn_funbox->kids;
8725 if (!js_FoldConstants(cx, pn->pn_body, tc))
8726 return JS_FALSE;
8727 pn->pn_funbox->kids = tc->functionList;
8728 tc->flags = oldflags;
8729 tc->functionList = oldlist;
8730 break;
8733 case PN_LIST:
8735 /* Propagate inCond through logical connectives. */
8736 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8738 /* Don't fold a parenthesized call expression. See bug 537673. */
8739 pn1 = pn2 = pn->pn_head;
8740 if ((pn->pn_type == TOK_LP || pn->pn_type == TOK_NEW) && pn2->pn_parens)
8741 pn2 = pn2->pn_next;
8743 /* Save the list head in pn1 for later use. */
8744 for (; pn2; pn2 = pn2->pn_next) {
8745 if (!js_FoldConstants(cx, pn2, tc, cond))
8746 return JS_FALSE;
8748 break;
8751 case PN_TERNARY:
8752 /* Any kid may be null (e.g. for (;;)). */
8753 pn1 = pn->pn_kid1;
8754 pn2 = pn->pn_kid2;
8755 pn3 = pn->pn_kid3;
8756 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8757 return JS_FALSE;
8758 if (pn2) {
8759 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8760 return JS_FALSE;
8761 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8762 RecycleTree(pn2, tc);
8763 pn->pn_kid2 = NULL;
8766 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8767 return JS_FALSE;
8768 break;
8770 case PN_BINARY:
8771 pn1 = pn->pn_left;
8772 pn2 = pn->pn_right;
8774 /* Propagate inCond through logical connectives. */
8775 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8776 if (!js_FoldConstants(cx, pn1, tc, inCond))
8777 return JS_FALSE;
8778 if (!js_FoldConstants(cx, pn2, tc, inCond))
8779 return JS_FALSE;
8780 break;
8783 /* First kid may be null (for default case in switch). */
8784 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8785 return JS_FALSE;
8786 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8787 return JS_FALSE;
8788 break;
8790 case PN_UNARY:
8791 pn1 = pn->pn_kid;
8794 * Kludge to deal with typeof expressions: because constant folding
8795 * can turn an expression into a name node, we have to check here,
8796 * before folding, to see if we should throw undefined name errors.
8798 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
8799 * null. This assumption does not hold true for other unary
8800 * expressions.
8802 if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
8803 pn->pn_op = JSOP_TYPEOFEXPR;
8805 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
8806 return JS_FALSE;
8807 break;
8809 case PN_NAME:
8811 * Skip pn1 down along a chain of dotted member expressions to avoid
8812 * excessive recursion. Our only goal here is to fold constants (if
8813 * any) in the primary expression operand to the left of the first
8814 * dot in the chain.
8816 if (!pn->pn_used) {
8817 pn1 = pn->pn_expr;
8818 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8819 pn1 = pn1->pn_expr;
8820 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8821 return JS_FALSE;
8823 break;
8825 case PN_NAMESET:
8826 pn1 = pn->pn_tree;
8827 if (!js_FoldConstants(cx, pn1, tc))
8828 return JS_FALSE;
8829 break;
8831 case PN_NULLARY:
8832 break;
8835 switch (pn->pn_type) {
8836 case TOK_IF:
8837 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8838 break;
8839 /* FALL THROUGH */
8841 case TOK_HOOK:
8842 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8843 switch (pn1->pn_type) {
8844 case TOK_NUMBER:
8845 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8846 pn2 = pn3;
8847 break;
8848 case TOK_STRING:
8849 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
8850 pn2 = pn3;
8851 break;
8852 case TOK_PRIMARY:
8853 if (pn1->pn_op == JSOP_TRUE)
8854 break;
8855 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8856 pn2 = pn3;
8857 break;
8859 /* FALL THROUGH */
8860 default:
8861 /* Early return to dodge common code that copies pn2 to pn. */
8862 return JS_TRUE;
8865 #if JS_HAS_GENERATOR_EXPRS
8866 /* Don't fold a trailing |if (0)| in a generator expression. */
8867 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8868 break;
8869 #endif
8871 if (pn2 && !pn2->pn_defn)
8872 pn->become(pn2);
8873 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8875 * False condition and no else, or an empty then-statement was
8876 * moved up over pn. Either way, make pn an empty block (not an
8877 * empty statement, which does not decompile, even when labeled).
8878 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8879 * or an empty statement for a child.
8881 pn->pn_type = TOK_LC;
8882 pn->pn_arity = PN_LIST;
8883 pn->makeEmpty();
8885 RecycleTree(pn2, tc);
8886 if (pn3 && pn3 != pn2)
8887 RecycleTree(pn3, tc);
8888 break;
8890 case TOK_OR:
8891 case TOK_AND:
8892 if (inCond) {
8893 if (pn->pn_arity == PN_LIST) {
8894 JSParseNode **pnp = &pn->pn_head;
8895 JS_ASSERT(*pnp == pn1);
8896 do {
8897 int cond = Boolish(pn1);
8898 if (cond == (pn->pn_type == TOK_OR)) {
8899 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
8900 pn3 = pn2->pn_next;
8901 RecycleTree(pn2, tc);
8902 --pn->pn_count;
8904 pn1->pn_next = NULL;
8905 break;
8907 if (cond != -1) {
8908 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8909 if (pn->pn_count == 1)
8910 break;
8911 *pnp = pn1->pn_next;
8912 RecycleTree(pn1, tc);
8913 --pn->pn_count;
8914 } else {
8915 pnp = &pn1->pn_next;
8917 } while ((pn1 = *pnp) != NULL);
8919 // We may have to change arity from LIST to BINARY.
8920 pn1 = pn->pn_head;
8921 if (pn->pn_count == 2) {
8922 pn2 = pn1->pn_next;
8923 pn1->pn_next = NULL;
8924 JS_ASSERT(!pn2->pn_next);
8925 pn->pn_arity = PN_BINARY;
8926 pn->pn_left = pn1;
8927 pn->pn_right = pn2;
8928 } else if (pn->pn_count == 1) {
8929 pn->become(pn1);
8930 RecycleTree(pn1, tc);
8932 } else {
8933 int cond = Boolish(pn1);
8934 if (cond == (pn->pn_type == TOK_OR)) {
8935 RecycleTree(pn2, tc);
8936 pn->become(pn1);
8937 } else if (cond != -1) {
8938 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
8939 RecycleTree(pn1, tc);
8940 pn->become(pn2);
8944 break;
8946 case TOK_ASSIGN:
8948 * Compound operators such as *= should be subject to folding, in case
8949 * the left-hand side is constant, and so that the decompiler produces
8950 * the same string that you get from decompiling a script or function
8951 * compiled from that same string. As with +, += is special.
8953 if (pn->pn_op == JSOP_NOP)
8954 break;
8955 if (pn->pn_op != JSOP_ADD)
8956 goto do_binary_op;
8957 /* FALL THROUGH */
8959 case TOK_PLUS:
8960 if (pn->pn_arity == PN_LIST) {
8961 size_t length, length2;
8962 jschar *chars;
8963 JSString *str, *str2;
8966 * Any string literal term with all others number or string means
8967 * this is a concatenation. If any term is not a string or number
8968 * literal, we can't fold.
8970 JS_ASSERT(pn->pn_count > 2);
8971 if (pn->pn_xflags & PNX_CANTFOLD)
8972 return JS_TRUE;
8973 if (pn->pn_xflags != PNX_STRCAT)
8974 goto do_binary_op;
8976 /* Ok, we're concatenating: convert non-string constant operands. */
8977 length = 0;
8978 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
8979 if (!FoldType(cx, pn2, TOK_STRING))
8980 return JS_FALSE;
8981 /* XXX fold only if all operands convert to string */
8982 if (pn2->pn_type != TOK_STRING)
8983 return JS_TRUE;
8984 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
8987 /* Allocate a new buffer and string descriptor for the result. */
8988 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
8989 if (!chars)
8990 return JS_FALSE;
8991 str = js_NewString(cx, chars, length);
8992 if (!str) {
8993 cx->free(chars);
8994 return JS_FALSE;
8997 /* Fill the buffer, advancing chars and recycling kids as we go. */
8998 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
8999 str2 = ATOM_TO_STRING(pn2->pn_atom);
9000 length2 = str2->flatLength();
9001 js_strncpy(chars, str2->flatChars(), length2);
9002 chars += length2;
9004 *chars = 0;
9006 /* Atomize the result string and mutate pn to refer to it. */
9007 pn->pn_atom = js_AtomizeString(cx, str, 0);
9008 if (!pn->pn_atom)
9009 return JS_FALSE;
9010 pn->pn_type = TOK_STRING;
9011 pn->pn_op = JSOP_STRING;
9012 pn->pn_arity = PN_NULLARY;
9013 break;
9016 /* Handle a binary string concatenation. */
9017 JS_ASSERT(pn->pn_arity == PN_BINARY);
9018 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9019 JSString *left, *right, *str;
9021 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9022 TOK_STRING)) {
9023 return JS_FALSE;
9025 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9026 return JS_TRUE;
9027 left = ATOM_TO_STRING(pn1->pn_atom);
9028 right = ATOM_TO_STRING(pn2->pn_atom);
9029 str = js_ConcatStrings(cx, left, right);
9030 if (!str)
9031 return JS_FALSE;
9032 pn->pn_atom = js_AtomizeString(cx, str, 0);
9033 if (!pn->pn_atom)
9034 return JS_FALSE;
9035 pn->pn_type = TOK_STRING;
9036 pn->pn_op = JSOP_STRING;
9037 pn->pn_arity = PN_NULLARY;
9038 RecycleTree(pn1, tc);
9039 RecycleTree(pn2, tc);
9040 break;
9043 /* Can't concatenate string literals, let's try numbers. */
9044 goto do_binary_op;
9046 case TOK_STAR:
9047 case TOK_SHOP:
9048 case TOK_MINUS:
9049 case TOK_DIVOP:
9050 do_binary_op:
9051 if (pn->pn_arity == PN_LIST) {
9052 JS_ASSERT(pn->pn_count > 2);
9053 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9054 if (!FoldType(cx, pn2, TOK_NUMBER))
9055 return JS_FALSE;
9057 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9058 /* XXX fold only if all operands convert to number */
9059 if (pn2->pn_type != TOK_NUMBER)
9060 break;
9062 if (!pn2) {
9063 JSOp op = PN_OP(pn);
9065 pn2 = pn1->pn_next;
9066 pn3 = pn2->pn_next;
9067 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9068 return JS_FALSE;
9069 while ((pn2 = pn3) != NULL) {
9070 pn3 = pn2->pn_next;
9071 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9072 return JS_FALSE;
9075 } else {
9076 JS_ASSERT(pn->pn_arity == PN_BINARY);
9077 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9078 !FoldType(cx, pn2, TOK_NUMBER)) {
9079 return JS_FALSE;
9081 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9082 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9083 return JS_FALSE;
9086 break;
9088 case TOK_UNARYOP:
9089 if (pn1->pn_type == TOK_NUMBER) {
9090 jsdouble d;
9092 /* Operate on one numeric constant. */
9093 d = pn1->pn_dval;
9094 switch (pn->pn_op) {
9095 case JSOP_BITNOT:
9096 d = ~js_DoubleToECMAInt32(d);
9097 break;
9099 case JSOP_NEG:
9100 d = -d;
9101 break;
9103 case JSOP_POS:
9104 break;
9106 case JSOP_NOT:
9107 pn->pn_type = TOK_PRIMARY;
9108 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9109 pn->pn_arity = PN_NULLARY;
9110 /* FALL THROUGH */
9112 default:
9113 /* Return early to dodge the common TOK_NUMBER code. */
9114 return JS_TRUE;
9116 pn->pn_type = TOK_NUMBER;
9117 pn->pn_op = JSOP_DOUBLE;
9118 pn->pn_arity = PN_NULLARY;
9119 pn->pn_dval = d;
9120 RecycleTree(pn1, tc);
9121 } else if (pn1->pn_type == TOK_PRIMARY) {
9122 if (pn->pn_op == JSOP_NOT &&
9123 (pn1->pn_op == JSOP_TRUE ||
9124 pn1->pn_op == JSOP_FALSE)) {
9125 pn->become(pn1);
9126 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9127 RecycleTree(pn1, tc);
9130 break;
9132 #if JS_HAS_XML_SUPPORT
9133 case TOK_XMLELEM:
9134 case TOK_XMLLIST:
9135 case TOK_XMLPTAGC:
9136 case TOK_XMLSTAGO:
9137 case TOK_XMLETAGO:
9138 case TOK_XMLNAME:
9139 if (pn->pn_arity == PN_LIST) {
9140 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9141 if (!FoldXMLConstants(cx, pn, tc))
9142 return JS_FALSE;
9144 break;
9146 case TOK_AT:
9147 if (pn1->pn_type == TOK_XMLNAME) {
9148 jsval v;
9149 JSObjectBox *xmlbox;
9151 v = ATOM_KEY(pn1->pn_atom);
9152 if (!js_ToAttributeName(cx, &v))
9153 return JS_FALSE;
9154 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9156 xmlbox = tc->parser->newObjectBox(JSVAL_TO_OBJECT(v));
9157 if (!xmlbox)
9158 return JS_FALSE;
9160 pn->pn_type = TOK_XMLNAME;
9161 pn->pn_op = JSOP_OBJECT;
9162 pn->pn_arity = PN_NULLARY;
9163 pn->pn_objbox = xmlbox;
9164 RecycleTree(pn1, tc);
9166 break;
9167 #endif /* JS_HAS_XML_SUPPORT */
9169 default:;
9172 if (inCond) {
9173 int cond = Boolish(pn);
9174 if (cond >= 0) {
9175 switch (pn->pn_arity) {
9176 case PN_LIST:
9177 pn2 = pn->pn_head;
9178 do {
9179 pn3 = pn2->pn_next;
9180 RecycleTree(pn2, tc);
9181 } while ((pn2 = pn3) != NULL);
9182 break;
9183 case PN_FUNC:
9184 RecycleFuncNameKids(pn, tc);
9185 break;
9186 case PN_NULLARY:
9187 break;
9188 default:
9189 JS_NOT_REACHED("unhandled arity");
9191 pn->pn_type = TOK_PRIMARY;
9192 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9193 pn->pn_arity = PN_NULLARY;
9197 return JS_TRUE;