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