github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/callgraph/vta/utils.go (about) 1 // Copyright 2021 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 vta 6 7 import ( 8 "go/types" 9 10 "github.com/powerman/golang-tools/go/callgraph" 11 "github.com/powerman/golang-tools/go/ssa" 12 ) 13 14 func canAlias(n1, n2 node) bool { 15 return isReferenceNode(n1) && isReferenceNode(n2) 16 } 17 18 func isReferenceNode(n node) bool { 19 if _, ok := n.(nestedPtrInterface); ok { 20 return true 21 } 22 if _, ok := n.(nestedPtrFunction); ok { 23 return true 24 } 25 26 if _, ok := n.Type().(*types.Pointer); ok { 27 return true 28 } 29 30 return false 31 } 32 33 // hasInFlow checks if a concrete type can flow to node `n`. 34 // Returns yes iff the type of `n` satisfies one the following: 35 // 1) is an interface 36 // 2) is a (nested) pointer to interface (needed for, say, 37 // slice elements of nested pointers to interface type) 38 // 3) is a function type (needed for higher-order type flow) 39 // 4) is a (nested) pointer to function (needed for, say, 40 // slice elements of nested pointers to function type) 41 // 5) is a global Recover or Panic node 42 func hasInFlow(n node) bool { 43 if _, ok := n.(panicArg); ok { 44 return true 45 } 46 if _, ok := n.(recoverReturn); ok { 47 return true 48 } 49 50 t := n.Type() 51 52 if i := interfaceUnderPtr(t); i != nil { 53 return true 54 } 55 if f := functionUnderPtr(t); f != nil { 56 return true 57 } 58 59 return isInterface(t) || isFunction(t) 60 } 61 62 // hasInitialTypes check if a node can have initial types. 63 // Returns true iff `n` is not a panic or recover node as 64 // those are artificial. 65 func hasInitialTypes(n node) bool { 66 switch n.(type) { 67 case panicArg, recoverReturn: 68 return false 69 default: 70 return true 71 } 72 } 73 74 func isInterface(t types.Type) bool { 75 _, ok := t.Underlying().(*types.Interface) 76 return ok 77 } 78 79 func isFunction(t types.Type) bool { 80 _, ok := t.Underlying().(*types.Signature) 81 return ok 82 } 83 84 // interfaceUnderPtr checks if type `t` is a potentially nested 85 // pointer to interface and if yes, returns the interface type. 86 // Otherwise, returns nil. 87 func interfaceUnderPtr(t types.Type) types.Type { 88 p, ok := t.Underlying().(*types.Pointer) 89 if !ok { 90 return nil 91 } 92 93 if isInterface(p.Elem()) { 94 return p.Elem() 95 } 96 97 return interfaceUnderPtr(p.Elem()) 98 } 99 100 // functionUnderPtr checks if type `t` is a potentially nested 101 // pointer to function type and if yes, returns the function type. 102 // Otherwise, returns nil. 103 func functionUnderPtr(t types.Type) types.Type { 104 p, ok := t.Underlying().(*types.Pointer) 105 if !ok { 106 return nil 107 } 108 109 if isFunction(p.Elem()) { 110 return p.Elem() 111 } 112 113 return functionUnderPtr(p.Elem()) 114 } 115 116 // sliceArrayElem returns the element type of type `t` that is 117 // expected to be a (pointer to) array or slice, consistent with 118 // the ssa.Index and ssa.IndexAddr instructions. Panics otherwise. 119 func sliceArrayElem(t types.Type) types.Type { 120 u := t.Underlying() 121 122 if p, ok := u.(*types.Pointer); ok { 123 u = p.Elem().Underlying() 124 } 125 126 if a, ok := u.(*types.Array); ok { 127 return a.Elem() 128 } 129 return u.(*types.Slice).Elem() 130 } 131 132 // siteCallees computes a set of callees for call site `c` given program `callgraph`. 133 func siteCallees(c ssa.CallInstruction, callgraph *callgraph.Graph) []*ssa.Function { 134 var matches []*ssa.Function 135 136 node := callgraph.Nodes[c.Parent()] 137 if node == nil { 138 return nil 139 } 140 141 for _, edge := range node.Out { 142 if edge.Site == c { 143 matches = append(matches, edge.Callee.Func) 144 } 145 } 146 return matches 147 } 148 149 func canHaveMethods(t types.Type) bool { 150 if _, ok := t.(*types.Named); ok { 151 return true 152 } 153 154 u := t.Underlying() 155 switch u.(type) { 156 case *types.Interface, *types.Signature, *types.Struct: 157 return true 158 default: 159 return false 160 } 161 } 162 163 // calls returns the set of call instructions in `f`. 164 func calls(f *ssa.Function) []ssa.CallInstruction { 165 var calls []ssa.CallInstruction 166 for _, bl := range f.Blocks { 167 for _, instr := range bl.Instrs { 168 if c, ok := instr.(ssa.CallInstruction); ok { 169 calls = append(calls, c) 170 } 171 } 172 } 173 return calls 174 } 175 176 // intersect produces an intersection of functions in `fs1` and `fs2`. 177 func intersect(fs1, fs2 []*ssa.Function) []*ssa.Function { 178 m := make(map[*ssa.Function]bool) 179 for _, f := range fs1 { 180 m[f] = true 181 } 182 183 var res []*ssa.Function 184 for _, f := range fs2 { 185 if m[f] { 186 res = append(res, f) 187 } 188 } 189 return res 190 }