github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/pointer/example_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pointer_test
     6  
     7  import (
     8  	"fmt"
     9  	"sort"
    10  
    11  	"github.com/powerman/golang-tools/go/callgraph"
    12  	"github.com/powerman/golang-tools/go/loader"
    13  	"github.com/powerman/golang-tools/go/pointer"
    14  	"github.com/powerman/golang-tools/go/ssa"
    15  	"github.com/powerman/golang-tools/go/ssa/ssautil"
    16  )
    17  
    18  // This program demonstrates how to use the pointer analysis to
    19  // obtain a conservative call-graph of a Go program.
    20  // It also shows how to compute the points-to set of a variable,
    21  // in this case, (C).f's ch parameter.
    22  //
    23  func Example() {
    24  	const myprog = `
    25  package main
    26  
    27  import "fmt"
    28  
    29  type I interface {
    30  	f(map[string]int)
    31  }
    32  
    33  type C struct{}
    34  
    35  func (C) f(m map[string]int) {
    36  	fmt.Println("C.f()")
    37  }
    38  
    39  func main() {
    40  	var i I = C{}
    41  	x := map[string]int{"one":1}
    42  	i.f(x) // dynamic method call
    43  }
    44  `
    45  	var conf loader.Config
    46  
    47  	// Parse the input file, a string.
    48  	// (Command-line tools should use conf.FromArgs.)
    49  	file, err := conf.ParseFile("myprog.go", myprog)
    50  	if err != nil {
    51  		fmt.Print(err) // parse error
    52  		return
    53  	}
    54  
    55  	// Create single-file main package and import its dependencies.
    56  	conf.CreateFromFiles("main", file)
    57  
    58  	iprog, err := conf.Load()
    59  	if err != nil {
    60  		fmt.Print(err) // type error in some package
    61  		return
    62  	}
    63  
    64  	// Create SSA-form program representation.
    65  	prog := ssautil.CreateProgram(iprog, 0)
    66  	mainPkg := prog.Package(iprog.Created[0].Pkg)
    67  
    68  	// Build SSA code for bodies of all functions in the whole program.
    69  	prog.Build()
    70  
    71  	// Configure the pointer analysis to build a call-graph.
    72  	config := &pointer.Config{
    73  		Mains:          []*ssa.Package{mainPkg},
    74  		BuildCallGraph: true,
    75  	}
    76  
    77  	// Query points-to set of (C).f's parameter m, a map.
    78  	C := mainPkg.Type("C").Type()
    79  	Cfm := prog.LookupMethod(C, mainPkg.Pkg, "f").Params[1]
    80  	config.AddQuery(Cfm)
    81  
    82  	// Run the pointer analysis.
    83  	result, err := pointer.Analyze(config)
    84  	if err != nil {
    85  		panic(err) // internal error in pointer analysis
    86  	}
    87  
    88  	// Find edges originating from the main package.
    89  	// By converting to strings, we de-duplicate nodes
    90  	// representing the same function due to context sensitivity.
    91  	var edges []string
    92  	callgraph.GraphVisitEdges(result.CallGraph, func(edge *callgraph.Edge) error {
    93  		caller := edge.Caller.Func
    94  		if caller.Pkg == mainPkg {
    95  			edges = append(edges, fmt.Sprint(caller, " --> ", edge.Callee.Func))
    96  		}
    97  		return nil
    98  	})
    99  
   100  	// Print the edges in sorted order.
   101  	sort.Strings(edges)
   102  	for _, edge := range edges {
   103  		fmt.Println(edge)
   104  	}
   105  	fmt.Println()
   106  
   107  	// Print the labels of (C).f(m)'s points-to set.
   108  	fmt.Println("m may point to:")
   109  	var labels []string
   110  	for _, l := range result.Queries[Cfm].PointsTo().Labels() {
   111  		label := fmt.Sprintf("  %s: %s", prog.Fset.Position(l.Pos()), l)
   112  		labels = append(labels, label)
   113  	}
   114  	sort.Strings(labels)
   115  	for _, label := range labels {
   116  		fmt.Println(label)
   117  	}
   118  
   119  	// Output:
   120  	// (main.C).f --> fmt.Println
   121  	// main.init --> fmt.init
   122  	// main.main --> (main.C).f
   123  	//
   124  	// m may point to:
   125  	//   myprog.go:18:21: makemap
   126  }