99 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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/call"
 | 
						|
	"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, 0)
 | 
						|
	if err != nil {
 | 
						|
		fmt.Print(err) // parse error
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Create single-file main package and import its dependencies.
 | 
						|
	mainInfo := imp.CreatePackage("main", 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.
 | 
						|
	config := &pointer.Config{
 | 
						|
		Mains:          []*ssa.Package{mainPkg},
 | 
						|
		BuildCallGraph: true,
 | 
						|
	}
 | 
						|
	result := pointer.Analyze(config)
 | 
						|
 | 
						|
	// 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
 | 
						|
	call.GraphVisitEdges(result.CallGraph, func(edge call.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)
 | 
						|
	}
 | 
						|
 | 
						|
	// Output:
 | 
						|
	// (main.C).f --> fmt.Println
 | 
						|
	// main.init --> fmt.init
 | 
						|
	// main.main --> (main.C).f
 | 
						|
}
 |