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, 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.
 | |
| 	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
 | |
| }
 |