LCOV - app.info - js/src/frontend/SemanticAnalysis.cpp

LCOV - code coverage report
| Current view: | directory - js/src/frontend - SemanticAnalysis.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 212 | 189 | 89.2 %
| Date: | 2012-04-21 | 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         1875968 : CleanFunctionList(ParseNodeAllocator *allocator, FunctionBox **funboxHead)
          85                 : {
          86         1875968 :     FunctionBox **link = funboxHead;
          87         3829260 :     while (FunctionBox *box = *link) {
          88          976646 :         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          976646 :         } 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          976646 :                 ParseNode **methodLink = &box->methods;
         107          991178 :                 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          976646 :             CleanFunctionList(allocator, &box->kids);
         122                 :
         123                 :             /* Keep the box on the list, and move to the next link. */
         124          976646 :             link = &box->siblings;
         125                 :         }
         126                 :     }
         127         1875968 : }
         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          496427 : FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue)
         161                 : {
         162          496427 :     unsigned allskipmin = UpvarCookie::FREE_LEVEL;
         163                 :
         164          989433 :     do {
         165          989433 :         ParseNode *fn = funbox->node;
         166          989433 :         JS_ASSERT(fn->isArity(PN_FUNC));
         167          989433 :         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          989433 :         if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) {
         184           11567 :             fn->setFunArg();
         185           22208 :             for (FunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
         186           10641 :                 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          989433 :         unsigned skipmin = UpvarCookie::FREE_LEVEL;
         195          989433 :         ParseNode *pn = fn->pn_body;
         196                 :
         197          989433 :         if (pn->isKind(PNK_UPVARS)) {
         198          821993 :             AtomDefnMapPtr &upvars = pn->pn_names;
         199          821993 :             JS_ASSERT(upvars->count() != 0);
         200                 :
         201         3277054 :             for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         202         2455061 :                 Definition *defn = r.front().value();
         203         2455061 :                 Definition *lexdep = defn->resolve();
         204                 :
         205         2455061 :                 if (!lexdep->isFreeVar()) {
         206          388018 :                     unsigned upvarLevel = lexdep->frameLevel();
         207                 :
         208          388018 :                     if (int(upvarLevel) <= fnlevel)
         209           67192 :                         fn->setFunArg();
         210                 :
         211          388018 :                     unsigned skip = (funbox->level + 1) - upvarLevel;
         212          388018 :                     if (skip < skipmin)
         213          225394 :                         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          989433 :         if (fn->isFunArg()) {
         226          790499 :             queue->push(funbox);
         227          790499 :             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          989433 :         if (funbox->kids) {
         235          166820 :             unsigned kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
         236                 :
         237          166820 :             JS_ASSERT(kidskipmin != 0);
         238          166820 :             if (kidskipmin != UpvarCookie::FREE_LEVEL) {
         239          134096 :                 --kidskipmin;
         240          134096 :                 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          989433 :         if (skipmin != UpvarCookie::FREE_LEVEL) {
         251          210441 :             if (skipmin < allskipmin)
         252          140869 :                 allskipmin = skipmin;
         253                 :         }
         254                 :     } while ((funbox = funbox->siblings) != NULL);
         255                 :
         256          496427 :     return allskipmin;
         257                 : }
         258                 :
         259                 : static bool
         260          323832 : MarkFunArgs(JSContext *cx, FunctionBox *funbox, uint32_t functionCount)
         261                 : {
         262          647664 :     FunctionBoxQueue queue;
         263          323832 :     if (!queue.init(functionCount)) {
         264               0 :         js_ReportOutOfMemory(cx);
         265               0 :         return false;
         266                 :     }
         267                 :
         268          323832 :     FindFunArgs(funbox, -1, &queue);
         269         1443392 :     while ((funbox = queue.pull()) != NULL) {
         270          795728 :         ParseNode *fn = funbox->node;
         271          795728 :         JS_ASSERT(fn->isFunArg());
         272                 :
         273          795728 :         ParseNode *pn = fn->pn_body;
         274          795728 :         if (pn->isKind(PNK_UPVARS)) {
         275          653126 :             AtomDefnMapPtr upvars = pn->pn_names;
         276          653126 :             JS_ASSERT(!upvars->empty());
         277                 :
         278         2555606 :             for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         279         1902480 :                 Definition *defn = r.front().value();
         280         1902480 :                 Definition *lexdep = defn->resolve();
         281                 :
         282         2317219 :                 if (!lexdep->isFreeVar() &&
         283          355419 :                     !lexdep->isFunArg() &&
         284           35724 :                     (lexdep->kind() == Definition::FUNCTION ||
         285           23596 :                      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          323832 :     return true;
         332                 : }
         333                 :
         334                 : static uint32_t
         335          122732 : MinBlockId(ParseNode *fn, uint32_t id)
         336                 : {
         337          122732 :     if (fn->pn_blockid < id)
         338               2 :         return false;
         339          122730 :     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          122730 :     return true;
         346                 : }
         347                 :
         348                 : static inline bool
         349          209916 : 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          209916 :     FunctionBox *afunbox = funbox;
         370          209916 :     unsigned dnLevel = dn->frameLevel();
         371                 :
         372          209916 :     JS_ASSERT(dnLevel <= funbox->level);
         373          423113 :     while (afunbox->level != dnLevel) {
         374           34737 :         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           34737 :         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           34737 :         if (!afunbox || afunbox->node->isFunArg())
         393           31456 :             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          178460 :     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          174417 :     if ((afunbox->parent ? afunbox->parent->tcflags : tcflags) & TCF_FUN_HEAVYWEIGHT)
         420           27488 :         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          146929 :     JSFunction *afun = afunbox->function();
         430          146929 :     if (!(afun->flags & JSFUN_LAMBDA)) {
         431           11745 :         if (dn->isBindingForm() || dn->pn_pos >= afunbox->node->pn_pos)
         432            5973 :             return false;
         433                 :     }
         434                 :
         435          140956 :     if (!dn->isInitialized())
         436             331 :         return false;
         437                 :
         438          140625 :     Definition::Kind dnKind = dn->kind();
         439          140625 :     if (dnKind != Definition::CONST) {
         440          140570 :         if (dn->isAssigned())
         441           10967 :             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          217080 :         if (dnKind == Definition::ARG &&
         455           87477 :             ((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          128374 :     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          123414 :         if (dn->pn_pos.end >= afunbox->node->pn_pos.end)
         482             682 :             return false;
         483          122732 :         if (!MinBlockId(afunbox->node, dn->pn_blockid))
         484               2 :             return false;
         485                 :     }
         486          127690 :     return true;
         487                 : }
         488                 :
         489                 : static void
         490          251664 : FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
         491                 : {
         492          251664 :     unsigned dnLevel = dn->frameLevel();
         493                 :
         494          580912 :     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          328892 :         if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
         502          251308 :             funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
         503          251308 :             break;
         504                 :         }
         505           77584 :         funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
         506                 :     }
         507                 :
         508          251664 :     if (!funbox && (*tcflags & TCF_IN_FUNCTION))
         509              54 :         *tcflags |= TCF_FUN_HEAVYWEIGHT;
         510          251664 : }
         511                 :
         512                 : static void
         513          487697 : SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
         514                 : {
         515         1464343 :     for (; funbox; funbox = funbox->siblings) {
         516          976646 :         ParseNode *fn = funbox->node;
         517          976646 :         ParseNode *pn = fn->pn_body;
         518                 :
         519          976646 :         if (funbox->kids)
         520          163865 :             SetFunctionKinds(funbox->kids, tcflags, isDirectEval);
         521                 :
         522          976646 :         JSFunction *fun = funbox->function();
         523                 :
         524          976646 :         JS_ASSERT(fun->kind() == JSFUN_INTERPRETED);
         525                 :
         526          976646 :         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
         527                 :             /* nothing to do */
         528          908476 :         } 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           18339 :             JS_ASSERT(!fun->isNullClosure());
         540                 :         } else {
         541          890137 :             bool hasUpvars = false;
         542          890137 :             bool canFlatten = true;
         543                 :
         544          890137 :             if (pn->isKind(PNK_UPVARS)) {
         545          729196 :                 AtomDefnMapPtr upvars = pn->pn_names;
         546          729196 :                 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         2499416 :                 for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         554         1852446 :                     Definition *defn = r.front().value();
         555         1852446 :                     Definition *lexdep = defn->resolve();
         556                 :
         557         1852446 :                     if (!lexdep->isFreeVar()) {
         558          209916 :                         hasUpvars = true;
         559          209916 :                         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           82226 :                             canFlatten = false;
         567           82226 :                             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          890137 :             if (fn->isOp(JSOP_DEFFUN))
         579             507 :                 canFlatten = false;
         580                 :
         581          890137 :             if (!hasUpvars) {
         582                 :                 /* No lexical dependencies => null closure, for best performance. */
         583          724104 :                 fun->setKind(JSFUN_NULL_CLOSURE);
         584          166033 :             } else if (canFlatten) {
         585           83807 :                 fun->setKind(JSFUN_FLAT_CLOSURE);
         586           83807 :                 switch (fn->getOp()) {
         587                 :                   case JSOP_DEFLOCALFUN:
         588               0 :                     fn->setOp(JSOP_DEFLOCALFUN_FC);
         589               0 :                     break;
         590                 :                   case JSOP_LAMBDA:
         591           81954 :                     fn->setOp(JSOP_LAMBDA_FC);
         592           81954 :                     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          976646 :         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          162516 :             AtomDefnMapPtr upvars = pn->pn_names;
         611          162516 :             JS_ASSERT(!upvars->empty());
         612                 :
         613          872111 :             for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         614          709595 :                 Definition *defn = r.front().value();
         615          709595 :                 Definition *lexdep = defn->resolve();
         616          709595 :                 if (!lexdep->isFreeVar())
         617          251664 :                     FlagHeavyweights(lexdep, funbox, tcflags);
         618                 :             }
         619                 :         }
         620                 :
         621          976646 :         if (funbox->joinable())
         622           91381 :             fun->setJoinable();
         623                 :     }
         624          487697 : }
         625                 :
         626                 : /*
         627                 :  * Walk the FunctionBox tree looking for functions whose call objects may
         628                 :  * acquire new bindings as they execute: non-strict functions that call eval,
         629                 :  * and functions that contain function statements (definitions not appearing
         630                 :  * within the top statement list, which don't take effect unless they are
         631                 :  * evaluated). Such call objects may acquire bindings that shadow variables
         632                 :  * defined in enclosing scopes, so any enclosed functions must have their
         633                 :  * bindings' extensibleParents flags set, and enclosed compiler-created blocks
         634                 :  * must have their OWN_SHAPE flags set; the comments for
         635                 :  * js::Bindings::extensibleParents explain why.
         636                 :  */
         637                 : static bool
         638          487697 : MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool hasExtensibleParent)
         639                 : {
         640         1464343 :     for (; funbox; funbox = funbox->siblings) {
         641                 :         /*
         642                 :          * It would be nice to use fun->kind() here to recognize functions
         643                 :          * that will never consult their parent chains, and thus don't need
         644                 :          * their 'extensible parents' flag set. Filed as bug 619750.
         645                 :          */
         646                 :
         647          976646 :         JS_ASSERT(!funbox->bindings.extensibleParents());
         648          976646 :         if (hasExtensibleParent) {
         649            8394 :             if (!funbox->bindings.setExtensibleParents(context))
         650               0 :                 return false;
         651                 :         }
         652                 :
         653          976646 :         if (funbox->kids) {
         654          163865 :             if (!MarkExtensibleScopeDescendants(context, funbox->kids,
         655          163865 :                                                 hasExtensibleParent || funbox->scopeIsExtensible())) {
         656               0 :                 return false;
         657                 :             }
         658                 :         }
         659                 :     }
         660                 :
         661          487697 :     return true;
         662                 : }
         663                 :
         664                 : bool
         665          899322 : frontend::AnalyzeFunctions(TreeContext *tc)
         666                 : {
         667          899322 :     CleanFunctionList(&tc->parser->allocator, &tc->functionList);
         668          899322 :     if (!tc->functionList)
         669          575490 :         return true;
         670          323832 :     if (!MarkFunArgs(tc->parser->context, tc->functionList, tc->parser->functionCount))
         671               0 :         return false;
         672          323832 :     if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
         673               0 :         return false;
         674          323832 :     bool isDirectEval = !!tc->parser->callerFrame;
         675          323832 :     SetFunctionKinds(tc->functionList, &tc->flags, isDirectEval);
         676          323832 :     return true;
         677                 : }

Generated by: LCOV version 1.7