126 lines
3.0 KiB
Go
126 lines
3.0 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"
|
|
|
|
"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
|
|
}
|