js/src/frontend/NameFunctions.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "frontend/NameFunctions.h"
     9 #include "jsfun.h"
    10 #include "jsprf.h"
    12 #include "frontend/BytecodeCompiler.h"
    13 #include "frontend/ParseNode.h"
    14 #include "frontend/SharedContext.h"
    15 #include "vm/StringBuffer.h"
    17 using namespace js;
    18 using namespace js::frontend;
    20 namespace {
    22 class NameResolver
    23 {
    24     static const size_t MaxParents = 100;
    26     ExclusiveContext *cx;
    27     size_t nparents;                /* number of parents in the parents array */
    28     ParseNode *parents[MaxParents]; /* history of ParseNodes we've been looking at */
    29     StringBuffer *buf;              /* when resolving, buffer to append to */
    31     /* Test whether a ParseNode represents a function invocation */
    32     bool call(ParseNode *pn) {
    33         return pn && pn->isKind(PNK_CALL);
    34     }
    36     /*
    37      * Append a reference to a property named |name| to |buf|. If |name| is
    38      * a proper identifier name, then we append '.name'; otherwise, we
    39      * append '["name"]'.
    40      *
    41      * Note that we need the IsIdentifier check for atoms from both
    42      * PNK_NAME nodes and PNK_STRING nodes: given code like a["b c"], the
    43      * front end will produce a PNK_DOT with a PNK_NAME child whose name
    44      * contains spaces.
    45      */
    46     bool appendPropertyReference(JSAtom *name) {
    47         if (IsIdentifier(name))
    48             return buf->append(".") && buf->append(name);
    50         /* Quote the string as needed. */
    51         JSString *source = js_QuoteString(cx, name, '"');
    52         return source && buf->append("[") && buf->append(source) && buf->append("]");
    53     }
    55     /* Append a number to buf. */
    56     bool appendNumber(double n) {
    57         char number[30];
    58         int digits = JS_snprintf(number, sizeof(number), "%g", n);
    59         return buf->appendInflated(number, digits);
    60     }
    62     /* Append "[<n>]" to buf, referencing a property named by a numeric literal. */
    63     bool appendNumericPropertyReference(double n) {
    64         return buf->append("[") && appendNumber(n) && buf->append("]");
    65     }
    67     /*
    68      * Walk over the given ParseNode, converting it to a stringified name that
    69      * respresents where the function is being assigned to.
    70      */
    71     bool nameExpression(ParseNode *n) {
    72         switch (n->getKind()) {
    73           case PNK_DOT:
    74             return nameExpression(n->expr()) && appendPropertyReference(n->pn_atom);
    76           case PNK_NAME:
    77             return buf->append(n->pn_atom);
    79           case PNK_THIS:
    80             return buf->append("this");
    82           case PNK_ELEM:
    83             return nameExpression(n->pn_left) &&
    84                    buf->append("[") &&
    85                    nameExpression(n->pn_right) &&
    86                    buf->append("]");
    88           case PNK_NUMBER:
    89             return appendNumber(n->pn_dval);
    91           default:
    92             /*
    93              * Technically this isn't an "abort" situation, we're just confused
    94              * on what to call this function, but failures in naming aren't
    95              * treated as fatal.
    96              */
    97             return false;
    98         }
    99     }
   101     /*
   102      * When naming an anonymous function, the process works loosely by walking
   103      * up the AST and then translating that to a string. The stringification
   104      * happens from some far-up assignment and then going back down the parse
   105      * tree to the function definition point.
   106      *
   107      * This function will walk up the parse tree, gathering relevant nodes used
   108      * for naming, and return the assignment node if there is one. The provided
   109      * array and size will be filled in, and the returned node could be nullptr
   110      * if no assignment is found. The first element of the array will be the
   111      * innermost node relevant to naming, and the last element will be the
   112      * outermost node.
   113      */
   114     ParseNode *gatherNameable(ParseNode **nameable, size_t *size) {
   115         *size = 0;
   117         for (int pos = nparents - 1; pos >= 0; pos--) {
   118             ParseNode *cur = parents[pos];
   119             if (cur->isAssignment())
   120                 return cur;
   122             switch (cur->getKind()) {
   123               case PNK_NAME:     return cur;  /* found the initialized declaration */
   124               case PNK_THIS:     return cur;  /* Setting a property of 'this'. */
   125               case PNK_FUNCTION: return nullptr; /* won't find an assignment or declaration */
   127               case PNK_RETURN:
   128                 /*
   129                  * Normally the relevant parent of a node is its direct parent, but
   130                  * sometimes with code like:
   131                  *
   132                  *    var foo = (function() { return function() {}; })();
   133                  *
   134                  * the outer function is just a helper to create a scope for the
   135                  * returned function. Hence the name of the returned function should
   136                  * actually be 'foo'.  This loop sees if the current node is a
   137                  * PNK_RETURN, and if there is a direct function call we skip to
   138                  * that.
   139                  */
   140                 for (int tmp = pos - 1; tmp > 0; tmp--) {
   141                     if (isDirectCall(tmp, cur)) {
   142                         pos = tmp;
   143                         break;
   144                     } else if (call(cur)) {
   145                         /* Don't skip too high in the tree */
   146                         break;
   147                     }
   148                     cur = parents[tmp];
   149                 }
   150                 break;
   152               case PNK_COLON:
   153                 /*
   154                  * Record the PNK_COLON but skip the PNK_OBJECT so we're not
   155                  * flagged as a contributor.
   156                  */
   157                 pos--;
   158                 /* fallthrough */
   160               default:
   161                 /* Save any other nodes we encounter on the way up. */
   162                 JS_ASSERT(*size < MaxParents);
   163                 nameable[(*size)++] = cur;
   164                 break;
   165             }
   166         }
   168         return nullptr;
   169     }
   171     /*
   172      * Resolve the name of a function. If the function already has a name
   173      * listed, then it is skipped. Otherwise an intelligent name is guessed to
   174      * assign to the function's displayAtom field
   175      */
   176     bool resolveFun(ParseNode *pn, HandleAtom prefix, MutableHandleAtom retAtom) {
   177         JS_ASSERT(pn != nullptr && pn->isKind(PNK_FUNCTION));
   178         RootedFunction fun(cx, pn->pn_funbox->function());
   180         StringBuffer buf(cx);
   181         this->buf = &buf;
   183         retAtom.set(nullptr);
   185         /* If the function already has a name, use that */
   186         if (fun->displayAtom() != nullptr) {
   187             if (prefix == nullptr) {
   188                 retAtom.set(fun->displayAtom());
   189                 return true;
   190             }
   191             if (!buf.append(prefix) ||
   192                 !buf.append("/") ||
   193                 !buf.append(fun->displayAtom()))
   194                 return false;
   195             retAtom.set(buf.finishAtom());
   196             return !!retAtom;
   197         }
   199         /* If a prefix is specified, then it is a form of namespace */
   200         if (prefix != nullptr && (!buf.append(prefix) || !buf.append("/")))
   201             return false;
   203         /* Gather all nodes relevant to naming */
   204         ParseNode *toName[MaxParents];
   205         size_t size;
   206         ParseNode *assignment = gatherNameable(toName, &size);
   208         /* If the function is assigned to something, then that is very relevant */
   209         if (assignment) {
   210             if (assignment->isAssignment())
   211                 assignment = assignment->pn_left;
   212             if (!nameExpression(assignment))
   213                 return true;
   214         }
   216         /*
   217          * Other than the actual assignment, other relevant nodes to naming are
   218          * those in object initializers and then particular nodes marking a
   219          * contribution.
   220          */
   221         for (int pos = size - 1; pos >= 0; pos--) {
   222             ParseNode *node = toName[pos];
   224             if (node->isKind(PNK_COLON)) {
   225                 ParseNode *left = node->pn_left;
   226                 if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) {
   227                     if (!appendPropertyReference(left->pn_atom))
   228                         return false;
   229                 } else if (left->isKind(PNK_NUMBER)) {
   230                     if (!appendNumericPropertyReference(left->pn_dval))
   231                         return false;
   232                 }
   233             } else {
   234                 /*
   235                  * Don't have consecutive '<' characters, and also don't start
   236                  * with a '<' character.
   237                  */
   238                 if (!buf.empty() && *(buf.end() - 1) != '<' && !buf.append("<"))
   239                     return false;
   240             }
   241         }
   243         /*
   244          * functions which are "genuinely anonymous" but are contained in some
   245          * other namespace are rather considered as "contributing" to the outer
   246          * function, so give them a contribution symbol here.
   247          */
   248         if (!buf.empty() && *(buf.end() - 1) == '/' && !buf.append("<"))
   249             return false;
   251         if (buf.empty())
   252             return true;
   254         retAtom.set(buf.finishAtom());
   255         if (!retAtom)
   256             return false;
   257         fun->setGuessedAtom(retAtom);
   258         return true;
   259     }
   261     /*
   262      * Tests whether parents[pos] is a function call whose callee is cur.
   263      * This is the case for functions which do things like simply create a scope
   264      * for new variables and then return an anonymous function using this scope.
   265      */
   266     bool isDirectCall(int pos, ParseNode *cur) {
   267         return pos >= 0 && call(parents[pos]) && parents[pos]->pn_head == cur;
   268     }
   270   public:
   271     explicit NameResolver(ExclusiveContext *cx) : cx(cx), nparents(0), buf(nullptr) {}
   273     /*
   274      * Resolve all names for anonymous functions recursively within the
   275      * ParseNode instance given. The prefix is for each subsequent name, and
   276      * should initially be nullptr.
   277      */
   278     bool resolve(ParseNode *cur, HandleAtom prefixArg = js::NullPtr()) {
   279         RootedAtom prefix(cx, prefixArg);
   280         if (cur == nullptr)
   281             return true;
   283         if (cur->isKind(PNK_FUNCTION) && cur->isArity(PN_CODE)) {
   284             RootedAtom prefix2(cx);
   285             if (!resolveFun(cur, prefix, &prefix2))
   286                 return false;
   288             /*
   289              * If a function looks like (function(){})() where the parent node
   290              * of the definition of the function is a call, then it shouldn't
   291              * contribute anything to the namespace, so don't bother updating
   292              * the prefix to whatever was returned.
   293              */
   294             if (!isDirectCall(nparents - 1, cur))
   295                 prefix = prefix2;
   296         }
   297         if (nparents >= MaxParents)
   298             return true;
   299         parents[nparents++] = cur;
   301         switch (cur->getArity()) {
   302           case PN_NULLARY:
   303             break;
   304           case PN_NAME:
   305             if (!resolve(cur->maybeExpr(), prefix))
   306                 return false;
   307             break;
   308           case PN_UNARY:
   309             if (!resolve(cur->pn_kid, prefix))
   310                 return false;
   311             break;
   312           case PN_BINARY:
   313           case PN_BINARY_OBJ:
   314             if (!resolve(cur->pn_left, prefix))
   315                 return false;
   317             /*
   318              * FIXME? Occasionally pn_left == pn_right for something like
   319              * destructuring assignment in (function({foo}){}), so skip the
   320              * duplicate here if this is the case because we want to traverse
   321              * everything at most once.
   322              */
   323             if (cur->pn_left != cur->pn_right)
   324                 if (!resolve(cur->pn_right, prefix))
   325                     return false;
   326             break;
   327           case PN_TERNARY:
   328             if (!resolve(cur->pn_kid1, prefix))
   329                 return false;
   330             if (!resolve(cur->pn_kid2, prefix))
   331                 return false;
   332             if (!resolve(cur->pn_kid3, prefix))
   333                 return false;
   334             break;
   335           case PN_CODE:
   336             JS_ASSERT(cur->isKind(PNK_FUNCTION));
   337             if (!resolve(cur->pn_body, prefix))
   338                 return false;
   339             break;
   340           case PN_LIST:
   341             for (ParseNode *nxt = cur->pn_head; nxt; nxt = nxt->pn_next)
   342                 if (!resolve(nxt, prefix))
   343                     return false;
   344             break;
   345         }
   346         nparents--;
   347         return true;
   348     }
   349 };
   351 } /* anonymous namespace */
   353 bool
   354 frontend::NameFunctions(ExclusiveContext *cx, ParseNode *pn)
   355 {
   356     NameResolver nr(cx);
   357     return nr.resolve(pn);
   358 }

mercurial