github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/godoc/analysis/callgraph14.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 analysis
     8  
     9  // This file computes the CALLERS and CALLEES relations from the call
    10  // graph.  CALLERS/CALLEES information is displayed in the lower pane
    11  // when a "func" token or ast.CallExpr.Lparen is clicked, respectively.
    12  
    13  import (
    14  	"fmt"
    15  	"go/ast"
    16  	"go/token"
    17  	"log"
    18  	"math/big"
    19  	"sort"
    20  
    21  	"golang.org/x/tools/go/callgraph"
    22  	"golang.org/x/tools/go/ssa"
    23  	"golang.org/x/tools/go/types"
    24  )
    25  
    26  // doCallgraph computes the CALLEES and CALLERS relations.
    27  func (a *analysis) doCallgraph(cg *callgraph.Graph) {
    28  	log.Print("Deleting synthetic nodes...")
    29  	// TODO(adonovan): opt: DeleteSyntheticNodes is asymptotically
    30  	// inefficient and can be (unpredictably) slow.
    31  	cg.DeleteSyntheticNodes()
    32  	log.Print("Synthetic nodes deleted")
    33  
    34  	// Populate nodes of package call graphs (PCGs).
    35  	for _, n := range cg.Nodes {
    36  		a.pcgAddNode(n.Func)
    37  	}
    38  	// Within each PCG, sort funcs by name.
    39  	for _, pcg := range a.pcgs {
    40  		pcg.sortNodes()
    41  	}
    42  
    43  	calledFuncs := make(map[ssa.CallInstruction]map[*ssa.Function]bool)
    44  	callingSites := make(map[*ssa.Function]map[ssa.CallInstruction]bool)
    45  	for _, n := range cg.Nodes {
    46  		for _, e := range n.Out {
    47  			if e.Site == nil {
    48  				continue // a call from a synthetic node such as <root>
    49  			}
    50  
    51  			// Add (site pos, callee) to calledFuncs.
    52  			// (Dynamic calls only.)
    53  			callee := e.Callee.Func
    54  
    55  			a.pcgAddEdge(n.Func, callee)
    56  
    57  			if callee.Synthetic != "" {
    58  				continue // call of a package initializer
    59  			}
    60  
    61  			if e.Site.Common().StaticCallee() == nil {
    62  				// dynamic call
    63  				// (CALLEES information for static calls
    64  				// is computed using SSA information.)
    65  				lparen := e.Site.Common().Pos()
    66  				if lparen != token.NoPos {
    67  					fns := calledFuncs[e.Site]
    68  					if fns == nil {
    69  						fns = make(map[*ssa.Function]bool)
    70  						calledFuncs[e.Site] = fns
    71  					}
    72  					fns[callee] = true
    73  				}
    74  			}
    75  
    76  			// Add (callee, site) to callingSites.
    77  			fns := callingSites[callee]
    78  			if fns == nil {
    79  				fns = make(map[ssa.CallInstruction]bool)
    80  				callingSites[callee] = fns
    81  			}
    82  			fns[e.Site] = true
    83  		}
    84  	}
    85  
    86  	// CALLEES.
    87  	log.Print("Callees...")
    88  	for site, fns := range calledFuncs {
    89  		var funcs funcsByPos
    90  		for fn := range fns {
    91  			funcs = append(funcs, fn)
    92  		}
    93  		sort.Sort(funcs)
    94  
    95  		a.addCallees(site, funcs)
    96  	}
    97  
    98  	// CALLERS
    99  	log.Print("Callers...")
   100  	for callee, sites := range callingSites {
   101  		pos := funcToken(callee)
   102  		if pos == token.NoPos {
   103  			log.Printf("CALLERS: skipping %s: no pos", callee)
   104  			continue
   105  		}
   106  
   107  		var this *types.Package // for relativizing names
   108  		if callee.Pkg != nil {
   109  			this = callee.Pkg.Pkg
   110  		}
   111  
   112  		// Compute sites grouped by parent, with text and URLs.
   113  		sitesByParent := make(map[*ssa.Function]sitesByPos)
   114  		for site := range sites {
   115  			fn := site.Parent()
   116  			sitesByParent[fn] = append(sitesByParent[fn], site)
   117  		}
   118  		var funcs funcsByPos
   119  		for fn := range sitesByParent {
   120  			funcs = append(funcs, fn)
   121  		}
   122  		sort.Sort(funcs)
   123  
   124  		v := callersJSON{
   125  			Callee:  callee.String(),
   126  			Callers: []callerJSON{}, // (JS wants non-nil)
   127  		}
   128  		for _, fn := range funcs {
   129  			caller := callerJSON{
   130  				Func:  prettyFunc(this, fn),
   131  				Sites: []anchorJSON{}, // (JS wants non-nil)
   132  			}
   133  			sites := sitesByParent[fn]
   134  			sort.Sort(sites)
   135  			for _, site := range sites {
   136  				pos := site.Common().Pos()
   137  				if pos != token.NoPos {
   138  					caller.Sites = append(caller.Sites, anchorJSON{
   139  						Text: fmt.Sprintf("%d", a.prog.Fset.Position(pos).Line),
   140  						Href: a.posURL(pos, len("(")),
   141  					})
   142  				}
   143  			}
   144  			v.Callers = append(v.Callers, caller)
   145  		}
   146  
   147  		fi, offset := a.fileAndOffset(pos)
   148  		fi.addLink(aLink{
   149  			start:   offset,
   150  			end:     offset + len("func"),
   151  			title:   fmt.Sprintf("%d callers", len(sites)),
   152  			onclick: fmt.Sprintf("onClickCallers(%d)", fi.addData(v)),
   153  		})
   154  	}
   155  
   156  	// PACKAGE CALLGRAPH
   157  	log.Print("Package call graph...")
   158  	for pkg, pcg := range a.pcgs {
   159  		// Maps (*ssa.Function).RelString() to index in JSON CALLGRAPH array.
   160  		index := make(map[string]int)
   161  
   162  		// Treat exported functions (and exported methods of
   163  		// exported named types) as roots even if they aren't
   164  		// actually called from outside the package.
   165  		for i, n := range pcg.nodes {
   166  			if i == 0 || n.fn.Object() == nil || !n.fn.Object().Exported() {
   167  				continue
   168  			}
   169  			recv := n.fn.Signature.Recv()
   170  			if recv == nil || deref(recv.Type()).(*types.Named).Obj().Exported() {
   171  				roots := &pcg.nodes[0].edges
   172  				roots.SetBit(roots, i, 1)
   173  			}
   174  			index[n.fn.RelString(pkg.Pkg)] = i
   175  		}
   176  
   177  		json := a.pcgJSON(pcg)
   178  
   179  		// TODO(adonovan): pkg.Path() is not unique!
   180  		// It is possible to declare a non-test package called x_test.
   181  		a.result.pkgInfo(pkg.Pkg.Path()).setCallGraph(json, index)
   182  	}
   183  }
   184  
   185  // addCallees adds client data and links for the facts that site calls fns.
   186  func (a *analysis) addCallees(site ssa.CallInstruction, fns []*ssa.Function) {
   187  	v := calleesJSON{
   188  		Descr:   site.Common().Description(),
   189  		Callees: []anchorJSON{}, // (JS wants non-nil)
   190  	}
   191  	var this *types.Package // for relativizing names
   192  	if p := site.Parent().Package(); p != nil {
   193  		this = p.Pkg
   194  	}
   195  
   196  	for _, fn := range fns {
   197  		v.Callees = append(v.Callees, anchorJSON{
   198  			Text: prettyFunc(this, fn),
   199  			Href: a.posURL(funcToken(fn), len("func")),
   200  		})
   201  	}
   202  
   203  	fi, offset := a.fileAndOffset(site.Common().Pos())
   204  	fi.addLink(aLink{
   205  		start:   offset,
   206  		end:     offset + len("("),
   207  		title:   fmt.Sprintf("%d callees", len(v.Callees)),
   208  		onclick: fmt.Sprintf("onClickCallees(%d)", fi.addData(v)),
   209  	})
   210  }
   211  
   212  // -- utilities --------------------------------------------------------
   213  
   214  // stable order within packages but undefined across packages.
   215  type funcsByPos []*ssa.Function
   216  
   217  func (a funcsByPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
   218  func (a funcsByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   219  func (a funcsByPos) Len() int           { return len(a) }
   220  
   221  type sitesByPos []ssa.CallInstruction
   222  
   223  func (a sitesByPos) Less(i, j int) bool { return a[i].Common().Pos() < a[j].Common().Pos() }
   224  func (a sitesByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   225  func (a sitesByPos) Len() int           { return len(a) }
   226  
   227  func funcToken(fn *ssa.Function) token.Pos {
   228  	switch syntax := fn.Syntax().(type) {
   229  	case *ast.FuncLit:
   230  		return syntax.Type.Func
   231  	case *ast.FuncDecl:
   232  		return syntax.Type.Func
   233  	}
   234  	return token.NoPos
   235  }
   236  
   237  // prettyFunc pretty-prints fn for the user interface.
   238  // TODO(adonovan): return HTML so we have more markup freedom.
   239  func prettyFunc(this *types.Package, fn *ssa.Function) string {
   240  	if fn.Parent() != nil {
   241  		return fmt.Sprintf("%s in %s",
   242  			types.TypeString(fn.Signature, types.RelativeTo(this)),
   243  			prettyFunc(this, fn.Parent()))
   244  	}
   245  	if fn.Synthetic != "" && fn.Name() == "init" {
   246  		// (This is the actual initializer, not a declared 'func init').
   247  		if fn.Pkg.Pkg == this {
   248  			return "package initializer"
   249  		}
   250  		return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
   251  	}
   252  	return fn.RelString(this)
   253  }
   254  
   255  // -- intra-package callgraph ------------------------------------------
   256  
   257  // pcgNode represents a node in the package call graph (PCG).
   258  type pcgNode struct {
   259  	fn     *ssa.Function
   260  	pretty string  // cache of prettyFunc(fn)
   261  	edges  big.Int // set of callee func indices
   262  }
   263  
   264  // A packageCallGraph represents the intra-package edges of the global call graph.
   265  // The zeroth node indicates "all external functions".
   266  type packageCallGraph struct {
   267  	nodeIndex map[*ssa.Function]int // maps func to node index (a small int)
   268  	nodes     []*pcgNode            // maps node index to node
   269  }
   270  
   271  // sortNodes populates pcg.nodes in name order and updates the nodeIndex.
   272  func (pcg *packageCallGraph) sortNodes() {
   273  	nodes := make([]*pcgNode, 0, len(pcg.nodeIndex))
   274  	nodes = append(nodes, &pcgNode{fn: nil, pretty: "<external>"})
   275  	for fn := range pcg.nodeIndex {
   276  		nodes = append(nodes, &pcgNode{
   277  			fn:     fn,
   278  			pretty: prettyFunc(fn.Pkg.Pkg, fn),
   279  		})
   280  	}
   281  	sort.Sort(pcgNodesByPretty(nodes[1:]))
   282  	for i, n := range nodes {
   283  		pcg.nodeIndex[n.fn] = i
   284  	}
   285  	pcg.nodes = nodes
   286  }
   287  
   288  func (pcg *packageCallGraph) addEdge(caller, callee *ssa.Function) {
   289  	var callerIndex int
   290  	if caller.Pkg == callee.Pkg {
   291  		// intra-package edge
   292  		callerIndex = pcg.nodeIndex[caller]
   293  		if callerIndex < 1 {
   294  			panic(caller)
   295  		}
   296  	}
   297  	edges := &pcg.nodes[callerIndex].edges
   298  	edges.SetBit(edges, pcg.nodeIndex[callee], 1)
   299  }
   300  
   301  func (a *analysis) pcgAddNode(fn *ssa.Function) {
   302  	if fn.Pkg == nil {
   303  		return
   304  	}
   305  	pcg, ok := a.pcgs[fn.Pkg]
   306  	if !ok {
   307  		pcg = &packageCallGraph{nodeIndex: make(map[*ssa.Function]int)}
   308  		a.pcgs[fn.Pkg] = pcg
   309  	}
   310  	pcg.nodeIndex[fn] = -1
   311  }
   312  
   313  func (a *analysis) pcgAddEdge(caller, callee *ssa.Function) {
   314  	if callee.Pkg != nil {
   315  		a.pcgs[callee.Pkg].addEdge(caller, callee)
   316  	}
   317  }
   318  
   319  // pcgJSON returns a new slice of callgraph JSON values.
   320  func (a *analysis) pcgJSON(pcg *packageCallGraph) []*PCGNodeJSON {
   321  	var nodes []*PCGNodeJSON
   322  	for _, n := range pcg.nodes {
   323  
   324  		// TODO(adonovan): why is there no good way to iterate
   325  		// over the set bits of a big.Int?
   326  		var callees []int
   327  		nbits := n.edges.BitLen()
   328  		for j := 0; j < nbits; j++ {
   329  			if n.edges.Bit(j) == 1 {
   330  				callees = append(callees, j)
   331  			}
   332  		}
   333  
   334  		var pos token.Pos
   335  		if n.fn != nil {
   336  			pos = funcToken(n.fn)
   337  		}
   338  		nodes = append(nodes, &PCGNodeJSON{
   339  			Func: anchorJSON{
   340  				Text: n.pretty,
   341  				Href: a.posURL(pos, len("func")),
   342  			},
   343  			Callees: callees,
   344  		})
   345  	}
   346  	return nodes
   347  }
   348  
   349  type pcgNodesByPretty []*pcgNode
   350  
   351  func (a pcgNodesByPretty) Less(i, j int) bool { return a[i].pretty < a[j].pretty }
   352  func (a pcgNodesByPretty) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   353  func (a pcgNodesByPretty) Len() int           { return len(a) }