go.tools/go/ssa: improve printing of anonymous functions.

Examples:
- "foo$1" becomes "pkg.foo$1"
- "init$1" (meaning the first declared "init" function) becomes "init#1",
   to distinguish it from "init$1" (meaning the first anonymous function
   within the synthetic "init" function that initializes package-level vars).

It is now an invariant that all source-level (non-synthetic)
functions have distinct names, and that all names include the
enclosing package.  Added test for this.

+ updated various clients.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/122750043
This commit is contained in:
Alan Donovan 2014-07-31 17:37:41 -04:00
parent b419465131
commit 4228ee8063
11 changed files with 63 additions and 35 deletions

View File

@ -60,26 +60,26 @@ func TestEnclosingFunction(t *testing.T) {
// Function literal: // Function literal:
{`package main {`package main
func f() { println(func() { print(300) }) }`, func f() { println(func() { print(300) }) }`,
"300", "f$1"}, "300", "main.f$1"},
// Doubly nested // Doubly nested
{`package main {`package main
func f() { println(func() { print(func() { print(350) })})}`, func f() { println(func() { print(func() { print(350) })})}`,
"350", "f$1$1"}, "350", "main.f$1$1"},
// Implicit init for package-level var initializer. // Implicit init for package-level var initializer.
{"package main; var a = 400", "400", "main.init"}, {"package main; var a = 400", "400", "main.init"},
// No code for constants: // No code for constants:
{"package main; const a = 500", "500", "(none)"}, {"package main; const a = 500", "500", "(none)"},
// Explicit init() // Explicit init()
{"package main; func init() { println(600) }", "600", "main.init$1"}, {"package main; func init() { println(600) }", "600", "main.init#1"},
// Multiple explicit init functions: // Multiple explicit init functions:
{`package main {`package main
func init() { println("foo") } func init() { println("foo") }
func init() { println(800) }`, func init() { println(800) }`,
"800", "main.init$2"}, "800", "main.init#2"},
// init() containing FuncLit. // init() containing FuncLit.
{`package main {`package main
func init() { println(func(){print(900)}) }`, func init() { println(func(){print(900)}) }`,
"900", "init$1$1"}, "900", "main.init#1$1"},
} }
for _, test := range tests { for _, test := range tests {
conf := loader.Config{Fset: token.NewFileSet()} conf := loader.Config{Fset: token.NewFileSet()}

View File

@ -19,7 +19,7 @@ func chan1() {
print(<-chA) // @pointsto main.incr print(<-chA) // @pointsto main.incr
print(chB) // @pointsto makechan@c1makeB:13 print(chB) // @pointsto makechan@c1makeB:13
print(<-chB) // @pointsto main.decr | chan1$1 print(<-chB) // @pointsto main.decr | main.chan1$1
} }
func chan2() { func chan2() {
@ -40,10 +40,10 @@ func chan2() {
print(<-chA) // @pointsto main.incr print(<-chA) // @pointsto main.incr
print(chB) // @pointsto makechan@c2makeB:13 print(chB) // @pointsto makechan@c2makeB:13
print(<-chB) // @pointsto main.decr | chan2$1 print(<-chB) // @pointsto main.decr | main.chan2$1
print(chAB) // @pointsto makechan@c2makeA:13 | makechan@c2makeB:13 print(chAB) // @pointsto makechan@c2makeA:13 | makechan@c2makeB:13
print(<-chAB) // @pointsto main.incr | main.decr | chan2$1 print(<-chAB) // @pointsto main.incr | main.decr | main.chan2$1
(<-chA)(3) (<-chA)(3)
} }
@ -59,7 +59,7 @@ func chan3() {
print(chA) // @pointsto makechan@c3makeA:13 print(chA) // @pointsto makechan@c3makeA:13
print(<-chA) // @pointsto main.incr print(<-chA) // @pointsto main.incr
print(chB) // @pointsto makechan@c3makeB:13 print(chB) // @pointsto makechan@c3makeB:13
print(<-chB) // @pointsto main.decr | chan3$1 print(<-chB) // @pointsto main.decr | main.chan3$1
(<-chA)(3) (<-chA)(3)
} }

View File

@ -28,9 +28,9 @@ func func1() {
print(&a) // @pointsto main.a print(&a) // @pointsto main.a
} }
// @calls main.func1 -> func1$2 // @calls main.func1 -> main.func1$2
// @calls main.func1 -> func1$1 // @calls main.func1 -> main.func1$1
// @calls func1$2 -> func1$1 // @calls main.func1$2 -> main.func1$1
func func2() { func func2() {
var x, y *int var x, y *int
@ -124,7 +124,7 @@ func func6() {
print(f()) // @pointsto main.a print(f()) // @pointsto main.a
} }
// @calls main.func6 -> func6$1 // @calls main.func6 -> main.func6$1
type I interface { type I interface {
f() f()

View File

@ -2110,7 +2110,7 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
if decl.Recv == nil && id.Name == "init" { if decl.Recv == nil && id.Name == "init" {
pkg.ninit++ pkg.ninit++
fn = &Function{ fn = &Function{
name: fmt.Sprintf("init$%d", pkg.ninit), name: fmt.Sprintf("init#%d", pkg.ninit),
Signature: new(types.Signature), Signature: new(types.Signature),
pos: decl.Name.NamePos, pos: decl.Name.NamePos,
Pkg: pkg, Pkg: pkg,

View File

@ -452,22 +452,33 @@ func (f *Function) emit(instr Instruction) Value {
// The specific formatting rules are not guaranteed and may change. // The specific formatting rules are not guaranteed and may change.
// //
// Examples: // Examples:
// "math.IsNaN" // a package-level function // "math.IsNaN" // a package-level function
// "IsNaN" // intra-package reference to same // "(*bytes.Buffer).Bytes" // a declared method or a wrapper
// "(*bytes.Buffer).Bytes" // a declared method or a wrapper // "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
// "(*Buffer).Bytes" // intra-package reference to same // "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
// "(*Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0) // "main.main$1" // an anonymous function in main
// "(*Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure) // "main.init#1" // a declared init function
// "main$1" // an anonymous function // "main.init" // the synthesized package initializer
// "init$1" // a declared init function
// "init" // the synthesized package initializer
// //
// If from==f.Pkg, suppress package qualification. // When these functions are referred to from within the same package
// (i.e. from == f.Pkg.Object), they are rendered without the package path.
// For example: "IsNaN", "(*Buffer).Bytes", etc.
//
// Invariant: all non-synthetic functions have distinct package-qualified names.
// //
func (f *Function) RelString(from *types.Package) string { func (f *Function) RelString(from *types.Package) string {
// Anonymous? // Anonymous?
if f.parent != nil { if f.parent != nil {
return f.name // An anonymous function's Name() looks like "parentName$1",
// but its String() should include the type/package/etc.
parent := f.parent.RelString(from)
for i, anon := range f.parent.AnonFuncs {
if anon == f {
return fmt.Sprintf("%s$%d", parent, 1+i)
}
}
return f.name // should never happen
} }
// Method (declared or wrapper)? // Method (declared or wrapper)?

View File

@ -93,8 +93,25 @@ func TestStdlib(t *testing.T) {
t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
} }
// Dump some statistics.
allFuncs := ssautil.AllFunctions(prog) allFuncs := ssautil.AllFunctions(prog)
// Check that all non-synthetic functions have distinct names.
byName := make(map[string]*ssa.Function)
for fn := range allFuncs {
if fn.Synthetic == "" {
str := fn.String()
prev := byName[str]
byName[str] = fn
if prev != nil {
t.Errorf("%s: duplicate function named %s",
prog.Fset.Position(fn.Pos()), str)
t.Errorf("%s: (previously defined here)",
prog.Fset.Position(prev.Pos()))
}
}
}
// Dump some statistics.
var numInstrs int var numInstrs int
for fn := range allFuncs { for fn := range allFuncs {
for _, b := range fn.Blocks { for _, b := range fn.Blocks {

View File

@ -44,7 +44,7 @@
"pos": "testdata/src/main/callgraph-json.go:9:6" "pos": "testdata/src/main/callgraph-json.go:9:6"
}, },
{ {
"name": "main$1", "name": "main.main$1",
"pos": "testdata/src/main/callgraph-json.go:31:8" "pos": "testdata/src/main/callgraph-json.go:31:8"
} }
] ]

View File

@ -31,7 +31,7 @@ Non-numbered nodes indicate back- or cross-edges to the node whose
6 main.A 6 main.A
7 main.B 7 main.B
8 main.call2 8 main.call2
9 main$1 9 main.main$1
main.main (3) main.main (3)
10 main.nop 10 main.nop

View File

@ -6,7 +6,7 @@
"desc": "dynamic function call", "desc": "dynamic function call",
"callees": [ "callees": [
{ {
"name": "main$1", "name": "main.main$1",
"pos": "testdata/src/main/calls-json.go:12:7" "pos": "testdata/src/main/calls-json.go:12:7"
} }
] ]
@ -16,7 +16,7 @@
"mode": "callstack", "mode": "callstack",
"callstack": { "callstack": {
"pos": "testdata/src/main/calls-json.go:12:7", "pos": "testdata/src/main/calls-json.go:12:7",
"target": "main$1", "target": "main.main$1",
"callers": [ "callers": [
{ {
"pos": "testdata/src/main/calls-json.go:8:3", "pos": "testdata/src/main/calls-json.go:8:3",

View File

@ -35,11 +35,11 @@ main.store is called from these 2 sites:
-------- @pointsto pointsto-result-f -------- -------- @pointsto pointsto-result-f --------
this func() *int may point to these objects: this func() *int may point to these objects:
main$1 main.main$1
-------- @callees callees-main.call-f -------- -------- @callees callees-main.call-f --------
this dynamic function call dispatches to: this dynamic function call dispatches to:
main$1 main.main$1
-------- @callers callers-main.call -------- -------- @callers callers-main.call --------
main.call is called from these 2 sites: main.call is called from these 2 sites:
@ -103,7 +103,7 @@ main.init is called from these 1 sites:
the root of the call graph the root of the call graph
-------- @callstack callstack-init -------- -------- @callstack callstack-init --------
Found a call path from root to main.init$1 Found a call path from root to main.init#1
main.init$1 main.init#1
static function call from main.init static function call from main.init

View File

@ -29,7 +29,7 @@ this func() may point to these objects:
-------- @pointsto ref-anon -------- -------- @pointsto ref-anon --------
this func() may point to these objects: this func() may point to these objects:
main$1 pointsto.main$1
-------- @pointsto ref-global -------- -------- @pointsto ref-global --------
this *string may point to these objects: this *string may point to these objects: