methods.go (5337B)
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/typesinternal" 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, sel.Type()) { 36 return nil // method on generic type or generic method 37 } 38 39 if prog.mode&LogSource != 0 { 40 defer logStack("MethodValue %s %v", T, sel)() 41 } 42 43 var b builder 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), nil) 65 fn.buildshared = b.shared() 66 b.enqueue(fn) 67 } else { 68 fn = prog.objectMethod(obj, nil, &b) 69 } 70 if fn.Signature.Recv() == nil { 71 panic(fn) 72 } 73 mset.mapping[id] = fn 74 } else { 75 b.waitForSharedFunction(fn) 76 } 77 78 return fn 79 }() 80 81 b.iterate() 82 83 return m 84 } 85 86 // objectMethod returns the Function for a given method symbol. 87 // The symbol may be an instance of a generic function. It need not 88 // belong to an existing SSA package created by a call to 89 // prog.CreatePackage. 90 // 91 // objectMethod panics if the function is not a method. 92 // 93 // Acquires prog.objectMethodsMu. 94 func (prog *Program) objectMethod(obj *types.Func, targs []types.Type, b *builder) *Function { 95 sig := obj.Type().(*types.Signature) 96 if sig.Recv() == nil { 97 panic("not a method: " + obj.String()) 98 } 99 100 // Instantiation of generic? 101 if orig := obj.Origin(); orig != obj || len(targs) > 0 { 102 return prog.objectMethod(orig, nil, b).instance(receiverTypeArgs(obj), targs, b) 103 } 104 105 // Belongs to a created package? 106 if fn := prog.FuncValue(obj); fn != nil { 107 return fn 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, "") 116 fn.Synthetic = "from type information (on demand)" 117 fn.buildshared = b.shared() 118 b.enqueue(fn) 119 120 if prog.objectMethods == nil { 121 prog.objectMethods = make(map[*types.Func]*Function) 122 } 123 prog.objectMethods[obj] = fn 124 } else { 125 b.waitForSharedFunction(fn) 126 } 127 return fn 128 } 129 130 // LookupMethod returns the implementation of the method of type T 131 // identified by (pkg, name). It returns nil if the method exists but 132 // is an interface method or generic method, and panics if T has no such method. 133 func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function { 134 sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name) 135 if sel == nil { 136 panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name))) 137 } 138 return prog.MethodValue(sel) 139 } 140 141 // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized). 142 type methodSet struct { 143 mapping map[string]*Function // populated lazily 144 } 145 146 // RuntimeTypes returns a new unordered slice containing all types in 147 // the program for which a runtime type is required. 148 // 149 // A runtime type is required for any non-parameterized, non-interface 150 // type that is converted to an interface, or for any type (including 151 // interface types) derivable from one through reflection. 152 // 153 // The methods of such types may be reachable through reflection or 154 // interface calls even if they are never called directly. 155 // 156 // Thread-safe. 157 // 158 // Acquires prog.makeInterfaceTypesMu. 159 func (prog *Program) RuntimeTypes() []types.Type { 160 prog.makeInterfaceTypesMu.Lock() 161 defer prog.makeInterfaceTypesMu.Unlock() 162 163 // Compute the derived types on demand, since many SSA clients 164 // never call RuntimeTypes, and those that do typically call 165 // it once (often within ssautil.AllFunctions, which will 166 // eventually not use it; see Go issue #69291.) This 167 // eliminates the need to eagerly compute all the element 168 // types during SSA building. 169 var runtimeTypes []types.Type 170 var set typeutil.Map // for de-duping identical types 171 for t := range prog.makeInterfaceTypes { 172 typesinternal.ForEachElement(prog.MethodSets.MethodSet, t, func(t types.Type, access bool) bool { 173 if !access { 174 return false // inaccessible to reflection 175 } 176 seen, _ := set.Set(t, true).(bool) 177 if !seen { 178 runtimeTypes = append(runtimeTypes, t) 179 } 180 return seen 181 }) 182 } 183 184 return runtimeTypes 185 }