// callEdge is called for each edge in the callgraph. // calleeid is the callee's object node (has otFunction flag). // func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) { obj := a.nodes[calleeid].obj if obj.flags&otFunction == 0 { panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid)) } callee := obj.cgn if cg := a.result.CallGraph; cg != nil { // TODO(adonovan): opt: I would expect duplicate edges // (to wrappers) to arise due to the elimination of // context information, but I haven't observed any. // Understand this better. callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn)) } if a.log != nil { fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee) } // Warn about calls to non-intrinsic external functions. // TODO(adonovan): de-dup these messages. if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil { a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn) a.warnf(fn.Pos(), " (declared here)") } }
// addEdge adds the specified call graph edge, and marks it reachable. // addrTaken indicates whether to mark the callee as "address-taken". func (r *rta) addEdge(site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) { r.addReachable(callee, addrTaken) if g := r.result.CallGraph; g != nil { if site.Parent() == nil { panic(site) } from := g.CreateNode(site.Parent()) to := g.CreateNode(callee) callgraph.AddEdge(from, site, to) } }
// CallGraph computes the call graph of the specified program // considering only static calls. // func CallGraph(prog *ssa.Program) *callgraph.Graph { cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph // TODO(adonovan): opt: use only a single pass over the ssa.Program. for f := range ssautil.AllFunctions(prog) { fnode := cg.CreateNode(f) for _, b := range f.Blocks { for _, instr := range b.Instrs { if site, ok := instr.(ssa.CallInstruction); ok { if g := site.Common().StaticCallee(); g != nil { gnode := cg.CreateNode(g) callgraph.AddEdge(fnode, site, gnode) } } } } } return cg }
// CallGraph computes the call graph of the specified program using the // Class Hierarchy Analysis algorithm. // func CallGraph(prog *ssa.Program) *callgraph.Graph { cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph allFuncs := ssautil.AllFunctions(prog) // funcsBySig contains all functions, keyed by signature. It is // the effective set of address-taken functions used to resolve // a dynamic call of a particular signature. var funcsBySig typeutil.Map // value is []*ssa.Function // methodsByName contains all methods, // grouped by name for efficient lookup. methodsByName := make(map[string][]*ssa.Function) // methodsMemo records, for every abstract method call call I.f on // interface type I, the set of concrete methods C.f of all // types C that satisfy interface I. methodsMemo := make(map[*types.Func][]*ssa.Function) lookupMethods := func(m *types.Func) []*ssa.Function { methods, ok := methodsMemo[m] if !ok { I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) for _, f := range methodsByName[m.Name()] { C := f.Signature.Recv().Type() // named or *named if types.Implements(C, I) { methods = append(methods, f) } } methodsMemo[m] = methods } return methods } for f := range allFuncs { if f.Signature.Recv() == nil { // Package initializers can never be address-taken. if f.Name() == "init" && f.Synthetic == "package initializer" { continue } funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) funcs = append(funcs, f) funcsBySig.Set(f.Signature, funcs) } else { methodsByName[f.Name()] = append(methodsByName[f.Name()], f) } } addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { gnode := cg.CreateNode(g) callgraph.AddEdge(fnode, site, gnode) } addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { // Because every call to a highly polymorphic and // frequently used abstract method such as // (io.Writer).Write is assumed to call every concrete // Write method in the program, the call graph can // contain a lot of duplication. // // TODO(adonovan): opt: consider factoring the callgraph // API so that the Callers component of each edge is a // slice of nodes, not a singleton. for _, g := range callees { addEdge(fnode, site, g) } } for f := range allFuncs { fnode := cg.CreateNode(f) for _, b := range f.Blocks { for _, instr := range b.Instrs { if site, ok := instr.(ssa.CallInstruction); ok { call := site.Common() if call.IsInvoke() { addEdges(fnode, site, lookupMethods(call.Method)) } else if g := call.StaticCallee(); g != nil { addEdge(fnode, site, g) } else if _, ok := call.Value.(*ssa.Builtin); !ok { callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) addEdges(fnode, site, callees) } } } } } return cg }