1
0
mirror of https://github.com/golang/go synced 2024-11-18 12:04:57 -07:00

cmd/gc: generate simpler names for closures

Fixes #8291

There were several complaints about closure names in the issue tracker.
The first problem is that you see names like net/http.func·001
in profiles, traces, etc. And there is no way to figure out what
is that function.
Another issue is non-US-ascii symbols. All programs out there
should accept UTF-8. But unfortunately it is not true in reality.
For example, less does not render middle dot properly.

This change prepends outer function name to closure name and
replaces middle dot with dot. Now names look like:

main.glob.func1
main.glob.func2
main.glob.func2.1
main.init.1
main.init.1.func1
main.init.1.func1.1
main.main.func1
main.main.func1.1

Change-Id: I725726af88f2ad3ced2e3450f0f06bf459fd91c0
Reviewed-on: https://go-review.googlesource.com/3964
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Dmitry Vyukov 2015-02-05 16:08:29 +03:00
parent 3ad906b208
commit 3a0fbfab57
4 changed files with 60 additions and 16 deletions

View File

@ -19,6 +19,7 @@ closurehdr(Node *ntype)
n = nod(OCLOSURE, N, N);
n->ntype = ntype;
n->funcdepth = funcdepth;
n->outerfunc = curfn;
funchdr(n);
@ -124,11 +125,55 @@ typecheckclosure(Node *func, int top)
xtop = list(xtop, makeclosure(func));
}
// closurename returns name for OCLOSURE n.
// It is not as simple as it ought to be, because we typecheck nested closures
// starting from the innermost one. So when we check the inner closure,
// we don't yet have name for the outer closure. This function uses recursion
// to generate names all the way up if necessary.
static Sym*
closurename(Node *n)
{
static int closgen;
char *outer, *prefix;
int gen;
if(n->sym != S)
return n->sym;
gen = 0;
outer = NULL;
prefix = NULL;
if(n->outerfunc == N) {
// Global closure.
outer = "glob";
prefix = "func";
gen = ++closgen;
} else if(n->outerfunc->op == ODCLFUNC) {
// The outermost closure inside of a named function.
outer = n->outerfunc->nname->sym->name;
prefix = "func";
// Yes, functions can be named _.
// Can't use function closgen in such case,
// because it would lead to name clashes.
if(!isblank(n->outerfunc->nname))
gen = ++n->outerfunc->closgen;
else
gen = ++closgen;
} else if(n->outerfunc->op == OCLOSURE) {
// Nested closure, recurse.
outer = closurename(n->outerfunc)->name;
prefix = "";
gen = ++n->outerfunc->closgen;
} else
fatal("closurename called for %hN", n);
snprint(namebuf, sizeof namebuf, "%s.%s%d", outer, prefix, gen);
n->sym = lookup(namebuf);
return n->sym;
}
static Node*
makeclosure(Node *func)
{
Node *xtype, *xfunc;
static int closgen;
/*
* wrap body in external function
@ -140,8 +185,7 @@ makeclosure(Node *func)
// create the function
xfunc = nod(ODCLFUNC, N, N);
snprint(namebuf, sizeof namebuf, "func·%.3d", ++closgen);
xfunc->nname = newname(lookup(namebuf));
xfunc->nname = newname(closurename(func));
xfunc->nname->sym->flags |= SymExported; // disable export
xfunc->nname->ntype = xtype;
xfunc->nname->defn = xfunc;
@ -158,7 +202,7 @@ makeclosure(Node *func)
xfunc->closure = func;
func->closure = xfunc;
func->nbody = nil;
func->list = nil;
func->rlist = nil;
@ -368,7 +412,7 @@ walkclosure(Node *func, NodeList **init)
// and has one float64 argument and no results,
// the generated code looks like:
//
// clos = &struct{F uintptr; A0 *int; A1 *string}{func·001, &i, &s}
// clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
//
// The use of the struct provides type information to the garbage
// collector so that it can walk the closure. We could use (in this case)
@ -378,7 +422,7 @@ walkclosure(Node *func, NodeList **init)
// same struct type can share the descriptor.
typ = nod(OTSTRUCT, N, N);
typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
typ->list = list1(nod(ODCLFIELD, newname(lookup(".F")), typenod(types[TUINTPTR])));
for(l=func->cvars; l; l=l->next) {
v = l->n;
if(v->op == OXXX)
@ -447,12 +491,11 @@ makepartialcall(Node *fn, Type *t0, Node *meth)
static Pkg* gopkg;
int i, ddd;
// TODO: names are not right
rcvrtype = fn->left->type;
if(exportname(meth->sym->name))
p = smprint("%-hT.%s·fm", rcvrtype, meth->sym->name);
p = smprint("(%-hT).%s-fm", rcvrtype, meth->sym->name);
else
p = smprint("%-hT.(%-S)·fm", rcvrtype, meth->sym);
p = smprint("(%-hT).(%-S)-fm", rcvrtype, meth->sym);
basetype = rcvrtype;
if(isptr[rcvrtype->etype])
basetype = basetype->type;

View File

@ -310,6 +310,8 @@ struct Node
NodeList* dcl; // autodcl for this func/closure
NodeList* inl; // copy of the body for use in inlining
NodeList* inldcl; // copy of dcl for use in inlining
int closgen;
Node* outerfunc;
// OLITERAL/OREGISTER
Val val;

View File

@ -11,14 +11,14 @@
* it is called by the initialization before
* main is run. to make it unique within a
* package and also uncallable, the name,
* normally "pkg.init", is altered to "pkg.init·1".
* normally "pkg.init", is altered to "pkg.init.1".
*/
Sym*
renameinit(void)
{
static int initgen;
snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
snprint(namebuf, sizeof(namebuf), "init.%d", ++initgen);
return lookup(namebuf);
}
@ -35,7 +35,7 @@ renameinit(void)
* // over all matching imported symbols
* <pkg>.init() (7)
* { <init stmts> } (8)
* init·<n>() // if any (9)
* init.<n>() // if any (9)
* initdone· = 2; (10)
* return (11)
* }
@ -69,8 +69,7 @@ anyinit(NodeList *n)
return 1;
// is there an explicit init function
snprint(namebuf, sizeof(namebuf), "init·1");
s = lookup(namebuf);
s = lookup("init.1");
if(s->def != N)
return 1;
@ -167,7 +166,7 @@ fninit(NodeList *n)
// (9)
// could check that it is fn of no args/returns
for(i=1;; i++) {
snprint(namebuf, sizeof(namebuf), "init·%d", i);
snprint(namebuf, sizeof(namebuf), "init.%d", i);
s = lookup(namebuf);
if(s->def == N)
break;

View File

@ -180,7 +180,7 @@ compile(Node *fn)
dowidth(curfn->type);
if(fn->nbody == nil) {
if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) {
if(pure_go || strncmp(fn->nname->sym->name, "init.", 5) == 0) {
yyerror("missing function body", fn);
goto ret;
}