...

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  // functions that do not correspond to source syntax. For historical
    80  // reasons, nodes for g.Root and package initializers are always
    81  // kept.
    82  //
    83  // As nodes are removed, edges are created to preserve the
    84  // reachability relation of the remaining nodes.
    85  func (g *Graph) DeleteSyntheticNodes() {
    86  	// Measurements on the standard library and go.tools show that
    87  	// resulting graph has ~15% fewer nodes and 4-8% fewer edges
    88  	// than the input.
    89  	//
    90  	// Inlining a wrapper of in-degree m, out-degree n adds m*n
    91  	// and removes m+n edges.  Since most wrappers are monomorphic
    92  	// (n=1) this results in a slight reduction.  Polymorphic
    93  	// wrappers (n>1), e.g. from embedding an interface value
    94  	// inside a struct to satisfy some interface, cause an
    95  	// increase in the graph, but they seem to be uncommon.
    96  
    97  	// Hash all existing edges to avoid creating duplicates.
    98  	edges := make(map[Edge]bool)
    99  	for _, cgn := range g.Nodes {
   100  		for _, e := range cgn.Out {
   101  			edges[*e] = true
   102  		}
   103  	}
   104  	for fn, cgn := range g.Nodes {
   105  		if cgn == g.Root || isInit(cgn.Func) || fn.Syntax() != nil {
   106  			continue // keep
   107  		}
   108  		for _, eIn := range cgn.In {
   109  			for _, eOut := range cgn.Out {
   110  				newEdge := Edge{eIn.Caller, eIn.Site, eOut.Callee}
   111  				if edges[newEdge] {
   112  					continue // don't add duplicate
   113  				}
   114  				AddEdge(eIn.Caller, eIn.Site, eOut.Callee)
   115  				edges[newEdge] = true
   116  			}
   117  		}
   118  		g.DeleteNode(cgn)
   119  	}
   120  }
   121  
   122  func isInit(fn *ssa.Function) bool {
   123  	return fn.Pkg != nil && fn.Pkg.Func("init") == fn
   124  }
   125  
   126  // DeleteNode removes node n and its edges from the graph g.
   127  // (NB: not efficient for batch deletion.)
   128  func (g *Graph) DeleteNode(n *Node) {
   129  	n.deleteIns()
   130  	n.deleteOuts()
   131  	delete(g.Nodes, n.Func)
   132  }
   133  
   134  // deleteIns deletes all incoming edges to n.
   135  func (n *Node) deleteIns() {
   136  	for _, e := range n.In {
   137  		removeOutEdge(e)
   138  	}
   139  	n.In = nil
   140  }
   141  
   142  // deleteOuts deletes all outgoing edges from n.
   143  func (n *Node) deleteOuts() {
   144  	for _, e := range n.Out {
   145  		removeInEdge(e)
   146  	}
   147  	n.Out = nil
   148  }
   149  
   150  // removeOutEdge removes edge.Caller's outgoing edge 'edge'.
   151  func removeOutEdge(edge *Edge) {
   152  	caller := edge.Caller
   153  	n := len(caller.Out)
   154  	for i, e := range caller.Out {
   155  		if e == edge {
   156  			// Replace it with the final element and shrink the slice.
   157  			caller.Out[i] = caller.Out[n-1]
   158  			caller.Out[n-1] = nil // aid GC
   159  			caller.Out = caller.Out[:n-1]
   160  			return
   161  		}
   162  	}
   163  	panic("edge not found: " + edge.String())
   164  }
   165  
   166  // removeInEdge removes edge.Callee's incoming edge 'edge'.
   167  func removeInEdge(edge *Edge) {
   168  	caller := edge.Callee
   169  	n := len(caller.In)
   170  	for i, e := range caller.In {
   171  		if e == edge {
   172  			// Replace it with the final element and shrink the slice.
   173  			caller.In[i] = caller.In[n-1]
   174  			caller.In[n-1] = nil // aid GC
   175  			caller.In = caller.In[:n-1]
   176  			return
   177  		}
   178  	}
   179  	panic("edge not found: " + edge.String())
   180  }
   181  

View as plain text