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

Contents of /trunk/js/jsparse.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * 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 #include "jsstdint.h"
59 #include "jsarena.h" /* Added by JSIFY */
60 #include "jsutil.h" /* Added by JSIFY */
61 #include "jsapi.h"
62 #include "jsarray.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsversion.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jsiter.h"
70 #include "jslock.h"
71 #include "jsnum.h"
72 #include "jsobj.h"
73 #include "jsopcode.h"
74 #include "jsparse.h"
75 #include "jsscan.h"
76 #include "jsscope.h"
77 #include "jsscript.h"
78 #include "jsstr.h"
79 #include "jsstaticcheck.h"
80 #include "jslibmath.h"
81 #include "jsvector.h"
82
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 #define pn_offsetof(m) offsetof(JSParseNode, m)
95
96 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 /*
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 JSVariablesParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
113 bool inLetHead);
114
115 typedef JSParseNode *
116 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 static JSVariablesParser Variables;
132 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 void
169 JSParseNode::become(JSParseNode *pn2)
170 {
171 JS_ASSERT(!pn_defn);
172 JS_ASSERT(!pn2->pn_defn);
173
174 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 }
185
186 /* 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 pn_parens = pn2->pn_parens;
194 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 pn_parens = false;
206 }
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 if (!tokenStream.init(cx, base, length, fp, filename, lineno)) {
216 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
217 return false;
218 }
219
220 /* Root atoms and objects allocated for the parsed tree. */
221 JS_KEEP_ATOMS(cx->runtime);
222 JS_PUSH_TEMP_ROOT_COMPILER(cx, this, &tempRoot);
223 return true;
224 }
225
226 JSCompiler::~JSCompiler()
227 {
228 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 JS_UNKEEP_ATOMS(cx->runtime);
235 tokenStream.close(cx);
236 JS_ARENA_RELEASE(&cx->tempPool, tempPoolMark);
237 }
238
239 void
240 JSCompiler::setPrincipals(JSPrincipals *prin)
241 {
242 JS_ASSERT(!principals);
243 if (prin)
244 JSPRINCIPALS_HOLD(context, prin);
245 principals = prin;
246 }
247
248 JSObjectBox *
249 JSCompiler::newObjectBox(JSObject *obj)
250 {
251 JS_ASSERT(obj);
252
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 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 JS_ASSERT(obj);
276 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 return NULL;
289 }
290 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 }
312
313 void
314 JSCompiler::trace(JSTracer *trc)
315 {
316 JSObjectBox *objbox;
317
318 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 }
324 }
325
326 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 static JSParseNode *
400 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
401 {
402 JSParseNode *next, **head;
403
404 if (!pn)
405 return NULL;
406
407 /* Catch back-to-back dup recycles. */
408 JS_ASSERT(pn != tc->compiler->nodeList);
409 next = pn->pn_next;
410 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 #ifdef METER_PARSENODES
427 recyclednodes++;
428 #endif
429 }
430 return next;
431 }
432
433 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 static JSParseNode *
460 NewOrRecycledNode(JSTreeContext *tc)
461 {
462 JSParseNode *pn, *pn2;
463
464 pn = tc->compiler->nodeList;
465 if (!pn) {
466 JSContext *cx = tc->compiler->context;
467
468 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
469 if (!pn)
470 js_ReportOutOfScriptQuota(cx);
471 } else {
472 tc->compiler->nodeList = pn->pn_next;
473
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 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 #ifdef METER_PARSENODES
493 recyclednodes += pn->pn_count;
494 #endif
495 break;
496 }
497 }
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 if (!pn->pn_used)
514 RecycleTree(pn->pn_expr, tc);
515 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 pn->pn_used = pn->pn_defn = false;
527 memset(&pn->pn_u, 0, sizeof pn->pn_u);
528 pn->pn_next = NULL;
529 }
530 return pn;
531 }
532
533 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 pn->pn_parens = false;
540 JS_ASSERT(!pn->pn_used);
541 JS_ASSERT(!pn->pn_defn);
542 pn->pn_next = pn->pn_link = NULL;
543 }
544
545 /*
546 * Allocate a JSParseNode from tc's node freelist or, failing that, from cx's
547 * temporary arena.
548 */
549 static JSParseNode *
550 NewParseNode(JSParseNodeArity arity, JSTreeContext *tc)
551 {
552 JSParseNode *pn;
553 JSToken *tp;
554
555 pn = NewOrRecycledNode(tc);
556 if (!pn)
557 return NULL;
558 tp = &CURRENT_TOKEN(&tc->compiler->tokenStream);
559 InitParseNode(pn, tp->type, JSOP_NOP, arity);
560 pn->pn_pos = tp->pos;
561 return pn;
562 }
563
564 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 static JSParseNode *
576 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 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 if (PN_TYPE(left) == tt &&
602 PN_OP(left) == op &&
603 (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 left->pn_parens = false;
608 left->initList(pn1);
609 left->append(pn2);
610 if (tt == TOK_PLUS) {
611 if (pn1->pn_type == TOK_STRING)
612 left->pn_xflags |= PNX_STRCAT;
613 else if (pn1->pn_type != TOK_NUMBER)
614 left->pn_xflags |= PNX_CANTFOLD;
615 if (pn2->pn_type == TOK_STRING)
616 left->pn_xflags |= PNX_STRCAT;
617 else if (pn2->pn_type != TOK_NUMBER)
618 left->pn_xflags |= PNX_CANTFOLD;
619 }
620 }
621 left->append(right);
622 left->pn_pos.end = right->pn_pos.end;
623 if (tt == TOK_PLUS) {
624 if (right->pn_type == TOK_STRING)
625 left->pn_xflags |= PNX_STRCAT;
626 else if (right->pn_type != TOK_NUMBER)
627 left->pn_xflags |= PNX_CANTFOLD;
628 }
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 pn = NewOrRecycledNode(tc);
649 if (!pn)
650 return NULL;
651 InitParseNode(pn, tt, op, PN_BINARY);
652 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 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 /*
728 * Parse a top-level JS script.
729 */
730 JSParseNode *
731 JSCompiler::parse(JSObject *chain)
732 {
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 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 if (pn) {
748 if (!js_MatchToken(context, TS(this), TOK_EOF)) {
749 js_ReportCompileErrorNumber(context, TS(this), NULL, JSREPORT_ERROR,
750 JSMSG_SYNTAX_ERROR);
751 pn = NULL;
752 } else {
753 if (!js_FoldConstants(context, pn, &tc))
754 pn = NULL;
755 }
756 }
757 return pn;
758 }
759
760 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 /*
784 * Compile a top-level script.
785 */
786 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 {
793 JSCompiler jsc(cx, principals, callerFrame);
794 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 TCF_STATIC_LEVEL_MASK)));
805
806 /*
807 * The scripted callerFrame can only be given for compile-and-go scripts
808 * and non-zero static level requires callerFrame.
809 */
810 JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
811 JS_ASSERT_IF(TCF_GET_STATIC_LEVEL(tcflags) != 0, callerFrame);
812
813 if (!jsc.init(chars, length, file, filename, lineno))
814 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 JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
822
823 MUST_FLOW_THROUGH("out");
824
825 /* 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 }
865
866 /*
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 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 for (;;) {
887 jsc.tokenStream.flags |= TSF_OPERAND;
888 tt = js_PeekToken(cx, &jsc.tokenStream);
889 jsc.tokenStream.flags &= ~TSF_OPERAND;
890 if (tt <= TOK_EOF) {
891 if (tt == TOK_EOF)
892 break;
893 JS_ASSERT(tt == TOK_ERROR);
894 goto out;
895 }
896
897 pn = Statement(cx, &jsc.tokenStream, &cg);
898 if (!pn)
899 goto out;
900 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 }
910
911 if (!js_EmitTree(cx, &cg, pn))
912 goto out;
913 #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 }
919 #endif
920 RecycleTree(pn, &cg);
921 }
922
923 #if JS_HAS_XML_SUPPORT
924 /*
925 * 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 if (pn && onlyXML && !callerFrame) {
931 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 * incremental code generation we need to patch the bytecode to adjust the
940 * local references to skip the globals.
941 */
942 scriptGlobals = cg.ngvars + cg.regexpList.length;
943 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 if (js_Emit1(cx, &cg, JSOP_STOP) < 0)
990 goto out;
991 #ifdef METER_PARSENODES
992 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
993 (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
994 #endif
995 #ifdef JS_ARENAMETER
996 JS_DumpArenaStats(stdout);
997 #endif
998 script = js_NewScriptFromCG(cx, &cg);
999 if (script && funbox)
1000 script->flags |= JSSF_SAVED_CALLER_FUN;
1001
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 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1019 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 return HasFinalReturn(pn->last());
1045
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 pn2 = pn2->expr();
1086 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 rv2 = HasFinalReturn(pn3->last());
1093 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 return HasFinalReturn(pn->expr());
1115
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 if (tc->fun->atom) {
1159 name = js_AtomToPrintableString(cx, tc->fun->atom);
1160 } else {
1161 errnum = anonerrnum;
1162 name = NULL;
1163 }
1164 return js_ReportCompileErrorNumber(cx, TS(tc->compiler), NULL, flags,
1165 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 pn = NewParseNode(PN_UNARY, tc);
1202 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 JS_ASSERT(!(tc->topStmt->flags & SIF_SCOPE));
1226 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 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
1237 return pn;
1238 }
1239
1240 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 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1284 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 dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
1316 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 pn->pn_parens = false;
1359 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 pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
1402 }
1403 pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
1404 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 /*
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 bool
1461 JSCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
1462 const jschar *chars, size_t length,
1463 const char *filename, uintN lineno)
1464 {
1465 JSCompiler jsc(cx, principals);
1466
1467 if (!jsc.init(chars, length, NULL, filename, lineno))
1468 return false;
1469
1470 /* No early return from after here until the js_FinishArenaPool calls. */
1471 JSArenaPool codePool, notePool;
1472 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 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 /*
1508 * Farble the body so that it looks like a block statement to js_EmitTree,
1509 * 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 */
1513 CURRENT_TOKEN(&jsc.tokenStream).type = TOK_LC;
1514 JSParseNode *pn = fn ? FunctionBody(cx, &jsc.tokenStream, &funcg) : NULL;
1515 if (pn) {
1516 if (!js_MatchToken(cx, &jsc.tokenStream, TOK_EOF)) {
1517 js_ReportCompileErrorNumber(cx, &jsc.tokenStream, NULL,
1518 JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
1519 pn = NULL;
1520 } 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 } else {
1527 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 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 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 union {
1565 struct {
1566 uintN overflow;
1567 } let;
1568 };
1569 bool fresh;
1570 };
1571
1572 static JSBool
1573 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
1574 JSLocalKind localKind, bool isArg)
1575 {
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 * activation objects whose name is 'arguments'. Assignment to such a
1582 * variable must be handled specially.
1583 *
1584 * Special case: an argument named 'arguments' *does* shadow the predefined
1585 * arguments property.
1586 */
1587 if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
1588 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 JSParseNode *pn;
1606
1607 /* 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 JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
1612
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 }
1619 JS_ASSERT(!tc->decls.lookup(atom));
1620
1621 pn = data->pn;
1622 if (!Define(pn, atom, tc))
1623 return JS_FALSE;
1624
1625 uintN index = tc->fun->u.i.nvars;
1626 if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR, true))
1627 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 return JS_TRUE;
1632 }
1633 #endif /* JS_HAS_DESTRUCTURING */
1634
1635 JSFunction *
1636 JSCompiler::newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda)
1637 {
1638 JSObject *parent;
1639 JSFunction *fun;
1640
1641 JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
1642
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 parent, atom);
1655
1656 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 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 static JSParseNode *
2450 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2451 uintN lambda)
2452 {
2453 JSOp op;
2454 JSParseNode *pn, *body, *result;
2455 JSTokenType tt;
2456 JSAtom *funAtom;
2457 JSAtomListElement *ale;
2458 #if JS_HAS_DESTRUCTURING
2459 JSParseNode *item, *list = NULL;
2460 bool destructuringArg = false;
2461 JSAtom *duplicatedArg = NULL;
2462 #endif
2463
2464 /* Make a TOK_FUNCTION node. */
2465 #if JS_HAS_GETTER_SETTER
2466 op = CURRENT_TOKEN(ts).t_op;
2467 #endif
2468 pn = NewParseNode(PN_FUNC, tc);
2469 if (!pn)
2470 return NULL;
2471 pn->pn_body = NULL;
2472 pn->pn_cookie = FREE_UPVAR_COOKIE;
2473
2474 /*
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 /* 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 ale = tc->decls.lookup(funAtom);
2507 if (ale) {
2508 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 const char *name = js_AtomToPrintableString(cx, funAtom);
2516 if (!name ||
2517 !js_ReportCompileErrorNumber(cx, ts, NULL,
2518 (dn_kind != JSDefinition::CONST)
2519 ? JSREPORT_WARNING | JSREPORT_STRICT
2520 : JSREPORT_ERROR,
2521 JSMSG_REDECLARED_VAR,
2522 JSDefinition::kindString(dn_kind),
2523 name)) {
2524 return NULL;
2525 }
2526 }
2527
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 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 if (topLevel) {
2573 pn->pn_dflags |= PND_TOPLEVEL;
2574
2575 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.nvars;
2591 if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
2592 return NULL;
2593 /* FALL THROUGH */
2594
2595 case JSLOCAL_VAR:
2596 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
2597 pn->pn_dflags |= PND_BOUND;
2598 break;
2599
2600 default:;
2601 }
2602 }
2603 }
2604 }
2605
2606 /* Initialize early for possible flags mutation via DestructuringExpr. */
2607 JSTreeContext funtc(tc->compiler);
2608
2609 JSFunctionBox *funbox = EnterFunction(pn, tc, &funtc, funAtom, lambda);
2610 if (!funbox)
2611 return NULL;
2612
2613 JSFunction *fun = (JSFunction *) funbox->object;
2614
2615 #if JS_HAS_GETTER_SETTER
2616 if (op != JSOP_NOP)
2617 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
2618 #endif
2619
2620 /* Now parse formal argument list and compute fun->nargs. */
2621 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
2622 if (!js_MatchToken(cx, ts, TOK_RP)) {
2623 do {
2624 tt = js_GetToken(cx, ts);
2625 switch (tt) {
2626 #if JS_HAS_DESTRUCTURING
2627 case TOK_LB:
2628 case TOK_LC:
2629 {
2630 BindData data;
2631 JSParseNode *lhs, *rhs;
2632 jsint slot;
2633
2634 /* See comment below in the TOK_NAME case. */
2635 if (duplicatedArg)
2636 goto report_dup_and_destructuring;
2637 destructuringArg = true;
2638
2639 /*
2640 * A destructuring formal parameter turns into one or more
2641 * local variables initialized from properties of a single
2642 * anonymous positional parameter, so here we must tweak our
2643 * binder and its data.
2644 */
2645 data.pn = NULL;
2646 data.op = JSOP_DEFVAR;
2647 data.binder = BindDestructuringArg;
2648 lhs = DestructuringExpr(cx, &data, &funtc, tt);
2649 if (!lhs)
2650 return NULL;
2651
2652 /*
2653 * Adjust fun->nargs to count the single anonymous positional
2654 * parameter that is to be destructured.
2655 */
2656 slot = fun->nargs;
2657 if (!js_AddLocal(cx, fun, NULL, JSLOCAL_ARG))
2658 return NULL;
2659
2660 /*
2661 * Synthesize a destructuring assignment from the single
2662 * anonymous positional parameter into the destructuring
2663 * left-hand-side expression and accumulate it in list.
2664 */
2665 rhs = NewNameNode(cx, ts, cx->runtime->atomState.emptyAtom, &funtc);
2666 if (!rhs)
2667 return NULL;
2668 rhs->pn_type = TOK_NAME;
2669 rhs->pn_op = JSOP_GETARG;
2670 rhs->pn_cookie = MAKE_UPVAR_COOKIE(funtc.staticLevel, slot);
2671 rhs->pn_dflags |= PND_BOUND;
2672
2673 item = NewBinary(TOK_ASSIGN, JSOP_NOP, lhs, rhs, &funtc);
2674 if (!item)
2675 return NULL;
2676 if (!list) {
2677 list = NewParseNode(PN_LIST, &funtc);
2678 if (!list)
2679 return NULL;
2680 list->pn_type = TOK_COMMA;
2681 list->makeEmpty();
2682 }
2683 list->append(item);
2684 break;
2685 }
2686 #endif /* JS_HAS_DESTRUCTURING */
2687
2688 case TOK_NAME:
2689 {
2690 JSAtom *atom = CURRENT_TOKEN(ts).t_atom;
2691 if (!DefineArg(pn, atom, fun->nargs, &funtc))
2692 return NULL;
2693 #if JS_HAS_DESTRUCTURING
2694 /*
2695 * ECMA-262 requires us to support duplicate parameter names, but if the
2696 * parameter list includes destructuring, we consider the code to have
2697 * opted in to higher standards, and forbid duplicates. We may see a
2698 * destructuring parameter later, so always note duplicates now.
2699 *
2700 * Duplicates are warned about (strict option) or cause errors (strict
2701 * mode code), but we do those tests in one place below, after having
2702 * parsed the body.
2703 */
2704 if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2705 duplicatedArg = atom;
2706 if (destructuringArg)
2707 goto report_dup_and_destructuring;
2708 #endif
2709 const char *name = js_AtomToPrintableString(cx, atom);
2710 if (!name ||
2711 !js_ReportCompileErrorNumber(cx, TS(funtc.compiler),
2712 NULL,
2713 JSREPORT_WARNING |
2714 JSREPORT_STRICT,
2715 JSMSG_DUPLICATE_FORMAL,
2716 name)) {
2717 return NULL;
2718 }
2719 }
2720 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2721 return NULL;
2722 break;
2723 }
2724
2725 default:
2726 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2727 JSMSG_MISSING_FORMAL);
2728 /* FALL THROUGH */
2729 case TOK_ERROR:
2730 return NULL;
2731
2732 #if JS_HAS_DESTRUCTURING
2733 report_dup_and_destructuring:
2734 JSDefinition *dn = ALE_DEFN(funtc.decls.lookup(duplicatedArg));
2735 js_ReportCompileErrorNumber(cx, TS(tc->compiler), dn,
2736 JSREPORT_ERROR,
2737 JSMSG_DESTRUCT_DUP_ARG);
2738 return NULL;
2739 #endif
2740 }
2741 } while (js_MatchToken(cx, ts, TOK_COMMA));
2742
2743 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
2744 }
2745
2746 #if JS_HAS_EXPR_CLOSURES
2747 ts->flags |= TSF_OPERAND;
2748 tt = js_GetToken(cx, ts);
2749 ts->flags &= ~TSF_OPERAND;
2750 if (tt != TOK_LC) {
2751 js_UngetToken(ts);
2752 fun->flags |= JSFUN_EXPR_CLOSURE;
2753 }
2754 #else
2755 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
2756 #endif
2757
2758 body = FunctionBody(cx, ts, &funtc);
2759 if (!body)
2760 return NULL;
2761
2762 #if JS_HAS_EXPR_CLOSURES
2763 if (tt == TOK_LC)
2764 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2765 else if (lambda == 0 && !MatchOrInsertSemicolon(cx, ts))
2766 return NULL;
2767 #else
2768 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
2769 #endif
2770 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2771
2772 #if JS_HAS_DESTRUCTURING
2773 /*
2774 * If there were destructuring formal parameters, prepend the initializing
2775 * comma expression that we synthesized to body. If the body is a lexical
2776 * scope node, we must make a special TOK_SEQ node, to prepend the formal
2777 * parameter destructuring code without bracing the decompilation of the
2778 * function body's lexical scope.
2779 */
2780 if (list) {
2781 if (body->pn_arity != PN_LIST) {
2782 JSParseNode *block;
2783
2784 block = NewParseNode(PN_LIST, tc);
2785 if (!block)
2786 return NULL;
2787 block->pn_type = TOK_SEQ;
2788 block->pn_pos = body->pn_pos;
2789 block->initList(body);
2790
2791 body = block;
2792 }
2793
2794 item = NewParseNode(PN_UNARY, tc);
2795 if (!item)
2796 return NULL;
2797
2798 item->pn_type = TOK_SEMI;
2799 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
2800 item->pn_kid = list;
2801 item->pn_next = body->pn_head;
2802 body->pn_head = item;
2803 if (body->pn_tail == &body->pn_head)
2804 body->pn_tail = &item->pn_next;
2805 ++body->pn_count;
2806 body->pn_xflags |= PNX_DESTRUCT;
2807 }
2808 #endif
2809
2810 /*
2811 * If we collected flags that indicate nested heavyweight functions, or
2812 * this function contains heavyweight-making statements (with statement,
2813 * visible eval call, or assignment to 'arguments'), flag the function as
2814 * heavyweight (requiring a call object per invocation).
2815 */
2816 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
2817 fun->flags |= JSFUN_HEAVYWEIGHT;
2818 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2819 } else {
2820 /*
2821 * If this function is a named statement function not at top-level
2822 * (i.e. not a top-level function definiton or expression), then our
2823 * enclosing function, if any, must be heavyweight.
2824 */
2825 if (!topLevel && lambda == 0 && funAtom)
2826 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2827 }
2828
2829 result = pn;
2830 if (lambda != 0) {
2831 /*
2832 * ECMA ed. 3 standard: function expression, possibly anonymous.
2833 */
2834 op = JSOP_LAMBDA;
2835 } else if (!funAtom) {
2836 /*
2837 * If this anonymous function definition is *not* embedded within a
2838 * larger expression, we treat it as an expression statement, not as
2839 * a function declaration -- and not as a syntax error (as ECMA-262
2840 * Edition 3 would have it). Backward compatibility must trump all,
2841 * unless JSOPTION_ANONFUNFIX is set.
2842 */
2843 result = NewParseNode(PN_UNARY, tc);
2844 if (!result)
2845 return NULL;
2846 result->pn_type = TOK_SEMI;
2847 result->pn_pos = pn->pn_pos;
2848 result->pn_kid = pn;
2849 op = JSOP_LAMBDA;
2850 } else if (!topLevel) {
2851 /*
2852 * ECMA ed. 3 extension: a function expression statement not at the
2853 * top level, e.g., in a compound statement such as the "then" part
2854 * of an "if" statement, binds a closure only if control reaches that
2855 * sub-statement.
2856 */
2857 op = JSOP_DEFFUN;
2858 } else {
2859 op = JSOP_NOP;
2860 }
2861
2862 funbox->kids = funtc.functionList;
2863
2864 pn->pn_funbox = funbox;
2865 pn->pn_op = op;
2866 if (pn->pn_body) {
2867 pn->pn_body->append(body);
2868 pn->pn_body->pn_pos = body->pn_pos;
2869 } else {
2870 pn->pn_body = body;
2871 }
2872
2873 pn->pn_blockid = tc->blockid();
2874
2875 if (!LeaveFunction(pn, &funtc, tc, funAtom, lambda))
2876 return NULL;
2877
2878 return result;
2879 }
2880
2881 static JSParseNode *
2882 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2883 {
2884 return FunctionDef(cx, ts, tc, 0);
2885 }
2886
2887 static JSParseNode *
2888 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2889 {
2890 return FunctionDef(cx, ts, tc, JSFUN_LAMBDA);
2891 }
2892
2893 /*
2894 * Parse the statements in a block, creating a TOK_LC node that lists the
2895 * statements' trees. If called from block-parsing code, the caller must
2896 * match { before and } after.
2897 */
2898 static JSParseNode *
2899 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2900 {
2901 JSParseNode *pn, *pn2, *saveBlock;
2902 JSTokenType tt;
2903
2904 JS_CHECK_RECURSION(cx, return NULL);
2905
2906 pn = NewParseNode(PN_LIST, tc);
2907 if (!pn)
2908 return NULL;
2909 pn->pn_type = TOK_LC;
2910 pn->makeEmpty();
2911 pn->pn_blockid = tc->blockid();
2912 saveBlock = tc->blockNode;
2913 tc->blockNode = pn;
2914
2915 for (;;) {
2916 ts->flags |= TSF_OPERAND;
2917 tt = js_PeekToken(cx, ts);
2918 ts->flags &= ~TSF_OPERAND;
2919 if (tt <= TOK_EOF || tt == TOK_RC) {
2920 if (tt == TOK_ERROR) {
2921 if (ts->flags & TSF_EOF)
2922 ts->flags |= TSF_UNEXPECTED_EOF;
2923 return NULL;
2924 }
2925 break;
2926 }
2927 pn2 = Statement(cx, ts, tc);
2928 if (!pn2) {
2929 if (ts->flags & TSF_EOF)
2930 ts->flags |= TSF_UNEXPECTED_EOF;
2931 return NULL;
2932 }
2933
2934 if (pn2->pn_type == TOK_FUNCTION) {
2935 /*
2936 * PNX_FUNCDEFS notifies the emitter that the block contains top-
2937 * level function definitions that should be processed before the
2938 * rest of nodes.
2939 *
2940 * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
2941 * is relevant only for function definitions not at top-level,
2942 * which we call function statements.
2943 */
2944 if (tc->atTopLevel())
2945 pn->pn_xflags |= PNX_FUNCDEFS;
2946 else
2947 tc->flags |= TCF_HAS_FUNCTION_STMT;
2948 }
2949 pn->append(pn2);
2950 }
2951
2952 /*
2953 * Handle the case where there was a let declaration under this block. If
2954 * it replaced tc->blockNode with a new block node then we must refresh pn
2955 * and then restore tc->blockNode.
2956 */
2957 if (tc->blockNode != pn)
2958 pn = tc->blockNode;
2959 tc->blockNode = saveBlock;
2960
2961 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2962 return pn;
2963 }
2964
2965 static JSParseNode *
2966 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2967 {
2968 JSParseNode *pn;
2969
2970 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
2971 pn = ParenExpr(cx, ts, tc, NULL, NULL);
2972 if (!pn)
2973 return NULL;
2974 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
2975
2976 /* Check for (a = b) and warn about possible (a == b) mistype. */
2977 if (pn->pn_type == TOK_ASSIGN &&
2978 pn->pn_op == JSOP_NOP &&
2979 !pn->pn_parens &&
2980 !js_ReportCompileErrorNumber(cx, ts, NULL,
2981 JSREPORT_WARNING | JSREPORT_STRICT,
2982 JSMSG_EQUAL_AS_ASSIGN,
2983 "")) {
2984 return NULL;
2985 }
2986 return pn;
2987 }
2988
2989 static JSBool
2990 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
2991 {
2992 JSAtom *label;
2993 JSTokenType tt;
2994
2995 tt = js_PeekTokenSameLine(cx, ts);
2996 if (tt == TOK_ERROR)
2997 return JS_FALSE;
2998 if (tt == TOK_NAME) {
2999 (void) js_GetToken(cx, ts);
3000 label = CURRENT_TOKEN(ts).t_atom;
3001 } else {
3002 label = NULL;
3003 }
3004 pn->pn_atom = label;
3005 return JS_TRUE;
3006 }
3007
3008 static JSBool
3009 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3010 {
3011 JSParseNode *pn;
3012 JSObject *blockObj;
3013 JSAtomListElement *ale;
3014 jsint n;
3015
3016 /*
3017 * Top-level 'let' is the same as 'var' currently -- this may change in a
3018 * successor standard to ES3.1 that specifies 'let'.
3019 */
3020 JS_ASSERT(!tc->atTopLevel());
3021
3022 pn = data->pn;
3023 blockObj = tc->blockChain;
3024 ale = tc->decls.lookup(atom);
3025 if (ale && ALE_DEFN(ale)->pn_blockid == tc->blockid()) {
3026 const char *name = js_AtomToPrintableString(cx, atom);
3027 if (name) {
3028 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3029 JSREPORT_ERROR, JSMSG_REDECLARED_VAR,
3030 (ale && ALE_DEFN(ale)->isConst())
3031 ? js_const_str
3032 : js_variable_str,
3033 name);
3034 }
3035 return JS_FALSE;
3036 }
3037
3038 n = OBJ_BLOCK_COUNT(cx, blockObj);
3039 if (n == JS_BIT(16)) {
3040 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3041 JSREPORT_ERROR, data->let.overflow);
3042 return JS_FALSE;
3043 }
3044
3045 /*
3046 * Pass push = true to Define so it pushes an ale ahead of any outer scope.
3047 * This is balanced by PopStatement, defined immediately below.
3048 */
3049 if (!Define(pn, atom, tc, true))
3050 return JS_FALSE;
3051
3052 /*
3053 * Assign block-local index to pn->pn_cookie right away, encoding it as an
3054 * upvar cookie whose skip tells the current static level. The emitter will
3055 * adjust the node's slot based on its stack depth model -- and, for global
3056 * and eval code, JSCompiler::compileScript will adjust the slot again to
3057 * include script->nfixed.
3058 */
3059 pn->pn_op = JSOP_GETLOCAL;
3060 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, n);
3061 pn->pn_dflags |= PND_LET | PND_BOUND;
3062
3063 /*
3064 * Define the let binding's property before storing pn in a reserved slot,
3065 * since block_reserveSlots depends on OBJ_SCOPE(blockObj)->entryCount.
3066 */
3067 if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
3068 return JS_FALSE;
3069
3070 /*
3071 * Store pn temporarily in what would be reserved slots in a cloned block
3072 * object (once the prototype's final population is known, after all 'let'
3073 * bindings for this block have been parsed). We will free these reserved
3074 * slots in jsemit.cpp:EmitEnterBlock.
3075 */
3076 uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
3077 if (slot >= STOBJ_NSLOTS(blockObj) &&
3078 !js_GrowSlots(cx, blockObj, slot + 1)) {
3079 return JS_FALSE;
3080 }
3081 OBJ_SCOPE(blockObj)->freeslot = slot + 1;
3082 STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn));
3083 return JS_TRUE;
3084 }
3085
3086 static void
3087 PopStatement(JSTreeContext *tc)
3088 {
3089 JSStmtInfo *stmt = tc->topStmt;
3090
3091 if (stmt->flags & SIF_SCOPE) {
3092 JSObject *obj = stmt->blockObj;
3093 JSScope *scope = OBJ_SCOPE(obj);
3094 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3095
3096 for (JSScopeProperty *sprop = scope->lastProp; sprop; sprop = sprop->parent) {
3097 JSAtom *atom = JSID_TO_ATOM(sprop->id);
3098
3099 /* Beware the empty destructuring dummy. */
3100 if (atom == tc->compiler->context->runtime->atomState.emptyAtom)
3101 continue;
3102 tc->decls.remove(tc->compiler, atom);
3103 }
3104
3105 /*
3106 * The block scope will not be modified again. It may be shared. Clear
3107 * scope->object to make scope->owned() false.
3108 */
3109 scope->object = NULL;
3110 }
3111 js_PopStatement(tc);
3112 }
3113
3114 static inline bool
3115 OuterLet(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *atom)
3116 {
3117 while (stmt->downScope) {
3118 stmt = js_LexicalLookup(tc, atom, NULL, stmt->downScope);
3119 if (!stmt)
3120 return false;
3121 if (stmt->type == STMT_BLOCK)
3122 return true;
3123 }
3124 return false;
3125 }
3126
3127 static JSBool
3128 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
3129 {
3130 JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
3131 JSParseNode *pn = data->pn;
3132
3133 if (stmt && stmt->type == STMT_WITH) {
3134 pn->pn_op = JSOP_NAME;
3135 data->fresh = false;
3136 return JS_TRUE;
3137 }
3138
3139 JSAtomListElement *ale = tc->decls.lookup(atom);
3140 JSOp op = data->op;
3141
3142 if (stmt || ale) {
3143 JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
3144 JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
3145 const char *name;
3146
3147 if (dn_kind == JSDefinition::ARG) {
3148 name = js_AtomToPrintableString(cx, atom);
3149 if (!name)
3150 return JS_FALSE;
3151
3152 if (op == JSOP_DEFCONST) {
3153 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3154 JSREPORT_ERROR, JSMSG_REDECLARED_PARAM,
3155 name);
3156 return JS_FALSE;
3157 }
3158 if (!js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3159 JSREPORT_WARNING | JSREPORT_STRICT,
3160 JSMSG_VAR_HIDES_ARG, name)) {
3161 return JS_FALSE;
3162 }
3163 } else {
3164 bool error = (op == JSOP_DEFCONST ||
3165 dn_kind == JSDefinition::CONST ||
3166 (dn_kind == JSDefinition::LET &&
3167 (stmt->type != STMT_CATCH || OuterLet(tc, stmt, atom))));
3168
3169 if (JS_HAS_STRICT_OPTION(cx)
3170 ? op != JSOP_DEFVAR || dn_kind != JSDefinition::VAR
3171 : error) {
3172 name = js_AtomToPrintableString(cx, atom);
3173 if (!name ||
3174 !js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn,
3175 !error
3176 ? JSREPORT_WARNING | JSREPORT_STRICT
3177 : JSREPORT_ERROR,
3178 JSMSG_REDECLARED_VAR,
3179 JSDefinition::kindString(dn_kind),
3180 name)) {
3181 return JS_FALSE;
3182 }
3183 }
3184 }
3185 }
3186
3187 if (!ale) {
3188 if (!Define(pn, atom, tc))
3189 return JS_FALSE;
3190 } else {
3191 /*
3192 * A var declaration never recreates an existing binding, it restates
3193 * it and possibly reinitializes its value. Beware that if pn becomes a
3194 * use of ALE_DEFN(ale), and if we have an initializer for this var or
3195 * const (typically a const would ;-), then pn must be rewritten into a
3196 * TOK_ASSIGN node. See Variables, further below.
3197 *
3198 * A case such as let (x = 1) { var x = 2; print(x); } is even harder.
3199 * There the x definition is hoisted but the x = 2 assignment mutates
3200 * the block-local binding of x.
3201 */
3202 JSDefinition *dn = ALE_DEFN(ale);
3203
3204 data->fresh = false;
3205
3206 if (!pn->pn_used) {
3207 /* Make pnu be a fresh name node that uses dn. */
3208 JSParseNode *pnu = pn;
3209
3210 if (pn->pn_defn) {
3211 pnu = NewNameNode(cx, TS(tc->compiler), atom, tc);
3212 if (!pnu)
3213 return JS_FALSE;
3214 }
3215
3216 LinkUseToDef(pnu, dn, tc);
3217 pnu->pn_op = JSOP_NAME;
3218 }
3219
3220 while (dn->kind() == JSDefinition::LET) {
3221 do {
3222 ale = ALE_NEXT(ale);
3223 } while (ale && ALE_ATOM(ale) != atom);
3224 if (!ale)
3225 break;
3226 dn = ALE_DEFN(ale);
3227 }
3228
3229 if (ale) {
3230 JS_ASSERT_IF(data->op == JSOP_DEFCONST,
3231 dn->kind() == JSDefinition::CONST);
3232 return JS_TRUE;
3233 }
3234
3235 /*
3236 * A var or const that is shadowed by one or more let bindings of the
3237 * same name, but that has not been declared until this point, must be
3238 * hoisted above the let bindings.
3239 */
3240 if (!pn->pn_defn) {
3241 JSHashEntry **hep;
3242
3243 ale = tc->lexdeps.rawLookup(atom, hep);
3244 if (ale) {
3245 pn = ALE_DEFN(ale);
3246 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
3247 } else {
3248 JSParseNode *pn2 = NewNameNode(cx, TS(tc->compiler), atom, tc);
3249 if (!pn2)
3250 return JS_FALSE;
3251
3252 /* The token stream may be past the location for pn. */
3253 pn2->pn_type = TOK_NAME;
3254 pn2->pn_pos = pn->pn_pos;
3255 pn = pn2;
3256 }
3257 pn->pn_op = JSOP_NAME;
3258 }
3259
3260 ale = tc->decls.add(tc->compiler, atom, JSAtomList::HOIST);
3261 if (!ale)
3262 return JS_FALSE;
3263 ALE_SET_DEFN(ale, pn);
3264 pn->pn_defn = true;
3265 pn->pn_dflags &= ~PND_PLACEHOLDER;
3266 }
3267
3268 if (data->op == JSOP_DEFCONST)
3269 pn->pn_dflags |= PND_CONST;
3270
3271 if (!(tc->flags & TCF_IN_FUNCTION)) {
3272 /*
3273 * If we are generating global or eval-called-from-global code, bind a
3274 * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
3275 * up global variable access by memoizing name-to-slot mappings in the
3276 * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
3277 * can't be done due to a pre-existing property of the same name as the
3278 * var or const but incompatible attributes/getter/setter/etc, these
3279 * ops devolve to JSOP_NAME, etc.
3280 *
3281 * For now, don't try to lookup eval frame variables at compile time.
3282 * Seems sub-optimal: why couldn't we find eval-called-from-a-function
3283 * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
3284 */
3285 pn->pn_op = JSOP_NAME;
3286 if ((tc->flags & TCF_COMPILING) && !tc->compiler->callerFrame) {
3287 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
3288
3289 /* Index atom so we can map fast global number to name. */
3290 ale = cg->atomList.add(tc->compiler, atom);
3291 if (!ale)
3292 return JS_FALSE;
3293
3294 /* Defend against cg->ngvars 16-bit overflow. */
3295 uintN slot = ALE_INDEX(ale);
3296 if ((slot + 1) >> 16)
3297 return JS_TRUE;
3298
3299 if ((uint16)(slot + 1) > cg->ngvars)
3300 cg->ngvars = (uint16)(slot + 1);
3301
3302 pn->pn_op = JSOP_GETGVAR;
3303 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
3304 pn->pn_dflags |= PND_BOUND | PND_GVAR;
3305 }
3306 return JS_TRUE;
3307 }
3308
3309 if (atom == cx->runtime->atomState.argumentsAtom) {
3310 pn->pn_op = JSOP_ARGUMENTS;
3311 pn->pn_dflags |= PND_BOUND;
3312 return JS_TRUE;
3313 }
3314
3315 JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
3316 if (localKind == JSLOCAL_NONE) {
3317 /*
3318 * Property not found in current variable scope: we have not seen this
3319 * variable before. Define a new local variable by adding a property to
3320 * the function's scope and allocating one slot in the function's vars
3321 * frame. Any locals declared in a with statement body are handled at
3322 * runtime, by script prolog JSOP_DEFVAR opcodes generated for global
3323 * and heavyweight-function-local vars.
3324 */
3325 localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
3326
3327 uintN index = tc->fun->u.i.nvars;
3328 if (!BindLocalVariable(cx, tc->fun, atom, localKind, false))
3329 return JS_FALSE;
3330 pn->pn_op = JSOP_GETLOCAL;
3331 pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, index);
3332 pn->pn_dflags |= PND_BOUND;
3333 return JS_TRUE;
3334 }
3335
3336 if (localKind == JSLOCAL_ARG) {
3337 /* We checked errors and strict warnings earlier -- see above. */
3338 JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
3339 } else {
3340 /* Not an argument, must be a redeclared local var. */
3341 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
3342 }
3343 pn->pn_op = JSOP_NAME;
3344 return JS_TRUE;
3345 }
3346
3347 static JSBool
3348 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
3349 {
3350 JSParseNode *pn2;
3351
3352 JS_ASSERT(pn->pn_arity == PN_LIST);
3353 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL || pn->pn_op == JSOP_APPLY);
3354 pn2 = pn->pn_head;
3355 if (pn2->pn_type == TOK_FUNCTION && (pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA)) {
3356 js_ReportCompileErrorNumber(cx, TS(tc->compiler), pn, JSREPORT_ERROR, msg);
3357 return JS_FALSE;
3358 }
3359 pn->pn_op = JSOP_SETCALL;
3360 return JS