...

Source file src/golang.org/x/tools/go/callgraph/util.go

Documentation: golang.org/x/tools/go/callgraph

     1  // Copyright 2013 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 callgraph
     6  
     7  import "golang.org/x/tools/go/ssa"
     8  
     9  // This file provides various utilities over call graphs, such as
    10  // visitation and path search.
    11  
    12  // CalleesOf returns a new set containing all direct callees of the
    13  // caller node.
    14  func CalleesOf(caller *Node) map[*Node]bool {
    15  	callees := make(map[*Node]bool)
    16  	for _, e := range caller.Out {
    17  		callees[e.Callee] = true
    18  	}
    19  	return callees
    20  }
    21  
    22  // GraphVisitEdges visits all the edges in graph g in depth-first order.
    23  // The edge function is called for each edge in postorder.  If it
    24  // returns non-nil, visitation stops and GraphVisitEdges returns that
    25  // value.
    26  func GraphVisitEdges(g *Graph, edge func(*Edge) error) error {
    27  	seen := make(map[*Node]bool)
    28  	var visit func(n *Node) error
    29  	visit = func(n *Node) error {
    30  		if !seen[n] {
    31  			seen[n] = true
    32  			for _, e := range n.Out {
    33  				if err := visit(e.Callee); err != nil {
    34  					return err
    35  				}
    36  				if err := edge(e); err != nil {
    37  					return err
    38  				}
    39  			}
    40  		}
    41  		return nil
    42  	}
    43  	for _, n := range g.Nodes {
    44  		if err := visit(n); err != nil {
    45  			return err
    46  		}
    47  	}
    48  	return nil
    49  }
    50  
    51  // PathSearch finds an arbitrary path starting at node start and
    52  // ending at some node for which isEnd() returns true.  On success,
    53  // PathSearch returns the path as an ordered list of edges; on
    54  // failure, it returns nil.
    55  func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge {
    56  	stack := make([]*Edge, 0, 32)
    57  	seen := make(map[*Node]bool)
    58  	var search func(n *Node) []*Edge
    59  	search = func(n *Node) []*Edge {
    60  		if !seen[n] {
    61  			seen[n] = true
    62  			if isEnd(n) {
    63  				return stack
    64  			}
    65  			for _, e := range n.Out {
    66  				stack = append(stack, e) // push
    67  				if found := search(e.Callee); found != nil {
    68  					return found
    69  				}
    70  				stack = stack[:len(stack)-1] // pop
    71  			}
    72  		}
    73  		return nil
    74  	}
    75  	return search(start)
    76  }
    77  
    78  // DeleteSyntheticNodes removes from call graph g all nodes for
    79  // synthetic functions (except g.Root and package initializers),
    80  // preserving the topology.  In effect, calls to synthetic wrappers
    81  // are "inlined".
    82  func (g *Graph) DeleteSyntheticNodes() {
    83  	// Measurements on the standard library and go.tools show that
    84  	// resulting graph has ~15% fewer nodes and 4-8% fewer edges
    85  	// than the input.
    86  	//
    87  	// Inlining a wrapper of in-degree m, out-degree n adds m*n
    88  	// and removes m+n edges.  Since most wrappers are monomorphic
    89  	// (n=1) this results in a slight reduction.  Polymorphic
    90  	// wrappers (n>1), e.g. from embedding an interface value
    91  	// inside a struct to satisfy some interface, cause an
    92  	// increase in the graph, but they seem to be uncommon.
    93  
    94  	// Hash all existing edges to avoid creating duplicates.
    95  	edges := make(map[Edge]bool)
    96  	for _, cgn := range g.Nodes {
    97  		for _, e := range cgn.Out {
    98  			edges[*e] = true
    99  		}
   100  	}
   101  	for fn, cgn := range g.Nodes {
   102  		if cgn == g.Root || fn.Synthetic == "" || isInit(cgn.Func) {
   103  			continue // keep
   104  		}
   105  		for _, eIn := range cgn.In {
   106  			for _, eOut := range cgn.Out {
   107  				newEdge := Edge{eIn.Caller, eIn.Site, eOut.Callee}
   108  				if edges[newEdge] {
   109  					continue // don't add duplicate
   110  				}
   111  				AddEdge(eIn.Caller, eIn.Site, eOut.Callee)
   112  				edges[newEdge] = true
   113  			}
   114  		}
   115  		g.DeleteNode(cgn)
   116  	}
   117  }
   118  
   119  func isInit(fn *ssa.Function) bool {
   120  	return fn.Pkg != nil && fn.Pkg.Func("init") == fn
   121  }
   122  
   123  // DeleteNode removes node n and its edges from the graph g.
   124  // (NB: not efficient for batch deletion.)
   125  func (g *Graph) DeleteNode(n *Node) {
   126  	n.deleteIns()
   127  	n.deleteOuts()
   128  	delete(g.Nodes, n.Func)
   129  }
   130  
   131  // deleteIns deletes all incoming edges to n.
   132  func (n *Node) deleteIns() {
   133  	for _, e := range n.In {
   134  		removeOutEdge(e)
   135  	}
   136  	n.In = nil
   137  }
   138  
   139  // deleteOuts deletes all outgoing edges from n.
   140  func (n *Node) deleteOuts() {
   141  	for _, e := range n.Out {
   142  		removeInEdge(e)
   143  	}
   144  	n.Out = nil
   145  }
   146  
   147  // removeOutEdge removes edge.Caller's outgoing edge 'edge'.
   148  func removeOutEdge(edge *Edge) {
   149  	caller := edge.Caller
   150  	n := len(caller.Out)
   151  	for i, e := range caller.Out {
   152  		if e == edge {
   153  			// Replace it with the final element and shrink the slice.
   154  			caller.Out[i] = caller.Out[n-1]
   155  			caller.Out[n-1] = nil // aid GC
   156  			caller.Out = caller.Out[:n-1]
   157  			return
   158  		}
   159  	}
   160  	panic("edge not found: " + edge.String())
   161  }
   162  
   163  // removeInEdge removes edge.Callee's incoming edge 'edge'.
   164  func removeInEdge(edge *Edge) {
   165  	caller := edge.Callee
   166  	n := len(caller.In)
   167  	for i, e := range caller.In {
   168  		if e == edge {
   169  			// Replace it with the final element and shrink the slice.
   170  			caller.In[i] = caller.In[n-1]
   171  			caller.In[n-1] = nil // aid GC
   172  			caller.In = caller.In[:n-1]
   173  			return
   174  		}
   175  	}
   176  	panic("edge not found: " + edge.String())
   177  }
   178  

View as plain text