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"
 | |
| 
 | |
| 	"golang.org/x/tools/go/callgraph"
 | |
| 	"golang.org/x/tools/go/loader"
 | |
| 	"golang.org/x/tools/go/pointer"
 | |
| 	"golang.org/x/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
 | |
| }
 |