LCOV - code coverage report
Current view: directory - js/src/frontend - SemanticAnalysis.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 210 187 89.0 %
Date: 2012-06-02 Functions: 9 9 100.0 %

       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                 : }

Generated by: LCOV version 1.7