126 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			2.9 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"
 | 
						|
	"sort"
 | 
						|
 | 
						|
	"code.google.com/p/go.tools/go/callgraph"
 | 
						|
	"code.google.com/p/go.tools/go/loader"
 | 
						|
	"code.google.com/p/go.tools/go/pointer"
 | 
						|
	"code.google.com/p/go.tools/go/ssa"
 | 
						|
)
 | 
						|
 | 
						|
// 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
 | 
						|
}
 | 
						|
`
 | 
						|
	// Construct a loader.
 | 
						|
	conf := loader.Config{SourceImports: true}
 | 
						|
 | 
						|
	// Parse the input file.
 | 
						|
	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 := ssa.Create(iprog, 0)
 | 
						|
	mainPkg := prog.Package(iprog.Created[0].Pkg)
 | 
						|
 | 
						|
	// Build SSA code for bodies of all functions in the whole program.
 | 
						|
	prog.BuildAll()
 | 
						|
 | 
						|
	// 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.Object, "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
 | 
						|
}
 |