golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/ssa/methods.go (about) 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 ssa 6 7 // This file defines utilities for population of method sets. 8 9 import ( 10 "fmt" 11 "go/types" 12 13 "golang.org/x/tools/go/types/typeutil" 14 "golang.org/x/tools/internal/aliases" 15 ) 16 17 // MethodValue returns the Function implementing method sel, building 18 // wrapper methods on demand. It returns nil if sel denotes an 19 // interface or generic method. 20 // 21 // Precondition: sel.Kind() == MethodVal. 22 // 23 // Thread-safe. 24 // 25 // Acquires prog.methodsMu. 26 func (prog *Program) MethodValue(sel *types.Selection) *Function { 27 if sel.Kind() != types.MethodVal { 28 panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel)) 29 } 30 T := sel.Recv() 31 if types.IsInterface(T) { 32 return nil // interface method or type parameter 33 } 34 35 if prog.isParameterized(T) { 36 return nil // generic method 37 } 38 39 if prog.mode&LogSource != 0 { 40 defer logStack("MethodValue %s %v", T, sel)() 41 } 42 43 var cr creator 44 45 m := func() *Function { 46 prog.methodsMu.Lock() 47 defer prog.methodsMu.Unlock() 48 49 // Get or create SSA method set. 50 mset, ok := prog.methodSets.At(T).(*methodSet) 51 if !ok { 52 mset = &methodSet{mapping: make(map[string]*Function)} 53 prog.methodSets.Set(T, mset) 54 } 55 56 // Get or create SSA method. 57 id := sel.Obj().Id() 58 fn, ok := mset.mapping[id] 59 if !ok { 60 obj := sel.Obj().(*types.Func) 61 needsPromotion := len(sel.Index()) > 1 62 needsIndirection := !isPointer(recvType(obj)) && isPointer(T) 63 if needsPromotion || needsIndirection { 64 fn = createWrapper(prog, toSelection(sel), &cr) 65 } else { 66 fn = prog.objectMethod(obj, &cr) 67 } 68 if fn.Signature.Recv() == nil { 69 panic(fn) 70 } 71 mset.mapping[id] = fn 72 } 73 74 return fn 75 }() 76 77 b := builder{created: &cr} 78 b.iterate() 79 80 return m 81 } 82 83 // objectMethod returns the Function for a given method symbol. 84 // The symbol may be an instance of a generic function. It need not 85 // belong to an existing SSA package created by a call to 86 // prog.CreatePackage. 87 // 88 // objectMethod panics if the function is not a method. 89 // 90 // Acquires prog.objectMethodsMu. 91 func (prog *Program) objectMethod(obj *types.Func, cr *creator) *Function { 92 sig := obj.Type().(*types.Signature) 93 if sig.Recv() == nil { 94 panic("not a method: " + obj.String()) 95 } 96 97 // Belongs to a created package? 98 if fn := prog.FuncValue(obj); fn != nil { 99 return fn 100 } 101 102 // Instantiation of generic? 103 if originObj := obj.Origin(); originObj != obj { 104 origin := prog.objectMethod(originObj, cr) 105 assert(origin.typeparams.Len() > 0, "origin is not generic") 106 targs := receiverTypeArgs(obj) 107 return origin.instance(targs, cr) 108 } 109 110 // Consult/update cache of methods created from types.Func. 111 prog.objectMethodsMu.Lock() 112 defer prog.objectMethodsMu.Unlock() 113 fn, ok := prog.objectMethods[obj] 114 if !ok { 115 fn = createFunction(prog, obj, obj.Name(), nil, nil, "", cr) 116 fn.Synthetic = "from type information (on demand)" 117 118 if prog.objectMethods == nil { 119 prog.objectMethods = make(map[*types.Func]*Function) 120 } 121 prog.objectMethods[obj] = fn 122 } 123 return fn 124 } 125 126 // LookupMethod returns the implementation of the method of type T 127 // identified by (pkg, name). It returns nil if the method exists but 128 // is an interface method or generic method, and panics if T has no such method. 129 func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function { 130 sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name) 131 if sel == nil { 132 panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name))) 133 } 134 return prog.MethodValue(sel) 135 } 136 137 // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized). 138 type methodSet struct { 139 mapping map[string]*Function // populated lazily 140 } 141 142 // RuntimeTypes returns a new unordered slice containing all types in 143 // the program for which a runtime type is required. 144 // 145 // A runtime type is required for any non-parameterized, non-interface 146 // type that is converted to an interface, or for any type (including 147 // interface types) derivable from one through reflection. 148 // 149 // The methods of such types may be reachable through reflection or 150 // interface calls even if they are never called directly. 151 // 152 // Thread-safe. 153 // 154 // Acquires prog.runtimeTypesMu. 155 func (prog *Program) RuntimeTypes() []types.Type { 156 prog.runtimeTypesMu.Lock() 157 defer prog.runtimeTypesMu.Unlock() 158 return prog.runtimeTypes.Keys() 159 } 160 161 // forEachReachable calls f for type T and each type reachable from 162 // its type through reflection. 163 // 164 // The function f must use memoization to break cycles and 165 // return false when the type has already been visited. 166 // 167 // TODO(adonovan): publish in typeutil and share with go/callgraph/rta. 168 func forEachReachable(msets *typeutil.MethodSetCache, T types.Type, f func(types.Type) bool) { 169 var visit func(T types.Type, skip bool) 170 visit = func(T types.Type, skip bool) { 171 if !skip { 172 if !f(T) { 173 return 174 } 175 } 176 177 // Recursion over signatures of each method. 178 tmset := msets.MethodSet(T) 179 for i := 0; i < tmset.Len(); i++ { 180 sig := tmset.At(i).Type().(*types.Signature) 181 // It is tempting to call visit(sig, false) 182 // but, as noted in golang.org/cl/65450043, 183 // the Signature.Recv field is ignored by 184 // types.Identical and typeutil.Map, which 185 // is confusing at best. 186 // 187 // More importantly, the true signature rtype 188 // reachable from a method using reflection 189 // has no receiver but an extra ordinary parameter. 190 // For the Read method of io.Reader we want: 191 // func(Reader, []byte) (int, error) 192 // but here sig is: 193 // func([]byte) (int, error) 194 // with .Recv = Reader (though it is hard to 195 // notice because it doesn't affect Signature.String 196 // or types.Identical). 197 // 198 // TODO(adonovan): construct and visit the correct 199 // non-method signature with an extra parameter 200 // (though since unnamed func types have no methods 201 // there is essentially no actual demand for this). 202 // 203 // TODO(adonovan): document whether or not it is 204 // safe to skip non-exported methods (as RTA does). 205 visit(sig.Params(), true) // skip the Tuple 206 visit(sig.Results(), true) // skip the Tuple 207 } 208 209 switch T := T.(type) { 210 case *aliases.Alias: 211 visit(aliases.Unalias(T), skip) // emulates the pre-Alias behavior 212 213 case *types.Basic: 214 // nop 215 216 case *types.Interface: 217 // nop---handled by recursion over method set. 218 219 case *types.Pointer: 220 visit(T.Elem(), false) 221 222 case *types.Slice: 223 visit(T.Elem(), false) 224 225 case *types.Chan: 226 visit(T.Elem(), false) 227 228 case *types.Map: 229 visit(T.Key(), false) 230 visit(T.Elem(), false) 231 232 case *types.Signature: 233 if T.Recv() != nil { 234 panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv())) 235 } 236 visit(T.Params(), true) // skip the Tuple 237 visit(T.Results(), true) // skip the Tuple 238 239 case *types.Named: 240 // A pointer-to-named type can be derived from a named 241 // type via reflection. It may have methods too. 242 visit(types.NewPointer(T), false) 243 244 // Consider 'type T struct{S}' where S has methods. 245 // Reflection provides no way to get from T to struct{S}, 246 // only to S, so the method set of struct{S} is unwanted, 247 // so set 'skip' flag during recursion. 248 visit(T.Underlying(), true) // skip the unnamed type 249 250 case *types.Array: 251 visit(T.Elem(), false) 252 253 case *types.Struct: 254 for i, n := 0, T.NumFields(); i < n; i++ { 255 // TODO(adonovan): document whether or not 256 // it is safe to skip non-exported fields. 257 visit(T.Field(i).Type(), false) 258 } 259 260 case *types.Tuple: 261 for i, n := 0, T.Len(); i < n; i++ { 262 visit(T.At(i).Type(), false) 263 } 264 265 case *types.TypeParam, *types.Union: 266 // forEachReachable must not be called on parameterized types. 267 panic(T) 268 269 default: 270 panic(T) 271 } 272 } 273 visit(T, false) 274 }