/[jscoverage]/trunk/js/jsparse.cpp
ViewVC logotype

Annotation of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (hide annotations)
Sun Sep 12 15:13:23 2010 UTC (11 years, 9 months ago) by siliconforks
File size: 302281 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

1 siliconforks 507 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 siliconforks 332 * vim: set ts=8 sw=4 et tw=99:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
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/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
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.
24     *
25     * Contributor(s):
26     *
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.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JS parser.
43     *
44     * This is a recursive-descent parser for the JavaScript language specified by
45     * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
46     * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
47     * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
48     * After tree construction, it rewrites trees to fold constants and evaluate
49     * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
50     * generate bytecode.
51     *
52     * This parser attempts no error recovery.
53     */
54     #include <stdlib.h>
55     #include <string.h>
56     #include <math.h>
57     #include "jstypes.h"
58 siliconforks 507 #include "jsstdint.h"
59 siliconforks 332 #include "jsarena.h" /* Added by JSIFY */
60     #include "jsutil.h" /* Added by JSIFY */
61     #include "jsapi.h"
62     #include "jsarray.h"
63     #include "jsatom.h"
64     #include "jscntxt.h"
65     #include "jsversion.h"
66     #include "jsemit.h"
67     #include "jsfun.h"
68     #include "jsinterp.h"
69     #include "jsiter.h"
70     #include "jslock.h"
71     #include "jsnum.h"
72     #include "jsobj.h"
73     #include "jsopcode.h"
74     #include "jsparse.h"
75     #include "jsscan.h"
76     #include "jsscope.h"
77     #include "jsscript.h"
78     #include "jsstr.h"
79     #include "jsstaticcheck.h"
80 siliconforks 507 #include "jslibmath.h"
81     #include "jsvector.h"
82 siliconforks 332
83     #if JS_HAS_XML_SUPPORT
84     #include "jsxml.h"
85     #endif
86    
87     #if JS_HAS_DESTRUCTURING
88     #include "jsdhash.h"
89     #endif
90    
91     /*
92     * Asserts to verify assumptions behind pn_ macros.
93     */
94 siliconforks 460 #define pn_offsetof(m) offsetof(JSParseNode, m)
95 siliconforks 332
96 siliconforks 460 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
97     JS_STATIC_ASSERT(pn_offsetof(pn_u.name.atom) == pn_offsetof(pn_u.apair.atom));
98    
99     #undef pn_offsetof
100    
101 siliconforks 332 /*
102     * JS parsers, from lowest to highest precedence.
103     *
104     * Each parser takes a context, a token stream, and a tree context struct.
105     * Each returns a parse node tree or null on error.
106     */
107    
108     typedef JSParseNode *
109     JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
110    
111     typedef JSParseNode *
112 siliconforks 460 JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
113     bool inLetHead);
114    
115     typedef JSParseNode *
116 siliconforks 332 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
117     JSBool allowCallSyntax);
118    
119     typedef JSParseNode *
120     JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
121     JSTokenType tt, JSBool afterDot);
122    
123     typedef JSParseNode *
124     JSParenParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
125     JSParseNode *pn1, JSBool *genexp);
126    
127     static JSParser FunctionStmt;
128     static JSParser FunctionExpr;
129     static JSParser Statements;
130     static JSParser Statement;
131 siliconforks 460 static JSVariablesParser Variables;
132 siliconforks 332 static JSParser Expr;
133     static JSParser AssignExpr;
134     static JSParser CondExpr;
135     static JSParser OrExpr;
136     static JSParser AndExpr;
137     static JSParser BitOrExpr;
138     static JSParser BitXorExpr;
139     static JSParser BitAndExpr;
140     static JSParser EqExpr;
141     static JSParser RelExpr;
142     static JSParser ShiftExpr;
143     static JSParser AddExpr;
144     static JSParser MulExpr;
145     static JSParser UnaryExpr;
146     static JSMemberParser MemberExpr;
147     static JSPrimaryParser PrimaryExpr;
148     static JSParenParser ParenExpr;
149    
150     /*
151     * Insist that the next token be of type tt, or report errno and return null.
152     * NB: this macro uses cx and ts from its lexical environment.
153     */
154     #define MUST_MATCH_TOKEN(tt, errno) \
155     JS_BEGIN_MACRO \
156     if (js_GetToken(cx, ts) != tt) { \
157     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
158     return NULL; \
159     } \
160     JS_END_MACRO
161    
162     #ifdef METER_PARSENODES
163     static uint32 parsenodes = 0;
164     static uint32 maxparsenodes = 0;
165     static uint32 recyclednodes = 0;
166     #endif
167    
168 siliconforks 460 void
169     JSParseNode::become(JSParseNode *pn2)
170 siliconforks 332 {
171 siliconforks 460 JS_ASSERT(!pn_defn);
172     JS_ASSERT(!pn2->pn_defn);
173 siliconforks 332
174 siliconforks 460 JS_ASSERT(!pn_used);
175     if (pn2->pn_used) {
176     JSParseNode **pnup = &pn2->pn_lexdef->dn_uses;
177     while (*pnup != pn2)
178     pnup = &(*pnup)->pn_link;
179     *pnup = this;
180     pn_link = pn2->pn_link;
181     pn_used = true;
182     pn2->pn_link = NULL;
183     pn2->pn_used = false;
184 siliconforks 332 }
185    
186 siliconforks 460 /* If this is a function node fix up the pn_funbox->node back-pointer. */
187     if (PN_TYPE(pn2) == TOK_FUNCTION && pn2->pn_arity == PN_FUNC)
188     pn2->pn_funbox->node = this;
189    
190     pn_type = pn2->pn_type;
191     pn_op = pn2->pn_op;
192     pn_arity = pn2->pn_arity;
193 siliconforks 507 pn_parens = pn2->pn_parens;
194 siliconforks 460 pn_u = pn2->pn_u;
195     pn2->clear();
196     }
197    
198     void
199     JSParseNode::clear()
200     {
201     pn_type = TOK_EOF;
202     pn_op = JSOP_NOP;
203     pn_used = pn_defn = false;
204     pn_arity = PN_NULLARY;
205 siliconforks 507 pn_parens = false;
206 siliconforks 460 }
207    
208     bool
209     JSCompiler::init(const jschar *base, size_t length,
210     FILE *fp, const char *filename, uintN lineno)
211     {
212     JSContext *cx = context;
213    
214     tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
215 siliconforks 507 if (!tokenStream.init(cx, base, length, fp, filename, lineno)) {
216 siliconforks 460 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
217     return false;
218     }
219    
220 siliconforks 332 /* Root atoms and objects allocated for the parsed tree. */
221     JS_KEEP_ATOMS(cx->runtime);
222 siliconforks 460 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
223     return true;
224 siliconforks 332 }
225    
226 siliconforks 460 JSCompiler::~JSCompiler()
227 siliconforks 332 {
228 siliconforks 460 JSContext *cx = context;
229    
230     if (principals)
231     JSPRINCIPALS_DROP(cx, principals);
232     JS_ASSERT(tempRoot.u.compiler == this);
233     JS_POP_TEMP_ROOT(cx, &tempRoot);
234 siliconforks 332 JS_UNKEEP_ATOMS(cx->runtime);
235 siliconforks 507 tokenStream.close(cx);
236 siliconforks 460 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
237 siliconforks 332 }
238    
239     void
240 siliconforks 460 JSCompiler::setPrincipals(JSPrincipals *prin)
241 siliconforks 332 {
242 siliconforks 460 JS_ASSERT(!principals);
243     if (prin)
244     JSPRINCIPALS_HOLD(context, prin);
245     principals = prin;
246 siliconforks 332 }
247    
248 siliconforks 460 JSObjectBox *
249     JSCompiler::newObjectBox(JSObject *obj)
250 siliconforks 332 {
251 siliconforks 460 JS_ASSERT(obj);
252 siliconforks 332
253     /*
254     * We use JSContext.tempPool to allocate parsed objects and place them on
255     * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
256     * containing the entries must be alive until we are done with scanning,
257     * parsing and code generation for the whole script or top-level function.
258     */
259 siliconforks 460 JSObjectBox *objbox;
260     JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
261     if (!objbox) {
262     js_ReportOutOfScriptQuota(context);
263     return NULL;
264     }
265     objbox->traceLink = traceListHead;
266     traceListHead = objbox;
267     objbox->emitLink = NULL;
268     objbox->object = obj;
269     return objbox;
270     }
271    
272     JSFunctionBox *
273     JSCompiler::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
274     {
275 siliconforks 332 JS_ASSERT(obj);
276 siliconforks 460 JS_ASSERT(HAS_FUNCTION_CLASS(obj));
277    
278     /*
279     * We use JSContext.tempPool to allocate parsed objects and place them on
280     * a list in JSTokenStream to ensure GC safety. Thus the tempPool arenas
281     * containing the entries must be alive until we are done with scanning,
282     * parsing and code generation for the whole script or top-level function.
283     */
284     JSFunctionBox *funbox;
285     JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
286     if (!funbox) {
287     js_ReportOutOfScriptQuota(context);
288 siliconforks 332 return NULL;
289     }
290 siliconforks 460 funbox->traceLink = traceListHead;
291     traceListHead = funbox;
292     funbox->emitLink = NULL;
293     funbox->object = obj;
294     funbox->node = fn;
295     funbox->siblings = tc->functionList;
296     tc->functionList = funbox;
297     ++tc->compiler->functionCount;
298     funbox->kids = NULL;
299     funbox->parent = tc->funbox;
300     funbox->queued = false;
301     funbox->inLoop = false;
302     for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
303     if (STMT_IS_LOOP(stmt)) {
304     funbox->inLoop = true;
305     break;
306     }
307     }
308     funbox->level = tc->staticLevel;
309     funbox->tcflags = TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
310     return funbox;
311 siliconforks 332 }
312    
313     void
314 siliconforks 460 JSCompiler::trace(JSTracer *trc)
315 siliconforks 332 {
316 siliconforks 460 JSObjectBox *objbox;
317 siliconforks 332
318 siliconforks 460 JS_ASSERT(tempRoot.u.compiler == this);
319     objbox = traceListHead;
320     while (objbox) {
321     JS_CALL_OBJECT_TRACER(trc, objbox->object, "parser.object");
322     objbox = objbox->traceLink;
323 siliconforks 332 }
324     }
325    
326 siliconforks 460 static void
327     UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc);
328    
329     static void
330     UnlinkFunctionBox(JSParseNode *pn, JSTreeContext *tc)
331     {
332     JSFunctionBox *funbox = pn->pn_funbox;
333     if (funbox) {
334     JS_ASSERT(funbox->node == pn);
335     funbox->node = NULL;
336    
337     JSFunctionBox **funboxp = &tc->functionList;
338     while (*funboxp) {
339     if (*funboxp == funbox) {
340     *funboxp = funbox->siblings;
341     break;
342     }
343     funboxp = &(*funboxp)->siblings;
344     }
345    
346     uint16 oldflags = tc->flags;
347     JSFunctionBox *oldlist = tc->functionList;
348    
349     tc->flags = (uint16) funbox->tcflags;
350     tc->functionList = funbox->kids;
351     UnlinkFunctionBoxes(pn->pn_body, tc);
352     funbox->kids = tc->functionList;
353     tc->flags = oldflags;
354     tc->functionList = oldlist;
355    
356     // FIXME: use a funbox freelist (consolidate aleFreeList and nodeList).
357     pn->pn_funbox = NULL;
358     }
359     }
360    
361     static void
362     UnlinkFunctionBoxes(JSParseNode *pn, JSTreeContext *tc)
363     {
364     if (pn) {
365     switch (pn->pn_arity) {
366     case PN_NULLARY:
367     return;
368     case PN_UNARY:
369     UnlinkFunctionBoxes(pn->pn_kid, tc);
370     return;
371     case PN_BINARY:
372     UnlinkFunctionBoxes(pn->pn_left, tc);
373     UnlinkFunctionBoxes(pn->pn_right, tc);
374     return;
375     case PN_TERNARY:
376     UnlinkFunctionBoxes(pn->pn_kid1, tc);
377     UnlinkFunctionBoxes(pn->pn_kid2, tc);
378     UnlinkFunctionBoxes(pn->pn_kid3, tc);
379     return;
380     case PN_LIST:
381     for (JSParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
382     UnlinkFunctionBoxes(pn2, tc);
383     return;
384     case PN_FUNC:
385     UnlinkFunctionBox(pn, tc);
386     return;
387     case PN_NAME:
388     UnlinkFunctionBoxes(pn->maybeExpr(), tc);
389     return;
390     case PN_NAMESET:
391     UnlinkFunctionBoxes(pn->pn_tree, tc);
392     }
393     }
394     }
395    
396     static void
397     RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc);
398    
399 siliconforks 332 static JSParseNode *
400     RecycleTree(JSParseNode *pn, JSTreeContext *tc)
401     {
402 siliconforks 460 JSParseNode *next, **head;
403 siliconforks 332
404     if (!pn)
405     return NULL;
406    
407     /* Catch back-to-back dup recycles. */
408 siliconforks 460 JS_ASSERT(pn != tc->compiler->nodeList);
409 siliconforks 332 next = pn->pn_next;
410 siliconforks 460 if (pn->pn_used || pn->pn_defn) {
411     /*
412     * JSAtomLists own definition nodes along with their used-node chains.
413     * Defer recycling such nodes until we unwind to top level to avoid
414     * linkage overhead or (alternatively) unlinking runtime complexity.
415     * Yes, this means dead code can contribute to static analysis results!
416     *
417     * Do recycle kids here, since they are no longer needed.
418     */
419     pn->pn_next = NULL;
420     RecycleFuncNameKids(pn, tc);
421     } else {
422     UnlinkFunctionBoxes(pn, tc);
423     head = &tc->compiler->nodeList;
424     pn->pn_next = *head;
425     *head = pn;
426 siliconforks 332 #ifdef METER_PARSENODES
427 siliconforks 460 recyclednodes++;
428 siliconforks 332 #endif
429 siliconforks 460 }
430 siliconforks 332 return next;
431     }
432    
433 siliconforks 460 static void
434     RecycleFuncNameKids(JSParseNode *pn, JSTreeContext *tc)
435     {
436     switch (pn->pn_arity) {
437     case PN_FUNC:
438     UnlinkFunctionBox(pn, tc);
439     /* FALL THROUGH */
440    
441     case PN_NAME:
442     /*
443     * Only a definition node might have a non-null strong pn_expr link
444     * to recycle, but we test !pn_used to handle PN_FUNC fall through.
445     * Every node with the pn_used flag set has a non-null pn_lexdef
446     * weak reference to its definition node.
447     */
448     if (!pn->pn_used && pn->pn_expr) {
449     RecycleTree(pn->pn_expr, tc);
450     pn->pn_expr = NULL;
451     }
452     break;
453    
454     default:
455     JS_ASSERT(PN_TYPE(pn) == TOK_FUNCTION);
456     }
457     }
458    
459 siliconforks 332 static JSParseNode *
460 siliconforks 460 NewOrRecycledNode(JSTreeContext *tc)
461 siliconforks 332 {
462 siliconforks 460 JSParseNode *pn, *pn2;
463 siliconforks 332
464 siliconforks 460 pn = tc->compiler->nodeList;
465 siliconforks 332 if (!pn) {
466 siliconforks 460 JSContext *cx = tc->compiler->context;
467    
468 siliconforks 332 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
469     if (!pn)
470     js_ReportOutOfScriptQuota(cx);
471     } else {
472 siliconforks 460 tc->compiler->nodeList = pn->pn_next;
473 siliconforks 332
474     /* Recycle immediate descendents only, to save work and working set. */
475     switch (pn->pn_arity) {
476     case PN_FUNC:
477     RecycleTree(pn->pn_body, tc);
478     break;
479     case PN_LIST:
480 siliconforks 460 pn2 = pn->pn_head;
481     if (pn2) {
482     while (pn2 && !pn2->pn_used && !pn2->pn_defn)
483     pn2 = pn2->pn_next;
484     if (pn2) {
485     pn2 = pn->pn_head;
486     do {
487     pn2 = RecycleTree(pn2, tc);
488     } while (pn2);
489     } else {
490     *pn->pn_tail = tc->compiler->nodeList;
491     tc->compiler->nodeList = pn->pn_head;
492 siliconforks 332 #ifdef METER_PARSENODES
493 siliconforks 460 recyclednodes += pn->pn_count;
494 siliconforks 332 #endif
495 siliconforks 460 break;
496     }
497 siliconforks 332 }
498     break;
499     case PN_TERNARY:
500     RecycleTree(pn->pn_kid1, tc);
501     RecycleTree(pn->pn_kid2, tc);
502     RecycleTree(pn->pn_kid3, tc);
503     break;
504     case PN_BINARY:
505     if (pn->pn_left != pn->pn_right)
506     RecycleTree(pn->pn_left, tc);
507     RecycleTree(pn->pn_right, tc);
508     break;
509     case PN_UNARY:
510     RecycleTree(pn->pn_kid, tc);
511     break;
512     case PN_NAME:
513 siliconforks 460 if (!pn->pn_used)
514     RecycleTree(pn->pn_expr, tc);
515 siliconforks 332 break;
516     case PN_NULLARY:
517     break;
518     }
519     }
520     if (pn) {
521     #ifdef METER_PARSENODES
522     parsenodes++;
523     if (parsenodes - recyclednodes > maxparsenodes)
524     maxparsenodes = parsenodes - recyclednodes;
525     #endif
526 siliconforks 460 pn->pn_used = pn->pn_defn = false;
527 siliconforks 332 memset(&pn->pn_u, 0, sizeof pn->pn_u);
528     pn->pn_next = NULL;
529     }
530     return pn;
531     }
532    
533 siliconforks 460 static inline void
534     InitParseNode(JSParseNode *pn, JSTokenType type, JSOp op, JSParseNodeArity arity)
535     {
536     pn->pn_type = type;
537     pn->pn_op = op;
538     pn->pn_arity = arity;
539 siliconforks 507 pn->pn_parens = false;
540 siliconforks 460 JS_ASSERT(!pn->pn_used);
541     JS_ASSERT(!pn->pn_defn);
542     pn->pn_next = pn->pn_link = NULL;
543     }
544    
545 siliconforks 332 /*
546 siliconforks 460 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
547     * temporary arena.
548 siliconforks 332 */
549     static JSParseNode *
550 siliconforks 460 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
551 siliconforks 332 {
552     JSParseNode *pn;
553     JSToken *tp;
554    
555 siliconforks 460 pn = NewOrRecycledNode(tc);
556 siliconforks 332 if (!pn)
557     return NULL;
558 siliconforks 460 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
559     InitParseNode(pn, tp->type, JSOP_NOP, arity);
560 siliconforks 332 pn->pn_pos = tp->pos;
561     return pn;
562     }
563    
564 siliconforks 460 static inline void
565     InitNameNodeCommon(JSParseNode *pn, JSTreeContext *tc)
566     {
567     pn->pn_expr = NULL;
568     pn->pn_cookie = FREE_UPVAR_COOKIE;
569     pn->pn_dflags = tc->atTopLevel() ? PND_TOPLEVEL : 0;
570     if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
571     pn->pn_dflags |= PND_BLOCKCHILD;
572     pn->pn_blockid = tc->blockid();
573     }
574    
575 siliconforks 332 static JSParseNode *
576 siliconforks 460 NewNameNode(JSContext *cx, JSTokenStream *ts, JSAtom *atom, JSTreeContext *tc)
577     {
578     JSParseNode *pn;
579    
580     pn = NewParseNode(PN_NAME, tc);
581     if (pn) {
582     pn->pn_atom = atom;
583     InitNameNodeCommon(pn, tc);
584     }
585     return pn;
586     }
587    
588     static JSParseNode *
589     NewBinary(JSTokenType tt, JSOp op, JSParseNode *left, JSParseNode *right,
590 siliconforks 332 JSTreeContext *tc)
591     {
592     JSParseNode *pn, *pn1, *pn2;
593    
594     if (!left || !right)
595     return NULL;
596    
597     /*
598     * Flatten a left-associative (left-heavy) tree of a given operator into
599     * a list, to reduce js_FoldConstants and js_EmitTree recursion.
600     */
601 siliconforks 460 if (PN_TYPE(left) == tt &&
602     PN_OP(left) == op &&
603 siliconforks 332 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
604     if (left->pn_arity != PN_LIST) {
605     pn1 = left->pn_left, pn2 = left->pn_right;
606     left->pn_arity = PN_LIST;
607 siliconforks 507 left->pn_parens = false;
608 siliconforks 460 left->initList(pn1);
609     left->append(pn2);
610 siliconforks 332 if (tt == TOK_PLUS) {
611     if (pn1->pn_type == TOK_STRING)
612 siliconforks 460 left->pn_xflags |= PNX_STRCAT;
613 siliconforks 332 else if (pn1->pn_type != TOK_NUMBER)
614 siliconforks 460 left->pn_xflags |= PNX_CANTFOLD;
615 siliconforks 332 if (pn2->pn_type == TOK_STRING)
616 siliconforks 460 left->pn_xflags |= PNX_STRCAT;
617 siliconforks 332 else if (pn2->pn_type != TOK_NUMBER)
618 siliconforks 460 left->pn_xflags |= PNX_CANTFOLD;
619 siliconforks 332 }
620     }
621 siliconforks 460 left->append(right);
622 siliconforks 332 left->pn_pos.end = right->pn_pos.end;
623     if (tt == TOK_PLUS) {
624     if (right->pn_type == TOK_STRING)
625 siliconforks 460 left->pn_xflags |= PNX_STRCAT;
626 siliconforks 332 else if (right->pn_type != TOK_NUMBER)
627 siliconforks 460 left->pn_xflags |= PNX_CANTFOLD;
628 siliconforks 332 }
629     return left;
630     }
631    
632     /*
633     * Fold constant addition immediately, to conserve node space and, what's
634     * more, so js_FoldConstants never sees mixed addition and concatenation
635     * operations with more than one leading non-string operand in a PN_LIST
636     * generated for expressions such as 1 + 2 + "pt" (which should evaluate
637     * to "3pt", not "12pt").
638     */
639     if (tt == TOK_PLUS &&
640     left->pn_type == TOK_NUMBER &&
641     right->pn_type == TOK_NUMBER) {
642     left->pn_dval += right->pn_dval;
643     left->pn_pos.end = right->pn_pos.end;
644     RecycleTree(right, tc);
645     return left;
646     }
647    
648 siliconforks 460 pn = NewOrRecycledNode(tc);
649 siliconforks 332 if (!pn)
650     return NULL;
651 siliconforks 460 InitParseNode(pn, tt, op, PN_BINARY);
652 siliconforks 332 pn->pn_pos.begin = left->pn_pos.begin;
653     pn->pn_pos.end = right->pn_pos.end;
654     pn->pn_left = left;
655     pn->pn_right = right;
656     return pn;
657     }
658    
659     #if JS_HAS_GETTER_SETTER
660     static JSTokenType
661     CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
662     {
663     JSAtom *atom;
664     JSRuntime *rt;
665     JSOp op;
666     const char *name;
667    
668     JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
669     atom = CURRENT_TOKEN(ts).t_atom;
670     rt = cx->runtime;
671     if (atom == rt->atomState.getterAtom)
672     op = JSOP_GETTER;
673     else if (atom == rt->atomState.setterAtom)
674     op = JSOP_SETTER;
675     else
676     return TOK_NAME;
677     if (js_PeekTokenSameLine(cx, ts) != tt)
678     return TOK_NAME;
679     (void) js_GetToken(cx, ts);
680     if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
681     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
682     JSMSG_BAD_GETTER_OR_SETTER,
683     (op == JSOP_GETTER)
684     ? js_getter_str
685     : js_setter_str);
686     return TOK_ERROR;
687     }
688     CURRENT_TOKEN(ts).t_op = op;
689     if (JS_HAS_STRICT_OPTION(cx)) {
690     name = js_AtomToPrintableString(cx, atom);
691     if (!name ||
692     !js_ReportCompileErrorNumber(cx, ts, NULL,
693     JSREPORT_WARNING | JSREPORT_STRICT,
694     JSMSG_DEPRECATED_USAGE,
695     name)) {
696     return TOK_ERROR;
697     }
698     }
699     return tt;
700     }
701     #endif
702    
703 siliconforks 460 static bool
704     GenerateBlockId(JSTreeContext *tc, uint32& blockid)
705     {
706     if (tc->blockidGen == JS_BIT(20)) {
707     JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
708     JSMSG_NEED_DIET, "program");
709     return false;
710     }
711     blockid = tc->blockidGen++;
712     return true;
713     }
714    
715     static bool
716     GenerateBlockIdForStmtNode(JSParseNode *pn, JSTreeContext *tc)
717     {
718     JS_ASSERT(tc->topStmt);
719     JS_ASSERT(STMT_MAYBE_SCOPE(tc->topStmt));
720     JS_ASSERT(pn->pn_type == TOK_LC || pn->pn_type == TOK_LEXICALSCOPE);
721     if (!GenerateBlockId(tc, tc->topStmt->blockid))
722     return false;
723     pn->pn_blockid = tc->topStmt->blockid;
724     return true;
725     }
726    
727 siliconforks 332 /*
728     * Parse a top-level JS script.
729     */
730     JSParseNode *
731 siliconforks 460 JSCompiler::parse(JSObject *chain)
732 siliconforks 332 {
733     /*
734     * Protect atoms from being collected by a GC activation, which might
735     * - nest on this thread due to out of memory (the so-called "last ditch"
736     * GC attempted within js_NewGCThing), or
737     * - run for any reason on another thread if this thread is suspended on
738     * an object lock before it finishes generating bytecode into a script
739     * protected from the GC by a root or a stack frame reference.
740     */
741 siliconforks 460 JSTreeContext tc(this);
742     tc.scopeChain = chain;
743     if (!GenerateBlockId(&tc, tc.bodyid))
744     return NULL;
745    
746     JSParseNode *pn = Statements(context, TS(this), &tc);
747 siliconforks 332 if (pn) {
748 siliconforks 460 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
749     js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
750 siliconforks 332 JSMSG_SYNTAX_ERROR);
751     pn = NULL;
752     } else {
753 siliconforks 460 if (!js_FoldConstants(context, pn, &tc))
754 siliconforks 332 pn = NULL;
755     }
756     }
757     return pn;
758     }
759    
760 siliconforks 460 JS_STATIC_ASSERT(FREE_STATIC_LEVEL == JS_BITMASK(JSFB_LEVEL_BITS));
761    
762     static inline bool
763     SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
764     {
765     /*
766     * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
767     * (0xffffffff) and other cookies with that level.
768     *
769     * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
770     * practically speaking it leaves more than enough room for upvars. In fact
771     * we might want to split cookie fields giving fewer bits for skip and more
772     * for slot, but only based on evidence.
773     */
774     if (staticLevel >= FREE_STATIC_LEVEL) {
775     JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
776     JSMSG_TOO_DEEP, js_function_str);
777     return false;
778     }
779     tc->staticLevel = staticLevel;
780     return true;
781     }
782    
783 siliconforks 332 /*
784     * Compile a top-level script.
785     */
786 siliconforks 460 JSScript *
787     JSCompiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
788     JSPrincipals *principals, uint32 tcflags,
789     const jschar *chars, size_t length,
790     FILE *file, const char *filename, uintN lineno,
791     JSString *source /* = NULL */)
792 siliconforks 332 {
793 siliconforks 460 JSCompiler jsc(cx, principals, callerFrame);
794 siliconforks 332 JSArenaPool codePool, notePool;
795     JSTokenType tt;
796     JSParseNode *pn;
797     uint32 scriptGlobals;
798     JSScript *script;
799     #ifdef METER_PARSENODES
800     void *sbrk(ptrdiff_t), *before = sbrk(0);
801     #endif
802    
803     JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
804 siliconforks 460 TCF_STATIC_LEVEL_MASK)));
805 siliconforks 332
806     /*
807     * The scripted callerFrame can only be given for compile-and-go scripts
808 siliconforks 460 * and non-zero static level requires callerFrame.
809 siliconforks 332 */
810     JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
811 siliconforks 460 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
812 siliconforks 332
813 siliconforks 460 if (!jsc.init(chars, length, file, filename, lineno))
814 siliconforks 332 return NULL;
815    
816     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
817     &cx->scriptStackQuota);
818     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
819     &cx->scriptStackQuota);
820    
821 siliconforks 460 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
822    
823 siliconforks 332 MUST_FLOW_THROUGH("out");
824    
825 siliconforks 460 /* Null script early in case of error, to reduce our code footprint. */
826     script = NULL;
827    
828     cg.flags |= uint16(tcflags);
829     cg.scopeChain = scopeChain;
830     if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
831     goto out;
832    
833     /*
834     * If funbox is non-null after we create the new script, callerFrame->fun
835     * was saved in the 0th object table entry.
836     */
837     JSObjectBox *funbox;
838     funbox = NULL;
839    
840     if (tcflags & TCF_COMPILE_N_GO) {
841     if (source) {
842     /*
843     * Save eval program source in script->atomMap.vector[0] for the
844     * eval cache (see obj_eval in jsobj.cpp).
845     */
846     JSAtom *atom = js_AtomizeString(cx, source, 0);
847     if (!atom || !cg.atomList.add(&jsc, atom))
848     goto out;
849     }
850    
851     if (callerFrame && callerFrame->fun) {
852     /*
853     * An eval script in a caller frame needs to have its enclosing
854     * function captured in case it refers to an upvar, and someone
855     * wishes to decompile it while it's running.
856     */
857     funbox = jsc.newObjectBox(FUN_OBJECT(callerFrame->fun));
858     if (!funbox)
859     goto out;
860     funbox->emitLink = cg.objectList.lastbox;
861     cg.objectList.lastbox = funbox;
862     cg.objectList.length++;
863     }
864 siliconforks 399 }
865    
866 siliconforks 460 /*
867     * Inline Statements to emit as we go to save AST space. We must generate
868     * our script-body blockid since we aren't calling Statements.
869     */
870     uint32 bodyid;
871     if (!GenerateBlockId(&cg, bodyid))
872     goto out;
873     cg.bodyid = bodyid;
874    
875     #if JS_HAS_XML_SUPPORT
876     pn = NULL;
877     bool onlyXML;
878     onlyXML = true;
879     #endif
880    
881 siliconforks 507 CG_SWITCH_TO_PROLOG(&cg);
882     if (js_Emit1(cx, &cg, JSOP_TRACE) < 0)
883     goto out;
884     CG_SWITCH_TO_MAIN(&cg);
885    
886 siliconforks 332 for (;;) {
887 siliconforks 460 jsc.tokenStream.flags |= TSF_OPERAND;
888     tt = js_PeekToken(cx, &jsc.tokenStream);
889     jsc.tokenStream.flags &= ~TSF_OPERAND;
890 siliconforks 332 if (tt <= TOK_EOF) {
891     if (tt == TOK_EOF)
892     break;
893     JS_ASSERT(tt == TOK_ERROR);
894     goto out;
895     }
896    
897 siliconforks 460 pn = Statement(cx, &jsc.tokenStream, &cg);
898     if (!pn)
899 siliconforks 332 goto out;
900 siliconforks 460 JS_ASSERT(!cg.blockNode);
901    
902     if (!js_FoldConstants(cx, pn, &cg))
903     goto out;
904    
905     if (cg.functionList) {
906     if (!jsc.analyzeFunctions(cg.functionList, cg.flags))
907     goto out;
908     cg.functionList = NULL;
909 siliconforks 332 }
910    
911 siliconforks 460 if (!js_EmitTree(cx, &cg, pn))
912 siliconforks 332 goto out;
913 siliconforks 460 #if JS_HAS_XML_SUPPORT
914     if (PN_TYPE(pn) != TOK_SEMI ||
915     !pn->pn_kid ||
916     !TREE_TYPE_IS_XML(PN_TYPE(pn->pn_kid))) {
917     onlyXML = false;
918 siliconforks 332 }
919 siliconforks 460 #endif
920     RecycleTree(pn, &cg);
921 siliconforks 332 }
922    
923 siliconforks 460 #if JS_HAS_XML_SUPPORT
924 siliconforks 332 /*
925 siliconforks 460 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
926     * For background, see:
927     *
928     * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
929     */
930 siliconforks 585 if (pn && onlyXML && !callerFrame) {
931 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL, JSREPORT_ERROR,
932     JSMSG_XML_WHOLE_PROGRAM);
933     goto out;
934     }
935     #endif
936    
937     /*
938     * Global variables and regexps share the index space with locals. Due to
939 siliconforks 332 * incremental code generation we need to patch the bytecode to adjust the
940     * local references to skip the globals.
941     */
942 siliconforks 460 scriptGlobals = cg.ngvars + cg.regexpList.length;
943 siliconforks 332 if (scriptGlobals != 0) {
944     jsbytecode *code, *end;
945     JSOp op;
946     const JSCodeSpec *cs;
947     uintN len, slot;
948    
949     if (scriptGlobals >= SLOTNO_LIMIT)
950     goto too_many_slots;
951     code = CG_BASE(&cg);
952     for (end = code + CG_OFFSET(&cg); code != end; code += len) {
953     JS_ASSERT(code < end);
954     op = (JSOp) *code;
955     cs = &js_CodeSpec[op];
956     len = (cs->length > 0)
957     ? (uintN) cs->length
958     : js_GetVariableBytecodeLength(code);
959     if (JOF_TYPE(cs->format) == JOF_LOCAL ||
960     (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
961     /*
962     * JSOP_GETARGPROP also has JOF_SLOTATOM type, but it may be
963     * emitted only for a function.
964     */
965     JS_ASSERT((JOF_TYPE(cs->format) == JOF_SLOTATOM) ==
966     (op == JSOP_GETLOCALPROP));
967     slot = GET_SLOTNO(code);
968     slot += scriptGlobals;
969     if (slot >= SLOTNO_LIMIT)
970     goto too_many_slots;
971     SET_SLOTNO(code, slot);
972     }
973     }
974     }
975    
976     #ifdef METER_PARSENODES
977     printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
978     (char *)sbrk(0) - (char *)before,
979     parsenodes,
980     maxparsenodes,
981     parsenodes - recyclednodes);
982     before = sbrk(0);
983     #endif
984    
985     /*
986     * Nowadays the threaded interpreter needs a stop instruction, so we
987     * do have to emit that here.
988     */
989 siliconforks 460 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
990 siliconforks 332 goto out;
991     #ifdef METER_PARSENODES
992     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
993 siliconforks 460 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
994 siliconforks 332 #endif
995     #ifdef JS_ARENAMETER
996     JS_DumpArenaStats(stdout);
997     #endif
998     script = js_NewScriptFromCG(cx, &cg);
999 siliconforks 460 if (script && funbox)
1000     script->flags |= JSSF_SAVED_CALLER_FUN;
1001 siliconforks 332
1002     #ifdef JS_SCOPE_DEPTH_METER
1003     if (script) {
1004     JSObject *obj = scopeChain;
1005     uintN depth = 1;
1006     while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
1007     ++depth;
1008     JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
1009     }
1010     #endif
1011    
1012     out:
1013     JS_FinishArenaPool(&codePool);
1014     JS_FinishArenaPool(&notePool);
1015     return script;
1016    
1017     too_many_slots:
1018 siliconforks 460 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1019 siliconforks 332 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
1020     script = NULL;
1021     goto out;
1022     }
1023    
1024     /*
1025     * Insist on a final return before control flows out of pn. Try to be a bit
1026     * smart about loops: do {...; return e2;} while(0) at the end of a function
1027     * that contains an early return e1 will get a strict warning. Similarly for
1028     * iloops: while (true){...} is treated as though ... returns.
1029     */
1030     #define ENDS_IN_OTHER 0
1031     #define ENDS_IN_RETURN 1
1032     #define ENDS_IN_BREAK 2
1033    
1034     static int
1035     HasFinalReturn(JSParseNode *pn)
1036     {
1037     JSParseNode *pn2, *pn3;
1038     uintN rv, rv2, hasDefault;
1039    
1040     switch (pn->pn_type) {
1041     case TOK_LC:
1042     if (!pn->pn_head)
1043     return ENDS_IN_OTHER;
1044 siliconforks 460 return HasFinalReturn(pn->last());
1045 siliconforks 332
1046     case TOK_IF:
1047     if (!pn->pn_kid3)
1048     return ENDS_IN_OTHER;
1049     return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
1050    
1051     case TOK_WHILE:
1052     pn2 = pn->pn_left;
1053     if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
1054     return ENDS_IN_RETURN;
1055     if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
1056     return ENDS_IN_RETURN;
1057     return ENDS_IN_OTHER;
1058    
1059     case TOK_DO:
1060     pn2 = pn->pn_right;
1061     if (pn2->pn_type == TOK_PRIMARY) {
1062     if (pn2->pn_op == JSOP_FALSE)
1063     return HasFinalReturn(pn->pn_left);
1064     if (pn2->pn_op == JSOP_TRUE)
1065     return ENDS_IN_RETURN;
1066     }
1067     if (pn2->pn_type == TOK_NUMBER) {
1068     if (pn2->pn_dval == 0)
1069     return HasFinalReturn(pn->pn_left);
1070     return ENDS_IN_RETURN;
1071     }
1072     return ENDS_IN_OTHER;
1073    
1074     case TOK_FOR:
1075     pn2 = pn->pn_left;
1076     if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
1077     return ENDS_IN_RETURN;
1078     return ENDS_IN_OTHER;
1079    
1080     case TOK_SWITCH:
1081     rv = ENDS_IN_RETURN;
1082     hasDefault = ENDS_IN_OTHER;
1083     pn2 = pn->pn_right;
1084     if (pn2->pn_type == TOK_LEXICALSCOPE)
1085 siliconforks 460 pn2 = pn2->expr();
1086 siliconforks 332 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
1087     if (pn2->pn_type == TOK_DEFAULT)
1088     hasDefault = ENDS_IN_RETURN;
1089     pn3 = pn2->pn_right;
1090     JS_ASSERT(pn3->pn_type == TOK_LC);
1091     if (pn3->pn_head) {
1092 siliconforks 460 rv2 = HasFinalReturn(pn3->last());
1093 siliconforks 332 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
1094     /* Falling through to next case or default. */;
1095     else
1096     rv &= rv2;
1097     }
1098     }
1099     /* If a final switch has no default case, we judge it harshly. */
1100     rv &= hasDefault;
1101     return rv;
1102    
1103     case TOK_BREAK:
1104     return ENDS_IN_BREAK;
1105    
1106     case TOK_WITH:
1107     return HasFinalReturn(pn->pn_right);
1108    
1109     case TOK_RETURN:
1110     return ENDS_IN_RETURN;
1111    
1112     case TOK_COLON:
1113     case TOK_LEXICALSCOPE:
1114 siliconforks 460 return HasFinalReturn(pn->expr());
1115 siliconforks 332
1116     case TOK_THROW:
1117     return ENDS_IN_RETURN;
1118    
1119     case TOK_TRY:
1120     /* If we have a finally block that returns, we are done. */
1121     if (pn->pn_kid3) {
1122     rv = HasFinalReturn(pn->pn_kid3);
1123     if (rv == ENDS_IN_RETURN)
1124     return rv;
1125     }
1126    
1127     /* Else check the try block and any and all catch statements. */
1128     rv = HasFinalReturn(pn->pn_kid1);
1129     if (pn->pn_kid2) {
1130     JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
1131     for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
1132     rv &= HasFinalReturn(pn2);
1133     }
1134     return rv;
1135    
1136     case TOK_CATCH:
1137     /* Check this catch block's body. */
1138     return HasFinalReturn(pn->pn_kid3);
1139    
1140     case TOK_LET:
1141     /* Non-binary let statements are let declarations. */
1142     if (pn->pn_arity != PN_BINARY)
1143     return ENDS_IN_OTHER;
1144     return HasFinalReturn(pn->pn_right);
1145    
1146     default:
1147     return ENDS_IN_OTHER;
1148     }
1149     }
1150    
1151     static JSBool
1152     ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
1153     uintN anonerrnum)
1154     {
1155     const char *name;
1156    
1157     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1158 siliconforks 460 if (tc->fun->atom) {
1159     name = js_AtomToPrintableString(cx, tc->fun->atom);
1160 siliconforks 332 } else {
1161     errnum = anonerrnum;
1162     name = NULL;
1163     }
1164 siliconforks 460 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1165 siliconforks 332 errnum, name);
1166     }
1167    
1168     static JSBool
1169     CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1170     {
1171     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1172     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
1173     ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
1174     JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
1175     }
1176    
1177     static JSParseNode *
1178     FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1179     {
1180     JSStmtInfo stmtInfo;
1181     uintN oldflags, firstLine;
1182     JSParseNode *pn;
1183    
1184     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1185     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1186     stmtInfo.flags = SIF_BODY_BLOCK;
1187    
1188     oldflags = tc->flags;
1189     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
1190    
1191     /*
1192     * Save the body's first line, and store it in pn->pn_pos.begin.lineno
1193     * later, because we may have not peeked in ts yet, so Statements won't
1194     * acquire a valid pn->pn_pos.begin from the current token.
1195     */
1196     firstLine = ts->lineno;
1197     #if JS_HAS_EXPR_CLOSURES
1198     if (CURRENT_TOKEN(ts).type == TOK_LC) {
1199     pn = Statements(cx, ts, tc);
1200     } else {
1201 siliconforks 460 pn = NewParseNode(PN_UNARY, tc);
1202 siliconforks 332 if (pn) {
1203     pn->pn_kid = AssignExpr(cx, ts, tc);
1204     if (!pn->pn_kid) {
1205     pn = NULL;
1206     } else {
1207     if (tc->flags & TCF_FUN_IS_GENERATOR) {
1208     ReportBadReturn(cx, tc, JSREPORT_ERROR,
1209     JSMSG_BAD_GENERATOR_RETURN,
1210     JSMSG_BAD_ANON_GENERATOR_RETURN);
1211     pn = NULL;
1212     } else {
1213     pn->pn_type = TOK_RETURN;
1214     pn->pn_op = JSOP_RETURN;
1215     pn->pn_pos.end = pn->pn_kid->pn_pos.end;
1216     }
1217     }
1218     }
1219     }
1220     #else
1221     pn = Statements(cx, ts, tc);
1222     #endif
1223    
1224     if (pn) {
1225 siliconforks 460 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1226 siliconforks 332 js_PopStatement(tc);
1227     pn->pn_pos.begin.lineno = firstLine;
1228    
1229     /* Check for falling off the end of a function that returns a value. */
1230     if (JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR) &&
1231     !CheckFinalReturn(cx, tc, pn)) {
1232     pn = NULL;
1233     }
1234     }
1235    
1236 siliconforks 460 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1237 siliconforks 332 return pn;
1238     }
1239    
1240 siliconforks 460 static JSAtomListElement *
1241     MakePlaceholder(JSParseNode *pn, JSTreeContext *tc)
1242     {
1243     JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, pn->pn_atom);
1244     if (!ale)
1245     return NULL;
1246    
1247     JSDefinition *dn = (JSDefinition *)
1248     NewNameNode(tc->compiler->context, TS(tc->compiler), pn->pn_atom, tc);
1249     if (!dn)
1250     return NULL;
1251    
1252     ALE_SET_DEFN(ale, dn);
1253     dn->pn_defn = true;
1254     dn->pn_dflags |= PND_PLACEHOLDER;
1255     return ale;
1256     }
1257    
1258     static bool
1259     Define(JSParseNode *pn, JSAtom *atom, JSTreeContext *tc, bool let = false)
1260     {
1261     JS_ASSERT(!pn->pn_used);
1262     JS_ASSERT_IF(pn->pn_defn, pn->isPlaceholder());
1263    
1264     JSHashEntry **hep;
1265     JSAtomListElement *ale = NULL;
1266     JSAtomList *list = NULL;
1267    
1268     if (let)
1269     ale = (list = &tc->decls)->rawLookup(atom, hep);
1270     if (!ale)
1271     ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
1272    
1273     if (ale) {
1274     JSDefinition *dn = ALE_DEFN(ale);
1275     if (dn != pn) {
1276     JSParseNode **pnup = &dn->dn_uses;
1277     JSParseNode *pnu;
1278     uintN start = let ? pn->pn_blockid : tc->bodyid;
1279    
1280     while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
1281     JS_ASSERT(pnu->pn_used);
1282     pnu->pn_lexdef = (JSDefinition *) pn;
1283 siliconforks 507 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1284 siliconforks 460 pnup = &pnu->pn_link;
1285     }
1286    
1287     if (pnu != dn->dn_uses) {
1288     *pnup = pn->dn_uses;
1289     pn->dn_uses = dn->dn_uses;
1290     dn->dn_uses = pnu;
1291    
1292     if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
1293     list->rawRemove(tc->compiler, ale, hep);
1294     }
1295     }
1296     }
1297    
1298     ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
1299     if (!ale)
1300     return false;
1301     ALE_SET_DEFN(ale, pn);
1302     pn->pn_defn = true;
1303     pn->pn_dflags &= ~PND_PLACEHOLDER;
1304     return true;
1305     }
1306    
1307     static void
1308     LinkUseToDef(JSParseNode *pn, JSDefinition *dn, JSTreeContext *tc)
1309     {
1310     JS_ASSERT(!pn->pn_used);
1311     JS_ASSERT(!pn->pn_defn);
1312     JS_ASSERT(pn != dn->dn_uses);
1313     pn->pn_link = dn->dn_uses;
1314     dn->dn_uses = pn;
1315 siliconforks 507 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1316 siliconforks 460 pn->pn_used = true;
1317     pn->pn_lexdef = dn;
1318     }
1319    
1320     static void
1321     ForgetUse(JSParseNode *pn)
1322     {
1323     if (!pn->pn_used) {
1324     JS_ASSERT(!pn->pn_defn);
1325     return;
1326     }
1327    
1328     JSParseNode **pnup = &pn->lexdef()->dn_uses;
1329     JSParseNode *pnu;
1330     while ((pnu = *pnup) != pn)
1331     pnup = &pnu->pn_link;
1332     *pnup = pn->pn_link;
1333     pn->pn_used = false;
1334     }
1335    
1336     static JSParseNode *
1337     MakeAssignment(JSParseNode *pn, JSParseNode *rhs, JSTreeContext *tc)
1338     {
1339     JSParseNode *lhs = NewOrRecycledNode(tc);
1340     if (!lhs)
1341     return NULL;
1342     *lhs = *pn;
1343    
1344     if (pn->pn_used) {
1345     JSDefinition *dn = pn->pn_lexdef;
1346     JSParseNode **pnup = &dn->dn_uses;
1347    
1348     while (*pnup != pn)
1349     pnup = &(*pnup)->pn_link;
1350     *pnup = lhs;
1351     lhs->pn_link = pn->pn_link;
1352     pn->pn_link = NULL;
1353     }
1354    
1355     pn->pn_type = TOK_ASSIGN;
1356     pn->pn_op = JSOP_NOP;
1357     pn->pn_arity = PN_BINARY;
1358 siliconforks 507 pn->pn_parens = false;
1359 siliconforks 460 pn->pn_used = pn->pn_defn = false;
1360     pn->pn_left = lhs;
1361     pn->pn_right = rhs;
1362     return lhs;
1363     }
1364    
1365     static JSParseNode *
1366     MakeDefIntoUse(JSDefinition *dn, JSParseNode *pn, JSAtom *atom, JSTreeContext *tc)
1367     {
1368     /*
1369     * If dn is var, const, or let, and it has an initializer, then we must
1370     * rewrite it to be an assignment node, whose freshly allocated left-hand
1371     * side becomes a use of pn.
1372     */
1373     if (dn->isBindingForm()) {
1374     JSParseNode *rhs = dn->expr();
1375     if (rhs) {
1376     JSParseNode *lhs = MakeAssignment(dn, rhs, tc);
1377     if (!lhs)
1378     return NULL;
1379     //pn->dn_uses = lhs;
1380     dn = (JSDefinition *) lhs;
1381     }
1382    
1383     dn->pn_op = (js_CodeSpec[dn->pn_op].format & JOF_SET) ? JSOP_SETNAME : JSOP_NAME;
1384     } else if (dn->kind() == JSDefinition::FUNCTION) {
1385     JS_ASSERT(dn->isTopLevel());
1386     JS_ASSERT(dn->pn_op == JSOP_NOP);
1387     dn->pn_type = TOK_NAME;
1388     dn->pn_arity = PN_NAME;
1389     dn->pn_atom = atom;
1390     }
1391    
1392     /* Now make dn no longer a definition, rather a use of pn. */
1393     JS_ASSERT(dn->pn_type == TOK_NAME);
1394     JS_ASSERT(dn->pn_arity == PN_NAME);
1395     JS_ASSERT(dn->pn_atom == atom);
1396    
1397     for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1398     JS_ASSERT(pnu->pn_used);
1399     JS_ASSERT(!pnu->pn_defn);
1400     pnu->pn_lexdef = (JSDefinition *) pn;
1401 siliconforks 507 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1402 siliconforks 460 }
1403 siliconforks 507 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1404 siliconforks 460 pn->dn_uses = dn;
1405    
1406     dn->pn_defn = false;
1407     dn->pn_used = true;
1408     dn->pn_lexdef = (JSDefinition *) pn;
1409     dn->pn_cookie = FREE_UPVAR_COOKIE;
1410     dn->pn_dflags &= ~PND_BOUND;
1411     return dn;
1412     }
1413    
1414     static bool
1415     DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
1416     {
1417     JSParseNode *argpn, *argsbody;
1418    
1419     /* Flag tc so we don't have to lookup arguments on every use. */
1420     if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1421     tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1422    
1423     /*
1424     * Make an argument definition node, distinguished by being in tc->decls
1425     * but having TOK_NAME type and JSOP_NOP op. Insert it in a TOK_ARGSBODY
1426     * list node returned via pn->pn_body.
1427     */
1428     argpn = NewNameNode(tc->compiler->context, TS(tc->compiler), atom, tc);
1429     if (!argpn)
1430     return false;
1431     JS_ASSERT(PN_TYPE(argpn) == TOK_NAME && PN_OP(argpn) == JSOP_NOP);
1432    
1433     /* Arguments are initialized by definition. */
1434     argpn->pn_dflags |= PND_INITIALIZED;
1435     if (!Define(argpn, atom, tc))
1436     return false;
1437    
1438     argsbody = pn->pn_body;
1439     if (!argsbody) {
1440     argsbody = NewParseNode(PN_LIST, tc);
1441     if (!argsbody)
1442     return false;
1443     argsbody->pn_type = TOK_ARGSBODY;
1444     argsbody->pn_op = JSOP_NOP;
1445     argsbody->makeEmpty();
1446     pn->pn_body = argsbody;
1447     }
1448     argsbody->append(argpn);
1449    
1450     argpn->pn_op = JSOP_GETARG;
1451     argpn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, i);
1452     argpn->pn_dflags |= PND_BOUND;
1453     return true;
1454     }
1455    
1456 siliconforks 332 /*
1457     * Compile a JS function body, which might appear as the value of an event
1458     * handler attribute in an HTML <INPUT> tag.
1459     */
1460 siliconforks 460 bool
1461     JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1462     const jschar *chars, size_t length,
1463     const char *filename, uintN lineno)
1464 siliconforks 332 {
1465 siliconforks 460 JSCompiler jsc(cx, principals);
1466 siliconforks 332
1467 siliconforks 460 if (!jsc.init(chars, length, NULL, filename, lineno))
1468     return false;
1469 siliconforks 332
1470 siliconforks 460 /* No early return from after here until the js_FinishArenaPool calls. */
1471     JSArenaPool codePool, notePool;
1472 siliconforks 332 JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
1473     &cx->scriptStackQuota);
1474     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
1475     &cx->scriptStackQuota);
1476    
1477 siliconforks 460 JSCodeGenerator funcg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
1478     funcg.flags |= TCF_IN_FUNCTION;
1479     funcg.fun = fun;
1480     if (!GenerateBlockId(&funcg, funcg.bodyid))
1481     return NULL;
1482    
1483     /* FIXME: make Function format the source for a function definition. */
1484     jsc.tokenStream.tokens[0].type = TOK_NAME;
1485     JSParseNode *fn = NewParseNode(PN_FUNC, &funcg);
1486     if (fn) {
1487     fn->pn_body = NULL;
1488     fn->pn_cookie = FREE_UPVAR_COOKIE;
1489    
1490     uintN nargs = fun->nargs;
1491     if (nargs) {
1492     jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
1493     if (!names) {
1494     fn = NULL;
1495     } else {
1496     for (uintN i = 0; i < nargs; i++) {
1497     JSAtom *name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1498     if (!DefineArg(fn, name, i, &funcg)) {
1499     fn = NULL;
1500     break;
1501     }
1502     }
1503     }
1504     }
1505     }
1506    
1507 siliconforks 332 /*
1508     * Farble the body so that it looks like a block statement to js_EmitTree,
1509 siliconforks 460 * which is called from js_EmitFunctionBody (see jsemit.cpp). After we're
1510     * done parsing, we must fold constants, analyze any nested functions, and
1511     * generate code for this function, including a stop opcode at the end.
1512 siliconforks 332 */
1513 siliconforks 460 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1514     JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1515 siliconforks 332 if (pn) {
1516 siliconforks 460 if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1517     js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1518 siliconforks 332 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1519     pn = NULL;
1520 siliconforks 460 } else if (!js_FoldConstants(cx, pn, &funcg)) {
1521     /* js_FoldConstants reported the error already. */
1522     pn = NULL;
1523     } else if (funcg.functionList &&
1524     !jsc.analyzeFunctions(funcg.functionList, funcg.flags)) {
1525     pn = NULL;
1526 siliconforks 332 } else {
1527 siliconforks 460 if (fn->pn_body) {
1528     JS_ASSERT(PN_TYPE(fn->pn_body) == TOK_ARGSBODY);
1529     fn->pn_body->append(pn);
1530     fn->pn_body->pn_pos = pn->pn_pos;
1531     pn = fn->pn_body;
1532     }
1533    
1534     if (!js_EmitFunctionScript(cx, &funcg, pn))
1535 siliconforks 332 pn = NULL;
1536     }
1537     }
1538    
1539     /* Restore saved state and release code generation arenas. */
1540     JS_FinishArenaPool(&codePool);
1541     JS_FinishArenaPool(&notePool);
1542     return pn != NULL;
1543     }
1544    
1545     /*
1546     * Parameter block types for the several Binder functions. We use a common
1547     * helper function signature in order to share code among destructuring and
1548     * simple variable declaration parsers. In the destructuring case, the binder
1549     * function is called indirectly from the variable declaration parser by way
1550     * of CheckDestructuring and its friends.
1551     */
1552     typedef struct BindData BindData;
1553    
1554     typedef JSBool
1555     (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
1556    
1557     struct BindData {
1558 siliconforks 460 BindData() : fresh(true) {}
1559    
1560     JSParseNode *pn; /* name node for definition processing and
1561     error source coordinates */
1562     JSOp op; /* prolog bytecode or nop */
1563     Binder binder; /* binder, discriminates u */
1564 siliconforks 332 union {
1565     struct {
1566 siliconforks 460 uintN overflow;
1567 siliconforks 332 } let;
1568 siliconforks 460 };
1569     bool fresh;
1570 siliconforks 332 };
1571    
1572     static JSBool
1573     BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1574 siliconforks 507 JSLocalKind localKind, bool isArg)
1575 siliconforks 332 {
1576     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1577    
1578     /*
1579     * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
1580     * Instead 'var arguments' always restates the predefined property of the
1581 siliconforks 460 * activation objects whose name is 'arguments'. Assignment to such a
1582     * variable must be handled specially.
1583 siliconforks 507 *
1584     * Special case: an argument named 'arguments' *does* shadow the predefined
1585     * arguments property.
1586 siliconforks 332 */
1587 siliconforks 507 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1588 siliconforks 332 return JS_TRUE;
1589    
1590     return js_AddLocal(cx, fun, atom, localKind);
1591     }
1592    
1593     #if JS_HAS_DESTRUCTURING
1594     /*
1595     * Forward declaration to maintain top-down presentation.
1596     */
1597     static JSParseNode *
1598     DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
1599     JSTokenType tt);
1600    
1601     static JSBool
1602     BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
1603     JSTreeContext *tc)
1604     {
1605 siliconforks 460 JSParseNode *pn;
1606 siliconforks 332
1607 siliconforks 460 /* Flag tc so we don't have to lookup arguments on every use. */
1608     if (atom == tc->compiler->context->runtime->atomState.argumentsAtom)
1609     tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
1610    
1611 siliconforks 332 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1612 siliconforks 460
1613     JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
1614     if (localKind != JSLOCAL_NONE) {
1615     js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL,
1616     JSREPORT_ERROR, JSMSG_DESTRUCT_DUP_ARG);
1617     return JS_FALSE;
1618 siliconforks 332 }
1619 siliconforks 507 JS_ASSERT(!tc->decls.lookup(atom));
1620 siliconforks 332
1621 siliconforks 507 pn = data->pn;
1622     if (!Define(pn, atom, tc))
1623     return JS_FALSE;
1624    
1625 siliconforks 460 uintN index = tc->fun->u.i.nvars;
1626 siliconforks 507 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1627 siliconforks 460 return JS_FALSE;
1628     pn->pn_op = JSOP_SETLOCAL;
1629     pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
1630     pn->pn_dflags |= PND_BOUND;
1631 siliconforks 332 return JS_TRUE;
1632     }
1633     #endif /* JS_HAS_DESTRUCTURING */
1634    
1635 siliconforks 460 JSFunction *
1636     JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1637 siliconforks 332 {
1638     JSObject *parent;
1639     JSFunction *fun;
1640    
1641     JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1642 siliconforks 460
1643     /*
1644     * Find the global compilation context in order to pre-set the newborn
1645     * function's parent slot to tc->scopeChain. If the global context is a
1646     * compile-and-go one, we leave the pre-set parent intact; otherwise we
1647     * clear parent and proto.
1648     */
1649     while (tc->parent)
1650     tc = tc->parent;
1651     parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
1652    
1653     fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
1654 siliconforks 332 parent, atom);
1655 siliconforks 460
1656 siliconforks 332 if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
1657     STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1658     STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1659     }
1660     return fun;
1661     }
1662    
1663 siliconforks 460 static JSBool
1664     MatchOrInsertSemicolon(JSContext *cx, JSTokenStream *ts)
1665     {
1666     JSTokenType tt;
1667    
1668     ts->flags |= TSF_OPERAND;
1669     tt = js_PeekTokenSameLine(cx, ts);
1670     ts->flags &= ~TSF_OPERAND;
1671     if (tt == TOK_ERROR)
1672     return JS_FALSE;
1673     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1674     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1675     JSMSG_SEMI_BEFORE_STMNT);
1676     return JS_FALSE;
1677     }
1678     (void) js_MatchToken(cx, ts, TOK_SEMI);
1679     return JS_TRUE;
1680     }
1681    
1682     bool
1683     JSCompiler::analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags)
1684     {
1685     if (!markFunArgs(funbox, tcflags))
1686     return false;
1687     setFunctionKinds(funbox, tcflags);
1688     return true;
1689     }
1690    
1691     /*
1692     * Mark as funargs any functions that reach up to one or more upvars across an
1693     * already-known funarg. The parser will flag the o_m lambda as a funarg in:
1694     *
1695     * function f(o, p) {
1696     * o.m = function o_m(a) {
1697     * function g() { return p; }
1698     * function h() { return a; }
1699     * return g() + h();
1700     * }
1701     * }
1702     *
1703     * but without this extra marking phase, function g will not be marked as a
1704     * funarg since it is called from within its parent scope. But g reaches up to
1705     * f's parameter p, so if o_m escapes f's activation scope, g does too and
1706     * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
1707     * nor uses an upvar "above" o_m's level.
1708     *
1709     * If function g itself contained lambdas that contained non-lambdas that reach
1710     * up above its level, then those non-lambdas would have to be marked too. This
1711     * process is potentially exponential in the number of functions, but generally
1712     * not so complex. But it can't be done during a single recursive traversal of
1713     * the funbox tree, so we must use a work queue.
1714     *
1715     * Return the minimal "skipmin" for funbox and its siblings. This is the delta
1716     * between the static level of the bodies of funbox and its peers (which must
1717     * be funbox->level + 1), and the static level of the nearest upvar among all
1718     * the upvars contained by funbox and its peers. If there are no upvars, return
1719     * FREE_STATIC_LEVEL. Thus this function never returns 0.
1720     */
1721     static uintN
1722     FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
1723     {
1724     uintN allskipmin = FREE_STATIC_LEVEL;
1725    
1726     do {
1727     JSParseNode *fn = funbox->node;
1728     JSFunction *fun = (JSFunction *) funbox->object;
1729     int fnlevel = level;
1730    
1731     /*
1732     * An eval can leak funbox, functions along its ancestor line, and its
1733     * immediate kids. Since FindFunArgs uses DFS and the parser propagates
1734     * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
1735     * already been marked as funargs by this point. Therefore we have to
1736     * flag only funbox->node and funbox->kids' nodes here.
1737     */
1738     if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1739     fn->setFunArg();
1740     for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
1741     kid->node->setFunArg();
1742     }
1743    
1744     /*
1745     * Compute in skipmin the least distance from fun's static level up to
1746     * an upvar, whether used directly by fun, or indirectly by a function
1747     * nested in fun.
1748     */
1749     uintN skipmin = FREE_STATIC_LEVEL;
1750     JSParseNode *pn = fn->pn_body;
1751    
1752     if (pn->pn_type == TOK_UPVARS) {
1753     JSAtomList upvars(pn->pn_names);
1754     JS_ASSERT(upvars.count != 0);
1755    
1756     JSAtomListIterator iter(&upvars);
1757     JSAtomListElement *ale;
1758    
1759     while ((ale = iter()) != NULL) {
1760     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1761    
1762     if (!lexdep->isFreeVar()) {
1763     uintN upvarLevel = lexdep->frameLevel();
1764    
1765     if (int(upvarLevel) <= fnlevel)
1766     fn->setFunArg();
1767    
1768     uintN skip = (funbox->level + 1) - upvarLevel;
1769     if (skip < skipmin)
1770     skipmin = skip;
1771     }
1772     }
1773     }
1774    
1775     /*
1776     * If this function escapes, whether directly (the parser detects such
1777     * escapes) or indirectly (because this non-escaping function uses an
1778     * upvar that reaches across an outer function boundary where the outer
1779     * function escapes), enqueue it for further analysis, and bump fnlevel
1780     * to trap any non-escaping children.
1781     */
1782     if (fn->isFunArg()) {
1783     queue->push(funbox);
1784     fnlevel = int(funbox->level);
1785     }
1786    
1787     /*
1788     * Now process the current function's children, and recalibrate their
1789     * cumulative skipmin to be relative to the current static level.
1790     */
1791     if (funbox->kids) {
1792     uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
1793    
1794     JS_ASSERT(kidskipmin != 0);
1795     if (kidskipmin != FREE_STATIC_LEVEL) {
1796     --kidskipmin;
1797     if (kidskipmin != 0 && kidskipmin < skipmin)
1798     skipmin = kidskipmin;
1799     }
1800     }
1801    
1802     /*
1803     * Finally, after we've traversed all of the current function's kids,
1804     * minimize fun's skipmin against our accumulated skipmin. Do likewise
1805     * with allskipmin, but minimize across funbox and all of its siblings,
1806     * to compute our return value.
1807     */
1808     if (skipmin != FREE_STATIC_LEVEL) {
1809     fun->u.i.skipmin = skipmin;
1810     if (skipmin < allskipmin)
1811     allskipmin = skipmin;
1812     }
1813     } while ((funbox = funbox->siblings) != NULL);
1814    
1815     return allskipmin;
1816     }
1817    
1818     bool
1819     JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
1820     {
1821     JSFunctionBoxQueue queue;
1822     if (!queue.init(functionCount))
1823     return false;
1824    
1825     FindFunArgs(funbox, -1, &queue);
1826     while ((funbox = queue.pull()) != NULL) {
1827     JSParseNode *fn = funbox->node;
1828     JS_ASSERT(fn->isFunArg());
1829    
1830     JSParseNode *pn = fn->pn_body;
1831     if (pn->pn_type == TOK_UPVARS) {
1832     JSAtomList upvars(pn->pn_names);
1833     JS_ASSERT(upvars.count != 0);
1834    
1835     JSAtomListIterator iter(&upvars);
1836     JSAtomListElement *ale;
1837    
1838     while ((ale = iter()) != NULL) {
1839     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1840    
1841     if (!lexdep->isFreeVar() &&
1842     !lexdep->isFunArg() &&
1843     lexdep->kind() == JSDefinition::FUNCTION) {
1844     /*
1845     * Mark this formerly-Algol-like function as an escaping
1846     * function (i.e., as a funarg), because it is used from a
1847     * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
1848     * access upvars.
1849     *
1850     * Progress is guaranteed because we set the funarg flag
1851     * here, which suppresses revisiting this function (thanks
1852     * to the !lexdep->isFunArg() test just above).
1853     */
1854     lexdep->setFunArg();
1855    
1856     JSFunctionBox *afunbox = lexdep->pn_funbox;
1857     queue.push(afunbox);
1858    
1859     /*
1860     * Walk over nested functions again, now that we have
1861     * changed the level across which it is unsafe to access
1862     * upvars using the runtime dynamic link (frame chain).
1863     */
1864     if (afunbox->kids)
1865     FindFunArgs(afunbox->kids, afunbox->level, &queue);
1866     }
1867     }
1868     }
1869     }
1870     return true;
1871     }
1872    
1873     static uint32
1874     MinBlockId(JSParseNode *fn, uint32 id)
1875     {
1876     if (fn->pn_blockid < id)
1877     return false;
1878     if (fn->pn_defn) {
1879     for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1880     if (pn->pn_blockid < id)
1881     return false;
1882     }
1883     }
1884     return true;
1885     }
1886    
1887     static bool
1888     OneBlockId(JSParseNode *fn, uint32 id)
1889     {
1890     if (fn->pn_blockid != id)
1891     return false;
1892     if (fn->pn_defn) {
1893     for (JSParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
1894     if (pn->pn_blockid != id)
1895     return false;
1896     }
1897     }
1898     return true;
1899     }
1900    
1901     void
1902     JSCompiler::setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags)
1903     {
1904     #ifdef JS_FUNCTION_METERING
1905     # define FUN_METER(x) JS_RUNTIME_METER(context->runtime, functionMeter.x)
1906     #else
1907     # define FUN_METER(x) ((void)0)
1908     #endif
1909     JSFunctionBox *parent = funbox->parent;
1910    
1911     for (;;) {
1912     JSParseNode *fn = funbox->node;
1913    
1914     if (funbox->kids)
1915     setFunctionKinds(funbox->kids, tcflags);
1916    
1917     JSParseNode *pn = fn->pn_body;
1918     JSFunction *fun = (JSFunction *) funbox->object;
1919    
1920     FUN_METER(allfun);
1921     if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
1922     FUN_METER(heavy);
1923     JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
1924     } else if (pn->pn_type != TOK_UPVARS) {
1925     /*
1926     * No lexical dependencies => null closure, for best performance.
1927     * A null closure needs no scope chain, but alas we've coupled
1928     * principals-finding to scope (for good fundamental reasons, but
1929     * the implementation overloads the parent slot and we should fix
1930     * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
1931     *
1932     * In more detail: the ES3 spec allows the implementation to create
1933     * "joined function objects", or not, at its discretion. But real-
1934     * world implementations always create unique function objects for
1935     * closures, and this can be detected via mutation. Open question:
1936     * do popular implementations create unique function objects for
1937     * null closures?
1938     *
1939     * FIXME: bug 476950.
1940     */
1941     FUN_METER(nofreeupvar);
1942     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1943     } else {
1944     JSAtomList upvars(pn->pn_names);
1945     JS_ASSERT(upvars.count != 0);
1946    
1947     JSAtomListIterator iter(&upvars);
1948     JSAtomListElement *ale;
1949    
1950     if (!fn->isFunArg()) {
1951     /*
1952     * This function is Algol-like, it never escapes. So long as it
1953     * does not assign to outer variables, it needs only an upvars
1954     * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
1955     * bytecode to reach up the frame stack at runtime based on
1956     * those upvars' cookies.
1957     *
1958     * Any assignments to upvars from functions called by this one
1959     * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
1960     * which load from stack homes when interpreting or from native
1961     * stack slots when executing a trace.
1962     *
1963     * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
1964     * nested function to assign to an outer lexical variable, so
1965     * we defer adding yet more code footprint in the absence of
1966     * evidence motivating these opcodes.
1967     */
1968     bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
1969     uintN nupvars = 0;
1970    
1971     /*
1972     * Check that at least one outer lexical binding was assigned
1973     * to (global variables don't count). This is conservative: we
1974     * could limit assignments to those in the current function,
1975     * but that's too much work. As with flat closures (handled
1976     * below), we optimize for the case where outer bindings are
1977     * not reassigned anywhere.
1978     */
1979     while ((ale = iter()) != NULL) {
1980     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
1981    
1982     if (!lexdep->isFreeVar()) {
1983     JS_ASSERT(lexdep->frameLevel() <= funbox->level);
1984     ++nupvars;
1985     if (lexdep->isAssigned())
1986     break;
1987     }
1988     }
1989     if (!ale)
1990     mutation = false;
1991    
1992     if (nupvars == 0) {
1993     FUN_METER(onlyfreevar);
1994     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
1995     } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
1996     /*
1997     * Algol-like functions can read upvars using the dynamic
1998     * link (cx->fp/fp->down). They do not need to entrain and
1999     * search their environment.
2000     */
2001     FUN_METER(display);
2002     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2003     } else {
2004     if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
2005     FUN_METER(setupvar);
2006     }
2007     } else {
2008     uintN nupvars = 0;
2009    
2010     /*
2011     * For each lexical dependency from this closure to an outer
2012     * binding, analyze whether it is safe to copy the binding's
2013     * value into a flat closure slot when the closure is formed.
2014     */
2015     while ((ale = iter()) != NULL) {
2016     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2017    
2018     if (!lexdep->isFreeVar()) {
2019     ++nupvars;
2020    
2021     /*
2022     * Consider the current function (the lambda, innermost
2023     * below) using a var x defined two static levels up:
2024     *
2025     * function f() {
2026     * // z = g();
2027     * var x = 42;
2028     * function g() {
2029     * return function () { return x; };
2030     * }
2031     * return g();
2032     * }
2033     *
2034     * So long as (1) the initialization in 'var x = 42'
2035     * dominates all uses of g and (2) x is not reassigned,
2036     * it is safe to optimize the lambda to a flat closure.
2037     * Uncommenting the early call to g makes it unsafe to
2038     * so optimize (z could name a global setter that calls
2039     * its argument).
2040     */
2041     JSFunctionBox *afunbox = funbox;
2042     uintN lexdepLevel = lexdep->frameLevel();
2043    
2044     JS_ASSERT(lexdepLevel <= funbox->level);
2045     while (afunbox->level != lexdepLevel) {
2046     afunbox = afunbox->parent;
2047    
2048     /*
2049     * afunbox can't be null because we are sure
2050     * to find a function box whose level == lexdepLevel
2051     * before walking off the top of the funbox tree.
2052     * See bug 493260 comments 16-18.
2053     *
2054     * Assert but check anyway, to check future changes
2055     * that bind eval upvars in the parser.
2056     */
2057     JS_ASSERT(afunbox);
2058    
2059     /*
2060     * If this function is reaching up across an
2061     * enclosing funarg, we cannot make a flat
2062     * closure. The display stops working once the
2063     * funarg escapes.
2064     */
2065     if (!afunbox || afunbox->node->isFunArg())
2066     goto break2;
2067     }
2068    
2069     /*
2070     * If afunbox's function (which is at the same level as
2071     * lexdep) is in a loop, pessimistically assume the
2072     * variable initializer may be in the same loop. A flat
2073     * closure would then be unsafe, as the captured
2074     * variable could be assigned after the closure is
2075     * created. See bug 493232.
2076     */
2077     if (afunbox->inLoop)
2078     break;
2079    
2080     /*
2081     * with and eval defeat lexical scoping; eval anywhere
2082     * in a variable's scope can assign to it. Both defeat
2083     * the flat closure optimization. The parser detects
2084     * these cases and flags the function heavyweight.
2085     */
2086     if ((afunbox->parent ? afunbox->parent->tcflags : tcflags)
2087     & TCF_FUN_HEAVYWEIGHT) {
2088     break;
2089     }
2090    
2091     /*
2092     * If afunbox's function is not a lambda, it will be
2093     * hoisted, so it could capture the undefined value
2094     * that by default initializes var/let/const
2095     * bindings. And if lexdep is a function that comes at
2096     * (meaning a function refers to its own name) or
2097     * strictly after afunbox, we also break to defeat the
2098     * flat closure optimization.
2099     */
2100     JSFunction *afun = (JSFunction *) afunbox->object;
2101     if (!(afun->flags & JSFUN_LAMBDA)) {
2102     if (lexdep->isBindingForm())
2103     break;
2104     if (lexdep->pn_pos >= afunbox->node->pn_pos)
2105     break;
2106     }
2107    
2108     if (!lexdep->isInitialized())
2109     break;
2110    
2111     JSDefinition::Kind lexdepKind = lexdep->kind();
2112     if (lexdepKind != JSDefinition::CONST) {
2113     if (lexdep->isAssigned())
2114     break;
2115    
2116     /*
2117     * Any formal could be mutated behind our back via
2118     * the arguments object, so deoptimize if the outer
2119     * function uses arguments.
2120     *
2121     * In a Function constructor call where the final
2122     * argument -- the body source for the function to
2123     * create -- contains a nested function definition
2124     * or expression, afunbox->parent will be null. The
2125     * body source might use |arguments| outside of any
2126     * nested functions it may contain, so we have to
2127     * check the tcflags parameter that was passed in
2128     * from JSCompiler::compileFunctionBody.
2129     */
2130     if (lexdepKind == JSDefinition::ARG &&
2131     ((afunbox->parent ? afunbox->parent->tcflags : tcflags) &
2132     TCF_FUN_USES_ARGUMENTS)) {
2133     break;
2134     }
2135     }
2136    
2137     /*
2138     * Check quick-and-dirty dominance relation. Function
2139     * definitions dominate their uses thanks to hoisting.
2140     * Other binding forms hoist as undefined, of course,
2141     * so check forward-reference and blockid relations.
2142     */
2143     if (lexdepKind != JSDefinition::FUNCTION) {
2144     /*
2145     * Watch out for code such as
2146     *
2147     * (function () {
2148     * ...
2149     * var jQuery = ... = function (...) {
2150     * return new jQuery.foo.bar(baz);
2151     * }
2152     * ...
2153     * })();
2154     *
2155     * where the jQuery var is not reassigned, but of
2156     * course is not initialized at the time that the
2157     * would-be-flat closure containing the jQuery
2158     * upvar is formed.
2159     */
2160     if (lexdep->pn_pos.end >= afunbox->node->pn_pos.end)
2161     break;
2162    
2163     if (lexdep->isTopLevel()
2164     ? !MinBlockId(afunbox->node, lexdep->pn_blockid)
2165     : !lexdep->isBlockChild() ||
2166     !afunbox->node->isBlockChild() ||
2167     !OneBlockId(afunbox->node, lexdep->pn_blockid)) {
2168     break;
2169     }
2170     }
2171     }
2172     }
2173    
2174     break2:
2175     if (nupvars == 0) {
2176     FUN_METER(onlyfreevar);
2177     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
2178     } else if (!ale) {
2179     /*
2180     * We made it all the way through the upvar loop, so it's
2181     * safe to optimize to a flat closure.
2182     */
2183     FUN_METER(flat);
2184     FUN_SET_KIND(fun, JSFUN_FLAT_CLOSURE);
2185     switch (PN_OP(fn)) {
2186     case JSOP_DEFFUN:
2187     fn->pn_op = JSOP_DEFFUN_FC;
2188     break;
2189     case JSOP_DEFLOCALFUN:
2190     fn->pn_op = JSOP_DEFLOCALFUN_FC;
2191     break;
2192     case JSOP_LAMBDA:
2193     fn->pn_op = JSOP_LAMBDA_FC;
2194     break;
2195     default:
2196     /* js_EmitTree's case TOK_FUNCTION: will select op. */
2197     JS_ASSERT(PN_OP(fn) == JSOP_NOP);
2198     }
2199     } else {
2200     FUN_METER(badfunarg);
2201     }
2202     }
2203     }
2204    
2205     if (FUN_KIND(fun) == JSFUN_INTERPRETED) {
2206     if (pn->pn_type != TOK_UPVARS) {
2207     if (parent)
2208     parent->tcflags |= TCF_FUN_HEAVYWEIGHT;
2209     } else {
2210     JSAtomList upvars(pn->pn_names);
2211     JS_ASSERT(upvars.count != 0);
2212    
2213     JSAtomListIterator iter(&upvars);
2214     JSAtomListElement *ale;
2215    
2216     /*
2217     * One or more upvars cannot be safely snapshot into a flat
2218     * closure's dslot (see JSOP_GETDSLOT), so we loop again over
2219     * all upvars, and for each non-free upvar, ensure that its
2220     * containing function has been flagged as heavyweight.
2221     *
2222     * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
2223     * generating any code for a tree of nested functions.
2224     */
2225     while ((ale = iter()) != NULL) {
2226     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
2227    
2228     if (!lexdep->isFreeVar()) {
2229     JSFunctionBox *afunbox = funbox->parent;
2230     uintN lexdepLevel = lexdep->frameLevel();
2231    
2232     while (afunbox) {
2233     /*
2234     * NB: afunbox->level is the static level of
2235     * the definition or expression of the function
2236     * parsed into afunbox, not the static level of
2237     * its body. Therefore we must add 1 to match
2238     * lexdep's level to find the afunbox whose
2239     * body contains the lexdep definition.
2240     */
2241     if (afunbox->level + 1U == lexdepLevel ||
2242     (lexdepLevel == 0 && lexdep->isLet())) {
2243     afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
2244     break;
2245     }
2246     afunbox = afunbox->parent;
2247     }
2248     if (!afunbox && (tcflags & TCF_IN_FUNCTION))
2249     tcflags |= TCF_FUN_HEAVYWEIGHT;
2250     }
2251     }
2252     }
2253     }
2254    
2255     funbox = funbox->siblings;
2256     if (!funbox)
2257     break;
2258     JS_ASSERT(funbox->parent == parent);
2259     }
2260     #undef FUN_METER
2261     }
2262    
2263     const char js_argument_str[] = "argument";
2264     const char js_variable_str[] = "variable";
2265     const char js_unknown_str[] = "unknown";
2266    
2267     const char *
2268     JSDefinition::kindString(Kind kind)
2269     {
2270     static const char *table[] = {
2271     js_var_str, js_const_str, js_let_str,
2272     js_function_str, js_argument_str, js_unknown_str
2273     };
2274    
2275     JS_ASSERT(unsigned(kind) <= unsigned(ARG));
2276     return table[kind];
2277     }
2278    
2279     static JSFunctionBox *
2280     EnterFunction(JSParseNode *fn, JSTreeContext *tc, JSTreeContext *funtc,
2281     JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2282     {
2283     JSFunction *fun = tc->compiler->newFunction(tc, funAtom, lambda);
2284     if (!fun)
2285     return NULL;
2286    
2287     /* Create box for fun->object early to protect against last-ditch GC. */
2288     JSFunctionBox *funbox = tc->compiler->newFunctionBox(FUN_OBJECT(fun), fn, tc);
2289     if (!funbox)
2290     return NULL;
2291    
2292     /* Initialize non-default members of funtc. */
2293     funtc->flags |= funbox->tcflags;
2294     funtc->blockidGen = tc->blockidGen;
2295     if (!GenerateBlockId(funtc, funtc->bodyid))
2296     return NULL;
2297     funtc->fun = fun;
2298     funtc->funbox = funbox;
2299     funtc->parent = tc;
2300     if (!SetStaticLevel(funtc, tc->staticLevel + 1))
2301     return NULL;
2302    
2303     return funbox;
2304     }
2305    
2306     static bool
2307     LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
2308     JSAtom *funAtom = NULL, uintN lambda = JSFUN_LAMBDA)
2309     {
2310     tc->blockidGen = funtc->blockidGen;
2311    
2312     fn->pn_funbox->tcflags |= funtc->flags & (TCF_FUN_FLAGS | TCF_COMPILE_N_GO);
2313    
2314     fn->pn_dflags |= PND_INITIALIZED;
2315     JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
2316     fn->pn_dflags & PND_TOPLEVEL);
2317     if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
2318     fn->pn_dflags |= PND_BLOCKCHILD;
2319    
2320     /*
2321     * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
2322     * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
2323     * params and body. We do this only if there are lexical dependencies not
2324     * satisfied by the function's declarations, to avoid penalizing functions
2325     * that use only their arguments and other local bindings.
2326     */
2327     if (funtc->lexdeps.count != 0) {
2328     JSAtomListIterator iter(&funtc->lexdeps);
2329     JSAtomListElement *ale;
2330     int foundCallee = 0;
2331    
2332     while ((ale = iter()) != NULL) {
2333     JSAtom *atom = ALE_ATOM(ale);
2334     JSDefinition *dn = ALE_DEFN(ale);
2335     JS_ASSERT(dn->isPlaceholder());
2336    
2337     if (atom == funAtom && lambda != 0) {
2338     dn->pn_op = JSOP_CALLEE;
2339     dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
2340     dn->pn_dflags |= PND_BOUND;
2341    
2342     /*
2343     * If this named function expression uses its own name other
2344     * than to call itself, flag this function as using arguments,
2345     * as if it had used arguments.callee instead of its own name.
2346     *
2347     * This abuses the plain sense of TCF_FUN_USES_ARGUMENTS, but
2348     * we are out of tcflags bits at the moment. If it deoptimizes
2349     * code unfairly (see JSCompiler::setFunctionKinds, where this
2350     * flag is interpreted in its broader sense, not only to mean
2351     * "this function might leak arguments.callee"), we can perhaps
2352     * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
2353     * use that more precisely, both here and for unnamed function
2354     * expressions.
2355     */
2356     if (dn->isFunArg())
2357     fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
2358     foundCallee = 1;
2359     continue;
2360     }
2361    
2362     if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
2363     dn->isAssigned()) {
2364     /*
2365     * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
2366     * any use of dn in funtc assigns. See NoteLValue for the easy
2367     * backward-reference case; this is the hard forward-reference
2368     * case where we pay a higher price.
2369     */
2370     for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
2371     if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
2372     fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
2373     break;
2374     }
2375     }
2376     }
2377    
2378     JSAtomListElement *outer_ale = tc->decls.lookup(atom);
2379     if (!outer_ale)
2380     outer_ale = tc->lexdeps.lookup(atom);
2381     if (outer_ale) {
2382     /*
2383     * Insert dn's uses list at the front of outer_dn's list.
2384     *
2385     * Without loss of generality or correctness, we allow a dn to
2386     * be in inner and outer lexdeps, since the purpose of lexdeps
2387     * is one-pass coordination of name use and definition across
2388     * functions, and if different dn's are used we'll merge lists
2389     * when leaving the inner function.
2390     *
2391     * The dn == outer_dn case arises with generator expressions
2392     * (see CompExprTransplanter::transplant, the PN_FUNC/PN_NAME
2393     * case), and nowhere else, currently.
2394     */
2395     JSDefinition *outer_dn = ALE_DEFN(outer_ale);
2396    
2397     if (dn != outer_dn) {
2398     JSParseNode **pnup = &dn->dn_uses;
2399     JSParseNode *pnu;
2400    
2401     while ((pnu = *pnup) != NULL) {
2402     pnu->pn_lexdef = outer_dn;
2403     pnup = &pnu->pn_link;
2404     }
2405    
2406     /*
2407     * Make dn be a use that redirects to outer_dn, because we
2408     * can't replace dn with outer_dn in all the pn_namesets in
2409     * the AST where it may be. Instead we make it forward to
2410     * outer_dn. See JSDefinition::resolve.
2411     */
2412     *pnup = outer_dn->dn_uses;
2413     outer_dn->dn_uses = dn;
2414     outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
2415     dn->pn_defn = false;
2416     dn->pn_used = true;
2417     dn->pn_lexdef = outer_dn;
2418     }
2419     } else {
2420     /* Add an outer lexical dependency for ale's definition. */
2421     outer_ale = tc->lexdeps.add(tc->compiler, atom);
2422     if (!outer_ale)
2423     return false;
2424     ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
2425     }
2426     }
2427    
2428     if (funtc->lexdeps.count - foundCallee != 0) {
2429     JSParseNode *body = fn->pn_body;
2430    
2431     fn->pn_body = NewParseNode(PN_NAMESET, tc);
2432     if (!fn->pn_body)
2433     return false;
2434    
2435     fn->pn_body->pn_type = TOK_UPVARS;
2436     fn->pn_body->pn_pos = body->pn_pos;
2437     if (foundCallee)
2438     funtc->lexdeps.remove(tc->compiler, funAtom);
2439     fn->pn_body->pn_names = funtc->lexdeps;
2440     fn->pn_body->pn_tree = body;
2441     }
2442    
2443     funtc->lexdeps.clear();
2444     }
2445    
2446     return true;
2447     }
2448    
2449 siliconforks 332 static JSParseNode *
2450     FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2451     uintN lambda)
2452     {
2453 siliconforks 460 JSOp op;
2454 siliconforks 332 JSParseNode *pn, *body, *result;
2455     JSTokenType tt;
2456     JSAtom *funAtom;
2457     JSAtomListElement *ale;
2458     #if JS_HAS_DESTRUCTURING
2459     JSParseNode *item, *list = NULL;
2460 siliconforks 507 bool destructuringArg = false;
2461     JSAtom *duplicatedArg = NULL;
2462 siliconforks 332 #endif
2463    
2464     /* Make a TOK_FUNCTION node. */
2465     #if JS_HAS_GETTER_SETTER
2466     op = CURRENT_TOKEN(ts).t_op;
2467     #endif
2468 siliconforks 460 pn = NewParseNode(PN_FUNC, tc);
2469 siliconforks 332 if (!pn)
2470     return NULL;
2471 siliconforks 460 pn->pn_body = NULL;
2472     pn->pn_cookie = FREE_UPVAR_COOKIE;
2473 siliconforks 332
2474 siliconforks 460 /*
2475     * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
2476     * is immediately applied (we clear PND_FUNARG if so -- see MemberExpr).
2477     *
2478     * Also treat function sub-statements (non-lambda, non-top-level functions)
2479     * as escaping funargs, since we can't statically analyze their definitions
2480     * and uses.
2481     */
2482     bool topLevel = tc->atTopLevel();
2483     pn->pn_dflags = (lambda || !topLevel) ? PND_FUNARG : 0;
2484    
2485 siliconforks 332 /* Scan the optional function name into funAtom. */
2486     ts->flags |= TSF_KEYWORD_IS_NAME;
2487     tt = js_GetToken(cx, ts);
2488     ts->flags &= ~TSF_KEYWORD_IS_NAME;
2489     if (tt == TOK_NAME) {
2490     funAtom = CURRENT_TOKEN(ts).t_atom;
2491     } else {
2492     if (lambda == 0 && (cx->options & JSOPTION_ANONFUNFIX)) {
2493     js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2494     JSMSG_SYNTAX_ERROR);
2495     return NULL;
2496     }
2497     funAtom = NULL;
2498     js_UngetToken(ts);
2499     }
2500    
2501     /*
2502     * Record names for function statements in tc->decls so we know when to
2503     * avoid optimizing variable references that might name a function.
2504     */
2505     if (lambda == 0 && funAtom) {
2506 siliconforks 460 ale = tc->decls.lookup(funAtom);
2507 siliconforks 332 if (ale) {
2508 siliconforks 460 JSDefinition *dn = ALE_DEFN(ale);
2509     JSDefinition::Kind dn_kind = dn->kind();
2510    
2511     JS_ASSERT(!dn->pn_used);
2512     JS_ASSERT(dn->pn_defn);
2513    
2514     if (JS_HAS_STRICT_OPTION(cx) || dn_kind == JSDefinition::CONST) {
2515 siliconforks 332 const char *name = js_AtomToPrintableString(cx, funAtom);
2516     if (!name ||
2517     !js_ReportCompileErrorNumber(cx, ts, NULL,
2518 siliconforks 460 (dn_kind != JSDefinition::CONST)
2519     ? JSREPORT_WARNING | JSREPORT_STRICT
2520 siliconforks 332 : JSREPORT_ERROR,
2521     JSMSG_REDECLARED_VAR,
2522 siliconforks 460 JSDefinition::kindString(dn_kind),
2523 siliconforks 332 name)) {
2524     return NULL;
2525     }
2526     }
2527 siliconforks 460
2528     if (topLevel) {
2529     ALE_SET_DEFN(ale, pn);
2530     pn->pn_defn = true;
2531     pn->dn_uses = dn; /* dn->dn_uses is now pn_link */
2532    
2533     if (!MakeDefIntoUse(dn, pn, funAtom, tc))
2534     return NULL;
2535     }
2536     } else if (topLevel) {
2537     /*
2538     * If this function was used before it was defined, claim the
2539     * pre-created definition node for this function that PrimaryExpr
2540     * put in tc->lexdeps on first forward reference, and recycle pn.
2541     */
2542     JSHashEntry **hep;
2543    
2544     ale = tc->lexdeps.rawLookup(funAtom, hep);
2545     if (ale) {
2546     JSDefinition *fn = ALE_DEFN(ale);
2547    
2548     JS_ASSERT(fn->pn_defn);
2549     fn->pn_type = TOK_FUNCTION;
2550     fn->pn_arity = PN_FUNC;
2551     fn->pn_pos.begin = pn->pn_pos.begin;
2552     fn->pn_body = NULL;
2553     fn->pn_cookie = FREE_UPVAR_COOKIE;
2554    
2555     tc->lexdeps.rawRemove(tc->compiler, ale, hep);
2556     RecycleTree(pn, tc);
2557     pn = fn;
2558     }
2559    
2560     if (!Define(pn, funAtom, tc))
2561 siliconforks 332 return NULL;
2562     }
2563    
2564     /*
2565     * A function nested at top level inside another's body needs only a
2566     * local variable to bind its name to its value, and not an activation
2567     * object property (it might also need the activation property, if the
2568     * outer function contains with statements, e.g., but the stack slot
2569     * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
2570     * JSOP_GETLOCAL bytecode).
2571     */
2572 siliconforks 460 if (topLevel) {
2573     pn->pn_dflags |= PND_TOPLEVEL;
2574 siliconforks 332
2575 siliconforks 460 if (tc->flags & TCF_IN_FUNCTION) {
2576     JSLocalKind localKind;
2577     uintN index;
2578    
2579     /*
2580     * Define a local in the outer function so that BindNameToSlot
2581     * can properly optimize accesses. Note that we need a local
2582     * variable, not an argument, for the function statement. Thus
2583     * we add a variable even if a parameter with the given name
2584     * already exists.
2585     */
2586     localKind = js_LookupLocal(cx, tc->fun, funAtom, &index);
2587     switch (localKind) {
2588     case JSLOCAL_NONE:
2589     case JSLOCAL_ARG:
2590     index = tc->fun->u.i.