diff --git a/pointer/TODO b/pointer/TODO index 0c9daecfc41..d5f12bc714d 100644 --- a/pointer/TODO +++ b/pointer/TODO @@ -4,7 +4,8 @@ Pointer analysis to-do list =========================== CONSTRAINT GENERATION: -- reflection intrinsics +- support reflection +- implement native intrinsics. These vary by platform. - unsafe.Pointer conversions. Three options: 1) unsoundly (but type-safely) treat p=unsafe.Pointer(x) conversions as allocations, losing aliases. This is what's currently implemented. @@ -29,3 +30,19 @@ SOLVER: fast doubly-linked list (See Zhanh et al PLDI'13) (insertion order with fast membership test) dannyb recommends sparse bitmap. + +API: +- Rely less on callbacks and more on a 'result' type + returned by Analyze(). +- Abstract the callgraph into a pure interface so that + we can provide other implementations in future (e.g. RTA-based). + Also provide the option to eliminate context-sensitivity + in a callgraph to yield a smaller (less precise) callgraph. +- Some optimisations (e.g. LE, PE) may change the API. + Think about them sooner rather than later. +- Eliminate Print probe now that we can query specific ssa.Values. + +Misc: +- os.Args should point to something; currently they don't. +- Test on all platforms. + Currently we assume these go/build tags: linux, amd64, !cgo. diff --git a/pointer/example_test.go b/pointer/example_test.go new file mode 100644 index 00000000000..fae61954cce --- /dev/null +++ b/pointer/example_test.go @@ -0,0 +1,108 @@ +// 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" + "go/build" + "go/parser" + "sort" + + "code.google.com/p/go.tools/importer" + "code.google.com/p/go.tools/pointer" + "code.google.com/p/go.tools/ssa" +) + +// This program demonstrates how to use the pointer analysis to +// obtain a conservative call-graph of a Go program. +// +func Example() { + const myprog = ` +package main + +import "fmt" + +type I interface { + f() +} + +type C struct{} + +func (C) f() { + fmt.Println("C.f()") +} + +func main() { + var i I = C{} + i.f() // dynamic method call +} +` + // Construct an importer. + // Imports will be loaded as if by 'go build'. + imp := importer.New(&importer.Config{Build: &build.Default}) + + // Parse the input file. + file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, parser.DeclarationErrors) + if err != nil { + fmt.Print(err) // parse error + return + } + + // Create a "main" package containing one file. + mainInfo := imp.LoadMainPackage(file) + + // Create SSA-form program representation. + var mode ssa.BuilderMode + prog := ssa.NewProgram(imp.Fset, mode) + if err := prog.CreatePackages(imp); err != nil { + fmt.Print(err) // type error in some package + return + } + mainPkg := prog.Package(mainInfo.Pkg) + + // Build SSA code for bodies of all functions in the whole program. + prog.BuildAll() + + // Run the pointer analysis and build the complete callgraph. + callgraph := make(pointer.CallGraph) + config := &pointer.Config{ + Mains: []*ssa.Package{mainPkg}, + Call: callgraph.AddEdge, + } + root := pointer.Analyze(config) + + // Visit callgraph in depth-first order. + // + // There may be multiple nodes for the + // same function due to context sensitivity. + var edges []string // call edges originating from the main package. + seen := make(map[pointer.CallGraphNode]bool) + var visit func(cgn pointer.CallGraphNode) + visit = func(cgn pointer.CallGraphNode) { + if seen[cgn] { + return // already seen + } + seen[cgn] = true + caller := cgn.Func() + for callee := range callgraph[cgn] { + if caller.Pkg == mainPkg { + edges = append(edges, fmt.Sprint(caller, " --> ", callee.Func())) + } + visit(callee) + } + } + visit(root) + + // Print the edges in sorted order. + sort.Strings(edges) + for _, edge := range edges { + fmt.Println(edge) + } + + // Output: + // (main.C).f --> fmt.Println + // main.init --> fmt.init + // main.main --> (main.C).f +}