GoPLS Viewer

Home|gopls/go/callgraph/cha/cha.go
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// Package cha computes the call graph of a Go program using the Class
6// Hierarchy Analysis (CHA) algorithm.
7//
8// CHA was first described in "Optimization of Object-Oriented Programs
9// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove,
10// and Craig Chambers, ECOOP'95.
11//
12// CHA is related to RTA (see go/callgraph/rta); the difference is that
13// CHA conservatively computes the entire "implements" relation between
14// interfaces and concrete types ahead of time, whereas RTA uses dynamic
15// programming to construct it on the fly as it encounters new functions
16// reachable from main.  CHA may thus include spurious call edges for
17// types that haven't been instantiated yet, or types that are never
18// instantiated.
19//
20// Since CHA conservatively assumes that all functions are address-taken
21// and all concrete types are put into interfaces, it is sound to run on
22// partial programs, such as libraries without a main or test function.
23package cha // import "golang.org/x/tools/go/callgraph/cha"
24
25// TODO(zpavlinovic): update CHA for how it handles generic function bodies.
26
27import (
28    "go/types"
29
30    "golang.org/x/tools/go/callgraph"
31    "golang.org/x/tools/go/ssa"
32    "golang.org/x/tools/go/ssa/ssautil"
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.
38func CallGraph(prog *ssa.Program) *callgraph.Graph {
39    cg := callgraph.New(nil// TODO(adonovan) eliminate concept of rooted callgraph
40
41    allFuncs := ssautil.AllFunctions(prog)
42
43    // funcsBySig contains all functions, keyed by signature.  It is
44    // the effective set of address-taken functions used to resolve
45    // a dynamic call of a particular signature.
46    var funcsBySig typeutil.Map // value is []*ssa.Function
47
48    // methodsByName contains all methods,
49    // grouped by name for efficient lookup.
50    // (methodsById would be better but not every SSA method has a go/types ID.)
51    methodsByName := make(map[string][]*ssa.Function)
52
53    // An imethod represents an interface method I.m.
54    // (There's no go/types object for it;
55    // a *types.Func may be shared by many interfaces due to interface embedding.)
56    type imethod struct {
57        I  *types.Interface
58        id string
59    }
60    // methodsMemo records, for every abstract method call I.m on
61    // interface type I, the set of concrete methods C.m of all
62    // types C that satisfy interface I.
63    //
64    // Abstract methods may be shared by several interfaces,
65    // hence we must pass I explicitly, not guess from m.
66    //
67    // methodsMemo is just a cache, so it needn't be a typeutil.Map.
68    methodsMemo := make(map[imethod][]*ssa.Function)
69    lookupMethods := func(I *types.Interfacem *types.Func) []*ssa.Function {
70        id := m.Id()
71        methodsok := methodsMemo[imethod{Iid}]
72        if !ok {
73            for _f := range methodsByName[m.Name()] {
74                C := f.Signature.Recv().Type() // named or *named
75                if types.Implements(CI) {
76                    methods = append(methodsf)
77                }
78            }
79            methodsMemo[imethod{Iid}] = methods
80        }
81        return methods
82    }
83
84    for f := range allFuncs {
85        if f.Signature.Recv() == nil {
86            // Package initializers can never be address-taken.
87            if f.Name() == "init" && f.Synthetic == "package initializer" {
88                continue
89            }
90            funcs_ := funcsBySig.At(f.Signature).([]*ssa.Function)
91            funcs = append(funcsf)
92            funcsBySig.Set(f.Signaturefuncs)
93        } else {
94            methodsByName[f.Name()] = append(methodsByName[f.Name()], f)
95        }
96    }
97
98    addEdge := func(fnode *callgraph.Nodesite ssa.CallInstructiong *ssa.Function) {
99        gnode := cg.CreateNode(g)
100        callgraph.AddEdge(fnodesitegnode)
101    }
102
103    addEdges := func(fnode *callgraph.Nodesite ssa.CallInstructioncallees []*ssa.Function) {
104        // Because every call to a highly polymorphic and
105        // frequently used abstract method such as
106        // (io.Writer).Write is assumed to call every concrete
107        // Write method in the program, the call graph can
108        // contain a lot of duplication.
109        //
110        // TODO(adonovan): opt: consider factoring the callgraph
111        // API so that the Callers component of each edge is a
112        // slice of nodes, not a singleton.
113        for _g := range callees {
114            addEdge(fnodesiteg)
115        }
116    }
117
118    for f := range allFuncs {
119        fnode := cg.CreateNode(f)
120        for _b := range f.Blocks {
121            for _instr := range b.Instrs {
122                if siteok := instr.(ssa.CallInstruction); ok {
123                    call := site.Common()
124                    if call.IsInvoke() {
125                        tiface := call.Value.Type().Underlying().(*types.Interface)
126                        addEdges(fnodesitelookupMethods(tifacecall.Method))
127                    } else if g := call.StaticCallee(); g != nil {
128                        addEdge(fnodesiteg)
129                    } else if _ok := call.Value.(*ssa.Builtin); !ok {
130                        callees_ := funcsBySig.At(call.Signature()).([]*ssa.Function)
131                        addEdges(fnodesitecallees)
132                    }
133                }
134            }
135        }
136    }
137
138    return cg
139}
140
MembersX
CallGraph.imethod.id
CallGraph.RangeStmt_4307.BlockStmt.RangeStmt_4363.BlockStmt.RangeStmt_4395.BlockStmt.BlockStmt.call
CallGraph.RangeStmt_4307.BlockStmt.RangeStmt_4363.BlockStmt.RangeStmt_4395.BlockStmt.BlockStmt.g
CallGraph.RangeStmt_4307.f
CallGraph.cg
CallGraph.allFuncs
CallGraph.methodsByName
CallGraph.imethod
CallGraph.BlockStmt.id
types
CallGraph
CallGraph.imethod.I
ssautil
CallGraph.prog
CallGraph.funcsBySig
CallGraph.BlockStmt.BlockStmt.RangeStmt_2939.BlockStmt.C
CallGraph.BlockStmt.gnode
typeutil
CallGraph.BlockStmt.BlockStmt.RangeStmt_2939.f
CallGraph.RangeStmt_3181.f
CallGraph.RangeStmt_4307.BlockStmt.RangeStmt_4363.BlockStmt.RangeStmt_4395.instr
callgraph
ssa
CallGraph.RangeStmt_4307.BlockStmt.RangeStmt_4363.b
CallGraph.methodsMemo
CallGraph.BlockStmt.RangeStmt_4243.g
CallGraph.RangeStmt_4307.BlockStmt.fnode
Members
X