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:
parent
b419465131
commit
4228ee8063
|
|
@ -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()}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)?
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue