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 : #include "frontend/SemanticAnalysis.h"
42 :
43 : #include "jsfun.h"
44 :
45 : #include "frontend/BytecodeEmitter.h"
46 : #include "frontend/Parser.h"
47 :
48 : #include "jsobjinlines.h"
49 : #include "jsfuninlines.h"
50 :
51 : using namespace js;
52 : using namespace js::frontend;
53 :
54 : /*
55 : * Walk the function box list at |*funboxHead|, removing boxes for deleted
56 : * functions and cleaning up method lists. We do this once, before
57 : * performing function analysis, to avoid traversing possibly long function
58 : * lists repeatedly when recycling nodes.
59 : *
60 : * There are actually three possible states for function boxes and their
61 : * nodes:
62 : *
63 : * - Live: funbox->node points to the node, and funbox->node->pn_funbox
64 : * points back to the funbox.
65 : *
66 : * - Recycled: funbox->node points to the node, but funbox->node->pn_funbox
67 : * is NULL. When a function node is part of a tree that gets recycled, we
68 : * must avoid corrupting any method list the node is on, so we leave the
69 : * function node unrecycled until we call CleanFunctionList. At recycle
70 : * time, we clear such nodes' pn_funbox pointers to indicate that they
71 : * are deleted and should be recycled once we get here.
72 : *
73 : * - Mutated: funbox->node is NULL; the contents of the node itself could
74 : * be anything. When we mutate a function node into some other kind of
75 : * node, we lose all indication that the node was ever part of the
76 : * function box tree; it could later be recycled, reallocated, and turned
77 : * into anything at all. (Fortunately, method list members never get
78 : * mutated, so we don't have to worry about that case.)
79 : * ParseNodeAllocator::prepareNodeForMutation clears the node's function
80 : * box's node pointer, disconnecting it entirely from the function box tree,
81 : * and marking the function box to be trimmed out.
82 : */
83 : static void
84 1879979 : CleanFunctionList(ParseNodeAllocator *allocator, FunctionBox **funboxHead)
85 : {
86 1879979 : FunctionBox **link = funboxHead;
87 3836993 : while (FunctionBox *box = *link) {
88 978507 : if (!box->node) {
89 : /*
90 : * This funbox's parse node was mutated into something else. Drop the box,
91 : * and stay at the same link.
92 : */
93 0 : *link = box->siblings;
94 978507 : } else if (!box->node->pn_funbox) {
95 : /*
96 : * This funbox's parse node is ready to be recycled. Drop the box, recycle
97 : * the node, and stay at the same link.
98 : */
99 0 : *link = box->siblings;
100 0 : allocator->freeNode(box->node);
101 : } else {
102 : /* The function is still live. */
103 :
104 : /* First, remove nodes for deleted functions from our methods list. */
105 : {
106 978507 : ParseNode **methodLink = &box->methods;
107 993039 : while (ParseNode *method = *methodLink) {
108 : /* Method nodes are never rewritten in place to be other kinds of nodes. */
109 7266 : JS_ASSERT(method->isArity(PN_FUNC));
110 7266 : if (!method->pn_funbox) {
111 : /* Deleted: drop the node, and stay on this link. */
112 0 : *methodLink = method->pn_link;
113 : } else {
114 : /* Live: keep the node, and move to the next link. */
115 7266 : methodLink = &method->pn_link;
116 : }
117 : }
118 : }
119 :
120 : /* Second, remove boxes for deleted functions from our kids list. */
121 978507 : CleanFunctionList(allocator, &box->kids);
122 :
123 : /* Keep the box on the list, and move to the next link. */
124 978507 : link = &box->siblings;
125 : }
126 : }
127 1879979 : }
128 :
129 : /*
130 : * Mark as funargs any functions that reach up to one or more upvars across an
131 : * already-known funarg. The parser will flag the o_m lambda as a funarg in:
132 : *
133 : * function f(o, p) {
134 : * o.m = function o_m(a) {
135 : * function g() { return p; }
136 : * function h() { return a; }
137 : * return g() + h();
138 : * }
139 : * }
140 : *
141 : * but without this extra marking phase, function g will not be marked as a
142 : * funarg since it is called from within its parent scope. But g reaches up to
143 : * f's parameter p, so if o_m escapes f's activation scope, g does too and
144 : * cannot assume that p's stack slot is still alive. In contast function h
145 : * neither escapes nor uses an upvar "above" o_m's level.
146 : *
147 : * If function g itself contained lambdas that contained non-lambdas that reach
148 : * up above its level, then those non-lambdas would have to be marked too. This
149 : * process is potentially exponential in the number of functions, but generally
150 : * not so complex. But it can't be done during a single recursive traversal of
151 : * the funbox tree, so we must use a work queue.
152 : *
153 : * Return the minimal "skipmin" for funbox and its siblings. This is the delta
154 : * between the static level of the bodies of funbox and its peers (which must
155 : * be funbox->level + 1), and the static level of the nearest upvar among all
156 : * the upvars contained by funbox and its peers. If there are no upvars, return
157 : * FREE_STATIC_LEVEL. Thus this function never returns 0.
158 : */
159 : static unsigned
160 497641 : FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue)
161 : {
162 497641 : unsigned allskipmin = UpvarCookie::FREE_LEVEL;
163 :
164 991294 : do {
165 991294 : ParseNode *fn = funbox->node;
166 991294 : JS_ASSERT(fn->isArity(PN_FUNC));
167 991294 : int fnlevel = level;
168 :
169 : /*
170 : * An eval can leak funbox, functions along its ancestor line, and its
171 : * immediate kids. Since FindFunArgs uses DFS and the parser propagates
172 : * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
173 : * already been marked as funargs by this point. Therefore we have to
174 : * flag only funbox->node and funbox->kids' nodes here.
175 : *
176 : * Generators need to be treated in the same way. Even if the value
177 : * of a generator function doesn't escape, anything defined or referred
178 : * to inside the generator can escape through a call to the generator.
179 : * We could imagine doing static analysis to track the calls and see
180 : * if any iterators or values returned by iterators escape, but that
181 : * would be hard, so instead we just assume everything might escape.
182 : */
183 991294 : if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) {
184 11587 : fn->setFunArg();
185 22247 : for (FunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
186 10660 : kid->node->setFunArg();
187 : }
188 :
189 : /*
190 : * Compute in skipmin the least distance from fun's static level up to
191 : * an upvar, whether used directly by fun, or indirectly by a function
192 : * nested in fun.
193 : */
194 991294 : unsigned skipmin = UpvarCookie::FREE_LEVEL;
195 991294 : ParseNode *pn = fn->pn_body;
196 :
197 991294 : if (pn->isKind(PNK_UPVARS)) {
198 823672 : AtomDefnMapPtr &upvars = pn->pn_names;
199 823672 : JS_ASSERT(upvars->count() != 0);
200 :
201 3283575 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
202 2459903 : Definition *defn = r.front().value();
203 2459903 : Definition *lexdep = defn->resolve();
204 :
205 2459903 : if (!lexdep->isFreeVar()) {
206 388225 : unsigned upvarLevel = lexdep->frameLevel();
207 :
208 388225 : if (int(upvarLevel) <= fnlevel)
209 67199 : fn->setFunArg();
210 :
211 388225 : unsigned skip = (funbox->level + 1) - upvarLevel;
212 388225 : if (skip < skipmin)
213 225530 : skipmin = skip;
214 : }
215 : }
216 : }
217 :
218 : /*
219 : * If this function escapes, whether directly (the parser detects such
220 : * escapes) or indirectly (because this non-escaping function uses an
221 : * upvar that reaches across an outer function boundary where the outer
222 : * function escapes), enqueue it for further analysis, and bump fnlevel
223 : * to trap any non-escaping children.
224 : */
225 991294 : if (fn->isFunArg()) {
226 791477 : queue->push(funbox);
227 791477 : fnlevel = int(funbox->level);
228 : }
229 :
230 : /*
231 : * Now process the current function's children, and recalibrate their
232 : * cumulative skipmin to be relative to the current static level.
233 : */
234 991294 : if (funbox->kids) {
235 167026 : unsigned kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
236 :
237 167026 : JS_ASSERT(kidskipmin != 0);
238 167026 : if (kidskipmin != UpvarCookie::FREE_LEVEL) {
239 134213 : --kidskipmin;
240 134213 : if (kidskipmin != 0 && kidskipmin < skipmin)
241 0 : skipmin = kidskipmin;
242 : }
243 : }
244 :
245 : /*
246 : * Finally, after we've traversed all of the current function's kids,
247 : * minimize allskipmin against our accumulated skipmin. Minimize across
248 : * funbox and all of its siblings, to compute our return value.
249 : */
250 991294 : if (skipmin != UpvarCookie::FREE_LEVEL) {
251 210577 : if (skipmin < allskipmin)
252 140986 : allskipmin = skipmin;
253 : }
254 : } while ((funbox = funbox->siblings) != NULL);
255 :
256 497641 : return allskipmin;
257 : }
258 :
259 : static bool
260 324840 : MarkFunArgs(JSContext *cx, FunctionBox *funbox, uint32_t functionCount)
261 : {
262 649680 : FunctionBoxQueue queue;
263 324840 : if (!queue.init(functionCount)) {
264 0 : js_ReportOutOfMemory(cx);
265 0 : return false;
266 : }
267 :
268 324840 : FindFunArgs(funbox, -1, &queue);
269 1446386 : while ((funbox = queue.pull()) != NULL) {
270 796706 : ParseNode *fn = funbox->node;
271 796706 : JS_ASSERT(fn->isFunArg());
272 :
273 796706 : ParseNode *pn = fn->pn_body;
274 796706 : if (pn->isKind(PNK_UPVARS)) {
275 653935 : AtomDefnMapPtr upvars = pn->pn_names;
276 653935 : JS_ASSERT(!upvars->empty());
277 :
278 2558229 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
279 1904294 : Definition *defn = r.front().value();
280 1904294 : Definition *lexdep = defn->resolve();
281 :
282 2319315 : if (!lexdep->isFreeVar() &&
283 355625 : !lexdep->isFunArg() &&
284 35762 : (lexdep->kind() == Definition::FUNCTION ||
285 23634 : lexdep->isOp(JSOP_CALLEE))) {
286 : /*
287 : * Mark this formerly-Algol-like function as an escaping
288 : * function (i.e., as a funarg), because it is used from
289 : * another funarg.
290 : *
291 : * Progress is guaranteed because we set the funarg flag
292 : * here, which suppresses revisiting this function (thanks
293 : * to the !lexdep->isFunArg() test just above).
294 : */
295 12128 : lexdep->setFunArg();
296 :
297 : FunctionBox *afunbox;
298 12128 : if (lexdep->isOp(JSOP_CALLEE)) {
299 : /*
300 : * A named function expression will not appear to be a
301 : * funarg if it is immediately applied. However, if its
302 : * name is used in an escaping function nested within
303 : * it, then it must become flagged as a funarg again.
304 : * See bug 545980.
305 : */
306 0 : afunbox = funbox;
307 0 : unsigned calleeLevel = lexdep->pn_cookie.level();
308 0 : unsigned staticLevel = afunbox->level + 1U;
309 0 : while (staticLevel != calleeLevel) {
310 0 : afunbox = afunbox->parent;
311 0 : --staticLevel;
312 : }
313 0 : JS_ASSERT(afunbox->level + 1U == calleeLevel);
314 0 : afunbox->node->setFunArg();
315 : } else {
316 12128 : afunbox = lexdep->pn_funbox;
317 : }
318 12128 : queue.push(afunbox);
319 :
320 : /*
321 : * Walk over nested functions again, now that we have
322 : * changed the level across which it is unsafe to access
323 : * upvars using the runtime dynamic link (frame chain).
324 : */
325 12128 : if (afunbox->kids)
326 5775 : FindFunArgs(afunbox->kids, afunbox->level, &queue);
327 : }
328 : }
329 : }
330 : }
331 324840 : return true;
332 : }
333 :
334 : static uint32_t
335 122859 : MinBlockId(ParseNode *fn, uint32_t id)
336 : {
337 122859 : if (fn->pn_blockid < id)
338 2 : return false;
339 122857 : if (fn->isDefn()) {
340 10192 : for (ParseNode *pn = fn->dn_uses; pn; pn = pn->pn_link) {
341 6676 : if (pn->pn_blockid < id)
342 0 : return false;
343 : }
344 : }
345 122857 : return true;
346 : }
347 :
348 : static inline bool
349 210060 : CanFlattenUpvar(Definition *dn, FunctionBox *funbox, uint32_t tcflags)
350 : {
351 : /*
352 : * Consider the current function (the lambda, innermost below) using a var
353 : * x defined two static levels up:
354 : *
355 : * function f() {
356 : * // z = g();
357 : * var x = 42;
358 : * function g() {
359 : * return function () { return x; };
360 : * }
361 : * return g();
362 : * }
363 : *
364 : * So long as (1) the initialization in 'var x = 42' dominates all uses of
365 : * g and (2) x is not reassigned, it is safe to optimize the lambda to a
366 : * flat closure. Uncommenting the early call to g makes this optimization
367 : * unsafe (z could name a global setter that calls its argument).
368 : */
369 210060 : FunctionBox *afunbox = funbox;
370 210060 : unsigned dnLevel = dn->frameLevel();
371 :
372 210060 : JS_ASSERT(dnLevel <= funbox->level);
373 423401 : while (afunbox->level != dnLevel) {
374 34742 : afunbox = afunbox->parent;
375 :
376 : /*
377 : * NB: afunbox can't be null because we are sure to find a function box
378 : * whose level == dnLevel before we would try to walk above the root of
379 : * the funbox tree. See bug 493260 comments 16-18.
380 : *
381 : * Assert but check anyway, to protect future changes that bind eval
382 : * upvars in the parser.
383 : */
384 34742 : JS_ASSERT(afunbox);
385 :
386 : /*
387 : * If this function is reaching up across an enclosing funarg, then we
388 : * cannot copy dn's value into a flat closure slot. The flat closure
389 : * code assumes the upvars to be copied are in frames still on the
390 : * stack.
391 : */
392 34742 : if (!afunbox || afunbox->node->isFunArg())
393 31461 : return false;
394 :
395 : /*
396 : * Reaching up for dn across a generator also means we can't flatten,
397 : * since the generator iterator does not run until later, in general.
398 : * See bug 563034.
399 : */
400 3281 : if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
401 0 : return false;
402 : }
403 :
404 : /*
405 : * If afunbox's function (which is at the same level as dn) is in a loop,
406 : * pessimistically assume the variable initializer may be in the same loop.
407 : * A flat closure would then be unsafe, as the captured variable could be
408 : * assigned after the closure is created. See bug 493232.
409 : */
410 178599 : if (afunbox->inLoop)
411 4043 : return false;
412 :
413 : /*
414 : * |with| and eval used as an operator defeat lexical scoping: they can be
415 : * used to assign to any in-scope variable. Therefore they must disable
416 : * flat closures that use such upvars. The parser detects these as special
417 : * forms and marks the function heavyweight.
418 : */
419 174556 : if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
420 27495 : return false;
421 :
422 : /*
423 : * If afunbox's function is not a lambda, it will be hoisted, so it could
424 : * capture the undefined value that by default initializes var, let, and
425 : * const bindings. And if dn is a function that comes at (meaning a
426 : * function refers to its own name) or strictly after afunbox, we also
427 : * defeat the flat closure optimization for this dn.
428 : */
429 147061 : JSFunction *afun = afunbox->function();
430 147061 : if (!(afun->flags & JSFUN_LAMBDA)) {
431 11746 : if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
432 5974 : return false;
433 : }
434 :
435 141087 : if (!dn->isInitialized())
436 331 : return false;
437 :
438 140756 : Definition::Kind dnKind = dn->kind();
439 140756 : if (dnKind != Definition::CONST) {
440 140701 : if (dn->isAssigned())
441 10969 : return false;
442 :
443 : /*
444 : * Any formal could be mutated behind our back via the arguments
445 : * object, so deoptimize if the outer function uses arguments.
446 : *
447 : * In a Function constructor call where the final argument -- the body
448 : * source for the function to create -- contains a nested function
449 : * definition or expression, afunbox->parent will be null. The body
450 : * source might use |arguments| outside of any nested functions it may
451 : * contain, so we have to check the tcflags parameter that was passed
452 : * in from js::frontend::CompileFunctionBody.
453 : */
454 217287 : if (dnKind == Definition::ARG &&
455 87555 : ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_USES_ARGUMENTS)) {
456 1284 : return false;
457 : }
458 : }
459 :
460 : /*
461 : * Check quick-and-dirty dominance relation. Function definitions dominate
462 : * their uses thanks to hoisting. Other binding forms hoist as undefined,
463 : * of course, so check forward-reference and blockid relations.
464 : */
465 128503 : if (dnKind != Definition::FUNCTION) {
466 : /*
467 : * Watch out for code such as
468 : *
469 : * (function () {
470 : * ...
471 : * var jQuery = ... = function (...) {
472 : * return new jQuery.foo.bar(baz);
473 : * }
474 : * ...
475 : * })();
476 : *
477 : * where the jQuery variable is not reassigned, but of course is not
478 : * initialized at the time that the would-be-flat closure containing
479 : * the jQuery upvar is formed.
480 : */
481 123543 : if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
482 684 : return false;
483 122859 : if (!MinBlockId(afunbox->node, dn->pn_blockid))
484 2 : return false;
485 : }
486 127817 : return true;
487 : }
488 :
489 : static void
490 251746 : FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
491 : {
492 251746 : unsigned dnLevel = dn->frameLevel();
493 :
494 581084 : while ((funbox = funbox->parent) != NULL) {
495 : /*
496 : * Notice that funbox->level is the static level of the definition or
497 : * expression of the function parsed into funbox, not the static level
498 : * of its body. Therefore we must add 1 to match dn's level to find the
499 : * funbox whose body contains the dn definition.
500 : */
501 328982 : if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
502 251390 : funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
503 251390 : break;
504 : }
505 77592 : funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
506 : }
507 :
508 251746 : if (!funbox && (*tcflags & TCF_IN_FUNCTION))
509 54 : *tcflags |= TCF_FUN_HEAVYWEIGHT;
510 251746 : }
511 :
512 : static void
513 488911 : SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
514 : {
515 1467418 : for (; funbox; funbox = funbox->siblings) {
516 978507 : ParseNode *fn = funbox->node;
517 978507 : ParseNode *pn = fn->pn_body;
518 :
519 978507 : if (funbox->kids)
520 164071 : SetFunctionKinds(funbox->kids, tcflags, isDirectEval);
521 :
522 978507 : JSFunction *fun = funbox->function();
523 :
524 978507 : JS_ASSERT(fun->kind() == JSFUN_INTERPRETED);
525 :
526 978507 : if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
527 : /* nothing to do */
528 910316 : } else if (isDirectEval || funbox->inAnyDynamicScope()) {
529 : /*
530 : * Either we are in a with-block or a function scope that is
531 : * subject to direct eval; or we are compiling strict direct eval
532 : * code.
533 : *
534 : * In either case, fun may reference names that are not bound but
535 : * are not necessarily global either. (In the strict direct eval
536 : * case, we could bind them, but currently do not bother; see
537 : * the comment about strict mode code in BindTopLevelVar.)
538 : */
539 18358 : JS_ASSERT(!fun->isNullClosure());
540 : } else {
541 891958 : bool hasUpvars = false;
542 891958 : bool canFlatten = true;
543 :
544 891958 : if (pn->isKind(PNK_UPVARS)) {
545 730835 : AtomDefnMapPtr upvars = pn->pn_names;
546 730835 : JS_ASSERT(!upvars->empty());
547 :
548 : /*
549 : * For each lexical dependency from this closure to an outer
550 : * binding, analyze whether it is safe to copy the binding's
551 : * value into a flat closure slot when the closure is formed.
552 : */
553 2505666 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
554 1857074 : Definition *defn = r.front().value();
555 1857074 : Definition *lexdep = defn->resolve();
556 :
557 1857074 : if (!lexdep->isFreeVar()) {
558 210060 : hasUpvars = true;
559 210060 : if (!CanFlattenUpvar(lexdep, funbox, *tcflags)) {
560 : /*
561 : * Can't flatten. Enclosing functions holding
562 : * variables used by this function will be flagged
563 : * heavyweight below. FIXME bug 545759: re-enable
564 : * partial flat closures.
565 : */
566 82243 : canFlatten = false;
567 82243 : break;
568 : }
569 : }
570 : }
571 : }
572 :
573 : /*
574 : * Top-level functions, and (extension) functions not at top level
575 : * which are also not directly within other functions, aren't
576 : * flattened.
577 : */
578 891958 : if (fn->isOp(JSOP_DEFFUN))
579 507 : canFlatten = false;
580 :
581 891958 : if (!hasUpvars) {
582 : /* No lexical dependencies => null closure, for best performance. */
583 725808 : fun->setKind(JSFUN_NULL_CLOSURE);
584 166150 : } else if (canFlatten) {
585 83907 : fun->setKind(JSFUN_FLAT_CLOSURE);
586 83907 : switch (fn->getOp()) {
587 : case JSOP_DEFLOCALFUN:
588 0 : fn->setOp(JSOP_DEFLOCALFUN_FC);
589 0 : break;
590 : case JSOP_LAMBDA:
591 82054 : fn->setOp(JSOP_LAMBDA_FC);
592 82054 : break;
593 : default:
594 : /* js::frontend::EmitTree's PNK_FUNCTION case sets op. */
595 1853 : JS_ASSERT(fn->isOp(JSOP_NOP));
596 : }
597 : }
598 : }
599 :
600 978507 : if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) {
601 : /*
602 : * One or more upvars cannot be safely snapshot into a flat
603 : * closure's non-reserved slot (see JSOP_GETFCSLOT), so we loop
604 : * again over all upvars, and for each non-free upvar, ensure that
605 : * its containing function has been flagged as heavyweight.
606 : *
607 : * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
608 : * generating any code for a tree of nested functions.
609 : */
610 162573 : AtomDefnMapPtr upvars = pn->pn_names;
611 162573 : JS_ASSERT(!upvars->empty());
612 :
613 872415 : for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
614 709842 : Definition *defn = r.front().value();
615 709842 : Definition *lexdep = defn->resolve();
616 709842 : if (!lexdep->isFreeVar())
617 251746 : FlagHeavyweights(lexdep, funbox, tcflags);
618 : }
619 : }
620 : }
621 488911 : }
622 :
623 : /*
624 : * Walk the FunctionBox tree looking for functions whose call objects may
625 : * acquire new bindings as they execute: non-strict functions that call eval,
626 : * and functions that contain function statements (definitions not appearing
627 : * within the top statement list, which don't take effect unless they are
628 : * evaluated). Such call objects may acquire bindings that shadow variables
629 : * defined in enclosing scopes, so any enclosed functions must have their
630 : * bindings' extensibleParents flags set, and enclosed compiler-created blocks
631 : * must have their OWN_SHAPE flags set; the comments for
632 : * js::Bindings::extensibleParents explain why.
633 : */
634 : static bool
635 488911 : MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool hasExtensibleParent)
636 : {
637 1467418 : for (; funbox; funbox = funbox->siblings) {
638 : /*
639 : * It would be nice to use fun->kind() here to recognize functions
640 : * that will never consult their parent chains, and thus don't need
641 : * their 'extensible parents' flag set. Filed as bug 619750.
642 : */
643 :
644 978507 : JS_ASSERT(!funbox->bindings.extensibleParents());
645 978507 : if (hasExtensibleParent) {
646 8413 : if (!funbox->bindings.setExtensibleParents(context))
647 0 : return false;
648 : }
649 :
650 978507 : if (funbox->kids) {
651 164071 : if (!MarkExtensibleScopeDescendants(context, funbox->kids,
652 164071 : hasExtensibleParent || funbox->scopeIsExtensible())) {
653 0 : return false;
654 : }
655 : }
656 : }
657 :
658 488911 : return true;
659 : }
660 :
661 : bool
662 901472 : frontend::AnalyzeFunctions(TreeContext *tc)
663 : {
664 901472 : CleanFunctionList(&tc->parser->allocator, &tc->functionList);
665 901472 : if (!tc->functionList)
666 576632 : return true;
667 324840 : if (!MarkFunArgs(tc->parser->context, tc->functionList, tc->parser->functionCount))
668 0 : return false;
669 324840 : if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
670 0 : return false;
671 324840 : bool isDirectEval = !!tc->parser->callerFrame;
672 324840 : SetFunctionKinds(tc->functionList, &tc->flags, isDirectEval);
673 324840 : return true;
674 : }
|