example_test.go
2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 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"
"llvm.org/llgo/third_party/gotools/go/callgraph"
"llvm.org/llgo/third_party/gotools/go/loader"
"llvm.org/llgo/third_party/gotools/go/pointer"
"llvm.org/llgo/third_party/gotools/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
}
`
var conf loader.Config
// Parse the input file, a string.
// (Command-line tools should use conf.FromArgs.)
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
}