/* * 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; }