| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package pointer_test | |
| import ( | |
| "fmt" | |
| "sort" | |
| "golang.org/x/tools/go/callgraph" | |
| "golang.org/x/tools/go/loader" | |
| "golang.org/x/tools/go/pointer" | |
| "golang.org/x/tools/go/ssa" | |
| "golang.org/x/tools/go/ssa/ssautil" | |
| ) | |
| // This program demonstrates how to use the pointer analysis to | |
| // obtain a conservative call-graph of a Go program. | |
| // It also shows how to compute the points-to set of a variable, | |
| // in this case, (C).f's ch parameter. | |
| func Example() { | |
| const myprog = ` | |
| package main | |
| import "fmt" | |
| type I interface { | |
| f(map[string]int) | |
| } | |
| type C struct{} | |
| func (C) f(m map[string]int) { | |
| fmt.Println("C.f()") | |
| } | |
| func main() { | |
| var i I = C{} | |
| x := map[string]int{"one":1} | |
| i.f(x) // dynamic method call | |
| } | |
| ` | |
| var conf loader.Config | |
| // Parse the input file, a string. | |
| // (Command-line tools should use conf.FromArgs.) | |
| file, err := conf.ParseFile("myprog.go", myprog) | |
| if err != nil { | |
| fmt.Print(err) // parse error | |
| return | |
| } | |
| // Create single-file main package and import its dependencies. | |
| conf.CreateFromFiles("main", file) | |
| iprog, err := conf.Load() | |
| if err != nil { | |
| fmt.Print(err) // type error in some package | |
| return | |
| } | |
| // Create SSA-form program representation. | |
| prog := ssautil.CreateProgram(iprog, ssa.InstantiateGenerics) | |
| mainPkg := prog.Package(iprog.Created[0].Pkg) | |
| // Build SSA code for bodies of all functions in the whole program. | |
| prog.Build() | |
| // Configure the pointer analysis to build a call-graph. | |
| config := &pointer.Config{ | |
| Mains: []*ssa.Package{mainPkg}, | |
| BuildCallGraph: true, | |
| } | |
| // Query points-to set of (C).f's parameter m, a map. | |
| C := mainPkg.Type("C").Type() | |
| Cfm := prog.LookupMethod(C, mainPkg.Pkg, "f").Params[1] | |
| config.AddQuery(Cfm) | |
| // Run the pointer analysis. | |
| result, err := pointer.Analyze(config) | |
| if err != nil { | |
| panic(err) // internal error in pointer analysis | |
| } | |
| // Find edges originating from the main package. | |
| // By converting to strings, we de-duplicate nodes | |
| // representing the same function due to context sensitivity. | |
| var edges []string | |
| callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error { | |
| caller := edge.Caller.Func | |
| if caller.Pkg == mainPkg { | |
| edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func)) | |
| } | |
| return nil | |
| }) | |
| // Print the edges in sorted order. | |
| sort.Strings(edges) | |
| for _, edge := range edges { | |
| fmt.Println(edge) | |
| } | |
| fmt.Println() | |
| // Print the labels of (C).f(m)'s points-to set. | |
| fmt.Println("m may point to:") | |
| var labels []string | |
| for _, l := range result.Queries[Cfm].PointsTo().Labels() { | |
| label := fmt.Sprintf(" %s: %s", prog.Fset.Position(l.Pos()), l) | |
| labels = append(labels, label) | |
| } | |
| sort.Strings(labels) | |
| for _, label := range labels { | |
| fmt.Println(label) | |
| } | |
| // Output: | |
| // (main.C).f --> fmt.Println | |
| // main.init --> fmt.init | |
| // main.main --> (main.C).f | |
| // | |
| // m may point to: | |
| // myprog.go:18:21: makemap | |
| } |