Bug 551763: Fix deletion of arguments ident. (r=Waldo)
[mozilla-central.git] / js / src / jsparse.cpp
blobefde7c1791a2032bdf61747a2499342148953364
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 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR, errno); \
111 return NULL; \
113 JS_END_MACRO
114 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
116 #ifdef METER_PARSENODES
117 static uint32 parsenodes = 0;
118 static uint32 maxparsenodes = 0;
119 static uint32 recyclednodes = 0;
120 #endif
122 void
123 JSParseNode::become(JSParseNode *pn2)
125 JS_ASSERT(!pn_defn);
126 JS_ASSERT(!pn2->pn_defn);
128 JS_ASSERT(!pn_used);
129 if (pn2->pn_used) {
130 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
131 while (*pnup != pn2)
132 pnup = &(*pnup)->pn_link;
133 *pnup = this;
134 pn_link = pn2->pn_link;
135 pn_used = true;
136 pn2->pn_link = NULL;
137 pn2->pn_used = false;
140 /* If this is a function node fix up the pn_funbox->node back-pointer. */
141 if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
142 pn2->pn_funbox->node = this;
144 pn_type = pn2->pn_type;
145 pn_op = pn2->pn_op;
146 pn_arity = pn2->pn_arity;
147 pn_parens = pn2->pn_parens;
148 pn_u = pn2->pn_u;
149 pn2->clear();
152 void
153 JSParseNode::clear()
155 pn_type = TOK_EOF;
156 pn_op = JSOP_NOP;
157 pn_used = pn_defn = false;
158 pn_arity = PN_NULLARY;
159 pn_parens = false;
162 bool
163 Parser::init(const jschar *base, size_t length,
164 FILE *fp, const char *filename, uintN lineno)
166 JSContext *cx = context;
168 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
169 if (!tokenStream.init(base, length, fp, filename, lineno)) {
170 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
171 return false;
173 return true;
176 Parser::~Parser()
178 JSContext *cx = context;
180 if (principals)
181 JSPRINCIPALS_DROP(cx, principals);
182 tokenStream.close();
183 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
186 void
187 Parser::setPrincipals(JSPrincipals *prin)
189 JS_ASSERT(!principals);
190 if (prin)
191 JSPRINCIPALS_HOLD(context, prin);
192 principals = prin;
195 JSObjectBox *
196 Parser::newObjectBox(JSObject *obj)
198 JS_ASSERT(obj);
201 * We use JSContext.tempPool to allocate parsed objects and place them on
202 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
203 * containing the entries must be alive until we are done with scanning,
204 * parsing and code generation for the whole script or top-level function.
206 JSObjectBox *objbox;
207 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
208 if (!objbox) {
209 js_ReportOutOfScriptQuota(context);
210 return NULL;
212 objbox->traceLink = traceListHead;
213 traceListHead = objbox;
214 objbox->emitLink = NULL;
215 objbox->object = obj;
216 return objbox;
219 JSFunctionBox *
220 Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
222 JS_ASSERT(obj);
223 JS_ASSERT(obj->isFunction());
226 * We use JSContext.tempPool to allocate parsed objects and place them on
227 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
228 * containing the entries must be alive until we are done with scanning,
229 * parsing and code generation for the whole script or top-level function.
231 JSFunctionBox *funbox;
232 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
233 if (!funbox) {
234 js_ReportOutOfScriptQuota(context);
235 return NULL;
237 funbox->traceLink = traceListHead;
238 traceListHead = funbox;
239 funbox->emitLink = NULL;
240 funbox->object = obj;
241 funbox->node = fn;
242 funbox->siblings = tc->functionList;
243 tc->functionList = funbox;
244 ++tc->parser->functionCount;
245 funbox->kids = NULL;
246 funbox->parent = tc->funbox;
247 funbox->methods = NULL;
248 funbox->queued = false;
249 funbox->inLoop = false;
250 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
251 if (STMT_IS_LOOP(stmt)) {
252 funbox->inLoop = true;
253 break;
256 funbox->level = tc->staticLevel;
257 funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
258 return funbox;
261 bool
262 JSFunctionBox::joinable() const
264 return FUN_NULL_CLOSURE((JSFunction *) object) &&
265 !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
268 bool
269 JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
271 if (slowMethods != 0) {
272 for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
273 if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
274 return true;
275 if (funbox->inLoop)
276 return true;
279 return false;
282 void
283 Parser::trace(JSTracer *trc)
285 JSObjectBox *objbox = traceListHead;
286 while (objbox) {
287 JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
288 objbox = objbox->traceLink;
292 static void
293 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
295 static void
296 UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
298 JSFunctionBox *funbox = pn->pn_funbox;
299 if (funbox) {
300 JS_ASSERT(funbox->node == pn);
301 funbox->node = NULL;
303 if (funbox->parent && PN_OP(pn) == JSOP_LAMBDA) {
305 * Remove pn from funbox->parent's methods list if it's there. See
306 * the TOK_SEMI case in Statement, near the bottom, the TOK_ASSIGN
307 * sub-case matching a constructor method assignment pattern.
309 JS_ASSERT(!pn->pn_defn);
310 JS_ASSERT(!pn->pn_used);
311 JSParseNode **pnp = &funbox->parent->methods;
312 while (JSParseNode *method = *pnp) {
313 if (method == pn) {
314 *pnp = method->pn_link;
315 break;
317 pnp = &method->pn_link;
321 JSFunctionBox **funboxp = &tc->functionList;
322 while (*funboxp) {
323 if (*funboxp == funbox) {
324 *funboxp = funbox->siblings;
325 break;
327 funboxp = &(*funboxp)->siblings;
330 uint32 oldflags = tc->flags;
331 JSFunctionBox *oldlist = tc->functionList;
333 tc->flags = funbox->tcflags;
334 tc->functionList = funbox->kids;
335 UnlinkFunctionBoxes(pn->pn_body, tc);
336 funbox->kids = tc->functionList;
337 tc->flags = oldflags;
338 tc->functionList = oldlist;
340 // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
341 pn->pn_funbox = NULL;
345 static void
346 UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
348 if (pn) {
349 switch (pn->pn_arity) {
350 case PN_NULLARY:
351 return;
352 case PN_UNARY:
353 UnlinkFunctionBoxes(pn->pn_kid, tc);
354 return;
355 case PN_BINARY:
356 UnlinkFunctionBoxes(pn->pn_left, tc);
357 UnlinkFunctionBoxes(pn->pn_right, tc);
358 return;
359 case PN_TERNARY:
360 UnlinkFunctionBoxes(pn->pn_kid1, tc);
361 UnlinkFunctionBoxes(pn->pn_kid2, tc);
362 UnlinkFunctionBoxes(pn->pn_kid3, tc);
363 return;
364 case PN_LIST:
365 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
366 UnlinkFunctionBoxes(pn2, tc);
367 return;
368 case PN_FUNC:
369 UnlinkFunctionBox(pn, tc);
370 return;
371 case PN_NAME:
372 UnlinkFunctionBoxes(pn->maybeExpr(), tc);
373 return;
374 case PN_NAMESET:
375 UnlinkFunctionBoxes(pn->pn_tree, tc);
380 static void
381 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
383 static JSParseNode *
384 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
386 JSParseNode *next, **head;
388 if (!pn)
389 return NULL;
391 /* Catch back-to-back dup recycles. */
392 JS_ASSERT(pn != tc->parser->nodeList);
393 next = pn->pn_next;
394 if (pn->pn_used || pn->pn_defn) {
396 * JSAtomLists own definition nodes along with their used-node chains.
397 * Defer recycling such nodes until we unwind to top level to avoid
398 * linkage overhead or (alternatively) unlinking runtime complexity.
399 * Yes, this means dead code can contribute to static analysis results!
401 * Do recycle kids here, since they are no longer needed.
403 pn->pn_next = NULL;
404 RecycleFuncNameKids(pn, tc);
405 } else {
406 UnlinkFunctionBoxes(pn, tc);
407 head = &tc->parser->nodeList;
408 pn->pn_next = *head;
409 *head = pn;
410 #ifdef METER_PARSENODES
411 recyclednodes++;
412 #endif
414 return next;
417 static void
418 RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
420 switch (pn->pn_arity) {
421 case PN_FUNC:
422 UnlinkFunctionBox(pn, tc);
423 /* FALL THROUGH */
425 case PN_NAME:
427 * Only a definition node might have a non-null strong pn_expr link
428 * to recycle, but we test !pn_used to handle PN_FUNC fall through.
429 * Every node with the pn_used flag set has a non-null pn_lexdef
430 * weak reference to its definition node.
432 if (!pn->pn_used && pn->pn_expr) {
433 RecycleTree(pn->pn_expr, tc);
434 pn->pn_expr = NULL;
436 break;
438 default:
439 JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
444 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
445 * temporary arena.
447 static JSParseNode *
448 NewOrRecycledNode(JSTreeContext *tc)
450 JSParseNode *pn, *pn2;
452 pn = tc->parser->nodeList;
453 if (!pn) {
454 JSContext *cx = tc->parser->context;
456 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
457 if (!pn)
458 js_ReportOutOfScriptQuota(cx);
459 } else {
460 tc->parser->nodeList = pn->pn_next;
462 /* Recycle immediate descendents only, to save work and working set. */
463 switch (pn->pn_arity) {
464 case PN_FUNC:
465 RecycleTree(pn->pn_body, tc);
466 break;
467 case PN_LIST:
468 pn2 = pn->pn_head;
469 if (pn2) {
470 while (pn2 && !pn2->pn_used && !pn2->pn_defn)
471 pn2 = pn2->pn_next;
472 if (pn2) {
473 pn2 = pn->pn_head;
474 do {
475 pn2 = RecycleTree(pn2, tc);
476 } while (pn2);
477 } else {
478 *pn->pn_tail = tc->parser->nodeList;
479 tc->parser->nodeList = pn->pn_head;
480 #ifdef METER_PARSENODES
481 recyclednodes += pn->pn_count;
482 #endif
483 break;
486 break;
487 case PN_TERNARY:
488 RecycleTree(pn->pn_kid1, tc);
489 RecycleTree(pn->pn_kid2, tc);
490 RecycleTree(pn->pn_kid3, tc);
491 break;
492 case PN_BINARY:
493 if (pn->pn_left != pn->pn_right)
494 RecycleTree(pn->pn_left, tc);
495 RecycleTree(pn->pn_right, tc);
496 break;
497 case PN_UNARY:
498 RecycleTree(pn->pn_kid, tc);
499 break;
500 case PN_NAME:
501 if (!pn->pn_used)
502 RecycleTree(pn->pn_expr, tc);
503 break;
504 case PN_NULLARY:
505 break;
508 if (pn) {
509 #ifdef METER_PARSENODES
510 parsenodes++;
511 if (parsenodes - recyclednodes > maxparsenodes)
512 maxparsenodes = parsenodes - recyclednodes;
513 #endif
514 pn->pn_used = pn->pn_defn = false;
515 memset(&pn->pn_u, 0, sizeof pn->pn_u);
516 pn->pn_next = NULL;
518 return pn;
521 /* used only by static create methods of subclasses */
523 JSParseNode *
524 JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc)
526 JSParseNode *pn = NewOrRecycledNode(tc);
527 if (!pn)
528 return NULL;
529 const Token &tok = tc->parser->tokenStream.currentToken();
530 pn->init(tok.type, JSOP_NOP, arity);
531 pn->pn_pos = tok.pos;
532 return pn;
535 JSParseNode *
536 JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParseNode *right,
537 JSTreeContext *tc)
539 JSParseNode *pn, *pn1, *pn2;
541 if (!left || !right)
542 return NULL;
545 * Flatten a left-associative (left-heavy) tree of a given operator into
546 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
548 if (PN_TYPE(left) == tt &&
549 PN_OP(left) == op &&
550 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
551 if (left->pn_arity != PN_LIST) {
552 pn1 = left->pn_left, pn2 = left->pn_right;
553 left->pn_arity = PN_LIST;
554 left->pn_parens = false;
555 left->initList(pn1);
556 left->append(pn2);
557 if (tt == TOK_PLUS) {
558 if (pn1->pn_type == TOK_STRING)
559 left->pn_xflags |= PNX_STRCAT;
560 else if (pn1->pn_type != TOK_NUMBER)
561 left->pn_xflags |= PNX_CANTFOLD;
562 if (pn2->pn_type == TOK_STRING)
563 left->pn_xflags |= PNX_STRCAT;
564 else if (pn2->pn_type != TOK_NUMBER)
565 left->pn_xflags |= PNX_CANTFOLD;
568 left->append(right);
569 left->pn_pos.end = right->pn_pos.end;
570 if (tt == TOK_PLUS) {
571 if (right->pn_type == TOK_STRING)
572 left->pn_xflags |= PNX_STRCAT;
573 else if (right->pn_type != TOK_NUMBER)
574 left->pn_xflags |= PNX_CANTFOLD;
576 return left;
580 * Fold constant addition immediately, to conserve node space and, what's
581 * more, so js_FoldConstants never sees mixed addition and concatenation
582 * operations with more than one leading non-string operand in a PN_LIST
583 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
584 * to "3pt", not "12pt").
586 if (tt == TOK_PLUS &&
587 left->pn_type == TOK_NUMBER &&
588 right->pn_type == TOK_NUMBER) {
589 left->pn_dval += right->pn_dval;
590 left->pn_pos.end = right->pn_pos.end;
591 RecycleTree(right, tc);
592 return left;
595 pn = NewOrRecycledNode(tc);
596 if (!pn)
597 return NULL;
598 pn->init(tt, op, PN_BINARY);
599 pn->pn_pos.begin = left->pn_pos.begin;
600 pn->pn_pos.end = right->pn_pos.end;
601 pn->pn_left = left;
602 pn->pn_right = right;
603 return (BinaryNode *)pn;
606 namespace js {
608 inline void
609 NameNode::initCommon(JSTreeContext *tc)
611 pn_expr = NULL;
612 pn_cookie = FREE_UPVAR_COOKIE;
613 pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
614 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
615 pn_dflags |= PND_BLOCKCHILD;
616 pn_blockid = tc->blockid();
619 NameNode *
620 NameNode::create(JSAtom *atom, JSTreeContext *tc)
622 JSParseNode *pn;
624 pn = JSParseNode::create(PN_NAME, tc);
625 if (pn) {
626 pn->pn_atom = atom;
627 ((NameNode *)pn)->initCommon(tc);
629 return (NameNode *)pn;
632 } /* namespace js */
634 static bool
635 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
637 if (tc->blockidGen == JS_BIT(20)) {
638 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
639 JSMSG_NEED_DIET, "program");
640 return false;
642 blockid = tc->blockidGen++;
643 return true;
646 static bool
647 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
649 JS_ASSERT(tc->topStmt);
650 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
651 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
652 if (!GenerateBlockId(tc, tc->topStmt->blockid))
653 return false;
654 pn->pn_blockid = tc->topStmt->blockid;
655 return true;
659 * Parse a top-level JS script.
661 JSParseNode *
662 Parser::parse(JSObject *chain)
665 * Protect atoms from being collected by a GC activation, which might
666 * - nest on this thread due to out of memory (the so-called "last ditch"
667 * GC attempted within js_NewGCThing), or
668 * - run for any reason on another thread if this thread is suspended on
669 * an object lock before it finishes generating bytecode into a script
670 * protected from the GC by a root or a stack frame reference.
672 JSTreeContext globaltc(this);
673 globaltc.scopeChain = chain;
674 if (!GenerateBlockId(&globaltc, globaltc.bodyid))
675 return NULL;
677 JSParseNode *pn = statements();
678 if (pn) {
679 if (!tokenStream.matchToken(TOK_EOF)) {
680 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
681 JSMSG_SYNTAX_ERROR);
682 pn = NULL;
683 } else {
684 if (!js_FoldConstants(context, pn, &globaltc))
685 pn = NULL;
688 return pn;
691 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
693 static inline bool
694 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
697 * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
698 * (0xffffffff) and other cookies with that level.
700 * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
701 * practically speaking it leaves more than enough room for upvars. In fact
702 * we might want to split cookie fields giving fewer bits for skip and more
703 * for slot, but only based on evidence.
705 if (staticLevel >= FREE_STATIC_LEVEL) {
706 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
707 JSMSG_TOO_DEEP, js_function_str);
708 return false;
710 tc->staticLevel = staticLevel;
711 return true;
715 * Compile a top-level script.
717 JSScript *
718 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
719 JSPrincipals *principals, uint32 tcflags,
720 const jschar *chars, size_t length,
721 FILE *file, const char *filename, uintN lineno,
722 JSString *source /* = NULL */,
723 unsigned staticLevel /* = 0 */)
725 JSArenaPool codePool, notePool;
726 TokenKind tt;
727 JSParseNode *pn;
728 uint32 scriptGlobals;
729 JSScript *script;
730 bool inDirectivePrologue;
731 #ifdef METER_PARSENODES
732 void *sbrk(ptrdiff_t), *before = sbrk(0);
733 #endif
735 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT)));
738 * The scripted callerFrame can only be given for compile-and-go scripts
739 * and non-zero static level requires callerFrame.
741 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
742 JS_ASSERT_IF(staticLevel != 0, callerFrame);
744 Compiler compiler(cx, principals, callerFrame);
745 if (!compiler.init(chars, length, file, filename, lineno))
746 return NULL;
748 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
749 &cx->scriptStackQuota);
750 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
751 &cx->scriptStackQuota);
753 Parser &parser = compiler.parser;
754 TokenStream &tokenStream = parser.tokenStream;
756 JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
757 if (!cg.init())
758 return NULL;
760 MUST_FLOW_THROUGH("out");
762 /* Null script early in case of error, to reduce our code footprint. */
763 script = NULL;
765 cg.flags |= tcflags;
766 cg.scopeChain = scopeChain;
767 if (!SetStaticLevel(&cg, staticLevel))
768 goto out;
770 /* If this is a direct call to eval, inherit the caller's strictness. */
771 if (callerFrame &&
772 callerFrame->script &&
773 callerFrame->script->strictModeCode) {
774 cg.flags |= TCF_STRICT_MODE_CODE;
775 tokenStream.setStrictMode();
779 * If funbox is non-null after we create the new script, callerFrame->fun
780 * was saved in the 0th object table entry.
782 JSObjectBox *funbox;
783 funbox = NULL;
785 if (tcflags & TCF_COMPILE_N_GO) {
786 if (source) {
788 * Save eval program source in script->atomMap.vector[0] for the
789 * eval cache (see obj_eval in jsobj.cpp).
791 JSAtom *atom = js_AtomizeString(cx, source, 0);
792 if (!atom || !cg.atomList.add(&parser, atom))
793 goto out;
796 if (callerFrame && callerFrame->fun) {
798 * An eval script in a caller frame needs to have its enclosing
799 * function captured in case it refers to an upvar, and someone
800 * wishes to decompile it while it's running.
802 funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun));
803 if (!funbox)
804 goto out;
805 funbox->emitLink = cg.objectList.lastbox;
806 cg.objectList.lastbox = funbox;
807 cg.objectList.length++;
812 * Inline this->statements to emit as we go to save AST space. We must
813 * generate our script-body blockid since we aren't calling Statements.
815 uint32 bodyid;
816 if (!GenerateBlockId(&cg, bodyid))
817 goto out;
818 cg.bodyid = bodyid;
820 #if JS_HAS_XML_SUPPORT
821 pn = NULL;
822 bool onlyXML;
823 onlyXML = true;
824 #endif
826 CG_SWITCH_TO_PROLOG(&cg);
827 if (js_Emit1(cx, &cg, JSOP_TRACE) < 0)
828 goto out;
829 CG_SWITCH_TO_MAIN(&cg);
831 inDirectivePrologue = true;
832 for (;;) {
833 tt = tokenStream.peekToken(TSF_OPERAND);
834 if (tt <= TOK_EOF) {
835 if (tt == TOK_EOF)
836 break;
837 JS_ASSERT(tt == TOK_ERROR);
838 goto out;
841 pn = parser.statement();
842 if (!pn)
843 goto out;
844 JS_ASSERT(!cg.blockNode);
846 if (inDirectivePrologue)
847 inDirectivePrologue = parser.recognizeDirectivePrologue(pn);
849 if (!js_FoldConstants(cx, pn, &cg))
850 goto out;
852 if (cg.functionList) {
853 if (!parser.analyzeFunctions(cg.functionList, cg.flags))
854 goto out;
855 cg.functionList = NULL;
858 if (!js_EmitTree(cx, &cg, pn))
859 goto out;
860 #if JS_HAS_XML_SUPPORT
861 if (PN_TYPE(pn) != TOK_SEMI ||
862 !pn->pn_kid ||
863 !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) {
864 onlyXML = false;
866 #endif
867 RecycleTree(pn, &cg);
870 #if JS_HAS_XML_SUPPORT
872 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
873 * For background, see:
875 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
877 if (pn && onlyXML && (tcflags & TCF_NO_SCRIPT_RVAL)) {
878 ReportCompileErrorNumber(cx, &tokenStream, NULL, JSREPORT_ERROR,
879 JSMSG_XML_WHOLE_PROGRAM);
880 goto out;
882 #endif
885 * Global variables (gvars) share the atom index space with locals. Due to
886 * incremental code generation we need to patch the bytecode to adjust the
887 * local references to skip the globals.
889 scriptGlobals = cg.ngvars;
890 if (scriptGlobals != 0 || cg.hasSharps()) {
891 jsbytecode *code, *end;
892 JSOp op;
893 const JSCodeSpec *cs;
894 uintN len, slot;
896 if (scriptGlobals >= SLOTNO_LIMIT)
897 goto too_many_slots;
898 code = CG_BASE(&cg);
899 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
900 JS_ASSERT(code < end);
901 op = (JSOp) *code;
902 cs = &js_CodeSpec[op];
903 len = (cs->length > 0)
904 ? (uintN) cs->length
905 : js_GetVariableBytecodeLength(code);
906 if ((cs->format & JOF_SHARPSLOT) ||
907 JOF_TYPE(cs->format) == JOF_LOCAL ||
908 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
910 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
911 * emitted only for a function.
913 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
914 (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
915 (op == JSOP_GETLOCALPROP));
916 slot = GET_SLOTNO(code);
917 slot += scriptGlobals;
918 if (!(cs->format & JOF_SHARPSLOT))
919 slot += cg.sharpSlots();
920 if (slot >= SLOTNO_LIMIT)
921 goto too_many_slots;
922 SET_SLOTNO(code, slot);
927 #ifdef METER_PARSENODES
928 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
929 (char *)sbrk(0) - (char *)before,
930 parsenodes,
931 maxparsenodes,
932 parsenodes - recyclednodes);
933 before = sbrk(0);
934 #endif
937 * Nowadays the threaded interpreter needs a stop instruction, so we
938 * do have to emit that here.
940 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
941 goto out;
942 #ifdef METER_PARSENODES
943 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
944 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
945 #endif
946 #ifdef JS_ARENAMETER
947 JS_DumpArenaStats(stdout);
948 #endif
949 script = js_NewScriptFromCG(cx, &cg);
950 if (script && funbox && script != script->emptyScript())
951 script->savedCallerFun = true;
953 #ifdef JS_SCOPE_DEPTH_METER
954 if (script) {
955 JSObject *obj = scopeChain;
956 uintN depth = 1;
957 while ((obj = obj->getParent()) != NULL)
958 ++depth;
959 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
961 #endif
963 out:
964 JS_FinishArenaPool(&codePool);
965 JS_FinishArenaPool(&notePool);
966 return script;
968 too_many_slots:
969 ReportCompileErrorNumber(cx, &tokenStream, NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
970 script = NULL;
971 goto out;
975 * Insist on a final return before control flows out of pn. Try to be a bit
976 * smart about loops: do {...; return e2;} while(0) at the end of a function
977 * that contains an early return e1 will get a strict warning. Similarly for
978 * iloops: while (true){...} is treated as though ... returns.
980 #define ENDS_IN_OTHER 0
981 #define ENDS_IN_RETURN 1
982 #define ENDS_IN_BREAK 2
984 static int
985 HasFinalReturn(JSParseNode *pn)
987 JSParseNode *pn2, *pn3;
988 uintN rv, rv2, hasDefault;
990 switch (pn->pn_type) {
991 case TOK_LC:
992 if (!pn->pn_head)
993 return ENDS_IN_OTHER;
994 return HasFinalReturn(pn->last());
996 case TOK_IF:
997 if (!pn->pn_kid3)
998 return ENDS_IN_OTHER;
999 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1001 case TOK_WHILE:
1002 pn2 = pn->pn_left;
1003 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1004 return ENDS_IN_RETURN;
1005 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1006 return ENDS_IN_RETURN;
1007 return ENDS_IN_OTHER;
1009 case TOK_DO:
1010 pn2 = pn->pn_right;
1011 if (pn2->pn_type == TOK_PRIMARY) {
1012 if (pn2->pn_op == JSOP_FALSE)
1013 return HasFinalReturn(pn->pn_left);
1014 if (pn2->pn_op == JSOP_TRUE)
1015 return ENDS_IN_RETURN;
1017 if (pn2->pn_type == TOK_NUMBER) {
1018 if (pn2->pn_dval == 0)
1019 return HasFinalReturn(pn->pn_left);
1020 return ENDS_IN_RETURN;
1022 return ENDS_IN_OTHER;
1024 case TOK_FOR:
1025 pn2 = pn->pn_left;
1026 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1027 return ENDS_IN_RETURN;
1028 return ENDS_IN_OTHER;
1030 case TOK_SWITCH:
1031 rv = ENDS_IN_RETURN;
1032 hasDefault = ENDS_IN_OTHER;
1033 pn2 = pn->pn_right;
1034 if (pn2->pn_type == TOK_LEXICALSCOPE)
1035 pn2 = pn2->expr();
1036 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1037 if (pn2->pn_type == TOK_DEFAULT)
1038 hasDefault = ENDS_IN_RETURN;
1039 pn3 = pn2->pn_right;
1040 JS_ASSERT(pn3->pn_type == TOK_LC);
1041 if (pn3->pn_head) {
1042 rv2 = HasFinalReturn(pn3->last());
1043 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1044 /* Falling through to next case or default. */;
1045 else
1046 rv &= rv2;
1049 /* If a final switch has no default case, we judge it harshly. */
1050 rv &= hasDefault;
1051 return rv;
1053 case TOK_BREAK:
1054 return ENDS_IN_BREAK;
1056 case TOK_WITH:
1057 return HasFinalReturn(pn->pn_right);
1059 case TOK_RETURN:
1060 return ENDS_IN_RETURN;
1062 case TOK_COLON:
1063 case TOK_LEXICALSCOPE:
1064 return HasFinalReturn(pn->expr());
1066 case TOK_THROW:
1067 return ENDS_IN_RETURN;
1069 case TOK_TRY:
1070 /* If we have a finally block that returns, we are done. */
1071 if (pn->pn_kid3) {
1072 rv = HasFinalReturn(pn->pn_kid3);
1073 if (rv == ENDS_IN_RETURN)
1074 return rv;
1077 /* Else check the try block and any and all catch statements. */
1078 rv = HasFinalReturn(pn->pn_kid1);
1079 if (pn->pn_kid2) {
1080 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1081 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1082 rv &= HasFinalReturn(pn2);
1084 return rv;
1086 case TOK_CATCH:
1087 /* Check this catch block's body. */
1088 return HasFinalReturn(pn->pn_kid3);
1090 case TOK_LET:
1091 /* Non-binary let statements are let declarations. */
1092 if (pn->pn_arity != PN_BINARY)
1093 return ENDS_IN_OTHER;
1094 return HasFinalReturn(pn->pn_right);
1096 default:
1097 return ENDS_IN_OTHER;
1101 static JSBool
1102 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1103 uintN anonerrnum)
1105 const char *name;
1107 JS_ASSERT(tc->inFunction());
1108 if (tc->fun->atom) {
1109 name = js_AtomToPrintableString(cx, tc->fun->atom);
1110 } else {
1111 errnum = anonerrnum;
1112 name = NULL;
1114 return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name);
1117 static JSBool
1118 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1120 JS_ASSERT(tc->inFunction());
1121 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1122 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1123 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1127 * Check that it is permitted to assign to lhs. Strict mode code may not
1128 * assign to 'eval' or 'arguments'.
1130 bool
1131 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1133 if (tc->needStrictChecks() &&
1134 lhs->pn_type == TOK_NAME) {
1135 JSAtom *atom = lhs->pn_atom;
1136 JSAtomState *atomState = &cx->runtime->atomState;
1137 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1138 const char *name = js_AtomToPrintableString(cx, atom);
1139 if (!name ||
1140 !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
1141 name)) {
1142 return false;
1146 return true;
1150 * Check that it is permitted to introduce a binding for atom. Strict
1151 * mode forbids introducing new definitions for 'eval' or 'arguments'.
1152 * Use pn for reporting error locations, or use tc's token stream if
1153 * pn is NULL.
1155 bool
1156 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
1158 if (!tc->needStrictChecks())
1159 return true;
1161 JSAtomState *atomState = &cx->runtime->atomState;
1162 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1163 const char *name = js_AtomToPrintableString(cx, atom);
1164 if (name)
1165 ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name);
1166 return false;
1168 return true;
1172 * In strict mode code, all formal parameter names must be distinct. If fun's
1173 * formals are legit given fun's strictness level, return true. Otherwise,
1174 * report an error and return false. Use pn for error position reporting,
1175 * unless we can find something more accurate in tc's decls.
1177 * In some cases the code to parse the argument list will already have noticed
1178 * the duplication; we could try to use that knowledge instead of re-checking
1179 * here. But since the strictness of the function's body determines what
1180 * constraints to apply to the argument list, we can't report the error until
1181 * after we've parsed the body. And as it turns out, the function's local name
1182 * list makes it reasonably cheap to find duplicates after the fact.
1184 static bool
1185 CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
1186 JSParseNode *pn)
1188 JSAtom *atom;
1190 if (!tc->needStrictChecks())
1191 return true;
1193 atom = fun->findDuplicateFormal();
1194 if (atom) {
1196 * We have found a duplicate parameter name. If we can find the
1197 * JSDefinition for the argument, that will have a more accurate source
1198 * location.
1200 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1201 if (dn->pn_op == JSOP_GETARG)
1202 pn = dn;
1203 const char *name = js_AtomToPrintableString(cx, atom);
1204 if (!name ||
1205 !ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_DUPLICATE_FORMAL, name)) {
1206 return false;
1210 if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
1211 JSAtomState *atoms = &cx->runtime->atomState;
1212 atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS
1213 ? atoms->argumentsAtom : atoms->evalAtom);
1214 /* The definition's source position will be more precise. */
1215 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
1216 JS_ASSERT(dn->pn_atom == atom);
1217 const char *name = js_AtomToPrintableString(cx, atom);
1218 if (!name ||
1219 !ReportStrictModeError(cx, TS(tc->parser), tc, dn, JSMSG_BAD_BINDING, name)) {
1220 return false;
1224 return true;
1227 JSParseNode *
1228 Parser::functionBody()
1230 JSStmtInfo stmtInfo;
1231 uintN oldflags, firstLine;
1232 JSParseNode *pn;
1234 JS_ASSERT(tc->inFunction());
1235 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1236 stmtInfo.flags = SIF_BODY_BLOCK;
1238 oldflags = tc->flags;
1239 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1242 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1243 * later, because we may have not peeked in tokenStream yet, so statements
1244 * won't acquire a valid pn->pn_pos.begin from the current token.
1246 firstLine = tokenStream.getLineno();
1247 #if JS_HAS_EXPR_CLOSURES
1248 if (tokenStream.currentToken().type == TOK_LC) {
1249 pn = statements();
1250 } else {
1251 pn = UnaryNode::create(tc);
1252 if (pn) {
1253 pn->pn_kid = assignExpr();
1254 if (!pn->pn_kid) {
1255 pn = NULL;
1256 } else {
1257 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1258 ReportBadReturn(context, tc, JSREPORT_ERROR,
1259 JSMSG_BAD_GENERATOR_RETURN,
1260 JSMSG_BAD_ANON_GENERATOR_RETURN);
1261 pn = NULL;
1262 } else {
1263 pn->pn_type = TOK_RETURN;
1264 pn->pn_op = JSOP_RETURN;
1265 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1270 #else
1271 pn = statements();
1272 #endif
1274 if (pn) {
1275 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1276 js_PopStatement(tc);
1277 pn->pn_pos.begin.lineno = firstLine;
1279 /* Check for falling off the end of a function that returns a value. */
1280 if (JS_HAS_STRICT_OPTION(context) && (tc->flags & TCF_RETURN_EXPR) &&
1281 !CheckFinalReturn(context, tc, pn)) {
1282 pn = NULL;
1286 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1287 return pn;
1290 static JSAtomListElement *
1291 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1293 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, pn->pn_atom);
1294 if (!ale)
1295 return NULL;
1297 JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
1298 if (!dn)
1299 return NULL;
1301 ALE_SET_DEFN(ale, dn);
1302 dn->pn_defn = true;
1303 dn->pn_dflags |= PND_PLACEHOLDER;
1304 return ale;
1307 static bool
1308 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1310 JS_ASSERT(!pn->pn_used);
1311 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1313 JSHashEntry **hep;
1314 JSAtomListElement *ale = NULL;
1315 JSAtomList *list = NULL;
1317 if (let)
1318 ale = (list = &tc->decls)->rawLookup(atom, hep);
1319 if (!ale)
1320 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1322 if (ale) {
1323 JSDefinition *dn = ALE_DEFN(ale);
1324 if (dn != pn) {
1325 JSParseNode **pnup = &dn->dn_uses;
1326 JSParseNode *pnu;
1327 uintN start = let ? pn->pn_blockid : tc->bodyid;
1329 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1330 JS_ASSERT(pnu->pn_used);
1331 pnu->pn_lexdef = (JSDefinition *) pn;
1332 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1333 pnup = &pnu->pn_link;
1336 if (pnu != dn->dn_uses) {
1337 *pnup = pn->dn_uses;
1338 pn->dn_uses = dn->dn_uses;
1339 dn->dn_uses = pnu;
1341 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1342 list->rawRemove(tc->parser, ale, hep);
1347 ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1348 if (!ale)
1349 return false;
1350 ALE_SET_DEFN(ale, pn);
1351 pn->pn_defn = true;
1352 pn->pn_dflags &= ~PND_PLACEHOLDER;
1353 return true;
1356 static void
1357 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1359 JS_ASSERT(!pn->pn_used);
1360 JS_ASSERT(!pn->pn_defn);
1361 JS_ASSERT(pn != dn->dn_uses);
1362 pn->pn_link = dn->dn_uses;
1363 dn->dn_uses = pn;
1364 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1365 pn->pn_used = true;
1366 pn->pn_lexdef = dn;
1369 static void
1370 ForgetUse(JSParseNode *pn)
1372 if (!pn->pn_used) {
1373 JS_ASSERT(!pn->pn_defn);
1374 return;
1377 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1378 JSParseNode *pnu;
1379 while ((pnu = *pnup) != pn)
1380 pnup = &pnu->pn_link;
1381 *pnup = pn->pn_link;
1382 pn->pn_used = false;
1385 static JSParseNode *
1386 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1388 JSParseNode *lhs = NewOrRecycledNode(tc);
1389 if (!lhs)
1390 return NULL;
1391 *lhs = *pn;
1393 if (pn->pn_used) {
1394 JSDefinition *dn = pn->pn_lexdef;
1395 JSParseNode **pnup = &dn->dn_uses;
1397 while (*pnup != pn)
1398 pnup = &(*pnup)->pn_link;
1399 *pnup = lhs;
1400 lhs->pn_link = pn->pn_link;
1401 pn->pn_link = NULL;
1404 pn->pn_type = TOK_ASSIGN;
1405 pn->pn_op = JSOP_NOP;
1406 pn->pn_arity = PN_BINARY;
1407 pn->pn_parens = false;
1408 pn->pn_used = pn->pn_defn = false;
1409 pn->pn_left = lhs;
1410 pn->pn_right = rhs;
1411 return lhs;
1414 static JSParseNode *
1415 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1418 * If dn is var, const, or let, and it has an initializer, then we must
1419 * rewrite it to be an assignment node, whose freshly allocated left-hand
1420 * side becomes a use of pn.
1422 if (dn->isBindingForm()) {
1423 JSParseNode *rhs = dn->expr();
1424 if (rhs) {
1425 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1426 if (!lhs)
1427 return NULL;
1428 //pn->dn_uses = lhs;
1429 dn = (JSDefinition *) lhs;
1432 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1433 } else if (dn->kind() == JSDefinition::FUNCTION) {
1434 JS_ASSERT(dn->isTopLevel());
1435 JS_ASSERT(dn->pn_op == JSOP_NOP);
1436 dn->pn_type = TOK_NAME;
1437 dn->pn_arity = PN_NAME;
1438 dn->pn_atom = atom;
1441 /* Now make dn no longer a definition, rather a use of pn. */
1442 JS_ASSERT(dn->pn_type == TOK_NAME);
1443 JS_ASSERT(dn->pn_arity == PN_NAME);
1444 JS_ASSERT(dn->pn_atom == atom);
1446 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1447 JS_ASSERT(pnu->pn_used);
1448 JS_ASSERT(!pnu->pn_defn);
1449 pnu->pn_lexdef = (JSDefinition *) pn;
1450 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1452 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1453 pn->dn_uses = dn;
1455 dn->pn_defn = false;
1456 dn->pn_used = true;
1457 dn->pn_lexdef = (JSDefinition *) pn;
1458 dn->pn_cookie = FREE_UPVAR_COOKIE;
1459 dn->pn_dflags &= ~PND_BOUND;
1460 return dn;
1463 static bool
1464 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1466 JSParseNode *argpn, *argsbody;
1468 /* Flag tc so we don't have to lookup arguments on every use. */
1469 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1470 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1471 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1472 tc->flags |= TCF_FUN_PARAM_EVAL;
1475 * Make an argument definition node, distinguished by being in tc->decls
1476 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1477 * list node returned via pn->pn_body.
1479 argpn = NameNode::create(atom, tc);
1480 if (!argpn)
1481 return false;
1482 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1484 /* Arguments are initialized by definition. */
1485 argpn->pn_dflags |= PND_INITIALIZED;
1486 if (!Define(argpn, atom, tc))
1487 return false;
1489 argsbody = pn->pn_body;
1490 if (!argsbody) {
1491 argsbody = ListNode::create(tc);
1492 if (!argsbody)
1493 return false;
1494 argsbody->pn_type = TOK_ARGSBODY;
1495 argsbody->pn_op = JSOP_NOP;
1496 argsbody->makeEmpty();
1497 pn->pn_body = argsbody;
1499 argsbody->append(argpn);
1501 argpn->pn_op = JSOP_GETARG;
1502 argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1503 argpn->pn_dflags |= PND_BOUND;
1504 return true;
1508 * Compile a JS function body, which might appear as the value of an event
1509 * handler attribute in an HTML <INPUT> tag.
1511 bool
1512 Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1513 const jschar *chars, size_t length,
1514 const char *filename, uintN lineno)
1516 Compiler compiler(cx, principals);
1518 if (!compiler.init(chars, length, NULL, filename, lineno))
1519 return false;
1521 /* No early return from after here until the js_FinishArenaPool calls. */
1522 JSArenaPool codePool, notePool;
1523 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1524 &cx->scriptStackQuota);
1525 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
1526 &cx->scriptStackQuota);
1528 Parser &parser = compiler.parser;
1529 TokenStream &tokenStream = parser.tokenStream;
1531 JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
1532 if (!funcg.init())
1533 return NULL;
1535 funcg.flags |= TCF_IN_FUNCTION;
1536 funcg.fun = fun;
1537 if (!GenerateBlockId(&funcg, funcg.bodyid))
1538 return NULL;
1540 /* FIXME: make Function format the source for a function definition. */
1541 tokenStream.mungeCurrentToken(TOK_NAME);
1542 JSParseNode *fn = FunctionNode::create(&funcg);
1543 if (fn) {
1544 fn->pn_body = NULL;
1545 fn->pn_cookie = FREE_UPVAR_COOKIE;
1547 uintN nargs = fun->nargs;
1548 if (nargs) {
1549 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1550 if (!names) {
1551 fn = NULL;
1552 } else {
1553 for (uintN i = 0; i < nargs; i++) {
1554 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1555 if (!DefineArg(fn, name, i, &funcg)) {
1556 fn = NULL;
1557 break;
1565 * Farble the body so that it looks like a block statement to js_EmitTree,
1566 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1567 * done parsing, we must fold constants, analyze any nested functions, and
1568 * generate code for this function, including a stop opcode at the end.
1570 tokenStream.mungeCurrentToken(TOK_LC);
1571 JSParseNode *pn = fn ? parser.functionBody() : NULL;
1572 if (pn) {
1573 if (!CheckStrictFormals(cx, &funcg, fun, pn)) {
1574 pn = NULL;
1575 } else if (!tokenStream.matchToken(TOK_EOF)) {
1576 ReportCompileErrorNumber(cx, &tokenStream, NULL, JSREPORT_ERROR,
1577 JSMSG_SYNTAX_ERROR);
1578 pn = NULL;
1579 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1580 /* js_FoldConstants reported the error already. */
1581 pn = NULL;
1582 } else if (funcg.functionList &&
1583 !parser.analyzeFunctions(funcg.functionList, funcg.flags)) {
1584 pn = NULL;
1585 } else {
1586 if (fn->pn_body) {
1587 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1588 fn->pn_body->append(pn);
1589 fn->pn_body->pn_pos = pn->pn_pos;
1590 pn = fn->pn_body;
1593 if (!js_EmitFunctionScript(cx, &funcg, pn))
1594 pn = NULL;
1598 /* Restore saved state and release code generation arenas. */
1599 JS_FinishArenaPool(&codePool);
1600 JS_FinishArenaPool(&notePool);
1601 return pn != NULL;
1605 * Parameter block types for the several Binder functions. We use a common
1606 * helper function signature in order to share code among destructuring and
1607 * simple variable declaration parsers. In the destructuring case, the binder
1608 * function is called indirectly from the variable declaration parser by way
1609 * of CheckDestructuring and its friends.
1611 typedef JSBool
1612 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1614 struct BindData {
1615 BindData() : fresh(true) {}
1617 JSParseNode *pn; /* name node for definition processing and
1618 error source coordinates */
1619 JSOp op; /* prolog bytecode or nop */
1620 Binder binder; /* binder, discriminates u */
1621 union {
1622 struct {
1623 uintN overflow;
1624 } let;
1626 bool fresh;
1629 static JSBool
1630 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1631 JSLocalKind localKind, bool isArg)
1633 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1636 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1637 * Instead 'var arguments' always restates the predefined property of the
1638 * activation objects whose name is 'arguments'. Assignment to such a
1639 * variable must be handled specially.
1641 * Special case: an argument named 'arguments' *does* shadow the predefined
1642 * arguments property.
1644 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1645 return JS_TRUE;
1647 return js_AddLocal(cx, fun, atom, localKind);
1650 #if JS_HAS_DESTRUCTURING
1651 static JSBool
1652 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1653 JSTreeContext *tc)
1655 JSParseNode *pn;
1657 /* Flag tc so we don't have to lookup arguments on every use. */
1658 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1659 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1660 if (atom == tc->parser->context->runtime->atomState.evalAtom)
1661 tc->flags |= TCF_FUN_PARAM_EVAL;
1663 JS_ASSERT(tc->inFunction());
1665 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1666 if (localKind != JSLOCAL_NONE) {
1667 ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
1668 JSMSG_DESTRUCT_DUP_ARG);
1669 return JS_FALSE;
1671 JS_ASSERT(!tc->decls.lookup(atom));
1673 pn = data->pn;
1674 if (!Define(pn, atom, tc))
1675 return JS_FALSE;
1677 uintN index = tc->fun->u.i.nvars;
1678 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1679 return JS_FALSE;
1680 pn->pn_op = JSOP_SETLOCAL;
1681 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1682 pn->pn_dflags |= PND_BOUND;
1683 return JS_TRUE;
1685 #endif /* JS_HAS_DESTRUCTURING */
1687 JSFunction *
1688 Parser::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1690 JSObject *parent;
1691 JSFunction *fun;
1693 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1696 * Find the global compilation context in order to pre-set the newborn
1697 * function's parent slot to tc->scopeChain. If the global context is a
1698 * compile-and-go one, we leave the pre-set parent intact; otherwise we
1699 * clear parent and proto.
1701 while (tc->parent)
1702 tc = tc->parent;
1703 parent = tc->inFunction() ? NULL : tc->scopeChain;
1705 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1706 parent, atom);
1708 if (fun && !tc->compileAndGo()) {
1709 FUN_OBJECT(fun)->clearParent();
1710 FUN_OBJECT(fun)->clearProto();
1712 return fun;
1715 static JSBool
1716 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
1718 TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
1719 if (tt == TOK_ERROR)
1720 return JS_FALSE;
1721 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1722 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
1723 return JS_FALSE;
1725 (void) ts->matchToken(TOK_SEMI);
1726 return JS_TRUE;
1729 bool
1730 Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
1732 if (!markFunArgs(funbox, tcflags))
1733 return false;
1734 setFunctionKinds(funbox, tcflags);
1735 return true;
1739 * Mark as funargs any functions that reach up to one or more upvars across an
1740 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1742 * function f(o, p) {
1743 * o.m = function o_m(a) {
1744 * function g() { return p; }
1745 * function h() { return a; }
1746 * return g() + h();
1750 * but without this extra marking phase, function g will not be marked as a
1751 * funarg since it is called from within its parent scope. But g reaches up to
1752 * f's parameter p, so if o_m escapes f's activation scope, g does too and
1753 * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1754 * nor uses an upvar "above" o_m's level.
1756 * If function g itself contained lambdas that contained non-lambdas that reach
1757 * up above its level, then those non-lambdas would have to be marked too. This
1758 * process is potentially exponential in the number of functions, but generally
1759 * not so complex. But it can't be done during a single recursive traversal of
1760 * the funbox tree, so we must use a work queue.
1762 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1763 * between the static level of the bodies of funbox and its peers (which must
1764 * be funbox->level + 1), and the static level of the nearest upvar among all
1765 * the upvars contained by funbox and its peers. If there are no upvars, return
1766 * FREE_STATIC_LEVEL. Thus this function never returns 0.
1768 static uintN
1769 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1771 uintN allskipmin = FREE_STATIC_LEVEL;
1773 do {
1774 JSParseNode *fn = funbox->node;
1775 JSFunction *fun = (JSFunction *) funbox->object;
1776 int fnlevel = level;
1779 * An eval can leak funbox, functions along its ancestor line, and its
1780 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1781 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1782 * already been marked as funargs by this point. Therefore we have to
1783 * flag only funbox->node and funbox->kids' nodes here.
1785 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1786 fn->setFunArg();
1787 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1788 kid->node->setFunArg();
1792 * Compute in skipmin the least distance from fun's static level up to
1793 * an upvar, whether used directly by fun, or indirectly by a function
1794 * nested in fun.
1796 uintN skipmin = FREE_STATIC_LEVEL;
1797 JSParseNode *pn = fn->pn_body;
1799 if (pn->pn_type == TOK_UPVARS) {
1800 JSAtomList upvars(pn->pn_names);
1801 JS_ASSERT(upvars.count != 0);
1803 JSAtomListIterator iter(&upvars);
1804 JSAtomListElement *ale;
1806 while ((ale = iter()) != NULL) {
1807 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1809 if (!lexdep->isFreeVar()) {
1810 uintN upvarLevel = lexdep->frameLevel();
1812 if (int(upvarLevel) <= fnlevel)
1813 fn->setFunArg();
1815 uintN skip = (funbox->level + 1) - upvarLevel;
1816 if (skip < skipmin)
1817 skipmin = skip;
1823 * If this function escapes, whether directly (the parser detects such
1824 * escapes) or indirectly (because this non-escaping function uses an
1825 * upvar that reaches across an outer function boundary where the outer
1826 * function escapes), enqueue it for further analysis, and bump fnlevel
1827 * to trap any non-escaping children.
1829 if (fn->isFunArg()) {
1830 queue->push(funbox);
1831 fnlevel = int(funbox->level);
1835 * Now process the current function's children, and recalibrate their
1836 * cumulative skipmin to be relative to the current static level.
1838 if (funbox->kids) {
1839 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1841 JS_ASSERT(kidskipmin != 0);
1842 if (kidskipmin != FREE_STATIC_LEVEL) {
1843 --kidskipmin;
1844 if (kidskipmin != 0 && kidskipmin < skipmin)
1845 skipmin = kidskipmin;
1850 * Finally, after we've traversed all of the current function's kids,
1851 * minimize fun's skipmin against our accumulated skipmin. Do likewise
1852 * with allskipmin, but minimize across funbox and all of its siblings,
1853 * to compute our return value.
1855 if (skipmin != FREE_STATIC_LEVEL) {
1856 fun->u.i.skipmin = skipmin;
1857 if (skipmin < allskipmin)
1858 allskipmin = skipmin;
1860 } while ((funbox = funbox->siblings) != NULL);
1862 return allskipmin;
1865 bool
1866 Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1868 JSFunctionBoxQueue queue;
1869 if (!queue.init(functionCount))
1870 return false;
1872 FindFunArgs(funbox, -1, &queue);
1873 while ((funbox = queue.pull()) != NULL) {
1874 JSParseNode *fn = funbox->node;
1875 JS_ASSERT(fn->isFunArg());
1877 JSParseNode *pn = fn->pn_body;
1878 if (pn->pn_type == TOK_UPVARS) {
1879 JSAtomList upvars(pn->pn_names);
1880 JS_ASSERT(upvars.count != 0);
1882 JSAtomListIterator iter(&upvars);
1883 JSAtomListElement *ale;
1885 while ((ale = iter()) != NULL) {
1886 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1888 if (!lexdep->isFreeVar() &&
1889 !lexdep->isFunArg() &&
1890 (lexdep->kind() == JSDefinition::FUNCTION ||
1891 PN_OP(lexdep) == JSOP_CALLEE)) {
1893 * Mark this formerly-Algol-like function as an escaping
1894 * function (i.e., as a funarg), because it is used from a
1895 * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1896 * access upvars.
1898 * Progress is guaranteed because we set the funarg flag
1899 * here, which suppresses revisiting this function (thanks
1900 * to the !lexdep->isFunArg() test just above).
1902 lexdep->setFunArg();
1904 JSFunctionBox *afunbox;
1905 if (PN_OP(lexdep) == JSOP_CALLEE) {
1907 * A named function expression will not appear to be a
1908 * funarg if it is immediately applied. However, if its
1909 * name is used in an escaping function nested within
1910 * it, then it must become flagged as a funarg again.
1911 * See bug 545980.
1913 afunbox = funbox;
1914 uintN calleeLevel = UPVAR_FRAME_SKIP(lexdep->pn_cookie);
1915 uintN staticLevel = afunbox->level + 1U;
1916 while (staticLevel != calleeLevel) {
1917 afunbox = afunbox->parent;
1918 --staticLevel;
1920 afunbox->node->setFunArg();
1921 } else {
1922 afunbox = lexdep->pn_funbox;
1924 queue.push(afunbox);
1927 * Walk over nested functions again, now that we have
1928 * changed the level across which it is unsafe to access
1929 * upvars using the runtime dynamic link (frame chain).
1931 if (afunbox->kids)
1932 FindFunArgs(afunbox->kids, afunbox->level, &queue);
1937 return true;
1940 static uint32
1941 MinBlockId(JSParseNode *fn, uint32 id)
1943 if (fn->pn_blockid < id)
1944 return false;
1945 if (fn->pn_defn) {
1946 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1947 if (pn->pn_blockid < id)
1948 return false;
1951 return true;
1954 static inline bool
1955 CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
1958 * Consider the current function (the lambda, innermost below) using a var
1959 * x defined two static levels up:
1961 * function f() {
1962 * // z = g();
1963 * var x = 42;
1964 * function g() {
1965 * return function () { return x; };
1967 * return g();
1970 * So long as (1) the initialization in 'var x = 42' dominates all uses of
1971 * g and (2) x is not reassigned, it is safe to optimize the lambda to a
1972 * flat closure. Uncommenting the early call to g makes this optimization
1973 * unsafe (z could name a global setter that calls its argument).
1975 JSFunctionBox *afunbox = funbox;
1976 uintN dnLevel = dn->frameLevel();
1978 JS_ASSERT(dnLevel <= funbox->level);
1979 while (afunbox->level != dnLevel) {
1980 afunbox = afunbox->parent;
1983 * NB: afunbox can't be null because we are sure to find a function box
1984 * whose level == dnLevel before we would try to walk above the root of
1985 * the funbox tree. See bug 493260 comments 16-18.
1987 * Assert but check anyway, to protect future changes that bind eval
1988 * upvars in the parser.
1990 JS_ASSERT(afunbox);
1993 * If this function is reaching up across an enclosing funarg, then we
1994 * cannot copy dn's value into a flat closure slot (the display stops
1995 * working once the funarg escapes).
1997 if (!afunbox || afunbox->node->isFunArg())
1998 return false;
2001 * Reaching up for dn across a generator also means we can't flatten,
2002 * since the generator iterator does not run until later, in general.
2003 * See bug 563034.
2005 if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
2006 return false;
2010 * If afunbox's function (which is at the same level as dn) is in a loop,
2011 * pessimistically assume the variable initializer may be in the same loop.
2012 * A flat closure would then be unsafe, as the captured variable could be
2013 * assigned after the closure is created. See bug 493232.
2015 if (afunbox->inLoop)
2016 return false;
2019 * |with| and eval used as an operator defeat lexical scoping: they can be
2020 * used to assign to any in-scope variable. Therefore they must disable
2021 * flat closures that use such upvars. The parser detects these as special
2022 * forms and marks the function heavyweight.
2024 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
2025 return false;
2028 * If afunbox's function is not a lambda, it will be hoisted, so it could
2029 * capture the undefined value that by default initializes var, let, and
2030 * const bindings. And if dn is a function that comes at (meaning a
2031 * function refers to its own name) or strictly after afunbox, we also
2032 * defeat the flat closure optimization for this dn.
2034 JSFunction *afun = (JSFunction *) afunbox->object;
2035 if (!(afun->flags & JSFUN_LAMBDA)) {
2036 if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
2037 return false;
2040 if (!dn->isInitialized())
2041 return false;
2043 JSDefinition::Kind dnKind = dn->kind();
2044 if (dnKind != JSDefinition::CONST) {
2045 if (dn->isAssigned())
2046 return false;
2049 * Any formal could be mutated behind our back via the arguments
2050 * object, so deoptimize if the outer function uses arguments.
2052 * In a Function constructor call where the final argument -- the body
2053 * source for the function to create -- contains a nested function
2054 * definition or expression, afunbox->parent will be null. The body
2055 * source might use |arguments| outside of any nested functions it may
2056 * contain, so we have to check the tcflags parameter that was passed
2057 * in from Compiler::compileFunctionBody.
2059 if (dnKind == JSDefinition::ARG &&
2060 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
2061 return false;
2066 * Check quick-and-dirty dominance relation. Function definitions dominate
2067 * their uses thanks to hoisting. Other binding forms hoist as undefined,
2068 * of course, so check forward-reference and blockid relations.
2070 if (dnKind != JSDefinition::FUNCTION) {
2072 * Watch out for code such as
2074 * (function () {
2075 * ...
2076 * var jQuery = ... = function (...) {
2077 * return new jQuery.foo.bar(baz);
2079 * ...
2080 * })();
2082 * where the jQuery variable is not reassigned, but of course is not
2083 * initialized at the time that the would-be-flat closure containing
2084 * the jQuery upvar is formed.
2086 if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
2087 return false;
2088 if (!MinBlockId(afunbox->node, dn->pn_blockid))
2089 return false;
2091 return true;
2094 static void
2095 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2097 uintN dnLevel = dn->frameLevel();
2099 while ((funbox = funbox->parent) != NULL) {
2101 * Notice that funbox->level is the static level of the definition or
2102 * expression of the function parsed into funbox, not the static level
2103 * of its body. Therefore we must add 1 to match dn's level to find the
2104 * funbox whose body contains the dn definition.
2106 if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
2107 funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2108 break;
2110 funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
2113 if (!funbox && (tcflags & TCF_IN_FUNCTION))
2114 tcflags |= TCF_FUN_HEAVYWEIGHT;
2117 static void
2118 DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
2120 uintN ndeoptimized = 0;
2121 const TokenPos &pos = funbox->node->pn_body->pn_pos;
2123 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2124 JS_ASSERT(pnu->pn_used);
2125 JS_ASSERT(!pnu->pn_defn);
2126 if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
2127 pnu->pn_dflags |= PND_DEOPTIMIZED;
2128 ++ndeoptimized;
2132 if (ndeoptimized != 0)
2133 FlagHeavyweights(dn, funbox, tcflags);
2136 void
2137 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
2139 #ifdef JS_FUNCTION_METERING
2140 # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
2141 #else
2142 # define FUN_METER(x) ((void)0)
2143 #endif
2145 for (;;) {
2146 JSParseNode *fn = funbox->node;
2147 JSParseNode *pn = fn->pn_body;
2149 if (funbox->kids) {
2150 setFunctionKinds(funbox->kids, tcflags);
2153 * We've unwound from recursively setting our kids' kinds, which
2154 * also classifies enclosing functions holding upvars referenced in
2155 * those descendants' bodies. So now we can check our "methods".
2157 * Despecialize from branded method-identity-based shape to sprop-
2158 * or slot-based shape if this function smells like a constructor
2159 * and too many of its methods are *not* joinable null closures
2160 * (i.e., they have one or more upvars fetched via the display).
2162 JSParseNode *pn2 = pn;
2163 if (PN_TYPE(pn2) == TOK_UPVARS)
2164 pn2 = pn2->pn_tree;
2165 if (PN_TYPE(pn2) == TOK_ARGSBODY)
2166 pn2 = pn2->last();
2168 #if JS_HAS_EXPR_CLOSURES
2169 if (PN_TYPE(pn2) == TOK_LC)
2170 #endif
2171 if (!(funbox->tcflags & TCF_RETURN_EXPR)) {
2172 uintN methodSets = 0, slowMethodSets = 0;
2174 for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
2175 JS_ASSERT(PN_OP(method) == JSOP_LAMBDA || PN_OP(method) == JSOP_LAMBDA_FC);
2176 ++methodSets;
2177 if (!method->pn_funbox->joinable())
2178 ++slowMethodSets;
2181 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
2182 funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
2186 JSFunction *fun = (JSFunction *) funbox->object;
2188 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2190 FUN_METER(allfun);
2191 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2192 FUN_METER(heavy);
2193 } else if (pn->pn_type != TOK_UPVARS) {
2195 * No lexical dependencies => null closure, for best performance.
2196 * A null closure needs no scope chain, but alas we've coupled
2197 * principals-finding to scope (for good fundamental reasons, but
2198 * the implementation overloads the parent slot and we should fix
2199 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2201 * In more detail: the ES3 spec allows the implementation to create
2202 * "joined function objects", or not, at its discretion. But real-
2203 * world implementations always create unique function objects for
2204 * closures, and this can be detected via mutation. Open question:
2205 * do popular implementations create unique function objects for
2206 * null closures?
2208 * FIXME: bug 476950.
2210 FUN_METER(nofreeupvar);
2211 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2212 } else {
2213 JSAtomList upvars(pn->pn_names);
2214 JS_ASSERT(upvars.count != 0);
2216 JSAtomListIterator iter(&upvars);
2217 JSAtomListElement *ale;
2219 if (!fn->isFunArg()) {
2221 * This function is Algol-like, it never escapes. So long as it
2222 * does not assign to outer variables, it needs only an upvars
2223 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
2224 * bytecode to reach up the frame stack at runtime based on
2225 * those upvars' cookies.
2227 * Any assignments to upvars from functions called by this one
2228 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
2229 * which load from stack homes when interpreting or from native
2230 * stack slots when executing a trace.
2232 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
2233 * nested function to assign to an outer lexical variable, so
2234 * we defer adding yet more code footprint in the absence of
2235 * evidence motivating these opcodes.
2237 bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
2238 uintN nupvars = 0;
2241 * Check that at least one outer lexical binding was assigned
2242 * to (global variables don't count). This is conservative: we
2243 * could limit assignments to those in the current function,
2244 * but that's too much work. As with flat closures (handled
2245 * below), we optimize for the case where outer bindings are
2246 * not reassigned anywhere.
2248 while ((ale = iter()) != NULL) {
2249 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2251 if (!lexdep->isFreeVar()) {
2252 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2253 ++nupvars;
2254 if (lexdep->isAssigned())
2255 break;
2258 if (!ale)
2259 mutation = false;
2261 if (nupvars == 0) {
2262 FUN_METER(onlyfreevar);
2263 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2264 } else if (!mutation &&
2265 !(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
2267 * Algol-like functions can read upvars using the dynamic
2268 * link (cx->fp/fp->down), optimized using the cx->display
2269 * lookup table indexed by static level. They do not need
2270 * to entrain and search their environment objects.
2272 FUN_METER(display);
2273 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2274 } else {
2275 if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
2276 FUN_METER(setupvar);
2278 } else {
2279 uintN nupvars = 0, nflattened = 0;
2282 * For each lexical dependency from this closure to an outer
2283 * binding, analyze whether it is safe to copy the binding's
2284 * value into a flat closure slot when the closure is formed.
2286 while ((ale = iter()) != NULL) {
2287 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2289 if (!lexdep->isFreeVar()) {
2290 ++nupvars;
2291 if (CanFlattenUpvar(lexdep, funbox, tcflags)) {
2292 ++nflattened;
2293 continue;
2295 DeoptimizeUsesWithin(lexdep, funbox, tcflags);
2299 if (nupvars == 0) {
2300 FUN_METER(onlyfreevar);
2301 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2302 } else if (nflattened == nupvars) {
2303 /* FIXME bug 545759: to test nflattened != 0 */
2305 * We made it all the way through the upvar loop, so it's
2306 * safe to optimize to a flat closure.
2308 FUN_METER(flat);
2309 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2310 switch (PN_OP(fn)) {
2311 case JSOP_DEFFUN:
2312 fn->pn_op = JSOP_DEFFUN_FC;
2313 break;
2314 case JSOP_DEFLOCALFUN:
2315 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2316 break;
2317 case JSOP_LAMBDA:
2318 fn->pn_op = JSOP_LAMBDA_FC;
2319 break;
2320 default:
2321 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2322 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2324 } else {
2325 FUN_METER(badfunarg);
2330 if (FUN_KIND(fun) == JSFUN_INTERPRETED && pn->pn_type == TOK_UPVARS) {
2332 * One or more upvars cannot be safely snapshot into a flat
2333 * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2334 * all upvars, and for each non-free upvar, ensure that its
2335 * containing function has been flagged as heavyweight.
2337 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2338 * generating any code for a tree of nested functions.
2340 JSAtomList upvars(pn->pn_names);
2341 JS_ASSERT(upvars.count != 0);
2343 JSAtomListIterator iter(&upvars);
2344 JSAtomListElement *ale;
2346 while ((ale = iter()) != NULL) {
2347 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2348 if (!lexdep->isFreeVar())
2349 FlagHeavyweights(lexdep, funbox, tcflags);
2353 funbox = funbox->siblings;
2354 if (!funbox)
2355 break;
2358 #undef FUN_METER
2361 const char js_argument_str[] = "argument";
2362 const char js_variable_str[] = "variable";
2363 const char js_unknown_str[] = "unknown";
2365 const char *
2366 JSDefinition::kindString(Kind kind)
2368 static const char *table[] = {
2369 js_var_str, js_const_str, js_let_str,
2370 js_function_str, js_argument_str, js_unknown_str
2373 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2374 return table[kind];
2377 static JSFunctionBox *
2378 EnterFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2379 uintN lambda = JSFUN_LAMBDA)
2381 JSTreeContext *tc = funtc->parent;
2382 JSFunction *fun = tc->parser->newFunction(tc, funAtom, lambda);
2383 if (!fun)
2384 return NULL;
2386 /* Create box for fun->object early to protect against last-ditch GC. */
2387 JSFunctionBox *funbox = tc->parser->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2388 if (!funbox)
2389 return NULL;
2391 /* Initialize non-default members of funtc. */
2392 funtc->flags |= funbox->tcflags;
2393 funtc->blockidGen = tc->blockidGen;
2394 if (!GenerateBlockId(funtc, funtc->bodyid))
2395 return NULL;
2396 funtc->fun = fun;
2397 funtc->funbox = funbox;
2398 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2399 return NULL;
2401 return funbox;
2404 static bool
2405 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2406 uintN lambda = JSFUN_LAMBDA)
2408 JSTreeContext *tc = funtc->parent;
2409 tc->blockidGen = funtc->blockidGen;
2411 JSFunctionBox *funbox = fn->pn_funbox;
2412 funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
2414 fn->pn_dflags |= PND_INITIALIZED;
2415 JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2416 fn->pn_dflags & PND_TOPLEVEL);
2417 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2418 fn->pn_dflags |= PND_BLOCKCHILD;
2421 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2422 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2423 * params and body. We do this only if there are lexical dependencies not
2424 * satisfied by the function's declarations, to avoid penalizing functions
2425 * that use only their arguments and other local bindings.
2427 if (funtc->lexdeps.count != 0) {
2428 JSAtomListIterator iter(&funtc->lexdeps);
2429 JSAtomListElement *ale;
2430 int foundCallee = 0;
2432 while ((ale = iter()) != NULL) {
2433 JSAtom *atom = ALE_ATOM(ale);
2434 JSDefinition *dn = ALE_DEFN(ale);
2435 JS_ASSERT(dn->isPlaceholder());
2437 if (atom == funAtom && lambda != 0) {
2438 dn->pn_op = JSOP_CALLEE;
2439 dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2440 dn->pn_dflags |= PND_BOUND;
2443 * If this named function expression uses its own name other
2444 * than to call itself, flag this function specially.
2446 if (dn->isFunArg())
2447 funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2448 foundCallee = 1;
2449 continue;
2452 if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2453 dn->isAssigned()) {
2455 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2456 * any use of dn in funtc assigns. See NoteLValue for the easy
2457 * backward-reference case; this is the hard forward-reference
2458 * case where we pay a higher price.
2460 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2461 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2462 funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2463 break;
2468 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2469 if (!outer_ale)
2470 outer_ale = tc->lexdeps.lookup(atom);
2471 if (outer_ale) {
2473 * Insert dn's uses list at the front of outer_dn's list.
2475 * Without loss of generality or correctness, we allow a dn to
2476 * be in inner and outer lexdeps, since the purpose of lexdeps
2477 * is one-pass coordination of name use and definition across
2478 * functions, and if different dn's are used we'll merge lists
2479 * when leaving the inner function.
2481 * The dn == outer_dn case arises with generator expressions
2482 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2483 * case), and nowhere else, currently.
2485 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2487 if (dn != outer_dn) {
2488 JSParseNode **pnup = &dn->dn_uses;
2489 JSParseNode *pnu;
2491 while ((pnu = *pnup) != NULL) {
2492 pnu->pn_lexdef = outer_dn;
2493 pnup = &pnu->pn_link;
2497 * Make dn be a use that redirects to outer_dn, because we
2498 * can't replace dn with outer_dn in all the pn_namesets in
2499 * the AST where it may be. Instead we make it forward to
2500 * outer_dn. See JSDefinition::resolve.
2502 *pnup = outer_dn->dn_uses;
2503 outer_dn->dn_uses = dn;
2504 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2505 dn->pn_defn = false;
2506 dn->pn_used = true;
2507 dn->pn_lexdef = outer_dn;
2509 } else {
2510 /* Add an outer lexical dependency for ale's definition. */
2511 outer_ale = tc->lexdeps.add(tc->parser, atom);
2512 if (!outer_ale)
2513 return false;
2514 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2518 if (funtc->lexdeps.count - foundCallee != 0) {
2519 JSParseNode *body = fn->pn_body;
2521 fn->pn_body = NameSetNode::create(tc);
2522 if (!fn->pn_body)
2523 return false;
2525 fn->pn_body->pn_type = TOK_UPVARS;
2526 fn->pn_body->pn_pos = body->pn_pos;
2527 if (foundCallee)
2528 funtc->lexdeps.remove(tc->parser, funAtom);
2529 fn->pn_body->pn_names = funtc->lexdeps;
2530 fn->pn_body->pn_tree = body;
2533 funtc->lexdeps.clear();
2536 return true;
2539 JSParseNode *
2540 Parser::functionDef(uintN lambda, bool namePermitted)
2542 JSParseNode *pn, *body, *result;
2543 TokenKind tt;
2544 JSAtomListElement *ale;
2545 #if JS_HAS_DESTRUCTURING
2546 JSParseNode *item, *list = NULL;
2547 bool destructuringArg = false;
2548 JSAtom *duplicatedArg = NULL;
2549 #endif
2552 * Save the current op for later so we can tag the created function as a
2553 * getter/setter if necessary.
2555 JSOp op = tokenStream.currentToken().t_op;
2557 /* Make a TOK_FUNCTION node. */
2558 pn = FunctionNode::create(tc);
2559 if (!pn)
2560 return NULL;
2561 pn->pn_body = NULL;
2562 pn->pn_cookie = FREE_UPVAR_COOKIE;
2565 * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2566 * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
2568 * Also treat function sub-statements (non-lambda, non-top-level functions)
2569 * as escaping funargs, since we can't statically analyze their definitions
2570 * and uses.
2572 bool topLevel = tc->atTopLevel();
2573 pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2575 /* Scan the optional function name into funAtom. */
2576 JSAtom *funAtom = NULL;
2577 if (namePermitted) {
2578 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
2579 if (tt == TOK_NAME) {
2580 funAtom = tokenStream.currentToken().t_atom;
2581 } else {
2582 if (lambda == 0 && (context->options & JSOPTION_ANONFUNFIX)) {
2583 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
2584 JSMSG_SYNTAX_ERROR);
2585 return NULL;
2587 tokenStream.ungetToken();
2592 * Record names for function statements in tc->decls so we know when to
2593 * avoid optimizing variable references that might name a function.
2595 if (lambda == 0 && funAtom) {
2596 ale = tc->decls.lookup(funAtom);
2597 if (ale) {
2598 JSDefinition *dn = ALE_DEFN(ale);
2599 JSDefinition::Kind dn_kind = dn->kind();
2601 JS_ASSERT(!dn->pn_used);
2602 JS_ASSERT(dn->pn_defn);
2604 if (JS_HAS_STRICT_OPTION(context) || dn_kind == JSDefinition::CONST) {
2605 const char *name = js_AtomToPrintableString(context, funAtom);
2606 if (!name ||
2607 !ReportCompileErrorNumber(context, &tokenStream, NULL,
2608 (dn_kind != JSDefinition::CONST)
2609 ? JSREPORT_WARNING | JSREPORT_STRICT
2610 : JSREPORT_ERROR,
2611 JSMSG_REDECLARED_VAR,
2612 JSDefinition::kindString(dn_kind),
2613 name)) {
2614 return NULL;
2618 if (topLevel) {
2619 ALE_SET_DEFN(ale, pn);
2620 pn->pn_defn = true;
2621 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2623 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2624 return NULL;
2626 } else if (topLevel) {
2628 * If this function was used before it was defined, claim the
2629 * pre-created definition node for this function that primaryExpr
2630 * put in tc->lexdeps on first forward reference, and recycle pn.
2632 JSHashEntry **hep;
2634 ale = tc->lexdeps.rawLookup(funAtom, hep);
2635 if (ale) {
2636 JSDefinition *fn = ALE_DEFN(ale);
2638 JS_ASSERT(fn->pn_defn);
2639 fn->pn_type = TOK_FUNCTION;
2640 fn->pn_arity = PN_FUNC;
2641 fn->pn_pos.begin = pn->pn_pos.begin;
2642 fn->pn_body = NULL;
2643 fn->pn_cookie = FREE_UPVAR_COOKIE;
2645 tc->lexdeps.rawRemove(tc->parser, ale, hep);
2646 RecycleTree(pn, tc);
2647 pn = fn;
2650 if (!Define(pn, funAtom, tc))
2651 return NULL;
2655 * A function nested at top level inside another's body needs only a
2656 * local variable to bind its name to its value, and not an activation
2657 * object property (it might also need the activation property, if the
2658 * outer function contains with statements, e.g., but the stack slot
2659 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2660 * JSOP_GETLOCAL bytecode).
2662 if (topLevel) {
2663 pn->pn_dflags |= PND_TOPLEVEL;
2665 if (tc->inFunction()) {
2666 JSLocalKind localKind;
2667 uintN index;
2670 * Define a local in the outer function so that BindNameToSlot
2671 * can properly optimize accesses. Note that we need a local
2672 * variable, not an argument, for the function statement. Thus
2673 * we add a variable even if a parameter with the given name
2674 * already exists.
2676 localKind = js_LookupLocal(context, tc->fun, funAtom, &index);
2677 switch (localKind) {
2678 case JSLOCAL_NONE:
2679 case JSLOCAL_ARG:
2680 index = tc->fun->u.i.nvars;
2681 if (!js_AddLocal(context, tc->fun, funAtom, JSLOCAL_VAR))
2682 return NULL;
2683 /* FALL THROUGH */
2685 case JSLOCAL_VAR:
2686 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2687 pn->pn_dflags |= PND_BOUND;
2688 break;
2690 default:;
2696 JSTreeContext *outertc = tc;
2698 /* Initialize early for possible flags mutation via destructuringExpr. */
2699 JSTreeContext funtc(tc->parser);
2701 JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
2702 if (!funbox)
2703 return NULL;
2705 JSFunction *fun = (JSFunction *) funbox->object;
2707 if (op != JSOP_NOP)
2708 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2710 /* Now parse formal argument list and compute fun->nargs. */
2711 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2712 if (!tokenStream.matchToken(TOK_RP)) {
2713 do {
2714 tt = tokenStream.getToken();
2715 switch (tt) {
2716 #if JS_HAS_DESTRUCTURING
2717 case TOK_LB:
2718 case TOK_LC:
2720 BindData data;
2721 JSParseNode *lhs, *rhs;
2722 jsint slot;
2724 /* See comment below in the TOK_NAME case. */
2725 if (duplicatedArg)
2726 goto report_dup_and_destructuring;
2727 destructuringArg = true;
2730 * A destructuring formal parameter turns into one or more
2731 * local variables initialized from properties of a single
2732 * anonymous positional parameter, so here we must tweak our
2733 * binder and its data.
2735 data.pn = NULL;
2736 data.op = JSOP_DEFVAR;
2737 data.binder = BindDestructuringArg;
2738 lhs = destructuringExpr(&data, tt);
2739 if (!lhs)
2740 return NULL;
2743 * Adjust fun->nargs to count the single anonymous positional
2744 * parameter that is to be destructured.
2746 slot = fun->nargs;
2747 if (!js_AddLocal(context, fun, NULL, JSLOCAL_ARG))
2748 return NULL;
2751 * Synthesize a destructuring assignment from the single
2752 * anonymous positional parameter into the destructuring
2753 * left-hand-side expression and accumulate it in list.
2755 rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
2756 if (!rhs)
2757 return NULL;
2758 rhs->pn_type = TOK_NAME;
2759 rhs->pn_op = JSOP_GETARG;
2760 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2761 rhs->pn_dflags |= PND_BOUND;
2763 item = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2764 if (!item)
2765 return NULL;
2766 if (!list) {
2767 list = ListNode::create(&funtc);
2768 if (!list)
2769 return NULL;
2770 list->pn_type = TOK_COMMA;
2771 list->makeEmpty();
2773 list->append(item);
2774 break;
2776 #endif /* JS_HAS_DESTRUCTURING */
2778 case TOK_NAME:
2780 JSAtom *atom = tokenStream.currentToken().t_atom;
2781 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2782 return NULL;
2783 #ifdef JS_HAS_DESTRUCTURING
2785 * ECMA-262 requires us to support duplicate parameter names, but if the
2786 * parameter list includes destructuring, we consider the code to have
2787 * opted in to higher standards, and forbid duplicates. We may see a
2788 * destructuring parameter later, so always note duplicates now.
2790 * Duplicates are warned about (strict option) or cause errors (strict
2791 * mode code), but we do those tests in one place below, after having
2792 * parsed the body.
2794 if (js_LookupLocal(context, fun, atom, NULL) != JSLOCAL_NONE) {
2795 duplicatedArg = atom;
2796 if (destructuringArg)
2797 goto report_dup_and_destructuring;
2799 #endif
2800 if (!js_AddLocal(context, fun, atom, JSLOCAL_ARG))
2801 return NULL;
2802 break;
2805 default:
2806 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
2807 JSMSG_MISSING_FORMAL);
2808 /* FALL THROUGH */
2809 case TOK_ERROR:
2810 return NULL;
2812 #if JS_HAS_DESTRUCTURING
2813 report_dup_and_destructuring:
2814 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
2815 ReportCompileErrorNumber(context, &tokenStream, dn, JSREPORT_ERROR,
2816 JSMSG_DESTRUCT_DUP_ARG);
2817 return NULL;
2818 #endif
2820 } while (tokenStream.matchToken(TOK_COMMA));
2822 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2825 #if JS_HAS_EXPR_CLOSURES
2826 tt = tokenStream.getToken(TSF_OPERAND);
2827 if (tt != TOK_LC) {
2828 tokenStream.ungetToken();
2829 fun->flags |= JSFUN_EXPR_CLOSURE;
2831 #else
2832 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2833 #endif
2835 body = functionBody();
2836 if (!body)
2837 return NULL;
2839 if (!CheckStrictBinding(context, &funtc, funAtom, pn))
2840 return NULL;
2842 if (!CheckStrictFormals(context, &funtc, fun, pn))
2843 return NULL;
2845 #if JS_HAS_EXPR_CLOSURES
2846 if (tt == TOK_LC)
2847 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2848 else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
2849 return NULL;
2850 #else
2851 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2852 #endif
2853 pn->pn_pos.end = tokenStream.currentToken().pos.end;
2855 #if JS_HAS_DESTRUCTURING
2857 * If there were destructuring formal parameters, prepend the initializing
2858 * comma expression that we synthesized to body. If the body is a lexical
2859 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2860 * parameter destructuring code without bracing the decompilation of the
2861 * function body's lexical scope.
2863 if (list) {
2864 if (body->pn_arity != PN_LIST) {
2865 JSParseNode *block;
2867 block = ListNode::create(outertc);
2868 if (!block)
2869 return NULL;
2870 block->pn_type = TOK_SEQ;
2871 block->pn_pos = body->pn_pos;
2872 block->initList(body);
2874 body = block;
2877 item = UnaryNode::create(outertc);
2878 if (!item)
2879 return NULL;
2881 item->pn_type = TOK_SEMI;
2882 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2883 item->pn_kid = list;
2884 item->pn_next = body->pn_head;
2885 body->pn_head = item;
2886 if (body->pn_tail == &body->pn_head)
2887 body->pn_tail = &item->pn_next;
2888 ++body->pn_count;
2889 body->pn_xflags |= PNX_DESTRUCT;
2891 #endif
2894 * If we collected flags that indicate nested heavyweight functions, or
2895 * this function contains heavyweight-making statements (with statement,
2896 * visible eval call, or assignment to 'arguments'), flag the function as
2897 * heavyweight (requiring a call object per invocation).
2899 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2900 fun->flags |= JSFUN_HEAVYWEIGHT;
2901 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2902 } else {
2904 * If this function is a named statement function not at top-level
2905 * (i.e. not a top-level function definiton or expression), then our
2906 * enclosing function, if any, must be heavyweight.
2908 if (!topLevel && lambda == 0 && funAtom)
2909 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
2912 result = pn;
2913 if (lambda != 0) {
2915 * ECMA ed. 3 standard: function expression, possibly anonymous.
2917 op = JSOP_LAMBDA;
2918 } else if (!funAtom) {
2920 * If this anonymous function definition is *not* embedded within a
2921 * larger expression, we treat it as an expression statement, not as
2922 * a function declaration -- and not as a syntax error (as ECMA-262
2923 * Edition 3 would have it). Backward compatibility must trump all,
2924 * unless JSOPTION_ANONFUNFIX is set.
2926 result = UnaryNode::create(outertc);
2927 if (!result)
2928 return NULL;
2929 result->pn_type = TOK_SEMI;
2930 result->pn_pos = pn->pn_pos;
2931 result->pn_kid = pn;
2932 op = JSOP_LAMBDA;
2933 } else if (!topLevel) {
2935 * ECMA ed. 3 extension: a function expression statement not at the
2936 * top level, e.g., in a compound statement such as the "then" part
2937 * of an "if" statement, binds a closure only if control reaches that
2938 * sub-statement.
2940 op = JSOP_DEFFUN;
2941 } else {
2942 op = JSOP_NOP;
2945 funbox->kids = funtc.functionList;
2947 pn->pn_funbox = funbox;
2948 pn->pn_op = op;
2949 if (pn->pn_body) {
2950 pn->pn_body->append(body);
2951 pn->pn_body->pn_pos = body->pn_pos;
2952 } else {
2953 pn->pn_body = body;
2956 pn->pn_blockid = outertc->blockid();
2958 if (!LeaveFunction(pn, &funtc, funAtom, lambda))
2959 return NULL;
2961 /* If the surrounding function is not strict code, reset the lexer. */
2962 if (!(outertc->flags & TCF_STRICT_MODE_CODE))
2963 tokenStream.setStrictMode(false);
2965 return result;
2968 JSParseNode *
2969 Parser::functionStmt()
2971 return functionDef(0, true);
2974 JSParseNode *
2975 Parser::functionExpr()
2977 return functionDef(JSFUN_LAMBDA, true);
2981 * Recognize Directive Prologue members and directives. Assuming pn
2982 * is a candidate for membership in a directive prologue, return
2983 * true if it is in fact a member. Recognize directives and set
2984 * tc's flags accordingly.
2986 * Note that the following is a strict mode function:
2988 * function foo() {
2989 * "blah" // inserted semi colon
2990 * "blurgh"
2991 * "use\x20loose"
2992 * "use strict"
2995 * That is, a statement can be a Directive Prologue member, even
2996 * if it can't possibly be a directive, now or in the future.
2998 bool
2999 Parser::recognizeDirectivePrologue(JSParseNode *pn)
3001 if (!pn->isDirectivePrologueMember())
3002 return false;
3003 if (pn->isDirective()) {
3004 JSAtom *directive = pn->pn_kid->pn_atom;
3005 if (directive == context->runtime->atomState.useStrictAtom) {
3006 tc->flags |= TCF_STRICT_MODE_CODE;
3007 tokenStream.setStrictMode();
3010 return true;
3014 * Parse the statements in a block, creating a TOK_LC node that lists the
3015 * statements' trees. If called from block-parsing code, the caller must
3016 * match { before and } after.
3018 JSParseNode *
3019 Parser::statements()
3021 JSParseNode *pn, *pn2, *saveBlock;
3022 TokenKind tt;
3023 bool inDirectivePrologue = tc->atTopLevel();
3025 JS_CHECK_RECURSION(context, return NULL);
3027 pn = ListNode::create(tc);
3028 if (!pn)
3029 return NULL;
3030 pn->pn_type = TOK_LC;
3031 pn->makeEmpty();
3032 pn->pn_blockid = tc->blockid();
3033 saveBlock = tc->blockNode;
3034 tc->blockNode = pn;
3036 for (;;) {
3037 tt = tokenStream.peekToken(TSF_OPERAND);
3038 if (tt <= TOK_EOF || tt == TOK_RC) {
3039 if (tt == TOK_ERROR) {
3040 if (tokenStream.isEOF())
3041 tokenStream.setUnexpectedEOF();
3042 return NULL;
3044 break;
3046 pn2 = statement();
3047 if (!pn2) {
3048 if (tokenStream.isEOF())
3049 tokenStream.setUnexpectedEOF();
3050 return NULL;
3053 if (inDirectivePrologue)
3054 inDirectivePrologue = recognizeDirectivePrologue(pn2);
3056 if (pn2->pn_type == TOK_FUNCTION) {
3058 * PNX_FUNCDEFS notifies the emitter that the block contains top-
3059 * level function definitions that should be processed before the
3060 * rest of nodes.
3062 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3063 * is relevant only for function definitions not at top-level,
3064 * which we call function statements.
3066 if (tc->atTopLevel())
3067 pn->pn_xflags |= PNX_FUNCDEFS;
3068 else
3069 tc->flags |= TCF_HAS_FUNCTION_STMT;
3071 pn->append(pn2);
3075 * Handle the case where there was a let declaration under this block. If
3076 * it replaced tc->blockNode with a new block node then we must refresh pn
3077 * and then restore tc->blockNode.
3079 if (tc->blockNode != pn)
3080 pn = tc->blockNode;
3081 tc->blockNode = saveBlock;
3083 pn->pn_pos.end = tokenStream.currentToken().pos.end;
3084 return pn;
3087 JSParseNode *
3088 Parser::condition()
3090 JSParseNode *pn;
3092 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3093 pn = parenExpr(NULL, NULL);
3094 if (!pn)
3095 return NULL;
3096 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3098 /* Check for (a = b) and warn about possible (a == b) mistype. */
3099 if (pn->pn_type == TOK_ASSIGN &&
3100 pn->pn_op == JSOP_NOP &&
3101 !pn->pn_parens &&
3102 !ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_WARNING | JSREPORT_STRICT,
3103 JSMSG_EQUAL_AS_ASSIGN, "")) {
3104 return NULL;
3106 return pn;
3109 static JSBool
3110 MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
3112 JSAtom *label;
3113 TokenKind tt;
3115 tt = ts->peekTokenSameLine();
3116 if (tt == TOK_ERROR)
3117 return JS_FALSE;
3118 if (tt == TOK_NAME) {
3119 (void) ts->getToken();
3120 label = ts->currentToken().t_atom;
3121 } else {
3122 label = NULL;
3124 pn->pn_atom = label;
3125 return JS_TRUE;
3128 static JSBool
3129 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3131 JSParseNode *pn;
3132 JSObject *blockObj;
3133 JSAtomListElement *ale;
3134 jsint n;
3137 * Top-level 'let' is the same as 'var' currently -- this may change in a
3138 * successor standard to ES3.1 that specifies 'let'.
3140 JS_ASSERT(!tc->atTopLevel());
3142 pn = data->pn;
3143 if (!CheckStrictBinding(cx, tc, atom, pn))
3144 return false;
3146 blockObj = tc->blockChain;
3147 ale = tc->decls.lookup(atom);
3148 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3149 const char *name = js_AtomToPrintableString(cx, atom);
3150 if (name) {
3151 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3152 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3153 (ale && ALE_DEFN(ale)->isConst())
3154 ? js_const_str
3155 : js_variable_str,
3156 name);
3158 return JS_FALSE;
3161 n = OBJ_BLOCK_COUNT(cx, blockObj);
3162 if (n == JS_BIT(16)) {
3163 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3164 JSREPORT_ERROR, data->let.overflow);
3165 return JS_FALSE;
3169 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3170 * This is balanced by PopStatement, defined immediately below.
3172 if (!Define(pn, atom, tc, true))
3173 return JS_FALSE;
3176 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3177 * upvar cookie whose skip tells the current static level. The emitter will
3178 * adjust the node's slot based on its stack depth model -- and, for global
3179 * and eval code, Compiler::compileScript will adjust the slot again to
3180 * include script->nfixed.
3182 pn->pn_op = JSOP_GETLOCAL;
3183 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3184 pn->pn_dflags |= PND_LET | PND_BOUND;
3187 * Define the let binding's property before storing pn in a reserved slot,
3188 * since block_reserveSlots depends on blockObj->scope()->entryCount.
3190 if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
3191 return JS_FALSE;
3194 * Store pn temporarily in what would be reserved slots in a cloned block
3195 * object (once the prototype's final population is known, after all 'let'
3196 * bindings for this block have been parsed). We will free these reserved
3197 * slots in jsemit.cpp:EmitEnterBlock.
3199 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3200 if (slot >= blockObj->numSlots() &&
3201 !blockObj->growSlots(cx, slot + 1)) {
3202 return JS_FALSE;
3204 blockObj->scope()->freeslot = slot + 1;
3205 blockObj->setSlot(slot, PRIVATE_TO_JSVAL(pn));
3206 return JS_TRUE;
3209 static void
3210 PopStatement(JSTreeContext *tc)
3212 JSStmtInfo *stmt = tc->topStmt;
3214 if (stmt->flags & SIF_SCOPE) {
3215 JSObject *obj = stmt->blockObj;
3216 JSScope *scope = obj->scope();
3217 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3219 for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
3220 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3222 /* Beware the empty destructuring dummy. */
3223 if (atom == tc->parser->context->runtime->atomState.emptyAtom)
3224 continue;
3225 tc->decls.remove(tc->parser, atom);
3228 js_PopStatement(tc);
3231 static inline bool
3232 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3234 while (stmt->downScope) {
3235 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3236 if (!stmt)
3237 return false;
3238 if (stmt->type == STMT_BLOCK)
3239 return true;
3241 return false;
3245 * If we are generating global or eval-called-from-global code, bind a "gvar"
3246 * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3247 * global variable access by memoizing name-to-slot mappings during execution
3248 * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3249 * can't be done due to a pre-existing property of the same name as the var or
3250 * const but incompatible attributes/getter/setter/etc, these ops devolve to
3251 * JSOP_NAME, etc.
3253 * For now, don't try to lookup eval frame variables at compile time. This is
3254 * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3255 * gets its own script and frame. The eval-from-function-code case is harder,
3256 * since functions do not atomize gvars and then reserve their atom indexes as
3257 * stack frame slots.
3259 static bool
3260 BindGvar(JSParseNode *pn, JSTreeContext *tc, bool inWith = false)
3262 JS_ASSERT(pn->pn_op == JSOP_NAME);
3263 JS_ASSERT(!tc->inFunction());
3265 if (tc->compiling() && !tc->parser->callerFrame) {
3266 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3268 /* Index pn->pn_atom so we can map fast global number to name. */
3269 JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
3270 if (!ale)
3271 return false;
3273 /* Defend against cg->ngvars 16-bit overflow. */
3274 uintN slot = ALE_INDEX(ale);
3275 if ((slot + 1) >> 16)
3276 return true;
3278 if ((uint16)(slot + 1) > cg->ngvars)
3279 cg->ngvars = (uint16)(slot + 1);
3281 if (!inWith) {
3282 pn->pn_op = JSOP_GETGVAR;
3283 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3284 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3288 return true;
3291 static JSBool
3292 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3294 JSParseNode *pn = data->pn;
3296 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3297 pn->pn_op = JSOP_NAME;
3299 if (!CheckStrictBinding(cx, tc, atom, pn))
3300 return false;
3302 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3304 if (stmt && stmt->type == STMT_WITH) {
3305 data->fresh = false;
3306 return tc->inFunction() || BindGvar(pn, tc, true);
3309 JSAtomListElement *ale = tc->decls.lookup(atom);
3310 JSOp op = data->op;
3312 if (stmt || ale) {
3313 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3314 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3315 const char *name;
3317 if (dn_kind == JSDefinition::ARG) {
3318 name = js_AtomToPrintableString(cx, atom);
3319 if (!name)
3320 return JS_FALSE;
3322 if (op == JSOP_DEFCONST) {
3323 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3324 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3325 name);
3326 return JS_FALSE;
3328 if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3329 JSREPORT_WARNING | JSREPORT_STRICT,
3330 JSMSG_VAR_HIDES_ARG, name)) {
3331 return JS_FALSE;
3333 } else {
3334 bool error = (op == JSOP_DEFCONST ||
3335 dn_kind == JSDefinition::CONST ||
3336 (dn_kind == JSDefinition::LET &&
3337 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3339 if (JS_HAS_STRICT_OPTION(cx)
3340 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3341 : error) {
3342 name = js_AtomToPrintableString(cx, atom);
3343 if (!name ||
3344 !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3345 !error
3346 ? JSREPORT_WARNING | JSREPORT_STRICT
3347 : JSREPORT_ERROR,
3348 JSMSG_REDECLARED_VAR,
3349 JSDefinition::kindString(dn_kind),
3350 name)) {
3351 return JS_FALSE;
3357 if (!ale) {
3358 if (!Define(pn, atom, tc))
3359 return JS_FALSE;
3360 } else {
3362 * A var declaration never recreates an existing binding, it restates
3363 * it and possibly reinitializes its value. Beware that if pn becomes a
3364 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3365 * const (typically a const would ;-), then pn must be rewritten into a
3366 * TOK_ASSIGN node. See Variables, further below.
3368 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3369 * There the x definition is hoisted but the x = 2 assignment mutates
3370 * the block-local binding of x.
3372 JSDefinition *dn = ALE_DEFN(ale);
3374 data->fresh = false;
3376 if (!pn->pn_used) {
3377 /* Make pnu be a fresh name node that uses dn. */
3378 JSParseNode *pnu = pn;
3380 if (pn->pn_defn) {
3381 pnu = NameNode::create(atom, tc);
3382 if (!pnu)
3383 return JS_FALSE;
3386 LinkUseToDef(pnu, dn, tc);
3387 pnu->pn_op = JSOP_NAME;
3390 while (dn->kind() == JSDefinition::LET) {
3391 do {
3392 ale = ALE_NEXT(ale);
3393 } while (ale && ALE_ATOM(ale) != atom);
3394 if (!ale)
3395 break;
3396 dn = ALE_DEFN(ale);
3399 if (ale) {
3400 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3401 dn->kind() == JSDefinition::CONST);
3402 return JS_TRUE;
3406 * A var or const that is shadowed by one or more let bindings of the
3407 * same name, but that has not been declared until this point, must be
3408 * hoisted above the let bindings.
3410 if (!pn->pn_defn) {
3411 JSHashEntry **hep;
3413 ale = tc->lexdeps.rawLookup(atom, hep);
3414 if (ale) {
3415 pn = ALE_DEFN(ale);
3416 tc->lexdeps.rawRemove(tc->parser, ale, hep);
3417 } else {
3418 JSParseNode *pn2 = NameNode::create(atom, tc);
3419 if (!pn2)
3420 return JS_FALSE;
3422 /* The token stream may be past the location for pn. */
3423 pn2->pn_type = TOK_NAME;
3424 pn2->pn_pos = pn->pn_pos;
3425 pn = pn2;
3427 pn->pn_op = JSOP_NAME;
3430 ale = tc->decls.add(tc->parser, atom, JSAtomList::HOIST);
3431 if (!ale)
3432 return JS_FALSE;
3433 ALE_SET_DEFN(ale, pn);
3434 pn->pn_defn = true;
3435 pn->pn_dflags &= ~PND_PLACEHOLDER;
3438 if (data->op == JSOP_DEFCONST)
3439 pn->pn_dflags |= PND_CONST;
3441 if (!tc->inFunction())
3442 return BindGvar(pn, tc);
3444 if (atom == cx->runtime->atomState.argumentsAtom) {
3445 pn->pn_op = JSOP_ARGUMENTS;
3446 pn->pn_dflags |= PND_BOUND;
3447 return JS_TRUE;
3450 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3451 if (localKind == JSLOCAL_NONE) {
3453 * Property not found in current variable scope: we have not seen this
3454 * variable before. Define a new local variable by adding a property to
3455 * the function's scope and allocating one slot in the function's vars
3456 * frame. Any locals declared in a with statement body are handled at
3457 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3458 * and heavyweight-function-local vars.
3460 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3462 uintN index = tc->fun->u.i.nvars;
3463 if (!BindLocalVariable(cx, tc->fun, atom, localKind, false))
3464 return JS_FALSE;
3465 pn->pn_op = JSOP_GETLOCAL;
3466 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3467 pn->pn_dflags |= PND_BOUND;
3468 return JS_TRUE;
3471 if (localKind == JSLOCAL_ARG) {
3472 /* We checked errors and strict warnings earlier -- see above. */
3473 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3474 } else {
3475 /* Not an argument, must be a redeclared local var. */
3476 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3478 return JS_TRUE;
3481 static JSBool
3482 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3484 JSParseNode *pn2;
3486 JS_ASSERT(pn->pn_arity == PN_LIST);
3487 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3488 pn2 = pn->pn_head;
3489 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3490 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
3491 return JS_FALSE;
3493 pn->pn_op = JSOP_SETCALL;
3494 return JS_TRUE;
3497 static void
3498 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
3500 if (pn->pn_used) {
3501 JSDefinition *dn = pn->pn_lexdef;
3504 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
3505 * occur as direct kids of the same block with no forward refs to x.
3507 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
3508 dn->isBlockChild() &&
3509 pn->isBlockChild() &&
3510 dn->pn_blockid == pn->pn_blockid &&
3511 dn->pn_pos.end <= pn->pn_pos.begin &&
3512 dn->dn_uses == pn) {
3513 dflag = PND_INITIALIZED;
3516 dn->pn_dflags |= dflag;
3518 if (dn->frameLevel() != tc->staticLevel) {
3520 * The above condition takes advantage of the all-ones nature of
3521 * FREE_UPVAR_COOKIE, and the reserved level FREE_STATIC_LEVEL.
3522 * We make a stronger assertion by excluding FREE_UPVAR_COOKIE.
3524 JS_ASSERT_IF(dn->pn_cookie != FREE_UPVAR_COOKIE,
3525 dn->frameLevel() < tc->staticLevel);
3526 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
3530 pn->pn_dflags |= dflag;
3532 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
3533 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3536 #if JS_HAS_DESTRUCTURING
3538 static JSBool
3539 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
3540 JSTreeContext *tc)
3542 JSAtom *atom;
3545 * Destructuring is a form of assignment, so just as for an initialized
3546 * simple variable, we must check for assignment to 'arguments' and flag
3547 * the enclosing function (if any) as heavyweight.
3549 JS_ASSERT(pn->pn_type == TOK_NAME);
3550 atom = pn->pn_atom;
3551 if (atom == cx->runtime->atomState.argumentsAtom)
3552 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3554 data->pn = pn;
3555 if (!data->binder(cx, data, atom, tc))
3556 return JS_FALSE;
3559 * Select the appropriate name-setting opcode, respecting eager selection
3560 * done by the data->binder function.
3562 if (pn->pn_dflags & PND_BOUND) {
3563 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
3564 ? JSOP_SETNAME
3565 : (pn->pn_dflags & PND_GVAR)
3566 ? JSOP_SETGVAR
3567 : JSOP_SETLOCAL;
3568 } else {
3569 pn->pn_op = (data->op == JSOP_DEFCONST)
3570 ? JSOP_SETCONST
3571 : JSOP_SETNAME;
3574 if (data->op == JSOP_DEFCONST)
3575 pn->pn_dflags |= PND_CONST;
3577 NoteLValue(cx, pn, tc, PND_INITIALIZED);
3578 return JS_TRUE;
3582 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
3583 * LHS expression except a destructuring initialiser, and R is on the stack.
3584 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
3585 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
3586 * then push its property name QN. At this point the stack looks like
3588 * [... R, R[P], QB, QN]
3590 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
3591 * its operands with left-hand side above right-hand side:
3593 * [rval, lval, xval]
3595 * and pops all three values, setting lval[xval] = rval. But we cannot select
3596 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
3597 * which can be optimized further. So we select JSOP_SETNAME.
3599 static JSBool
3600 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3602 switch (pn->pn_type) {
3603 case TOK_NAME:
3604 NoteLValue(cx, pn, tc);
3605 /* FALL THROUGH */
3607 case TOK_DOT:
3608 case TOK_LB:
3609 pn->pn_op = JSOP_SETNAME;
3610 break;
3612 case TOK_LP:
3613 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
3614 return JS_FALSE;
3615 break;
3617 #if JS_HAS_XML_SUPPORT
3618 case TOK_UNARYOP:
3619 if (pn->pn_op == JSOP_XMLNAME) {
3620 pn->pn_op = JSOP_BINDXMLNAME;
3621 break;
3623 /* FALL THROUGH */
3624 #endif
3626 default:
3627 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3628 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
3629 return JS_FALSE;
3632 return JS_TRUE;
3635 typedef struct FindPropValData {
3636 uint32 numvars; /* # of destructuring vars in left side */
3637 uint32 maxstep; /* max # of steps searching right side */
3638 JSDHashTable table; /* hash table for O(1) right side search */
3639 } FindPropValData;
3641 typedef struct FindPropValEntry {
3642 JSDHashEntryHdr hdr;
3643 JSParseNode *pnkey;
3644 JSParseNode *pnval;
3645 } FindPropValEntry;
3647 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
3648 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
3649 ((pnkey)->pn_type == TOK_NUMBER || \
3650 (pnkey)->pn_type == TOK_STRING || \
3651 (pnkey)->pn_type == TOK_NAME)) || \
3652 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
3654 static JSDHashNumber
3655 HashFindPropValKey(JSDHashTable *table, const void *key)
3657 const JSParseNode *pnkey = (const JSParseNode *)key;
3659 ASSERT_VALID_PROPERTY_KEY(pnkey);
3660 return (pnkey->pn_type == TOK_NUMBER)
3661 ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
3662 : ATOM_HASH(pnkey->pn_atom);
3665 static JSBool
3666 MatchFindPropValEntry(JSDHashTable *table,
3667 const JSDHashEntryHdr *entry,
3668 const void *key)
3670 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
3671 const JSParseNode *pnkey = (const JSParseNode *)key;
3673 ASSERT_VALID_PROPERTY_KEY(pnkey);
3674 return pnkey->pn_type == fpve->pnkey->pn_type &&
3675 ((pnkey->pn_type == TOK_NUMBER)
3676 ? pnkey->pn_dval == fpve->pnkey->pn_dval
3677 : pnkey->pn_atom == fpve->pnkey->pn_atom);
3680 static const JSDHashTableOps FindPropValOps = {
3681 JS_DHashAllocTable,
3682 JS_DHashFreeTable,
3683 HashFindPropValKey,
3684 MatchFindPropValEntry,
3685 JS_DHashMoveEntryStub,
3686 JS_DHashClearEntryStub,
3687 JS_DHashFinalizeStub,
3688 NULL
3691 #define STEP_HASH_THRESHOLD 10
3692 #define BIG_DESTRUCTURING 5
3693 #define BIG_OBJECT_INIT 20
3695 static JSParseNode *
3696 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
3698 FindPropValEntry *entry;
3699 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
3700 uint32 step;
3702 /* If we have a hash table, use it as the sole source of truth. */
3703 if (data->table.ops) {
3704 entry = (FindPropValEntry *)
3705 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
3706 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
3709 /* If pn is not an object initialiser node, we can't do anything here. */
3710 if (pn->pn_type != TOK_RC)
3711 return NULL;
3714 * We must search all the way through pn's list, to handle the case of an
3715 * id duplicated for two or more property initialisers.
3717 pnhit = NULL;
3718 step = 0;
3719 ASSERT_VALID_PROPERTY_KEY(pnid);
3720 pnhead = pn->pn_head;
3721 if (pnid->pn_type == TOK_NUMBER) {
3722 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3723 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3724 if (pnprop->pn_op == JSOP_NOP) {
3725 pnkey = pnprop->pn_left;
3726 ASSERT_VALID_PROPERTY_KEY(pnkey);
3727 if (pnkey->pn_type == TOK_NUMBER &&
3728 pnkey->pn_dval == pnid->pn_dval) {
3729 pnhit = pnprop;
3731 ++step;
3734 } else {
3735 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
3736 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3737 if (pnprop->pn_op == JSOP_NOP) {
3738 pnkey = pnprop->pn_left;
3739 ASSERT_VALID_PROPERTY_KEY(pnkey);
3740 if (pnkey->pn_type == pnid->pn_type &&
3741 pnkey->pn_atom == pnid->pn_atom) {
3742 pnhit = pnprop;
3744 ++step;
3748 if (!pnhit)
3749 return NULL;
3751 /* Hit via full search -- see whether it's time to create the hash table. */
3752 JS_ASSERT(!data->table.ops);
3753 if (step > data->maxstep) {
3754 data->maxstep = step;
3755 if (step >= STEP_HASH_THRESHOLD &&
3756 data->numvars >= BIG_DESTRUCTURING &&
3757 pn->pn_count >= BIG_OBJECT_INIT &&
3758 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
3759 sizeof(FindPropValEntry),
3760 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
3762 for (pn = pnhead; pn; pn = pn->pn_next) {
3763 JS_ASSERT(pnprop->pn_type == TOK_COLON);
3764 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
3765 entry = (FindPropValEntry *)
3766 JS_DHashTableOperate(&data->table, pn->pn_left,
3767 JS_DHASH_ADD);
3768 entry->pnval = pn->pn_right;
3772 return pnhit->pn_right;
3776 * Destructuring patterns can appear in two kinds of contexts:
3778 * - assignment-like: assignment expressions and |for| loop heads. In
3779 * these cases, the patterns' property value positions can be
3780 * arbitrary lvalue expressions; the destructuring is just a fancy
3781 * assignment.
3783 * - declaration-like: |var| and |let| declarations, functions' formal
3784 * parameter lists, |catch| clauses, and comprehension tails. In
3785 * these cases, the patterns' property value positions must be
3786 * simple names; the destructuring defines them as new variables.
3788 * In both cases, other code parses the pattern as an arbitrary
3789 * primaryExpr, and then, here in CheckDestructuring, verify that the
3790 * tree is a valid destructuring expression.
3792 * In assignment-like contexts, we parse the pattern with the
3793 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
3794 * pattern are parsed normally. primaryExpr links variable references
3795 * into the appropriate use chains; creates placeholder definitions;
3796 * and so on. CheckDestructuring is called with |data| NULL (since we
3797 * won't be binding any new names), and we specialize lvalues as
3798 * appropriate. If right is NULL, we just check for well-formed lvalues.
3800 * In declaration-like contexts, the normal variable reference
3801 * processing would just be an obstruction, because we're going to
3802 * define the names that appear in the property value positions as new
3803 * variables anyway. In this case, we parse the pattern with
3804 * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
3805 * whatever name nodes it creates unconnected. Then, here in
3806 * CheckDestructuring, we require the pattern's property value
3807 * positions to be simple names, and define them as appropriate to the
3808 * context. For these calls, |data| points to the right sort of
3809 * BindData.
3811 * See also UndominateInitializers, immediately below. If you change
3812 * either of these functions, you might have to change the other to
3813 * match.
3815 static JSBool
3816 CheckDestructuring(JSContext *cx, BindData *data,
3817 JSParseNode *left, JSParseNode *right,
3818 JSTreeContext *tc)
3820 JSBool ok;
3821 FindPropValData fpvd;
3822 JSParseNode *lhs, *rhs, *pn, *pn2;
3824 if (left->pn_type == TOK_ARRAYCOMP) {
3825 ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
3826 JSMSG_ARRAY_COMP_LEFTSIDE);
3827 return JS_FALSE;
3830 #if JS_HAS_DESTRUCTURING_SHORTHAND
3831 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3832 ReportCompileErrorNumber(cx, TS(tc->parser), right, JSREPORT_ERROR,
3833 JSMSG_BAD_OBJECT_INIT);
3834 return JS_FALSE;
3836 #endif
3838 fpvd.table.ops = NULL;
3839 lhs = left->pn_head;
3840 if (left->pn_type == TOK_RB) {
3841 rhs = (right && right->pn_type == left->pn_type)
3842 ? right->pn_head
3843 : NULL;
3845 while (lhs) {
3846 pn = lhs, pn2 = rhs;
3848 /* Nullary comma is an elision; binary comma is an expression.*/
3849 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
3850 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3851 ok = CheckDestructuring(cx, data, pn, pn2, tc);
3852 } else {
3853 if (data) {
3854 if (pn->pn_type != TOK_NAME)
3855 goto no_var_name;
3857 ok = BindDestructuringVar(cx, data, pn, tc);
3858 } else {
3859 ok = BindDestructuringLHS(cx, pn, tc);
3862 if (!ok)
3863 goto out;
3866 lhs = lhs->pn_next;
3867 if (rhs)
3868 rhs = rhs->pn_next;
3870 } else {
3871 JS_ASSERT(left->pn_type == TOK_RC);
3872 fpvd.numvars = left->pn_count;
3873 fpvd.maxstep = 0;
3874 rhs = NULL;
3876 while (lhs) {
3877 JS_ASSERT(lhs->pn_type == TOK_COLON);
3878 pn = lhs->pn_right;
3880 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3881 if (right)
3882 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
3883 ok = CheckDestructuring(cx, data, pn, rhs, tc);
3884 } else if (data) {
3885 if (pn->pn_type != TOK_NAME)
3886 goto no_var_name;
3888 ok = BindDestructuringVar(cx, data, pn, tc);
3889 } else {
3890 ok = BindDestructuringLHS(cx, pn, tc);
3892 if (!ok)
3893 goto out;
3895 lhs = lhs->pn_next;
3900 * The catch/finally handler implementation in the interpreter assumes
3901 * that any operation that introduces a new scope (like a "let" or "with"
3902 * block) increases the stack depth. This way, it is possible to restore
3903 * the scope chain based on stack depth of the handler alone. "let" with
3904 * an empty destructuring pattern like in
3906 * let [] = 1;
3908 * would violate this assumption as the there would be no let locals to
3909 * store on the stack. To satisfy it we add an empty property to such
3910 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
3911 * slots, would be always positive.
3913 * Note that we add such a property even if the block has locals due to
3914 * later let declarations in it. We optimize for code simplicity here,
3915 * not the fastest runtime performance with empty [] or {}.
3917 if (data &&
3918 data->binder == BindLet &&
3919 OBJ_BLOCK_COUNT(cx, tc->blockChain) == 0) {
3920 ok = !!js_DefineNativeProperty(cx, tc->blockChain,
3921 ATOM_TO_JSID(cx->runtime->
3922 atomState.emptyAtom),
3923 JSVAL_VOID, NULL, NULL,
3924 JSPROP_ENUMERATE |
3925 JSPROP_PERMANENT |
3926 JSPROP_SHARED,
3927 JSScopeProperty::HAS_SHORTID, 0, NULL);
3928 if (!ok)
3929 goto out;
3932 ok = JS_TRUE;
3934 out:
3935 if (fpvd.table.ops)
3936 JS_DHashTableFinish(&fpvd.table);
3937 return ok;
3939 no_var_name:
3940 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
3941 JSMSG_NO_VARIABLE_NAME);
3942 ok = JS_FALSE;
3943 goto out;
3947 * This is a greatly pared down version of CheckDestructuring that extends the
3948 * pn_pos.end source coordinate of each name in a destructuring binding such as
3950 * var [x, y] = [function () y, 42];
3952 * to cover its corresponding initializer, so that the initialized binding does
3953 * not appear to dominate any closures in its initializer. See bug 496134.
3955 * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
3956 * very precise. With one-pass SSA construction from structured source code
3957 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
3958 * Languages", Brandis and Mössenböck), we could do much better.
3960 * See CheckDestructuring, immediately above. If you change either of these
3961 * functions, you might have to change the other to match.
3963 static JSBool
3964 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
3966 FindPropValData fpvd;
3967 JSParseNode *lhs, *rhs;
3969 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
3970 JS_ASSERT(right);
3972 #if JS_HAS_DESTRUCTURING_SHORTHAND
3973 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
3974 ReportCompileErrorNumber(tc->parser->context, TS(tc->parser), right, JSREPORT_ERROR,
3975 JSMSG_BAD_OBJECT_INIT);
3976 return JS_FALSE;
3978 #endif
3980 if (right->pn_type != left->pn_type)
3981 return JS_TRUE;
3983 fpvd.table.ops = NULL;
3984 lhs = left->pn_head;
3985 if (left->pn_type == TOK_RB) {
3986 rhs = right->pn_head;
3988 while (lhs && rhs) {
3989 /* Nullary comma is an elision; binary comma is an expression.*/
3990 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
3991 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
3992 if (!UndominateInitializers(lhs, rhs, tc))
3993 return JS_FALSE;
3994 } else {
3995 lhs->pn_pos.end = rhs->pn_pos.end;
3999 lhs = lhs->pn_next;
4000 rhs = rhs->pn_next;
4002 } else {
4003 JS_ASSERT(left->pn_type == TOK_RC);
4004 fpvd.numvars = left->pn_count;
4005 fpvd.maxstep = 0;
4007 while (lhs) {
4008 JS_ASSERT(lhs->pn_type == TOK_COLON);
4009 JSParseNode *pn = lhs->pn_right;
4011 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4012 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4013 if (rhs && !UndominateInitializers(pn, rhs, tc))
4014 return JS_FALSE;
4015 } else {
4016 if (rhs)
4017 pn->pn_pos.end = rhs->pn_pos.end;
4020 lhs = lhs->pn_next;
4023 return JS_TRUE;
4026 JSParseNode *
4027 Parser::destructuringExpr(BindData *data, TokenKind tt)
4029 JSParseNode *pn;
4031 tc->flags |= TCF_DECL_DESTRUCTURING;
4032 pn = primaryExpr(tt, JS_FALSE);
4033 tc->flags &= ~TCF_DECL_DESTRUCTURING;
4034 if (!pn)
4035 return NULL;
4036 if (!CheckDestructuring(context, data, pn, NULL, tc))
4037 return NULL;
4038 return pn;
4042 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4043 * This function assumes the cloned tree is for use in the same statement and
4044 * binding context as the original tree.
4046 static JSParseNode *
4047 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4049 JSParseNode *pn, *pn2, *opn2;
4051 pn = NewOrRecycledNode(tc);
4052 if (!pn)
4053 return NULL;
4054 pn->pn_type = opn->pn_type;
4055 pn->pn_pos = opn->pn_pos;
4056 pn->pn_op = opn->pn_op;
4057 pn->pn_used = opn->pn_used;
4058 pn->pn_defn = opn->pn_defn;
4059 pn->pn_arity = opn->pn_arity;
4060 pn->pn_parens = opn->pn_parens;
4062 switch (pn->pn_arity) {
4063 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4065 case PN_FUNC:
4066 NULLCHECK(pn->pn_funbox =
4067 tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
4068 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4069 pn->pn_cookie = opn->pn_cookie;
4070 pn->pn_dflags = opn->pn_dflags;
4071 pn->pn_blockid = opn->pn_blockid;
4072 break;
4074 case PN_LIST:
4075 pn->makeEmpty();
4076 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4077 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4078 pn->append(pn2);
4080 pn->pn_xflags = opn->pn_xflags;
4081 break;
4083 case PN_TERNARY:
4084 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4085 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4086 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4087 break;
4089 case PN_BINARY:
4090 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4091 if (opn->pn_right != opn->pn_left)
4092 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4093 else
4094 pn->pn_right = pn->pn_left;
4095 pn->pn_val = opn->pn_val;
4096 pn->pn_iflags = opn->pn_iflags;
4097 break;
4099 case PN_UNARY:
4100 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4101 pn->pn_num = opn->pn_num;
4102 pn->pn_hidden = opn->pn_hidden;
4103 break;
4105 case PN_NAME:
4106 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4107 pn->pn_u = opn->pn_u;
4108 if (opn->pn_used) {
4110 * The old name is a use of its pn_lexdef. Make the clone also be a
4111 * use of that definition.
4113 JSDefinition *dn = pn->pn_lexdef;
4115 pn->pn_link = dn->dn_uses;
4116 dn->dn_uses = pn;
4117 } else if (opn->pn_expr) {
4118 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4121 * If the old name is a definition, the new one has pn_defn set.
4122 * Make the old name a use of the new node.
4124 if (opn->pn_defn) {
4125 opn->pn_defn = false;
4126 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4129 break;
4131 case PN_NAMESET:
4132 pn->pn_names = opn->pn_names;
4133 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4134 break;
4136 case PN_NULLARY:
4137 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4138 pn->pn_u = opn->pn_u;
4139 break;
4141 #undef NULLCHECK
4143 return pn;
4146 #endif /* JS_HAS_DESTRUCTURING */
4148 extern const char js_with_statement_str[];
4150 static JSParseNode *
4151 ContainsStmt(JSParseNode *pn, TokenKind tt)
4153 JSParseNode *pn2, *pnt;
4155 if (!pn)
4156 return NULL;
4157 if (PN_TYPE(pn) == tt)
4158 return pn;
4159 switch (pn->pn_arity) {
4160 case PN_LIST:
4161 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4162 pnt = ContainsStmt(pn2, tt);
4163 if (pnt)
4164 return pnt;
4166 break;
4167 case PN_TERNARY:
4168 pnt = ContainsStmt(pn->pn_kid1, tt);
4169 if (pnt)
4170 return pnt;
4171 pnt = ContainsStmt(pn->pn_kid2, tt);
4172 if (pnt)
4173 return pnt;
4174 return ContainsStmt(pn->pn_kid3, tt);
4175 case PN_BINARY:
4177 * Limit recursion if pn is a binary expression, which can't contain a
4178 * var statement.
4180 if (pn->pn_op != JSOP_NOP)
4181 return NULL;
4182 pnt = ContainsStmt(pn->pn_left, tt);
4183 if (pnt)
4184 return pnt;
4185 return ContainsStmt(pn->pn_right, tt);
4186 case PN_UNARY:
4187 if (pn->pn_op != JSOP_NOP)
4188 return NULL;
4189 return ContainsStmt(pn->pn_kid, tt);
4190 case PN_NAME:
4191 return ContainsStmt(pn->maybeExpr(), tt);
4192 case PN_NAMESET:
4193 return ContainsStmt(pn->pn_tree, tt);
4194 default:;
4196 return NULL;
4199 JSParseNode *
4200 Parser::returnOrYield(bool useAssignExpr)
4202 TokenKind tt, tt2;
4203 JSParseNode *pn, *pn2;
4205 tt = tokenStream.currentToken().type;
4206 if (tt == TOK_RETURN && !tc->inFunction()) {
4207 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
4208 JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4209 return NULL;
4212 pn = UnaryNode::create(tc);
4213 if (!pn)
4214 return NULL;
4216 #if JS_HAS_GENERATORS
4217 if (tt == TOK_YIELD)
4218 tc->flags |= TCF_FUN_IS_GENERATOR;
4219 #endif
4221 /* This is ugly, but we don't want to require a semicolon. */
4222 tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
4223 if (tt2 == TOK_ERROR)
4224 return NULL;
4226 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4227 #if JS_HAS_GENERATORS
4228 && (tt != TOK_YIELD ||
4229 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4230 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4231 #endif
4233 pn2 = useAssignExpr ? assignExpr() : expr();
4234 if (!pn2)
4235 return NULL;
4236 #if JS_HAS_GENERATORS
4237 if (tt == TOK_RETURN)
4238 #endif
4239 tc->flags |= TCF_RETURN_EXPR;
4240 pn->pn_pos.end = pn2->pn_pos.end;
4241 pn->pn_kid = pn2;
4242 } else {
4243 #if JS_HAS_GENERATORS
4244 if (tt == TOK_RETURN)
4245 #endif
4246 tc->flags |= TCF_RETURN_VOID;
4249 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4250 /* As in Python (see PEP-255), disallow return v; in generators. */
4251 ReportBadReturn(context, tc, JSREPORT_ERROR,
4252 JSMSG_BAD_GENERATOR_RETURN,
4253 JSMSG_BAD_ANON_GENERATOR_RETURN);
4254 return NULL;
4257 if (JS_HAS_STRICT_OPTION(context) &&
4258 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4259 !ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4260 JSMSG_NO_RETURN_VALUE,
4261 JSMSG_ANON_NO_RETURN_VALUE)) {
4262 return NULL;
4265 return pn;
4268 static JSParseNode *
4269 PushLexicalScope(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
4270 JSStmtInfo *stmt)
4272 JSParseNode *pn;
4273 JSObject *obj;
4274 JSObjectBox *blockbox;
4276 pn = LexicalScopeNode::create(tc);
4277 if (!pn)
4278 return NULL;
4280 obj = js_NewBlockObject(cx);
4281 if (!obj)
4282 return NULL;
4284 blockbox = tc->parser->newObjectBox(obj);
4285 if (!blockbox)
4286 return NULL;
4288 js_PushBlockScope(tc, stmt, obj, -1);
4289 pn->pn_type = TOK_LEXICALSCOPE;
4290 pn->pn_op = JSOP_LEAVEBLOCK;
4291 pn->pn_objbox = blockbox;
4292 pn->pn_cookie = FREE_UPVAR_COOKIE;
4293 pn->pn_dflags = 0;
4294 if (!GenerateBlockId(tc, stmt->blockid))
4295 return NULL;
4296 pn->pn_blockid = stmt->blockid;
4297 return pn;
4300 #if JS_HAS_BLOCK_SCOPE
4302 JSParseNode *
4303 Parser::letBlock(JSBool statement)
4305 JSParseNode *pn, *pnblock, *pnlet;
4306 JSStmtInfo stmtInfo;
4308 JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
4310 /* Create the let binary node. */
4311 pnlet = BinaryNode::create(tc);
4312 if (!pnlet)
4313 return NULL;
4315 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4317 /* This is a let block or expression of the form: let (a, b, c) .... */
4318 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
4319 if (!pnblock)
4320 return NULL;
4321 pn = pnblock;
4322 pn->pn_expr = pnlet;
4324 pnlet->pn_left = variables(true);
4325 if (!pnlet->pn_left)
4326 return NULL;
4327 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4329 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4331 if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
4333 * If this is really an expression in let statement guise, then we
4334 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4335 * the return value of the expression.
4337 pn = UnaryNode::create(tc);
4338 if (!pn)
4339 return NULL;
4340 pn->pn_type = TOK_SEMI;
4341 pn->pn_num = -1;
4342 pn->pn_kid = pnblock;
4344 statement = JS_FALSE;
4347 if (statement) {
4348 pnlet->pn_right = statements();
4349 if (!pnlet->pn_right)
4350 return NULL;
4351 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4352 } else {
4354 * Change pnblock's opcode to the variant that propagates the last
4355 * result down after popping the block, and clear statement.
4357 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4358 pnlet->pn_right = assignExpr();
4359 if (!pnlet->pn_right)
4360 return NULL;
4363 PopStatement(tc);
4364 return pn;
4367 #endif /* JS_HAS_BLOCK_SCOPE */
4369 static bool
4370 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4372 js_PushStatement(tc, stmt, type, -1);
4373 return GenerateBlockId(tc, stmt->blockid);
4376 static JSParseNode *
4377 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4379 JSParseNode *pn = NULL;
4381 JSAtomListElement *ale = tc->decls.lookup(atom);
4382 if (ale) {
4383 pn = ALE_DEFN(ale);
4384 JS_ASSERT(!pn->isPlaceholder());
4385 } else {
4386 ale = tc->lexdeps.lookup(atom);
4387 if (ale) {
4388 pn = ALE_DEFN(ale);
4389 JS_ASSERT(pn->isPlaceholder());
4393 if (pn) {
4394 JS_ASSERT(pn->pn_defn);
4397 * A let binding at top level becomes a var before we get here, so if
4398 * pn and tc have the same blockid then that id must not be the bodyid.
4399 * If pn is a forward placeholder definition from the same or a higher
4400 * block then we claim it.
4402 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4403 pn->pn_blockid != tc->bodyid);
4405 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4406 if (let)
4407 pn->pn_blockid = tc->blockid();
4409 tc->lexdeps.remove(tc->parser, atom);
4410 return pn;
4414 /* Make a new node for this declarator name (or destructuring pattern). */
4415 pn = NameNode::create(atom, tc);
4416 if (!pn)
4417 return NULL;
4418 return pn;
4421 #if JS_HAS_BLOCK_SCOPE
4422 static bool
4423 RebindLets(JSParseNode *pn, JSTreeContext *tc)
4425 if (!pn)
4426 return true;
4428 switch (pn->pn_arity) {
4429 case PN_LIST:
4430 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
4431 RebindLets(pn2, tc);
4432 break;
4434 case PN_TERNARY:
4435 RebindLets(pn->pn_kid1, tc);
4436 RebindLets(pn->pn_kid2, tc);
4437 RebindLets(pn->pn_kid3, tc);
4438 break;
4440 case PN_BINARY:
4441 RebindLets(pn->pn_left, tc);
4442 RebindLets(pn->pn_right, tc);
4443 break;
4445 case PN_UNARY:
4446 RebindLets(pn->pn_kid, tc);
4447 break;
4449 case PN_FUNC:
4450 RebindLets(pn->pn_body, tc);
4451 break;
4453 case PN_NAME:
4454 RebindLets(pn->maybeExpr(), tc);
4456 if (pn->pn_defn) {
4457 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
4458 } else if (pn->pn_used) {
4459 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
4460 ForgetUse(pn);
4462 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
4463 if (ale) {
4464 while ((ale = ALE_NEXT(ale)) != NULL) {
4465 if (ALE_ATOM(ale) == pn->pn_atom) {
4466 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4467 return true;
4472 ale = tc->lexdeps.lookup(pn->pn_atom);
4473 if (!ale) {
4474 ale = MakePlaceholder(pn, tc);
4475 if (!ale)
4476 return NULL;
4478 JSDefinition *dn = ALE_DEFN(ale);
4479 dn->pn_type = TOK_NAME;
4480 dn->pn_op = JSOP_NOP;
4482 LinkUseToDef(pn, ALE_DEFN(ale), tc);
4485 break;
4487 case PN_NAMESET:
4488 RebindLets(pn->pn_tree, tc);
4489 break;
4492 return true;
4494 #endif /* JS_HAS_BLOCK_SCOPE */
4496 JSParseNode *
4497 Parser::statement()
4499 TokenKind tt;
4500 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
4501 JSStmtInfo stmtInfo, *stmt, *stmt2;
4502 JSAtom *label;
4504 JS_CHECK_RECURSION(context, return NULL);
4506 tt = tokenStream.getToken(TSF_OPERAND);
4508 switch (tt) {
4509 case TOK_FUNCTION:
4510 #if JS_HAS_XML_SUPPORT
4511 tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
4512 if (tt == TOK_DBLCOLON)
4513 goto expression;
4514 #endif
4515 return functionStmt();
4517 case TOK_IF:
4518 /* An IF node has three kids: condition, then, and optional else. */
4519 pn = TernaryNode::create(tc);
4520 if (!pn)
4521 return NULL;
4522 pn1 = condition();
4523 if (!pn1)
4524 return NULL;
4525 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
4526 pn2 = statement();
4527 if (!pn2)
4528 return NULL;
4529 if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
4530 stmtInfo.type = STMT_ELSE;
4531 pn3 = statement();
4532 if (!pn3)
4533 return NULL;
4534 pn->pn_pos.end = pn3->pn_pos.end;
4535 } else {
4536 pn3 = NULL;
4537 pn->pn_pos.end = pn2->pn_pos.end;
4539 PopStatement(tc);
4540 pn->pn_kid1 = pn1;
4541 pn->pn_kid2 = pn2;
4542 pn->pn_kid3 = pn3;
4543 return pn;
4545 case TOK_SWITCH:
4547 JSParseNode *pn5, *saveBlock;
4548 JSBool seenDefault = JS_FALSE;
4550 pn = BinaryNode::create(tc);
4551 if (!pn)
4552 return NULL;
4553 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
4555 /* pn1 points to the switch's discriminant. */
4556 pn1 = parenExpr(NULL, NULL);
4557 if (!pn1)
4558 return NULL;
4560 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
4561 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
4564 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
4565 * because that function states tc->topStmt->blockid.
4567 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
4569 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
4570 pn2 = ListNode::create(tc);
4571 if (!pn2)
4572 return NULL;
4573 pn2->makeEmpty();
4574 if (!GenerateBlockIdForStmtNode(pn2, tc))
4575 return NULL;
4576 saveBlock = tc->blockNode;
4577 tc->blockNode = pn2;
4579 while ((tt = tokenStream.getToken()) != TOK_RC) {
4580 switch (tt) {
4581 case TOK_DEFAULT:
4582 if (seenDefault) {
4583 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
4584 JSMSG_TOO_MANY_DEFAULTS);
4585 return NULL;
4587 seenDefault = JS_TRUE;
4588 /* FALL THROUGH */
4590 case TOK_CASE:
4591 pn3 = BinaryNode::create(tc);
4592 if (!pn3)
4593 return NULL;
4594 if (tt == TOK_CASE) {
4595 pn3->pn_left = expr();
4596 if (!pn3->pn_left)
4597 return NULL;
4599 pn2->append(pn3);
4600 if (pn2->pn_count == JS_BIT(16)) {
4601 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
4602 JSMSG_TOO_MANY_CASES);
4603 return NULL;
4605 break;
4607 case TOK_ERROR:
4608 return NULL;
4610 default:
4611 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
4612 JSMSG_BAD_SWITCH);
4613 return NULL;
4615 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
4617 pn4 = ListNode::create(tc);
4618 if (!pn4)
4619 return NULL;
4620 pn4->pn_type = TOK_LC;
4621 pn4->makeEmpty();
4622 while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
4623 tt != TOK_CASE && tt != TOK_DEFAULT) {
4624 if (tt == TOK_ERROR)
4625 return NULL;
4626 pn5 = statement();
4627 if (!pn5)
4628 return NULL;
4629 pn4->pn_pos.end = pn5->pn_pos.end;
4630 pn4->append(pn5);
4633 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
4634 if (pn4->pn_head)
4635 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
4636 pn3->pn_pos.end = pn4->pn_pos.end;
4637 pn3->pn_right = pn4;
4641 * Handle the case where there was a let declaration in any case in
4642 * the switch body, but not within an inner block. If it replaced
4643 * tc->blockNode with a new block node then we must refresh pn2 and
4644 * then restore tc->blockNode.
4646 if (tc->blockNode != pn2)
4647 pn2 = tc->blockNode;
4648 tc->blockNode = saveBlock;
4649 PopStatement(tc);
4651 pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
4652 pn->pn_left = pn1;
4653 pn->pn_right = pn2;
4654 return pn;
4657 case TOK_WHILE:
4658 pn = BinaryNode::create(tc);
4659 if (!pn)
4660 return NULL;
4661 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
4662 pn2 = condition();
4663 if (!pn2)
4664 return NULL;
4665 pn->pn_left = pn2;
4666 pn2 = statement();
4667 if (!pn2)
4668 return NULL;
4669 PopStatement(tc);
4670 pn->pn_pos.end = pn2->pn_pos.end;
4671 pn->pn_right = pn2;
4672 return pn;
4674 case TOK_DO:
4675 pn = BinaryNode::create(tc);
4676 if (!pn)
4677 return NULL;
4678 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
4679 pn2 = statement();
4680 if (!pn2)
4681 return NULL;
4682 pn->pn_left = pn2;
4683 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
4684 pn2 = condition();
4685 if (!pn2)
4686 return NULL;
4687 PopStatement(tc);
4688 pn->pn_pos.end = pn2->pn_pos.end;
4689 pn->pn_right = pn2;
4690 if (JSVERSION_NUMBER(context) != JSVERSION_ECMA_3) {
4692 * All legacy and extended versions must do automatic semicolon
4693 * insertion after do-while. See the testcase and discussion in
4694 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
4696 (void) tokenStream.matchToken(TOK_SEMI);
4697 return pn;
4699 break;
4701 case TOK_FOR:
4703 JSParseNode *pnseq = NULL;
4704 #if JS_HAS_BLOCK_SCOPE
4705 JSParseNode *pnlet = NULL;
4706 JSStmtInfo blockInfo;
4707 #endif
4709 /* A FOR node is binary, left is loop control and right is the body. */
4710 pn = BinaryNode::create(tc);
4711 if (!pn)
4712 return NULL;
4713 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
4715 pn->pn_op = JSOP_ITER;
4716 pn->pn_iflags = 0;
4717 if (tokenStream.matchToken(TOK_NAME)) {
4718 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
4719 pn->pn_iflags = JSITER_FOREACH;
4720 else
4721 tokenStream.ungetToken();
4724 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
4725 tt = tokenStream.peekToken(TSF_OPERAND);
4727 #if JS_HAS_BLOCK_SCOPE
4728 bool let = false;
4729 #endif
4731 if (tt == TOK_SEMI) {
4732 if (pn->pn_iflags & JSITER_FOREACH)
4733 goto bad_for_each;
4735 /* No initializer -- set first kid of left sub-node to null. */
4736 pn1 = NULL;
4737 } else {
4739 * Set pn1 to a var list or an initializing expression.
4741 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
4742 * of the for statement. This flag will be used by the RelExpr
4743 * production; if it is set, then the 'in' keyword will not be
4744 * recognized as an operator, leaving it available to be parsed as
4745 * part of a for/in loop.
4747 * A side effect of this restriction is that (unparenthesized)
4748 * expressions involving an 'in' operator are illegal in the init
4749 * clause of an ordinary for loop.
4751 tc->flags |= TCF_IN_FOR_INIT;
4752 if (tt == TOK_VAR) {
4753 (void) tokenStream.getToken();
4754 pn1 = variables(false);
4755 #if JS_HAS_BLOCK_SCOPE
4756 } else if (tt == TOK_LET) {
4757 let = true;
4758 (void) tokenStream.getToken();
4759 if (tokenStream.peekToken() == TOK_LP) {
4760 pn1 = letBlock(JS_FALSE);
4761 tt = TOK_LEXICALSCOPE;
4762 } else {
4763 pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
4764 if (!pnlet)
4765 return NULL;
4766 blockInfo.flags |= SIF_FOR_BLOCK;
4767 pn1 = variables(false);
4769 #endif
4770 } else {
4771 pn1 = expr();
4773 tc->flags &= ~TCF_IN_FOR_INIT;
4774 if (!pn1)
4775 return NULL;
4779 * We can be sure that it's a for/in loop if there's still an 'in'
4780 * keyword here, even if JavaScript recognizes 'in' as an operator,
4781 * as we've excluded 'in' from being parsed in RelExpr by setting
4782 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
4784 if (pn1 && tokenStream.matchToken(TOK_IN)) {
4785 pn->pn_iflags |= JSITER_ENUMERATE;
4786 stmtInfo.type = STMT_FOR_IN_LOOP;
4788 /* Check that the left side of the 'in' is valid. */
4789 JS_ASSERT(!TokenKindIsDecl(tt) || PN_TYPE(pn1) == tt);
4790 if (TokenKindIsDecl(tt)
4791 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
4792 #if JS_HAS_DESTRUCTURING
4793 || (JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4794 pn->pn_op == JSOP_ITER &&
4795 !(pn->pn_iflags & JSITER_FOREACH) &&
4796 (pn1->pn_head->pn_type == TOK_RC ||
4797 (pn1->pn_head->pn_type == TOK_RB &&
4798 pn1->pn_head->pn_count != 2) ||
4799 (pn1->pn_head->pn_type == TOK_ASSIGN &&
4800 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
4801 pn1->pn_head->pn_left->pn_count != 2))))
4802 #endif
4804 : (pn1->pn_type != TOK_NAME &&
4805 pn1->pn_type != TOK_DOT &&
4806 #if JS_HAS_DESTRUCTURING
4807 ((JSVERSION_NUMBER(context) == JSVERSION_1_7 &&
4808 pn->pn_op == JSOP_ITER &&
4809 !(pn->pn_iflags & JSITER_FOREACH))
4810 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
4811 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
4812 #endif
4813 pn1->pn_type != TOK_LP &&
4814 #if JS_HAS_XML_SUPPORT
4815 (pn1->pn_type != TOK_UNARYOP ||
4816 pn1->pn_op != JSOP_XMLNAME) &&
4817 #endif
4818 pn1->pn_type != TOK_LB)) {
4819 ReportCompileErrorNumber(context, &tokenStream, pn1, JSREPORT_ERROR,
4820 JSMSG_BAD_FOR_LEFTSIDE);
4821 return NULL;
4824 /* pn2 points to the name or destructuring pattern on in's left. */
4825 pn2 = NULL;
4826 uintN dflag = PND_ASSIGNED;
4828 if (TokenKindIsDecl(tt)) {
4829 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
4830 pn1->pn_xflags |= PNX_FORINVAR;
4833 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'let',
4834 * 'var', or 'const' to hoist the initializer or the entire
4835 * decl out of the loop head. TOK_VAR is the type for both
4836 * 'var' and 'const'.
4838 pn2 = pn1->pn_head;
4839 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
4840 #if JS_HAS_DESTRUCTURING
4841 || pn2->pn_type == TOK_ASSIGN
4842 #endif
4844 pnseq = ListNode::create(tc);
4845 if (!pnseq)
4846 return NULL;
4847 pnseq->pn_type = TOK_SEQ;
4848 pnseq->pn_pos.begin = pn->pn_pos.begin;
4850 #if JS_HAS_BLOCK_SCOPE
4851 if (tt == TOK_LET) {
4853 * Hoist just the 'i' from 'for (let x = i in o)' to
4854 * before the loop, glued together via pnseq.
4856 pn3 = UnaryNode::create(tc);
4857 if (!pn3)
4858 return NULL;
4859 pn3->pn_type = TOK_SEMI;
4860 pn3->pn_op = JSOP_NOP;
4861 #if JS_HAS_DESTRUCTURING
4862 if (pn2->pn_type == TOK_ASSIGN) {
4863 pn4 = pn2->pn_right;
4864 pn2 = pn1->pn_head = pn2->pn_left;
4865 } else
4866 #endif
4868 pn4 = pn2->pn_expr;
4869 pn2->pn_expr = NULL;
4871 if (!RebindLets(pn4, tc))
4872 return NULL;
4873 pn3->pn_pos = pn4->pn_pos;
4874 pn3->pn_kid = pn4;
4875 pnseq->initList(pn3);
4876 } else
4877 #endif /* JS_HAS_BLOCK_SCOPE */
4879 dflag = PND_INITIALIZED;
4882 * All of 'var x = i' is hoisted above 'for (x in o)',
4883 * so clear PNX_FORINVAR.
4885 * Request JSOP_POP here since the var is for a simple
4886 * name (it is not a destructuring binding's left-hand
4887 * side) and it has an initializer.
4889 pn1->pn_xflags &= ~PNX_FORINVAR;
4890 pn1->pn_xflags |= PNX_POPVAR;
4891 pnseq->initList(pn1);
4893 #if JS_HAS_DESTRUCTURING
4894 if (pn2->pn_type == TOK_ASSIGN) {
4895 pn1 = CloneParseTree(pn2->pn_left, tc);
4896 if (!pn1)
4897 return NULL;
4898 } else
4899 #endif
4901 JS_ASSERT(pn2->pn_type == TOK_NAME);
4902 pn1 = NameNode::create(pn2->pn_atom, tc);
4903 if (!pn1)
4904 return NULL;
4905 pn1->pn_type = TOK_NAME;
4906 pn1->pn_op = JSOP_NAME;
4907 pn1->pn_pos = pn2->pn_pos;
4908 if (pn2->pn_defn)
4909 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
4911 pn2 = pn1;
4916 if (!pn2) {
4917 pn2 = pn1;
4918 if (pn2->pn_type == TOK_LP &&
4919 !MakeSetCall(context, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
4920 return NULL;
4922 #if JS_HAS_XML_SUPPORT
4923 if (pn2->pn_type == TOK_UNARYOP)
4924 pn2->pn_op = JSOP_BINDXMLNAME;
4925 #endif
4928 switch (pn2->pn_type) {
4929 case TOK_NAME:
4930 /* Beware 'for (arguments in ...)' with or without a 'var'. */
4931 NoteLValue(context, pn2, tc, dflag);
4932 break;
4934 #if JS_HAS_DESTRUCTURING
4935 case TOK_ASSIGN:
4936 pn2 = pn2->pn_left;
4937 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
4938 /* FALL THROUGH */
4939 case TOK_RB:
4940 case TOK_RC:
4941 /* Check for valid lvalues in var-less destructuring for-in. */
4942 if (pn1 == pn2 && !CheckDestructuring(context, NULL, pn2, NULL, tc))
4943 return NULL;
4945 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
4947 * Destructuring for-in requires [key, value] enumeration
4948 * in JS1.7.
4950 JS_ASSERT(pn->pn_op == JSOP_ITER);
4951 if (!(pn->pn_iflags & JSITER_FOREACH))
4952 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
4954 break;
4955 #endif
4957 default:;
4961 * Parse the object expression as the right operand of 'in', first
4962 * removing the top statement from the statement-stack if this is a
4963 * 'for (let x in y)' loop.
4965 #if JS_HAS_BLOCK_SCOPE
4966 JSStmtInfo *save = tc->topStmt;
4967 if (let)
4968 tc->topStmt = save->down;
4969 #endif
4970 pn2 = expr();
4971 #if JS_HAS_BLOCK_SCOPE
4972 if (let)
4973 tc->topStmt = save;
4974 #endif
4976 pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
4977 if (!pn2)
4978 return NULL;
4979 pn->pn_left = pn2;
4980 } else {
4981 if (pn->pn_iflags & JSITER_FOREACH)
4982 goto bad_for_each;
4983 pn->pn_op = JSOP_NOP;
4985 /* Parse the loop condition or null into pn2. */
4986 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
4987 tt = tokenStream.peekToken(TSF_OPERAND);
4988 if (tt == TOK_SEMI) {
4989 pn2 = NULL;
4990 } else {
4991 pn2 = expr();
4992 if (!pn2)
4993 return NULL;
4996 /* Parse the update expression or null into pn3. */
4997 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
4998 tt = tokenStream.peekToken(TSF_OPERAND);
4999 if (tt == TOK_RP) {
5000 pn3 = NULL;
5001 } else {
5002 pn3 = expr();
5003 if (!pn3)
5004 return NULL;
5007 /* Build the FORHEAD node to use as the left kid of pn. */
5008 pn4 = TernaryNode::create(tc);
5009 if (!pn4)
5010 return NULL;
5011 pn4->pn_type = TOK_FORHEAD;
5012 pn4->pn_op = JSOP_NOP;
5013 pn4->pn_kid1 = pn1;
5014 pn4->pn_kid2 = pn2;
5015 pn4->pn_kid3 = pn3;
5016 pn->pn_left = pn4;
5019 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5021 /* Parse the loop body into pn->pn_right. */
5022 pn2 = statement();
5023 if (!pn2)
5024 return NULL;
5025 pn->pn_right = pn2;
5027 /* Record the absolute line number for source note emission. */
5028 pn->pn_pos.end = pn2->pn_pos.end;
5030 #if JS_HAS_BLOCK_SCOPE
5031 if (pnlet) {
5032 PopStatement(tc);
5033 pnlet->pn_expr = pn;
5034 pn = pnlet;
5036 #endif
5037 if (pnseq) {
5038 pnseq->pn_pos.end = pn->pn_pos.end;
5039 pnseq->append(pn);
5040 pn = pnseq;
5042 PopStatement(tc);
5043 return pn;
5045 bad_for_each:
5046 ReportCompileErrorNumber(context, &tokenStream, pn, JSREPORT_ERROR,
5047 JSMSG_BAD_FOR_EACH_LOOP);
5048 return NULL;
5051 case TOK_TRY: {
5052 JSParseNode *catchList, *lastCatch;
5055 * try nodes are ternary.
5056 * kid1 is the try statement
5057 * kid2 is the catch node list or null
5058 * kid3 is the finally statement
5060 * catch nodes are ternary.
5061 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5062 * kid2 is the catch guard or null if no guard
5063 * kid3 is the catch block
5065 * catch lvalue nodes are either:
5066 * TOK_NAME for a single identifier
5067 * TOK_RB or TOK_RC for a destructuring left-hand side
5069 * finally nodes are TOK_LC statement lists.
5071 pn = TernaryNode::create(tc);
5072 if (!pn)
5073 return NULL;
5074 pn->pn_op = JSOP_NOP;
5076 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5077 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5078 return NULL;
5079 pn->pn_kid1 = statements();
5080 if (!pn->pn_kid1)
5081 return NULL;
5082 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5083 PopStatement(tc);
5085 catchList = NULL;
5086 tt = tokenStream.getToken();
5087 if (tt == TOK_CATCH) {
5088 catchList = ListNode::create(tc);
5089 if (!catchList)
5090 return NULL;
5091 catchList->pn_type = TOK_RESERVED;
5092 catchList->makeEmpty();
5093 lastCatch = NULL;
5095 do {
5096 JSParseNode *pnblock;
5097 BindData data;
5099 /* Check for another catch after unconditional catch. */
5100 if (lastCatch && !lastCatch->pn_kid2) {
5101 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5102 JSMSG_CATCH_AFTER_GENERAL);
5103 return NULL;
5107 * Create a lexical scope node around the whole catch clause,
5108 * including the head.
5110 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
5111 if (!pnblock)
5112 return NULL;
5113 stmtInfo.type = STMT_CATCH;
5116 * Legal catch forms are:
5117 * catch (lhs)
5118 * catch (lhs if <boolean_expression>)
5119 * where lhs is a name or a destructuring left-hand side.
5120 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5122 pn2 = TernaryNode::create(tc);
5123 if (!pn2)
5124 return NULL;
5125 pnblock->pn_expr = pn2;
5126 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5129 * Contrary to ECMA Ed. 3, the catch variable is lexically
5130 * scoped, not a property of a new Object instance. This is
5131 * an intentional change that anticipates ECMA Ed. 4.
5133 data.pn = NULL;
5134 data.op = JSOP_NOP;
5135 data.binder = BindLet;
5136 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5138 tt = tokenStream.getToken();
5139 switch (tt) {
5140 #if JS_HAS_DESTRUCTURING
5141 case TOK_LB:
5142 case TOK_LC:
5143 pn3 = destructuringExpr(&data, tt);
5144 if (!pn3)
5145 return NULL;
5146 break;
5147 #endif
5149 case TOK_NAME:
5150 label = tokenStream.currentToken().t_atom;
5151 pn3 = NewBindingNode(label, tc, true);
5152 if (!pn3)
5153 return NULL;
5154 data.pn = pn3;
5155 if (!data.binder(context, &data, label, tc))
5156 return NULL;
5157 break;
5159 default:
5160 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5161 JSMSG_CATCH_IDENTIFIER);
5162 return NULL;
5165 pn2->pn_kid1 = pn3;
5166 #if JS_HAS_CATCH_GUARD
5168 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5169 * to avoid conflicting with the JS2/ECMAv4 type annotation
5170 * catchguard syntax.
5172 if (tokenStream.matchToken(TOK_IF)) {
5173 pn2->pn_kid2 = expr();
5174 if (!pn2->pn_kid2)
5175 return NULL;
5177 #endif
5178 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5180 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5181 pn2->pn_kid3 = statements();
5182 if (!pn2->pn_kid3)
5183 return NULL;
5184 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5185 PopStatement(tc);
5187 catchList->append(pnblock);
5188 lastCatch = pn2;
5189 tt = tokenStream.getToken(TSF_OPERAND);
5190 } while (tt == TOK_CATCH);
5192 pn->pn_kid2 = catchList;
5194 if (tt == TOK_FINALLY) {
5195 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5196 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5197 return NULL;
5198 pn->pn_kid3 = statements();
5199 if (!pn->pn_kid3)
5200 return NULL;
5201 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5202 PopStatement(tc);
5203 } else {
5204 tokenStream.ungetToken();
5206 if (!catchList && !pn->pn_kid3) {
5207 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5208 JSMSG_CATCH_OR_FINALLY);
5209 return NULL;
5211 return pn;
5214 case TOK_THROW:
5215 pn = UnaryNode::create(tc);
5216 if (!pn)
5217 return NULL;
5219 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
5220 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
5221 if (tt == TOK_ERROR)
5222 return NULL;
5223 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
5224 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5225 JSMSG_SYNTAX_ERROR);
5226 return NULL;
5229 pn2 = expr();
5230 if (!pn2)
5231 return NULL;
5232 pn->pn_pos.end = pn2->pn_pos.end;
5233 pn->pn_op = JSOP_THROW;
5234 pn->pn_kid = pn2;
5235 break;
5237 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
5238 case TOK_CATCH:
5239 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5240 JSMSG_CATCH_WITHOUT_TRY);
5241 return NULL;
5243 case TOK_FINALLY:
5244 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5245 JSMSG_FINALLY_WITHOUT_TRY);
5246 return NULL;
5248 case TOK_BREAK:
5249 pn = NullaryNode::create(tc);
5250 if (!pn)
5251 return NULL;
5252 if (!MatchLabel(context, &tokenStream, pn))
5253 return NULL;
5254 stmt = tc->topStmt;
5255 label = pn->pn_atom;
5256 if (label) {
5257 for (; ; stmt = stmt->down) {
5258 if (!stmt) {
5259 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5260 JSMSG_LABEL_NOT_FOUND);
5261 return NULL;
5263 if (stmt->type == STMT_LABEL && stmt->label == label)
5264 break;
5266 } else {
5267 for (; ; stmt = stmt->down) {
5268 if (!stmt) {
5269 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5270 JSMSG_TOUGH_BREAK);
5271 return NULL;
5273 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
5274 break;
5277 if (label)
5278 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5279 break;
5281 case TOK_CONTINUE:
5282 pn = NullaryNode::create(tc);
5283 if (!pn)
5284 return NULL;
5285 if (!MatchLabel(context, &tokenStream, pn))
5286 return NULL;
5287 stmt = tc->topStmt;
5288 label = pn->pn_atom;
5289 if (label) {
5290 for (stmt2 = NULL; ; stmt = stmt->down) {
5291 if (!stmt) {
5292 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5293 JSMSG_LABEL_NOT_FOUND);
5294 return NULL;
5296 if (stmt->type == STMT_LABEL) {
5297 if (stmt->label == label) {
5298 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
5299 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5300 JSMSG_BAD_CONTINUE);
5301 return NULL;
5303 break;
5305 } else {
5306 stmt2 = stmt;
5309 } else {
5310 for (; ; stmt = stmt->down) {
5311 if (!stmt) {
5312 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5313 JSMSG_BAD_CONTINUE);
5314 return NULL;
5316 if (STMT_IS_LOOP(stmt))
5317 break;
5320 if (label)
5321 pn->pn_pos.end = tokenStream.currentToken().pos.end;
5322 break;
5324 case TOK_WITH:
5326 * In most cases, we want the constructs forbidden in strict mode
5327 * code to be a subset of those that JSOPTION_STRICT warns about, and
5328 * we should use ReportStrictModeError. However, 'with' is the sole
5329 * instance of a construct that is forbidden in strict mode code, but
5330 * doesn't even merit a warning under JSOPTION_STRICT. See
5331 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5333 if (tc->flags & TCF_STRICT_MODE_CODE) {
5334 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5335 JSMSG_STRICT_CODE_WITH);
5336 return NULL;
5339 pn = BinaryNode::create(tc);
5340 if (!pn)
5341 return NULL;
5342 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5343 pn2 = parenExpr(NULL, NULL);
5344 if (!pn2)
5345 return NULL;
5346 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5347 pn->pn_left = pn2;
5349 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5350 pn2 = statement();
5351 if (!pn2)
5352 return NULL;
5353 PopStatement(tc);
5355 pn->pn_pos.end = pn2->pn_pos.end;
5356 pn->pn_right = pn2;
5357 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5358 return pn;
5360 case TOK_VAR:
5361 pn = variables(false);
5362 if (!pn)
5363 return NULL;
5365 /* Tell js_EmitTree to generate a final POP. */
5366 pn->pn_xflags |= PNX_POPVAR;
5367 break;
5369 #if JS_HAS_BLOCK_SCOPE
5370 case TOK_LET:
5372 JSObject *obj;
5373 JSObjectBox *blockbox;
5375 /* Check for a let statement or let expression. */
5376 if (tokenStream.peekToken() == TOK_LP) {
5377 pn = letBlock(JS_TRUE);
5378 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5379 return pn;
5381 /* Let expressions require automatic semicolon insertion. */
5382 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5383 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5384 break;
5388 * This is a let declaration. We must be directly under a block per
5389 * the proposed ES4 specs, but not an implicit block created due to
5390 * 'for (let ...)'. If we pass this error test, make the enclosing
5391 * JSStmtInfo be our scope. Further let declarations in this block
5392 * will find this scope statement and use the same block object.
5394 * If we are the first let declaration in this block (i.e., when the
5395 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5396 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5398 stmt = tc->topStmt;
5399 if (stmt &&
5400 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5401 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5402 JSMSG_LET_DECL_NOT_IN_BLOCK);
5403 return NULL;
5406 if (stmt && (stmt->flags & SIF_SCOPE)) {
5407 JS_ASSERT(tc->blockChain == stmt->blockObj);
5408 obj = tc->blockChain;
5409 } else {
5410 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5412 * ES4 specifies that let at top level and at body-block scope
5413 * does not shadow var, so convert back to var.
5415 tokenStream.mungeCurrentToken(TOK_VAR, JSOP_DEFVAR);
5417 pn = variables(false);
5418 if (!pn)
5419 return NULL;
5420 pn->pn_xflags |= PNX_POPVAR;
5421 break;
5425 * Some obvious assertions here, but they may help clarify the
5426 * situation. This stmt is not yet a scope, so it must not be a
5427 * catch block (catch is a lexical scope by definition).
5429 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5430 JS_ASSERT(stmt != tc->topScopeStmt);
5431 JS_ASSERT(stmt->type == STMT_BLOCK ||
5432 stmt->type == STMT_SWITCH ||
5433 stmt->type == STMT_TRY ||
5434 stmt->type == STMT_FINALLY);
5435 JS_ASSERT(!stmt->downScope);
5437 /* Convert the block statement into a scope statement. */
5438 JSObject *obj = js_NewBlockObject(tc->parser->context);
5439 if (!obj)
5440 return NULL;
5442 blockbox = tc->parser->newObjectBox(obj);
5443 if (!blockbox)
5444 return NULL;
5447 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5448 * list stack, if it isn't already there. If it is there, but it
5449 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5450 * block.
5452 stmt->flags |= SIF_SCOPE;
5453 stmt->downScope = tc->topScopeStmt;
5454 tc->topScopeStmt = stmt;
5455 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5456 (tc->maxScopeDepth = tc->scopeDepth));
5458 obj->setParent(tc->blockChain);
5459 tc->blockChain = obj;
5460 stmt->blockObj = obj;
5462 #ifdef DEBUG
5463 pn1 = tc->blockNode;
5464 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
5465 #endif
5467 /* Create a new lexical scope node for these statements. */
5468 pn1 = LexicalScopeNode::create(tc);
5469 if (!pn1)
5470 return NULL;
5472 pn1->pn_type = TOK_LEXICALSCOPE;
5473 pn1->pn_op = JSOP_LEAVEBLOCK;
5474 pn1->pn_pos = tc->blockNode->pn_pos;
5475 pn1->pn_objbox = blockbox;
5476 pn1->pn_expr = tc->blockNode;
5477 pn1->pn_blockid = tc->blockNode->pn_blockid;
5478 tc->blockNode = pn1;
5481 pn = variables(false);
5482 if (!pn)
5483 return NULL;
5484 pn->pn_xflags = PNX_POPVAR;
5485 break;
5487 #endif /* JS_HAS_BLOCK_SCOPE */
5489 case TOK_RETURN:
5490 pn = returnOrYield(false);
5491 if (!pn)
5492 return NULL;
5493 break;
5495 case TOK_LC:
5497 uintN oldflags;
5499 oldflags = tc->flags;
5500 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
5501 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
5502 return NULL;
5503 pn = statements();
5504 if (!pn)
5505 return NULL;
5507 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
5508 PopStatement(tc);
5511 * If we contain a function statement and our container is top-level
5512 * or another block, flag pn to preserve braces when decompiling.
5514 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
5515 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
5516 pn->pn_xflags |= PNX_NEEDBRACES;
5518 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
5519 return pn;
5522 case TOK_EOL:
5523 case TOK_SEMI:
5524 pn = UnaryNode::create(tc);
5525 if (!pn)
5526 return NULL;
5527 pn->pn_type = TOK_SEMI;
5528 return pn;
5530 #if JS_HAS_DEBUGGER_KEYWORD
5531 case TOK_DEBUGGER:
5532 pn = NullaryNode::create(tc);
5533 if (!pn)
5534 return NULL;
5535 pn->pn_type = TOK_DEBUGGER;
5536 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5537 break;
5538 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5540 #if JS_HAS_XML_SUPPORT
5541 case TOK_DEFAULT:
5542 pn = UnaryNode::create(tc);
5543 if (!pn)
5544 return NULL;
5545 if (!tokenStream.matchToken(TOK_NAME) ||
5546 tokenStream.currentToken().t_atom != context->runtime->atomState.xmlAtom ||
5547 !tokenStream.matchToken(TOK_NAME) ||
5548 tokenStream.currentToken().t_atom != context->runtime->atomState.namespaceAtom ||
5549 !tokenStream.matchToken(TOK_ASSIGN) ||
5550 tokenStream.currentToken().t_op != JSOP_NOP) {
5551 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5552 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
5553 return NULL;
5556 /* Is this an E4X dagger I see before me? */
5557 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5558 pn2 = expr();
5559 if (!pn2)
5560 return NULL;
5561 pn->pn_op = JSOP_DEFXMLNS;
5562 pn->pn_pos.end = pn2->pn_pos.end;
5563 pn->pn_kid = pn2;
5564 break;
5565 #endif
5567 case TOK_ERROR:
5568 return NULL;
5570 default:
5571 #if JS_HAS_XML_SUPPORT
5572 expression:
5573 #endif
5574 tokenStream.ungetToken();
5575 pn2 = expr();
5576 if (!pn2)
5577 return NULL;
5579 if (tokenStream.peekToken() == TOK_COLON) {
5580 if (pn2->pn_type != TOK_NAME) {
5581 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5582 JSMSG_BAD_LABEL);
5583 return NULL;
5585 label = pn2->pn_atom;
5586 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
5587 if (stmt->type == STMT_LABEL && stmt->label == label) {
5588 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5589 JSMSG_DUPLICATE_LABEL);
5590 return NULL;
5593 ForgetUse(pn2);
5595 (void) tokenStream.getToken();
5597 /* Push a label struct and parse the statement. */
5598 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5599 stmtInfo.label = label;
5600 pn = statement();
5601 if (!pn)
5602 return NULL;
5604 /* Normalize empty statement to empty block for the decompiler. */
5605 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5606 pn->pn_type = TOK_LC;
5607 pn->pn_arity = PN_LIST;
5608 pn->makeEmpty();
5611 /* Pop the label, set pn_expr, and return early. */
5612 PopStatement(tc);
5613 pn2->pn_type = TOK_COLON;
5614 pn2->pn_pos.end = pn->pn_pos.end;
5615 pn2->pn_expr = pn;
5616 return pn2;
5619 pn = UnaryNode::create(tc);
5620 if (!pn)
5621 return NULL;
5622 pn->pn_type = TOK_SEMI;
5623 pn->pn_pos = pn2->pn_pos;
5624 pn->pn_kid = pn2;
5626 switch (PN_TYPE(pn2)) {
5627 case TOK_LP:
5629 * Flag lambdas immediately applied as statements as instances of
5630 * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5632 if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION &&
5633 !pn2->pn_head->pn_funbox->node->isFunArg()) {
5634 pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
5636 break;
5637 case TOK_ASSIGN:
5639 * Keep track of all apparent methods created by assignments such
5640 * as this.foo = function (...) {...} in a function that could end
5641 * up a constructor function. See Parser::setFunctionKinds.
5643 if (tc->funbox &&
5644 PN_OP(pn2) == JSOP_NOP &&
5645 PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5646 PN_OP(pn2->pn_left->pn_expr) == JSOP_THIS &&
5647 PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
5648 JS_ASSERT(!pn2->pn_defn);
5649 JS_ASSERT(!pn2->pn_used);
5650 pn2->pn_right->pn_link = tc->funbox->methods;
5651 tc->funbox->methods = pn2->pn_right;
5653 break;
5654 default:;
5656 break;
5659 /* Check termination of this primitive statement. */
5660 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5663 static void
5664 NoteArgumentsUse(JSTreeContext *tc)
5666 JS_ASSERT(tc->inFunction());
5667 tc->flags |= TCF_FUN_USES_ARGUMENTS;
5668 if (tc->funbox)
5669 tc->funbox->node->pn_dflags |= PND_FUNARG;
5672 JSParseNode *
5673 Parser::variables(bool inLetHead)
5675 TokenKind tt;
5676 bool let;
5677 JSStmtInfo *scopeStmt;
5678 BindData data;
5679 JSParseNode *pn, *pn2;
5680 JSAtom *atom;
5683 * The three options here are:
5684 * - TOK_LET: We are parsing a let declaration.
5685 * - TOK_LP: We are parsing the head of a let block.
5686 * - Otherwise, we're parsing var declarations.
5688 tt = tokenStream.currentToken().type;
5689 let = (tt == TOK_LET || tt == TOK_LP);
5690 JS_ASSERT(let || tt == TOK_VAR);
5692 #if JS_HAS_BLOCK_SCOPE
5693 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
5694 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
5695 #endif
5697 /* Make sure that statement set up the tree context correctly. */
5698 scopeStmt = tc->topScopeStmt;
5699 if (let) {
5700 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
5701 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
5702 scopeStmt = scopeStmt->downScope;
5704 JS_ASSERT(scopeStmt);
5707 data.op = let ? JSOP_NOP : tokenStream.currentToken().t_op;
5708 pn = ListNode::create(tc);
5709 if (!pn)
5710 return NULL;
5711 pn->pn_op = data.op;
5712 pn->makeEmpty();
5715 * SpiderMonkey const is really "write once per initialization evaluation"
5716 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
5717 * this code will change soon.
5719 if (let) {
5720 JS_ASSERT(tc->blockChain == scopeStmt->blockObj);
5721 data.binder = BindLet;
5722 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
5723 } else {
5724 data.binder = BindVarOrConst;
5727 do {
5728 tt = tokenStream.getToken();
5729 #if JS_HAS_DESTRUCTURING
5730 if (tt == TOK_LB || tt == TOK_LC) {
5731 tc->flags |= TCF_DECL_DESTRUCTURING;
5732 pn2 = primaryExpr(tt, JS_FALSE);
5733 tc->flags &= ~TCF_DECL_DESTRUCTURING;
5734 if (!pn2)
5735 return NULL;
5737 if (!CheckDestructuring(context, &data, pn2, NULL, tc))
5738 return NULL;
5739 if ((tc->flags & TCF_IN_FOR_INIT) &&
5740 tokenStream.peekToken() == TOK_IN) {
5741 pn->append(pn2);
5742 continue;
5745 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
5746 if (tokenStream.currentToken().t_op != JSOP_NOP)
5747 goto bad_var_init;
5749 #if JS_HAS_BLOCK_SCOPE
5750 if (popScope) {
5751 tc->topStmt = save->down;
5752 tc->topScopeStmt = saveScope->downScope;
5754 #endif
5755 JSParseNode *init = assignExpr();
5756 #if JS_HAS_BLOCK_SCOPE
5757 if (popScope) {
5758 tc->topStmt = save;
5759 tc->topScopeStmt = saveScope;
5761 #endif
5763 if (!init || !UndominateInitializers(pn2, init, tc))
5764 return NULL;
5766 pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
5767 if (!pn2)
5768 return NULL;
5769 pn->append(pn2);
5770 continue;
5772 #endif /* JS_HAS_DESTRUCTURING */
5774 if (tt != TOK_NAME) {
5775 if (tt != TOK_ERROR) {
5776 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5777 JSMSG_NO_VARIABLE_NAME);
5779 return NULL;
5782 atom = tokenStream.currentToken().t_atom;
5783 pn2 = NewBindingNode(atom, tc, let);
5784 if (!pn2)
5785 return NULL;
5786 if (data.op == JSOP_DEFCONST)
5787 pn2->pn_dflags |= PND_CONST;
5788 data.pn = pn2;
5789 if (!data.binder(context, &data, atom, tc))
5790 return NULL;
5791 pn->append(pn2);
5793 if (tokenStream.matchToken(TOK_ASSIGN)) {
5794 if (tokenStream.currentToken().t_op != JSOP_NOP)
5795 goto bad_var_init;
5797 #if JS_HAS_BLOCK_SCOPE
5798 if (popScope) {
5799 tc->topStmt = save->down;
5800 tc->topScopeStmt = saveScope->downScope;
5802 #endif
5803 JSParseNode *init = assignExpr();
5804 #if JS_HAS_BLOCK_SCOPE
5805 if (popScope) {
5806 tc->topStmt = save;
5807 tc->topScopeStmt = saveScope;
5809 #endif
5810 if (!init)
5811 return NULL;
5813 if (pn2->pn_used) {
5814 pn2 = MakeAssignment(pn2, init, tc);
5815 if (!pn2)
5816 return NULL;
5817 } else {
5818 pn2->pn_expr = init;
5821 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
5822 ? JSOP_SETNAME
5823 : (pn2->pn_dflags & PND_GVAR)
5824 ? JSOP_SETGVAR
5825 : (pn2->pn_dflags & PND_BOUND)
5826 ? JSOP_SETLOCAL
5827 : (data.op == JSOP_DEFCONST)
5828 ? JSOP_SETCONST
5829 : JSOP_SETNAME;
5831 NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
5833 /* The declarator's position must include the initializer. */
5834 pn2->pn_pos.end = init->pn_pos.end;
5836 if (tc->inFunction() &&
5837 atom == context->runtime->atomState.argumentsAtom) {
5838 NoteArgumentsUse(tc);
5839 if (!let)
5840 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5843 } while (tokenStream.matchToken(TOK_COMMA));
5845 pn->pn_pos.end = pn->last()->pn_pos.end;
5846 return pn;
5848 bad_var_init:
5849 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5850 JSMSG_BAD_VAR_INIT);
5851 return NULL;
5854 JSParseNode *
5855 Parser::expr()
5857 JSParseNode *pn, *pn2;
5859 pn = assignExpr();
5860 if (pn && tokenStream.matchToken(TOK_COMMA)) {
5861 pn2 = ListNode::create(tc);
5862 if (!pn2)
5863 return NULL;
5864 pn2->pn_pos.begin = pn->pn_pos.begin;
5865 pn2->initList(pn);
5866 pn = pn2;
5867 do {
5868 #if JS_HAS_GENERATORS
5869 pn2 = pn->last();
5870 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
5871 ReportCompileErrorNumber(context, &tokenStream, pn2, JSREPORT_ERROR,
5872 JSMSG_BAD_GENERATOR_SYNTAX,
5873 js_yield_str);
5874 return NULL;
5876 #endif
5877 pn2 = assignExpr();
5878 if (!pn2)
5879 return NULL;
5880 pn->append(pn2);
5881 } while (tokenStream.matchToken(TOK_COMMA));
5882 pn->pn_pos.end = pn->last()->pn_pos.end;
5884 return pn;
5887 JSParseNode *
5888 Parser::assignExpr()
5890 JSParseNode *pn, *rhs;
5891 TokenKind tt;
5892 JSOp op;
5894 JS_CHECK_RECURSION(context, return NULL);
5896 #if JS_HAS_GENERATORS
5897 if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
5898 return returnOrYield(true);
5899 #endif
5901 pn = condExpr();
5902 if (!pn)
5903 return NULL;
5905 tt = tokenStream.getToken();
5906 if (tt != TOK_ASSIGN) {
5907 tokenStream.ungetToken();
5908 return pn;
5911 op = tokenStream.currentToken().t_op;
5912 switch (pn->pn_type) {
5913 case TOK_NAME:
5914 if (!CheckStrictAssignment(context, tc, pn))
5915 return NULL;
5916 pn->pn_op = JSOP_SETNAME;
5917 NoteLValue(context, pn, tc);
5918 break;
5919 case TOK_DOT:
5920 pn->pn_op = JSOP_SETPROP;
5921 break;
5922 case TOK_LB:
5923 pn->pn_op = JSOP_SETELEM;
5924 break;
5925 #if JS_HAS_DESTRUCTURING
5926 case TOK_RB:
5927 case TOK_RC:
5928 if (op != JSOP_NOP) {
5929 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5930 JSMSG_BAD_DESTRUCT_ASS);
5931 return NULL;
5933 rhs = assignExpr();
5934 if (!rhs || !CheckDestructuring(context, NULL, pn, rhs, tc))
5935 return NULL;
5936 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5937 #endif
5938 case TOK_LP:
5939 if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
5940 return NULL;
5941 break;
5942 #if JS_HAS_XML_SUPPORT
5943 case TOK_UNARYOP:
5944 if (pn->pn_op == JSOP_XMLNAME) {
5945 pn->pn_op = JSOP_SETXMLNAME;
5946 break;
5948 /* FALL THROUGH */
5949 #endif
5950 default:
5951 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
5952 JSMSG_BAD_LEFTSIDE_OF_ASS);
5953 return NULL;
5956 rhs = assignExpr();
5957 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
5958 JSDefinition *dn = pn->pn_lexdef;
5961 * If the definition is not flagged as assigned, we must have imputed
5962 * the initialized flag to it, to optimize for flat closures. But that
5963 * optimization uses source coordinates to check dominance relations,
5964 * so we must extend the end of the definition to cover the right-hand
5965 * side of this assignment, i.e., the initializer.
5967 if (!dn->isAssigned()) {
5968 JS_ASSERT(dn->isInitialized());
5969 dn->pn_pos.end = rhs->pn_pos.end;
5973 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
5976 JSParseNode *
5977 Parser::condExpr()
5979 JSParseNode *pn, *pn1, *pn2, *pn3;
5980 uintN oldflags;
5982 pn = orExpr();
5983 if (pn && tokenStream.matchToken(TOK_HOOK)) {
5984 pn1 = pn;
5985 pn = TernaryNode::create(tc);
5986 if (!pn)
5987 return NULL;
5990 * Always accept the 'in' operator in the middle clause of a ternary,
5991 * where it's unambiguous, even if we might be parsing the init of a
5992 * for statement.
5994 oldflags = tc->flags;
5995 tc->flags &= ~TCF_IN_FOR_INIT;
5996 pn2 = assignExpr();
5997 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
5999 if (!pn2)
6000 return NULL;
6001 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
6002 pn3 = assignExpr();
6003 if (!pn3)
6004 return NULL;
6005 pn->pn_pos.begin = pn1->pn_pos.begin;
6006 pn->pn_pos.end = pn3->pn_pos.end;
6007 pn->pn_kid1 = pn1;
6008 pn->pn_kid2 = pn2;
6009 pn->pn_kid3 = pn3;
6011 return pn;
6014 JSParseNode *
6015 Parser::orExpr()
6017 JSParseNode *pn;
6019 pn = andExpr();
6020 while (pn && tokenStream.matchToken(TOK_OR))
6021 pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, andExpr(), tc);
6022 return pn;
6025 JSParseNode *
6026 Parser::andExpr()
6028 JSParseNode *pn;
6030 pn = bitOrExpr();
6031 while (pn && tokenStream.matchToken(TOK_AND))
6032 pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, bitOrExpr(), tc);
6033 return pn;
6036 JSParseNode *
6037 Parser::bitOrExpr()
6039 JSParseNode *pn;
6041 pn = bitXorExpr();
6042 while (pn && tokenStream.matchToken(TOK_BITOR)) {
6043 pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, bitXorExpr(), tc);
6045 return pn;
6048 JSParseNode *
6049 Parser::bitXorExpr()
6051 JSParseNode *pn;
6053 pn = bitAndExpr();
6054 while (pn && tokenStream.matchToken(TOK_BITXOR)) {
6055 pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, bitAndExpr(), tc);
6057 return pn;
6060 JSParseNode *
6061 Parser::bitAndExpr()
6063 JSParseNode *pn;
6065 pn = eqExpr();
6066 while (pn && tokenStream.matchToken(TOK_BITAND))
6067 pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, eqExpr(), tc);
6068 return pn;
6071 JSParseNode *
6072 Parser::eqExpr()
6074 JSParseNode *pn;
6075 JSOp op;
6077 pn = relExpr();
6078 while (pn && tokenStream.matchToken(TOK_EQOP)) {
6079 op = tokenStream.currentToken().t_op;
6080 pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, relExpr(), tc);
6082 return pn;
6085 JSParseNode *
6086 Parser::relExpr()
6088 JSParseNode *pn;
6089 TokenKind tt;
6090 JSOp op;
6091 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6094 * Uses of the in operator in shiftExprs are always unambiguous,
6095 * so unset the flag that prohibits recognizing it.
6097 tc->flags &= ~TCF_IN_FOR_INIT;
6099 pn = shiftExpr();
6100 while (pn &&
6101 (tokenStream.matchToken(TOK_RELOP) ||
6103 * Recognize the 'in' token as an operator only if we're not
6104 * currently in the init expr of a for loop.
6106 (inForInitFlag == 0 && tokenStream.matchToken(TOK_IN)) ||
6107 tokenStream.matchToken(TOK_INSTANCEOF))) {
6108 tt = tokenStream.currentToken().type;
6109 op = tokenStream.currentToken().t_op;
6110 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, shiftExpr(), tc);
6112 /* Restore previous state of inForInit flag. */
6113 tc->flags |= inForInitFlag;
6115 return pn;
6118 JSParseNode *
6119 Parser::shiftExpr()
6121 JSParseNode *pn;
6122 JSOp op;
6124 pn = addExpr();
6125 while (pn && tokenStream.matchToken(TOK_SHOP)) {
6126 op = tokenStream.currentToken().t_op;
6127 pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, addExpr(), tc);
6129 return pn;
6132 JSParseNode *
6133 Parser::addExpr()
6135 JSParseNode *pn;
6136 TokenKind tt;
6137 JSOp op;
6139 pn = mulExpr();
6140 while (pn &&
6141 (tokenStream.matchToken(TOK_PLUS) ||
6142 tokenStream.matchToken(TOK_MINUS))) {
6143 tt = tokenStream.currentToken().type;
6144 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6145 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, mulExpr(), tc);
6147 return pn;
6150 JSParseNode *
6151 Parser::mulExpr()
6153 JSParseNode *pn;
6154 TokenKind tt;
6155 JSOp op;
6157 pn = unaryExpr();
6158 while (pn &&
6159 (tokenStream.matchToken(TOK_STAR) ||
6160 tokenStream.matchToken(TOK_DIVOP))) {
6161 tt = tokenStream.currentToken().type;
6162 op = tokenStream.currentToken().t_op;
6163 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, unaryExpr(), tc);
6165 return pn;
6168 static JSParseNode *
6169 SetLvalKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6170 JSParseNode *pn, JSParseNode *kid, const char *name)
6172 if (kid->pn_type != TOK_NAME &&
6173 kid->pn_type != TOK_DOT &&
6174 (kid->pn_type != TOK_LP ||
6175 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL && kid->pn_op != JSOP_APPLY)) &&
6176 #if JS_HAS_XML_SUPPORT
6177 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6178 #endif
6179 kid->pn_type != TOK_LB) {
6180 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
6181 return NULL;
6183 if (!CheckStrictAssignment(cx, tc, kid))
6184 return NULL;
6185 pn->pn_kid = kid;
6186 return kid;
6189 static const char incop_name_str[][10] = {"increment", "decrement"};
6191 static JSBool
6192 SetIncOpKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6193 JSParseNode *pn, JSParseNode *kid,
6194 TokenKind tt, JSBool preorder)
6196 JSOp op;
6198 kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6199 if (!kid)
6200 return JS_FALSE;
6201 switch (kid->pn_type) {
6202 case TOK_NAME:
6203 op = (tt == TOK_INC)
6204 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6205 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6206 NoteLValue(cx, kid, tc);
6207 break;
6209 case TOK_DOT:
6210 op = (tt == TOK_INC)
6211 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6212 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6213 break;
6215 case TOK_LP:
6216 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6217 return JS_FALSE;
6218 /* FALL THROUGH */
6219 #if JS_HAS_XML_SUPPORT
6220 case TOK_UNARYOP:
6221 if (kid->pn_op == JSOP_XMLNAME)
6222 kid->pn_op = JSOP_SETXMLNAME;
6223 /* FALL THROUGH */
6224 #endif
6225 case TOK_LB:
6226 op = (tt == TOK_INC)
6227 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6228 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6229 break;
6231 default:
6232 JS_ASSERT(0);
6233 op = JSOP_NOP;
6235 pn->pn_op = op;
6236 return JS_TRUE;
6239 JSParseNode *
6240 Parser::unaryExpr()
6242 JSParseNode *pn, *pn2;
6244 JS_CHECK_RECURSION(context, return NULL);
6246 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6247 switch (tt) {
6248 case TOK_UNARYOP:
6249 case TOK_PLUS:
6250 case TOK_MINUS:
6251 pn = UnaryNode::create(tc);
6252 if (!pn)
6253 return NULL;
6254 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6255 pn->pn_op = tokenStream.currentToken().t_op;
6256 pn2 = unaryExpr();
6257 if (!pn2)
6258 return NULL;
6259 pn->pn_pos.end = pn2->pn_pos.end;
6260 pn->pn_kid = pn2;
6261 break;
6263 case TOK_INC:
6264 case TOK_DEC:
6265 pn = UnaryNode::create(tc);
6266 if (!pn)
6267 return NULL;
6268 pn2 = memberExpr(JS_TRUE);
6269 if (!pn2)
6270 return NULL;
6271 if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, JS_TRUE))
6272 return NULL;
6273 pn->pn_pos.end = pn2->pn_pos.end;
6274 break;
6276 case TOK_DELETE:
6278 pn = UnaryNode::create(tc);
6279 if (!pn)
6280 return NULL;
6281 pn2 = unaryExpr();
6282 if (!pn2)
6283 return NULL;
6284 pn->pn_pos.end = pn2->pn_pos.end;
6287 * Under ECMA3, deleting any unary expression is valid -- it simply
6288 * returns true. Here we fold constants before checking for a call
6289 * expression, in order to rule out delete of a generator expression.
6291 if (!js_FoldConstants(context, pn2, tc))
6292 return NULL;
6293 switch (pn2->pn_type) {
6294 case TOK_LP:
6295 if (pn2->pn_op != JSOP_SETCALL &&
6296 !MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND)) {
6297 return NULL;
6299 break;
6300 case TOK_NAME:
6301 if (!ReportStrictModeError(context, &tokenStream, tc, pn,
6302 JSMSG_DEPRECATED_DELETE_OPERAND))
6303 return NULL;
6304 pn2->pn_op = JSOP_DELNAME;
6305 if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
6306 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6307 break;
6308 default:;
6310 pn->pn_kid = pn2;
6311 break;
6313 case TOK_ERROR:
6314 return NULL;
6316 default:
6317 tokenStream.ungetToken();
6318 pn = memberExpr(JS_TRUE);
6319 if (!pn)
6320 return NULL;
6322 /* Don't look across a newline boundary for a postfix incop. */
6323 if (tokenStream.onCurrentLine(pn->pn_pos)) {
6324 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6325 if (tt == TOK_INC || tt == TOK_DEC) {
6326 (void) tokenStream.getToken();
6327 pn2 = UnaryNode::create(tc);
6328 if (!pn2)
6329 return NULL;
6330 if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, JS_FALSE))
6331 return NULL;
6332 pn2->pn_pos.begin = pn->pn_pos.begin;
6333 pn = pn2;
6336 break;
6338 return pn;
6341 #if JS_HAS_GENERATORS
6344 * A dedicated helper for transplanting the comprehension expression E in
6346 * [E for (V in I)] // array comprehension
6347 * (E for (V in I)) // generator expression
6349 * from its initial location in the AST, on the left of the 'for', to its final
6350 * position on the right. To avoid a separate pass we do this by adjusting the
6351 * blockids and name binding links that were established when E was parsed.
6353 * A generator expression desugars like so:
6355 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6357 * so the transplanter must adjust static level as well as blockid. E's source
6358 * coordinates in root->pn_pos are critical to deciding which binding links to
6359 * preserve and which to cut.
6361 * NB: This is not a general tree transplanter -- it knows in particular that
6362 * the one or more bindings induced by V have not yet been created.
6364 class CompExprTransplanter {
6365 JSParseNode *root;
6366 JSTreeContext *tc;
6367 bool genexp;
6368 uintN adjust;
6369 uintN funcLevel;
6371 public:
6372 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6373 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6377 bool transplant(JSParseNode *pn);
6381 * Any definitions nested within the comprehension expression of a generator
6382 * expression must move "down" one static level, which of course increases the
6383 * upvar-frame-skip count.
6385 static bool
6386 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6388 if (pn->pn_cookie != FREE_UPVAR_COOKIE) {
6389 uintN level = UPVAR_FRAME_SKIP(pn->pn_cookie) + 1;
6391 JS_ASSERT(level >= tc->staticLevel);
6392 if (level >= FREE_STATIC_LEVEL) {
6393 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
6394 JSMSG_TOO_DEEP, js_function_str);
6395 return false;
6398 pn->pn_cookie = MAKE_UPVAR_COOKIE(level, UPVAR_FRAME_SLOT(pn->pn_cookie));
6400 return true;
6403 static void
6404 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6406 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6407 pn->pn_blockid += adjust;
6408 if (pn->pn_blockid >= tc->blockidGen)
6409 tc->blockidGen = pn->pn_blockid + 1;
6412 bool
6413 CompExprTransplanter::transplant(JSParseNode *pn)
6415 if (!pn)
6416 return true;
6418 switch (pn->pn_arity) {
6419 case PN_LIST:
6420 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
6421 transplant(pn2);
6422 if (pn->pn_pos >= root->pn_pos)
6423 AdjustBlockId(pn, adjust, tc);
6424 break;
6426 case PN_TERNARY:
6427 transplant(pn->pn_kid1);
6428 transplant(pn->pn_kid2);
6429 transplant(pn->pn_kid3);
6430 break;
6432 case PN_BINARY:
6433 transplant(pn->pn_left);
6435 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
6436 if (pn->pn_right != pn->pn_left)
6437 transplant(pn->pn_right);
6438 break;
6440 case PN_UNARY:
6441 transplant(pn->pn_kid);
6442 break;
6444 case PN_FUNC:
6447 * Only the first level of transplant recursion through functions needs
6448 * to reparent the funbox, since all descendant functions are correctly
6449 * linked under the top-most funbox. But every visit to this case needs
6450 * to update funbox->level.
6452 * Recall that funbox->level is the static level of the code containing
6453 * the definition or expression of the function and not the static level
6454 * of the function's body.
6456 JSFunctionBox *funbox = pn->pn_funbox;
6458 funbox->level = tc->staticLevel + funcLevel;
6459 if (++funcLevel == 1 && genexp) {
6460 JSFunctionBox *parent = tc->funbox;
6462 JSFunctionBox **funboxp = &tc->parent->functionList;
6463 while (*funboxp != funbox)
6464 funboxp = &(*funboxp)->siblings;
6465 *funboxp = funbox->siblings;
6467 funbox->parent = parent;
6468 funbox->siblings = parent->kids;
6469 parent->kids = funbox;
6470 funbox->level = tc->staticLevel;
6472 /* FALL THROUGH */
6475 case PN_NAME:
6476 transplant(pn->maybeExpr());
6477 if (pn->pn_arity == PN_FUNC)
6478 --funcLevel;
6480 if (pn->pn_defn) {
6481 if (genexp && !BumpStaticLevel(pn, tc))
6482 return false;
6483 } else if (pn->pn_used) {
6484 JS_ASSERT(pn->pn_op != JSOP_NOP);
6485 JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
6487 JSDefinition *dn = pn->pn_lexdef;
6488 JS_ASSERT(dn->pn_defn);
6491 * Adjust the definition's block id only if it is a placeholder not
6492 * to the left of the root node, and if pn is the last use visited
6493 * in the comprehension expression (to avoid adjusting the blockid
6494 * multiple times).
6496 * Non-placeholder definitions within the comprehension expression
6497 * will be visited further below.
6499 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
6500 if (genexp && !BumpStaticLevel(dn, tc))
6501 return false;
6502 AdjustBlockId(dn, adjust, tc);
6505 JSAtom *atom = pn->pn_atom;
6506 #ifdef DEBUG
6507 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
6508 JS_ASSERT(!stmt || stmt != tc->topStmt);
6509 #endif
6510 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
6511 JS_ASSERT(!tc->decls.lookup(atom));
6513 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
6514 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, dn->pn_atom);
6515 if (!ale)
6516 return false;
6518 if (dn->pn_pos >= root->pn_pos) {
6519 tc->parent->lexdeps.remove(tc->parser, atom);
6520 } else {
6521 JSDefinition *dn2 = (JSDefinition *)NameNode::create(dn->pn_atom, tc);
6522 if (!dn2)
6523 return false;
6525 dn2->pn_type = dn->pn_type;
6526 dn2->pn_pos = root->pn_pos;
6527 dn2->pn_defn = true;
6528 dn2->pn_dflags |= PND_PLACEHOLDER;
6530 JSParseNode **pnup = &dn->dn_uses;
6531 JSParseNode *pnu;
6532 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
6533 pnu->pn_lexdef = dn2;
6534 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
6535 pnup = &pnu->pn_link;
6537 dn2->dn_uses = dn->dn_uses;
6538 dn->dn_uses = *pnup;
6539 *pnup = NULL;
6541 dn = dn2;
6544 ALE_SET_DEFN(ale, dn);
6549 if (pn->pn_pos >= root->pn_pos)
6550 AdjustBlockId(pn, adjust, tc);
6551 break;
6553 case PN_NAMESET:
6554 transplant(pn->pn_tree);
6555 break;
6557 return true;
6561 * Starting from a |for| keyword after the first array initialiser element or
6562 * an expression in an open parenthesis, parse the tail of the comprehension
6563 * or generator expression signified by this |for| keyword in context.
6565 * Return null on failure, else return the top-most parse node for the array
6566 * comprehension or generator expression, with a unary node as the body of the
6567 * (possibly nested) for-loop, initialized by |type, op, kid|.
6569 JSParseNode *
6570 Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
6571 TokenKind type, JSOp op)
6573 uintN adjust;
6574 JSParseNode *pn, *pn2, *pn3, **pnp;
6575 JSStmtInfo stmtInfo;
6576 BindData data;
6577 TokenKind tt;
6578 JSAtom *atom;
6580 JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
6582 if (type == TOK_SEMI) {
6584 * Generator expression desugars to an immediately applied lambda that
6585 * yields the next value from a for-in loop (possibly nested, and with
6586 * optional if guard). Make pn be the TOK_LC body node.
6588 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6589 if (!pn)
6590 return NULL;
6591 adjust = pn->pn_blockid - blockid;
6592 } else {
6593 JS_ASSERT(type == TOK_ARRAYPUSH);
6596 * Make a parse-node and literal object representing the block scope of
6597 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
6598 * aka the array initialiser case, has passed the blockid to claim for
6599 * the comprehension's block scope. We allocate that id or one above it
6600 * here, by calling js_PushLexicalScope.
6602 * In the case of a comprehension expression that has nested blocks
6603 * (e.g., let expressions), we will allocate a higher blockid but then
6604 * slide all blocks "to the right" to make room for the comprehension's
6605 * block scope.
6607 adjust = tc->blockid();
6608 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
6609 if (!pn)
6610 return NULL;
6612 JS_ASSERT(blockid <= pn->pn_blockid);
6613 JS_ASSERT(blockid < tc->blockidGen);
6614 JS_ASSERT(tc->bodyid < blockid);
6615 pn->pn_blockid = stmtInfo.blockid = blockid;
6616 JS_ASSERT(adjust < blockid);
6617 adjust = blockid - adjust;
6620 pnp = &pn->pn_expr;
6622 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
6623 transplanter.transplant(kid);
6625 data.pn = NULL;
6626 data.op = JSOP_NOP;
6627 data.binder = BindLet;
6628 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
6630 do {
6632 * FOR node is binary, left is loop control and right is body. Use
6633 * index to count each block-local let-variable on the left-hand side
6634 * of the IN.
6636 pn2 = BinaryNode::create(tc);
6637 if (!pn2)
6638 return NULL;
6640 pn2->pn_op = JSOP_ITER;
6641 pn2->pn_iflags = JSITER_ENUMERATE;
6642 if (tokenStream.matchToken(TOK_NAME)) {
6643 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
6644 pn2->pn_iflags |= JSITER_FOREACH;
6645 else
6646 tokenStream.ungetToken();
6648 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
6650 atom = NULL;
6651 tt = tokenStream.getToken();
6652 switch (tt) {
6653 #if JS_HAS_DESTRUCTURING
6654 case TOK_LB:
6655 case TOK_LC:
6656 tc->flags |= TCF_DECL_DESTRUCTURING;
6657 pn3 = primaryExpr(tt, JS_FALSE);
6658 tc->flags &= ~TCF_DECL_DESTRUCTURING;
6659 if (!pn3)
6660 return NULL;
6661 break;
6662 #endif
6664 case TOK_NAME:
6665 atom = tokenStream.currentToken().t_atom;
6668 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
6669 * JSOP_GETLOCAL here, because we don't yet know the block's depth
6670 * in the operand stack frame. The code generator computes that,
6671 * and it tries to bind all names to slots, so we must let it do
6672 * the deed.
6674 pn3 = NewBindingNode(atom, tc, true);
6675 if (!pn3)
6676 return NULL;
6677 break;
6679 default:
6680 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
6681 JSMSG_NO_VARIABLE_NAME);
6683 case TOK_ERROR:
6684 return NULL;
6687 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
6688 JSParseNode *pn4 = expr();
6689 if (!pn4)
6690 return NULL;
6691 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
6693 switch (tt) {
6694 #if JS_HAS_DESTRUCTURING
6695 case TOK_LB:
6696 case TOK_LC:
6697 if (!CheckDestructuring(context, &data, pn3, NULL, tc))
6698 return NULL;
6700 if (JSVERSION_NUMBER(context) == JSVERSION_1_7) {
6701 /* Destructuring requires [key, value] enumeration in JS1.7. */
6702 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
6703 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
6704 JSMSG_BAD_FOR_LEFTSIDE);
6705 return NULL;
6708 JS_ASSERT(pn2->pn_op == JSOP_ITER);
6709 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
6710 if (!(pn2->pn_iflags & JSITER_FOREACH))
6711 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
6713 break;
6714 #endif
6716 case TOK_NAME:
6717 data.pn = pn3;
6718 if (!data.binder(context, &data, atom, tc))
6719 return NULL;
6720 break;
6722 default:;
6725 pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
6726 if (!pn2->pn_left)
6727 return NULL;
6728 *pnp = pn2;
6729 pnp = &pn2->pn_right;
6730 } while (tokenStream.matchToken(TOK_FOR));
6732 if (tokenStream.matchToken(TOK_IF)) {
6733 pn2 = TernaryNode::create(tc);
6734 if (!pn2)
6735 return NULL;
6736 pn2->pn_kid1 = condition();
6737 if (!pn2->pn_kid1)
6738 return NULL;
6739 *pnp = pn2;
6740 pnp = &pn2->pn_kid2;
6743 pn2 = UnaryNode::create(tc);
6744 if (!pn2)
6745 return NULL;
6746 pn2->pn_type = type;
6747 pn2->pn_op = op;
6748 pn2->pn_kid = kid;
6749 *pnp = pn2;
6751 PopStatement(tc);
6752 return pn;
6755 #if JS_HAS_GENERATOR_EXPRS
6758 * Starting from a |for| keyword after an expression, parse the comprehension
6759 * tail completing this generator expression. Wrap the expression at kid in a
6760 * generator function that is immediately called to evaluate to the generator
6761 * iterator that is the value of this generator expression.
6763 * Callers pass a blank unary node via pn, which generatorExpr fills in as the
6764 * yield expression, which ComprehensionTail in turn wraps in a TOK_SEMI-type
6765 * expression-statement node that constitutes the body of the |for| loop(s) in
6766 * the generator function.
6768 * Note how unlike Python, we do not evaluate the expression to the right of
6769 * the first |in| in the chain of |for| heads. Instead, a generator expression
6770 * is merely sugar for a generator function expression and its application.
6772 JSParseNode *
6773 Parser::generatorExpr(JSParseNode *pn, JSParseNode *kid)
6775 /* Initialize pn, connecting it to kid. */
6776 JS_ASSERT(pn->pn_arity == PN_UNARY);
6777 pn->pn_type = TOK_YIELD;
6778 pn->pn_op = JSOP_YIELD;
6779 pn->pn_parens = true;
6780 pn->pn_pos = kid->pn_pos;
6781 pn->pn_kid = kid;
6782 pn->pn_hidden = true;
6784 /* Make a new node for the desugared generator function. */
6785 JSParseNode *genfn = FunctionNode::create(tc);
6786 if (!genfn)
6787 return NULL;
6788 genfn->pn_type = TOK_FUNCTION;
6789 genfn->pn_op = JSOP_LAMBDA;
6790 JS_ASSERT(!genfn->pn_body);
6791 genfn->pn_dflags = PND_FUNARG;
6794 JSTreeContext *outertc = tc;
6795 JSTreeContext gentc(tc->parser);
6797 JSFunctionBox *funbox = EnterFunction(genfn, &gentc);
6798 if (!funbox)
6799 return NULL;
6802 * We have to dance around a bit to propagate sharp variables from
6803 * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
6804 * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
6805 * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
6806 * do propagate to gentc.
6808 if (outertc->flags & TCF_HAS_SHARPS) {
6809 gentc.flags |= TCF_IN_FUNCTION;
6810 if (!gentc.ensureSharpSlots())
6811 return NULL;
6815 * We assume conservatively that any deoptimization flag in tc->flags
6816 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
6817 * propagate these flags into genfn. For code simplicity we also do
6818 * not detect if the flags were only set in the kid and could be
6819 * removed from tc->flags.
6821 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
6822 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
6823 funbox->tcflags |= gentc.flags;
6824 genfn->pn_funbox = funbox;
6825 genfn->pn_blockid = gentc.bodyid;
6827 JSParseNode *body = comprehensionTail(pn, outertc->blockid());
6828 if (!body)
6829 return NULL;
6830 JS_ASSERT(!genfn->pn_body);
6831 genfn->pn_body = body;
6832 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
6833 genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
6835 if (!LeaveFunction(genfn, &gentc))
6836 return NULL;
6840 * Our result is a call expression that invokes the anonymous generator
6841 * function object.
6843 JSParseNode *result = ListNode::create(tc);
6844 if (!result)
6845 return NULL;
6846 result->pn_type = TOK_LP;
6847 result->pn_op = JSOP_CALL;
6848 result->pn_pos.begin = genfn->pn_pos.begin;
6849 result->initList(genfn);
6850 return result;
6853 static const char js_generator_str[] = "generator";
6855 #endif /* JS_HAS_GENERATOR_EXPRS */
6856 #endif /* JS_HAS_GENERATORS */
6858 JSBool
6859 Parser::argumentList(JSParseNode *listNode)
6861 if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
6862 return JS_TRUE;
6864 do {
6865 JSParseNode *argNode = assignExpr();
6866 if (!argNode)
6867 return JS_FALSE;
6868 #if JS_HAS_GENERATORS
6869 if (argNode->pn_type == TOK_YIELD &&
6870 !argNode->pn_parens &&
6871 tokenStream.peekToken() == TOK_COMMA) {
6872 ReportCompileErrorNumber(context, &tokenStream, argNode, JSREPORT_ERROR,
6873 JSMSG_BAD_GENERATOR_SYNTAX,
6874 js_yield_str);
6875 return JS_FALSE;
6877 #endif
6878 #if JS_HAS_GENERATOR_EXPRS
6879 if (tokenStream.matchToken(TOK_FOR)) {
6880 JSParseNode *pn = UnaryNode::create(tc);
6881 if (!pn)
6882 return JS_FALSE;
6883 argNode = generatorExpr(pn, argNode);
6884 if (!argNode)
6885 return JS_FALSE;
6886 if (listNode->pn_count > 1 ||
6887 tokenStream.peekToken() == TOK_COMMA) {
6888 ReportCompileErrorNumber(context, &tokenStream, argNode, JSREPORT_ERROR,
6889 JSMSG_BAD_GENERATOR_SYNTAX,
6890 js_generator_str);
6891 return JS_FALSE;
6894 #endif
6895 listNode->append(argNode);
6896 } while (tokenStream.matchToken(TOK_COMMA));
6898 if (tokenStream.getToken() != TOK_RP) {
6899 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
6900 JSMSG_PAREN_AFTER_ARGS);
6901 return JS_FALSE;
6903 return JS_TRUE;
6906 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
6907 static JSParseNode *
6908 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
6910 if (pn->pn_type == TOK_FUNCTION) {
6911 JS_ASSERT(pn->pn_arity == PN_FUNC);
6913 JSFunctionBox *funbox = pn->pn_funbox;
6914 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
6915 if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
6916 pn->pn_dflags &= ~PND_FUNARG;
6918 return pn;
6921 JSParseNode *
6922 Parser::memberExpr(JSBool allowCallSyntax)
6924 JSParseNode *pn, *pn2, *pn3;
6926 JS_CHECK_RECURSION(context, return NULL);
6928 /* Check for new expression first. */
6929 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6930 if (tt == TOK_NEW) {
6931 pn = ListNode::create(tc);
6932 if (!pn)
6933 return NULL;
6934 pn2 = memberExpr(JS_FALSE);
6935 if (!pn2)
6936 return NULL;
6937 pn2 = CheckForImmediatelyAppliedLambda(pn2);
6938 pn->pn_op = JSOP_NEW;
6939 pn->initList(pn2);
6940 pn->pn_pos.begin = pn2->pn_pos.begin;
6942 if (tokenStream.matchToken(TOK_LP) && !argumentList(pn))
6943 return NULL;
6944 if (pn->pn_count > ARGC_LIMIT) {
6945 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
6946 JSMSG_TOO_MANY_CON_ARGS);
6947 return NULL;
6949 pn->pn_pos.end = pn->last()->pn_pos.end;
6950 } else {
6951 pn = primaryExpr(tt, JS_FALSE);
6952 if (!pn)
6953 return NULL;
6955 if (pn->pn_type == TOK_ANYNAME ||
6956 pn->pn_type == TOK_AT ||
6957 pn->pn_type == TOK_DBLCOLON) {
6958 pn2 = NewOrRecycledNode(tc);
6959 if (!pn2)
6960 return NULL;
6961 pn2->pn_type = TOK_UNARYOP;
6962 pn2->pn_pos = pn->pn_pos;
6963 pn2->pn_op = JSOP_XMLNAME;
6964 pn2->pn_arity = PN_UNARY;
6965 pn2->pn_parens = false;
6966 pn2->pn_kid = pn;
6967 pn = pn2;
6971 while ((tt = tokenStream.getToken()) > TOK_EOF) {
6972 if (tt == TOK_DOT) {
6973 pn2 = NameNode::create(NULL, tc);
6974 if (!pn2)
6975 return NULL;
6976 #if JS_HAS_XML_SUPPORT
6977 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
6978 pn3 = primaryExpr(tt, JS_TRUE);
6979 if (!pn3)
6980 return NULL;
6982 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
6983 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
6984 pn2->pn_op = JSOP_GETPROP;
6985 pn2->pn_expr = pn;
6986 pn2->pn_atom = pn3->pn_atom;
6987 RecycleTree(pn3, tc);
6988 } else {
6989 if (tt == TOK_LP) {
6990 pn2->pn_type = TOK_FILTER;
6991 pn2->pn_op = JSOP_FILTER;
6993 /* A filtering predicate is like a with statement. */
6994 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6995 } else if (TokenKindIsXML(PN_TYPE(pn3))) {
6996 pn2->pn_type = TOK_LB;
6997 pn2->pn_op = JSOP_GETELEM;
6998 } else {
6999 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7000 JSMSG_NAME_AFTER_DOT);
7001 return NULL;
7003 pn2->pn_arity = PN_BINARY;
7004 pn2->pn_left = pn;
7005 pn2->pn_right = pn3;
7007 #else
7008 MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME, JSMSG_NAME_AFTER_DOT, TSF_KEYWORD_IS_NAME);
7009 pn2->pn_op = JSOP_GETPROP;
7010 pn2->pn_expr = pn;
7011 pn2->pn_atom = tokenStream.currentToken().t_atom;
7012 #endif
7013 pn2->pn_pos.begin = pn->pn_pos.begin;
7014 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7015 #if JS_HAS_XML_SUPPORT
7016 } else if (tt == TOK_DBLDOT) {
7017 pn2 = BinaryNode::create(tc);
7018 if (!pn2)
7019 return NULL;
7020 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7021 pn3 = primaryExpr(tt, JS_TRUE);
7022 if (!pn3)
7023 return NULL;
7024 tt = PN_TYPE(pn3);
7025 if (tt == TOK_NAME && !pn3->pn_parens) {
7026 pn3->pn_type = TOK_STRING;
7027 pn3->pn_arity = PN_NULLARY;
7028 pn3->pn_op = JSOP_QNAMEPART;
7029 } else if (!TokenKindIsXML(tt)) {
7030 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7031 JSMSG_NAME_AFTER_DOT);
7032 return NULL;
7034 pn2->pn_op = JSOP_DESCENDANTS;
7035 pn2->pn_left = pn;
7036 pn2->pn_right = pn3;
7037 pn2->pn_pos.begin = pn->pn_pos.begin;
7038 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7039 #endif
7040 } else if (tt == TOK_LB) {
7041 pn2 = BinaryNode::create(tc);
7042 if (!pn2)
7043 return NULL;
7044 pn3 = expr();
7045 if (!pn3)
7046 return NULL;
7048 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
7049 pn2->pn_pos.begin = pn->pn_pos.begin;
7050 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7053 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7054 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7055 * the interpreter from fast property access. However, if the
7056 * bracketed string is a uint32, we rewrite pn3 to be a number
7057 * instead of a string.
7059 do {
7060 if (pn3->pn_type == TOK_STRING) {
7061 jsuint index;
7063 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
7064 pn2->pn_type = TOK_DOT;
7065 pn2->pn_op = JSOP_GETPROP;
7066 pn2->pn_arity = PN_NAME;
7067 pn2->pn_expr = pn;
7068 pn2->pn_atom = pn3->pn_atom;
7069 break;
7071 pn3->pn_type = TOK_NUMBER;
7072 pn3->pn_op = JSOP_DOUBLE;
7073 pn3->pn_dval = index;
7075 pn2->pn_op = JSOP_GETELEM;
7076 pn2->pn_left = pn;
7077 pn2->pn_right = pn3;
7078 } while (0);
7079 } else if (allowCallSyntax && tt == TOK_LP) {
7080 pn2 = ListNode::create(tc);
7081 if (!pn2)
7082 return NULL;
7083 pn2->pn_op = JSOP_CALL;
7085 pn = CheckForImmediatelyAppliedLambda(pn);
7086 if (pn->pn_op == JSOP_NAME) {
7087 if (pn->pn_atom == context->runtime->atomState.evalAtom) {
7088 /* Select JSOP_EVAL and flag tc as heavyweight. */
7089 pn2->pn_op = JSOP_EVAL;
7090 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7092 } else if (pn->pn_op == JSOP_GETPROP) {
7093 if (pn->pn_atom == context->runtime->atomState.applyAtom ||
7094 pn->pn_atom == context->runtime->atomState.callAtom) {
7095 /* Select JSOP_APPLY given foo.apply(...). */
7096 pn2->pn_op = JSOP_APPLY;
7100 pn2->initList(pn);
7101 pn2->pn_pos.begin = pn->pn_pos.begin;
7103 if (!argumentList(pn2))
7104 return NULL;
7105 if (pn2->pn_count > ARGC_LIMIT) {
7106 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7107 JSMSG_TOO_MANY_FUN_ARGS);
7108 return NULL;
7110 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7111 } else {
7112 tokenStream.ungetToken();
7113 return pn;
7116 pn = pn2;
7118 if (tt == TOK_ERROR)
7119 return NULL;
7120 return pn;
7123 JSParseNode *
7124 Parser::bracketedExpr()
7126 uintN oldflags;
7127 JSParseNode *pn;
7130 * Always accept the 'in' operator in a parenthesized expression,
7131 * where it's unambiguous, even if we might be parsing the init of a
7132 * for statement.
7134 oldflags = tc->flags;
7135 tc->flags &= ~TCF_IN_FOR_INIT;
7136 pn = expr();
7137 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7138 return pn;
7141 #if JS_HAS_XML_SUPPORT
7143 JSParseNode *
7144 Parser::endBracketedExpr()
7146 JSParseNode *pn;
7148 pn = bracketedExpr();
7149 if (!pn)
7150 return NULL;
7152 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7153 return pn;
7157 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7159 * AttributeIdentifier:
7160 * @ PropertySelector
7161 * @ QualifiedIdentifier
7162 * @ [ Expression ]
7164 * PropertySelector:
7165 * Identifier
7168 * QualifiedIdentifier:
7169 * PropertySelector :: PropertySelector
7170 * PropertySelector :: [ Expression ]
7172 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7174 * AttributeIdentifier:
7175 * @ QualifiedIdentifier
7176 * @ [ Expression ]
7178 * PropertySelector:
7179 * Identifier
7182 * QualifiedIdentifier:
7183 * PropertySelector :: PropertySelector
7184 * PropertySelector :: [ Expression ]
7185 * PropertySelector
7187 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7188 * for that rule to result in a name node, but ECMA-357 extends the grammar
7189 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7191 * QualifiedIdentifier:
7192 * PropertySelector QualifiedSuffix
7194 * QualifiedSuffix:
7195 * :: PropertySelector
7196 * :: [ Expression ]
7197 * /nothing/
7199 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7201 * PrimaryExpression:
7202 * Identifier QualifiedSuffix
7204 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7205 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7207 JSParseNode *
7208 Parser::propertySelector()
7210 JSParseNode *pn;
7212 pn = NullaryNode::create(tc);
7213 if (!pn)
7214 return NULL;
7215 if (pn->pn_type == TOK_STAR) {
7216 pn->pn_type = TOK_ANYNAME;
7217 pn->pn_op = JSOP_ANYNAME;
7218 pn->pn_atom = context->runtime->atomState.starAtom;
7219 } else {
7220 JS_ASSERT(pn->pn_type == TOK_NAME);
7221 pn->pn_op = JSOP_QNAMEPART;
7222 pn->pn_arity = PN_NAME;
7223 pn->pn_atom = tokenStream.currentToken().t_atom;
7224 pn->pn_cookie = FREE_UPVAR_COOKIE;
7226 return pn;
7229 JSParseNode *
7230 Parser::qualifiedSuffix(JSParseNode *pn)
7232 JSParseNode *pn2, *pn3;
7233 TokenKind tt;
7235 JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
7236 pn2 = NameNode::create(NULL, tc);
7237 if (!pn2)
7238 return NULL;
7240 /* Left operand of :: must be evaluated if it is an identifier. */
7241 if (pn->pn_op == JSOP_QNAMEPART)
7242 pn->pn_op = JSOP_NAME;
7244 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7245 if (tt == TOK_STAR || tt == TOK_NAME) {
7246 /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7247 pn2->pn_op = JSOP_QNAMECONST;
7248 pn2->pn_pos.begin = pn->pn_pos.begin;
7249 pn2->pn_atom = (tt == TOK_STAR)
7250 ? context->runtime->atomState.starAtom
7251 : tokenStream.currentToken().t_atom;
7252 pn2->pn_expr = pn;
7253 pn2->pn_cookie = FREE_UPVAR_COOKIE;
7254 return pn2;
7257 if (tt != TOK_LB) {
7258 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7259 JSMSG_SYNTAX_ERROR);
7260 return NULL;
7262 pn3 = endBracketedExpr();
7263 if (!pn3)
7264 return NULL;
7266 pn2->pn_op = JSOP_QNAME;
7267 pn2->pn_arity = PN_BINARY;
7268 pn2->pn_pos.begin = pn->pn_pos.begin;
7269 pn2->pn_pos.end = pn3->pn_pos.end;
7270 pn2->pn_left = pn;
7271 pn2->pn_right = pn3;
7272 return pn2;
7275 JSParseNode *
7276 Parser::qualifiedIdentifier()
7278 JSParseNode *pn;
7280 pn = propertySelector();
7281 if (!pn)
7282 return NULL;
7283 if (tokenStream.matchToken(TOK_DBLCOLON)) {
7284 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7285 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7286 pn = qualifiedSuffix(pn);
7288 return pn;
7291 JSParseNode *
7292 Parser::attributeIdentifier()
7294 JSParseNode *pn, *pn2;
7295 TokenKind tt;
7297 JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
7298 pn = UnaryNode::create(tc);
7299 if (!pn)
7300 return NULL;
7301 pn->pn_op = JSOP_TOATTRNAME;
7302 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7303 if (tt == TOK_STAR || tt == TOK_NAME) {
7304 pn2 = qualifiedIdentifier();
7305 } else if (tt == TOK_LB) {
7306 pn2 = endBracketedExpr();
7307 } else {
7308 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7309 JSMSG_SYNTAX_ERROR);
7310 return NULL;
7312 if (!pn2)
7313 return NULL;
7314 pn->pn_kid = pn2;
7315 return pn;
7319 * Make a TOK_LC unary node whose pn_kid is an expression.
7321 JSParseNode *
7322 Parser::xmlExpr(JSBool inTag)
7324 JSParseNode *pn, *pn2;
7326 JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
7327 pn = UnaryNode::create(tc);
7328 if (!pn)
7329 return NULL;
7332 * Turn off XML tag mode. We save the old value of the flag because it may
7333 * already be off: XMLExpr is called both from within a tag, and from
7334 * within text contained in an element, but outside of any start, end, or
7335 * point tag.
7337 bool oldflag = tokenStream.isXMLTagMode();
7338 tokenStream.setXMLTagMode(false);
7339 pn2 = expr();
7340 if (!pn2)
7341 return NULL;
7343 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7344 tokenStream.setXMLTagMode(oldflag);
7345 pn->pn_kid = pn2;
7346 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7347 return pn;
7351 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7352 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7353 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7354 * child of a container tag.
7356 JSParseNode *
7357 Parser::xmlAtomNode()
7359 JSParseNode *pn = NullaryNode::create(tc);
7360 if (!pn)
7361 return NULL;
7362 const Token &tok = tokenStream.currentToken();
7363 pn->pn_op = tok.t_op;
7364 pn->pn_atom = tok.t_atom;
7365 if (tok.type == TOK_XMLPI)
7366 pn->pn_atom2 = tok.t_atom2;
7367 return pn;
7371 * Parse the productions:
7373 * XMLNameExpr:
7374 * XMLName XMLNameExpr?
7375 * { Expr } XMLNameExpr?
7377 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7378 * a list of names and/or expressions, a single expression, or a single name.
7379 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7380 * will be TOK_LC.
7382 JSParseNode *
7383 Parser::xmlNameExpr()
7385 JSParseNode *pn, *pn2, *list;
7386 TokenKind tt;
7388 pn = list = NULL;
7389 do {
7390 tt = tokenStream.currentToken().type;
7391 if (tt == TOK_LC) {
7392 pn2 = xmlExpr(JS_TRUE);
7393 if (!pn2)
7394 return NULL;
7395 } else {
7396 JS_ASSERT(tt == TOK_XMLNAME);
7397 pn2 = xmlAtomNode();
7398 if (!pn2)
7399 return NULL;
7402 if (!pn) {
7403 pn = pn2;
7404 } else {
7405 if (!list) {
7406 list = ListNode::create(tc);
7407 if (!list)
7408 return NULL;
7409 list->pn_type = TOK_XMLNAME;
7410 list->pn_pos.begin = pn->pn_pos.begin;
7411 list->initList(pn);
7412 list->pn_xflags = PNX_CANTFOLD;
7413 pn = list;
7415 pn->pn_pos.end = pn2->pn_pos.end;
7416 pn->append(pn2);
7418 } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
7420 tokenStream.ungetToken();
7421 return pn;
7425 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7426 * at compile time into a JSXML tree.
7428 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
7429 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
7430 : (pn)->pn_type != TOK_LC)
7433 * Parse the productions:
7435 * XMLTagContent:
7436 * XMLNameExpr
7437 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
7438 * XMLTagContent S XMLNameExpr S? = S? { Expr }
7440 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
7441 * produces a list of name and attribute values and/or braced expressions, a
7442 * single expression, or a single name.
7444 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
7445 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
7446 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
7447 * we parsed exactly one expression.
7449 JSParseNode *
7450 Parser::xmlTagContent(TokenKind tagtype, JSAtom **namep)
7452 JSParseNode *pn, *pn2, *list;
7453 TokenKind tt;
7455 pn = xmlNameExpr();
7456 if (!pn)
7457 return NULL;
7458 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
7459 list = NULL;
7461 while (tokenStream.matchToken(TOK_XMLSPACE)) {
7462 tt = tokenStream.getToken();
7463 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7464 tokenStream.ungetToken();
7465 break;
7468 pn2 = xmlNameExpr();
7469 if (!pn2)
7470 return NULL;
7471 if (!list) {
7472 list = ListNode::create(tc);
7473 if (!list)
7474 return NULL;
7475 list->pn_type = tagtype;
7476 list->pn_pos.begin = pn->pn_pos.begin;
7477 list->initList(pn);
7478 pn = list;
7480 pn->append(pn2);
7481 if (!XML_FOLDABLE(pn2))
7482 pn->pn_xflags |= PNX_CANTFOLD;
7484 tokenStream.matchToken(TOK_XMLSPACE);
7485 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
7486 tokenStream.matchToken(TOK_XMLSPACE);
7488 tt = tokenStream.getToken();
7489 if (tt == TOK_XMLATTR) {
7490 pn2 = xmlAtomNode();
7491 } else if (tt == TOK_LC) {
7492 pn2 = xmlExpr(JS_TRUE);
7493 pn->pn_xflags |= PNX_CANTFOLD;
7494 } else {
7495 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7496 JSMSG_BAD_XML_ATTR_VALUE);
7497 return NULL;
7499 if (!pn2)
7500 return NULL;
7501 pn->pn_pos.end = pn2->pn_pos.end;
7502 pn->append(pn2);
7505 return pn;
7508 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
7509 JS_BEGIN_MACRO \
7510 if ((tt) <= TOK_EOF) { \
7511 if ((tt) == TOK_EOF) { \
7512 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR, \
7513 JSMSG_END_OF_XML_SOURCE); \
7515 return result; \
7517 JS_END_MACRO
7520 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
7521 * that opens the end tag for the container.
7523 JSBool
7524 Parser::xmlElementContent(JSParseNode *pn)
7526 tokenStream.setXMLTagMode(false);
7527 for (;;) {
7528 TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
7529 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7531 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
7532 JSAtom *textAtom = tokenStream.currentToken().t_atom;
7533 if (textAtom) {
7534 /* Non-zero-length XML text scanned. */
7535 JSParseNode *pn2 = xmlAtomNode();
7536 if (!pn2)
7537 return JS_FALSE;
7538 pn->pn_pos.end = pn2->pn_pos.end;
7539 pn->append(pn2);
7542 tt = tokenStream.getToken(TSF_OPERAND);
7543 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
7544 if (tt == TOK_XMLETAGO)
7545 break;
7547 JSParseNode *pn2;
7548 if (tt == TOK_LC) {
7549 pn2 = xmlExpr(JS_FALSE);
7550 pn->pn_xflags |= PNX_CANTFOLD;
7551 } else if (tt == TOK_XMLSTAGO) {
7552 pn2 = xmlElementOrList(JS_FALSE);
7553 if (pn2) {
7554 pn2->pn_xflags &= ~PNX_XMLROOT;
7555 pn->pn_xflags |= pn2->pn_xflags;
7557 } else {
7558 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
7559 tt == TOK_XMLPI);
7560 pn2 = xmlAtomNode();
7562 if (!pn2)
7563 return JS_FALSE;
7564 pn->pn_pos.end = pn2->pn_pos.end;
7565 pn->append(pn2);
7567 tokenStream.setXMLTagMode(true);
7569 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
7570 return JS_TRUE;
7574 * Return a PN_LIST node containing an XML or XMLList Initialiser.
7576 JSParseNode *
7577 Parser::xmlElementOrList(JSBool allowList)
7579 JSParseNode *pn, *pn2, *list;
7580 TokenKind tt;
7581 JSAtom *startAtom, *endAtom;
7583 JS_CHECK_RECURSION(context, return NULL);
7585 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
7586 pn = ListNode::create(tc);
7587 if (!pn)
7588 return NULL;
7590 tokenStream.setXMLTagMode(true);
7591 tt = tokenStream.getToken();
7592 if (tt == TOK_ERROR)
7593 return NULL;
7595 if (tt == TOK_XMLNAME || tt == TOK_LC) {
7597 * XMLElement. Append the tag and its contents, if any, to pn.
7599 pn2 = xmlTagContent(TOK_XMLSTAGO, &startAtom);
7600 if (!pn2)
7601 return NULL;
7602 tokenStream.matchToken(TOK_XMLSPACE);
7604 tt = tokenStream.getToken();
7605 if (tt == TOK_XMLPTAGC) {
7606 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
7607 if (pn2->pn_type == TOK_XMLSTAGO) {
7608 pn->makeEmpty();
7609 RecycleTree(pn, tc);
7610 pn = pn2;
7611 } else {
7612 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
7613 pn2->pn_type == TOK_LC);
7614 pn->initList(pn2);
7615 if (!XML_FOLDABLE(pn2))
7616 pn->pn_xflags |= PNX_CANTFOLD;
7618 pn->pn_type = TOK_XMLPTAGC;
7619 pn->pn_xflags |= PNX_XMLROOT;
7620 } else {
7621 /* We had better have a tag-close (>) at this point. */
7622 if (tt != TOK_XMLTAGC) {
7623 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7624 JSMSG_BAD_XML_TAG_SYNTAX);
7625 return NULL;
7627 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7629 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
7630 if (pn2->pn_type != TOK_XMLSTAGO) {
7631 pn->initList(pn2);
7632 if (!XML_FOLDABLE(pn2))
7633 pn->pn_xflags |= PNX_CANTFOLD;
7634 pn2 = pn;
7635 pn = ListNode::create(tc);
7636 if (!pn)
7637 return NULL;
7640 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
7641 pn->pn_type = TOK_XMLELEM;
7642 pn->pn_pos.begin = pn2->pn_pos.begin;
7643 pn->initList(pn2);
7644 if (!XML_FOLDABLE(pn2))
7645 pn->pn_xflags |= PNX_CANTFOLD;
7646 pn->pn_xflags |= PNX_XMLROOT;
7648 /* Get element contents and delimiting end-tag-open sequence. */
7649 if (!xmlElementContent(pn))
7650 return NULL;
7652 tt = tokenStream.getToken();
7653 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
7654 if (tt != TOK_XMLNAME && tt != TOK_LC) {
7655 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7656 JSMSG_BAD_XML_TAG_SYNTAX);
7657 return NULL;
7660 /* Parse end tag; check mismatch at compile-time if we can. */
7661 pn2 = xmlTagContent(TOK_XMLETAGO, &endAtom);
7662 if (!pn2)
7663 return NULL;
7664 if (pn2->pn_type == TOK_XMLETAGO) {
7665 /* Oops, end tag has attributes! */
7666 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7667 JSMSG_BAD_XML_TAG_SYNTAX);
7668 return NULL;
7670 if (endAtom && startAtom && endAtom != startAtom) {
7671 JSString *str = ATOM_TO_STRING(startAtom);
7673 /* End vs. start tag name mismatch: point to the tag name. */
7674 ReportCompileErrorNumber(context, &tokenStream, pn2, JSREPORT_UC | JSREPORT_ERROR,
7675 JSMSG_XML_TAG_NAME_MISMATCH, str->chars());
7676 return NULL;
7679 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
7680 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
7681 list = ListNode::create(tc);
7682 if (!list)
7683 return NULL;
7684 list->pn_type = TOK_XMLETAGO;
7685 list->initList(pn2);
7686 pn->append(list);
7687 if (!XML_FOLDABLE(pn2)) {
7688 list->pn_xflags |= PNX_CANTFOLD;
7689 pn->pn_xflags |= PNX_CANTFOLD;
7692 tokenStream.matchToken(TOK_XMLSPACE);
7693 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
7696 /* Set pn_op now that pn has been updated to its final value. */
7697 pn->pn_op = JSOP_TOXML;
7698 } else if (allowList && tt == TOK_XMLTAGC) {
7699 /* XMLList Initialiser. */
7700 pn->pn_type = TOK_XMLLIST;
7701 pn->pn_op = JSOP_TOXMLLIST;
7702 pn->makeEmpty();
7703 pn->pn_xflags |= PNX_XMLROOT;
7704 if (!xmlElementContent(pn))
7705 return NULL;
7707 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
7708 } else {
7709 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7710 JSMSG_BAD_XML_NAME_SYNTAX);
7711 return NULL;
7713 tokenStream.setXMLTagMode(false);
7715 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7716 return pn;
7719 JSParseNode *
7720 Parser::xmlElementOrListRoot(JSBool allowList)
7722 uint32 oldopts;
7723 JSParseNode *pn;
7726 * Force XML support to be enabled so that comments and CDATA literals
7727 * are recognized, instead of <! followed by -- starting an HTML comment
7728 * to end of line (used in script tags to hide content from old browsers
7729 * that don't recognize <script>).
7731 oldopts = JS_SetOptions(context, context->options | JSOPTION_XML);
7732 pn = xmlElementOrList(allowList);
7733 JS_SetOptions(context, oldopts);
7734 return pn;
7737 JSParseNode *
7738 Parser::parseXMLText(JSObject *chain, bool allowList)
7741 * Push a compiler frame if we have no frames, or if the top frame is a
7742 * lightweight function activation, or if its scope chain doesn't match
7743 * the one passed to us.
7745 JSTreeContext xmltc(this);
7746 xmltc.scopeChain = chain;
7748 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
7749 tokenStream.setXMLOnlyMode();
7750 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
7752 JSParseNode *pn;
7753 if (tt != TOK_XMLSTAGO) {
7754 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7755 JSMSG_BAD_XML_MARKUP);
7756 pn = NULL;
7757 } else {
7758 pn = xmlElementOrListRoot(allowList);
7760 tokenStream.setXMLOnlyMode(false);
7762 return pn;
7765 #endif /* JS_HAS_XMLSUPPORT */
7767 #if JS_HAS_BLOCK_SCOPE
7769 * Check whether blockid is an active scoping statement in tc. This code is
7770 * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
7771 * (below) where the hits come from Scheme-ish let bindings in for loop heads
7772 * and let blocks and expressions (not let declarations).
7774 * Unlike let declarations ("let as the new var"), which is a kind of letrec
7775 * due to hoisting, let in a for loop head, let block, or let expression acts
7776 * like Scheme's let: initializers are evaluated without the new let bindings
7777 * being in scope.
7779 * Name binding analysis is eager with fixups, rather than multi-pass, and let
7780 * bindings push on the front of the tc->decls JSAtomList (either the singular
7781 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
7782 * scope bindings of the same name.
7784 * This simplifies binding lookup code at the price of a linear search here,
7785 * but only if code uses let (var predominates), and even then this function's
7786 * loop iterates more than once only in crazy cases.
7788 static inline bool
7789 BlockIdInScope(uintN blockid, JSTreeContext *tc)
7791 if (blockid > tc->blockid())
7792 return false;
7793 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
7794 if (stmt->blockid == blockid)
7795 return true;
7797 return false;
7799 #endif
7801 JSParseNode *
7802 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
7804 JSParseNode *pn, *pn2, *pn3;
7805 JSOp op;
7807 JS_CHECK_RECURSION(context, return NULL);
7809 switch (tt) {
7810 case TOK_FUNCTION:
7811 #if JS_HAS_XML_SUPPORT
7812 if (tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
7813 pn2 = NullaryNode::create(tc);
7814 if (!pn2)
7815 return NULL;
7816 pn2->pn_type = TOK_FUNCTION;
7817 pn = qualifiedSuffix(pn2);
7818 if (!pn)
7819 return NULL;
7820 break;
7822 #endif
7823 pn = functionExpr();
7824 if (!pn)
7825 return NULL;
7826 break;
7828 case TOK_LB:
7830 JSBool matched;
7831 jsuint index;
7833 pn = ListNode::create(tc);
7834 if (!pn)
7835 return NULL;
7836 pn->pn_type = TOK_RB;
7837 pn->pn_op = JSOP_NEWINIT;
7838 pn->makeEmpty();
7840 #if JS_HAS_GENERATORS
7841 pn->pn_blockid = tc->blockidGen;
7842 #endif
7844 matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
7845 if (!matched) {
7846 for (index = 0; ; index++) {
7847 if (index == JS_ARGS_LENGTH_MAX) {
7848 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
7849 JSMSG_ARRAY_INIT_TOO_BIG);
7850 return NULL;
7853 tt = tokenStream.peekToken(TSF_OPERAND);
7854 if (tt == TOK_RB) {
7855 pn->pn_xflags |= PNX_ENDCOMMA;
7856 break;
7859 if (tt == TOK_COMMA) {
7860 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
7861 tokenStream.matchToken(TOK_COMMA);
7862 pn2 = NullaryNode::create(tc);
7863 pn->pn_xflags |= PNX_HOLEY;
7864 } else {
7865 pn2 = assignExpr();
7867 if (!pn2)
7868 return NULL;
7869 pn->append(pn2);
7871 if (tt != TOK_COMMA) {
7872 /* If we didn't already match TOK_COMMA in above case. */
7873 if (!tokenStream.matchToken(TOK_COMMA))
7874 break;
7878 #if JS_HAS_GENERATORS
7880 * At this point, (index == 0 && pn->pn_count != 0) implies one
7881 * element initialiser was parsed.
7883 * An array comprehension of the form:
7885 * [i * j for (i in o) for (j in p) if (i != j)]
7887 * translates to roughly the following let expression:
7889 * let (array = new Array, i, j) {
7890 * for (i in o) let {
7891 * for (j in p)
7892 * if (i != j)
7893 * array.push(i * j)
7895 * array
7898 * where array is a nameless block-local variable. The "roughly"
7899 * means that an implementation may optimize away the array.push.
7900 * An array comprehension opens exactly one block scope, no matter
7901 * how many for heads it contains.
7903 * Each let () {...} or for (let ...) ... compiles to:
7905 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
7907 * where <o> is a literal object representing the block scope,
7908 * with <n> properties, naming each var declared in the block.
7910 * Each var declaration in a let-block binds a name in <o> at
7911 * compile time, and allocates a slot on the operand stack at
7912 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
7913 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
7914 * JSOP_FORLOCAL. These ops all have an immediate operand, the
7915 * local slot's stack index from fp->spbase.
7917 * The array comprehension iteration step, array.push(i * j) in
7918 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
7919 * where <array> is the index of array's stack slot.
7921 if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
7922 JSParseNode *pnexp, *pntop;
7924 /* Relabel pn as an array comprehension node. */
7925 pn->pn_type = TOK_ARRAYCOMP;
7928 * Remove the comprehension expression from pn's linked list
7929 * and save it via pnexp. We'll re-install it underneath the
7930 * ARRAYPUSH node after we parse the rest of the comprehension.
7932 pnexp = pn->last();
7933 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
7934 pn->pn_tail = (--pn->pn_count == 1)
7935 ? &pn->pn_head->pn_next
7936 : &pn->pn_head;
7937 *pn->pn_tail = NULL;
7939 pntop = comprehensionTail(pnexp, pn->pn_blockid,
7940 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
7941 if (!pntop)
7942 return NULL;
7943 pn->append(pntop);
7945 #endif /* JS_HAS_GENERATORS */
7947 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
7949 pn->pn_pos.end = tokenStream.currentToken().pos.end;
7950 return pn;
7953 case TOK_LC:
7955 JSBool afterComma;
7956 JSParseNode *pnval;
7959 * A map from property names we've seen thus far to bit masks.
7960 * (We use ALE_INDEX/ALE_SET_INDEX). An atom's mask includes
7961 * JSPROP_SETTER if we've seen a setter for it, JSPROP_GETTER
7962 * if we've seen as getter, and both of those if we've just
7963 * seen an ordinary value.
7965 JSAutoAtomList seen(tc->parser);
7967 pn = ListNode::create(tc);
7968 if (!pn)
7969 return NULL;
7970 pn->pn_type = TOK_RC;
7971 pn->pn_op = JSOP_NEWINIT;
7972 pn->makeEmpty();
7974 afterComma = JS_FALSE;
7975 for (;;) {
7976 JSAtom *atom;
7977 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7978 switch (tt) {
7979 case TOK_NUMBER:
7980 pn3 = NullaryNode::create(tc);
7981 if (!pn3)
7982 return NULL;
7983 pn3->pn_dval = tokenStream.currentToken().t_dval;
7984 if (tc->needStrictChecks()) {
7985 atom = js_AtomizeDouble(context, pn3->pn_dval);
7986 if (!atom)
7987 return NULL;
7988 } else {
7989 atom = NULL; /* for the compiler */
7991 break;
7992 case TOK_NAME:
7994 atom = tokenStream.currentToken().t_atom;
7995 if (atom == context->runtime->atomState.getAtom)
7996 op = JSOP_GETTER;
7997 else if (atom == context->runtime->atomState.setAtom)
7998 op = JSOP_SETTER;
7999 else
8000 goto property_name;
8002 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
8003 if (tt == TOK_NAME || tt == TOK_STRING) {
8004 atom = tokenStream.currentToken().t_atom;
8005 pn3 = NameNode::create(atom, tc);
8006 if (!pn3)
8007 return NULL;
8008 } else if (tt == TOK_NUMBER) {
8009 pn3 = NullaryNode::create(tc);
8010 if (!pn3)
8011 return NULL;
8012 pn3->pn_dval = tokenStream.currentToken().t_dval;
8013 if (tc->needStrictChecks()) {
8014 atom = js_AtomizeDouble(context, pn3->pn_dval);
8015 if (!atom)
8016 return NULL;
8017 } else {
8018 atom = NULL; /* for the compiler */
8020 } else {
8021 tokenStream.ungetToken();
8022 goto property_name;
8025 /* We have to fake a 'function' token here. */
8026 tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
8027 pn2 = functionDef(JSFUN_LAMBDA, false);
8028 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
8029 goto skip;
8031 property_name:
8032 case TOK_STRING:
8033 atom = tokenStream.currentToken().t_atom;
8034 pn3 = NullaryNode::create(tc);
8035 if (!pn3)
8036 return NULL;
8037 pn3->pn_atom = atom;
8038 break;
8039 case TOK_RC:
8040 goto end_obj_init;
8041 default:
8042 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8043 JSMSG_BAD_PROP_ID);
8044 return NULL;
8047 op = JSOP_INITPROP;
8048 tt = tokenStream.getToken();
8049 if (tt == TOK_COLON) {
8050 pnval = assignExpr();
8051 } else {
8052 #if JS_HAS_DESTRUCTURING_SHORTHAND
8053 if (tt != TOK_COMMA && tt != TOK_RC) {
8054 #endif
8055 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8056 JSMSG_COLON_AFTER_ID);
8057 return NULL;
8058 #if JS_HAS_DESTRUCTURING_SHORTHAND
8062 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8063 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8065 tokenStream.ungetToken();
8066 pn->pn_xflags |= PNX_DESTRUCT;
8067 pnval = pn3;
8068 if (pnval->pn_type == TOK_NAME) {
8069 pnval->pn_arity = PN_NAME;
8070 ((NameNode *)pnval)->initCommon(tc);
8072 #endif
8075 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
8076 skip:
8077 if (!pn2)
8078 return NULL;
8079 pn->append(pn2);
8082 * In strict mode code, check for duplicate property names. Treat
8083 * getters and setters as distinct attributes of each property. A
8084 * plain old value conflicts with a getter or a setter.
8086 if (tc->needStrictChecks()) {
8087 unsigned attributesMask;
8088 if (op == JSOP_INITPROP) {
8089 attributesMask = JSPROP_GETTER | JSPROP_SETTER;
8090 } else if (op == JSOP_GETTER) {
8091 attributesMask = JSPROP_GETTER;
8092 } else if (op == JSOP_SETTER) {
8093 attributesMask = JSPROP_SETTER;
8094 } else {
8095 JS_NOT_REACHED("bad opcode in object initializer");
8096 attributesMask = 0;
8100 * Use only string-valued atoms for detecting duplicate
8101 * properties so that 1 and "1" properly collide.
8103 if (ATOM_IS_DOUBLE(atom)) {
8104 JSString *str = js_NumberToString(context, pn3->pn_dval);
8105 if (!str)
8106 return JS_FALSE;
8107 atom = js_AtomizeString(context, str, 0);
8108 if (!atom)
8109 return JS_FALSE;
8112 JSAtomListElement *ale = seen.lookup(atom);
8113 if (ale) {
8114 if (ALE_INDEX(ale) & attributesMask) {
8115 const char *name = js_AtomToPrintableString(context, atom);
8116 if (!name ||
8117 !ReportStrictModeError(context, &tokenStream, tc, NULL,
8118 JSMSG_DUPLICATE_PROPERTY, name)) {
8119 return NULL;
8122 ALE_SET_INDEX(ale, attributesMask | ALE_INDEX(ale));
8123 } else {
8124 ale = seen.add(tc->parser, atom);
8125 if (!ale)
8126 return NULL;
8127 ALE_SET_INDEX(ale, attributesMask);
8131 tt = tokenStream.getToken();
8132 if (tt == TOK_RC)
8133 goto end_obj_init;
8134 if (tt != TOK_COMMA) {
8135 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8136 JSMSG_CURLY_AFTER_LIST);
8137 return NULL;
8139 afterComma = JS_TRUE;
8142 end_obj_init:
8143 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8144 return pn;
8147 #if JS_HAS_BLOCK_SCOPE
8148 case TOK_LET:
8149 pn = letBlock(JS_FALSE);
8150 if (!pn)
8151 return NULL;
8152 break;
8153 #endif
8155 #if JS_HAS_SHARP_VARS
8156 case TOK_DEFSHARP:
8157 pn = UnaryNode::create(tc);
8158 if (!pn)
8159 return NULL;
8160 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8161 tt = tokenStream.getToken(TSF_OPERAND);
8162 if (tt == TOK_USESHARP || tt == TOK_DEFSHARP ||
8163 #if JS_HAS_XML_SUPPORT
8164 tt == TOK_STAR || tt == TOK_AT ||
8165 tt == TOK_XMLSTAGO /* XXXbe could be sharp? */ ||
8166 #endif
8167 tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_PRIMARY) {
8168 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8169 JSMSG_BAD_SHARP_VAR_DEF);
8170 return NULL;
8172 pn->pn_kid = primaryExpr(tt, JS_FALSE);
8173 if (!pn->pn_kid)
8174 return NULL;
8175 if (!tc->ensureSharpSlots())
8176 return NULL;
8177 break;
8179 case TOK_USESHARP:
8180 /* Check for forward/dangling references at runtime, to allow eval. */
8181 pn = NullaryNode::create(tc);
8182 if (!pn)
8183 return NULL;
8184 if (!tc->ensureSharpSlots())
8185 return NULL;
8186 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8187 break;
8188 #endif /* JS_HAS_SHARP_VARS */
8190 case TOK_LP:
8192 JSBool genexp;
8194 pn = parenExpr(NULL, &genexp);
8195 if (!pn)
8196 return NULL;
8197 pn->pn_parens = true;
8198 if (!genexp)
8199 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8200 break;
8203 #if JS_HAS_XML_SUPPORT
8204 case TOK_STAR:
8205 pn = qualifiedIdentifier();
8206 if (!pn)
8207 return NULL;
8208 break;
8210 case TOK_AT:
8211 pn = attributeIdentifier();
8212 if (!pn)
8213 return NULL;
8214 break;
8216 case TOK_XMLSTAGO:
8217 pn = xmlElementOrListRoot(JS_TRUE);
8218 if (!pn)
8219 return NULL;
8220 break;
8221 #endif /* JS_HAS_XML_SUPPORT */
8223 case TOK_STRING:
8224 #if JS_HAS_SHARP_VARS
8225 /* FALL THROUGH */
8226 #endif
8228 #if JS_HAS_XML_SUPPORT
8229 case TOK_XMLCDATA:
8230 case TOK_XMLCOMMENT:
8231 case TOK_XMLPI:
8232 #endif
8233 pn = NullaryNode::create(tc);
8234 if (!pn)
8235 return NULL;
8236 pn->pn_atom = tokenStream.currentToken().t_atom;
8237 #if JS_HAS_XML_SUPPORT
8238 if (tt == TOK_XMLPI)
8239 pn->pn_atom2 = tokenStream.currentToken().t_atom2;
8240 else
8241 #endif
8242 pn->pn_op = tokenStream.currentToken().t_op;
8243 break;
8245 case TOK_NAME:
8246 pn = NameNode::create(tokenStream.currentToken().t_atom, tc);
8247 if (!pn)
8248 return NULL;
8249 JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
8250 pn->pn_op = JSOP_NAME;
8252 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8253 pn->pn_atom == context->runtime->atomState.argumentsAtom) {
8255 * Flag arguments usage so we can avoid unsafe optimizations such
8256 * as formal parameter assignment analysis (because of the hated
8257 * feature whereby arguments alias formals). We do this even for
8258 * a reference of the form foo.arguments, which ancient code may
8259 * still use instead of arguments (more hate).
8261 NoteArgumentsUse(tc);
8264 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8265 * to do this work (new rule for the emitter to count on).
8267 if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8268 pn->pn_op = JSOP_ARGUMENTS;
8269 pn->pn_dflags |= PND_BOUND;
8271 } else if ((!afterDot
8272 #if JS_HAS_XML_SUPPORT
8273 || tokenStream.peekToken() == TOK_DBLCOLON
8274 #endif
8275 ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8276 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8277 if (!stmt || stmt->type != STMT_WITH) {
8278 JSDefinition *dn;
8280 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8281 if (ale) {
8282 dn = ALE_DEFN(ale);
8283 #if JS_HAS_BLOCK_SCOPE
8285 * Skip out-of-scope let bindings along an ALE list or hash
8286 * chain. These can happen due to |let (x = x) x| block and
8287 * expression bindings, where the x on the right of = comes
8288 * from an outer scope. See bug 496532.
8290 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8291 do {
8292 ale = ALE_NEXT(ale);
8293 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8294 if (!ale)
8295 break;
8296 dn = ALE_DEFN(ale);
8298 #endif
8301 if (ale) {
8302 dn = ALE_DEFN(ale);
8303 } else {
8304 ale = tc->lexdeps.lookup(pn->pn_atom);
8305 if (ale) {
8306 dn = ALE_DEFN(ale);
8307 } else {
8309 * No definition before this use in any lexical scope.
8310 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8311 * new node for the forward-referenced definition. This
8312 * placeholder definition node will be adopted when we
8313 * parse the real defining declaration form, or left as
8314 * a free variable definition if we never see the real
8315 * definition.
8317 ale = MakePlaceholder(pn, tc);
8318 if (!ale)
8319 return NULL;
8320 dn = ALE_DEFN(ale);
8323 * In case this is a forward reference to a function,
8324 * we pessimistically set PND_FUNARG if the next token
8325 * is not a left parenthesis.
8327 * If the definition eventually parsed into dn is not a
8328 * function, this flag won't hurt, and if we do parse a
8329 * function with pn's name, then the PND_FUNARG flag is
8330 * necessary for safe context->display-based optimiza-
8331 * tion of the closure's static link.
8333 JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
8334 JS_ASSERT(dn->pn_op == JSOP_NOP);
8335 if (tokenStream.peekToken() != TOK_LP)
8336 dn->pn_dflags |= PND_FUNARG;
8340 JS_ASSERT(dn->pn_defn);
8341 LinkUseToDef(pn, dn, tc);
8343 /* Here we handle the backward function reference case. */
8344 if (tokenStream.peekToken() != TOK_LP)
8345 dn->pn_dflags |= PND_FUNARG;
8347 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8351 #if JS_HAS_XML_SUPPORT
8352 if (tokenStream.matchToken(TOK_DBLCOLON)) {
8353 if (afterDot) {
8354 JSString *str;
8357 * Here primaryExpr is called after . or .. followed by a name
8358 * followed by ::. This is the only case where a keyword after
8359 * . or .. is not treated as a property name.
8361 str = ATOM_TO_STRING(pn->pn_atom);
8362 tt = js_CheckKeyword(str->chars(), str->length());
8363 if (tt == TOK_FUNCTION) {
8364 pn->pn_arity = PN_NULLARY;
8365 pn->pn_type = TOK_FUNCTION;
8366 } else if (tt != TOK_EOF) {
8367 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8368 JSMSG_KEYWORD_NOT_NS);
8369 return NULL;
8372 pn = qualifiedSuffix(pn);
8373 if (!pn)
8374 return NULL;
8376 #endif
8377 break;
8379 case TOK_REGEXP:
8381 JSObject *obj;
8383 pn = NullaryNode::create(tc);
8384 if (!pn)
8385 return NULL;
8387 obj = js_NewRegExpObject(context, &tokenStream,
8388 tokenStream.getTokenbuf().begin(),
8389 tokenStream.getTokenbuf().length(),
8390 tokenStream.currentToken().t_reflags);
8391 if (!obj)
8392 return NULL;
8393 if (!tc->compileAndGo()) {
8394 obj->clearParent();
8395 obj->clearProto();
8398 pn->pn_objbox = tc->parser->newObjectBox(obj);
8399 if (!pn->pn_objbox)
8400 return NULL;
8402 pn->pn_op = JSOP_REGEXP;
8403 break;
8406 case TOK_NUMBER:
8407 pn = NullaryNode::create(tc);
8408 if (!pn)
8409 return NULL;
8410 pn->pn_op = JSOP_DOUBLE;
8411 pn->pn_dval = tokenStream.currentToken().t_dval;
8412 break;
8414 case TOK_PRIMARY:
8415 pn = NullaryNode::create(tc);
8416 if (!pn)
8417 return NULL;
8418 pn->pn_op = tokenStream.currentToken().t_op;
8419 break;
8421 case TOK_ERROR:
8422 /* The scanner or one of its subroutines reported the error. */
8423 return NULL;
8425 default:
8426 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8427 JSMSG_SYNTAX_ERROR);
8428 return NULL;
8430 return pn;
8433 JSParseNode *
8434 Parser::parenExpr(JSParseNode *pn1, JSBool *genexp)
8436 TokenPtr begin;
8437 JSParseNode *pn;
8439 JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
8440 begin = tokenStream.currentToken().pos.begin;
8442 if (genexp)
8443 *genexp = JS_FALSE;
8444 pn = bracketedExpr();
8445 if (!pn)
8446 return NULL;
8448 #if JS_HAS_GENERATOR_EXPRS
8449 if (tokenStream.matchToken(TOK_FOR)) {
8450 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
8451 ReportCompileErrorNumber(context, &tokenStream, pn, JSREPORT_ERROR,
8452 JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
8453 return NULL;
8455 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
8456 ReportCompileErrorNumber(context, &tokenStream, pn->last(), JSREPORT_ERROR,
8457 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
8458 return NULL;
8460 if (!pn1) {
8461 pn1 = UnaryNode::create(tc);
8462 if (!pn1)
8463 return NULL;
8465 pn = generatorExpr(pn1, pn);
8466 if (!pn)
8467 return NULL;
8468 pn->pn_pos.begin = begin;
8469 if (genexp) {
8470 if (tokenStream.getToken() != TOK_RP) {
8471 ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
8472 JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
8473 return NULL;
8475 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8476 *genexp = JS_TRUE;
8479 #endif /* JS_HAS_GENERATOR_EXPRS */
8481 return pn;
8485 * Fold from one constant type to another.
8486 * XXX handles only strings and numbers for now
8488 static JSBool
8489 FoldType(JSContext *cx, JSParseNode *pn, TokenKind type)
8491 if (PN_TYPE(pn) != type) {
8492 switch (type) {
8493 case TOK_NUMBER:
8494 if (pn->pn_type == TOK_STRING) {
8495 jsdouble d;
8496 if (!JS_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
8497 return JS_FALSE;
8498 pn->pn_dval = d;
8499 pn->pn_type = TOK_NUMBER;
8500 pn->pn_op = JSOP_DOUBLE;
8502 break;
8504 case TOK_STRING:
8505 if (pn->pn_type == TOK_NUMBER) {
8506 JSString *str = js_NumberToString(cx, pn->pn_dval);
8507 if (!str)
8508 return JS_FALSE;
8509 pn->pn_atom = js_AtomizeString(cx, str, 0);
8510 if (!pn->pn_atom)
8511 return JS_FALSE;
8512 pn->pn_type = TOK_STRING;
8513 pn->pn_op = JSOP_STRING;
8515 break;
8517 default:;
8520 return JS_TRUE;
8524 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
8525 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
8526 * a successful call to this function.
8528 static JSBool
8529 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
8530 JSParseNode *pn, JSTreeContext *tc)
8532 jsdouble d, d2;
8533 int32 i, j;
8535 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
8536 d = pn1->pn_dval;
8537 d2 = pn2->pn_dval;
8538 switch (op) {
8539 case JSOP_LSH:
8540 case JSOP_RSH:
8541 i = js_DoubleToECMAInt32(d);
8542 j = js_DoubleToECMAInt32(d2);
8543 j &= 31;
8544 d = (op == JSOP_LSH) ? i << j : i >> j;
8545 break;
8547 case JSOP_URSH:
8548 j = js_DoubleToECMAInt32(d2);
8549 j &= 31;
8550 d = js_DoubleToECMAUint32(d) >> j;
8551 break;
8553 case JSOP_ADD:
8554 d += d2;
8555 break;
8557 case JSOP_SUB:
8558 d -= d2;
8559 break;
8561 case JSOP_MUL:
8562 d *= d2;
8563 break;
8565 case JSOP_DIV:
8566 if (d2 == 0) {
8567 #if defined(XP_WIN)
8568 /* XXX MSVC miscompiles such that (NaN == 0) */
8569 if (JSDOUBLE_IS_NaN(d2))
8570 d = js_NaN;
8571 else
8572 #endif
8573 if (d == 0 || JSDOUBLE_IS_NaN(d))
8574 d = js_NaN;
8575 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
8576 d = js_NegativeInfinity;
8577 else
8578 d = js_PositiveInfinity;
8579 } else {
8580 d /= d2;
8582 break;
8584 case JSOP_MOD:
8585 if (d2 == 0) {
8586 d = js_NaN;
8587 } else {
8588 d = js_fmod(d, d2);
8590 break;
8592 default:;
8595 /* Take care to allow pn1 or pn2 to alias pn. */
8596 if (pn1 != pn)
8597 RecycleTree(pn1, tc);
8598 if (pn2 != pn)
8599 RecycleTree(pn2, tc);
8600 pn->pn_type = TOK_NUMBER;
8601 pn->pn_op = JSOP_DOUBLE;
8602 pn->pn_arity = PN_NULLARY;
8603 pn->pn_dval = d;
8604 return JS_TRUE;
8607 #if JS_HAS_XML_SUPPORT
8609 static JSBool
8610 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
8612 TokenKind tt;
8613 JSParseNode **pnp, *pn1, *pn2;
8614 JSString *accum, *str;
8615 uint32 i, j;
8617 JS_ASSERT(pn->pn_arity == PN_LIST);
8618 tt = PN_TYPE(pn);
8619 pnp = &pn->pn_head;
8620 pn1 = *pnp;
8621 accum = NULL;
8622 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8623 if (tt == TOK_XMLETAGO)
8624 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
8625 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
8626 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
8630 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
8631 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
8632 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
8633 * Therefore, we have to add additonal protection from GC nesting under
8634 * js_ConcatStrings.
8636 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
8637 /* The parser already rejected end-tags with attributes. */
8638 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
8639 switch (pn2->pn_type) {
8640 case TOK_XMLATTR:
8641 if (!accum)
8642 goto cantfold;
8643 /* FALL THROUGH */
8644 case TOK_XMLNAME:
8645 case TOK_XMLSPACE:
8646 case TOK_XMLTEXT:
8647 case TOK_STRING:
8648 if (pn2->pn_arity == PN_LIST)
8649 goto cantfold;
8650 str = ATOM_TO_STRING(pn2->pn_atom);
8651 break;
8653 case TOK_XMLCDATA:
8654 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
8655 if (!str)
8656 return JS_FALSE;
8657 break;
8659 case TOK_XMLCOMMENT:
8660 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
8661 if (!str)
8662 return JS_FALSE;
8663 break;
8665 case TOK_XMLPI:
8666 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
8667 ATOM_TO_STRING(pn2->pn_atom2));
8668 if (!str)
8669 return JS_FALSE;
8670 break;
8672 cantfold:
8673 default:
8674 JS_ASSERT(*pnp == pn1);
8675 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
8676 (i & 1) ^ (j & 1)) {
8677 #ifdef DEBUG_brendanXXX
8678 printf("1: %d, %d => ", i, j);
8679 if (accum)
8680 js_FileEscapedString(stdout, accum, 0);
8681 else
8682 fputs("NULL", stdout);
8683 fputc('\n', stdout);
8684 #endif
8685 } else if (accum && pn1 != pn2) {
8686 while (pn1->pn_next != pn2) {
8687 pn1 = RecycleTree(pn1, tc);
8688 --pn->pn_count;
8690 pn1->pn_type = TOK_XMLTEXT;
8691 pn1->pn_op = JSOP_STRING;
8692 pn1->pn_arity = PN_NULLARY;
8693 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8694 if (!pn1->pn_atom)
8695 return JS_FALSE;
8696 JS_ASSERT(pnp != &pn1->pn_next);
8697 *pnp = pn1;
8699 pnp = &pn2->pn_next;
8700 pn1 = *pnp;
8701 accum = NULL;
8702 continue;
8705 if (accum) {
8707 AutoValueRooter tvr(cx, accum);
8708 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
8709 ? js_AddAttributePart(cx, i & 1, accum, str)
8710 : js_ConcatStrings(cx, accum, str);
8712 if (!str)
8713 return JS_FALSE;
8714 #ifdef DEBUG_brendanXXX
8715 printf("2: %d, %d => ", i, j);
8716 js_FileEscapedString(stdout, str, 0);
8717 printf(" (%u)\n", str->length());
8718 #endif
8719 ++j;
8721 accum = str;
8724 if (accum) {
8725 str = NULL;
8726 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
8727 if (tt == TOK_XMLPTAGC)
8728 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
8729 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
8730 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
8732 if (str) {
8733 accum = js_ConcatStrings(cx, accum, str);
8734 if (!accum)
8735 return JS_FALSE;
8738 JS_ASSERT(*pnp == pn1);
8739 while (pn1->pn_next) {
8740 pn1 = RecycleTree(pn1, tc);
8741 --pn->pn_count;
8743 pn1->pn_type = TOK_XMLTEXT;
8744 pn1->pn_op = JSOP_STRING;
8745 pn1->pn_arity = PN_NULLARY;
8746 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
8747 if (!pn1->pn_atom)
8748 return JS_FALSE;
8749 JS_ASSERT(pnp != &pn1->pn_next);
8750 *pnp = pn1;
8753 if (pn1 && pn->pn_count == 1) {
8755 * Only one node under pn, and it has been folded: move pn1 onto pn
8756 * unless pn is an XML root (in which case we need it to tell the code
8757 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
8758 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
8759 * extra "<" and "/>" bracketing at runtime.
8761 if (!(pn->pn_xflags & PNX_XMLROOT)) {
8762 pn->become(pn1);
8763 } else if (tt == TOK_XMLPTAGC) {
8764 pn->pn_type = TOK_XMLELEM;
8765 pn->pn_op = JSOP_TOXML;
8768 return JS_TRUE;
8771 #endif /* JS_HAS_XML_SUPPORT */
8773 static int
8774 Boolish(JSParseNode *pn)
8776 switch (pn->pn_op) {
8777 case JSOP_DOUBLE:
8778 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
8780 case JSOP_STRING:
8781 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
8783 #if JS_HAS_GENERATOR_EXPRS
8784 case JSOP_CALL:
8787 * A generator expression as an if or loop condition has no effects, it
8788 * simply results in a truthy object reference. This condition folding
8789 * is needed for the decompiler. See bug 442342 and bug 443074.
8791 if (pn->pn_count != 1)
8792 break;
8793 JSParseNode *pn2 = pn->pn_head;
8794 if (pn2->pn_type != TOK_FUNCTION)
8795 break;
8796 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
8797 break;
8798 /* FALL THROUGH */
8800 #endif
8802 case JSOP_DEFFUN:
8803 case JSOP_LAMBDA:
8804 case JSOP_THIS:
8805 case JSOP_TRUE:
8806 return 1;
8808 case JSOP_NULL:
8809 case JSOP_FALSE:
8810 return 0;
8812 default:;
8814 return -1;
8817 JSBool
8818 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
8820 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
8822 JS_CHECK_RECURSION(cx, return JS_FALSE);
8824 switch (pn->pn_arity) {
8825 case PN_FUNC:
8827 uint32 oldflags = tc->flags;
8828 JSFunctionBox *oldlist = tc->functionList;
8830 tc->flags = pn->pn_funbox->tcflags;
8831 tc->functionList = pn->pn_funbox->kids;
8832 if (!js_FoldConstants(cx, pn->pn_body, tc))
8833 return JS_FALSE;
8834 pn->pn_funbox->kids = tc->functionList;
8835 tc->flags = oldflags;
8836 tc->functionList = oldlist;
8837 break;
8840 case PN_LIST:
8842 /* Propagate inCond through logical connectives. */
8843 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
8845 /* Don't fold a parenthesized call expression. See bug 537673. */
8846 pn1 = pn2 = pn->pn_head;
8847 if ((pn->pn_type == TOK_LP || pn->pn_type == TOK_NEW) && pn2->pn_parens)
8848 pn2 = pn2->pn_next;
8850 /* Save the list head in pn1 for later use. */
8851 for (; pn2; pn2 = pn2->pn_next) {
8852 if (!js_FoldConstants(cx, pn2, tc, cond))
8853 return JS_FALSE;
8855 break;
8858 case PN_TERNARY:
8859 /* Any kid may be null (e.g. for (;;)). */
8860 pn1 = pn->pn_kid1;
8861 pn2 = pn->pn_kid2;
8862 pn3 = pn->pn_kid3;
8863 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
8864 return JS_FALSE;
8865 if (pn2) {
8866 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
8867 return JS_FALSE;
8868 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
8869 RecycleTree(pn2, tc);
8870 pn->pn_kid2 = NULL;
8873 if (pn3 && !js_FoldConstants(cx, pn3, tc))
8874 return JS_FALSE;
8875 break;
8877 case PN_BINARY:
8878 pn1 = pn->pn_left;
8879 pn2 = pn->pn_right;
8881 /* Propagate inCond through logical connectives. */
8882 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
8883 if (!js_FoldConstants(cx, pn1, tc, inCond))
8884 return JS_FALSE;
8885 if (!js_FoldConstants(cx, pn2, tc, inCond))
8886 return JS_FALSE;
8887 break;
8890 /* First kid may be null (for default case in switch). */
8891 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
8892 return JS_FALSE;
8893 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
8894 return JS_FALSE;
8895 break;
8897 case PN_UNARY:
8898 pn1 = pn->pn_kid;
8901 * Kludge to deal with typeof expressions: because constant folding
8902 * can turn an expression into a name node, we have to check here,
8903 * before folding, to see if we should throw undefined name errors.
8905 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
8906 * null. This assumption does not hold true for other unary
8907 * expressions.
8909 if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
8910 pn->pn_op = JSOP_TYPEOFEXPR;
8912 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
8913 return JS_FALSE;
8914 break;
8916 case PN_NAME:
8918 * Skip pn1 down along a chain of dotted member expressions to avoid
8919 * excessive recursion. Our only goal here is to fold constants (if
8920 * any) in the primary expression operand to the left of the first
8921 * dot in the chain.
8923 if (!pn->pn_used) {
8924 pn1 = pn->pn_expr;
8925 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
8926 pn1 = pn1->pn_expr;
8927 if (pn1 && !js_FoldConstants(cx, pn1, tc))
8928 return JS_FALSE;
8930 break;
8932 case PN_NAMESET:
8933 pn1 = pn->pn_tree;
8934 if (!js_FoldConstants(cx, pn1, tc))
8935 return JS_FALSE;
8936 break;
8938 case PN_NULLARY:
8939 break;
8942 switch (pn->pn_type) {
8943 case TOK_IF:
8944 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
8945 break;
8946 /* FALL THROUGH */
8948 case TOK_HOOK:
8949 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
8950 switch (pn1->pn_type) {
8951 case TOK_NUMBER:
8952 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
8953 pn2 = pn3;
8954 break;
8955 case TOK_STRING:
8956 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
8957 pn2 = pn3;
8958 break;
8959 case TOK_PRIMARY:
8960 if (pn1->pn_op == JSOP_TRUE)
8961 break;
8962 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
8963 pn2 = pn3;
8964 break;
8966 /* FALL THROUGH */
8967 default:
8968 /* Early return to dodge common code that copies pn2 to pn. */
8969 return JS_TRUE;
8972 #if JS_HAS_GENERATOR_EXPRS
8973 /* Don't fold a trailing |if (0)| in a generator expression. */
8974 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
8975 break;
8976 #endif
8978 if (pn2 && !pn2->pn_defn)
8979 pn->become(pn2);
8980 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
8982 * False condition and no else, or an empty then-statement was
8983 * moved up over pn. Either way, make pn an empty block (not an
8984 * empty statement, which does not decompile, even when labeled).
8985 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
8986 * or an empty statement for a child.
8988 pn->pn_type = TOK_LC;
8989 pn->pn_arity = PN_LIST;
8990 pn->makeEmpty();
8992 RecycleTree(pn2, tc);
8993 if (pn3 && pn3 != pn2)
8994 RecycleTree(pn3, tc);
8995 break;
8997 case TOK_OR:
8998 case TOK_AND:
8999 if (inCond) {
9000 if (pn->pn_arity == PN_LIST) {
9001 JSParseNode **pnp = &pn->pn_head;
9002 JS_ASSERT(*pnp == pn1);
9003 do {
9004 int cond = Boolish(pn1);
9005 if (cond == (pn->pn_type == TOK_OR)) {
9006 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
9007 pn3 = pn2->pn_next;
9008 RecycleTree(pn2, tc);
9009 --pn->pn_count;
9011 pn1->pn_next = NULL;
9012 break;
9014 if (cond != -1) {
9015 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9016 if (pn->pn_count == 1)
9017 break;
9018 *pnp = pn1->pn_next;
9019 RecycleTree(pn1, tc);
9020 --pn->pn_count;
9021 } else {
9022 pnp = &pn1->pn_next;
9024 } while ((pn1 = *pnp) != NULL);
9026 // We may have to change arity from LIST to BINARY.
9027 pn1 = pn->pn_head;
9028 if (pn->pn_count == 2) {
9029 pn2 = pn1->pn_next;
9030 pn1->pn_next = NULL;
9031 JS_ASSERT(!pn2->pn_next);
9032 pn->pn_arity = PN_BINARY;
9033 pn->pn_left = pn1;
9034 pn->pn_right = pn2;
9035 } else if (pn->pn_count == 1) {
9036 pn->become(pn1);
9037 RecycleTree(pn1, tc);
9039 } else {
9040 int cond = Boolish(pn1);
9041 if (cond == (pn->pn_type == TOK_OR)) {
9042 RecycleTree(pn2, tc);
9043 pn->become(pn1);
9044 } else if (cond != -1) {
9045 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9046 RecycleTree(pn1, tc);
9047 pn->become(pn2);
9051 break;
9053 case TOK_ASSIGN:
9055 * Compound operators such as *= should be subject to folding, in case
9056 * the left-hand side is constant, and so that the decompiler produces
9057 * the same string that you get from decompiling a script or function
9058 * compiled from that same string. As with +, += is special.
9060 if (pn->pn_op == JSOP_NOP)
9061 break;
9062 if (pn->pn_op != JSOP_ADD)
9063 goto do_binary_op;
9064 /* FALL THROUGH */
9066 case TOK_PLUS:
9067 if (pn->pn_arity == PN_LIST) {
9068 size_t length, length2;
9069 jschar *chars;
9070 JSString *str, *str2;
9073 * Any string literal term with all others number or string means
9074 * this is a concatenation. If any term is not a string or number
9075 * literal, we can't fold.
9077 JS_ASSERT(pn->pn_count > 2);
9078 if (pn->pn_xflags & PNX_CANTFOLD)
9079 return JS_TRUE;
9080 if (pn->pn_xflags != PNX_STRCAT)
9081 goto do_binary_op;
9083 /* Ok, we're concatenating: convert non-string constant operands. */
9084 length = 0;
9085 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9086 if (!FoldType(cx, pn2, TOK_STRING))
9087 return JS_FALSE;
9088 /* XXX fold only if all operands convert to string */
9089 if (pn2->pn_type != TOK_STRING)
9090 return JS_TRUE;
9091 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9094 /* Allocate a new buffer and string descriptor for the result. */
9095 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9096 if (!chars)
9097 return JS_FALSE;
9098 str = js_NewString(cx, chars, length);
9099 if (!str) {
9100 cx->free(chars);
9101 return JS_FALSE;
9104 /* Fill the buffer, advancing chars and recycling kids as we go. */
9105 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9106 str2 = ATOM_TO_STRING(pn2->pn_atom);
9107 length2 = str2->flatLength();
9108 js_strncpy(chars, str2->flatChars(), length2);
9109 chars += length2;
9111 *chars = 0;
9113 /* Atomize the result string and mutate pn to refer to it. */
9114 pn->pn_atom = js_AtomizeString(cx, str, 0);
9115 if (!pn->pn_atom)
9116 return JS_FALSE;
9117 pn->pn_type = TOK_STRING;
9118 pn->pn_op = JSOP_STRING;
9119 pn->pn_arity = PN_NULLARY;
9120 break;
9123 /* Handle a binary string concatenation. */
9124 JS_ASSERT(pn->pn_arity == PN_BINARY);
9125 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9126 JSString *left, *right, *str;
9128 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9129 TOK_STRING)) {
9130 return JS_FALSE;
9132 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9133 return JS_TRUE;
9134 left = ATOM_TO_STRING(pn1->pn_atom);
9135 right = ATOM_TO_STRING(pn2->pn_atom);
9136 str = js_ConcatStrings(cx, left, right);
9137 if (!str)
9138 return JS_FALSE;
9139 pn->pn_atom = js_AtomizeString(cx, str, 0);
9140 if (!pn->pn_atom)
9141 return JS_FALSE;
9142 pn->pn_type = TOK_STRING;
9143 pn->pn_op = JSOP_STRING;
9144 pn->pn_arity = PN_NULLARY;
9145 RecycleTree(pn1, tc);
9146 RecycleTree(pn2, tc);
9147 break;
9150 /* Can't concatenate string literals, let's try numbers. */
9151 goto do_binary_op;
9153 case TOK_STAR:
9154 case TOK_SHOP:
9155 case TOK_MINUS:
9156 case TOK_DIVOP:
9157 do_binary_op:
9158 if (pn->pn_arity == PN_LIST) {
9159 JS_ASSERT(pn->pn_count > 2);
9160 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9161 if (!FoldType(cx, pn2, TOK_NUMBER))
9162 return JS_FALSE;
9164 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9165 /* XXX fold only if all operands convert to number */
9166 if (pn2->pn_type != TOK_NUMBER)
9167 break;
9169 if (!pn2) {
9170 JSOp op = PN_OP(pn);
9172 pn2 = pn1->pn_next;
9173 pn3 = pn2->pn_next;
9174 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9175 return JS_FALSE;
9176 while ((pn2 = pn3) != NULL) {
9177 pn3 = pn2->pn_next;
9178 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9179 return JS_FALSE;
9182 } else {
9183 JS_ASSERT(pn->pn_arity == PN_BINARY);
9184 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9185 !FoldType(cx, pn2, TOK_NUMBER)) {
9186 return JS_FALSE;
9188 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9189 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9190 return JS_FALSE;
9193 break;
9195 case TOK_UNARYOP:
9196 if (pn1->pn_type == TOK_NUMBER) {
9197 jsdouble d;
9199 /* Operate on one numeric constant. */
9200 d = pn1->pn_dval;
9201 switch (pn->pn_op) {
9202 case JSOP_BITNOT:
9203 d = ~js_DoubleToECMAInt32(d);
9204 break;
9206 case JSOP_NEG:
9207 d = -d;
9208 break;
9210 case JSOP_POS:
9211 break;
9213 case JSOP_NOT:
9214 pn->pn_type = TOK_PRIMARY;
9215 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9216 pn->pn_arity = PN_NULLARY;
9217 /* FALL THROUGH */
9219 default:
9220 /* Return early to dodge the common TOK_NUMBER code. */
9221 return JS_TRUE;
9223 pn->pn_type = TOK_NUMBER;
9224 pn->pn_op = JSOP_DOUBLE;
9225 pn->pn_arity = PN_NULLARY;
9226 pn->pn_dval = d;
9227 RecycleTree(pn1, tc);
9228 } else if (pn1->pn_type == TOK_PRIMARY) {
9229 if (pn->pn_op == JSOP_NOT &&
9230 (pn1->pn_op == JSOP_TRUE ||
9231 pn1->pn_op == JSOP_FALSE)) {
9232 pn->become(pn1);
9233 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9234 RecycleTree(pn1, tc);
9237 break;
9239 #if JS_HAS_XML_SUPPORT
9240 case TOK_XMLELEM:
9241 case TOK_XMLLIST:
9242 case TOK_XMLPTAGC:
9243 case TOK_XMLSTAGO:
9244 case TOK_XMLETAGO:
9245 case TOK_XMLNAME:
9246 if (pn->pn_arity == PN_LIST) {
9247 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9248 if (!FoldXMLConstants(cx, pn, tc))
9249 return JS_FALSE;
9251 break;
9253 case TOK_AT:
9254 if (pn1->pn_type == TOK_XMLNAME) {
9255 jsval v;
9256 JSObjectBox *xmlbox;
9258 v = ATOM_KEY(pn1->pn_atom);
9259 if (!js_ToAttributeName(cx, &v))
9260 return JS_FALSE;
9261 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
9263 xmlbox = tc->parser->newObjectBox(JSVAL_TO_OBJECT(v));
9264 if (!xmlbox)
9265 return JS_FALSE;
9267 pn->pn_type = TOK_XMLNAME;
9268 pn->pn_op = JSOP_OBJECT;
9269 pn->pn_arity = PN_NULLARY;
9270 pn->pn_objbox = xmlbox;
9271 RecycleTree(pn1, tc);
9273 break;
9274 #endif /* JS_HAS_XML_SUPPORT */
9276 default:;
9279 if (inCond) {
9280 int cond = Boolish(pn);
9281 if (cond >= 0) {
9282 switch (pn->pn_arity) {
9283 case PN_LIST:
9284 pn2 = pn->pn_head;
9285 do {
9286 pn3 = pn2->pn_next;
9287 RecycleTree(pn2, tc);
9288 } while ((pn2 = pn3) != NULL);
9289 break;
9290 case PN_FUNC:
9291 RecycleFuncNameKids(pn, tc);
9292 break;
9293 case PN_NULLARY:
9294 break;
9295 default:
9296 JS_NOT_REACHED("unhandled arity");
9298 pn->pn_type = TOK_PRIMARY;
9299 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9300 pn->pn_arity = PN_NULLARY;
9304 return JS_TRUE;