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  }