gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/ssa/wrappers.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 // +build go1.5 6 7 package ssa 8 9 // This file defines synthesis of Functions that delegate to declared 10 // methods; they come in three kinds: 11 // 12 // (1) wrappers: methods that wrap declared methods, performing 13 // implicit pointer indirections and embedded field selections. 14 // 15 // (2) thunks: funcs that wrap declared methods. Like wrappers, 16 // thunks perform indirections and field selections. The thunk's 17 // first parameter is used as the receiver for the method call. 18 // 19 // (3) bounds: funcs that wrap declared methods. The bound's sole 20 // free variable, supplied by a closure, is used as the receiver 21 // for the method call. No indirections or field selections are 22 // performed since they can be done before the call. 23 24 import ( 25 "fmt" 26 27 "go/types" 28 ) 29 30 // -- wrappers ----------------------------------------------------------- 31 32 // makeWrapper returns a synthetic method that delegates to the 33 // declared method denoted by meth.Obj(), first performing any 34 // necessary pointer indirections or field selections implied by meth. 35 // 36 // The resulting method's receiver type is meth.Recv(). 37 // 38 // This function is versatile but quite subtle! Consider the 39 // following axes of variation when making changes: 40 // - optional receiver indirection 41 // - optional implicit field selections 42 // - meth.Obj() may denote a concrete or an interface method 43 // - the result may be a thunk or a wrapper. 44 // 45 // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) 46 // 47 func makeWrapper(prog *Program, sel *types.Selection) *Function { 48 obj := sel.Obj().(*types.Func) // the declared function 49 sig := sel.Type().(*types.Signature) // type of this wrapper 50 51 var recv *types.Var // wrapper's receiver or thunk's params[0] 52 name := obj.Name() 53 var description string 54 var start int // first regular param 55 if sel.Kind() == types.MethodExpr { 56 name += "$thunk" 57 description = "thunk" 58 recv = sig.Params().At(0) 59 start = 1 60 } else { 61 description = "wrapper" 62 recv = sig.Recv() 63 } 64 65 description = fmt.Sprintf("%s for %s", description, sel.Obj()) 66 if prog.mode&LogSource != 0 { 67 defer logStack("make %s to (%s)", description, recv.Type())() 68 } 69 fn := &Function{ 70 name: name, 71 method: sel, 72 object: obj, 73 Signature: sig, 74 Synthetic: description, 75 Prog: prog, 76 pos: obj.Pos(), 77 } 78 fn.startBody() 79 fn.addSpilledParam(recv) 80 createParams(fn, start) 81 82 indices := sel.Index() 83 84 var v Value = fn.Locals[0] // spilled receiver 85 if isPointer(sel.Recv()) { 86 v = emitLoad(fn, v) 87 88 // For simple indirection wrappers, perform an informative nil-check: 89 // "value method (T).f called using nil *T pointer" 90 if len(indices) == 1 && !isPointer(recvType(obj)) { 91 var c Call 92 c.Call.Value = &Builtin{ 93 name: "ssa:wrapnilchk", 94 sig: types.NewSignature(nil, 95 types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)), 96 types.NewTuple(anonVar(sel.Recv())), false), 97 } 98 c.Call.Args = []Value{ 99 v, 100 stringConst(deref(sel.Recv()).String()), 101 stringConst(sel.Obj().Name()), 102 } 103 c.setType(v.Type()) 104 v = fn.emit(&c) 105 } 106 } 107 108 // Invariant: v is a pointer, either 109 // value of *A receiver param, or 110 // address of A spilled receiver. 111 112 // We use pointer arithmetic (FieldAddr possibly followed by 113 // Load) in preference to value extraction (Field possibly 114 // preceded by Load). 115 116 v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) 117 118 // Invariant: v is a pointer, either 119 // value of implicit *C field, or 120 // address of implicit C field. 121 122 var c Call 123 if r := recvType(obj); !isInterface(r) { // concrete method 124 if !isPointer(r) { 125 v = emitLoad(fn, v) 126 } 127 c.Call.Value = prog.declaredFunc(obj) 128 c.Call.Args = append(c.Call.Args, v) 129 } else { 130 c.Call.Method = obj 131 c.Call.Value = emitLoad(fn, v) 132 } 133 for _, arg := range fn.Params[1:] { 134 c.Call.Args = append(c.Call.Args, arg) 135 } 136 emitTailCall(fn, &c) 137 fn.finishBody() 138 return fn 139 } 140 141 // createParams creates parameters for wrapper method fn based on its 142 // Signature.Params, which do not include the receiver. 143 // start is the index of the first regular parameter to use. 144 // 145 func createParams(fn *Function, start int) { 146 var last *Parameter 147 tparams := fn.Signature.Params() 148 for i, n := start, tparams.Len(); i < n; i++ { 149 last = fn.addParamObj(tparams.At(i)) 150 } 151 if fn.Signature.Variadic() { 152 last.typ = types.NewSlice(last.typ) 153 } 154 } 155 156 // -- bounds ----------------------------------------------------------- 157 158 // makeBound returns a bound method wrapper (or "bound"), a synthetic 159 // function that delegates to a concrete or interface method denoted 160 // by obj. The resulting function has no receiver, but has one free 161 // variable which will be used as the method's receiver in the 162 // tail-call. 163 // 164 // Use MakeClosure with such a wrapper to construct a bound method 165 // closure. e.g.: 166 // 167 // type T int or: type T interface { meth() } 168 // func (t T) meth() 169 // var t T 170 // f := t.meth 171 // f() // calls t.meth() 172 // 173 // f is a closure of a synthetic wrapper defined as if by: 174 // 175 // f := func() { return t.meth() } 176 // 177 // Unlike makeWrapper, makeBound need perform no indirection or field 178 // selections because that can be done before the closure is 179 // constructed. 180 // 181 // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) 182 // 183 func makeBound(prog *Program, obj *types.Func) *Function { 184 prog.methodsMu.Lock() 185 defer prog.methodsMu.Unlock() 186 fn, ok := prog.bounds[obj] 187 if !ok { 188 description := fmt.Sprintf("bound method wrapper for %s", obj) 189 if prog.mode&LogSource != 0 { 190 defer logStack("%s", description)() 191 } 192 fn = &Function{ 193 name: obj.Name() + "$bound", 194 object: obj, 195 Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver 196 Synthetic: description, 197 Prog: prog, 198 pos: obj.Pos(), 199 } 200 201 fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} 202 fn.FreeVars = []*FreeVar{fv} 203 fn.startBody() 204 createParams(fn, 0) 205 var c Call 206 207 if !isInterface(recvType(obj)) { // concrete 208 c.Call.Value = prog.declaredFunc(obj) 209 c.Call.Args = []Value{fv} 210 } else { 211 c.Call.Value = fv 212 c.Call.Method = obj 213 } 214 for _, arg := range fn.Params { 215 c.Call.Args = append(c.Call.Args, arg) 216 } 217 emitTailCall(fn, &c) 218 fn.finishBody() 219 220 prog.bounds[obj] = fn 221 } 222 return fn 223 } 224 225 // -- thunks ----------------------------------------------------------- 226 227 // makeThunk returns a thunk, a synthetic function that delegates to a 228 // concrete or interface method denoted by sel.Obj(). The resulting 229 // function has no receiver, but has an additional (first) regular 230 // parameter. 231 // 232 // Precondition: sel.Kind() == types.MethodExpr. 233 // 234 // type T int or: type T interface { meth() } 235 // func (t T) meth() 236 // f := T.meth 237 // var t T 238 // f(t) // calls t.meth() 239 // 240 // f is a synthetic wrapper defined as if by: 241 // 242 // f := func(t T) { return t.meth() } 243 // 244 // TODO(adonovan): opt: currently the stub is created even when used 245 // directly in a function call: C.f(i, 0). This is less efficient 246 // than inlining the stub. 247 // 248 // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) 249 // 250 func makeThunk(prog *Program, sel *types.Selection) *Function { 251 if sel.Kind() != types.MethodExpr { 252 panic(sel) 253 } 254 255 key := selectionKey{ 256 kind: sel.Kind(), 257 recv: sel.Recv(), 258 obj: sel.Obj(), 259 index: fmt.Sprint(sel.Index()), 260 indirect: sel.Indirect(), 261 } 262 263 prog.methodsMu.Lock() 264 defer prog.methodsMu.Unlock() 265 266 // Canonicalize key.recv to avoid constructing duplicate thunks. 267 canonRecv, ok := prog.canon.At(key.recv).(types.Type) 268 if !ok { 269 canonRecv = key.recv 270 prog.canon.Set(key.recv, canonRecv) 271 } 272 key.recv = canonRecv 273 274 fn, ok := prog.thunks[key] 275 if !ok { 276 fn = makeWrapper(prog, sel) 277 if fn.Signature.Recv() != nil { 278 panic(fn) // unexpected receiver 279 } 280 prog.thunks[key] = fn 281 } 282 return fn 283 } 284 285 func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { 286 return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic()) 287 } 288 289 // selectionKey is like types.Selection but a usable map key. 290 type selectionKey struct { 291 kind types.SelectionKind 292 recv types.Type // canonicalized via Program.canon 293 obj types.Object 294 index string 295 indirect bool 296 }