GoPLS Viewer

Home|gopls/go/callgraph/util.go
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
5package callgraph
6
7import "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.
14func 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.
26func GraphVisitEdges(g *Graphedge func(*Edgeerrorerror {
27    seen := make(map[*Node]bool)
28    var visit func(n *Nodeerror
29    visit = func(n *Nodeerror {
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.
55func PathSearch(start *NodeisEnd func(*Nodebool) []*Edge {
56    stack := make([]*Edge032)
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(stacke// 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".
82func (g *GraphDeleteSyntheticNodes() {
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 fncgn := 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.CallereIn.SiteeOut.Callee}
108                if edges[newEdge] {
109                    continue // don't add duplicate
110                }
111                AddEdge(eIn.CallereIn.SiteeOut.Callee)
112                edges[newEdge] = true
113            }
114        }
115        g.DeleteNode(cgn)
116    }
117}
118
119func isInit(fn *ssa.Functionbool {
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.)
125func (g *GraphDeleteNode(n *Node) {
126    n.deleteIns()
127    n.deleteOuts()
128    delete(g.Nodesn.Func)
129}
130
131// deleteIns deletes all incoming edges to n.
132func (n *NodedeleteIns() {
133    for _e := range n.In {
134        removeOutEdge(e)
135    }
136    n.In = nil
137}
138
139// deleteOuts deletes all outgoing edges from n.
140func (n *NodedeleteOuts() {
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'.
148func removeOutEdge(edge *Edge) {
149    caller := edge.Caller
150    n := len(caller.Out)
151    for ie := 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'.
164func removeInEdge(edge *Edge) {
165    caller := edge.Callee
166    n := len(caller.In)
167    for ie := 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
MembersX
Node.deleteOuts.RangeStmt_3825.e
removeOutEdge.RangeStmt_4028.i
Graph.DeleteSyntheticNodes.RangeStmt_2814.BlockStmt.RangeStmt_2846.e
Graph.DeleteSyntheticNodes.RangeStmt_2902.BlockStmt.RangeStmt_3022.BlockStmt.RangeStmt_3054.BlockStmt.newEdge
Graph.DeleteNode
Node.deleteIns.n
Node.deleteIns
Node.deleteIns.RangeStmt_3683.e
removeOutEdge.edge
GraphVisitEdges
GraphVisitEdges.BlockStmt.BlockStmt.RangeStmt_969.e
Graph.DeleteSyntheticNodes.g
Graph.DeleteSyntheticNodes
Graph.DeleteSyntheticNodes.RangeStmt_2902.BlockStmt.RangeStmt_3022.BlockStmt.RangeStmt_3054.eOut
Graph.DeleteNode.n
removeInEdge
CalleesOf.caller
GraphVisitEdges.BlockStmt.BlockStmt.RangeStmt_969.BlockStmt.err
PathSearch.isEnd
PathSearch.BlockStmt.BlockStmt.RangeStmt_1736.BlockStmt.found
Graph.DeleteSyntheticNodes.RangeStmt_2902.fn
Node.deleteOuts.n
removeOutEdge
CalleesOf.callees
GraphVisitEdges.RangeStmt_1145.BlockStmt.err
PathSearch.start
PathSearch.stack
Graph.DeleteSyntheticNodes.RangeStmt_2814.cgn
Node.deleteOuts
GraphVisitEdges.edge
Graph.DeleteSyntheticNodes.RangeStmt_2902.BlockStmt.RangeStmt_3022.eIn
removeInEdge.caller
removeInEdge.n
removeOutEdge.RangeStmt_4028.e
removeInEdge.edge
CalleesOf.RangeStmt_478.e
GraphVisitEdges.seen
PathSearch.BlockStmt.BlockStmt.RangeStmt_1736.e
Graph.DeleteSyntheticNodes.RangeStmt_2902.cgn
isInit
removeOutEdge.n
removeInEdge.RangeStmt_4441.e
GraphVisitEdges.g
PathSearch
isInit.fn
Graph.DeleteNode.g
removeInEdge.RangeStmt_4441.i
CalleesOf
GraphVisitEdges.RangeStmt_1145.n
PathSearch.seen
Graph.DeleteSyntheticNodes.edges
removeOutEdge.caller
Members
X