| // 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" |
| "go/build" |
| "go/parser" |
| "sort" |
| |
| "code.google.com/p/go.tools/importer" |
| "code.google.com/p/go.tools/pointer" |
| "code.google.com/p/go.tools/ssa" |
| ) |
| |
| // This program demonstrates how to use the pointer analysis to |
| // obtain a conservative call-graph of a Go program. |
| // |
| func Example() { |
| const myprog = ` |
| package main |
| |
| import "fmt" |
| |
| type I interface { |
| f() |
| } |
| |
| type C struct{} |
| |
| func (C) f() { |
| fmt.Println("C.f()") |
| } |
| |
| func main() { |
| var i I = C{} |
| i.f() // dynamic method call |
| } |
| ` |
| // Construct an importer. |
| // Imports will be loaded as if by 'go build'. |
| imp := importer.New(&importer.Config{Build: &build.Default}) |
| |
| // Parse the input file. |
| file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, parser.DeclarationErrors) |
| if err != nil { |
| fmt.Print(err) // parse error |
| return |
| } |
| |
| // Create a "main" package containing one file. |
| mainInfo := imp.LoadMainPackage(file) |
| |
| // Create SSA-form program representation. |
| var mode ssa.BuilderMode |
| prog := ssa.NewProgram(imp.Fset, mode) |
| if err := prog.CreatePackages(imp); err != nil { |
| fmt.Print(err) // type error in some package |
| return |
| } |
| mainPkg := prog.Package(mainInfo.Pkg) |
| |
| // Build SSA code for bodies of all functions in the whole program. |
| prog.BuildAll() |
| |
| // Run the pointer analysis and build the complete callgraph. |
| callgraph := make(pointer.CallGraph) |
| config := &pointer.Config{ |
| Mains: []*ssa.Package{mainPkg}, |
| Call: callgraph.AddEdge, |
| } |
| root := pointer.Analyze(config) |
| |
| // Visit callgraph in depth-first order. |
| // |
| // There may be multiple nodes for the |
| // same function due to context sensitivity. |
| var edges []string // call edges originating from the main package. |
| seen := make(map[pointer.CallGraphNode]bool) |
| var visit func(cgn pointer.CallGraphNode) |
| visit = func(cgn pointer.CallGraphNode) { |
| if seen[cgn] { |
| return // already seen |
| } |
| seen[cgn] = true |
| caller := cgn.Func() |
| for callee := range callgraph[cgn] { |
| if caller.Pkg == mainPkg { |
| edges = append(edges, fmt.Sprint(caller, " --> ", callee.Func())) |
| } |
| visit(callee) |
| } |
| } |
| visit(root) |
| |
| // Print the edges in sorted order. |
| sort.Strings(edges) |
| for _, edge := range edges { |
| fmt.Println(edge) |
| } |
| |
| // Output: |
| // (main.C).f --> fmt.Println |
| // main.init --> fmt.init |
| // main.main --> (main.C).f |
| } |