109 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			109 lines
		
	
	
		
			2.4 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/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
 | |
| }
 |