github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/callgraph/cha/cha14.go (about) 1 // Copyright 2014 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 // +build !go1.5 6 7 // Package cha computes the call graph of a Go program using the Class 8 // Hierarchy Analysis (CHA) algorithm. 9 // 10 // CHA was first described in "Optimization of Object-Oriented Programs 11 // Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove, 12 // and Craig Chambers, ECOOP'95. 13 // 14 // CHA is related to RTA (see go/callgraph/rta); the difference is that 15 // CHA conservatively computes the entire "implements" relation between 16 // interfaces and concrete types ahead of time, whereas RTA uses dynamic 17 // programming to construct it on the fly as it encounters new functions 18 // reachable from main. CHA may thus include spurious call edges for 19 // types that haven't been instantiated yet, or types that are never 20 // instantiated. 21 // 22 // Since CHA conservatively assumes that all functions are address-taken 23 // and all concrete types are put into interfaces, it is sound to run on 24 // partial programs, such as libraries without a main or test function. 25 // 26 package cha // import "golang.org/x/tools/go/callgraph/cha" 27 28 import ( 29 "golang.org/x/tools/go/callgraph" 30 "golang.org/x/tools/go/ssa" 31 "golang.org/x/tools/go/ssa/ssautil" 32 "golang.org/x/tools/go/types" 33 "golang.org/x/tools/go/types/typeutil" 34 ) 35 36 // CallGraph computes the call graph of the specified program using the 37 // Class Hierarchy Analysis algorithm. 38 // 39 func CallGraph(prog *ssa.Program) *callgraph.Graph { 40 cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph 41 42 allFuncs := ssautil.AllFunctions(prog) 43 44 // funcsBySig contains all functions, keyed by signature. It is 45 // the effective set of address-taken functions used to resolve 46 // a dynamic call of a particular signature. 47 var funcsBySig typeutil.Map // value is []*ssa.Function 48 49 // methodsByName contains all methods, 50 // grouped by name for efficient lookup. 51 methodsByName := make(map[string][]*ssa.Function) 52 53 // methodsMemo records, for every abstract method call call I.f on 54 // interface type I, the set of concrete methods C.f of all 55 // types C that satisfy interface I. 56 methodsMemo := make(map[*types.Func][]*ssa.Function) 57 lookupMethods := func(m *types.Func) []*ssa.Function { 58 methods, ok := methodsMemo[m] 59 if !ok { 60 I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) 61 for _, f := range methodsByName[m.Name()] { 62 C := f.Signature.Recv().Type() // named or *named 63 if types.Implements(C, I) { 64 methods = append(methods, f) 65 } 66 } 67 methodsMemo[m] = methods 68 } 69 return methods 70 } 71 72 for f := range allFuncs { 73 if f.Signature.Recv() == nil { 74 // Package initializers can never be address-taken. 75 if f.Name() == "init" && f.Synthetic == "package initializer" { 76 continue 77 } 78 funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) 79 funcs = append(funcs, f) 80 funcsBySig.Set(f.Signature, funcs) 81 } else { 82 methodsByName[f.Name()] = append(methodsByName[f.Name()], f) 83 } 84 } 85 86 addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { 87 gnode := cg.CreateNode(g) 88 callgraph.AddEdge(fnode, site, gnode) 89 } 90 91 addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { 92 // Because every call to a highly polymorphic and 93 // frequently used abstract method such as 94 // (io.Writer).Write is assumed to call every concrete 95 // Write method in the program, the call graph can 96 // contain a lot of duplication. 97 // 98 // TODO(adonovan): opt: consider factoring the callgraph 99 // API so that the Callers component of each edge is a 100 // slice of nodes, not a singleton. 101 for _, g := range callees { 102 addEdge(fnode, site, g) 103 } 104 } 105 106 for f := range allFuncs { 107 fnode := cg.CreateNode(f) 108 for _, b := range f.Blocks { 109 for _, instr := range b.Instrs { 110 if site, ok := instr.(ssa.CallInstruction); ok { 111 call := site.Common() 112 if call.IsInvoke() { 113 addEdges(fnode, site, lookupMethods(call.Method)) 114 } else if g := call.StaticCallee(); g != nil { 115 addEdge(fnode, site, g) 116 } else if _, ok := call.Value.(*ssa.Builtin); !ok { 117 callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) 118 addEdges(fnode, site, callees) 119 } 120 } 121 } 122 } 123 } 124 125 return cg 126 }