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: | 76 | 69 | 90.8 %
| Date: | 2012-04-07 | Functions: | 5 | 5 | 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          474930 : CleanFunctionList(ParseNodeAllocator *allocator, FunctionBox **funboxHead)
          85                 : {
          86          474930 :     FunctionBox **link = funboxHead;
          87          783554 :     while (FunctionBox *box = *link) {
          88          154312 :         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          154312 :         } 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          154312 :                 ParseNode **methodLink = &box->methods;
         107          154690 :                 while (ParseNode *method = *methodLink) {
         108                 :                     /* Method nodes are never rewritten in place to be other kinds of nodes. */
         109             189 :                     JS_ASSERT(method->isArity(PN_FUNC));
         110             189 :                     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             189 :                         methodLink = &method->pn_link;
         116                 :                     }
         117                 :                 }
         118                 :             }
         119                 :
         120                 :             /* Second, remove boxes for deleted functions from our kids list. */
         121          154312 :             CleanFunctionList(allocator, &box->kids);
         122                 :
         123                 :             /* Keep the box on the list, and move to the next link. */
         124          154312 :             link = &box->siblings;
         125                 :         }
         126                 :     }
         127          474930 : }
         128                 :
         129                 : static void
         130           11315 : FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
         131                 : {
         132           11315 :     unsigned dnLevel = dn->frameLevel();
         133                 :
         134           24043 :     while ((funbox = funbox->parent) != NULL) {
         135                 :         /*
         136                 :          * Notice that funbox->level is the static level of the definition or
         137                 :          * expression of the function parsed into funbox, not the static level
         138                 :          * of its body. Therefore we must add 1 to match dn's level to find the
         139                 :          * funbox whose body contains the dn definition.
         140                 :          */
         141           12448 :         if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
         142           11035 :             funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
         143           11035 :             break;
         144                 :         }
         145                 :     }
         146                 :
         147           11315 :     if (!funbox && (*tcflags & TCF_IN_FUNCTION))
         148              82 :         *tcflags |= TCF_FUN_HEAVYWEIGHT;
         149           11315 : }
         150                 :
         151                 : static void
         152          149767 : SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval)
         153                 : {
         154          304079 :     for (; funbox; funbox = funbox->siblings) {
         155          154312 :         ParseNode *fn = funbox->node;
         156          154312 :         ParseNode *pn = fn->pn_body;
         157                 :
         158          154312 :         if (funbox->kids)
         159            8412 :             SetFunctionKinds(funbox->kids, tcflags, isDirectEval);
         160                 :
         161          154312 :         JSFunction *fun = funbox->function();
         162                 :
         163          154312 :         JS_ASSERT(fun->kind() == JSFUN_INTERPRETED);
         164                 :
         165          154312 :         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
         166                 :             /* nothing to do */
         167          146043 :         } else if (isDirectEval || funbox->inAnyDynamicScope()) {
         168                 :             /*
         169                 :              * Either we are in a with-block or a function scope that is
         170                 :              * subject to direct eval; or we are compiling strict direct eval
         171                 :              * code.
         172                 :              *
         173                 :              * In either case, fun may reference names that are not bound but
         174                 :              * are not necessarily global either. (In the strict direct eval
         175                 :              * case, we could bind them, but currently do not bother; see
         176                 :              * the comment about strict mode code in BindTopLevelVar.)
         177                 :              */
         178           10530 :             JS_ASSERT(!fun->isNullClosure());
         179                 :         } else {
         180          135513 :             bool hasUpvars = false;
         181                 :
         182          135513 :             if (pn->isKind(PNK_UPVARS)) {
         183           45626 :                 AtomDefnMapPtr upvars = pn->pn_names;
         184           45626 :                 JS_ASSERT(!upvars->empty());
         185                 :
         186                 :                 /* Determine whether the this function contains upvars. */
         187          104592 :                 for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         188           63476 :                     if (!r.front().value()->resolve()->isFreeVar()) {
         189            4510 :                         hasUpvars = true;
         190            4510 :                         break;
         191                 :                     }
         192                 :                 }
         193                 :             }
         194                 :
         195          135513 :             if (!hasUpvars) {
         196                 :                 /* No lexical dependencies => null closure, for best performance. */
         197          131003 :                 fun->setKind(JSFUN_NULL_CLOSURE);
         198                 :             }
         199                 :         }
         200                 :
         201          154312 :         if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) {
         202                 :             /*
         203                 :              * We loop again over all upvars, and for each non-free upvar,
         204                 :              * ensure that its containing function has been flagged as
         205                 :              * heavyweight.
         206                 :              *
         207                 :              * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before
         208                 :              * generating any code for a tree of nested functions.
         209                 :              */
         210           19521 :             AtomDefnMapPtr upvars = pn->pn_names;
         211           19521 :             JS_ASSERT(!upvars->empty());
         212                 :
         213           55109 :             for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
         214           35588 :                 Definition *defn = r.front().value();
         215           35588 :                 Definition *lexdep = defn->resolve();
         216           35588 :                 if (!lexdep->isFreeVar())
         217           11315 :                     FlagHeavyweights(lexdep, funbox, tcflags);
         218                 :             }
         219                 :         }
         220                 :     }
         221          149767 : }
         222                 :
         223                 : /*
         224                 :  * Walk the FunctionBox tree looking for functions whose call objects may
         225                 :  * acquire new bindings as they execute: non-strict functions that call eval,
         226                 :  * and functions that contain function statements (definitions not appearing
         227                 :  * within the top statement list, which don't take effect unless they are
         228                 :  * evaluated). Such call objects may acquire bindings that shadow variables
         229                 :  * defined in enclosing scopes, so any enclosed functions must have their
         230                 :  * bindings' extensibleParents flags set, and enclosed compiler-created blocks
         231                 :  * must have their OWN_SHAPE flags set; the comments for
         232                 :  * js::Bindings::extensibleParents explain why.
         233                 :  */
         234                 : static bool
         235          149767 : MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool hasExtensibleParent)
         236                 : {
         237          304079 :     for (; funbox; funbox = funbox->siblings) {
         238                 :         /*
         239                 :          * It would be nice to use fun->kind() here to recognize functions
         240                 :          * that will never consult their parent chains, and thus don't need
         241                 :          * their 'extensible parents' flag set. Filed as bug 619750.
         242                 :          */
         243                 :
         244          154312 :         JS_ASSERT(!funbox->bindings.extensibleParents());
         245          154312 :         if (hasExtensibleParent) {
         246             559 :             if (!funbox->bindings.setExtensibleParents(context))
         247               0 :                 return false;
         248                 :         }
         249                 :
         250          154312 :         if (funbox->kids) {
         251            8412 :             if (!MarkExtensibleScopeDescendants(context, funbox->kids,
         252            8412 :                                                 hasExtensibleParent || funbox->scopeIsExtensible())) {
         253               0 :                 return false;
         254                 :             }
         255                 :         }
         256                 :     }
         257                 :
         258          149767 :     return true;
         259                 : }
         260                 :
         261                 : bool
         262          320618 : frontend::AnalyzeFunctions(TreeContext *tc)
         263                 : {
         264          320618 :     CleanFunctionList(&tc->parser->allocator, &tc->functionList);
         265          320618 :     if (!tc->functionList)
         266          179263 :         return true;
         267          141355 :     if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
         268               0 :         return false;
         269          141355 :     bool isDirectEval = !!tc->parser->callerFrame;
         270          141355 :     SetFunctionKinds(tc->functionList, &tc->flags, isDirectEval);
         271          141355 :     return true;
         272                 : }

Generated by: LCOV version 1.7