/*
     * When naming an anonymous function, the process works loosely by walking
     * up the AST and then translating that to a string. The stringification
     * happens from some far-up assignment and then going back down the parse
     * tree to the function definition point.
     *
     * This function will walk up the parse tree, gathering relevant nodes used
     * for naming, and return the assignment node if there is one. The provided
     * array and size will be filled in, and the returned node could be NULL if
     * no assignment is found. The first element of the array will be the
     * innermost node relevant to naming, and the last element will be the
     * outermost node.
     */
    ParseNode *gatherNameable(ParseNode **nameable, size_t *size) {
        *size = 0;

        for (int pos = nparents - 1; pos >= 0; pos--) {
            ParseNode *cur = parents[pos];
            if (cur->isAssignment())
                return cur;

            switch (cur->getKind()) {
                case PNK_NAME:     return cur;  /* found the initialized declaration */
                case PNK_FUNCTION: return NULL; /* won't find an assignment or declaration */

                case PNK_RETURN:
                    /*
                     * Normally the relevant parent of a node is its direct parent, but
                     * sometimes with code like:
                     *
                     *    var foo = (function() { return function() {}; })();
                     *
                     * the outer function is just a helper to create a scope for the
                     * returned function. Hence the name of the returned function should
                     * actually be 'foo'.  This loop sees if the current node is a
                     * PNK_RETURN, and if there is a direct function call we skip to
                     * that.
                     */
                    for (int tmp = pos - 1; tmp > 0; tmp--) {
                        if (isDirectCall(tmp, cur)) {
                            pos = tmp;
                            break;
                        } else if (call(cur)) {
                            /* Don't skip too high in the tree */
                            break;
                        }
                        cur = parents[tmp];
                    }
                    break;

                case PNK_COLON:
                    /*
                     * If this is a PNK_COLON, but our parent is not a PNK_OBJECT,
                     * then this is a label and we're done naming. Otherwise we
                     * record the PNK_COLON but skip the PNK_OBJECT so we're not
                     * flagged as a contributor.
                     */
                    if (pos == 0 || !parents[pos - 1]->isKind(PNK_OBJECT))
                        return NULL;
                    pos--;
                    /* fallthrough */

                /* Save any other nodes we encounter on the way up. */
                default:
                    JS_ASSERT(*size < MaxParents);
                    nameable[(*size)++] = cur;
                    break;
            }

        }

        return NULL;
    }
    /*
     * Resolve the name of a function. If the function already has a name
     * listed, then it is skipped. Otherwise an intelligent name is guessed to
     * assign to the function's displayAtom field
     */
    JSAtom *resolveFun(ParseNode *pn, HandleAtom prefix) {
        JS_ASSERT(pn != NULL && pn->isKind(PNK_FUNCTION));
        RootedFunction fun(cx, pn->pn_funbox->function());

        StringBuffer buf(cx);
        this->buf = &buf;

        /* If the function already has a name, use that */
        if (fun->displayAtom() != NULL) {
            if (prefix == NULL)
                return fun->displayAtom();
            if (!buf.append(prefix) ||
                !buf.append("/") ||
                !buf.append(fun->displayAtom()))
                return NULL;
            return buf.finishAtom();
        }

        /* If a prefix is specified, then it is a form of namespace */
        if (prefix != NULL && (!buf.append(prefix) || !buf.append("/")))
            return NULL;

        /* Gather all nodes relevant to naming */
        ParseNode *toName[MaxParents];
        size_t size;
        ParseNode *assignment = gatherNameable(toName, &size);

        /* If the function is assigned to something, then that is very relevant */
        if (assignment) {
            if (assignment->isAssignment())
                assignment = assignment->pn_left;
            if (!nameExpression(assignment))
                return NULL;
        }

        /*
         * Other than the actual assignment, other relevant nodes to naming are
         * those in object initializers and then particular nodes marking a
         * contribution.
         */
        for (int pos = size - 1; pos >= 0; pos--) {
            ParseNode *node = toName[pos];

            if (node->isKind(PNK_COLON)) {
                ParseNode *left = node->pn_left;
                if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) {
                    if (!appendPropertyReference(left->pn_atom))
                        return NULL;
                } else if (left->isKind(PNK_NUMBER)) {
                    if (!appendNumericPropertyReference(left->pn_dval))
                        return NULL;
                }
            } else {
                /*
                 * Don't have consecutive '<' characters, and also don't start
                 * with a '<' character.
                 */
                if (!buf.empty() && *(buf.end() - 1) != '<' && !buf.append("<"))
                    return NULL;
            }
        }

        /*
         * functions which are "genuinely anonymous" but are contained in some
         * other namespace are rather considered as "contributing" to the outer
         * function, so give them a contribution symbol here.
         */
        if (!buf.empty() && *(buf.end() - 1) == '/' && !buf.append("<"))
            return NULL;
        if (buf.empty())
            return NULL;

        JSAtom *atom = buf.finishAtom();
        if (!atom)
            return NULL;
        fun->setGuessedAtom(atom);
        return atom;
    }