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
17 * The Original Code is Mozilla Communicator client code, released
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.
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 ***** */
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
52 * This parser attempts no error recovery.
65 #include "jsversion.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
83 #if JS_HAS_XML_SUPPORT
87 #if JS_HAS_DESTRUCTURING
91 #include "jsatominlines.h"
92 #include "jsinterpinlines.h"
93 #include "jsobjinlines.h"
94 #include "jsregexpinlines.h"
95 #include "jsscriptinlines.h"
97 // Grr, windows.h or something under it #defines CONST...
103 using namespace js::gc;
106 * Asserts to verify assumptions behind pn_ macros.
108 #define pn_offsetof(m) offsetof(JSParseNode, m)
110 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
111 JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
116 * Insist that the next token be of type tt, or report errno and return null.
117 * NB: this macro uses cx and ts from its lexical environment.
119 #define MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, __flags) \
121 if (tokenStream.getToken((__flags)) != tt) { \
122 reportErrorNumber(NULL, JSREPORT_ERROR, errno); \
126 #define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_WITH_FLAGS(tt, errno, 0)
128 #ifdef METER_PARSENODES
129 static uint32 parsenodes = 0;
130 static uint32 maxparsenodes = 0;
131 static uint32 recyclednodes = 0;
135 JSParseNode::become(JSParseNode *pn2)
138 JS_ASSERT(!pn2->pn_defn);
142 JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
144 pnup = &(*pnup)->pn_link;
146 pn_link = pn2->pn_link;
149 pn2->pn_used = false;
152 pn_type = pn2->pn_type;
154 pn_arity = pn2->pn_arity;
155 pn_parens = pn2->pn_parens;
159 * If any pointers are pointing to pn2, change them to point to this
160 * instead, since pn2 will be cleared and probably recycled.
162 if (PN_TYPE(this) == TOK_FUNCTION && pn_arity == PN_FUNC) {
163 /* Function node: fix up the pn_funbox->node back-pointer. */
164 JS_ASSERT(pn_funbox->node == pn2);
165 pn_funbox->node = this;
166 } else if (pn_arity == PN_LIST && !pn_head) {
167 /* Empty list: fix up the pn_tail pointer. */
168 JS_ASSERT(pn_count == 0);
169 JS_ASSERT(pn_tail == &pn2->pn_head);
181 pn_used = pn_defn = false;
182 pn_arity = PN_NULLARY;
186 Parser::Parser(JSContext *cx, JSPrincipals *prin, JSStackFrame *cfp)
187 : js::AutoGCRooter(cx, PARSER),
193 callerVarObj(cfp ? &cfp->varobj(cx->containingSegment(cfp)) : NULL),
198 keepAtoms(cx->runtime)
200 js::PodArrayZero(tempFreeList);
202 JS_ASSERT_IF(cfp, cfp->isScriptFrame());
206 Parser::init(const jschar *base, size_t length, const char *filename, uintN lineno,
209 JSContext *cx = context;
210 tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
211 if (!tokenStream.init(base, length, filename, lineno, version)) {
212 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
220 JSContext *cx = context;
223 JSPRINCIPALS_DROP(cx, principals);
225 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
229 Parser::setPrincipals(JSPrincipals *prin)
231 JS_ASSERT(!principals);
233 JSPRINCIPALS_HOLD(context, prin);
238 Parser::newObjectBox(JSObject *obj)
243 * We use JSContext.tempPool to allocate parsed objects and place them on
244 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
245 * containing the entries must be alive until we are done with scanning,
246 * parsing and code generation for the whole script or top-level function.
249 JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
251 js_ReportOutOfScriptQuota(context);
254 objbox->traceLink = traceListHead;
255 traceListHead = objbox;
256 objbox->emitLink = NULL;
257 objbox->object = obj;
258 objbox->isFunctionBox = false;
263 Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
266 JS_ASSERT(obj->isFunction());
269 * We use JSContext.tempPool to allocate parsed objects and place them on
270 * a list in this Parser to ensure GC safety. Thus the tempPool arenas
271 * containing the entries must be alive until we are done with scanning,
272 * parsing and code generation for the whole script or top-level function.
274 JSFunctionBox *funbox;
275 JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
277 js_ReportOutOfScriptQuota(context);
280 funbox->traceLink = traceListHead;
281 traceListHead = funbox;
282 funbox->emitLink = NULL;
283 funbox->object = obj;
284 funbox->isFunctionBox = true;
286 funbox->siblings = tc->functionList;
287 tc->functionList = funbox;
288 ++tc->parser->functionCount;
290 funbox->parent = tc->funbox;
291 funbox->methods = NULL;
292 new (&funbox->bindings) Bindings(context);
293 funbox->queued = false;
294 funbox->inLoop = false;
295 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
296 if (STMT_IS_LOOP(stmt)) {
297 funbox->inLoop = true;
301 funbox->level = tc->staticLevel;
302 funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
303 if (tc->innermostWith)
304 funbox->tcflags |= TCF_IN_WITH;
309 JSFunctionBox::joinable() const
311 return FUN_NULL_CLOSURE((JSFunction *) object) &&
312 !(tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME));
316 JSFunctionBox::inAnyDynamicScope() const
318 for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
319 if (funbox->tcflags & (TCF_IN_WITH | TCF_FUN_CALLS_EVAL))
326 JSFunctionBox::shouldUnbrand(uintN methods, uintN slowMethods) const
328 if (slowMethods != 0) {
329 for (const JSFunctionBox *funbox = this; funbox; funbox = funbox->parent) {
330 if (!(funbox->tcflags & TCF_FUN_MODULE_PATTERN))
340 Parser::trace(JSTracer *trc)
342 JSObjectBox *objbox = traceListHead;
344 MarkObject(trc, *objbox->object, "parser.object");
345 if (objbox->isFunctionBox)
346 static_cast<JSFunctionBox *>(objbox)->bindings.trace(trc);
347 objbox = objbox->traceLink;
350 for (JSTreeContext *tc = this->tc; tc; tc = tc->parent)
354 /* Add |node| to |parser|'s free node list. */
356 AddNodeToFreeList(JSParseNode *pn, js::Parser *parser)
358 /* Catch back-to-back dup recycles. */
359 JS_ASSERT(pn != parser->nodeList);
362 * It's too hard to clear these nodes from the JSAtomLists, etc. that
363 * hold references to them, so we never free them. It's our caller's
364 * job to recognize and process these, since their children do need to
367 JS_ASSERT(!pn->pn_used);
368 JS_ASSERT(!pn->pn_defn);
371 /* Poison the node, to catch attempts to use it without initializing it. */
372 memset(pn, 0xab, sizeof(*pn));
375 pn->pn_next = parser->nodeList;
376 parser->nodeList = pn;
378 #ifdef METER_PARSENODES
383 /* Add |node| to |tc|'s parser's free node list. */
385 AddNodeToFreeList(JSParseNode *pn, JSTreeContext *tc)
387 AddNodeToFreeList(pn, tc->parser);
391 * Walk the function box list at |*funboxHead|, removing boxes for deleted
392 * functions and cleaning up method lists. We do this once, before
393 * performing function analysis, to avoid traversing possibly long function
394 * lists repeatedly when recycling nodes.
396 * There are actually three possible states for function boxes and their
399 * - Live: funbox->node points to the node, and funbox->node->pn_funbox
400 * points back to the funbox.
402 * - Recycled: funbox->node points to the node, but funbox->node->pn_funbox
403 * is NULL. When a function node is part of a tree that gets recycled, we
404 * must avoid corrupting any method list the node is on, so we leave the
405 * function node unrecycled until we call cleanFunctionList. At recycle
406 * time, we clear such nodes' pn_funbox pointers to indicate that they
407 * are deleted and should be recycled once we get here.
409 * - Mutated: funbox->node is NULL; the contents of the node itself could
410 * be anything. When we mutate a function node into some other kind of
411 * node, we lose all indication that the node was ever part of the
412 * function box tree; it could later be recycled, reallocated, and turned
413 * into anything at all. (Fortunately, method list members never get
414 * mutated, so we don't have to worry about that case.)
415 * PrepareNodeForMutation clears the node's function box's node pointer,
416 * disconnecting it entirely from the function box tree, and marking the
417 * function box to be trimmed out.
420 Parser::cleanFunctionList(JSFunctionBox **funboxHead)
422 JSFunctionBox **link = funboxHead;
423 while (JSFunctionBox *box = *link) {
426 * This funbox's parse node was mutated into something else. Drop the box,
427 * and stay at the same link.
429 *link = box->siblings;
430 } else if (!box->node->pn_funbox) {
432 * This funbox's parse node is ready to be recycled. Drop the box, recycle
433 * the node, and stay at the same link.
435 *link = box->siblings;
436 AddNodeToFreeList(box->node, this);
438 /* The function is still live. */
440 /* First, remove nodes for deleted functions from our methods list. */
442 JSParseNode **methodLink = &box->methods;
443 while (JSParseNode *method = *methodLink) {
444 /* Method nodes are never rewritten in place to be other kinds of nodes. */
445 JS_ASSERT(method->pn_arity == PN_FUNC);
446 if (!method->pn_funbox) {
447 /* Deleted: drop the node, and stay on this link. */
448 *methodLink = method->pn_link;
450 /* Live: keep the node, and move to the next link. */
451 methodLink = &method->pn_link;
456 /* Second, remove boxes for deleted functions from our kids list. */
457 cleanFunctionList(&box->kids);
459 /* Keep the box on the list, and move to the next link. */
460 link = &box->siblings;
468 * A work pool of JSParseNodes. The work pool is a stack, chained together
469 * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
470 * when recycling deep parse trees.
472 * Since parse nodes are probably allocated in something close to the order
473 * they appear in a depth-first traversal of the tree, making the work pool
474 * a stack should give us pretty good locality.
478 NodeStack() : top(NULL) { }
479 bool empty() { return top == NULL; }
480 void push(JSParseNode *pn) {
484 void pushUnlessNull(JSParseNode *pn) { if (pn) push(pn); }
485 /* Push the children of the PN_LIST node |pn| on the stack. */
486 void pushList(JSParseNode *pn) {
487 /* This clobbers pn->pn_head if the list is empty; should be okay. */
493 JSParseNode *hold = top; /* my kingdom for a prog1 */
504 * Push the children of |pn| on |stack|. Return true if |pn| itself could be
505 * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
506 * nodes, and all function nodes; see comments for
507 * js::Parser::cleanFunctionList). Some callers want to free |pn|; others
508 * (PrepareNodeForMutation) don't care about |pn|, and just need to take care of
512 PushNodeChildren(JSParseNode *pn, NodeStack *stack)
514 switch (pn->pn_arity) {
517 * Function nodes are linked into the function box tree, and may
518 * appear on method lists. Both of those lists are singly-linked,
519 * so trying to update them now could result in quadratic behavior
520 * when recycling trees containing many functions; and the lists
521 * can be very long. So we put off cleaning the lists up until just
522 * before function analysis, when we call
523 * js::Parser::cleanFunctionList.
525 * In fact, we can't recycle the parse node yet, either: it may
526 * appear on a method list, and reusing the node would corrupt
527 * that. Instead, we clear its pn_funbox pointer to mark it as
528 * deleted; js::Parser::cleanFunctionList recycles it as well.
530 * We do recycle the nodes around it, though, so we must clear
531 * pointers to them to avoid leaving dangling references where
532 * someone can find them.
534 pn->pn_funbox = NULL;
535 stack->pushUnlessNull(pn->pn_body);
541 * Because used/defn nodes appear in JSAtomLists and elsewhere, we
542 * don't recycle them. (We'll recover their storage when we free
543 * the temporary arena.) However, we do recycle the nodes around
544 * them, so clean up the pointers to avoid dangling references. The
545 * top-level decls table carries references to them that later
546 * iterations through the compileScript loop may find, so they need
549 * pn_expr and pn_lexdef share storage; the latter isn't an owning
553 stack->pushUnlessNull(pn->pn_expr);
556 return !pn->pn_used && !pn->pn_defn;
562 stack->pushUnlessNull(pn->pn_kid1);
563 stack->pushUnlessNull(pn->pn_kid2);
564 stack->pushUnlessNull(pn->pn_kid3);
567 if (pn->pn_left != pn->pn_right)
568 stack->pushUnlessNull(pn->pn_left);
569 stack->pushUnlessNull(pn->pn_right);
572 stack->pushUnlessNull(pn->pn_kid);
576 * E4X function namespace nodes are PN_NULLARY, but can appear on use
579 return !pn->pn_used && !pn->pn_defn;
586 * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
587 * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
588 * metadata structures (the function box tree).
591 PrepareNodeForMutation(JSParseNode *pn, JSTreeContext *tc)
593 if (pn->pn_arity != PN_NULLARY) {
594 if (pn->pn_arity == PN_FUNC) {
596 * Since this node could be turned into anything, we can't
597 * ensure it won't be subsequently recycled, so we must
598 * disconnect it from the funbox tree entirely.
600 * Note that pn_funbox may legitimately be NULL. functionDef
601 * applies MakeDefIntoUse to definition nodes, which can come
602 * from prior iterations of the big loop in compileScript. In
603 * such cases, the defn nodes have been visited by the recycler
604 * (but not actually recycled!), and their funbox pointers
605 * cleared. But it's fine to mutate them into uses of some new
609 pn->pn_funbox->node = NULL;
612 /* Put |pn|'s children (but not |pn| itself) on a work stack. */
614 PushNodeChildren(pn, &stack);
616 * For each node on the work stack, push its children on the work stack,
617 * and free the node if we can.
619 while (!stack.empty()) {
621 if (PushNodeChildren(pn, &stack))
622 AddNodeToFreeList(pn, tc);
628 * Return the nodes in the subtree |pn| to the parser's free node list, for
631 * Note that all functions in |pn| that are not enclosed by other functions
632 * in |pn| must be direct children of |tc|, because we only clean up |tc|'s
633 * function and method lists. You must not reach into a function and
634 * recycle some part of it (unless you've updated |tc|->functionList, the
635 * way js_FoldConstants does).
638 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
643 JSParseNode *savedNext = pn->pn_next;
647 if (PushNodeChildren(pn, &stack))
648 AddNodeToFreeList(pn, tc);
658 * Allocate a JSParseNode from tc's node freelist or, failing that, from
659 * cx's temporary arena.
662 NewOrRecycledNode(JSTreeContext *tc)
666 pn = tc->parser->nodeList;
668 JSContext *cx = tc->parser->context;
670 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
672 js_ReportOutOfScriptQuota(cx);
674 tc->parser->nodeList = pn->pn_next;
678 #ifdef METER_PARSENODES
680 if (parsenodes - recyclednodes > maxparsenodes)
681 maxparsenodes = parsenodes - recyclednodes;
683 pn->pn_used = pn->pn_defn = false;
684 memset(&pn->pn_u, 0, sizeof pn->pn_u);
690 /* used only by static create methods of subclasses */
693 JSParseNode::create(JSParseNodeArity arity, JSTreeContext *tc)
695 JSParseNode *pn = NewOrRecycledNode(tc);
698 const Token &tok = tc->parser->tokenStream.currentToken();
699 pn->init(tok.type, JSOP_NOP, arity);
700 pn->pn_pos = tok.pos;
705 JSParseNode::newBinaryOrAppend(TokenKind tt, JSOp op, JSParseNode *left, JSParseNode *right,
708 JSParseNode *pn, *pn1, *pn2;
714 * Flatten a left-associative (left-heavy) tree of a given operator into
715 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
717 if (PN_TYPE(left) == tt &&
719 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
720 if (left->pn_arity != PN_LIST) {
721 pn1 = left->pn_left, pn2 = left->pn_right;
722 left->pn_arity = PN_LIST;
723 left->pn_parens = false;
726 if (tt == TOK_PLUS) {
727 if (pn1->pn_type == TOK_STRING)
728 left->pn_xflags |= PNX_STRCAT;
729 else if (pn1->pn_type != TOK_NUMBER)
730 left->pn_xflags |= PNX_CANTFOLD;
731 if (pn2->pn_type == TOK_STRING)
732 left->pn_xflags |= PNX_STRCAT;
733 else if (pn2->pn_type != TOK_NUMBER)
734 left->pn_xflags |= PNX_CANTFOLD;
738 left->pn_pos.end = right->pn_pos.end;
739 if (tt == TOK_PLUS) {
740 if (right->pn_type == TOK_STRING)
741 left->pn_xflags |= PNX_STRCAT;
742 else if (right->pn_type != TOK_NUMBER)
743 left->pn_xflags |= PNX_CANTFOLD;
749 * Fold constant addition immediately, to conserve node space and, what's
750 * more, so js_FoldConstants never sees mixed addition and concatenation
751 * operations with more than one leading non-string operand in a PN_LIST
752 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
753 * to "3pt", not "12pt").
755 if (tt == TOK_PLUS &&
756 left->pn_type == TOK_NUMBER &&
757 right->pn_type == TOK_NUMBER) {
758 left->pn_dval += right->pn_dval;
759 left->pn_pos.end = right->pn_pos.end;
760 RecycleTree(right, tc);
764 pn = NewOrRecycledNode(tc);
767 pn->init(tt, op, PN_BINARY);
768 pn->pn_pos.begin = left->pn_pos.begin;
769 pn->pn_pos.end = right->pn_pos.end;
771 pn->pn_right = right;
772 return (BinaryNode *)pn;
778 NameNode::initCommon(JSTreeContext *tc)
781 pn_cookie.makeFree();
782 pn_dflags = (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
785 pn_blockid = tc->blockid();
789 NameNode::create(JSAtom *atom, JSTreeContext *tc)
793 pn = JSParseNode::create(PN_NAME, tc);
796 ((NameNode *)pn)->initCommon(tc);
798 return (NameNode *)pn;
804 GenerateBlockId(JSTreeContext *tc, uint32& blockid)
806 if (tc->blockidGen == JS_BIT(20)) {
807 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
808 JSMSG_NEED_DIET, "program");
811 blockid = tc->blockidGen++;
816 GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
818 JS_ASSERT(tc->topStmt);
819 JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
820 JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
821 if (!GenerateBlockId(tc, tc->topStmt->blockid))
823 pn->pn_blockid = tc->topStmt->blockid;
828 * Parse a top-level JS script.
831 Parser::parse(JSObject *chain)
834 * Protect atoms from being collected by a GC activation, which might
835 * - nest on this thread due to out of memory (the so-called "last ditch"
836 * GC attempted within js_NewGCThing), or
837 * - run for any reason on another thread if this thread is suspended on
838 * an object lock before it finishes generating bytecode into a script
839 * protected from the GC by a root or a stack frame reference.
841 JSTreeContext globaltc(this);
842 globaltc.setScopeChain(chain);
843 if (!GenerateBlockId(&globaltc, globaltc.bodyid))
846 JSParseNode *pn = statements();
848 if (!tokenStream.matchToken(TOK_EOF)) {
849 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
852 if (!js_FoldConstants(context, pn, &globaltc))
859 JS_STATIC_ASSERT(UpvarCookie::FREE_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
862 SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
865 * This is a lot simpler than error-checking every UpvarCookie::set, and
866 * practically speaking it leaves more than enough room for upvars.
868 if (UpvarCookie::isLevelReserved(staticLevel)) {
869 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
870 JSMSG_TOO_DEEP, js_function_str);
873 tc->staticLevel = staticLevel;
878 * Compile a top-level script.
880 Compiler::Compiler(JSContext *cx, JSPrincipals *prin, JSStackFrame *cfp)
881 : parser(cx, prin, cfp)
886 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
887 JSPrincipals *principals, uint32 tcflags,
888 const jschar *chars, size_t length,
889 const char *filename, uintN lineno, JSVersion version,
890 JSString *source /* = NULL */,
891 uintN staticLevel /* = 0 */)
893 JSArenaPool codePool, notePool;
897 bool inDirectivePrologue;
898 #ifdef METER_PARSENODES
899 void *sbrk(ptrdiff_t), *before = sbrk(0);
902 JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_NEED_MUTABLE_SCRIPT |
903 TCF_COMPILE_FOR_EVAL)));
906 * The scripted callerFrame can only be given for compile-and-go scripts
907 * and non-zero static level requires callerFrame.
909 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
910 JS_ASSERT_IF(staticLevel != 0, callerFrame);
912 Compiler compiler(cx, principals, callerFrame);
913 if (!compiler.init(chars, length, filename, lineno, version))
916 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
917 &cx->scriptStackQuota);
918 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote),
919 &cx->scriptStackQuota);
921 Parser &parser = compiler.parser;
922 TokenStream &tokenStream = parser.tokenStream;
924 JSCodeGenerator cg(&parser, &codePool, ¬ePool, tokenStream.getLineno());
928 MUST_FLOW_THROUGH("out");
930 // We can specialize a bit for the given scope chain if that scope chain is the global object.
931 JSObject *globalObj = scopeChain && scopeChain == scopeChain->getGlobal()
932 ? scopeChain->getGlobal()
934 js::GlobalScope globalScope(cx, globalObj, &cg);
936 JS_ASSERT(globalObj->isNative());
937 JS_ASSERT((globalObj->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS);
940 /* Null script early in case of error, to reduce our code footprint. */
943 globalScope.cg = &cg;
945 cg.setScopeChain(scopeChain);
946 compiler.globalScope = &globalScope;
947 if (!SetStaticLevel(&cg, staticLevel))
950 /* If this is a direct call to eval, inherit the caller's strictness. */
952 callerFrame->isScriptFrame() &&
953 callerFrame->script()->strictModeCode) {
954 cg.flags |= TCF_STRICT_MODE_CODE;
955 tokenStream.setStrictMode();
959 * If funbox is non-null after we create the new script, callerFrame->fun
960 * was saved in the 0th object table entry.
965 if (tcflags & TCF_COMPILE_N_GO) {
968 * Save eval program source in script->atomMap.vector[0] for the
969 * eval cache (see EvalCacheLookup in jsobj.cpp).
971 JSAtom *atom = js_AtomizeString(cx, source, 0);
972 if (!atom || !cg.atomList.add(&parser, atom))
976 if (callerFrame && callerFrame->isFunctionFrame()) {
978 * An eval script in a caller frame needs to have its enclosing
979 * function captured in case it refers to an upvar, and someone
980 * wishes to decompile it while it's running.
982 funbox = parser.newObjectBox(FUN_OBJECT(callerFrame->fun()));
985 funbox->emitLink = cg.objectList.lastbox;
986 cg.objectList.lastbox = funbox;
987 cg.objectList.length++;
992 * Inline this->statements to emit as we go to save AST space. We must
993 * generate our script-body blockid since we aren't calling Statements.
996 if (!GenerateBlockId(&cg, bodyid))
1000 #if JS_HAS_XML_SUPPORT
1006 inDirectivePrologue = true;
1007 tokenStream.setOctalCharacterEscape(false);
1009 tt = tokenStream.peekToken(TSF_OPERAND);
1010 if (tt <= TOK_EOF) {
1013 JS_ASSERT(tt == TOK_ERROR);
1017 pn = parser.statement();
1020 JS_ASSERT(!cg.blockNode);
1022 if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue))
1025 if (!js_FoldConstants(cx, pn, &cg))
1028 if (!parser.analyzeFunctions(&cg))
1030 cg.functionList = NULL;
1032 if (!js_EmitTree(cx, &cg, pn))
1035 #if JS_HAS_XML_SUPPORT
1036 if (PN_TYPE(pn) != TOK_SEMI ||
1038 !TreeTypeIsXML(PN_TYPE(pn->pn_kid))) {
1042 RecycleTree(pn, &cg);
1045 #if JS_HAS_XML_SUPPORT
1047 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
1048 * For background, see:
1050 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
1052 if (pn && onlyXML && !callerFrame) {
1053 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM);
1059 * Global variables (gvars) share the atom index space with locals. Due to
1060 * incremental code generation we need to patch the bytecode to adjust the
1061 * local references to skip the globals.
1063 if (cg.hasSharps()) {
1064 jsbytecode *code, *end;
1066 const JSCodeSpec *cs;
1069 code = CG_BASE(&cg);
1070 for (end = code + CG_OFFSET(&cg); code != end; code += len) {
1071 JS_ASSERT(code < end);
1073 cs = &js_CodeSpec[op];
1074 len = (cs->length > 0)
1075 ? (uintN) cs->length
1076 : js_GetVariableBytecodeLength(code);
1077 if ((cs->format & JOF_SHARPSLOT) ||
1078 JOF_TYPE(cs->format) == JOF_LOCAL ||
1079 (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
1081 * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
1082 * emitted only for a function.
1084 JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
1085 (JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
1086 (op == JSOP_GETLOCALPROP));
1087 slot = GET_SLOTNO(code);
1088 if (!(cs->format & JOF_SHARPSLOT))
1089 slot += cg.sharpSlots();
1090 if (slot >= SLOTNO_LIMIT)
1091 goto too_many_slots;
1092 SET_SLOTNO(code, slot);
1097 #ifdef METER_PARSENODES
1098 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
1099 (char *)sbrk(0) - (char *)before,
1102 parsenodes - recyclednodes);
1107 * Nowadays the threaded interpreter needs a stop instruction, so we
1108 * do have to emit that here.
1110 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
1113 #ifdef METER_PARSENODES
1114 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
1115 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
1118 #ifdef JS_ARENAMETER
1119 JS_DumpArenaStats(stdout);
1122 JS_ASSERT(cg.version() == version);
1124 script = JSScript::NewScriptFromCG(cx, &cg);
1125 if (script && funbox)
1126 script->savedCallerFun = true;
1128 #ifdef JS_SCOPE_DEPTH_METER
1130 JSObject *obj = scopeChain;
1132 while ((obj = obj->getParent()) != NULL)
1134 JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1138 if (!defineGlobals(cx, globalScope, script))
1142 JS_FinishArenaPool(&codePool);
1143 JS_FinishArenaPool(¬ePool);
1147 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1152 js_DestroyScript(cx, script);
1159 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
1161 if (!globalScope.defs.length())
1164 JSObject *globalObj = globalScope.globalObj;
1166 /* Define and update global properties. */
1167 for (size_t i = 0; i < globalScope.defs.length(); i++) {
1168 GlobalScope::GlobalDef &def = globalScope.defs[i];
1170 /* Names that could be resolved ahead of time can be skipped. */
1174 jsid id = ATOM_TO_JSID(def.atom);
1178 JSFunction *fun = (JSFunction *)def.funbox->object;
1181 * No need to check for redeclarations or anything, global
1182 * optimizations only take place if the property is not defined.
1184 rval.setObject(*fun);
1186 rval.setUndefined();
1191 if (!js_DefineNativeProperty(cx, globalObj, id, rval, PropertyStub, StrictPropertyStub,
1192 JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, &prop)) {
1197 const Shape *shape = (const Shape *)prop;
1198 def.knownSlot = shape->slot;
1201 js::Vector<JSScript *, 16, ContextAllocPolicy> worklist(cx);
1202 if (!worklist.append(script))
1206 * Recursively walk through all scripts we just compiled. For each script,
1207 * go through all global uses. Each global use indexes into globalScope->defs.
1208 * Use this information to repoint each use to the correct slot in the global
1211 while (worklist.length()) {
1212 JSScript *inner = worklist.back();
1215 if (JSScript::isValidOffset(inner->objectsOffset)) {
1216 JSObjectArray *arr = inner->objects();
1217 for (size_t i = 0; i < arr->length; i++) {
1218 JSObject *obj = arr->vector[i];
1219 if (!obj->isFunction())
1221 JSFunction *fun = obj->getFunctionPrivate();
1222 JS_ASSERT(fun->isInterpreted());
1223 JSScript *inner = fun->u.i.script;
1224 if (!JSScript::isValidOffset(inner->globalsOffset) &&
1225 !JSScript::isValidOffset(inner->objectsOffset)) {
1228 if (!worklist.append(inner))
1233 if (!JSScript::isValidOffset(inner->globalsOffset))
1236 GlobalSlotArray *globalUses = inner->globals();
1237 uint32 nGlobalUses = globalUses->length;
1238 for (uint32 i = 0; i < nGlobalUses; i++) {
1239 uint32 index = globalUses->vector[i].slot;
1240 JS_ASSERT(index < globalScope.defs.length());
1241 globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
1249 * Insist on a final return before control flows out of pn. Try to be a bit
1250 * smart about loops: do {...; return e2;} while(0) at the end of a function
1251 * that contains an early return e1 will get a strict warning. Similarly for
1252 * iloops: while (true){...} is treated as though ... returns.
1254 #define ENDS_IN_OTHER 0
1255 #define ENDS_IN_RETURN 1
1256 #define ENDS_IN_BREAK 2
1259 HasFinalReturn(JSParseNode *pn)
1261 JSParseNode *pn2, *pn3;
1262 uintN rv, rv2, hasDefault;
1264 switch (pn->pn_type) {
1267 return ENDS_IN_OTHER;
1268 return HasFinalReturn(pn->last());
1272 return ENDS_IN_OTHER;
1273 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1277 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1278 return ENDS_IN_RETURN;
1279 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1280 return ENDS_IN_RETURN;
1281 return ENDS_IN_OTHER;
1285 if (pn2->pn_type == TOK_PRIMARY) {
1286 if (pn2->pn_op == JSOP_FALSE)
1287 return HasFinalReturn(pn->pn_left);
1288 if (pn2->pn_op == JSOP_TRUE)
1289 return ENDS_IN_RETURN;
1291 if (pn2->pn_type == TOK_NUMBER) {
1292 if (pn2->pn_dval == 0)
1293 return HasFinalReturn(pn->pn_left);
1294 return ENDS_IN_RETURN;
1296 return ENDS_IN_OTHER;
1300 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1301 return ENDS_IN_RETURN;
1302 return ENDS_IN_OTHER;
1305 rv = ENDS_IN_RETURN;
1306 hasDefault = ENDS_IN_OTHER;
1308 if (pn2->pn_type == TOK_LEXICALSCOPE)
1310 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1311 if (pn2->pn_type == TOK_DEFAULT)
1312 hasDefault = ENDS_IN_RETURN;
1313 pn3 = pn2->pn_right;
1314 JS_ASSERT(pn3->pn_type == TOK_LC);
1316 rv2 = HasFinalReturn(pn3->last());
1317 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1318 /* Falling through to next case or default. */;
1323 /* If a final switch has no default case, we judge it harshly. */
1328 return ENDS_IN_BREAK;
1331 return HasFinalReturn(pn->pn_right);
1334 return ENDS_IN_RETURN;
1337 case TOK_LEXICALSCOPE:
1338 return HasFinalReturn(pn->expr());
1341 return ENDS_IN_RETURN;
1344 /* If we have a finally block that returns, we are done. */
1346 rv = HasFinalReturn(pn->pn_kid3);
1347 if (rv == ENDS_IN_RETURN)
1351 /* Else check the try block and any and all catch statements. */
1352 rv = HasFinalReturn(pn->pn_kid1);
1354 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1355 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1356 rv &= HasFinalReturn(pn2);
1361 /* Check this catch block's body. */
1362 return HasFinalReturn(pn->pn_kid3);
1365 /* Non-binary let statements are let declarations. */
1366 if (pn->pn_arity != PN_BINARY)
1367 return ENDS_IN_OTHER;
1368 return HasFinalReturn(pn->pn_right);
1371 return ENDS_IN_OTHER;
1376 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1379 JSAutoByteString name;
1380 if (tc->fun()->atom) {
1381 if (!js_AtomToPrintableString(cx, tc->fun()->atom, &name))
1384 errnum = anonerrnum;
1386 return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name.ptr());
1390 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1392 JS_ASSERT(tc->inFunction());
1393 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1394 ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1395 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1399 * Check that it is permitted to assign to lhs. Strict mode code may not
1400 * assign to 'eval' or 'arguments'.
1403 CheckStrictAssignment(JSContext *cx, JSTreeContext *tc, JSParseNode *lhs)
1405 if (tc->needStrictChecks() && lhs->pn_type == TOK_NAME) {
1406 JSAtom *atom = lhs->pn_atom;
1407 JSAtomState *atomState = &cx->runtime->atomState;
1408 if (atom == atomState->evalAtom || atom == atomState->argumentsAtom) {
1409 JSAutoByteString name;
1410 if (!js_AtomToPrintableString(cx, atom, &name) ||
1411 !ReportStrictModeError(cx, TS(tc->parser), tc, lhs, JSMSG_DEPRECATED_ASSIGN,
1421 * Check that it is permitted to introduce a binding for atom. Strict mode
1422 * forbids introducing new definitions for 'eval', 'arguments', or for any
1423 * strict mode reserved keyword. Use pn for reporting error locations, or use
1424 * tc's token stream if pn is NULL.
1427 CheckStrictBinding(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSParseNode *pn)
1429 if (!tc->needStrictChecks())
1432 JSAtomState *atomState = &cx->runtime->atomState;
1433 if (atom == atomState->evalAtom ||
1434 atom == atomState->argumentsAtom ||
1435 FindKeyword(atom->charsZ(), atom->length()))
1437 JSAutoByteString name;
1438 if (!js_AtomToPrintableString(cx, atom, &name))
1440 return ReportStrictModeError(cx, TS(tc->parser), tc, pn, JSMSG_BAD_BINDING, name.ptr());
1447 ReportBadParameter(JSContext *cx, JSTreeContext *tc, JSAtom *name, uintN errorNumber)
1449 JSDefinition *dn = ALE_DEFN(tc->decls.lookup(name));
1450 JSAutoByteString bytes;
1451 return js_AtomToPrintableString(cx, name, &bytes) &&
1452 ReportStrictModeError(cx, TS(tc->parser), tc, dn, errorNumber, bytes.ptr());
1456 * In strict mode code, all parameter names must be distinct, must not be
1457 * strict mode reserved keywords, and must not be 'eval' or 'arguments'. We
1458 * must perform these checks here, and not eagerly during parsing, because a
1459 * function's body may turn on strict mode for the function head.
1462 CheckStrictParameters(JSContext *cx, JSTreeContext *tc)
1464 JS_ASSERT(tc->inFunction());
1466 if (!tc->needStrictChecks() || tc->bindings.countArgs() == 0)
1469 JSAtom *argumentsAtom = cx->runtime->atomState.argumentsAtom;
1470 JSAtom *evalAtom = cx->runtime->atomState.evalAtom;
1472 /* name => whether we've warned about the name already */
1473 HashMap<JSAtom *, bool> parameters(cx);
1474 if (!parameters.init(tc->bindings.countArgs()))
1477 /* Start with lastVariable(), not lastArgument(), for destructuring. */
1478 for (Shape::Range r = tc->bindings.lastVariable(); !r.empty(); r.popFront()) {
1479 jsid id = r.front().id;
1480 if (!JSID_IS_ATOM(id))
1483 JSAtom *name = JSID_TO_ATOM(id);
1485 if (name == argumentsAtom || name == evalAtom) {
1486 if (!ReportBadParameter(cx, tc, name, JSMSG_BAD_BINDING))
1490 if (tc->inStrictMode() && FindKeyword(name->charsZ(), name->length())) {
1492 * JSOPTION_STRICT is supposed to warn about future keywords, too,
1493 * but we took care of that in the scanner.
1495 JS_ALWAYS_TRUE(!ReportBadParameter(cx, tc, name, JSMSG_RESERVED_ID));
1500 * Check for a duplicate parameter: warn or report an error exactly
1501 * once for each duplicated parameter.
1503 if (HashMap<JSAtom *, bool>::AddPtr p = parameters.lookupForAdd(name)) {
1504 if (!p->value && !ReportBadParameter(cx, tc, name, JSMSG_DUPLICATE_FORMAL))
1508 if (!parameters.add(p, name, false))
1517 Parser::functionBody()
1519 JSStmtInfo stmtInfo;
1520 uintN oldflags, firstLine;
1523 JS_ASSERT(tc->inFunction());
1524 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1525 stmtInfo.flags = SIF_BODY_BLOCK;
1527 oldflags = tc->flags;
1528 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1531 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1532 * later, because we may have not peeked in tokenStream yet, so statements
1533 * won't acquire a valid pn->pn_pos.begin from the current token.
1535 firstLine = tokenStream.getLineno();
1536 #if JS_HAS_EXPR_CLOSURES
1537 if (tokenStream.currentToken().type == TOK_LC) {
1540 pn = UnaryNode::create(tc);
1542 pn->pn_kid = assignExpr();
1546 if (tc->flags & TCF_FUN_IS_GENERATOR) {
1547 ReportBadReturn(context, tc, JSREPORT_ERROR,
1548 JSMSG_BAD_GENERATOR_RETURN,
1549 JSMSG_BAD_ANON_GENERATOR_RETURN);
1552 pn->pn_type = TOK_RETURN;
1553 pn->pn_op = JSOP_RETURN;
1554 pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1564 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1565 js_PopStatement(tc);
1566 pn->pn_pos.begin.lineno = firstLine;
1568 /* Check for falling off the end of a function that returns a value. */
1569 if (context->hasStrictOption() && (tc->flags & TCF_RETURN_EXPR) &&
1570 !CheckFinalReturn(context, tc, pn)) {
1575 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1579 static JSAtomListElement *
1580 MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1582 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, pn->pn_atom);
1586 JSDefinition *dn = (JSDefinition *)NameNode::create(pn->pn_atom, tc);
1590 ALE_SET_DEFN(ale, dn);
1591 dn->pn_type = TOK_NAME;
1592 dn->pn_op = JSOP_NOP;
1594 dn->pn_dflags |= PND_PLACEHOLDER;
1599 Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1601 JS_ASSERT(!pn->pn_used);
1602 JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1605 JSAtomListElement *ale = NULL;
1606 JSAtomList *list = NULL;
1609 ale = (list = &tc->decls)->rawLookup(atom, hep);
1611 ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1614 JSDefinition *dn = ALE_DEFN(ale);
1616 JSParseNode **pnup = &dn->dn_uses;
1618 uintN start = let ? pn->pn_blockid : tc->bodyid;
1620 while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1621 JS_ASSERT(pnu->pn_used);
1622 pnu->pn_lexdef = (JSDefinition *) pn;
1623 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1624 pnup = &pnu->pn_link;
1627 if (pnu != dn->dn_uses) {
1628 *pnup = pn->dn_uses;
1629 pn->dn_uses = dn->dn_uses;
1632 if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1633 list->rawRemove(tc->parser, ale, hep);
1638 ale = tc->decls.add(tc->parser, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1641 ALE_SET_DEFN(ale, pn);
1643 pn->pn_dflags &= ~PND_PLACEHOLDER;
1645 pn->pn_dflags |= PND_TOPLEVEL;
1650 LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1652 JS_ASSERT(!pn->pn_used);
1653 JS_ASSERT(!pn->pn_defn);
1654 JS_ASSERT(pn != dn->dn_uses);
1655 pn->pn_link = dn->dn_uses;
1657 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1663 ForgetUse(JSParseNode *pn)
1666 JS_ASSERT(!pn->pn_defn);
1670 JSParseNode **pnup = &pn->lexdef()->dn_uses;
1672 while ((pnu = *pnup) != pn)
1673 pnup = &pnu->pn_link;
1674 *pnup = pn->pn_link;
1675 pn->pn_used = false;
1678 static JSParseNode *
1679 MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1681 JSParseNode *lhs = NewOrRecycledNode(tc);
1687 JSDefinition *dn = pn->pn_lexdef;
1688 JSParseNode **pnup = &dn->dn_uses;
1691 pnup = &(*pnup)->pn_link;
1693 lhs->pn_link = pn->pn_link;
1697 pn->pn_type = TOK_ASSIGN;
1698 pn->pn_op = JSOP_NOP;
1699 pn->pn_arity = PN_BINARY;
1700 pn->pn_parens = false;
1701 pn->pn_used = pn->pn_defn = false;
1707 static JSParseNode *
1708 MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1711 * If dn is arg, or in [var, const, let] and has an initializer, then we
1712 * must rewrite it to be an assignment node, whose freshly allocated
1713 * left-hand side becomes a use of pn.
1715 if (dn->isBindingForm()) {
1716 JSParseNode *rhs = dn->expr();
1718 JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1721 //pn->dn_uses = lhs;
1722 dn = (JSDefinition *) lhs;
1725 dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1726 } else if (dn->kind() == JSDefinition::FUNCTION) {
1727 JS_ASSERT(dn->pn_op == JSOP_NOP);
1728 PrepareNodeForMutation(dn, tc);
1729 dn->pn_type = TOK_NAME;
1730 dn->pn_arity = PN_NAME;
1734 /* Now make dn no longer a definition, rather a use of pn. */
1735 JS_ASSERT(dn->pn_type == TOK_NAME);
1736 JS_ASSERT(dn->pn_arity == PN_NAME);
1737 JS_ASSERT(dn->pn_atom == atom);
1739 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1740 JS_ASSERT(pnu->pn_used);
1741 JS_ASSERT(!pnu->pn_defn);
1742 pnu->pn_lexdef = (JSDefinition *) pn;
1743 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1745 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1748 dn->pn_defn = false;
1750 dn->pn_lexdef = (JSDefinition *) pn;
1751 dn->pn_cookie.makeFree();
1752 dn->pn_dflags &= ~PND_BOUND;
1757 DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1759 JSParseNode *argpn, *argsbody;
1761 /* Flag tc so we don't have to lookup arguments on every use. */
1762 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1763 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1766 * Make an argument definition node, distinguished by being in tc->decls
1767 * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1768 * list node returned via pn->pn_body.
1770 argpn = NameNode::create(atom, tc);
1773 JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1775 /* Arguments are initialized by definition. */
1776 argpn->pn_dflags |= PND_INITIALIZED;
1777 if (!Define(argpn, atom, tc))
1780 argsbody = pn->pn_body;
1782 argsbody = ListNode::create(tc);
1785 argsbody->pn_type = TOK_ARGSBODY;
1786 argsbody->pn_op = JSOP_NOP;
1787 argsbody->makeEmpty();
1788 pn->pn_body = argsbody;
1790 argsbody->append(argpn);
1792 argpn->pn_op = JSOP_GETARG;
1793 argpn->pn_cookie.set(tc->staticLevel, i);
1794 argpn->pn_dflags |= PND_BOUND;
1799 * Compile a JS function body, which might appear as the value of an event
1800 * handler attribute in an HTML <INPUT> tag.
1803 Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1804 Bindings *bindings, const jschar *chars, size_t length,
1805 const char *filename, uintN lineno, JSVersion version)
1807 Compiler compiler(cx, principals);
1809 if (!compiler.init(chars, length, filename, lineno, version))
1812 /* No early return from after here until the js_FinishArenaPool calls. */
1813 JSArenaPool codePool, notePool;
1814 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
1815 &cx->scriptStackQuota);
1816 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote),
1817 &cx->scriptStackQuota);
1819 Parser &parser = compiler.parser;
1820 TokenStream &tokenStream = parser.tokenStream;
1822 JSCodeGenerator funcg(&parser, &codePool, ¬ePool, tokenStream.getLineno());
1826 funcg.flags |= TCF_IN_FUNCTION;
1827 funcg.setFunction(fun);
1828 funcg.bindings.transfer(cx, bindings);
1829 fun->setArgCount(funcg.bindings.countArgs());
1830 if (!GenerateBlockId(&funcg, funcg.bodyid))
1833 /* FIXME: make Function format the source for a function definition. */
1834 tokenStream.mungeCurrentToken(TOK_NAME);
1835 JSParseNode *fn = FunctionNode::create(&funcg);
1838 fn->pn_cookie.makeFree();
1840 uintN nargs = fun->nargs;
1843 * NB: do not use AutoLocalNameArray because it will release space
1844 * allocated from cx->tempPool by DefineArg.
1846 jsuword *names = funcg.bindings.getLocalNameArray(cx, &cx->tempPool);
1850 for (uintN i = 0; i < nargs; i++) {
1851 JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1852 if (!DefineArg(fn, name, i, &funcg)) {
1862 * Farble the body so that it looks like a block statement to js_EmitTree,
1863 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1864 * done parsing, we must fold constants, analyze any nested functions, and
1865 * generate code for this function, including a stop opcode at the end.
1867 tokenStream.mungeCurrentToken(TOK_LC);
1868 JSParseNode *pn = fn ? parser.functionBody() : NULL;
1870 if (!CheckStrictParameters(cx, &funcg)) {
1872 } else if (!tokenStream.matchToken(TOK_EOF)) {
1873 parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1875 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1876 /* js_FoldConstants reported the error already. */
1878 } else if (!parser.analyzeFunctions(&funcg)) {
1882 JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1883 fn->pn_body->append(pn);
1884 fn->pn_body->pn_pos = pn->pn_pos;
1888 if (!js_EmitFunctionScript(cx, &funcg, pn))
1893 /* Restore saved state and release code generation arenas. */
1894 JS_FinishArenaPool(&codePool);
1895 JS_FinishArenaPool(¬ePool);
1900 * Parameter block types for the several Binder functions. We use a common
1901 * helper function signature in order to share code among destructuring and
1902 * simple variable declaration parsers. In the destructuring case, the binder
1903 * function is called indirectly from the variable declaration parser by way
1904 * of CheckDestructuring and its friends.
1907 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1910 BindData() : fresh(true) {}
1912 JSParseNode *pn; /* name node for definition processing and
1913 error source coordinates */
1914 JSOp op; /* prolog bytecode or nop */
1915 Binder binder; /* binder, discriminates u */
1925 BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSAtom *atom, BindingKind kind, bool isArg)
1927 JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
1930 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1931 * Instead 'var arguments' always restates the predefined property of the
1932 * activation objects whose name is 'arguments'. Assignment to such a
1933 * variable must be handled specially.
1935 * Special case: an argument named 'arguments' *does* shadow the predefined
1936 * arguments property.
1938 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1941 return tc->bindings.add(cx, atom, kind);
1944 #if JS_HAS_DESTRUCTURING
1946 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1948 /* Flag tc so we don't have to lookup arguments on every use. */
1949 if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
1950 tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1952 JS_ASSERT(tc->inFunction());
1955 * NB: Check tc->decls rather than tc->bindings, because destructuring
1956 * bindings aren't added to tc->bindings until after all arguments have
1959 if (tc->decls.lookup(atom)) {
1960 ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
1961 JSMSG_DESTRUCT_DUP_ARG);
1965 JSParseNode *pn = data->pn;
1968 * Distinguish destructured-to binding nodes as vars, not args, by setting
1969 * pn_op to JSOP_SETLOCAL. Parser::functionDef checks for this pn_op value
1970 * when processing the destructuring-assignment AST prelude induced by such
1971 * destructuring args in Parser::functionArguments.
1973 * We must set the PND_BOUND flag too to prevent pn_op from being reset to
1974 * JSOP_SETNAME by BindDestructuringVar. The only field not initialized is
1975 * pn_cookie; it gets set in functionDef in the first "if (prelude)" block.
1976 * We have to wait to set the cookie until we can call JSFunction::addLocal
1977 * with kind = JSLOCAL_VAR, after all JSLOCAL_ARG locals have been added.
1979 * Thus a destructuring formal parameter binds an ARG (as in arguments[i]
1980 * element) with a null atom name for the object or array passed in to be
1981 * destructured, and zero or more VARs (as in named local variables) for
1982 * the destructured-to identifiers in the property value positions within
1983 * the object or array destructuring pattern, and all ARGs for the formal
1984 * parameter list bound as locals before any VAR for a destructured name.
1986 pn->pn_op = JSOP_SETLOCAL;
1987 pn->pn_dflags |= PND_BOUND;
1989 return Define(pn, atom, tc);
1991 #endif /* JS_HAS_DESTRUCTURING */
1994 Parser::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1999 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
2002 * Find the global compilation context in order to pre-set the newborn
2003 * function's parent slot to tc->scopeChain. If the global context is a
2004 * compile-and-go one, we leave the pre-set parent intact; otherwise we
2005 * clear parent and proto.
2009 parent = tc->inFunction() ? NULL : tc->scopeChain();
2011 fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, parent, atom);
2012 if (fun && !tc->compileAndGo()) {
2013 FUN_OBJECT(fun)->clearParent();
2014 FUN_OBJECT(fun)->clearProto();
2020 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
2022 TokenKind tt = ts->peekTokenSameLine(TSF_OPERAND);
2023 if (tt == TOK_ERROR)
2025 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2026 /* Advance the scanner for proper error location reporting. */
2027 ts->getToken(TSF_OPERAND);
2028 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SEMI_BEFORE_STMNT);
2031 (void) ts->matchToken(TOK_SEMI);
2036 Parser::analyzeFunctions(JSTreeContext *tc)
2038 cleanFunctionList(&tc->functionList);
2039 if (!tc->functionList)
2041 if (!markFunArgs(tc->functionList))
2043 setFunctionKinds(tc->functionList, &tc->flags);
2048 * Mark as funargs any functions that reach up to one or more upvars across an
2049 * already-known funarg. The parser will flag the o_m lambda as a funarg in:
2051 * function f(o, p) {
2052 * o.m = function o_m(a) {
2053 * function g() { return p; }
2054 * function h() { return a; }
2059 * but without this extra marking phase, function g will not be marked as a
2060 * funarg since it is called from within its parent scope. But g reaches up to
2061 * f's parameter p, so if o_m escapes f's activation scope, g does too and
2062 * cannot assume that p's stack slot is still alive. In contast function h
2063 * neither escapes nor uses an upvar "above" o_m's level.
2065 * If function g itself contained lambdas that contained non-lambdas that reach
2066 * up above its level, then those non-lambdas would have to be marked too. This
2067 * process is potentially exponential in the number of functions, but generally
2068 * not so complex. But it can't be done during a single recursive traversal of
2069 * the funbox tree, so we must use a work queue.
2071 * Return the minimal "skipmin" for funbox and its siblings. This is the delta
2072 * between the static level of the bodies of funbox and its peers (which must
2073 * be funbox->level + 1), and the static level of the nearest upvar among all
2074 * the upvars contained by funbox and its peers. If there are no upvars, return
2075 * FREE_STATIC_LEVEL. Thus this function never returns 0.
2078 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
2080 uintN allskipmin = UpvarCookie::FREE_LEVEL;
2083 JSParseNode *fn = funbox->node;
2084 JS_ASSERT(fn->pn_arity == PN_FUNC);
2085 JSFunction *fun = (JSFunction *) funbox->object;
2086 int fnlevel = level;
2089 * An eval can leak funbox, functions along its ancestor line, and its
2090 * immediate kids. Since FindFunArgs uses DFS and the parser propagates
2091 * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
2092 * already been marked as funargs by this point. Therefore we have to
2093 * flag only funbox->node and funbox->kids' nodes here.
2095 * Generators need to be treated in the same way. Even if the value
2096 * of a generator function doesn't escape, anything defined or referred
2097 * to inside the generator can escape through a call to the generator.
2098 * We could imagine doing static analysis to track the calls and see
2099 * if any iterators or values returned by iterators escape, but that
2100 * would be hard, so instead we just assume everything might escape.
2102 if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) {
2104 for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
2105 kid->node->setFunArg();
2109 * Compute in skipmin the least distance from fun's static level up to
2110 * an upvar, whether used directly by fun, or indirectly by a function
2113 uintN skipmin = UpvarCookie::FREE_LEVEL;
2114 JSParseNode *pn = fn->pn_body;
2116 if (pn->pn_type == TOK_UPVARS) {
2117 JSAtomList upvars(pn->pn_names);
2118 JS_ASSERT(upvars.count != 0);
2120 JSAtomListIterator iter(&upvars);
2121 JSAtomListElement *ale;
2123 while ((ale = iter()) != NULL) {
2124 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2126 if (!lexdep->isFreeVar()) {
2127 uintN upvarLevel = lexdep->frameLevel();
2129 if (int(upvarLevel) <= fnlevel)
2132 uintN skip = (funbox->level + 1) - upvarLevel;
2140 * If this function escapes, whether directly (the parser detects such
2141 * escapes) or indirectly (because this non-escaping function uses an
2142 * upvar that reaches across an outer function boundary where the outer
2143 * function escapes), enqueue it for further analysis, and bump fnlevel
2144 * to trap any non-escaping children.
2146 if (fn->isFunArg()) {
2147 queue->push(funbox);
2148 fnlevel = int(funbox->level);
2152 * Now process the current function's children, and recalibrate their
2153 * cumulative skipmin to be relative to the current static level.
2156 uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
2158 JS_ASSERT(kidskipmin != 0);
2159 if (kidskipmin != UpvarCookie::FREE_LEVEL) {
2161 if (kidskipmin != 0 && kidskipmin < skipmin)
2162 skipmin = kidskipmin;
2167 * Finally, after we've traversed all of the current function's kids,
2168 * minimize fun's skipmin against our accumulated skipmin. Do likewise
2169 * with allskipmin, but minimize across funbox and all of its siblings,
2170 * to compute our return value.
2172 if (skipmin != UpvarCookie::FREE_LEVEL) {
2173 fun->u.i.skipmin = skipmin;
2174 if (skipmin < allskipmin)
2175 allskipmin = skipmin;
2177 } while ((funbox = funbox->siblings) != NULL);
2183 Parser::markFunArgs(JSFunctionBox *funbox)
2185 JSFunctionBoxQueue queue;
2186 if (!queue.init(functionCount))
2189 FindFunArgs(funbox, -1, &queue);
2190 while ((funbox = queue.pull()) != NULL) {
2191 JSParseNode *fn = funbox->node;
2192 JS_ASSERT(fn->isFunArg());
2194 JSParseNode *pn = fn->pn_body;
2195 if (pn->pn_type == TOK_UPVARS) {
2196 JSAtomList upvars(pn->pn_names);
2197 JS_ASSERT(upvars.count != 0);
2199 JSAtomListIterator iter(&upvars);
2200 JSAtomListElement *ale;
2202 while ((ale = iter()) != NULL) {
2203 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2205 if (!lexdep->isFreeVar() &&
2206 !lexdep->isFunArg() &&
2207 (lexdep->kind() == JSDefinition::FUNCTION ||
2208 PN_OP(lexdep) == JSOP_CALLEE)) {
2210 * Mark this formerly-Algol-like function as an escaping
2211 * function (i.e., as a funarg), because it is used from
2214 * Progress is guaranteed because we set the funarg flag
2215 * here, which suppresses revisiting this function (thanks
2216 * to the !lexdep->isFunArg() test just above).
2218 lexdep->setFunArg();
2220 JSFunctionBox *afunbox;
2221 if (PN_OP(lexdep) == JSOP_CALLEE) {
2223 * A named function expression will not appear to be a
2224 * funarg if it is immediately applied. However, if its
2225 * name is used in an escaping function nested within
2226 * it, then it must become flagged as a funarg again.
2230 uintN calleeLevel = lexdep->pn_cookie.level();
2231 uintN staticLevel = afunbox->level + 1U;
2232 while (staticLevel != calleeLevel) {
2233 afunbox = afunbox->parent;
2236 JS_ASSERT(afunbox->level + 1U == calleeLevel);
2237 afunbox->node->setFunArg();
2239 afunbox = lexdep->pn_funbox;
2241 queue.push(afunbox);
2244 * Walk over nested functions again, now that we have
2245 * changed the level across which it is unsafe to access
2246 * upvars using the runtime dynamic link (frame chain).
2249 FindFunArgs(afunbox->kids, afunbox->level, &queue);
2258 MinBlockId(JSParseNode *fn, uint32 id)
2260 if (fn->pn_blockid < id)
2263 for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
2264 if (pn->pn_blockid < id)
2272 CanFlattenUpvar(JSDefinition *dn, JSFunctionBox *funbox, uint32 tcflags)
2275 * Consider the current function (the lambda, innermost below) using a var
2276 * x defined two static levels up:
2282 * return function () { return x; };
2287 * So long as (1) the initialization in 'var x = 42' dominates all uses of
2288 * g and (2) x is not reassigned, it is safe to optimize the lambda to a
2289 * flat closure. Uncommenting the early call to g makes this optimization
2290 * unsafe (z could name a global setter that calls its argument).
2292 JSFunctionBox *afunbox = funbox;
2293 uintN dnLevel = dn->frameLevel();
2295 JS_ASSERT(dnLevel <= funbox->level);
2296 while (afunbox->level != dnLevel) {
2297 afunbox = afunbox->parent;
2300 * NB: afunbox can't be null because we are sure to find a function box
2301 * whose level == dnLevel before we would try to walk above the root of
2302 * the funbox tree. See bug 493260 comments 16-18.
2304 * Assert but check anyway, to protect future changes that bind eval
2305 * upvars in the parser.
2310 * If this function is reaching up across an enclosing funarg, then we
2311 * cannot copy dn's value into a flat closure slot (the display stops
2312 * working once the funarg escapes).
2314 if (!afunbox || afunbox->node->isFunArg())
2318 * Reaching up for dn across a generator also means we can't flatten,
2319 * since the generator iterator does not run until later, in general.
2322 if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
2327 * If afunbox's function (which is at the same level as dn) is in a loop,
2328 * pessimistically assume the variable initializer may be in the same loop.
2329 * A flat closure would then be unsafe, as the captured variable could be
2330 * assigned after the closure is created. See bug 493232.
2332 if (afunbox->inLoop)
2336 * |with| and eval used as an operator defeat lexical scoping: they can be
2337 * used to assign to any in-scope variable. Therefore they must disable
2338 * flat closures that use such upvars. The parser detects these as special
2339 * forms and marks the function heavyweight.
2341 if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
2345 * If afunbox's function is not a lambda, it will be hoisted, so it could
2346 * capture the undefined value that by default initializes var, let, and
2347 * const bindings. And if dn is a function that comes at (meaning a
2348 * function refers to its own name) or strictly after afunbox, we also
2349 * defeat the flat closure optimization for this dn.
2351 JSFunction *afun = (JSFunction *) afunbox->object;
2352 if (!(afun->flags & JSFUN_LAMBDA)) {
2353 if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
2357 if (!dn->isInitialized())
2360 JSDefinition::Kind dnKind = dn->kind();
2361 if (dnKind != JSDefinition::CONST) {
2362 if (dn->isAssigned())
2366 * Any formal could be mutated behind our back via the arguments
2367 * object, so deoptimize if the outer function uses arguments.
2369 * In a Function constructor call where the final argument -- the body
2370 * source for the function to create -- contains a nested function
2371 * definition or expression, afunbox->parent will be null. The body
2372 * source might use |arguments| outside of any nested functions it may
2373 * contain, so we have to check the tcflags parameter that was passed
2374 * in from Compiler::compileFunctionBody.
2376 if (dnKind == JSDefinition::ARG &&
2377 ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
2383 * Check quick-and-dirty dominance relation. Function definitions dominate
2384 * their uses thanks to hoisting. Other binding forms hoist as undefined,
2385 * of course, so check forward-reference and blockid relations.
2387 if (dnKind != JSDefinition::FUNCTION) {
2389 * Watch out for code such as
2393 * var jQuery = ... = function (...) {
2394 * return new jQuery.foo.bar(baz);
2399 * where the jQuery variable is not reassigned, but of course is not
2400 * initialized at the time that the would-be-flat closure containing
2401 * the jQuery upvar is formed.
2403 if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
2405 if (!MinBlockId(afunbox->node, dn->pn_blockid))
2412 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32 *tcflags)
2414 uintN dnLevel = dn->frameLevel();
2416 while ((funbox = funbox->parent) != NULL) {
2418 * Notice that funbox->level is the static level of the definition or
2419 * expression of the function parsed into funbox, not the static level
2420 * of its body. Therefore we must add 1 to match dn's level to find the
2421 * funbox whose body contains the dn definition.
2423 if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
2424 funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2427 funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
2430 if (!funbox && (*tcflags & TCF_IN_FUNCTION))
2431 *tcflags |= TCF_FUN_HEAVYWEIGHT;
2435 DeoptimizeUsesWithin(JSDefinition *dn, const TokenPos &pos)
2437 uintN ndeoptimized = 0;
2439 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2440 JS_ASSERT(pnu->pn_used);
2441 JS_ASSERT(!pnu->pn_defn);
2442 if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) {
2443 pnu->pn_dflags |= PND_DEOPTIMIZED;
2448 return ndeoptimized != 0;
2452 Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags)
2454 #define FUN_METER(x) JS_FUNCTION_METER(context, x)
2457 JSParseNode *fn = funbox->node;
2458 JSParseNode *pn = fn->pn_body;
2461 setFunctionKinds(funbox->kids, tcflags);
2464 * We've unwound from recursively setting our kids' kinds, which
2465 * also classifies enclosing functions holding upvars referenced in
2466 * those descendants' bodies. So now we can check our "methods".
2468 * Despecialize from branded method-identity-based shape to shape-
2469 * or slot-based shape if this function smells like a constructor
2470 * and too many of its methods are *not* joinable null closures
2471 * (i.e., they have one or more upvars fetched via the display).
2473 JSParseNode *pn2 = pn;
2474 if (PN_TYPE(pn2) == TOK_UPVARS)
2476 if (PN_TYPE(pn2) == TOK_ARGSBODY)
2479 #if JS_HAS_EXPR_CLOSURES
2480 if (PN_TYPE(pn2) == TOK_LC)
2482 if (!(funbox->tcflags & TCF_RETURN_EXPR)) {
2483 uintN methodSets = 0, slowMethodSets = 0;
2485 for (JSParseNode *method = funbox->methods; method; method = method->pn_link) {
2486 JS_ASSERT(PN_OP(method) == JSOP_LAMBDA || PN_OP(method) == JSOP_LAMBDA_FC);
2488 if (!method->pn_funbox->joinable())
2492 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
2493 funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
2497 JSFunction *fun = (JSFunction *) funbox->object;
2499 JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
2502 if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
2504 } else if (funbox->inAnyDynamicScope()) {
2505 JS_ASSERT(!FUN_NULL_CLOSURE(fun));
2506 FUN_METER(indynamicscope);
2507 } else if (pn->pn_type != TOK_UPVARS) {
2509 * No lexical dependencies => null closure, for best performance.
2510 * A null closure needs no scope chain, but alas we've coupled
2511 * principals-finding to scope (for good fundamental reasons, but
2512 * the implementation overloads the parent slot and we should fix
2513 * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
2515 * In more detail: the ES3 spec allows the implementation to create
2516 * "joined function objects", or not, at its discretion. But real-
2517 * world implementations always create unique function objects for
2518 * closures, and this can be detected via mutation. Open question:
2519 * do popular implementations create unique function objects for
2522 * FIXME: bug 476950.
2524 FUN_METER(nofreeupvar);
2525 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2527 JSAtomList upvars(pn->pn_names);
2528 JS_ASSERT(upvars.count != 0);
2530 JSAtomListIterator iter(&upvars);
2531 JSAtomListElement *ale;
2533 if (!fn->isFunArg()) {
2535 * This function is Algol-like, it never escapes.
2537 * Check that at least one outer lexical binding was assigned
2538 * to (global variables don't count). This is conservative: we
2539 * could limit assignments to those in the current function,
2540 * but that's too much work. As with flat closures (handled
2541 * below), we optimize for the case where outer bindings are
2542 * not reassigned anywhere.
2544 while ((ale = iter()) != NULL) {
2545 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2547 if (!lexdep->isFreeVar()) {
2548 JS_ASSERT(lexdep->frameLevel() <= funbox->level);
2554 FUN_METER(onlyfreevar);
2555 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2558 uintN nupvars = 0, nflattened = 0;
2561 * For each lexical dependency from this closure to an outer
2562 * binding, analyze whether it is safe to copy the binding's
2563 * value into a flat closure slot when the closure is formed.
2565 while ((ale = iter()) != NULL) {
2566 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2568 if (!lexdep->isFreeVar()) {
2570 if (CanFlattenUpvar(lexdep, funbox, *tcflags)) {
2576 * FIXME bug 545759: to test nflattened != 0 instead of
2577 * nflattened == nupvars below, we'll need to avoid n^2
2578 * bugs such as 617430 in uncommenting the following:
2580 * if (DeoptimizeUsesWithin(lexdep, funbox->node->pn_body->pn_pos))
2581 * FlagHeavyweights(lexdep, funbox, tcflags);
2583 * For now it's best to avoid this tedious, use-wise
2584 * deoptimization and let fun remain an unoptimized
2585 * closure. This is safe because we leave fun's kind
2586 * set to interpreted, so all functions holding its
2587 * upvars will be flagged as heavyweight.
2593 FUN_METER(onlyfreevar);
2594 FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2595 } else if (nflattened == nupvars) {
2597 * We made it all the way through the upvar loop, so it's
2598 * safe to optimize to a flat closure.
2601 FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2602 switch (PN_OP(fn)) {
2604 fn->pn_op = JSOP_DEFFUN_FC;
2606 case JSOP_DEFLOCALFUN:
2607 fn->pn_op = JSOP_DEFLOCALFUN_FC;
2610 fn->pn_op = JSOP_LAMBDA_FC;
2613 /* js_EmitTree's case TOK_FUNCTION: will select op. */
2614 JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2617 FUN_METER(badfunarg);
2622 if (FUN_KIND(fun) == JSFUN_INTERPRETED && pn->pn_type == TOK_UPVARS) {
2624 * One or more upvars cannot be safely snapshot into a flat
2625 * closure's non-reserved slot (see JSOP_GETFCSLOT), so we loop
2626 * again over all upvars, and for each non-free upvar, ensure that
2627 * its containing function has been flagged as heavyweight.
2629 * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2630 * generating any code for a tree of nested functions.
2632 JSAtomList upvars(pn->pn_names);
2633 JS_ASSERT(upvars.count != 0);
2635 JSAtomListIterator iter(&upvars);
2636 JSAtomListElement *ale;
2638 while ((ale = iter()) != NULL) {
2639 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2640 if (!lexdep->isFreeVar())
2641 FlagHeavyweights(lexdep, funbox, tcflags);
2645 if (funbox->joinable())
2648 funbox = funbox->siblings;
2656 const char js_argument_str[] = "argument";
2657 const char js_variable_str[] = "variable";
2658 const char js_unknown_str[] = "unknown";
2661 JSDefinition::kindString(Kind kind)
2663 static const char *table[] = {
2664 js_var_str, js_const_str, js_let_str,
2665 js_function_str, js_argument_str, js_unknown_str
2668 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2672 static JSFunctionBox *
2673 EnterFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2674 uintN lambda = JSFUN_LAMBDA)
2676 JSTreeContext *tc = funtc->parent;
2677 JSFunction *fun = tc->parser->newFunction(tc, funAtom, lambda);
2681 /* Create box for fun->object early to protect against last-ditch GC. */
2682 JSFunctionBox *funbox = tc->parser->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2686 /* Initialize non-default members of funtc. */
2687 funtc->flags |= funbox->tcflags;
2688 funtc->blockidGen = tc->blockidGen;
2689 if (!GenerateBlockId(funtc, funtc->bodyid))
2691 funtc->setFunction(fun);
2692 funtc->funbox = funbox;
2693 if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2700 LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
2701 uintN lambda = JSFUN_LAMBDA)
2703 JSTreeContext *tc = funtc->parent;
2704 tc->blockidGen = funtc->blockidGen;
2706 JSFunctionBox *funbox = fn->pn_funbox;
2707 funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO | TCF_RETURN_EXPR);
2709 fn->pn_dflags |= PND_INITIALIZED;
2710 if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2711 fn->pn_dflags |= PND_BLOCKCHILD;
2714 * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2715 * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2716 * params and body. We do this only if there are lexical dependencies not
2717 * satisfied by the function's declarations, to avoid penalizing functions
2718 * that use only their arguments and other local bindings.
2720 if (funtc->lexdeps.count != 0) {
2721 JSAtomListIterator iter(&funtc->lexdeps);
2722 JSAtomListElement *ale;
2723 int foundCallee = 0;
2725 while ((ale = iter()) != NULL) {
2726 JSAtom *atom = ALE_ATOM(ale);
2727 JSDefinition *dn = ALE_DEFN(ale);
2728 JS_ASSERT(dn->isPlaceholder());
2730 if (atom == funAtom && lambda != 0) {
2731 dn->pn_op = JSOP_CALLEE;
2732 dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
2733 dn->pn_dflags |= PND_BOUND;
2736 * If this named function expression uses its own name other
2737 * than to call itself, flag this function specially.
2740 funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
2745 if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2748 * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2749 * any use of dn in funtc assigns. See NoteLValue for the easy
2750 * backward-reference case; this is the hard forward-reference
2751 * case where we pay a higher price.
2753 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2754 if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2755 funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2761 JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2764 * Make sure to deoptimize lexical dependencies that are polluted
2765 * by eval or with, to safely bind globals (see bug 561923).
2767 if (funtc->callsEval() ||
2768 (outer_ale && tc->innermostWith &&
2769 ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) {
2770 DeoptimizeUsesWithin(dn, fn->pn_pos);
2774 outer_ale = tc->lexdeps.lookup(atom);
2777 * Create a new placeholder for our outer lexdep. We could simply re-use
2778 * the inner placeholder, but that introduces subtleties in the case where
2779 * we find a later definition that captures an existing lexdep. For
2782 * function f() { function g() { x; } let x; }
2784 * Here, g's TOK_UPVARS node lists the placeholder for x, which must be
2785 * captured by the 'let' declaration later, since 'let's are hoisted.
2786 * Taking g's placeholder as our own would work fine. But consider:
2788 * function f() { x; { function g() { x; } let x; } }
2790 * Here, the 'let' must not capture all the uses of f's lexdep entry for
2791 * x, but it must capture the x node referred to from g's TOK_UPVARS node.
2792 * Always turning inherited lexdeps into uses of a new outer definition
2793 * allows us to handle both these cases in a natural way.
2795 outer_ale = MakePlaceholder(dn, tc);
2798 JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2801 * Insert dn's uses list at the front of outer_dn's list.
2803 * Without loss of generality or correctness, we allow a dn to
2804 * be in inner and outer lexdeps, since the purpose of lexdeps
2805 * is one-pass coordination of name use and definition across
2806 * functions, and if different dn's are used we'll merge lists
2807 * when leaving the inner function.
2809 * The dn == outer_dn case arises with generator expressions
2810 * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2811 * case), and nowhere else, currently.
2813 if (dn != outer_dn) {
2814 JSParseNode **pnup = &dn->dn_uses;
2817 while ((pnu = *pnup) != NULL) {
2818 pnu->pn_lexdef = outer_dn;
2819 pnup = &pnu->pn_link;
2823 * Make dn be a use that redirects to outer_dn, because we
2824 * can't replace dn with outer_dn in all the pn_namesets in
2825 * the AST where it may be. Instead we make it forward to
2826 * outer_dn. See JSDefinition::resolve.
2828 *pnup = outer_dn->dn_uses;
2829 outer_dn->dn_uses = dn;
2830 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2831 dn->pn_defn = false;
2833 dn->pn_lexdef = outer_dn;
2836 /* Mark the outer dn as escaping. */
2837 outer_dn->pn_dflags |= PND_CLOSED;
2840 if (funtc->lexdeps.count - foundCallee != 0) {
2841 JSParseNode *body = fn->pn_body;
2843 fn->pn_body = NameSetNode::create(tc);
2847 fn->pn_body->pn_type = TOK_UPVARS;
2848 fn->pn_body->pn_pos = body->pn_pos;
2850 funtc->lexdeps.remove(tc->parser, funAtom);
2851 fn->pn_body->pn_names = funtc->lexdeps;
2852 fn->pn_body->pn_tree = body;
2855 funtc->lexdeps.clear();
2859 * Check whether any parameters have been assigned within this function.
2860 * In strict mode parameters do not alias arguments[i], and to make the
2861 * arguments object reflect initial parameter values prior to any mutation
2862 * we create it eagerly whenever parameters are (or might, in the case of
2863 * calls to eval) be assigned.
2865 if (funtc->inStrictMode() && funbox->object->getFunctionPrivate()->nargs > 0) {
2866 JSAtomListIterator iter(&funtc->decls);
2867 JSAtomListElement *ale;
2869 while ((ale = iter()) != NULL) {
2870 JSDefinition *dn = ALE_DEFN(ale);
2871 if (dn->kind() == JSDefinition::ARG && dn->isAssigned()) {
2872 funbox->tcflags |= TCF_FUN_MUTATES_PARAMETER;
2878 funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
2884 DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
2887 * FIXME? this Parser method was factored from Parser::functionDef with minimal
2888 * change, hence the funtc ref param and funbox. It probably should match
2889 * functionBody, etc., and use tc and tc->funbox instead of taking explicit
2893 Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **listp)
2895 if (tokenStream.getToken() != TOK_LP) {
2896 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
2900 if (!tokenStream.matchToken(TOK_RP)) {
2901 #if JS_HAS_DESTRUCTURING
2902 JSAtom *duplicatedArg = NULL;
2903 bool destructuringArg = false;
2904 JSParseNode *list = NULL;
2908 switch (TokenKind tt = tokenStream.getToken()) {
2909 #if JS_HAS_DESTRUCTURING
2913 /* See comment below in the TOK_NAME case. */
2915 goto report_dup_and_destructuring;
2916 destructuringArg = true;
2919 * A destructuring formal parameter turns into one or more
2920 * local variables initialized from properties of a single
2921 * anonymous positional parameter, so here we must tweak our
2922 * binder and its data.
2926 data.op = JSOP_DEFVAR;
2927 data.binder = BindDestructuringArg;
2928 JSParseNode *lhs = destructuringExpr(&data, tt);
2933 * Adjust fun->nargs to count the single anonymous positional
2934 * parameter that is to be destructured.
2937 if (!funtc.bindings.addDestructuring(context, &slot))
2941 * Synthesize a destructuring assignment from the single
2942 * anonymous positional parameter into the destructuring
2943 * left-hand-side expression and accumulate it in list.
2945 JSParseNode *rhs = NameNode::create(context->runtime->atomState.emptyAtom, &funtc);
2948 rhs->pn_type = TOK_NAME;
2949 rhs->pn_op = JSOP_GETARG;
2950 rhs->pn_cookie.set(funtc.staticLevel, slot);
2951 rhs->pn_dflags |= PND_BOUND;
2954 JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2958 list = ListNode::create(&funtc);
2961 list->pn_type = TOK_VAR;
2968 #endif /* JS_HAS_DESTRUCTURING */
2972 JSAtom *atom = tokenStream.currentToken().t_atom;
2974 #ifdef JS_HAS_DESTRUCTURING
2976 * ECMA-262 requires us to support duplicate parameter names,
2977 * but if the parameter list includes destructuring, we
2978 * consider the code to have "opted in" to higher standards and
2979 * forbid duplicates. We may see a destructuring parameter
2980 * later, so always note duplicates now.
2982 * Duplicates are warned about (strict option) or cause errors
2983 * (strict mode code), but we do those tests in one place
2984 * below, after having parsed the body in case it begins with a
2985 * "use strict"; directive.
2987 * NB: Check funtc.decls rather than funtc.bindings, because
2988 * destructuring bindings aren't added to funtc.bindings
2989 * until after all arguments have been parsed.
2991 if (funtc.decls.lookup(atom)) {
2992 duplicatedArg = atom;
2993 if (destructuringArg)
2994 goto report_dup_and_destructuring;
2999 if (!funtc.bindings.addArgument(context, atom, &slot))
3001 if (!DefineArg(funbox->node, atom, slot, &funtc))
3007 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_MISSING_FORMAL);
3012 #if JS_HAS_DESTRUCTURING
3013 report_dup_and_destructuring:
3014 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
3015 reportErrorNumber(dn, JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
3019 } while (tokenStream.matchToken(TOK_COMMA));
3021 if (tokenStream.getToken() != TOK_RP) {
3022 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_FORMAL);
3031 Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
3033 /* Make a TOK_FUNCTION node. */
3034 tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
3035 JSParseNode *pn = FunctionNode::create(tc);
3039 pn->pn_cookie.makeFree();
3042 * If a lambda, mark this function as escaping (as a "funarg") unless it is
3043 * immediately applied (we clear PND_FUNARG if so -- see memberExpr).
3045 * Treat function sub-statements (non-lambda, non-body-level functions) as
3046 * escaping funargs, since we can't statically analyze their definitions
3049 bool bodyLevel = tc->atBodyLevel();
3050 pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
3053 * Record names for function statements in tc->decls so we know when to
3054 * avoid optimizing variable references that might name a function.
3056 if (lambda == 0 && funAtom) {
3057 if (JSAtomListElement *ale = tc->decls.lookup(funAtom)) {
3058 JSDefinition *dn = ALE_DEFN(ale);
3059 JSDefinition::Kind dn_kind = dn->kind();
3061 JS_ASSERT(!dn->pn_used);
3062 JS_ASSERT(dn->pn_defn);
3064 if (context->hasStrictOption() || dn_kind == JSDefinition::CONST) {
3065 JSAutoByteString name;
3066 if (!js_AtomToPrintableString(context, funAtom, &name) ||
3067 !reportErrorNumber(NULL,
3068 (dn_kind != JSDefinition::CONST)
3069 ? JSREPORT_WARNING | JSREPORT_STRICT
3071 JSMSG_REDECLARED_VAR,
3072 JSDefinition::kindString(dn_kind),
3079 ALE_SET_DEFN(ale, pn);
3081 pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
3083 if (!MakeDefIntoUse(dn, pn, funAtom, tc))
3086 } else if (bodyLevel) {
3088 * If this function was used before it was defined, claim the
3089 * pre-created definition node for this function that primaryExpr
3090 * put in tc->lexdeps on first forward reference, and recycle pn.
3094 ale = tc->lexdeps.rawLookup(funAtom, hep);
3096 JSDefinition *fn = ALE_DEFN(ale);
3098 JS_ASSERT(fn->pn_defn);
3099 fn->pn_type = TOK_FUNCTION;
3100 fn->pn_arity = PN_FUNC;
3101 fn->pn_pos.begin = pn->pn_pos.begin;
3103 fn->pn_cookie.makeFree();
3105 tc->lexdeps.rawRemove(tc->parser, ale, hep);
3106 RecycleTree(pn, tc);
3110 if (!Define(pn, funAtom, tc))
3115 * A function directly inside another's body needs only a local
3116 * variable to bind its name to its value, and not an activation object
3117 * property (it might also need the activation property, if the outer
3118 * function contains with statements, e.g., but the stack slot wins
3119 * when jsemit.cpp's BindNameToSlot can optimize a JSOP_NAME into a
3120 * JSOP_GETLOCAL bytecode).
3122 if (bodyLevel && tc->inFunction()) {
3124 * Define a local in the outer function so that BindNameToSlot
3125 * can properly optimize accesses. Note that we need a local
3126 * variable, not an argument, for the function statement. Thus
3127 * we add a variable even if a parameter with the given name
3131 switch (tc->bindings.lookup(context, funAtom, &index)) {
3134 index = tc->bindings.countVars();
3135 if (!tc->bindings.addVariable(context, funAtom))
3140 pn->pn_cookie.set(tc->staticLevel, index);
3141 pn->pn_dflags |= PND_BOUND;
3149 JSTreeContext *outertc = tc;
3151 /* Initialize early for possible flags mutation via destructuringExpr. */
3152 JSTreeContext funtc(tc->parser);
3154 JSFunctionBox *funbox = EnterFunction(pn, &funtc, funAtom, lambda);
3158 JSFunction *fun = (JSFunction *) funbox->object;
3160 /* Now parse formal argument list and compute fun->nargs. */
3161 JSParseNode *prelude = NULL;
3162 if (!functionArguments(funtc, funbox, &prelude))
3165 fun->setArgCount(funtc.bindings.countArgs());
3167 #if JS_HAS_DESTRUCTURING
3169 * If there were destructuring formal parameters, bind the destructured-to
3170 * local variables now that we've parsed all the regular and destructuring
3171 * formal parameters. Because js::Bindings::add must be called first for
3172 * all ARGUMENTs, then all VARIABLEs and CONSTANTs, and finally all UPVARs,
3173 * we can't bind vars induced by formal parameter destructuring until after
3174 * Parser::functionArguments has returned.
3177 JSAtomListIterator iter(&funtc.decls);
3179 while (JSAtomListElement *ale = iter()) {
3180 JSParseNode *apn = ALE_DEFN(ale);
3182 /* Filter based on pn_op -- see BindDestructuringArg, above. */
3183 if (apn->pn_op != JSOP_SETLOCAL)
3186 uint16 index = funtc.bindings.countVars();
3187 if (!BindLocalVariable(context, &funtc, apn->pn_atom, VARIABLE, true))
3189 apn->pn_cookie.set(funtc.staticLevel, index);
3194 if (type == GETTER && fun->nargs > 0) {
3195 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
3196 "getter", "no", "s");
3199 if (type == SETTER && fun->nargs != 1) {
3200 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ACCESSOR_WRONG_ARGS,
3201 "setter", "one", "");
3205 #if JS_HAS_EXPR_CLOSURES
3206 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
3208 tokenStream.ungetToken();
3209 fun->flags |= JSFUN_EXPR_CLOSURE;
3212 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
3215 JSParseNode *body = functionBody();
3219 if (funAtom && !CheckStrictBinding(context, &funtc, funAtom, pn))
3222 if (!CheckStrictParameters(context, &funtc))
3225 #if JS_HAS_EXPR_CLOSURES
3227 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
3228 else if (lambda == 0 && !MatchOrInsertSemicolon(context, &tokenStream))
3231 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
3233 pn->pn_pos.end = tokenStream.currentToken().pos.end;
3236 * Fruit of the poisonous tree: if a closure calls eval, we consider the
3237 * parent to call eval. We need this for two reasons: (1) the Jaegermonkey
3238 * optimizations really need to know if eval is called transitively, and
3239 * (2) in strict mode, eval called transitively requires eager argument
3240 * creation in strict mode parent functions.
3242 * For the latter, we really only need to propagate callsEval if both
3243 * functions are strict mode, but we don't lose much by always propagating.
3244 * The only optimization we lose this way is in the case where a function
3245 * is strict, does not mutate arguments, does not call eval directly, but
3246 * calls eval transitively.
3248 if (funtc.callsEval())
3249 outertc->noteCallsEval();
3251 #if JS_HAS_DESTRUCTURING
3253 * If there were destructuring formal parameters, prepend the initializing
3254 * comma expression that we synthesized to body. If the body is a return
3255 * node, we must make a special TOK_SEQ node, to prepend the destructuring
3256 * code without bracing the decompilation of the function body.
3259 if (body->pn_arity != PN_LIST) {
3262 block = ListNode::create(outertc);
3265 block->pn_type = TOK_SEQ;
3266 block->pn_pos = body->pn_pos;
3267 block->initList(body);
3272 JSParseNode *item = UnaryNode::create(outertc);
3276 item->pn_type = TOK_SEMI;
3277 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
3278 item->pn_kid = prelude;
3279 item->pn_next = body->pn_head;
3280 body->pn_head = item;
3281 if (body->pn_tail == &body->pn_head)
3282 body->pn_tail = &item->pn_next;
3284 body->pn_xflags |= PNX_DESTRUCT;
3289 * If we collected flags that indicate nested heavyweight functions, or
3290 * this function contains heavyweight-making statements (with statement,
3291 * visible eval call, or assignment to 'arguments'), flag the function as
3292 * heavyweight (requiring a call object per invocation).
3294 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
3295 fun->flags |= JSFUN_HEAVYWEIGHT;
3296 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
3299 * If this function is not at body level of a program or function (i.e.
3300 * it is a function statement that is not a direct child of a program
3301 * or function), then our enclosing function, if any, must be
3304 if (!bodyLevel && lambda == 0 && funAtom)
3305 outertc->flags |= TCF_FUN_HEAVYWEIGHT;
3308 JSParseNode *result = pn;
3312 * ECMA ed. 3 standard: function expression, possibly anonymous.
3315 } else if (!funAtom) {
3317 * If this anonymous function definition is *not* embedded within a
3318 * larger expression, we treat it as an expression statement, not as
3319 * a function declaration -- and not as a syntax error (as ECMA-262
3320 * Edition 3 would have it). Backward compatibility must trump all,
3321 * unless JSOPTION_ANONFUNFIX is set.
3323 result = UnaryNode::create(outertc);
3326 result->pn_type = TOK_SEMI;
3327 result->pn_pos = pn->pn_pos;
3328 result->pn_kid = pn;
3330 } else if (!bodyLevel) {
3332 * ECMA ed. 3 extension: a function expression statement not at the
3333 * top level, e.g., in a compound statement such as the "then" part
3334 * of an "if" statement, binds a closure only if control reaches that
3338 outertc->noteMightAliasLocals();
3341 funbox->kids = funtc.functionList;
3343 pn->pn_funbox = funbox;
3346 pn->pn_body->append(body);
3347 pn->pn_body->pn_pos = body->pn_pos;
3352 if (!outertc->inFunction() && bodyLevel && funAtom && !lambda && outertc->compiling()) {
3353 JS_ASSERT(pn->pn_cookie.isFree());
3354 if (!DefineGlobal(pn, outertc->asCodeGenerator(), funAtom))
3358 pn->pn_blockid = outertc->blockid();
3360 if (!LeaveFunction(pn, &funtc, funAtom, lambda))
3363 /* If the surrounding function is not strict code, reset the lexer. */
3364 if (!outertc->inStrictMode())
3365 tokenStream.setStrictMode(false);
3371 Parser::functionStmt()
3373 JSAtom *name = NULL;
3374 if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME) {
3375 name = tokenStream.currentToken().t_atom;
3377 if (hasAnonFunFix()) {
3378 /* Extension: accept unnamed function expressions as statements. */
3379 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
3382 tokenStream.ungetToken();
3385 /* We forbid function statements in strict mode code. */
3386 if (!tc->atBodyLevel() && tc->inStrictMode()) {
3387 reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
3391 return functionDef(name, GENERAL, 0);
3395 Parser::functionExpr()
3397 JSAtom *name = NULL;
3398 if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
3399 name = tokenStream.currentToken().t_atom;
3401 tokenStream.ungetToken();
3402 return functionDef(name, GENERAL, JSFUN_LAMBDA);
3406 * Recognize Directive Prologue members and directives. Assuming |pn| is a
3407 * candidate for membership in a directive prologue, recognize directives and
3408 * set |tc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
3409 * |pn_prologue| flag.
3411 * Note that the following is a strict mode function:
3414 * "blah" // inserted semi colon
3420 * That is, even though "use\x20loose" can never be a directive, now or in the
3421 * future (because of the hex escape), the Directive Prologue extends through it
3422 * to the "use strict" statement, which is indeed a directive.
3425 Parser::recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember)
3427 *isDirectivePrologueMember = pn->isStringExprStatement();
3428 if (!*isDirectivePrologueMember)
3431 JSParseNode *kid = pn->pn_kid;
3432 if (kid->isEscapeFreeStringLiteral()) {
3434 * Mark this statement as being a possibly legitimate part of a
3435 * directive prologue, so the byte code emitter won't warn about it
3436 * being useless code. (We mustn't just omit the statement entirely yet,
3437 * as it could be producing the value of an eval or JSScript execution.)
3439 * Note that even if the string isn't one we recognize as a directive,
3440 * the emitter still shouldn't flag it as useless, as it could become a
3441 * directive in the future. We don't want to interfere with people
3442 * taking advantage of directive-prologue-enabled features that appear
3443 * in other browsers first.
3445 pn->pn_prologue = true;
3447 JSAtom *directive = kid->pn_atom;
3448 if (directive == context->runtime->atomState.useStrictAtom) {
3450 * Unfortunately, Directive Prologue members in general may contain
3451 * escapes, even while "use strict" directives may not. Therefore
3452 * we must check whether an octal character escape has been seen in
3453 * any previous directives whenever we encounter a "use strict"
3454 * directive, so that the octal escape is properly treated as a
3455 * syntax error. An example of this case:
3459 * "\145"; // octal escape
3460 * "use strict"; // retroactively makes "\145" a syntax error
3463 if (tokenStream.hasOctalCharacterEscape()) {
3464 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DEPRECATED_OCTAL);
3468 tc->flags |= TCF_STRICT_MODE_CODE;
3469 tokenStream.setStrictMode();
3476 * Parse the statements in a block, creating a TOK_LC node that lists the
3477 * statements' trees. If called from block-parsing code, the caller must
3478 * match { before and } after.
3481 Parser::statements()
3483 JSParseNode *pn, *pn2, *saveBlock;
3486 JS_CHECK_RECURSION(context, return NULL);
3488 pn = ListNode::create(tc);
3491 pn->pn_type = TOK_LC;
3493 pn->pn_blockid = tc->blockid();
3494 saveBlock = tc->blockNode;
3497 bool inDirectivePrologue = tc->atBodyLevel();
3498 tokenStream.setOctalCharacterEscape(false);
3500 tt = tokenStream.peekToken(TSF_OPERAND);
3501 if (tt <= TOK_EOF || tt == TOK_RC) {
3502 if (tt == TOK_ERROR) {
3503 if (tokenStream.isEOF())
3504 tokenStream.setUnexpectedEOF();
3511 if (tokenStream.isEOF())
3512 tokenStream.setUnexpectedEOF();
3516 if (inDirectivePrologue && !recognizeDirectivePrologue(pn2, &inDirectivePrologue))
3519 if (pn2->pn_type == TOK_FUNCTION) {
3521 * PNX_FUNCDEFS notifies the emitter that the block contains body-
3522 * level function definitions that should be processed before the
3525 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
3526 * is relevant only for function definitions not at body-level,
3527 * which we call function statements.
3529 if (tc->atBodyLevel())
3530 pn->pn_xflags |= PNX_FUNCDEFS;
3532 tc->flags |= TCF_HAS_FUNCTION_STMT;
3538 * Handle the case where there was a let declaration under this block. If
3539 * it replaced tc->blockNode with a new block node then we must refresh pn
3540 * and then restore tc->blockNode.
3542 if (tc->blockNode != pn)
3544 tc->blockNode = saveBlock;
3546 pn->pn_pos.end = tokenStream.currentToken().pos.end;
3555 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
3559 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
3561 /* Check for (a = b) and warn about possible (a == b) mistype. */
3562 if (pn->pn_type == TOK_ASSIGN &&
3563 pn->pn_op == JSOP_NOP &&
3565 !reportErrorNumber(NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_EQUAL_AS_ASSIGN, "")) {
3572 MatchLabel(JSContext *cx, TokenStream *ts, JSParseNode *pn)
3577 tt = ts->peekTokenSameLine();
3578 if (tt == TOK_ERROR)
3580 if (tt == TOK_NAME) {
3581 (void) ts->getToken();
3582 label = ts->currentToken().t_atom;
3586 pn->pn_atom = label;
3591 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3595 JSAtomListElement *ale;
3599 * Body-level 'let' is the same as 'var' currently -- this may change in a
3600 * successor standard to ES5 that specifies 'let'.
3602 JS_ASSERT(!tc->atBodyLevel());
3605 if (!CheckStrictBinding(cx, tc, atom, pn))
3608 blockObj = tc->blockChain();
3609 ale = tc->decls.lookup(atom);
3610 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3611 JSAutoByteString name;
3612 if (js_AtomToPrintableString(cx, atom, &name)) {
3613 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3614 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3615 (ale && ALE_DEFN(ale)->isConst())
3623 n = OBJ_BLOCK_COUNT(cx, blockObj);
3624 if (n == JS_BIT(16)) {
3625 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3626 JSREPORT_ERROR, data->let.overflow);
3631 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3632 * This is balanced by PopStatement, defined immediately below.
3634 if (!Define(pn, atom, tc, true))
3638 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3639 * upvar cookie whose skip tells the current static level. The emitter will
3640 * adjust the node's slot based on its stack depth model -- and, for global
3641 * and eval code, Compiler::compileScript will adjust the slot again to
3642 * include script->nfixed.
3644 pn->pn_op = JSOP_GETLOCAL;
3645 pn->pn_cookie.set(tc->staticLevel, uint16(n));
3646 pn->pn_dflags |= PND_LET | PND_BOUND;
3649 * Define the let binding's property before storing pn in the the binding's
3650 * slot indexed by n off the class-reserved slot base.
3652 const Shape *shape = blockObj->defineBlockVariable(cx, ATOM_TO_JSID(atom), n);
3657 * Store pn temporarily in what would be shape-mapped slots in a cloned
3658 * block object (once the prototype's final population is known, after all
3659 * 'let' bindings for this block have been parsed). We free these slots in
3660 * jsemit.cpp:EmitEnterBlock so they don't tie up unused space in the so-
3661 * called "static" prototype Block.
3663 blockObj->setSlot(shape->slot, PrivateValue(pn));
3668 PopStatement(JSTreeContext *tc)
3670 JSStmtInfo *stmt = tc->topStmt;
3672 if (stmt->flags & SIF_SCOPE) {
3673 JSObject *obj = stmt->blockBox->object;
3674 JS_ASSERT(!obj->isClonedBlock());
3676 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
3677 JSAtom *atom = JSID_TO_ATOM(r.front().id);
3679 /* Beware the empty destructuring dummy. */
3680 if (atom == tc->parser->context->runtime->atomState.emptyAtom)
3682 tc->decls.remove(tc->parser, atom);
3685 js_PopStatement(tc);
3689 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3691 while (stmt->downScope) {
3692 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3695 if (stmt->type == STMT_BLOCK)
3702 * If we are generating global or eval-called-from-global code, bind a "gvar"
3703 * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
3704 * global variable access by memoizing name-to-slot mappings during execution
3705 * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3706 * can't be done due to a pre-existing property of the same name as the var or
3707 * const but incompatible attributes/getter/setter/etc, these ops devolve to
3710 * For now, don't try to lookup eval frame variables at compile time. This is
3711 * sub-optimal: we could handle eval-called-from-global-code gvars since eval
3712 * gets its own script and frame. The eval-from-function-code case is harder,
3713 * since functions do not atomize gvars and then reserve their atom indexes as
3714 * stack frame slots.
3717 DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom)
3719 GlobalScope *globalScope = cg->compiler()->globalScope;
3720 JSObject *globalObj = globalScope->globalObj;
3722 if (!cg->compileAndGo() || !globalObj || cg->compilingForEval())
3725 JSAtomListElement *ale = globalScope->names.lookup(atom);
3727 JSContext *cx = cg->parser->context;
3731 if (!globalObj->lookupProperty(cx, ATOM_TO_JSID(atom), &holder, &prop))
3734 JSFunctionBox *funbox = (pn->pn_type == TOK_FUNCTION) ? pn->pn_funbox : NULL;
3736 GlobalScope::GlobalDef def;
3739 * A few cases where we don't bother aggressively caching:
3740 * 1) Function value changes.
3741 * 2) Configurable properties.
3742 * 3) Properties without slots, or with getters/setters.
3744 const Shape *shape = (const Shape *)prop;
3746 globalObj != holder ||
3747 shape->configurable() ||
3748 !shape->hasSlot() ||
3749 !shape->hasDefaultGetterOrIsMethod() ||
3750 !shape->hasDefaultSetter()) {
3754 def = GlobalScope::GlobalDef(shape->slot);
3756 def = GlobalScope::GlobalDef(atom, funbox);
3759 if (!globalScope->defs.append(def))
3762 ale = globalScope->names.add(cg->parser, atom);
3766 JS_ASSERT(ALE_INDEX(ale) == globalScope->defs.length() - 1);
3769 * Functions can be redeclared, and the last one takes effect. Check
3770 * for this and make sure to rewrite the definition.
3772 * Note: This could overwrite an existing variable declaration, for
3777 * This rewrite is allowed because the function will be statically
3778 * hoisted to the top of the script, and the |c = []| will just
3779 * overwrite it at runtime.
3781 if (pn->pn_type == TOK_FUNCTION) {
3782 JS_ASSERT(pn->pn_arity = PN_FUNC);
3783 uint32 index = ALE_INDEX(ale);
3784 globalScope->defs[index].funbox = pn->pn_funbox;
3788 pn->pn_dflags |= PND_GVAR;
3794 BindTopLevelVar(JSContext *cx, BindData *data, JSAtomListElement *ale, JSParseNode *pn,
3795 JSAtom *varname, JSTreeContext *tc)
3797 JS_ASSERT(pn->pn_op == JSOP_NAME);
3798 JS_ASSERT(!tc->inFunction());
3800 /* There's no need to optimize bindings if we're not compiling code. */
3801 if (!tc->compiling())
3805 * Bindings at top level in eval code aren't like bindings at top level in
3806 * regular code, and we must handle them specially.
3808 if (tc->parser->callerFrame) {
3810 * If the eval code is not strict mode code, such bindings are created
3811 * from scratch in the the caller's environment (if the eval is direct)
3812 * or in the global environment (if the eval is indirect) -- and they
3813 * can be deleted. Therefore we can't bind early.
3815 if (!tc->inStrictMode())
3819 * But if the eval code is strict mode code, bindings are added to a
3820 * new environment specifically for that eval code's compilation, and
3821 * they can't be deleted. Thus strict mode eval code does not affect
3822 * the caller's environment, and we can bind such names early. (But
3823 * note: strict mode eval code can still affect the global environment
3824 * by performing an indirect eval of non-strict mode code.)
3826 * However, optimizing such bindings requires either precarious
3827 * type-punning or, ideally, a new kind of Call object specifically for
3828 * strict mode eval frames. Further, strict mode eval is not (yet)
3829 * common. So for now (until we rewrite the scope chain to not use
3830 * objects?) just disable optimizations for top-level names in eval
3836 if (pn->pn_dflags & PND_CONST)
3840 * If this is a global variable, we're compile-and-go, and a global object
3841 * is present, try to bake in either an already available slot or a
3842 * predicted slot that will be defined after compiling is completed.
3844 return DefineGlobal(pn, tc->asCodeGenerator(), pn->pn_atom);
3848 BindFunctionLocal(JSContext *cx, BindData *data, JSAtomListElement *ale, JSParseNode *pn,
3849 JSAtom *name, JSTreeContext *tc)
3851 JS_ASSERT(tc->inFunction());
3853 if (name == cx->runtime->atomState.argumentsAtom) {
3854 pn->pn_op = JSOP_ARGUMENTS;
3855 pn->pn_dflags |= PND_BOUND;
3859 BindingKind kind = tc->bindings.lookup(cx, name, NULL);
3862 * Property not found in current variable scope: we have not seen this
3863 * variable before, so bind a new local variable for it. Any locals
3864 * declared in a with statement body are handled at runtime, by script
3865 * prolog JSOP_DEFVAR opcodes generated for global and heavyweight-
3866 * function-local vars.
3868 kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE;
3870 uintN index = tc->bindings.countVars();
3871 if (!BindLocalVariable(cx, tc, name, kind, false))
3873 pn->pn_op = JSOP_GETLOCAL;
3874 pn->pn_cookie.set(tc->staticLevel, index);
3875 pn->pn_dflags |= PND_BOUND;
3879 if (kind == ARGUMENT) {
3880 JS_ASSERT(tc->inFunction());
3881 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3883 JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
3890 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3892 JSParseNode *pn = data->pn;
3894 /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
3895 pn->pn_op = JSOP_NAME;
3897 if (!CheckStrictBinding(cx, tc, atom, pn))
3900 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3902 if (stmt && stmt->type == STMT_WITH) {
3903 data->fresh = false;
3904 pn->pn_dflags |= PND_DEOPTIMIZED;
3905 tc->noteMightAliasLocals();
3909 JSAtomListElement *ale = tc->decls.lookup(atom);
3913 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3914 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3916 if (dn_kind == JSDefinition::ARG) {
3917 JSAutoByteString name;
3918 if (!js_AtomToPrintableString(cx, atom, &name))
3921 if (op == JSOP_DEFCONST) {
3922 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3923 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3927 if (!ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3928 JSREPORT_WARNING | JSREPORT_STRICT,
3929 JSMSG_VAR_HIDES_ARG, name.ptr())) {
3933 bool error = (op == JSOP_DEFCONST ||
3934 dn_kind == JSDefinition::CONST ||
3935 (dn_kind == JSDefinition::LET &&
3936 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3938 if (cx->hasStrictOption()
3939 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3941 JSAutoByteString name;
3942 if (!js_AtomToPrintableString(cx, atom, &name) ||
3943 !ReportCompileErrorNumber(cx, TS(tc->parser), pn,
3945 ? JSREPORT_WARNING | JSREPORT_STRICT
3947 JSMSG_REDECLARED_VAR,
3948 JSDefinition::kindString(dn_kind),
3957 if (!Define(pn, atom, tc))
3961 * A var declaration never recreates an existing binding, it restates
3962 * it and possibly reinitializes its value. Beware that if pn becomes a
3963 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3964 * const (typically a const would ;-), then pn must be rewritten into a
3965 * TOK_ASSIGN node. See Variables, further below.
3967 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3968 * There the x definition is hoisted but the x = 2 assignment mutates
3969 * the block-local binding of x.
3971 JSDefinition *dn = ALE_DEFN(ale);
3973 data->fresh = false;
3976 /* Make pnu be a fresh name node that uses dn. */
3977 JSParseNode *pnu = pn;
3980 pnu = NameNode::create(atom, tc);
3985 LinkUseToDef(pnu, dn, tc);
3986 pnu->pn_op = JSOP_NAME;
3989 while (dn->kind() == JSDefinition::LET) {
3991 ale = ALE_NEXT(ale);
3992 } while (ale && ALE_ATOM(ale) != atom);
3999 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
4000 dn->kind() == JSDefinition::CONST);
4005 * A var or const that is shadowed by one or more let bindings of the
4006 * same name, but that has not been declared until this point, must be
4007 * hoisted above the let bindings.
4012 ale = tc->lexdeps.rawLookup(atom, hep);
4015 tc->lexdeps.rawRemove(tc->parser, ale, hep);
4017 JSParseNode *pn2 = NameNode::create(atom, tc);
4021 /* The token stream may be past the location for pn. */
4022 pn2->pn_type = TOK_NAME;
4023 pn2->pn_pos = pn->pn_pos;
4026 pn->pn_op = JSOP_NAME;
4029 ale = tc->decls.add(tc->parser, atom, JSAtomList::HOIST);
4032 ALE_SET_DEFN(ale, pn);
4034 pn->pn_dflags &= ~PND_PLACEHOLDER;
4037 if (data->op == JSOP_DEFCONST)
4038 pn->pn_dflags |= PND_CONST;
4040 if (tc->inFunction())
4041 return BindFunctionLocal(cx, data, ale, pn, atom, tc);
4043 return BindTopLevelVar(cx, data, ale, pn, atom, tc);
4047 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
4049 JS_ASSERT(pn->pn_arity == PN_LIST);
4050 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL ||
4051 pn->pn_op == JSOP_FUNCALL || pn->pn_op == JSOP_FUNAPPLY);
4052 if (!ReportStrictModeError(cx, TS(tc->parser), tc, pn, msg))
4055 JSParseNode *pn2 = pn->pn_head;
4056 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
4057 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR, msg);
4060 pn->pn_xflags |= PNX_SETCALL;
4065 NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_ASSIGNED)
4068 JSDefinition *dn = pn->pn_lexdef;
4071 * Save the win of PND_INITIALIZED if we can prove 'var x;' and 'x = y'
4072 * occur as direct kids of the same block with no forward refs to x.
4074 if (!(dn->pn_dflags & (PND_INITIALIZED | PND_CONST | PND_PLACEHOLDER)) &&
4075 dn->isBlockChild() &&
4076 pn->isBlockChild() &&
4077 dn->pn_blockid == pn->pn_blockid &&
4078 dn->pn_pos.end <= pn->pn_pos.begin &&
4079 dn->dn_uses == pn) {
4080 dflag = PND_INITIALIZED;
4083 dn->pn_dflags |= dflag;
4085 if (dn->pn_cookie.isFree() || dn->frameLevel() < tc->staticLevel)
4086 tc->flags |= TCF_FUN_SETS_OUTER_NAME;
4089 pn->pn_dflags |= dflag;
4092 * Both arguments and the enclosing function's name are immutable bindings
4093 * in ES5, so assignments to them must do nothing or throw a TypeError
4094 * depending on code strictness. Assignment to arguments is a syntax error
4095 * in strict mode and thus cannot happen. Outside strict mode, we optimize
4096 * away assignment to the function name. For assignment to function name
4097 * to fail in strict mode, we must have a binding for it in the scope
4098 * chain; we ensure this happens by making such functions heavyweight.
4100 JSAtom *lname = pn->pn_atom;
4101 if (lname == cx->runtime->atomState.argumentsAtom ||
4102 (tc->inFunction() && lname == tc->fun()->atom)) {
4103 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4107 #if JS_HAS_DESTRUCTURING
4110 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
4116 * Destructuring is a form of assignment, so just as for an initialized
4117 * simple variable, we must check for assignment to 'arguments' and flag
4118 * the enclosing function (if any) as heavyweight.
4120 JS_ASSERT(pn->pn_type == TOK_NAME);
4122 if (atom == cx->runtime->atomState.argumentsAtom)
4123 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4126 if (!data->binder(cx, data, atom, tc))
4130 * Select the appropriate name-setting opcode, respecting eager selection
4131 * done by the data->binder function.
4133 if (pn->pn_dflags & PND_BOUND) {
4134 JS_ASSERT(!(pn->pn_dflags & PND_GVAR));
4135 pn->pn_op = (pn->pn_op == JSOP_ARGUMENTS)
4139 pn->pn_op = (data->op == JSOP_DEFCONST)
4144 if (data->op == JSOP_DEFCONST)
4145 pn->pn_dflags |= PND_CONST;
4147 NoteLValue(cx, pn, tc, PND_INITIALIZED);
4152 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
4153 * LHS expression except a destructuring initialiser, and R is on the stack.
4154 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
4155 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
4156 * then push its property name QN. At this point the stack looks like
4158 * [... R, R[P], QB, QN]
4160 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
4161 * its operands with left-hand side above right-hand side:
4163 * [rval, lval, xval]
4165 * and pops all three values, setting lval[xval] = rval. But we cannot select
4166 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
4167 * which can be optimized further. So we select JSOP_SETNAME.
4170 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4172 switch (pn->pn_type) {
4174 NoteLValue(cx, pn, tc);
4180 * We may be called on a name node that has already been specialized,
4181 * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
4182 * case. See bug 558633.
4184 if (!(js_CodeSpec[pn->pn_op].format & JOF_SET))
4185 pn->pn_op = JSOP_SETNAME;
4189 if (!MakeSetCall(cx, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
4193 #if JS_HAS_XML_SUPPORT
4195 if (pn->pn_op == JSOP_XMLNAME) {
4196 pn->pn_op = JSOP_BINDXMLNAME;
4203 ReportCompileErrorNumber(cx, TS(tc->parser), pn,
4204 JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
4211 typedef struct FindPropValData {
4212 uint32 numvars; /* # of destructuring vars in left side */
4213 uint32 maxstep; /* max # of steps searching right side */
4214 JSDHashTable table; /* hash table for O(1) right side search */
4217 typedef struct FindPropValEntry {
4218 JSDHashEntryHdr hdr;
4223 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
4224 JS_ASSERT(((pnkey)->pn_arity == PN_NULLARY && \
4225 ((pnkey)->pn_type == TOK_NUMBER || \
4226 (pnkey)->pn_type == TOK_STRING || \
4227 (pnkey)->pn_type == TOK_NAME)) || \
4228 ((pnkey)->pn_arity == PN_NAME && (pnkey)->pn_type == TOK_NAME))
4230 static JSDHashNumber
4231 HashFindPropValKey(JSDHashTable *table, const void *key)
4233 const JSParseNode *pnkey = (const JSParseNode *)key;
4235 ASSERT_VALID_PROPERTY_KEY(pnkey);
4236 return (pnkey->pn_type == TOK_NUMBER)
4237 ? (JSDHashNumber) JS_HASH_DOUBLE(pnkey->pn_dval)
4238 : ATOM_HASH(pnkey->pn_atom);
4242 MatchFindPropValEntry(JSDHashTable *table,
4243 const JSDHashEntryHdr *entry,
4246 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
4247 const JSParseNode *pnkey = (const JSParseNode *)key;
4249 ASSERT_VALID_PROPERTY_KEY(pnkey);
4250 return pnkey->pn_type == fpve->pnkey->pn_type &&
4251 ((pnkey->pn_type == TOK_NUMBER)
4252 ? pnkey->pn_dval == fpve->pnkey->pn_dval
4253 : pnkey->pn_atom == fpve->pnkey->pn_atom);
4256 static const JSDHashTableOps FindPropValOps = {
4260 MatchFindPropValEntry,
4261 JS_DHashMoveEntryStub,
4262 JS_DHashClearEntryStub,
4263 JS_DHashFinalizeStub,
4267 #define STEP_HASH_THRESHOLD 10
4268 #define BIG_DESTRUCTURING 5
4269 #define BIG_OBJECT_INIT 20
4271 static JSParseNode *
4272 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
4274 FindPropValEntry *entry;
4275 JSParseNode *pnhit, *pnhead, *pnprop, *pnkey;
4278 /* If we have a hash table, use it as the sole source of truth. */
4279 if (data->table.ops) {
4280 entry = (FindPropValEntry *)
4281 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
4282 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
4285 /* If pn is not an object initialiser node, we can't do anything here. */
4286 if (pn->pn_type != TOK_RC)
4290 * We must search all the way through pn's list, to handle the case of an
4291 * id duplicated for two or more property initialisers.
4295 ASSERT_VALID_PROPERTY_KEY(pnid);
4296 pnhead = pn->pn_head;
4297 if (pnid->pn_type == TOK_NUMBER) {
4298 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
4299 JS_ASSERT(pnprop->pn_type == TOK_COLON);
4300 if (pnprop->pn_op == JSOP_NOP) {
4301 pnkey = pnprop->pn_left;
4302 ASSERT_VALID_PROPERTY_KEY(pnkey);
4303 if (pnkey->pn_type == TOK_NUMBER &&
4304 pnkey->pn_dval == pnid->pn_dval) {
4311 for (pnprop = pnhead; pnprop; pnprop = pnprop->pn_next) {
4312 JS_ASSERT(pnprop->pn_type == TOK_COLON);
4313 if (pnprop->pn_op == JSOP_NOP) {
4314 pnkey = pnprop->pn_left;
4315 ASSERT_VALID_PROPERTY_KEY(pnkey);
4316 if (pnkey->pn_type == pnid->pn_type &&
4317 pnkey->pn_atom == pnid->pn_atom) {
4327 /* Hit via full search -- see whether it's time to create the hash table. */
4328 JS_ASSERT(!data->table.ops);
4329 if (step > data->maxstep) {
4330 data->maxstep = step;
4331 if (step >= STEP_HASH_THRESHOLD &&
4332 data->numvars >= BIG_DESTRUCTURING &&
4333 pn->pn_count >= BIG_OBJECT_INIT &&
4334 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
4335 sizeof(FindPropValEntry),
4336 JS_DHASH_DEFAULT_CAPACITY(pn->pn_count)))
4338 for (pn = pnhead; pn; pn = pn->pn_next) {
4339 JS_ASSERT(pnprop->pn_type == TOK_COLON);
4340 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
4341 entry = (FindPropValEntry *)
4342 JS_DHashTableOperate(&data->table, pn->pn_left,
4344 entry->pnval = pn->pn_right;
4348 return pnhit->pn_right;
4352 * Destructuring patterns can appear in two kinds of contexts:
4354 * - assignment-like: assignment expressions and |for| loop heads. In
4355 * these cases, the patterns' property value positions can be
4356 * arbitrary lvalue expressions; the destructuring is just a fancy
4359 * - declaration-like: |var| and |let| declarations, functions' formal
4360 * parameter lists, |catch| clauses, and comprehension tails. In
4361 * these cases, the patterns' property value positions must be
4362 * simple names; the destructuring defines them as new variables.
4364 * In both cases, other code parses the pattern as an arbitrary
4365 * primaryExpr, and then, here in CheckDestructuring, verify that the
4366 * tree is a valid destructuring expression.
4368 * In assignment-like contexts, we parse the pattern with the
4369 * TCF_DECL_DESTRUCTURING flag clear, so the lvalue expressions in the
4370 * pattern are parsed normally. primaryExpr links variable references
4371 * into the appropriate use chains; creates placeholder definitions;
4372 * and so on. CheckDestructuring is called with |data| NULL (since we
4373 * won't be binding any new names), and we specialize lvalues as
4374 * appropriate. If right is NULL, we just check for well-formed lvalues.
4376 * In declaration-like contexts, the normal variable reference
4377 * processing would just be an obstruction, because we're going to
4378 * define the names that appear in the property value positions as new
4379 * variables anyway. In this case, we parse the pattern with
4380 * TCF_DECL_DESTRUCTURING set, which directs primaryExpr to leave
4381 * whatever name nodes it creates unconnected. Then, here in
4382 * CheckDestructuring, we require the pattern's property value
4383 * positions to be simple names, and define them as appropriate to the
4384 * context. For these calls, |data| points to the right sort of
4387 * See also UndominateInitializers, immediately below. If you change
4388 * either of these functions, you might have to change the other to
4392 CheckDestructuring(JSContext *cx, BindData *data,
4393 JSParseNode *left, JSParseNode *right,
4397 FindPropValData fpvd;
4398 JSParseNode *lhs, *rhs, *pn, *pn2;
4400 if (left->pn_type == TOK_ARRAYCOMP) {
4401 ReportCompileErrorNumber(cx, TS(tc->parser), left, JSREPORT_ERROR,
4402 JSMSG_ARRAY_COMP_LEFTSIDE);
4406 #if JS_HAS_DESTRUCTURING_SHORTHAND
4407 if (right && right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
4408 ReportCompileErrorNumber(cx, TS(tc->parser), right, JSREPORT_ERROR,
4409 JSMSG_BAD_OBJECT_INIT);
4414 fpvd.table.ops = NULL;
4415 lhs = left->pn_head;
4416 if (left->pn_type == TOK_RB) {
4417 rhs = (right && right->pn_type == left->pn_type)
4422 pn = lhs, pn2 = rhs;
4424 /* Nullary comma is an elision; binary comma is an expression.*/
4425 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
4426 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4427 ok = CheckDestructuring(cx, data, pn, pn2, tc);
4430 if (pn->pn_type != TOK_NAME)
4433 ok = BindDestructuringVar(cx, data, pn, tc);
4435 ok = BindDestructuringLHS(cx, pn, tc);
4447 JS_ASSERT(left->pn_type == TOK_RC);
4448 fpvd.numvars = left->pn_count;
4453 JS_ASSERT(lhs->pn_type == TOK_COLON);
4456 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4458 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4459 ok = CheckDestructuring(cx, data, pn, rhs, tc);
4461 if (pn->pn_type != TOK_NAME)
4464 ok = BindDestructuringVar(cx, data, pn, tc);
4466 ok = BindDestructuringLHS(cx, pn, tc);
4476 * The catch/finally handler implementation in the interpreter assumes
4477 * that any operation that introduces a new scope (like a "let" or "with"
4478 * block) increases the stack depth. This way, it is possible to restore
4479 * the scope chain based on stack depth of the handler alone. "let" with
4480 * an empty destructuring pattern like in
4484 * would violate this assumption as the there would be no let locals to
4485 * store on the stack. To satisfy it we add an empty property to such
4486 * blocks so that OBJ_BLOCK_COUNT(cx, blockObj), which gives the number of
4487 * slots, would be always positive.
4489 * Note that we add such a property even if the block has locals due to
4490 * later let declarations in it. We optimize for code simplicity here,
4491 * not the fastest runtime performance with empty [] or {}.
4494 data->binder == BindLet &&
4495 OBJ_BLOCK_COUNT(cx, tc->blockChain()) == 0) {
4496 ok = !!js_DefineNativeProperty(cx, tc->blockChain(),
4497 ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
4498 UndefinedValue(), NULL, NULL,
4499 JSPROP_ENUMERATE | JSPROP_PERMANENT,
4500 Shape::HAS_SHORTID, 0, NULL);
4509 JS_DHashTableFinish(&fpvd.table);
4513 ReportCompileErrorNumber(cx, TS(tc->parser), pn, JSREPORT_ERROR,
4514 JSMSG_NO_VARIABLE_NAME);
4520 * This is a greatly pared down version of CheckDestructuring that extends the
4521 * pn_pos.end source coordinate of each name in a destructuring binding such as
4523 * var [x, y] = [function () y, 42];
4525 * to cover its corresponding initializer, so that the initialized binding does
4526 * not appear to dominate any closures in its initializer. See bug 496134.
4528 * The quick-and-dirty dominance computation in Parser::setFunctionKinds is not
4529 * very precise. With one-pass SSA construction from structured source code
4530 * (see "Single-Pass Generation of Static Single Assignment Form for Structured
4531 * Languages", Brandis and Mössenböck), we could do much better.
4533 * See CheckDestructuring, immediately above. If you change either of these
4534 * functions, you might have to change the other to match.
4537 UndominateInitializers(JSParseNode *left, JSParseNode *right, JSTreeContext *tc)
4539 FindPropValData fpvd;
4540 JSParseNode *lhs, *rhs;
4542 JS_ASSERT(left->pn_type != TOK_ARRAYCOMP);
4545 #if JS_HAS_DESTRUCTURING_SHORTHAND
4546 if (right->pn_arity == PN_LIST && (right->pn_xflags & PNX_DESTRUCT)) {
4547 ReportCompileErrorNumber(tc->parser->context, TS(tc->parser), right, JSREPORT_ERROR,
4548 JSMSG_BAD_OBJECT_INIT);
4553 if (right->pn_type != left->pn_type)
4556 fpvd.table.ops = NULL;
4557 lhs = left->pn_head;
4558 if (left->pn_type == TOK_RB) {
4559 rhs = right->pn_head;
4561 while (lhs && rhs) {
4562 /* Nullary comma is an elision; binary comma is an expression.*/
4563 if (lhs->pn_type != TOK_COMMA || lhs->pn_arity != PN_NULLARY) {
4564 if (lhs->pn_type == TOK_RB || lhs->pn_type == TOK_RC) {
4565 if (!UndominateInitializers(lhs, rhs, tc))
4568 lhs->pn_pos.end = rhs->pn_pos.end;
4576 JS_ASSERT(left->pn_type == TOK_RC);
4577 fpvd.numvars = left->pn_count;
4581 JS_ASSERT(lhs->pn_type == TOK_COLON);
4582 JSParseNode *pn = lhs->pn_right;
4584 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
4585 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
4586 if (rhs && !UndominateInitializers(pn, rhs, tc))
4590 pn->pn_pos.end = rhs->pn_pos.end;
4600 Parser::destructuringExpr(BindData *data, TokenKind tt)
4604 tc->flags |= TCF_DECL_DESTRUCTURING;
4605 pn = primaryExpr(tt, JS_FALSE);
4606 tc->flags &= ~TCF_DECL_DESTRUCTURING;
4609 if (!CheckDestructuring(context, data, pn, NULL, tc))
4615 * Currently used only #if JS_HAS_DESTRUCTURING, in Statement's TOK_FOR case.
4616 * This function assumes the cloned tree is for use in the same statement and
4617 * binding context as the original tree.
4619 static JSParseNode *
4620 CloneParseTree(JSParseNode *opn, JSTreeContext *tc)
4622 JSParseNode *pn, *pn2, *opn2;
4624 pn = NewOrRecycledNode(tc);
4627 pn->pn_type = opn->pn_type;
4628 pn->pn_pos = opn->pn_pos;
4629 pn->pn_op = opn->pn_op;
4630 pn->pn_used = opn->pn_used;
4631 pn->pn_defn = opn->pn_defn;
4632 pn->pn_arity = opn->pn_arity;
4633 pn->pn_parens = opn->pn_parens;
4635 switch (pn->pn_arity) {
4636 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
4639 NULLCHECK(pn->pn_funbox =
4640 tc->parser->newFunctionBox(opn->pn_funbox->object, pn, tc));
4641 NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, tc));
4642 pn->pn_cookie = opn->pn_cookie;
4643 pn->pn_dflags = opn->pn_dflags;
4644 pn->pn_blockid = opn->pn_blockid;
4649 for (opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
4650 NULLCHECK(pn2 = CloneParseTree(opn2, tc));
4653 pn->pn_xflags = opn->pn_xflags;
4657 NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, tc));
4658 NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, tc));
4659 NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, tc));
4663 NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, tc));
4664 if (opn->pn_right != opn->pn_left)
4665 NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, tc));
4667 pn->pn_right = pn->pn_left;
4668 pn->pn_pval = opn->pn_pval;
4669 pn->pn_iflags = opn->pn_iflags;
4673 NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc));
4674 pn->pn_num = opn->pn_num;
4675 pn->pn_hidden = opn->pn_hidden;
4679 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
4680 pn->pn_u = opn->pn_u;
4683 * The old name is a use of its pn_lexdef. Make the clone also be a
4684 * use of that definition.
4686 JSDefinition *dn = pn->pn_lexdef;
4688 pn->pn_link = dn->dn_uses;
4690 } else if (opn->pn_expr) {
4691 NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, tc));
4694 * If the old name is a definition, the new one has pn_defn set.
4695 * Make the old name a use of the new node.
4698 opn->pn_defn = false;
4699 LinkUseToDef(opn, (JSDefinition *) pn, tc);
4705 pn->pn_names = opn->pn_names;
4706 NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, tc));
4710 // Even PN_NULLARY may have data (apair for E4X -- what a botch).
4711 pn->pn_u = opn->pn_u;
4719 #endif /* JS_HAS_DESTRUCTURING */
4721 extern const char js_with_statement_str[];
4723 static JSParseNode *
4724 ContainsStmt(JSParseNode *pn, TokenKind tt)
4726 JSParseNode *pn2, *pnt;
4730 if (PN_TYPE(pn) == tt)
4732 switch (pn->pn_arity) {
4734 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4735 pnt = ContainsStmt(pn2, tt);
4741 pnt = ContainsStmt(pn->pn_kid1, tt);
4744 pnt = ContainsStmt(pn->pn_kid2, tt);
4747 return ContainsStmt(pn->pn_kid3, tt);
4750 * Limit recursion if pn is a binary expression, which can't contain a
4753 if (pn->pn_op != JSOP_NOP)
4755 pnt = ContainsStmt(pn->pn_left, tt);
4758 return ContainsStmt(pn->pn_right, tt);
4760 if (pn->pn_op != JSOP_NOP)
4762 return ContainsStmt(pn->pn_kid, tt);
4764 return ContainsStmt(pn->maybeExpr(), tt);
4766 return ContainsStmt(pn->pn_tree, tt);
4773 Parser::returnOrYield(bool useAssignExpr)
4776 JSParseNode *pn, *pn2;
4778 tt = tokenStream.currentToken().type;
4779 if (tt == TOK_RETURN && !tc->inFunction()) {
4780 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
4784 pn = UnaryNode::create(tc);
4788 #if JS_HAS_GENERATORS
4789 if (tt == TOK_YIELD)
4790 tc->flags |= TCF_FUN_IS_GENERATOR;
4793 /* This is ugly, but we don't want to require a semicolon. */
4794 tt2 = tokenStream.peekTokenSameLine(TSF_OPERAND);
4795 if (tt2 == TOK_ERROR)
4798 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
4799 #if JS_HAS_GENERATORS
4800 && (tt != TOK_YIELD ||
4801 (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP &&
4802 tt2 != TOK_COLON && tt2 != TOK_COMMA))
4805 pn2 = useAssignExpr ? assignExpr() : expr();
4808 #if JS_HAS_GENERATORS
4809 if (tt == TOK_RETURN)
4811 tc->flags |= TCF_RETURN_EXPR;
4812 pn->pn_pos.end = pn2->pn_pos.end;
4815 #if JS_HAS_GENERATORS
4816 if (tt == TOK_RETURN)
4818 tc->flags |= TCF_RETURN_VOID;
4821 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
4822 /* As in Python (see PEP-255), disallow return v; in generators. */
4823 ReportBadReturn(context, tc, JSREPORT_ERROR,
4824 JSMSG_BAD_GENERATOR_RETURN,
4825 JSMSG_BAD_ANON_GENERATOR_RETURN);
4829 if (context->hasStrictOption() &&
4830 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
4831 !ReportBadReturn(context, tc, JSREPORT_WARNING | JSREPORT_STRICT,
4832 JSMSG_NO_RETURN_VALUE,
4833 JSMSG_ANON_NO_RETURN_VALUE)) {
4840 static JSParseNode *
4841 PushLexicalScope(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
4846 JSObjectBox *blockbox;
4848 pn = LexicalScopeNode::create(tc);
4852 obj = js_NewBlockObject(cx);
4856 blockbox = tc->parser->newObjectBox(obj);
4860 js_PushBlockScope(tc, stmt, blockbox, -1);
4861 pn->pn_type = TOK_LEXICALSCOPE;
4862 pn->pn_op = JSOP_LEAVEBLOCK;
4863 pn->pn_objbox = blockbox;
4864 pn->pn_cookie.makeFree();
4866 if (!GenerateBlockId(tc, stmt->blockid))
4868 pn->pn_blockid = stmt->blockid;
4872 #if JS_HAS_BLOCK_SCOPE
4875 Parser::letBlock(JSBool statement)
4877 JSParseNode *pn, *pnblock, *pnlet;
4878 JSStmtInfo stmtInfo;
4880 JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
4882 /* Create the let binary node. */
4883 pnlet = BinaryNode::create(tc);
4887 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
4889 /* This is a let block or expression of the form: let (a, b, c) .... */
4890 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
4894 pn->pn_expr = pnlet;
4896 pnlet->pn_left = variables(true);
4897 if (!pnlet->pn_left)
4899 pnlet->pn_left->pn_xflags = PNX_POPVAR;
4901 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
4903 if (statement && !tokenStream.matchToken(TOK_LC, TSF_OPERAND)) {
4905 * Strict mode eliminates a grammar ambiguity with unparenthesized
4906 * LetExpressions in an ExpressionStatement. If followed immediately
4907 * by an arguments list, it's ambiguous whether the let expression
4908 * is the callee or the call is inside the let expression body.
4912 if (!ReportStrictModeError(context, &tokenStream, tc, pnlet,
4913 JSMSG_STRICT_CODE_LET_EXPR_STMT)) {
4918 * If this is really an expression in let statement guise, then we
4919 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
4920 * the return value of the expression.
4922 pn = UnaryNode::create(tc);
4925 pn->pn_type = TOK_SEMI;
4927 pn->pn_kid = pnblock;
4929 statement = JS_FALSE;
4933 pnlet->pn_right = statements();
4934 if (!pnlet->pn_right)
4936 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
4939 * Change pnblock's opcode to the variant that propagates the last
4940 * result down after popping the block, and clear statement.
4942 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
4943 pnlet->pn_right = assignExpr();
4944 if (!pnlet->pn_right)
4952 #endif /* JS_HAS_BLOCK_SCOPE */
4955 PushBlocklikeStatement(JSStmtInfo *stmt, JSStmtType type, JSTreeContext *tc)
4957 js_PushStatement(tc, stmt, type, -1);
4958 return GenerateBlockId(tc, stmt->blockid);
4961 static JSParseNode *
4962 NewBindingNode(JSAtom *atom, JSTreeContext *tc, bool let = false)
4964 JSParseNode *pn = NULL;
4966 JSAtomListElement *ale = tc->decls.lookup(atom);
4969 JS_ASSERT(!pn->isPlaceholder());
4971 ale = tc->lexdeps.lookup(atom);
4974 JS_ASSERT(pn->isPlaceholder());
4979 JS_ASSERT(pn->pn_defn);
4982 * A let binding at top level becomes a var before we get here, so if
4983 * pn and tc have the same blockid then that id must not be the bodyid.
4984 * If pn is a forward placeholder definition from the same or a higher
4985 * block then we claim it.
4987 JS_ASSERT_IF(let && pn->pn_blockid == tc->blockid(),
4988 pn->pn_blockid != tc->bodyid);
4990 if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
4992 pn->pn_blockid = tc->blockid();
4994 tc->lexdeps.remove(tc->parser, atom);
4999 /* Make a new node for this declarator name (or destructuring pattern). */
5000 pn = NameNode::create(atom, tc);
5006 #if JS_HAS_BLOCK_SCOPE
5008 RebindLets(JSParseNode *pn, JSTreeContext *tc)
5013 switch (pn->pn_arity) {
5015 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
5016 RebindLets(pn2, tc);
5020 RebindLets(pn->pn_kid1, tc);
5021 RebindLets(pn->pn_kid2, tc);
5022 RebindLets(pn->pn_kid3, tc);
5026 RebindLets(pn->pn_left, tc);
5027 RebindLets(pn->pn_right, tc);
5031 RebindLets(pn->pn_kid, tc);
5035 RebindLets(pn->pn_body, tc);
5039 RebindLets(pn->maybeExpr(), tc);
5042 JS_ASSERT(pn->pn_blockid > tc->topStmt->blockid);
5043 } else if (pn->pn_used) {
5044 if (pn->pn_lexdef->pn_blockid == tc->topStmt->blockid) {
5047 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
5049 while ((ale = ALE_NEXT(ale)) != NULL) {
5050 if (ALE_ATOM(ale) == pn->pn_atom) {
5051 LinkUseToDef(pn, ALE_DEFN(ale), tc);
5057 ale = tc->lexdeps.lookup(pn->pn_atom);
5059 ale = MakePlaceholder(pn, tc);
5063 LinkUseToDef(pn, ALE_DEFN(ale), tc);
5069 RebindLets(pn->pn_tree, tc);
5075 #endif /* JS_HAS_BLOCK_SCOPE */
5078 Parser::switchStatement()
5080 JSParseNode *pn5, *saveBlock;
5081 JSBool seenDefault = JS_FALSE;
5083 JSParseNode *pn = BinaryNode::create(tc);
5086 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
5088 /* pn1 points to the switch's discriminant. */
5089 JSParseNode *pn1 = parenExpr();
5093 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
5094 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
5097 * NB: we must push stmtInfo before calling GenerateBlockIdForStmtNode
5098 * because that function states tc->topStmt->blockid.
5100 JSStmtInfo stmtInfo;
5101 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
5103 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
5104 JSParseNode *pn2 = ListNode::create(tc);
5108 if (!GenerateBlockIdForStmtNode(pn2, tc))
5110 saveBlock = tc->blockNode;
5111 tc->blockNode = pn2;
5114 while ((tt = tokenStream.getToken()) != TOK_RC) {
5119 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_DEFAULTS);
5122 seenDefault = JS_TRUE;
5127 pn3 = BinaryNode::create(tc);
5130 if (tt == TOK_CASE) {
5131 pn3->pn_left = expr();
5136 if (pn2->pn_count == JS_BIT(16)) {
5137 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_CASES);
5147 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_SWITCH);
5150 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
5152 JSParseNode *pn4 = ListNode::create(tc);
5155 pn4->pn_type = TOK_LC;
5157 while ((tt = tokenStream.peekToken(TSF_OPERAND)) != TOK_RC &&
5158 tt != TOK_CASE && tt != TOK_DEFAULT) {
5159 if (tt == TOK_ERROR)
5164 pn4->pn_pos.end = pn5->pn_pos.end;
5168 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
5170 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
5171 pn3->pn_pos.end = pn4->pn_pos.end;
5172 pn3->pn_right = pn4;
5176 * Handle the case where there was a let declaration in any case in
5177 * the switch body, but not within an inner block. If it replaced
5178 * tc->blockNode with a new block node then we must refresh pn2 and
5179 * then restore tc->blockNode.
5181 if (tc->blockNode != pn2)
5182 pn2 = tc->blockNode;
5183 tc->blockNode = saveBlock;
5186 pn->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
5193 Parser::forStatement()
5195 JSParseNode *pnseq = NULL;
5196 #if JS_HAS_BLOCK_SCOPE
5197 JSParseNode *pnlet = NULL;
5198 JSStmtInfo blockInfo;
5201 /* A FOR node is binary, left is loop control and right is the body. */
5202 JSParseNode *pn = BinaryNode::create(tc);
5205 JSStmtInfo stmtInfo;
5206 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
5208 pn->pn_op = JSOP_ITER;
5210 if (tokenStream.matchToken(TOK_NAME)) {
5211 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
5212 pn->pn_iflags = JSITER_FOREACH;
5214 tokenStream.ungetToken();
5217 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
5218 TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
5220 #if JS_HAS_BLOCK_SCOPE
5225 if (tt == TOK_SEMI) {
5226 if (pn->pn_iflags & JSITER_FOREACH)
5229 /* No initializer -- set first kid of left sub-node to null. */
5233 * Set pn1 to a var list or an initializing expression.
5235 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
5236 * of the for statement. This flag will be used by the RelExpr
5237 * production; if it is set, then the 'in' keyword will not be
5238 * recognized as an operator, leaving it available to be parsed as
5239 * part of a for/in loop.
5241 * A side effect of this restriction is that (unparenthesized)
5242 * expressions involving an 'in' operator are illegal in the init
5243 * clause of an ordinary for loop.
5245 tc->flags |= TCF_IN_FOR_INIT;
5246 if (tt == TOK_VAR) {
5247 (void) tokenStream.getToken();
5248 pn1 = variables(false);
5249 #if JS_HAS_BLOCK_SCOPE
5250 } else if (tt == TOK_LET) {
5252 (void) tokenStream.getToken();
5253 if (tokenStream.peekToken() == TOK_LP) {
5254 pn1 = letBlock(JS_FALSE);
5255 tt = TOK_LEXICALSCOPE;
5257 pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo);
5260 blockInfo.flags |= SIF_FOR_BLOCK;
5261 pn1 = variables(false);
5267 tc->flags &= ~TCF_IN_FOR_INIT;
5273 * We can be sure that it's a for/in loop if there's still an 'in'
5274 * keyword here, even if JavaScript recognizes 'in' as an operator,
5275 * as we've excluded 'in' from being parsed in RelExpr by setting
5276 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
5278 if (pn1 && tokenStream.matchToken(TOK_IN)) {
5279 pn->pn_iflags |= JSITER_ENUMERATE;
5280 stmtInfo.type = STMT_FOR_IN_LOOP;
5282 /* Check that the left side of the 'in' is valid. */
5283 JS_ASSERT(!TokenKindIsDecl(tt) || PN_TYPE(pn1) == tt);
5284 if (TokenKindIsDecl(tt)
5285 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
5286 #if JS_HAS_DESTRUCTURING
5287 || (versionNumber() == JSVERSION_1_7 &&
5288 pn->pn_op == JSOP_ITER &&
5289 !(pn->pn_iflags & JSITER_FOREACH) &&
5290 (pn1->pn_head->pn_type == TOK_RC ||
5291 (pn1->pn_head->pn_type == TOK_RB &&
5292 pn1->pn_head->pn_count != 2) ||
5293 (pn1->pn_head->pn_type == TOK_ASSIGN &&
5294 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
5295 pn1->pn_head->pn_left->pn_count != 2))))
5298 : (pn1->pn_type != TOK_NAME &&
5299 pn1->pn_type != TOK_DOT &&
5300 #if JS_HAS_DESTRUCTURING
5301 ((versionNumber() == JSVERSION_1_7 &&
5302 pn->pn_op == JSOP_ITER &&
5303 !(pn->pn_iflags & JSITER_FOREACH))
5304 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
5305 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
5307 pn1->pn_type != TOK_LP &&
5308 #if JS_HAS_XML_SUPPORT
5309 (pn1->pn_type != TOK_UNARYOP ||
5310 pn1->pn_op != JSOP_XMLNAME) &&
5312 pn1->pn_type != TOK_LB)) {
5313 reportErrorNumber(pn1, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
5317 /* pn2 points to the name or destructuring pattern on in's left. */
5318 JSParseNode *pn2 = NULL;
5319 uintN dflag = PND_ASSIGNED;
5321 if (TokenKindIsDecl(tt)) {
5322 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
5323 pn1->pn_xflags |= PNX_FORINVAR;
5326 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
5327 * 'const' to hoist the initializer or the entire decl out of
5328 * the loop head. TOK_VAR is the type for both 'var' and 'const'.
5331 if ((pn2->pn_type == TOK_NAME && pn2->maybeExpr())
5332 #if JS_HAS_DESTRUCTURING
5333 || pn2->pn_type == TOK_ASSIGN
5336 #if JS_HAS_BLOCK_SCOPE
5337 if (tt == TOK_LET) {
5338 reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT);
5341 #endif /* JS_HAS_BLOCK_SCOPE */
5343 pnseq = ListNode::create(tc);
5346 pnseq->pn_type = TOK_SEQ;
5347 pnseq->pn_pos.begin = pn->pn_pos.begin;
5349 dflag = PND_INITIALIZED;
5352 * All of 'var x = i' is hoisted above 'for (x in o)',
5353 * so clear PNX_FORINVAR.
5355 * Request JSOP_POP here since the var is for a simple
5356 * name (it is not a destructuring binding's left-hand
5357 * side) and it has an initializer.
5359 pn1->pn_xflags &= ~PNX_FORINVAR;
5360 pn1->pn_xflags |= PNX_POPVAR;
5361 pnseq->initList(pn1);
5363 #if JS_HAS_DESTRUCTURING
5364 if (pn2->pn_type == TOK_ASSIGN) {
5365 pn1 = CloneParseTree(pn2->pn_left, tc);
5371 JS_ASSERT(pn2->pn_type == TOK_NAME);
5372 pn1 = NameNode::create(pn2->pn_atom, tc);
5375 pn1->pn_type = TOK_NAME;
5376 pn1->pn_op = JSOP_NAME;
5377 pn1->pn_pos = pn2->pn_pos;
5379 LinkUseToDef(pn1, (JSDefinition *) pn2, tc);
5387 if (pn2->pn_type == TOK_LP &&
5388 !MakeSetCall(context, pn2, tc, JSMSG_BAD_LEFTSIDE_OF_ASS)) {
5391 #if JS_HAS_XML_SUPPORT
5392 if (pn2->pn_type == TOK_UNARYOP)
5393 pn2->pn_op = JSOP_BINDXMLNAME;
5397 switch (pn2->pn_type) {
5399 /* Beware 'for (arguments in ...)' with or without a 'var'. */
5400 NoteLValue(context, pn2, tc, dflag);
5403 #if JS_HAS_DESTRUCTURING
5406 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
5410 /* Check for valid lvalues in var-less destructuring for-in. */
5411 if (pn1 == pn2 && !CheckDestructuring(context, NULL, pn2, NULL, tc))
5414 if (versionNumber() == JSVERSION_1_7) {
5416 * Destructuring for-in requires [key, value] enumeration
5419 JS_ASSERT(pn->pn_op == JSOP_ITER);
5420 if (!(pn->pn_iflags & JSITER_FOREACH))
5421 pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
5430 * Parse the object expression as the right operand of 'in', first
5431 * removing the top statement from the statement-stack if this is a
5432 * 'for (let x in y)' loop.
5434 #if JS_HAS_BLOCK_SCOPE
5435 JSStmtInfo *save = tc->topStmt;
5437 tc->topStmt = save->down;
5440 #if JS_HAS_BLOCK_SCOPE
5445 pn2 = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn1, pn2, tc);
5450 if (pn->pn_iflags & JSITER_FOREACH)
5452 pn->pn_op = JSOP_NOP;
5454 /* Parse the loop condition or null into pn2. */
5455 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
5456 tt = tokenStream.peekToken(TSF_OPERAND);
5458 if (tt == TOK_SEMI) {
5466 /* Parse the update expression or null into pn3. */
5467 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
5468 tt = tokenStream.peekToken(TSF_OPERAND);
5478 /* Build the FORHEAD node to use as the left kid of pn. */
5479 JSParseNode *pn4 = TernaryNode::create(tc);
5482 pn4->pn_type = TOK_FORHEAD;
5483 pn4->pn_op = JSOP_NOP;
5490 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5492 /* Parse the loop body into pn->pn_right. */
5499 /* Record the absolute line number for source note emission. */
5500 pn->pn_pos.end = pn2->pn_pos.end;
5502 #if JS_HAS_BLOCK_SCOPE
5505 pnlet->pn_expr = pn;
5510 pnseq->pn_pos.end = pn->pn_pos.end;
5518 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP);
5523 Parser::tryStatement()
5525 JSParseNode *catchList, *lastCatch;
5528 * try nodes are ternary.
5529 * kid1 is the try statement
5530 * kid2 is the catch node list or null
5531 * kid3 is the finally statement
5533 * catch nodes are ternary.
5534 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
5535 * kid2 is the catch guard or null if no guard
5536 * kid3 is the catch block
5538 * catch lvalue nodes are either:
5539 * TOK_NAME for a single identifier
5540 * TOK_RB or TOK_RC for a destructuring left-hand side
5542 * finally nodes are TOK_LC statement lists.
5544 JSParseNode *pn = TernaryNode::create(tc);
5547 pn->pn_op = JSOP_NOP;
5549 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
5550 JSStmtInfo stmtInfo;
5551 if (!PushBlocklikeStatement(&stmtInfo, STMT_TRY, tc))
5553 pn->pn_kid1 = statements();
5556 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
5560 TokenKind tt = tokenStream.getToken();
5561 if (tt == TOK_CATCH) {
5562 catchList = ListNode::create(tc);
5565 catchList->pn_type = TOK_RESERVED;
5566 catchList->makeEmpty();
5570 JSParseNode *pnblock;
5573 /* Check for another catch after unconditional catch. */
5574 if (lastCatch && !lastCatch->pn_kid2) {
5575 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_AFTER_GENERAL);
5580 * Create a lexical scope node around the whole catch clause,
5581 * including the head.
5583 pnblock = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
5586 stmtInfo.type = STMT_CATCH;
5589 * Legal catch forms are:
5591 * catch (lhs if <boolean_expression>)
5592 * where lhs is a name or a destructuring left-hand side.
5593 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
5595 JSParseNode *pn2 = TernaryNode::create(tc);
5598 pnblock->pn_expr = pn2;
5599 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
5602 * Contrary to ECMA Ed. 3, the catch variable is lexically
5603 * scoped, not a property of a new Object instance. This is
5604 * an intentional change that anticipates ECMA Ed. 4.
5608 data.binder = BindLet;
5609 data.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
5611 tt = tokenStream.getToken();
5614 #if JS_HAS_DESTRUCTURING
5617 pn3 = destructuringExpr(&data, tt);
5625 JSAtom *label = tokenStream.currentToken().t_atom;
5626 pn3 = NewBindingNode(label, tc, true);
5630 if (!data.binder(context, &data, label, tc))
5636 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_IDENTIFIER);
5641 #if JS_HAS_CATCH_GUARD
5643 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
5644 * to avoid conflicting with the JS2/ECMAv4 type annotation
5645 * catchguard syntax.
5647 if (tokenStream.matchToken(TOK_IF)) {
5648 pn2->pn_kid2 = expr();
5653 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
5655 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
5656 pn2->pn_kid3 = statements();
5659 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
5662 catchList->append(pnblock);
5664 tt = tokenStream.getToken(TSF_OPERAND);
5665 } while (tt == TOK_CATCH);
5667 pn->pn_kid2 = catchList;
5669 if (tt == TOK_FINALLY) {
5670 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
5671 if (!PushBlocklikeStatement(&stmtInfo, STMT_FINALLY, tc))
5673 pn->pn_kid3 = statements();
5676 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
5679 tokenStream.ungetToken();
5681 if (!catchList && !pn->pn_kid3) {
5682 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_OR_FINALLY);
5689 Parser::withStatement()
5692 * In most cases, we want the constructs forbidden in strict mode
5693 * code to be a subset of those that JSOPTION_STRICT warns about, and
5694 * we should use ReportStrictModeError. However, 'with' is the sole
5695 * instance of a construct that is forbidden in strict mode code, but
5696 * doesn't even merit a warning under JSOPTION_STRICT. See
5697 * https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
5699 if (tc->flags & TCF_STRICT_MODE_CODE) {
5700 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_STRICT_CODE_WITH);
5704 JSParseNode *pn = BinaryNode::create(tc);
5707 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
5708 JSParseNode *pn2 = parenExpr();
5711 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
5714 JSParseNode *oldWith = tc->innermostWith;
5715 tc->innermostWith = pn;
5717 JSStmtInfo stmtInfo;
5718 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
5724 pn->pn_pos.end = pn2->pn_pos.end;
5726 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5727 tc->innermostWith = oldWith;
5730 * Make sure to deoptimize lexical dependencies inside the |with|
5731 * to safely optimize binding globals (see bug 561923).
5733 JSAtomListIterator iter(&tc->lexdeps);
5734 while (JSAtomListElement *ale = iter()) {
5735 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
5736 DeoptimizeUsesWithin(lexdep, pn->pn_pos);
5742 #if JS_HAS_BLOCK_SCOPE
5744 Parser::letStatement()
5746 JSObjectBox *blockbox;
5750 /* Check for a let statement or let expression. */
5751 if (tokenStream.peekToken() == TOK_LP) {
5752 pn = letBlock(JS_TRUE);
5753 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
5756 /* Let expressions require automatic semicolon insertion. */
5757 JS_ASSERT(pn->pn_type == TOK_SEMI ||
5758 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
5763 * This is a let declaration. We must be directly under a block per
5764 * the proposed ES4 specs, but not an implicit block created due to
5765 * 'for (let ...)'. If we pass this error test, make the enclosing
5766 * JSStmtInfo be our scope. Further let declarations in this block
5767 * will find this scope statement and use the same block object.
5769 * If we are the first let declaration in this block (i.e., when the
5770 * enclosing maybe-scope JSStmtInfo isn't yet a scope statement) then
5771 * we also need to set tc->blockNode to be our TOK_LEXICALSCOPE.
5773 JSStmtInfo *stmt = tc->topStmt;
5775 (!STMT_MAYBE_SCOPE(stmt) || (stmt->flags & SIF_FOR_BLOCK))) {
5776 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LET_DECL_NOT_IN_BLOCK);
5780 if (stmt && (stmt->flags & SIF_SCOPE)) {
5781 JS_ASSERT(tc->blockChainBox == stmt->blockBox);
5783 if (!stmt || (stmt->flags & SIF_BODY_BLOCK)) {
5785 * ES4 specifies that let at top level and at body-block scope
5786 * does not shadow var, so convert back to var.
5788 tokenStream.mungeCurrentToken(TOK_VAR, JSOP_DEFVAR);
5790 pn = variables(false);
5793 pn->pn_xflags |= PNX_POPVAR;
5798 * Some obvious assertions here, but they may help clarify the
5799 * situation. This stmt is not yet a scope, so it must not be a
5800 * catch block (catch is a lexical scope by definition).
5802 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
5803 JS_ASSERT(stmt != tc->topScopeStmt);
5804 JS_ASSERT(stmt->type == STMT_BLOCK ||
5805 stmt->type == STMT_SWITCH ||
5806 stmt->type == STMT_TRY ||
5807 stmt->type == STMT_FINALLY);
5808 JS_ASSERT(!stmt->downScope);
5810 /* Convert the block statement into a scope statement. */
5811 JSObject *obj = js_NewBlockObject(tc->parser->context);
5815 blockbox = tc->parser->newObjectBox(obj);
5820 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
5821 * list stack, if it isn't already there. If it is there, but it
5822 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
5825 stmt->flags |= SIF_SCOPE;
5826 stmt->downScope = tc->topScopeStmt;
5827 tc->topScopeStmt = stmt;
5828 JS_SCOPE_DEPTH_METERING(++tc->scopeDepth > tc->maxScopeDepth &&
5829 (tc->maxScopeDepth = tc->scopeDepth));
5831 obj->setParent(tc->blockChain());
5832 blockbox->parent = tc->blockChainBox;
5833 tc->blockChainBox = blockbox;
5834 stmt->blockBox = blockbox;
5837 JSParseNode *tmp = tc->blockNode;
5838 JS_ASSERT(!tmp || tmp->pn_type != TOK_LEXICALSCOPE);
5841 /* Create a new lexical scope node for these statements. */
5842 JSParseNode *pn1 = LexicalScopeNode::create(tc);
5846 pn1->pn_type = TOK_LEXICALSCOPE;
5847 pn1->pn_op = JSOP_LEAVEBLOCK;
5848 pn1->pn_pos = tc->blockNode->pn_pos;
5849 pn1->pn_objbox = blockbox;
5850 pn1->pn_expr = tc->blockNode;
5851 pn1->pn_blockid = tc->blockNode->pn_blockid;
5852 tc->blockNode = pn1;
5855 pn = variables(false);
5858 pn->pn_xflags = PNX_POPVAR;
5861 /* Check termination of this primitive statement. */
5862 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5867 Parser::expressionStatement()
5869 tokenStream.ungetToken();
5870 JSParseNode *pn2 = expr();
5874 if (tokenStream.peekToken() == TOK_COLON) {
5875 if (pn2->pn_type != TOK_NAME) {
5876 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LABEL);
5879 JSAtom *label = pn2->pn_atom;
5880 for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
5881 if (stmt->type == STMT_LABEL && stmt->label == label) {
5882 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_DUPLICATE_LABEL);
5888 (void) tokenStream.getToken();
5890 /* Push a label struct and parse the statement. */
5891 JSStmtInfo stmtInfo;
5892 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
5893 stmtInfo.label = label;
5894 JSParseNode *pn = statement();
5898 /* Normalize empty statement to empty block for the decompiler. */
5899 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
5900 pn->pn_type = TOK_LC;
5901 pn->pn_arity = PN_LIST;
5905 /* Pop the label, set pn_expr, and return early. */
5907 pn2->pn_type = TOK_COLON;
5908 pn2->pn_pos.end = pn->pn_pos.end;
5913 JSParseNode *pn = UnaryNode::create(tc);
5916 pn->pn_type = TOK_SEMI;
5917 pn->pn_pos = pn2->pn_pos;
5920 switch (PN_TYPE(pn2)) {
5923 * Flag lambdas immediately applied as statements as instances of
5924 * the JS "module pattern". See CheckForImmediatelyAppliedLambda.
5926 if (PN_TYPE(pn2->pn_head) == TOK_FUNCTION &&
5927 !pn2->pn_head->pn_funbox->node->isFunArg()) {
5928 pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
5933 * Keep track of all apparent methods created by assignments such
5934 * as this.foo = function (...) {...} in a function that could end
5935 * up a constructor function. See Parser::setFunctionKinds.
5938 PN_OP(pn2) == JSOP_NOP &&
5939 PN_OP(pn2->pn_left) == JSOP_SETPROP &&
5940 PN_OP(pn2->pn_left->pn_expr) == JSOP_THIS &&
5941 PN_OP(pn2->pn_right) == JSOP_LAMBDA) {
5942 JS_ASSERT(!pn2->pn_defn);
5943 JS_ASSERT(!pn2->pn_used);
5944 pn2->pn_right->pn_link = tc->funbox->methods;
5945 tc->funbox->methods = pn2->pn_right;
5951 /* Check termination of this primitive statement. */
5952 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
5960 JS_CHECK_RECURSION(context, return NULL);
5962 switch (tokenStream.getToken(TSF_OPERAND)) {
5965 #if JS_HAS_XML_SUPPORT
5966 TokenKind tt = tokenStream.peekToken(TSF_KEYWORD_IS_NAME);
5967 if (tt == TOK_DBLCOLON)
5970 return functionStmt();
5975 /* An IF node has three kids: condition, then, and optional else. */
5976 pn = TernaryNode::create(tc);
5979 JSParseNode *pn1 = condition();
5982 JSStmtInfo stmtInfo;
5983 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
5984 JSParseNode *pn2 = statement();
5988 if (tokenStream.matchToken(TOK_ELSE, TSF_OPERAND)) {
5989 stmtInfo.type = STMT_ELSE;
5993 pn->pn_pos.end = pn3->pn_pos.end;
5996 pn->pn_pos.end = pn2->pn_pos.end;
6006 return switchStatement();
6010 pn = BinaryNode::create(tc);
6013 JSStmtInfo stmtInfo;
6014 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
6015 JSParseNode *pn2 = condition();
6019 JSParseNode *pn3 = statement();
6023 pn->pn_pos.end = pn3->pn_pos.end;
6030 pn = BinaryNode::create(tc);
6033 JSStmtInfo stmtInfo;
6034 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
6035 JSParseNode *pn2 = statement();
6039 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
6040 JSParseNode *pn3 = condition();
6044 pn->pn_pos.end = pn3->pn_pos.end;
6046 if (versionNumber() != JSVERSION_ECMA_3) {
6048 * All legacy and extended versions must do automatic semicolon
6049 * insertion after do-while. See the testcase and discussion in
6050 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
6052 (void) tokenStream.matchToken(TOK_SEMI);
6059 return forStatement();
6062 return tryStatement();
6066 pn = UnaryNode::create(tc);
6070 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
6071 TokenKind tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6072 if (tt == TOK_ERROR)
6074 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
6075 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
6079 JSParseNode *pn2 = expr();
6082 pn->pn_pos.end = pn2->pn_pos.end;
6083 pn->pn_op = JSOP_THROW;
6088 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
6090 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CATCH_WITHOUT_TRY);
6094 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_FINALLY_WITHOUT_TRY);
6099 pn = NullaryNode::create(tc);
6102 if (!MatchLabel(context, &tokenStream, pn))
6104 JSStmtInfo *stmt = tc->topStmt;
6105 JSAtom *label = pn->pn_atom;
6107 for (; ; stmt = stmt->down) {
6109 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
6112 if (stmt->type == STMT_LABEL && stmt->label == label)
6116 for (; ; stmt = stmt->down) {
6118 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOUGH_BREAK);
6121 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
6126 pn->pn_pos.end = tokenStream.currentToken().pos.end;
6132 pn = NullaryNode::create(tc);
6135 if (!MatchLabel(context, &tokenStream, pn))
6137 JSStmtInfo *stmt = tc->topStmt;
6138 JSAtom *label = pn->pn_atom;
6140 for (JSStmtInfo *stmt2 = NULL; ; stmt = stmt->down) {
6142 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_LABEL_NOT_FOUND);
6145 if (stmt->type == STMT_LABEL) {
6146 if (stmt->label == label) {
6147 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
6148 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
6158 for (; ; stmt = stmt->down) {
6160 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_CONTINUE);
6163 if (STMT_IS_LOOP(stmt))
6168 pn->pn_pos.end = tokenStream.currentToken().pos.end;
6173 return withStatement();
6176 pn = variables(false);
6180 /* Tell js_EmitTree to generate a final POP. */
6181 pn->pn_xflags |= PNX_POPVAR;
6184 #if JS_HAS_BLOCK_SCOPE
6186 return letStatement();
6187 #endif /* JS_HAS_BLOCK_SCOPE */
6190 pn = returnOrYield(false);
6199 oldflags = tc->flags;
6200 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
6201 JSStmtInfo stmtInfo;
6202 if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, tc))
6208 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
6212 * If we contain a function statement and our container is top-level
6213 * or another block, flag pn to preserve braces when decompiling.
6215 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
6216 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
6217 pn->pn_xflags |= PNX_NEEDBRACES;
6219 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
6225 pn = UnaryNode::create(tc);
6228 pn->pn_type = TOK_SEMI;
6232 pn = NullaryNode::create(tc);
6235 pn->pn_type = TOK_DEBUGGER;
6236 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6239 #if JS_HAS_XML_SUPPORT
6242 pn = UnaryNode::create(tc);
6245 if (!tokenStream.matchToken(TOK_NAME) ||
6246 tokenStream.currentToken().t_atom != context->runtime->atomState.xmlAtom ||
6247 !tokenStream.matchToken(TOK_NAME) ||
6248 tokenStream.currentToken().t_atom != context->runtime->atomState.namespaceAtom ||
6249 !tokenStream.matchToken(TOK_ASSIGN) ||
6250 tokenStream.currentToken().t_op != JSOP_NOP) {
6251 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DEFAULT_XML_NAMESPACE);
6255 /* Is this an E4X dagger I see before me? */
6256 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6257 JSParseNode *pn2 = expr();
6260 pn->pn_op = JSOP_DEFXMLNS;
6261 pn->pn_pos.end = pn2->pn_pos.end;
6271 #if JS_HAS_XML_SUPPORT
6274 return expressionStatement();
6277 /* Check termination of this primitive statement. */
6278 return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
6282 Parser::variables(bool inLetHead)
6286 JSStmtInfo *scopeStmt;
6288 JSParseNode *pn, *pn2;
6292 * The three options here are:
6293 * - TOK_LET: We are parsing a let declaration.
6294 * - TOK_LP: We are parsing the head of a let block.
6295 * - Otherwise, we're parsing var declarations.
6297 tt = tokenStream.currentToken().type;
6298 let = (tt == TOK_LET || tt == TOK_LP);
6299 JS_ASSERT(let || tt == TOK_VAR);
6301 #if JS_HAS_BLOCK_SCOPE
6302 bool popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
6303 JSStmtInfo *save = tc->topStmt, *saveScope = tc->topScopeStmt;
6306 /* Make sure that statement set up the tree context correctly. */
6307 scopeStmt = tc->topScopeStmt;
6309 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
6310 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
6311 scopeStmt = scopeStmt->downScope;
6313 JS_ASSERT(scopeStmt);
6316 data.op = let ? JSOP_NOP : tokenStream.currentToken().t_op;
6317 pn = ListNode::create(tc);
6320 pn->pn_op = data.op;
6324 * SpiderMonkey const is really "write once per initialization evaluation"
6325 * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
6326 * this code will change soon.
6329 JS_ASSERT(tc->blockChainBox == scopeStmt->blockBox);
6330 data.binder = BindLet;
6331 data.let.overflow = JSMSG_TOO_MANY_LOCALS;
6333 data.binder = BindVarOrConst;
6337 tt = tokenStream.getToken();
6338 #if JS_HAS_DESTRUCTURING
6339 if (tt == TOK_LB || tt == TOK_LC) {
6340 tc->flags |= TCF_DECL_DESTRUCTURING;
6341 pn2 = primaryExpr(tt, JS_FALSE);
6342 tc->flags &= ~TCF_DECL_DESTRUCTURING;
6346 if (!CheckDestructuring(context, &data, pn2, NULL, tc))
6348 if ((tc->flags & TCF_IN_FOR_INIT) &&
6349 tokenStream.peekToken() == TOK_IN) {
6354 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
6355 if (tokenStream.currentToken().t_op != JSOP_NOP)
6358 #if JS_HAS_BLOCK_SCOPE
6360 tc->topStmt = save->down;
6361 tc->topScopeStmt = saveScope->downScope;
6364 JSParseNode *init = assignExpr();
6365 #if JS_HAS_BLOCK_SCOPE
6368 tc->topScopeStmt = saveScope;
6372 if (!init || !UndominateInitializers(pn2, init, tc))
6375 pn2 = JSParseNode::newBinaryOrAppend(TOK_ASSIGN, JSOP_NOP, pn2, init, tc);
6381 #endif /* JS_HAS_DESTRUCTURING */
6383 if (tt != TOK_NAME) {
6384 if (tt != TOK_ERROR) {
6385 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
6390 atom = tokenStream.currentToken().t_atom;
6391 pn2 = NewBindingNode(atom, tc, let);
6394 if (data.op == JSOP_DEFCONST)
6395 pn2->pn_dflags |= PND_CONST;
6397 if (!data.binder(context, &data, atom, tc))
6401 if (tokenStream.matchToken(TOK_ASSIGN)) {
6402 if (tokenStream.currentToken().t_op != JSOP_NOP)
6405 #if JS_HAS_BLOCK_SCOPE
6407 tc->topStmt = save->down;
6408 tc->topScopeStmt = saveScope->downScope;
6411 JSParseNode *init = assignExpr();
6412 #if JS_HAS_BLOCK_SCOPE
6415 tc->topScopeStmt = saveScope;
6422 pn2 = MakeAssignment(pn2, init, tc);
6426 pn2->pn_expr = init;
6429 JS_ASSERT_IF(pn2->pn_dflags & PND_GVAR, !(pn2->pn_dflags & PND_BOUND));
6431 pn2->pn_op = (PN_OP(pn2) == JSOP_ARGUMENTS)
6433 : (pn2->pn_dflags & PND_BOUND)
6435 : (data.op == JSOP_DEFCONST)
6439 NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
6441 /* The declarator's position must include the initializer. */
6442 pn2->pn_pos.end = init->pn_pos.end;
6444 if (tc->inFunction() &&
6445 atom == context->runtime->atomState.argumentsAtom) {
6446 tc->noteArgumentsUse();
6448 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6451 } while (tokenStream.matchToken(TOK_COMMA));
6453 pn->pn_pos.end = pn->last()->pn_pos.end;
6457 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_VAR_INIT);
6464 JSParseNode *pn = assignExpr();
6465 if (pn && tokenStream.matchToken(TOK_COMMA)) {
6466 JSParseNode *pn2 = ListNode::create(tc);
6469 pn2->pn_pos.begin = pn->pn_pos.begin;
6473 #if JS_HAS_GENERATORS
6475 if (pn2->pn_type == TOK_YIELD && !pn2->pn_parens) {
6476 reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
6484 } while (tokenStream.matchToken(TOK_COMMA));
6485 pn->pn_pos.end = pn->last()->pn_pos.end;
6491 Parser::assignExpr()
6493 JS_CHECK_RECURSION(context, return NULL);
6495 #if JS_HAS_GENERATORS
6496 if (tokenStream.matchToken(TOK_YIELD, TSF_OPERAND))
6497 return returnOrYield(true);
6500 JSParseNode *pn = condExpr();
6504 TokenKind tt = tokenStream.getToken();
6505 if (tt != TOK_ASSIGN) {
6506 tokenStream.ungetToken();
6510 JSOp op = tokenStream.currentToken().t_op;
6511 switch (pn->pn_type) {
6513 if (!CheckStrictAssignment(context, tc, pn))
6515 pn->pn_op = JSOP_SETNAME;
6516 NoteLValue(context, pn, tc);
6519 pn->pn_op = JSOP_SETPROP;
6522 pn->pn_op = JSOP_SETELEM;
6524 #if JS_HAS_DESTRUCTURING
6528 if (op != JSOP_NOP) {
6529 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_DESTRUCT_ASS);
6532 JSParseNode *rhs = assignExpr();
6533 if (!rhs || !CheckDestructuring(context, NULL, pn, rhs, tc))
6535 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
6539 if (!MakeSetCall(context, pn, tc, JSMSG_BAD_LEFTSIDE_OF_ASS))
6542 #if JS_HAS_XML_SUPPORT
6544 if (pn->pn_op == JSOP_XMLNAME) {
6545 pn->pn_op = JSOP_SETXMLNAME;
6551 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_LEFTSIDE_OF_ASS);
6555 JSParseNode *rhs = assignExpr();
6556 if (rhs && PN_TYPE(pn) == TOK_NAME && pn->pn_used) {
6557 JSDefinition *dn = pn->pn_lexdef;
6560 * If the definition is not flagged as assigned, we must have imputed
6561 * the initialized flag to it, to optimize for flat closures. But that
6562 * optimization uses source coordinates to check dominance relations,
6563 * so we must extend the end of the definition to cover the right-hand
6564 * side of this assignment, i.e., the initializer.
6566 if (!dn->isAssigned()) {
6567 JS_ASSERT(dn->isInitialized());
6568 dn->pn_pos.end = rhs->pn_pos.end;
6572 return JSParseNode::newBinaryOrAppend(TOK_ASSIGN, op, pn, rhs, tc);
6578 JSParseNode *pn = orExpr();
6579 if (pn && tokenStream.matchToken(TOK_HOOK)) {
6580 JSParseNode *pn1 = pn;
6581 pn = TernaryNode::create(tc);
6586 * Always accept the 'in' operator in the middle clause of a ternary,
6587 * where it's unambiguous, even if we might be parsing the init of a
6590 uintN oldflags = tc->flags;
6591 tc->flags &= ~TCF_IN_FOR_INIT;
6592 JSParseNode *pn2 = assignExpr();
6593 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
6597 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
6598 JSParseNode *pn3 = assignExpr();
6601 pn->pn_pos.begin = pn1->pn_pos.begin;
6602 pn->pn_pos.end = pn3->pn_pos.end;
6613 JSParseNode *pn = andExpr();
6614 while (pn && tokenStream.matchToken(TOK_OR))
6615 pn = JSParseNode::newBinaryOrAppend(TOK_OR, JSOP_OR, pn, andExpr(), tc);
6622 JSParseNode *pn = bitOrExpr();
6623 while (pn && tokenStream.matchToken(TOK_AND))
6624 pn = JSParseNode::newBinaryOrAppend(TOK_AND, JSOP_AND, pn, bitOrExpr(), tc);
6631 JSParseNode *pn = bitXorExpr();
6632 while (pn && tokenStream.matchToken(TOK_BITOR))
6633 pn = JSParseNode::newBinaryOrAppend(TOK_BITOR, JSOP_BITOR, pn, bitXorExpr(), tc);
6638 Parser::bitXorExpr()
6640 JSParseNode *pn = bitAndExpr();
6641 while (pn && tokenStream.matchToken(TOK_BITXOR))
6642 pn = JSParseNode::newBinaryOrAppend(TOK_BITXOR, JSOP_BITXOR, pn, bitAndExpr(), tc);
6647 Parser::bitAndExpr()
6649 JSParseNode *pn = eqExpr();
6650 while (pn && tokenStream.matchToken(TOK_BITAND))
6651 pn = JSParseNode::newBinaryOrAppend(TOK_BITAND, JSOP_BITAND, pn, eqExpr(), tc);
6658 JSParseNode *pn = relExpr();
6659 while (pn && tokenStream.matchToken(TOK_EQOP)) {
6660 JSOp op = tokenStream.currentToken().t_op;
6661 pn = JSParseNode::newBinaryOrAppend(TOK_EQOP, op, pn, relExpr(), tc);
6669 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
6672 * Uses of the in operator in shiftExprs are always unambiguous,
6673 * so unset the flag that prohibits recognizing it.
6675 tc->flags &= ~TCF_IN_FOR_INIT;
6677 JSParseNode *pn = shiftExpr();
6679 (tokenStream.matchToken(TOK_RELOP) ||
6681 * Recognize the 'in' token as an operator only if we're not
6682 * currently in the init expr of a for loop.
6684 (inForInitFlag == 0 && tokenStream.matchToken(TOK_IN)) ||
6685 tokenStream.matchToken(TOK_INSTANCEOF))) {
6686 TokenKind tt = tokenStream.currentToken().type;
6687 JSOp op = tokenStream.currentToken().t_op;
6688 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, shiftExpr(), tc);
6690 /* Restore previous state of inForInit flag. */
6691 tc->flags |= inForInitFlag;
6699 JSParseNode *pn = addExpr();
6700 while (pn && tokenStream.matchToken(TOK_SHOP)) {
6701 JSOp op = tokenStream.currentToken().t_op;
6702 pn = JSParseNode::newBinaryOrAppend(TOK_SHOP, op, pn, addExpr(), tc);
6710 JSParseNode *pn = mulExpr();
6712 (tokenStream.matchToken(TOK_PLUS) ||
6713 tokenStream.matchToken(TOK_MINUS))) {
6714 TokenKind tt = tokenStream.currentToken().type;
6715 JSOp op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
6716 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, mulExpr(), tc);
6724 JSParseNode *pn = unaryExpr();
6725 while (pn && (tokenStream.matchToken(TOK_STAR) || tokenStream.matchToken(TOK_DIVOP))) {
6726 TokenKind tt = tokenStream.currentToken().type;
6727 JSOp op = tokenStream.currentToken().t_op;
6728 pn = JSParseNode::newBinaryOrAppend(tt, op, pn, unaryExpr(), tc);
6733 static JSParseNode *
6734 SetLvalKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6735 JSParseNode *pn, JSParseNode *kid, const char *name)
6737 if (kid->pn_type != TOK_NAME &&
6738 kid->pn_type != TOK_DOT &&
6739 (kid->pn_type != TOK_LP ||
6740 (kid->pn_op != JSOP_CALL && kid->pn_op != JSOP_EVAL &&
6741 kid->pn_op != JSOP_FUNCALL && kid->pn_op != JSOP_FUNAPPLY)) &&
6742 #if JS_HAS_XML_SUPPORT
6743 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
6745 kid->pn_type != TOK_LB) {
6746 ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_OPERAND, name);
6749 if (!CheckStrictAssignment(cx, tc, kid))
6755 static const char incop_name_str[][10] = {"increment", "decrement"};
6758 SetIncOpKid(JSContext *cx, TokenStream *ts, JSTreeContext *tc,
6759 JSParseNode *pn, JSParseNode *kid,
6760 TokenKind tt, JSBool preorder)
6764 kid = SetLvalKid(cx, ts, tc, pn, kid, incop_name_str[tt == TOK_DEC]);
6767 switch (kid->pn_type) {
6769 op = (tt == TOK_INC)
6770 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
6771 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
6772 NoteLValue(cx, kid, tc);
6776 op = (tt == TOK_INC)
6777 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
6778 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
6782 if (!MakeSetCall(cx, kid, tc, JSMSG_BAD_INCOP_OPERAND))
6785 #if JS_HAS_XML_SUPPORT
6787 if (kid->pn_op == JSOP_XMLNAME)
6788 kid->pn_op = JSOP_SETXMLNAME;
6792 op = (tt == TOK_INC)
6793 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
6794 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
6808 JSParseNode *pn, *pn2;
6810 JS_CHECK_RECURSION(context, return NULL);
6812 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
6817 pn = UnaryNode::create(tc);
6820 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
6821 pn->pn_op = tokenStream.currentToken().t_op;
6825 pn->pn_pos.end = pn2->pn_pos.end;
6831 pn = UnaryNode::create(tc);
6834 pn2 = memberExpr(JS_TRUE);
6837 if (!SetIncOpKid(context, &tokenStream, tc, pn, pn2, tt, JS_TRUE))
6839 pn->pn_pos.end = pn2->pn_pos.end;
6844 pn = UnaryNode::create(tc);
6850 pn->pn_pos.end = pn2->pn_pos.end;
6853 * Under ECMA3, deleting any unary expression is valid -- it simply
6854 * returns true. Here we fold constants before checking for a call
6855 * expression, in order to rule out delete of a generator expression.
6857 if (!js_FoldConstants(context, pn2, tc))
6859 switch (pn2->pn_type) {
6861 if (!(pn2->pn_xflags & PNX_SETCALL)) {
6863 * Call MakeSetCall to check for errors, but clear PNX_SETCALL
6864 * because the optimizer will eliminate the useless delete.
6866 if (!MakeSetCall(context, pn2, tc, JSMSG_BAD_DELETE_OPERAND))
6868 pn2->pn_xflags &= ~PNX_SETCALL;
6872 if (!ReportStrictModeError(context, &tokenStream, tc, pn,
6873 JSMSG_DEPRECATED_DELETE_OPERAND)) {
6876 pn2->pn_op = JSOP_DELNAME;
6877 if (pn2->pn_atom == context->runtime->atomState.argumentsAtom)
6878 tc->flags |= TCF_FUN_HEAVYWEIGHT;
6889 tokenStream.ungetToken();
6890 pn = memberExpr(JS_TRUE);
6894 /* Don't look across a newline boundary for a postfix incop. */
6895 if (tokenStream.onCurrentLine(pn->pn_pos)) {
6896 tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
6897 if (tt == TOK_INC || tt == TOK_DEC) {
6898 (void) tokenStream.getToken();
6899 pn2 = UnaryNode::create(tc);
6902 if (!SetIncOpKid(context, &tokenStream, tc, pn2, pn, tt, JS_FALSE))
6904 pn2->pn_pos.begin = pn->pn_pos.begin;
6913 #if JS_HAS_GENERATORS
6916 * A dedicated helper for transplanting the comprehension expression E in
6918 * [E for (V in I)] // array comprehension
6919 * (E for (V in I)) // generator expression
6921 * from its initial location in the AST, on the left of the 'for', to its final
6922 * position on the right. To avoid a separate pass we do this by adjusting the
6923 * blockids and name binding links that were established when E was parsed.
6925 * A generator expression desugars like so:
6927 * (E for (V in I)) => (function () { for (var V in I) yield E; })()
6929 * so the transplanter must adjust static level as well as blockid. E's source
6930 * coordinates in root->pn_pos are critical to deciding which binding links to
6931 * preserve and which to cut.
6933 * NB: This is not a general tree transplanter -- it knows in particular that
6934 * the one or more bindings induced by V have not yet been created.
6936 class CompExprTransplanter {
6944 CompExprTransplanter(JSParseNode *pn, JSTreeContext *tc, bool ge, uintN adj)
6945 : root(pn), tc(tc), genexp(ge), adjust(adj), funcLevel(0)
6949 bool transplant(JSParseNode *pn);
6953 * Any definitions nested within the comprehension expression of a generator
6954 * expression must move "down" one static level, which of course increases the
6955 * upvar-frame-skip count.
6958 BumpStaticLevel(JSParseNode *pn, JSTreeContext *tc)
6960 if (!pn->pn_cookie.isFree()) {
6961 uintN level = pn->pn_cookie.level() + 1;
6963 JS_ASSERT(level >= tc->staticLevel);
6964 if (level >= UpvarCookie::FREE_LEVEL) {
6965 JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
6966 JSMSG_TOO_DEEP, js_function_str);
6970 pn->pn_cookie.set(level, pn->pn_cookie.slot());
6976 AdjustBlockId(JSParseNode *pn, uintN adjust, JSTreeContext *tc)
6978 JS_ASSERT(pn->pn_arity == PN_LIST || pn->pn_arity == PN_FUNC || pn->pn_arity == PN_NAME);
6979 pn->pn_blockid += adjust;
6980 if (pn->pn_blockid >= tc->blockidGen)
6981 tc->blockidGen = pn->pn_blockid + 1;
6985 CompExprTransplanter::transplant(JSParseNode *pn)
6990 switch (pn->pn_arity) {
6992 for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6993 if (!transplant(pn2))
6996 if (pn->pn_pos >= root->pn_pos)
6997 AdjustBlockId(pn, adjust, tc);
7001 if (!transplant(pn->pn_kid1) ||
7002 !transplant(pn->pn_kid2) ||
7003 !transplant(pn->pn_kid3))
7008 if (!transplant(pn->pn_left))
7011 /* Binary TOK_COLON nodes can have left == right. See bug 492714. */
7012 if (pn->pn_right != pn->pn_left) {
7013 if (!transplant(pn->pn_right))
7019 if (!transplant(pn->pn_kid))
7026 * Only the first level of transplant recursion through functions needs
7027 * to reparent the funbox, since all descendant functions are correctly
7028 * linked under the top-most funbox. But every visit to this case needs
7029 * to update funbox->level.
7031 * Recall that funbox->level is the static level of the code containing
7032 * the definition or expression of the function and not the static level
7033 * of the function's body.
7035 JSFunctionBox *funbox = pn->pn_funbox;
7037 funbox->level = tc->staticLevel + funcLevel;
7038 if (++funcLevel == 1 && genexp) {
7039 JSFunctionBox *parent = tc->funbox;
7041 JSFunctionBox **funboxp = &tc->parent->functionList;
7042 while (*funboxp != funbox)
7043 funboxp = &(*funboxp)->siblings;
7044 *funboxp = funbox->siblings;
7046 funbox->parent = parent;
7047 funbox->siblings = parent->kids;
7048 parent->kids = funbox;
7049 funbox->level = tc->staticLevel;
7055 if (!transplant(pn->maybeExpr()))
7057 if (pn->pn_arity == PN_FUNC)
7061 if (genexp && !BumpStaticLevel(pn, tc))
7063 } else if (pn->pn_used) {
7064 JS_ASSERT(pn->pn_op != JSOP_NOP);
7065 JS_ASSERT(pn->pn_cookie.isFree());
7067 JSDefinition *dn = pn->pn_lexdef;
7068 JS_ASSERT(dn->pn_defn);
7071 * Adjust the definition's block id only if it is a placeholder not
7072 * to the left of the root node, and if pn is the last use visited
7073 * in the comprehension expression (to avoid adjusting the blockid
7076 * Non-placeholder definitions within the comprehension expression
7077 * will be visited further below.
7079 if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
7080 if (genexp && !BumpStaticLevel(dn, tc))
7082 AdjustBlockId(dn, adjust, tc);
7085 JSAtom *atom = pn->pn_atom;
7087 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
7088 JS_ASSERT(!stmt || stmt != tc->topStmt);
7090 if (genexp && PN_OP(dn) != JSOP_CALLEE) {
7091 JS_ASSERT(!tc->decls.lookup(atom));
7093 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
7094 JSAtomListElement *ale = tc->lexdeps.add(tc->parser, atom);
7098 if (dn->pn_pos >= root->pn_pos) {
7099 tc->parent->lexdeps.remove(tc->parser, atom);
7101 JSDefinition *dn2 = (JSDefinition *)NameNode::create(atom, tc);
7105 dn2->pn_type = TOK_NAME;
7106 dn2->pn_op = JSOP_NOP;
7107 dn2->pn_defn = true;
7108 dn2->pn_dflags |= PND_PLACEHOLDER;
7109 dn2->pn_pos = root->pn_pos;
7111 JSParseNode **pnup = &dn->dn_uses;
7113 while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
7114 pnu->pn_lexdef = dn2;
7115 dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
7116 pnup = &pnu->pn_link;
7118 dn2->dn_uses = dn->dn_uses;
7119 dn->dn_uses = *pnup;
7125 ALE_SET_DEFN(ale, dn);
7130 if (pn->pn_pos >= root->pn_pos)
7131 AdjustBlockId(pn, adjust, tc);
7135 if (!transplant(pn->pn_tree))
7143 * Starting from a |for| keyword after the first array initialiser element or
7144 * an expression in an open parenthesis, parse the tail of the comprehension
7145 * or generator expression signified by this |for| keyword in context.
7147 * Return null on failure, else return the top-most parse node for the array
7148 * comprehension or generator expression, with a unary node as the body of the
7149 * (possibly nested) for-loop, initialized by |type, op, kid|.
7152 Parser::comprehensionTail(JSParseNode *kid, uintN blockid,
7153 TokenKind type, JSOp op)
7156 JSParseNode *pn, *pn2, *pn3, **pnp;
7157 JSStmtInfo stmtInfo;
7162 JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
7164 if (type == TOK_SEMI) {
7166 * Generator expression desugars to an immediately applied lambda that
7167 * yields the next value from a for-in loop (possibly nested, and with
7168 * optional if guard). Make pn be the TOK_LC body node.
7170 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
7173 adjust = pn->pn_blockid - blockid;
7175 JS_ASSERT(type == TOK_ARRAYPUSH);
7178 * Make a parse-node and literal object representing the block scope of
7179 * this array comprehension. Our caller in primaryExpr, the TOK_LB case
7180 * aka the array initialiser case, has passed the blockid to claim for
7181 * the comprehension's block scope. We allocate that id or one above it
7182 * here, by calling js_PushLexicalScope.
7184 * In the case of a comprehension expression that has nested blocks
7185 * (e.g., let expressions), we will allocate a higher blockid but then
7186 * slide all blocks "to the right" to make room for the comprehension's
7189 adjust = tc->blockid();
7190 pn = PushLexicalScope(context, &tokenStream, tc, &stmtInfo);
7194 JS_ASSERT(blockid <= pn->pn_blockid);
7195 JS_ASSERT(blockid < tc->blockidGen);
7196 JS_ASSERT(tc->bodyid < blockid);
7197 pn->pn_blockid = stmtInfo.blockid = blockid;
7198 JS_ASSERT(adjust < blockid);
7199 adjust = blockid - adjust;
7204 CompExprTransplanter transplanter(kid, tc, type == TOK_SEMI, adjust);
7205 transplanter.transplant(kid);
7209 data.binder = BindLet;
7210 data.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
7214 * FOR node is binary, left is loop control and right is body. Use
7215 * index to count each block-local let-variable on the left-hand side
7218 pn2 = BinaryNode::create(tc);
7222 pn2->pn_op = JSOP_ITER;
7223 pn2->pn_iflags = JSITER_ENUMERATE;
7224 if (tokenStream.matchToken(TOK_NAME)) {
7225 if (tokenStream.currentToken().t_atom == context->runtime->atomState.eachAtom)
7226 pn2->pn_iflags |= JSITER_FOREACH;
7228 tokenStream.ungetToken();
7230 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
7233 tt = tokenStream.getToken();
7235 #if JS_HAS_DESTRUCTURING
7238 tc->flags |= TCF_DECL_DESTRUCTURING;
7239 pn3 = primaryExpr(tt, JS_FALSE);
7240 tc->flags &= ~TCF_DECL_DESTRUCTURING;
7247 atom = tokenStream.currentToken().t_atom;
7250 * Create a name node with pn_op JSOP_NAME. We can't set pn_op to
7251 * JSOP_GETLOCAL here, because we don't yet know the block's depth
7252 * in the operand stack frame. The code generator computes that,
7253 * and it tries to bind all names to slots, so we must let it do
7256 pn3 = NewBindingNode(atom, tc, true);
7262 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NO_VARIABLE_NAME);
7268 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
7269 JSParseNode *pn4 = expr();
7272 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
7275 #if JS_HAS_DESTRUCTURING
7278 if (!CheckDestructuring(context, &data, pn3, NULL, tc))
7281 if (versionNumber() == JSVERSION_1_7) {
7282 /* Destructuring requires [key, value] enumeration in JS1.7. */
7283 if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
7284 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_FOR_LEFTSIDE);
7288 JS_ASSERT(pn2->pn_op == JSOP_ITER);
7289 JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
7290 if (!(pn2->pn_iflags & JSITER_FOREACH))
7291 pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
7298 if (!data.binder(context, &data, atom, tc))
7305 pn2->pn_left = JSParseNode::newBinaryOrAppend(TOK_IN, JSOP_NOP, pn3, pn4, tc);
7309 pnp = &pn2->pn_right;
7310 } while (tokenStream.matchToken(TOK_FOR));
7312 if (tokenStream.matchToken(TOK_IF)) {
7313 pn2 = TernaryNode::create(tc);
7316 pn2->pn_kid1 = condition();
7320 pnp = &pn2->pn_kid2;
7323 pn2 = UnaryNode::create(tc);
7326 pn2->pn_type = type;
7335 #if JS_HAS_GENERATOR_EXPRS
7338 * Starting from a |for| keyword after an expression, parse the comprehension
7339 * tail completing this generator expression. Wrap the expression at kid in a
7340 * generator function that is immediately called to evaluate to the generator
7341 * iterator that is the value of this generator expression.
7343 * |kid| must be the expression before the |for| keyword; we return an
7344 * application of a generator function that includes the |for| loops and
7345 * |if| guards, with |kid| as the operand of a |yield| expression as the
7346 * innermost loop body.
7348 * Note how unlike Python, we do not evaluate the expression to the right of
7349 * the first |in| in the chain of |for| heads. Instead, a generator expression
7350 * is merely sugar for a generator function expression and its application.
7353 Parser::generatorExpr(JSParseNode *kid)
7355 /* Create a |yield| node for |kid|. */
7356 JSParseNode *pn = UnaryNode::create(tc);
7359 pn->pn_type = TOK_YIELD;
7360 pn->pn_op = JSOP_YIELD;
7361 pn->pn_parens = true;
7362 pn->pn_pos = kid->pn_pos;
7364 pn->pn_hidden = true;
7366 /* Make a new node for the desugared generator function. */
7367 JSParseNode *genfn = FunctionNode::create(tc);
7370 genfn->pn_type = TOK_FUNCTION;
7371 genfn->pn_op = JSOP_LAMBDA;
7372 JS_ASSERT(!genfn->pn_body);
7373 genfn->pn_dflags = PND_FUNARG;
7376 JSTreeContext *outertc = tc;
7377 JSTreeContext gentc(tc->parser);
7379 JSFunctionBox *funbox = EnterFunction(genfn, &gentc);
7384 * We have to dance around a bit to propagate sharp variables from
7385 * outertc to gentc before setting TCF_HAS_SHARPS implicitly by
7386 * propagating all of outertc's TCF_FUN_FLAGS flags. As below, we have
7387 * to be conservative by leaving TCF_HAS_SHARPS set in outertc if we
7388 * do propagate to gentc.
7390 if (outertc->flags & TCF_HAS_SHARPS) {
7391 gentc.flags |= TCF_IN_FUNCTION;
7392 if (!gentc.ensureSharpSlots())
7397 * We assume conservatively that any deoptimization flag in tc->flags
7398 * besides TCF_FUN_PARAM_ARGUMENTS can come from the kid. So we
7399 * propagate these flags into genfn. For code simplicity we also do
7400 * not detect if the flags were only set in the kid and could be
7401 * removed from tc->flags.
7403 gentc.flags |= TCF_FUN_IS_GENERATOR | TCF_GENEXP_LAMBDA |
7404 (tc->flags & (TCF_FUN_FLAGS & ~TCF_FUN_PARAM_ARGUMENTS));
7405 funbox->tcflags |= gentc.flags;
7406 genfn->pn_funbox = funbox;
7407 genfn->pn_blockid = gentc.bodyid;
7409 JSParseNode *body = comprehensionTail(pn, outertc->blockid());
7412 JS_ASSERT(!genfn->pn_body);
7413 genfn->pn_body = body;
7414 genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
7415 genfn->pn_pos.end = body->pn_pos.end = tokenStream.currentToken().pos.end;
7417 if (!LeaveFunction(genfn, &gentc))
7422 * Our result is a call expression that invokes the anonymous generator
7425 JSParseNode *result = ListNode::create(tc);
7428 result->pn_type = TOK_LP;
7429 result->pn_op = JSOP_CALL;
7430 result->pn_pos.begin = genfn->pn_pos.begin;
7431 result->initList(genfn);
7435 static const char js_generator_str[] = "generator";
7437 #endif /* JS_HAS_GENERATOR_EXPRS */
7438 #endif /* JS_HAS_GENERATORS */
7441 Parser::argumentList(JSParseNode *listNode)
7443 if (tokenStream.matchToken(TOK_RP, TSF_OPERAND))
7447 JSParseNode *argNode = assignExpr();
7450 #if JS_HAS_GENERATORS
7451 if (argNode->pn_type == TOK_YIELD &&
7452 !argNode->pn_parens &&
7453 tokenStream.peekToken() == TOK_COMMA) {
7454 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
7458 #if JS_HAS_GENERATOR_EXPRS
7459 if (tokenStream.matchToken(TOK_FOR)) {
7460 argNode = generatorExpr(argNode);
7463 if (listNode->pn_count > 1 ||
7464 tokenStream.peekToken() == TOK_COMMA) {
7465 reportErrorNumber(argNode, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
7471 listNode->append(argNode);
7472 } while (tokenStream.matchToken(TOK_COMMA));
7474 if (tokenStream.getToken() != TOK_RP) {
7475 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_AFTER_ARGS);
7481 /* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
7482 static JSParseNode *
7483 CheckForImmediatelyAppliedLambda(JSParseNode *pn)
7485 if (pn->pn_type == TOK_FUNCTION) {
7486 JS_ASSERT(pn->pn_arity == PN_FUNC);
7488 JSFunctionBox *funbox = pn->pn_funbox;
7489 JS_ASSERT(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA);
7490 if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
7491 pn->pn_dflags &= ~PND_FUNARG;
7497 Parser::memberExpr(JSBool allowCallSyntax)
7499 JSParseNode *pn, *pn2, *pn3;
7501 JS_CHECK_RECURSION(context, return NULL);
7503 /* Check for new expression first. */
7504 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
7505 if (tt == TOK_NEW) {
7506 pn = ListNode::create(tc);
7509 pn2 = memberExpr(JS_FALSE);
7512 pn2 = CheckForImmediatelyAppliedLambda(pn2);
7513 pn->pn_op = JSOP_NEW;
7515 pn->pn_pos.begin = pn2->pn_pos.begin;
7517 if (tokenStream.matchToken(TOK_LP) && !argumentList(pn))
7519 if (pn->pn_count > ARGC_LIMIT) {
7520 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7521 JSMSG_TOO_MANY_CON_ARGS);
7524 pn->pn_pos.end = pn->last()->pn_pos.end;
7526 pn = primaryExpr(tt, JS_FALSE);
7530 if (pn->pn_type == TOK_ANYNAME ||
7531 pn->pn_type == TOK_AT ||
7532 pn->pn_type == TOK_DBLCOLON) {
7533 pn2 = NewOrRecycledNode(tc);
7536 pn2->pn_type = TOK_UNARYOP;
7537 pn2->pn_pos = pn->pn_pos;
7538 pn2->pn_op = JSOP_XMLNAME;
7539 pn2->pn_arity = PN_UNARY;
7540 pn2->pn_parens = false;
7546 while ((tt = tokenStream.getToken()) > TOK_EOF) {
7547 if (tt == TOK_DOT) {
7548 pn2 = NameNode::create(NULL, tc);
7551 #if JS_HAS_XML_SUPPORT
7552 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7553 pn3 = primaryExpr(tt, JS_TRUE);
7557 /* Check both tt and pn_type, to distinguish |x.(y)| and |x.y::z| from |x.y|. */
7558 if (tt == TOK_NAME && pn3->pn_type == TOK_NAME) {
7559 pn2->pn_op = JSOP_GETPROP;
7561 pn2->pn_atom = pn3->pn_atom;
7562 RecycleTree(pn3, tc);
7565 pn2->pn_type = TOK_FILTER;
7566 pn2->pn_op = JSOP_FILTER;
7568 /* A filtering predicate is like a with statement. */
7569 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7570 } else if (TokenKindIsXML(PN_TYPE(pn3))) {
7571 pn2->pn_type = TOK_LB;
7572 pn2->pn_op = JSOP_GETELEM;
7574 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
7577 pn2->pn_arity = PN_BINARY;
7579 pn2->pn_right = pn3;
7582 MUST_MATCH_TOKEN_WITH_FLAGS(TOK_NAME, JSMSG_NAME_AFTER_DOT, TSF_KEYWORD_IS_NAME);
7583 pn2->pn_op = JSOP_GETPROP;
7585 pn2->pn_atom = tokenStream.currentToken().t_atom;
7587 pn2->pn_pos.begin = pn->pn_pos.begin;
7588 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7589 #if JS_HAS_XML_SUPPORT
7590 } else if (tt == TOK_DBLDOT) {
7591 pn2 = BinaryNode::create(tc);
7594 tt = tokenStream.getToken(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
7595 pn3 = primaryExpr(tt, JS_TRUE);
7599 if (tt == TOK_NAME && !pn3->pn_parens) {
7600 pn3->pn_type = TOK_STRING;
7601 pn3->pn_arity = PN_NULLARY;
7602 pn3->pn_op = JSOP_QNAMEPART;
7603 } else if (!TokenKindIsXML(tt)) {
7604 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NAME_AFTER_DOT);
7607 pn2->pn_op = JSOP_DESCENDANTS;
7609 pn2->pn_right = pn3;
7610 pn2->pn_pos.begin = pn->pn_pos.begin;
7611 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7613 } else if (tt == TOK_LB) {
7614 pn2 = BinaryNode::create(tc);
7621 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
7622 pn2->pn_pos.begin = pn->pn_pos.begin;
7623 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7626 * Optimize o['p'] to o.p by rewriting pn2, but avoid rewriting
7627 * o['0'] to use JSOP_GETPROP, to keep fast indexing disjoint in
7628 * the interpreter from fast property access. However, if the
7629 * bracketed string is a uint32, we rewrite pn3 to be a number
7630 * instead of a string.
7633 if (pn3->pn_type == TOK_STRING) {
7636 if (!js_IdIsIndex(ATOM_TO_JSID(pn3->pn_atom), &index)) {
7637 pn2->pn_type = TOK_DOT;
7638 pn2->pn_op = JSOP_GETPROP;
7639 pn2->pn_arity = PN_NAME;
7641 pn2->pn_atom = pn3->pn_atom;
7644 pn3->pn_type = TOK_NUMBER;
7645 pn3->pn_op = JSOP_DOUBLE;
7646 pn3->pn_dval = index;
7648 pn2->pn_op = JSOP_GETELEM;
7650 pn2->pn_right = pn3;
7652 } else if (allowCallSyntax && tt == TOK_LP) {
7653 pn2 = ListNode::create(tc);
7656 pn2->pn_op = JSOP_CALL;
7658 pn = CheckForImmediatelyAppliedLambda(pn);
7659 if (pn->pn_op == JSOP_NAME) {
7660 if (pn->pn_atom == context->runtime->atomState.evalAtom) {
7661 /* Select JSOP_EVAL and flag tc as heavyweight. */
7662 pn2->pn_op = JSOP_EVAL;
7663 tc->noteCallsEval();
7664 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7666 } else if (pn->pn_op == JSOP_GETPROP) {
7667 /* Select JSOP_FUNAPPLY given foo.apply(...). */
7668 if (pn->pn_atom == context->runtime->atomState.applyAtom)
7669 pn2->pn_op = JSOP_FUNAPPLY;
7670 else if (pn->pn_atom == context->runtime->atomState.callAtom)
7671 pn2->pn_op = JSOP_FUNCALL;
7675 pn2->pn_pos.begin = pn->pn_pos.begin;
7677 if (!argumentList(pn2))
7679 if (pn2->pn_count > ARGC_LIMIT) {
7680 JS_ReportErrorNumber(context, js_GetErrorMessage, NULL,
7681 JSMSG_TOO_MANY_FUN_ARGS);
7684 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
7686 tokenStream.ungetToken();
7692 if (tt == TOK_ERROR)
7698 Parser::bracketedExpr()
7704 * Always accept the 'in' operator in a parenthesized expression,
7705 * where it's unambiguous, even if we might be parsing the init of a
7708 oldflags = tc->flags;
7709 tc->flags &= ~TCF_IN_FOR_INIT;
7711 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
7715 #if JS_HAS_XML_SUPPORT
7718 Parser::endBracketedExpr()
7722 pn = bracketedExpr();
7726 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
7731 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
7733 * AttributeIdentifier:
7734 * @ PropertySelector
7735 * @ QualifiedIdentifier
7742 * QualifiedIdentifier:
7743 * PropertySelector :: PropertySelector
7744 * PropertySelector :: [ Expression ]
7746 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
7748 * AttributeIdentifier:
7749 * @ QualifiedIdentifier
7756 * QualifiedIdentifier:
7757 * PropertySelector :: PropertySelector
7758 * PropertySelector :: [ Expression ]
7761 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
7762 * for that rule to result in a name node, but ECMA-357 extends the grammar
7763 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
7765 * QualifiedIdentifier:
7766 * PropertySelector QualifiedSuffix
7769 * :: PropertySelector
7773 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
7775 * PrimaryExpression:
7776 * Identifier QualifiedSuffix
7778 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
7779 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
7782 Parser::propertySelector()
7786 pn = NullaryNode::create(tc);
7789 if (pn->pn_type == TOK_STAR) {
7790 pn->pn_type = TOK_ANYNAME;
7791 pn->pn_op = JSOP_ANYNAME;
7792 pn->pn_atom = context->runtime->atomState.starAtom;
7794 JS_ASSERT(pn->pn_type == TOK_NAME);
7795 pn->pn_op = JSOP_QNAMEPART;
7796 pn->pn_arity = PN_NAME;
7797 pn->pn_atom = tokenStream.currentToken().t_atom;
7798 pn->pn_cookie.makeFree();
7804 Parser::qualifiedSuffix(JSParseNode *pn)
7806 JSParseNode *pn2, *pn3;
7809 JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
7810 pn2 = NameNode::create(NULL, tc);
7814 /* Left operand of :: must be evaluated if it is an identifier. */
7815 if (pn->pn_op == JSOP_QNAMEPART)
7816 pn->pn_op = JSOP_NAME;
7818 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7819 if (tt == TOK_STAR || tt == TOK_NAME) {
7820 /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
7821 pn2->pn_op = JSOP_QNAMECONST;
7822 pn2->pn_pos.begin = pn->pn_pos.begin;
7823 pn2->pn_atom = (tt == TOK_STAR)
7824 ? context->runtime->atomState.starAtom
7825 : tokenStream.currentToken().t_atom;
7827 pn2->pn_cookie.makeFree();
7832 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7835 pn3 = endBracketedExpr();
7839 pn2->pn_op = JSOP_QNAME;
7840 pn2->pn_arity = PN_BINARY;
7841 pn2->pn_pos.begin = pn->pn_pos.begin;
7842 pn2->pn_pos.end = pn3->pn_pos.end;
7844 pn2->pn_right = pn3;
7849 Parser::qualifiedIdentifier()
7853 pn = propertySelector();
7856 if (tokenStream.matchToken(TOK_DBLCOLON)) {
7857 /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
7858 tc->flags |= TCF_FUN_HEAVYWEIGHT;
7859 pn = qualifiedSuffix(pn);
7865 Parser::attributeIdentifier()
7867 JSParseNode *pn, *pn2;
7870 JS_ASSERT(tokenStream.currentToken().type == TOK_AT);
7871 pn = UnaryNode::create(tc);
7874 pn->pn_op = JSOP_TOATTRNAME;
7875 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
7876 if (tt == TOK_STAR || tt == TOK_NAME) {
7877 pn2 = qualifiedIdentifier();
7878 } else if (tt == TOK_LB) {
7879 pn2 = endBracketedExpr();
7881 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
7891 * Make a TOK_LC unary node whose pn_kid is an expression.
7894 Parser::xmlExpr(JSBool inTag)
7896 JSParseNode *pn, *pn2;
7898 JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
7899 pn = UnaryNode::create(tc);
7904 * Turn off XML tag mode. We save the old value of the flag because it may
7905 * already be off: XMLExpr is called both from within a tag, and from
7906 * within text contained in an element, but outside of any start, end, or
7909 bool oldflag = tokenStream.isXMLTagMode();
7910 tokenStream.setXMLTagMode(false);
7915 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
7916 tokenStream.setXMLTagMode(oldflag);
7918 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
7923 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
7924 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
7925 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
7926 * child of a container tag.
7929 Parser::xmlAtomNode()
7931 JSParseNode *pn = NullaryNode::create(tc);
7934 const Token &tok = tokenStream.currentToken();
7935 pn->pn_op = tok.t_op;
7936 pn->pn_atom = tok.t_atom;
7937 if (tok.type == TOK_XMLPI)
7938 pn->pn_atom2 = tok.t_atom2;
7943 * Parse the productions:
7946 * XMLName XMLNameExpr?
7947 * { Expr } XMLNameExpr?
7949 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
7950 * a list of names and/or expressions, a single expression, or a single name.
7951 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
7955 Parser::xmlNameExpr()
7957 JSParseNode *pn, *pn2, *list;
7962 tt = tokenStream.currentToken().type;
7964 pn2 = xmlExpr(JS_TRUE);
7968 JS_ASSERT(tt == TOK_XMLNAME);
7969 pn2 = xmlAtomNode();
7978 list = ListNode::create(tc);
7981 list->pn_type = TOK_XMLNAME;
7982 list->pn_pos.begin = pn->pn_pos.begin;
7984 list->pn_xflags = PNX_CANTFOLD;
7987 pn->pn_pos.end = pn2->pn_pos.end;
7990 } while ((tt = tokenStream.getToken()) == TOK_XMLNAME || tt == TOK_LC);
7992 tokenStream.ungetToken();
7997 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
7998 * at compile time into a JSXML tree.
8000 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
8001 ? ((pn)->pn_xflags & PNX_CANTFOLD) == 0 \
8002 : (pn)->pn_type != TOK_LC)
8005 * Parse the productions:
8009 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
8010 * XMLTagContent S XMLNameExpr S? = S? { Expr }
8012 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
8013 * produces a list of name and attribute values and/or braced expressions, a
8014 * single expression, or a single name.
8016 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
8017 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
8018 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
8019 * we parsed exactly one expression.
8022 Parser::xmlTagContent(TokenKind tagtype, JSAtom **namep)
8024 JSParseNode *pn, *pn2, *list;
8030 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
8033 while (tokenStream.matchToken(TOK_XMLSPACE)) {
8034 tt = tokenStream.getToken();
8035 if (tt != TOK_XMLNAME && tt != TOK_LC) {
8036 tokenStream.ungetToken();
8040 pn2 = xmlNameExpr();
8044 list = ListNode::create(tc);
8047 list->pn_type = tagtype;
8048 list->pn_pos.begin = pn->pn_pos.begin;
8053 if (!XML_FOLDABLE(pn2))
8054 pn->pn_xflags |= PNX_CANTFOLD;
8056 tokenStream.matchToken(TOK_XMLSPACE);
8057 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
8058 tokenStream.matchToken(TOK_XMLSPACE);
8060 tt = tokenStream.getToken();
8061 if (tt == TOK_XMLATTR) {
8062 pn2 = xmlAtomNode();
8063 } else if (tt == TOK_LC) {
8064 pn2 = xmlExpr(JS_TRUE);
8065 pn->pn_xflags |= PNX_CANTFOLD;
8067 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_ATTR_VALUE);
8072 pn->pn_pos.end = pn2->pn_pos.end;
8079 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
8081 if ((tt) <= TOK_EOF) { \
8082 if ((tt) == TOK_EOF) { \
8083 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_END_OF_XML_SOURCE); \
8090 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
8091 * that opens the end tag for the container.
8094 Parser::xmlElementContent(JSParseNode *pn)
8096 tokenStream.setXMLTagMode(false);
8098 TokenKind tt = tokenStream.getToken(TSF_XMLTEXTMODE);
8099 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
8101 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
8102 JSAtom *textAtom = tokenStream.currentToken().t_atom;
8104 /* Non-zero-length XML text scanned. */
8105 JSParseNode *pn2 = xmlAtomNode();
8108 pn->pn_pos.end = pn2->pn_pos.end;
8112 tt = tokenStream.getToken(TSF_OPERAND);
8113 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
8114 if (tt == TOK_XMLETAGO)
8119 pn2 = xmlExpr(JS_FALSE);
8120 pn->pn_xflags |= PNX_CANTFOLD;
8121 } else if (tt == TOK_XMLSTAGO) {
8122 pn2 = xmlElementOrList(JS_FALSE);
8124 pn2->pn_xflags &= ~PNX_XMLROOT;
8125 pn->pn_xflags |= pn2->pn_xflags;
8128 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
8130 pn2 = xmlAtomNode();
8134 pn->pn_pos.end = pn2->pn_pos.end;
8137 tokenStream.setXMLTagMode(true);
8139 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
8144 * Return a PN_LIST node containing an XML or XMLList Initialiser.
8147 Parser::xmlElementOrList(JSBool allowList)
8149 JSParseNode *pn, *pn2, *list;
8151 JSAtom *startAtom, *endAtom;
8153 JS_CHECK_RECURSION(context, return NULL);
8155 JS_ASSERT(tokenStream.currentToken().type == TOK_XMLSTAGO);
8156 pn = ListNode::create(tc);
8160 tokenStream.setXMLTagMode(true);
8161 tt = tokenStream.getToken();
8162 if (tt == TOK_ERROR)
8165 if (tt == TOK_XMLNAME || tt == TOK_LC) {
8167 * XMLElement. Append the tag and its contents, if any, to pn.
8169 pn2 = xmlTagContent(TOK_XMLSTAGO, &startAtom);
8172 tokenStream.matchToken(TOK_XMLSPACE);
8174 tt = tokenStream.getToken();
8175 if (tt == TOK_XMLPTAGC) {
8176 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
8177 if (pn2->pn_type == TOK_XMLSTAGO) {
8179 RecycleTree(pn, tc);
8182 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
8183 pn2->pn_type == TOK_LC);
8185 if (!XML_FOLDABLE(pn2))
8186 pn->pn_xflags |= PNX_CANTFOLD;
8188 pn->pn_type = TOK_XMLPTAGC;
8189 pn->pn_xflags |= PNX_XMLROOT;
8191 /* We had better have a tag-close (>) at this point. */
8192 if (tt != TOK_XMLTAGC) {
8193 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8196 pn2->pn_pos.end = tokenStream.currentToken().pos.end;
8198 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
8199 if (pn2->pn_type != TOK_XMLSTAGO) {
8201 if (!XML_FOLDABLE(pn2))
8202 pn->pn_xflags |= PNX_CANTFOLD;
8204 pn = ListNode::create(tc);
8209 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
8210 pn->pn_type = TOK_XMLELEM;
8211 pn->pn_pos.begin = pn2->pn_pos.begin;
8213 if (!XML_FOLDABLE(pn2))
8214 pn->pn_xflags |= PNX_CANTFOLD;
8215 pn->pn_xflags |= PNX_XMLROOT;
8217 /* Get element contents and delimiting end-tag-open sequence. */
8218 if (!xmlElementContent(pn))
8221 tt = tokenStream.getToken();
8222 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
8223 if (tt != TOK_XMLNAME && tt != TOK_LC) {
8224 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8228 /* Parse end tag; check mismatch at compile-time if we can. */
8229 pn2 = xmlTagContent(TOK_XMLETAGO, &endAtom);
8232 if (pn2->pn_type == TOK_XMLETAGO) {
8233 /* Oops, end tag has attributes! */
8234 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_TAG_SYNTAX);
8237 if (endAtom && startAtom && endAtom != startAtom) {
8238 /* End vs. start tag name mismatch: point to the tag name. */
8239 reportErrorNumber(pn2, JSREPORT_UC | JSREPORT_ERROR, JSMSG_XML_TAG_NAME_MISMATCH,
8240 startAtom->chars());
8244 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
8245 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
8246 list = ListNode::create(tc);
8249 list->pn_type = TOK_XMLETAGO;
8250 list->initList(pn2);
8252 if (!XML_FOLDABLE(pn2)) {
8253 list->pn_xflags |= PNX_CANTFOLD;
8254 pn->pn_xflags |= PNX_CANTFOLD;
8257 tokenStream.matchToken(TOK_XMLSPACE);
8258 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
8261 /* Set pn_op now that pn has been updated to its final value. */
8262 pn->pn_op = JSOP_TOXML;
8263 } else if (allowList && tt == TOK_XMLTAGC) {
8264 /* XMLList Initialiser. */
8265 pn->pn_type = TOK_XMLLIST;
8266 pn->pn_op = JSOP_TOXMLLIST;
8268 pn->pn_xflags |= PNX_XMLROOT;
8269 if (!xmlElementContent(pn))
8272 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
8274 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_NAME_SYNTAX);
8277 tokenStream.setXMLTagMode(false);
8279 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8284 Parser::xmlElementOrListRoot(JSBool allowList)
8287 * Force XML support to be enabled so that comments and CDATA literals
8288 * are recognized, instead of <! followed by -- starting an HTML comment
8289 * to end of line (used in script tags to hide content from old browsers
8290 * that don't recognize <script>).
8292 bool hadXML = tokenStream.hasXML();
8293 tokenStream.setXML(true);
8294 JSParseNode *pn = xmlElementOrList(allowList);
8295 tokenStream.setXML(hadXML);
8300 Parser::parseXMLText(JSObject *chain, bool allowList)
8303 * Push a compiler frame if we have no frames, or if the top frame is a
8304 * lightweight function activation, or if its scope chain doesn't match
8305 * the one passed to us.
8307 JSTreeContext xmltc(this);
8308 xmltc.setScopeChain(chain);
8310 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
8311 tokenStream.setXMLOnlyMode();
8312 TokenKind tt = tokenStream.getToken(TSF_OPERAND);
8315 if (tt != TOK_XMLSTAGO) {
8316 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_XML_MARKUP);
8319 pn = xmlElementOrListRoot(allowList);
8321 tokenStream.setXMLOnlyMode(false);
8326 #endif /* JS_HAS_XMLSUPPORT */
8328 #if JS_HAS_BLOCK_SCOPE
8330 * Check whether blockid is an active scoping statement in tc. This code is
8331 * necessary to qualify tc->decls.lookup() hits in primaryExpr's TOK_NAME case
8332 * (below) where the hits come from Scheme-ish let bindings in for loop heads
8333 * and let blocks and expressions (not let declarations).
8335 * Unlike let declarations ("let as the new var"), which is a kind of letrec
8336 * due to hoisting, let in a for loop head, let block, or let expression acts
8337 * like Scheme's let: initializers are evaluated without the new let bindings
8340 * Name binding analysis is eager with fixups, rather than multi-pass, and let
8341 * bindings push on the front of the tc->decls JSAtomList (either the singular
8342 * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
8343 * scope bindings of the same name.
8345 * This simplifies binding lookup code at the price of a linear search here,
8346 * but only if code uses let (var predominates), and even then this function's
8347 * loop iterates more than once only in crazy cases.
8350 BlockIdInScope(uintN blockid, JSTreeContext *tc)
8352 if (blockid > tc->blockid())
8354 for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
8355 if (stmt->blockid == blockid)
8363 JSParseNode::isConstant()
8380 return (pn_op == JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST);
8387 Parser::primaryExpr(TokenKind tt, JSBool afterDot)
8389 JSParseNode *pn, *pn2, *pn3;
8392 JS_CHECK_RECURSION(context, return NULL);
8396 #if JS_HAS_XML_SUPPORT
8397 if (tokenStream.matchToken(TOK_DBLCOLON, TSF_KEYWORD_IS_NAME)) {
8398 pn2 = NullaryNode::create(tc);
8401 pn2->pn_type = TOK_FUNCTION;
8402 pn = qualifiedSuffix(pn2);
8408 pn = functionExpr();
8418 pn = ListNode::create(tc);
8421 pn->pn_type = TOK_RB;
8422 pn->pn_op = JSOP_NEWINIT;
8425 #if JS_HAS_GENERATORS
8426 pn->pn_blockid = tc->blockidGen;
8429 matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
8431 for (index = 0; ; index++) {
8432 if (index == JS_ARGS_LENGTH_MAX) {
8433 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
8437 tt = tokenStream.peekToken(TSF_OPERAND);
8439 pn->pn_xflags |= PNX_ENDCOMMA;
8443 if (tt == TOK_COMMA) {
8444 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
8445 tokenStream.matchToken(TOK_COMMA);
8446 pn2 = NullaryNode::create(tc);
8447 pn->pn_xflags |= PNX_HOLEY | PNX_NONCONST;
8450 if (pn2 && !pn2->isConstant())
8451 pn->pn_xflags |= PNX_NONCONST;
8457 if (tt != TOK_COMMA) {
8458 /* If we didn't already match TOK_COMMA in above case. */
8459 if (!tokenStream.matchToken(TOK_COMMA))
8464 #if JS_HAS_GENERATORS
8466 * At this point, (index == 0 && pn->pn_count != 0) implies one
8467 * element initialiser was parsed.
8469 * An array comprehension of the form:
8471 * [i * j for (i in o) for (j in p) if (i != j)]
8473 * translates to roughly the following let expression:
8475 * let (array = new Array, i, j) {
8476 * for (i in o) let {
8484 * where array is a nameless block-local variable. The "roughly"
8485 * means that an implementation may optimize away the array.push.
8486 * An array comprehension opens exactly one block scope, no matter
8487 * how many for heads it contains.
8489 * Each let () {...} or for (let ...) ... compiles to:
8491 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
8493 * where <o> is a literal object representing the block scope,
8494 * with <n> properties, naming each var declared in the block.
8496 * Each var declaration in a let-block binds a name in <o> at
8497 * compile time, and allocates a slot on the operand stack at
8498 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
8499 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
8500 * JSOP_FORLOCAL. These ops all have an immediate operand, the
8501 * local slot's stack index from fp->spbase.
8503 * The array comprehension iteration step, array.push(i * j) in
8504 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
8505 * where <array> is the index of array's stack slot.
8507 if (index == 0 && pn->pn_count != 0 && tokenStream.matchToken(TOK_FOR)) {
8508 JSParseNode *pnexp, *pntop;
8510 /* Relabel pn as an array comprehension node. */
8511 pn->pn_type = TOK_ARRAYCOMP;
8514 * Remove the comprehension expression from pn's linked list
8515 * and save it via pnexp. We'll re-install it underneath the
8516 * ARRAYPUSH node after we parse the rest of the comprehension.
8519 JS_ASSERT(pn->pn_count == 1);
8521 pn->pn_tail = &pn->pn_head;
8522 *pn->pn_tail = NULL;
8524 pntop = comprehensionTail(pnexp, pn->pn_blockid,
8525 TOK_ARRAYPUSH, JSOP_ARRAYPUSH);
8530 #endif /* JS_HAS_GENERATORS */
8532 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
8534 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8544 * A map from property names we've seen thus far to a mask of property
8545 * assignment types, stored and retrieved with ALE_SET_INDEX/ALE_INDEX.
8547 JSAutoAtomList seen(tc->parser);
8548 enum AssignmentType {
8551 VALUE = 0x4 | GET | SET
8554 pn = ListNode::create(tc);
8557 pn->pn_type = TOK_RC;
8558 pn->pn_op = JSOP_NEWINIT;
8561 afterComma = JS_FALSE;
8564 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
8567 pn3 = NullaryNode::create(tc);
8570 pn3->pn_dval = tokenStream.currentToken().t_dval;
8571 if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
8576 atom = tokenStream.currentToken().t_atom;
8577 if (atom == context->runtime->atomState.getAtom)
8579 else if (atom == context->runtime->atomState.setAtom)
8584 tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
8585 if (tt == TOK_NAME || tt == TOK_STRING) {
8586 atom = tokenStream.currentToken().t_atom;
8587 pn3 = NameNode::create(atom, tc);
8590 } else if (tt == TOK_NUMBER) {
8591 pn3 = NullaryNode::create(tc);
8594 pn3->pn_dval = tokenStream.currentToken().t_dval;
8595 if (!js_ValueToAtom(context, DoubleValue(pn3->pn_dval), &atom))
8598 tokenStream.ungetToken();
8602 pn->pn_xflags |= PNX_NONCONST;
8604 /* NB: Getter function in { get x(){} } is unnamed. */
8605 pn2 = functionDef(NULL, op == JSOP_SETTER ? SETTER : GETTER, JSFUN_LAMBDA);
8606 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pn2, tc);
8611 atom = tokenStream.currentToken().t_atom;
8612 pn3 = NullaryNode::create(tc);
8615 pn3->pn_atom = atom;
8620 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_PROP_ID);
8625 tt = tokenStream.getToken();
8626 if (tt == TOK_COLON) {
8627 pnval = assignExpr();
8628 if (pnval && !pnval->isConstant())
8629 pn->pn_xflags |= PNX_NONCONST;
8631 #if JS_HAS_DESTRUCTURING_SHORTHAND
8632 if (tt != TOK_COMMA && tt != TOK_RC) {
8634 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_COLON_AFTER_ID);
8636 #if JS_HAS_DESTRUCTURING_SHORTHAND
8640 * Support, e.g., |var {x, y} = o| as destructuring shorthand
8641 * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
8643 tokenStream.ungetToken();
8644 pn->pn_xflags |= PNX_DESTRUCT | PNX_NONCONST;
8646 if (pnval->pn_type == TOK_NAME) {
8647 pnval->pn_arity = PN_NAME;
8648 ((NameNode *)pnval)->initCommon(tc);
8653 pn2 = JSParseNode::newBinaryOrAppend(TOK_COLON, op, pn3, pnval, tc);
8660 * Check for duplicate property names. Duplicate data properties
8661 * only conflict in strict mode. Duplicate getter or duplicate
8662 * setter halves always conflict. A data property conflicts with
8663 * any part of an accessor property.
8665 AssignmentType assignType;
8666 if (op == JSOP_INITPROP) {
8668 } else if (op == JSOP_GETTER) {
8670 } else if (op == JSOP_SETTER) {
8673 JS_NOT_REACHED("bad opcode in object initializer");
8674 assignType = VALUE; /* try to error early */
8677 if (JSAtomListElement *ale = seen.lookup(atom)) {
8678 AssignmentType oldAssignType = AssignmentType(ALE_INDEX(ale));
8679 if ((oldAssignType & assignType) &&
8680 (oldAssignType != VALUE || assignType != VALUE || tc->needStrictChecks()))
8682 JSAutoByteString name;
8683 if (!js_AtomToPrintableString(context, atom, &name))
8686 uintN flags = (oldAssignType == VALUE &&
8687 assignType == VALUE &&
8688 !tc->inStrictMode())
8691 if (!ReportCompileErrorNumber(context, &tokenStream, NULL, flags,
8692 JSMSG_DUPLICATE_PROPERTY, name.ptr()))
8697 ALE_SET_INDEX(ale, assignType | oldAssignType);
8699 ale = seen.add(tc->parser, atom);
8702 ALE_SET_INDEX(ale, assignType);
8705 tt = tokenStream.getToken();
8708 if (tt != TOK_COMMA) {
8709 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_CURLY_AFTER_LIST);
8712 afterComma = JS_TRUE;
8716 pn->pn_pos.end = tokenStream.currentToken().pos.end;
8720 #if JS_HAS_BLOCK_SCOPE
8722 pn = letBlock(JS_FALSE);
8728 #if JS_HAS_SHARP_VARS
8730 pn = UnaryNode::create(tc);
8733 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8734 tt = tokenStream.getToken(TSF_OPERAND);
8735 pn->pn_kid = primaryExpr(tt, JS_FALSE);
8738 if (PN_TYPE(pn->pn_kid) == TOK_USESHARP ||
8739 PN_TYPE(pn->pn_kid) == TOK_DEFSHARP ||
8740 PN_TYPE(pn->pn_kid) == TOK_STRING ||
8741 PN_TYPE(pn->pn_kid) == TOK_NUMBER ||
8742 PN_TYPE(pn->pn_kid) == TOK_PRIMARY) {
8743 reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF);
8746 if (!tc->ensureSharpSlots())
8751 /* Check for forward/dangling references at runtime, to allow eval. */
8752 pn = NullaryNode::create(tc);
8755 if (!tc->ensureSharpSlots())
8757 pn->pn_num = (jsint) tokenStream.currentToken().t_dval;
8759 #endif /* JS_HAS_SHARP_VARS */
8765 pn = parenExpr(&genexp);
8768 pn->pn_parens = true;
8770 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
8774 #if JS_HAS_XML_SUPPORT
8776 pn = qualifiedIdentifier();
8782 pn = attributeIdentifier();
8788 pn = xmlElementOrListRoot(JS_TRUE);
8792 #endif /* JS_HAS_XML_SUPPORT */
8795 #if JS_HAS_SHARP_VARS
8799 #if JS_HAS_XML_SUPPORT
8801 case TOK_XMLCOMMENT:
8804 pn = NullaryNode::create(tc);
8807 pn->pn_atom = tokenStream.currentToken().t_atom;
8808 #if JS_HAS_XML_SUPPORT
8809 if (tt == TOK_XMLPI)
8810 pn->pn_atom2 = tokenStream.currentToken().t_atom2;
8813 pn->pn_op = tokenStream.currentToken().t_op;
8817 pn = NameNode::create(tokenStream.currentToken().t_atom, tc);
8820 JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
8821 pn->pn_op = JSOP_NAME;
8823 if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
8824 pn->pn_atom == context->runtime->atomState.argumentsAtom) {
8826 * Flag arguments usage so we can avoid unsafe optimizations such
8827 * as formal parameter assignment analysis (because of the hated
8828 * feature whereby arguments alias formals). We do this even for
8829 * a reference of the form foo.arguments, which ancient code may
8830 * still use instead of arguments (more hate).
8832 tc->noteArgumentsUse();
8835 * Bind early to JSOP_ARGUMENTS to relieve later code from having
8836 * to do this work (new rule for the emitter to count on).
8838 if (!afterDot && !(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
8839 pn->pn_op = JSOP_ARGUMENTS;
8840 pn->pn_dflags |= PND_BOUND;
8842 } else if ((!afterDot
8843 #if JS_HAS_XML_SUPPORT
8844 || tokenStream.peekToken() == TOK_DBLCOLON
8846 ) && !(tc->flags & TCF_DECL_DESTRUCTURING)) {
8847 JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
8851 JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
8854 #if JS_HAS_BLOCK_SCOPE
8856 * Skip out-of-scope let bindings along an ALE list or hash
8857 * chain. These can happen due to |let (x = x) x| block and
8858 * expression bindings, where the x on the right of = comes
8859 * from an outer scope. See bug 496532.
8861 while (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc)) {
8863 ale = ALE_NEXT(ale);
8864 } while (ale && ALE_ATOM(ale) != pn->pn_atom);
8875 ale = tc->lexdeps.lookup(pn->pn_atom);
8880 * No definition before this use in any lexical scope.
8881 * Add a mapping in tc->lexdeps from pn->pn_atom to a
8882 * new node for the forward-referenced definition. This
8883 * placeholder definition node will be adopted when we
8884 * parse the real defining declaration form, or left as
8885 * a free variable definition if we never see the real
8888 ale = MakePlaceholder(pn, tc);
8894 * In case this is a forward reference to a function,
8895 * we pessimistically set PND_FUNARG if the next token
8896 * is not a left parenthesis.
8898 * If the definition eventually parsed into dn is not a
8899 * function, this flag won't hurt, and if we do parse a
8900 * function with pn's name, then the PND_FUNARG flag is
8901 * necessary for safe context->display-based optimiza-
8902 * tion of the closure's static link.
8904 if (tokenStream.peekToken() != TOK_LP)
8905 dn->pn_dflags |= PND_FUNARG;
8909 JS_ASSERT(dn->pn_defn);
8910 LinkUseToDef(pn, dn, tc);
8912 /* Here we handle the backward function reference case. */
8913 if (tokenStream.peekToken() != TOK_LP)
8914 dn->pn_dflags |= PND_FUNARG;
8916 pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
8917 if (stmt && stmt->type == STMT_WITH)
8918 pn->pn_dflags |= PND_DEOPTIMIZED;
8921 #if JS_HAS_XML_SUPPORT
8922 if (tokenStream.matchToken(TOK_DBLCOLON)) {
8925 * Here primaryExpr is called after . or .. followed by a name
8926 * followed by ::. This is the only case where a keyword after
8927 * . or .. is not treated as a property name.
8929 const KeywordInfo *ki = FindKeyword(pn->pn_atom->charsZ(), pn->pn_atom->length());
8931 if (ki->tokentype != TOK_FUNCTION) {
8932 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_KEYWORD_NOT_NS);
8936 pn->pn_arity = PN_NULLARY;
8937 pn->pn_type = TOK_FUNCTION;
8940 pn = qualifiedSuffix(pn);
8949 pn = NullaryNode::create(tc);
8954 if (context->hasfp()) {
8955 obj = RegExp::createObject(context, context->regExpStatics(),
8956 tokenStream.getTokenbuf().begin(),
8957 tokenStream.getTokenbuf().length(),
8958 tokenStream.currentToken().t_reflags);
8960 obj = RegExp::createObjectNoStatics(context,
8961 tokenStream.getTokenbuf().begin(),
8962 tokenStream.getTokenbuf().length(),
8963 tokenStream.currentToken().t_reflags);
8968 if (!tc->compileAndGo()) {
8973 pn->pn_objbox = tc->parser->newObjectBox(obj);
8977 pn->pn_op = JSOP_REGEXP;
8982 pn = NullaryNode::create(tc);
8985 pn->pn_op = JSOP_DOUBLE;
8986 pn->pn_dval = tokenStream.currentToken().t_dval;
8990 pn = NullaryNode::create(tc);
8993 pn->pn_op = tokenStream.currentToken().t_op;
8997 /* The scanner or one of its subroutines reported the error. */
9001 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
9008 Parser::parenExpr(JSBool *genexp)
9013 JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
9014 begin = tokenStream.currentToken().pos.begin;
9018 pn = bracketedExpr();
9022 #if JS_HAS_GENERATOR_EXPRS
9023 if (tokenStream.matchToken(TOK_FOR)) {
9024 if (pn->pn_type == TOK_YIELD && !pn->pn_parens) {
9025 reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
9028 if (pn->pn_type == TOK_COMMA && !pn->pn_parens) {
9029 reportErrorNumber(pn->last(), JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
9033 pn = generatorExpr(pn);
9036 pn->pn_pos.begin = begin;
9038 if (tokenStream.getToken() != TOK_RP) {
9039 reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_SYNTAX,
9043 pn->pn_pos.end = tokenStream.currentToken().pos.end;
9047 #endif /* JS_HAS_GENERATOR_EXPRS */
9053 * Fold from one constant type to another.
9054 * XXX handles only strings and numbers for now
9057 FoldType(JSContext *cx, JSParseNode *pn, TokenKind type)
9059 if (PN_TYPE(pn) != type) {
9062 if (pn->pn_type == TOK_STRING) {
9064 if (!ValueToNumber(cx, StringValue(ATOM_TO_STRING(pn->pn_atom)), &d))
9067 pn->pn_type = TOK_NUMBER;
9068 pn->pn_op = JSOP_DOUBLE;
9073 if (pn->pn_type == TOK_NUMBER) {
9074 JSString *str = js_NumberToString(cx, pn->pn_dval);
9077 pn->pn_atom = js_AtomizeString(cx, str, 0);
9080 pn->pn_type = TOK_STRING;
9081 pn->pn_op = JSOP_STRING;
9092 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
9093 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
9094 * a successful call to this function.
9097 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
9098 JSParseNode *pn, JSTreeContext *tc)
9103 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
9109 i = js_DoubleToECMAInt32(d);
9110 j = js_DoubleToECMAInt32(d2);
9112 d = (op == JSOP_LSH) ? i << j : i >> j;
9116 j = js_DoubleToECMAInt32(d2);
9118 d = js_DoubleToECMAUint32(d) >> j;
9136 /* XXX MSVC miscompiles such that (NaN == 0) */
9137 if (JSDOUBLE_IS_NaN(d2))
9141 if (d == 0 || JSDOUBLE_IS_NaN(d))
9143 else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
9144 d = js_NegativeInfinity;
9146 d = js_PositiveInfinity;
9163 /* Take care to allow pn1 or pn2 to alias pn. */
9165 RecycleTree(pn1, tc);
9167 RecycleTree(pn2, tc);
9168 pn->pn_type = TOK_NUMBER;
9169 pn->pn_op = JSOP_DOUBLE;
9170 pn->pn_arity = PN_NULLARY;
9175 #if JS_HAS_XML_SUPPORT
9178 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
9181 JSParseNode **pnp, *pn1, *pn2;
9182 JSString *accum, *str;
9185 JS_ASSERT(pn->pn_arity == PN_LIST);
9191 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
9192 if (tt == TOK_XMLETAGO)
9193 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
9194 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
9195 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
9199 * GC Rooting here is tricky: for most of the loop, |accum| is safe via
9200 * the newborn string root. However, when |pn2->pn_type| is TOK_XMLCDATA,
9201 * TOK_XMLCOMMENT, or TOK_XMLPI it is knocked out of the newborn root.
9202 * Therefore, we have to add additonal protection from GC nesting under
9205 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
9206 /* The parser already rejected end-tags with attributes. */
9207 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
9208 switch (pn2->pn_type) {
9217 if (pn2->pn_arity == PN_LIST)
9219 str = ATOM_TO_STRING(pn2->pn_atom);
9223 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
9228 case TOK_XMLCOMMENT:
9229 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
9235 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
9236 ATOM_TO_STRING(pn2->pn_atom2));
9243 JS_ASSERT(*pnp == pn1);
9244 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
9245 (i & 1) ^ (j & 1)) {
9246 #ifdef DEBUG_brendanXXX
9247 printf("1: %d, %d => ", i, j);
9249 FileEscapedString(stdout, accum, 0);
9251 fputs("NULL", stdout);
9252 fputc('\n', stdout);
9254 } else if (accum && pn1 != pn2) {
9255 while (pn1->pn_next != pn2) {
9256 pn1 = RecycleTree(pn1, tc);
9259 pn1->pn_type = TOK_XMLTEXT;
9260 pn1->pn_op = JSOP_STRING;
9261 pn1->pn_arity = PN_NULLARY;
9262 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
9265 JS_ASSERT(pnp != &pn1->pn_next);
9268 pnp = &pn2->pn_next;
9276 AutoStringRooter tvr(cx, accum);
9277 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
9278 ? js_AddAttributePart(cx, i & 1, accum, str)
9279 : js_ConcatStrings(cx, accum, str);
9283 #ifdef DEBUG_brendanXXX
9284 printf("2: %d, %d => ", i, j);
9285 FileEscapedString(stdout, str, 0);
9286 printf(" (%u)\n", str->length());
9295 if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
9296 if (tt == TOK_XMLPTAGC)
9297 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
9298 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
9299 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
9302 accum = js_ConcatStrings(cx, accum, str);
9307 JS_ASSERT(*pnp == pn1);
9308 while (pn1->pn_next) {
9309 pn1 = RecycleTree(pn1, tc);
9312 pn1->pn_type = TOK_XMLTEXT;
9313 pn1->pn_op = JSOP_STRING;
9314 pn1->pn_arity = PN_NULLARY;
9315 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
9318 JS_ASSERT(pnp != &pn1->pn_next);
9322 if (pn1 && pn->pn_count == 1) {
9324 * Only one node under pn, and it has been folded: move pn1 onto pn
9325 * unless pn is an XML root (in which case we need it to tell the code
9326 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
9327 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
9328 * extra "<" and "/>" bracketing at runtime.
9330 if (!(pn->pn_xflags & PNX_XMLROOT)) {
9332 } else if (tt == TOK_XMLPTAGC) {
9333 pn->pn_type = TOK_XMLELEM;
9334 pn->pn_op = JSOP_TOXML;
9340 #endif /* JS_HAS_XML_SUPPORT */
9343 Boolish(JSParseNode *pn)
9345 switch (pn->pn_op) {
9347 return pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval);
9350 return ATOM_TO_STRING(pn->pn_atom)->length() != 0;
9352 #if JS_HAS_GENERATOR_EXPRS
9356 * A generator expression as an if or loop condition has no effects, it
9357 * simply results in a truthy object reference. This condition folding
9358 * is needed for the decompiler. See bug 442342 and bug 443074.
9360 if (pn->pn_count != 1)
9362 JSParseNode *pn2 = pn->pn_head;
9363 if (pn2->pn_type != TOK_FUNCTION)
9365 if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
9386 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, bool inCond)
9388 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
9390 JS_CHECK_RECURSION(cx, return JS_FALSE);
9392 switch (pn->pn_arity) {
9395 uint32 oldflags = tc->flags;
9396 JSFunctionBox *oldlist = tc->functionList;
9398 tc->flags = pn->pn_funbox->tcflags;
9399 tc->functionList = pn->pn_funbox->kids;
9400 if (!js_FoldConstants(cx, pn->pn_body, tc))
9402 pn->pn_funbox->kids = tc->functionList;
9403 tc->flags = oldflags;
9404 tc->functionList = oldlist;
9410 /* Propagate inCond through logical connectives. */
9411 bool cond = inCond && (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND);
9413 /* Don't fold a parenthesized call expression. See bug 537673. */
9414 pn1 = pn2 = pn->pn_head;
9415 if ((pn->pn_type == TOK_LP || pn->pn_type == TOK_NEW) && pn2->pn_parens)
9418 /* Save the list head in pn1 for later use. */
9419 for (; pn2; pn2 = pn2->pn_next) {
9420 if (!js_FoldConstants(cx, pn2, tc, cond))
9427 /* Any kid may be null (e.g. for (;;)). */
9431 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_IF))
9434 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_FORHEAD))
9436 if (pn->pn_type == TOK_FORHEAD && pn2->pn_op == JSOP_TRUE) {
9437 RecycleTree(pn2, tc);
9441 if (pn3 && !js_FoldConstants(cx, pn3, tc))
9449 /* Propagate inCond through logical connectives. */
9450 if (pn->pn_type == TOK_OR || pn->pn_type == TOK_AND) {
9451 if (!js_FoldConstants(cx, pn1, tc, inCond))
9453 if (!js_FoldConstants(cx, pn2, tc, inCond))
9458 /* First kid may be null (for default case in switch). */
9459 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_type == TOK_WHILE))
9461 if (!js_FoldConstants(cx, pn2, tc, pn->pn_type == TOK_DO))
9469 * Kludge to deal with typeof expressions: because constant folding
9470 * can turn an expression into a name node, we have to check here,
9471 * before folding, to see if we should throw undefined name errors.
9473 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
9474 * null. This assumption does not hold true for other unary
9477 if (pn->pn_op == JSOP_TYPEOF && pn1->pn_type != TOK_NAME)
9478 pn->pn_op = JSOP_TYPEOFEXPR;
9480 if (pn1 && !js_FoldConstants(cx, pn1, tc, pn->pn_op == JSOP_NOT))
9486 * Skip pn1 down along a chain of dotted member expressions to avoid
9487 * excessive recursion. Our only goal here is to fold constants (if
9488 * any) in the primary expression operand to the left of the first
9493 while (pn1 && pn1->pn_arity == PN_NAME && !pn1->pn_used)
9495 if (pn1 && !js_FoldConstants(cx, pn1, tc))
9502 if (!js_FoldConstants(cx, pn1, tc))
9510 switch (pn->pn_type) {
9512 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
9517 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
9518 switch (pn1->pn_type) {
9520 if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
9524 if (ATOM_TO_STRING(pn1->pn_atom)->length() == 0)
9528 if (pn1->pn_op == JSOP_TRUE)
9530 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
9536 /* Early return to dodge common code that copies pn2 to pn. */
9540 #if JS_HAS_GENERATOR_EXPRS
9541 /* Don't fold a trailing |if (0)| in a generator expression. */
9542 if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
9546 if (pn2 && !pn2->pn_defn)
9548 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
9550 * False condition and no else, or an empty then-statement was
9551 * moved up over pn. Either way, make pn an empty block (not an
9552 * empty statement, which does not decompile, even when labeled).
9553 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
9554 * or an empty statement for a child.
9556 pn->pn_type = TOK_LC;
9557 pn->pn_arity = PN_LIST;
9560 RecycleTree(pn2, tc);
9561 if (pn3 && pn3 != pn2)
9562 RecycleTree(pn3, tc);
9568 if (pn->pn_arity == PN_LIST) {
9569 JSParseNode **pnp = &pn->pn_head;
9570 JS_ASSERT(*pnp == pn1);
9572 int cond = Boolish(pn1);
9573 if (cond == (pn->pn_type == TOK_OR)) {
9574 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
9576 RecycleTree(pn2, tc);
9579 pn1->pn_next = NULL;
9583 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9584 if (pn->pn_count == 1)
9586 *pnp = pn1->pn_next;
9587 RecycleTree(pn1, tc);
9590 pnp = &pn1->pn_next;
9592 } while ((pn1 = *pnp) != NULL);
9594 // We may have to change arity from LIST to BINARY.
9596 if (pn->pn_count == 2) {
9598 pn1->pn_next = NULL;
9599 JS_ASSERT(!pn2->pn_next);
9600 pn->pn_arity = PN_BINARY;
9603 } else if (pn->pn_count == 1) {
9605 RecycleTree(pn1, tc);
9608 int cond = Boolish(pn1);
9609 if (cond == (pn->pn_type == TOK_OR)) {
9610 RecycleTree(pn2, tc);
9612 } else if (cond != -1) {
9613 JS_ASSERT(cond == (pn->pn_type == TOK_AND));
9614 RecycleTree(pn1, tc);
9623 * Compound operators such as *= should be subject to folding, in case
9624 * the left-hand side is constant, and so that the decompiler produces
9625 * the same string that you get from decompiling a script or function
9626 * compiled from that same string. As with +, += is special.
9628 if (pn->pn_op == JSOP_NOP)
9630 if (pn->pn_op != JSOP_ADD)
9635 if (pn->pn_arity == PN_LIST) {
9636 size_t length, length2;
9638 JSString *str, *str2;
9641 * Any string literal term with all others number or string means
9642 * this is a concatenation. If any term is not a string or number
9643 * literal, we can't fold.
9645 JS_ASSERT(pn->pn_count > 2);
9646 if (pn->pn_xflags & PNX_CANTFOLD)
9648 if (pn->pn_xflags != PNX_STRCAT)
9651 /* Ok, we're concatenating: convert non-string constant operands. */
9653 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9654 if (!FoldType(cx, pn2, TOK_STRING))
9656 /* XXX fold only if all operands convert to string */
9657 if (pn2->pn_type != TOK_STRING)
9659 length += ATOM_TO_STRING(pn2->pn_atom)->flatLength();
9662 /* Allocate a new buffer and string descriptor for the result. */
9663 chars = (jschar *) cx->malloc((length + 1) * sizeof(jschar));
9667 str = js_NewString(cx, chars, length);
9673 /* Fill the buffer, advancing chars and recycling kids as we go. */
9674 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
9675 str2 = ATOM_TO_STRING(pn2->pn_atom);
9676 length2 = str2->flatLength();
9677 js_strncpy(chars, str2->flatChars(), length2);
9680 JS_ASSERT(*chars == 0);
9682 /* Atomize the result string and mutate pn to refer to it. */
9683 pn->pn_atom = js_AtomizeString(cx, str, 0);
9686 pn->pn_type = TOK_STRING;
9687 pn->pn_op = JSOP_STRING;
9688 pn->pn_arity = PN_NULLARY;
9692 /* Handle a binary string concatenation. */
9693 JS_ASSERT(pn->pn_arity == PN_BINARY);
9694 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
9695 JSString *left, *right, *str;
9697 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
9701 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
9703 left = ATOM_TO_STRING(pn1->pn_atom);
9704 right = ATOM_TO_STRING(pn2->pn_atom);
9705 str = js_ConcatStrings(cx, left, right);
9708 pn->pn_atom = js_AtomizeString(cx, str, 0);
9711 pn->pn_type = TOK_STRING;
9712 pn->pn_op = JSOP_STRING;
9713 pn->pn_arity = PN_NULLARY;
9714 RecycleTree(pn1, tc);
9715 RecycleTree(pn2, tc);
9719 /* Can't concatenate string literals, let's try numbers. */
9727 if (pn->pn_arity == PN_LIST) {
9728 JS_ASSERT(pn->pn_count > 2);
9729 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9730 if (!FoldType(cx, pn2, TOK_NUMBER))
9733 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
9734 /* XXX fold only if all operands convert to number */
9735 if (pn2->pn_type != TOK_NUMBER)
9739 JSOp op = PN_OP(pn);
9743 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
9745 while ((pn2 = pn3) != NULL) {
9747 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
9752 JS_ASSERT(pn->pn_arity == PN_BINARY);
9753 if (!FoldType(cx, pn1, TOK_NUMBER) ||
9754 !FoldType(cx, pn2, TOK_NUMBER)) {
9757 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
9758 if (!FoldBinaryNumeric(cx, PN_OP(pn), pn1, pn2, pn, tc))
9765 if (pn1->pn_type == TOK_NUMBER) {
9768 /* Operate on one numeric constant. */
9770 switch (pn->pn_op) {
9772 d = ~js_DoubleToECMAInt32(d);
9783 pn->pn_type = TOK_PRIMARY;
9784 pn->pn_op = (d == 0 || JSDOUBLE_IS_NaN(d)) ? JSOP_TRUE : JSOP_FALSE;
9785 pn->pn_arity = PN_NULLARY;
9789 /* Return early to dodge the common TOK_NUMBER code. */
9792 pn->pn_type = TOK_NUMBER;
9793 pn->pn_op = JSOP_DOUBLE;
9794 pn->pn_arity = PN_NULLARY;
9796 RecycleTree(pn1, tc);
9797 } else if (pn1->pn_type == TOK_PRIMARY) {
9798 if (pn->pn_op == JSOP_NOT &&
9799 (pn1->pn_op == JSOP_TRUE ||
9800 pn1->pn_op == JSOP_FALSE)) {
9802 pn->pn_op = (pn->pn_op == JSOP_TRUE) ? JSOP_FALSE : JSOP_TRUE;
9803 RecycleTree(pn1, tc);
9808 #if JS_HAS_XML_SUPPORT
9815 if (pn->pn_arity == PN_LIST) {
9816 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
9817 if (!FoldXMLConstants(cx, pn, tc))
9823 if (pn1->pn_type == TOK_XMLNAME) {
9824 JSObjectBox *xmlbox;
9826 Value v = StringValue(ATOM_TO_STRING(pn1->pn_atom));
9827 if (!js_ToAttributeName(cx, &v))
9829 JS_ASSERT(v.isObject());
9831 xmlbox = tc->parser->newObjectBox(&v.toObject());
9835 pn->pn_type = TOK_XMLNAME;
9836 pn->pn_op = JSOP_OBJECT;
9837 pn->pn_arity = PN_NULLARY;
9838 pn->pn_objbox = xmlbox;
9839 RecycleTree(pn1, tc);
9842 #endif /* JS_HAS_XML_SUPPORT */
9848 int cond = Boolish(pn);
9851 * We can turn function nodes into constant nodes here, but mutating function
9852 * nodes is tricky --- in particular, mutating a function node that appears on
9853 * a method list corrupts the method list. However, methods are M's in
9854 * statements of the form 'this.foo = M;', which we never fold, so we're okay.
9856 PrepareNodeForMutation(pn, tc);
9857 pn->pn_type = TOK_PRIMARY;
9858 pn->pn_op = cond ? JSOP_TRUE : JSOP_FALSE;
9859 pn->pn_arity = PN_NULLARY;