1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2018-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * method.go 12 * 13 * Created on Mar 28, 2018 14 * Author Massimiliano Ghilardi 15 */ 16 17package xreflect 18 19import ( 20 "go/ast" 21 "go/types" 22 "reflect" 23 24 "github.com/cosmos72/gomacro/typeutil" 25) 26 27// return detailed string representation of a method signature, including its receiver if present 28func (m Method) String() string { 29 return typeutil.String2(m.Name, m.GoFun.Type()) 30} 31 32// For interfaces, NumMethod returns *total* number of methods for interface t, 33// including wrapper methods for embedded interfaces. 34// For all other named types, NumMethod returns the number of explicitly declared methods, 35// ignoring wrapper methods for embedded fields. 36// Returns 0 for other unnamed types. 37func (t *xtype) NumMethod() int { 38 num := 0 39 if gt, ok := t.gtype.Underlying().(*types.Interface); ok { 40 num = gt.NumMethods() 41 } else if gt, ok := t.gtype.(*types.Named); ok { 42 num = gt.NumMethods() 43 } 44 return num 45} 46 47// NumExplicitMethod returns the number of explicitly declared methods of named type or interface t. 48// Wrapper methods for embedded fields or embedded interfaces are not counted. 49func (t *xtype) NumExplicitMethod() int { 50 num := 0 51 if gt, ok := t.gtype.Underlying().(*types.Interface); ok { 52 num = gt.NumExplicitMethods() 53 } else if gt, ok := t.gtype.(*types.Named); ok { 54 num = gt.NumMethods() 55 } 56 return num 57} 58 59// NumAllMethod returns the *total* number of methods for interface or named type t, 60// including wrapper methods for embedded fields or embedded interfaces. 61// Note: it has slightly different semantics from go/types.(*Named).NumMethods(), 62// since the latter returns 0 for named interfaces, and callers need to manually invoke 63// goNamedType.Underlying().NumMethods() to retrieve the number of methods 64// of a named interface 65func (t *xtype) NumAllMethod() int { 66 return goTypeNumAllMethod(t.gtype, make(map[types.Type]struct{})) 67} 68 69// recursively count total number of methods for type t, 70// including wrapper methods for embedded fields or embedded interfaces 71func goTypeNumAllMethod(gt types.Type, visited map[types.Type]struct{}) int { 72 count := 0 73 for { 74 if _, ok := visited[gt]; ok { 75 break 76 } 77 visited[gt] = struct{}{} 78 switch t := gt.(type) { 79 case *types.Named: 80 count += t.NumMethods() 81 u := t.Underlying() 82 if u != gt { 83 gt = u 84 continue 85 } 86 case *types.Interface: 87 count += t.NumMethods() 88 case *types.Struct: 89 n := t.NumFields() 90 for i := 0; i < n; i++ { 91 if f := t.Field(i); f.Anonymous() { 92 count += goTypeNumAllMethod(f.Type(), visited) 93 } 94 } 95 } 96 break 97 } 98 return count 99} 100 101// For interfaces, Method returns the i-th method, including methods from embedded interfaces. 102// For all other named types, Method returns the i-th explicitly declared method, ignoring wrapper methods for embedded fields. 103// It panics if i is outside the range 0 .. NumMethod()-1 104func (t *xtype) Method(i int) Method { 105 checkMethod(t, i) 106 v := t.universe 107 if v.ThreadSafe { 108 defer un(lock(v)) 109 } 110 return t.method(i) 111} 112 113func checkMethod(t *xtype, i int) { 114 if t.kind == reflect.Ptr { 115 xerrorf(t, "Method of %s type %v. Invoke Method() on type's Elem() instead", i, t.kind, t) 116 } 117 if !t.Named() && t.kind != reflect.Interface { 118 xerrorf(t, "Method of type %v that cannot have methods", t.kind, t) 119 } 120} 121 122func (t *xtype) method(i int) Method { 123 checkMethod(t, i) 124 gfunc := t.gmethod(i) 125 name := gfunc.Name() 126 resizemethodvalues(t) 127 128 rtype := t.rtype 129 var rfunctype reflect.Type 130 rfunc := t.methodvalues[i] 131 if rfunc.Kind() == reflect.Func { 132 // easy, method is cached already 133 rfunctype = rfunc.Type() 134 } else if _, ok := t.gtype.Underlying().(*types.Interface); ok { 135 if rtype.Kind() == reflect.Ptr && isReflectInterfaceStruct(rtype.Elem()) { 136 // rtype is our emulated interface type, 137 // i.e. a pointer to a struct containing: InterfaceHeader, [0]struct { embeddeds }, methods (without receiver) 138 rfield := rtype.Elem().Field(i + 2) 139 rfunctype = rAddReceiver(rtype, rfield.Type) 140 } else if rtype.Kind() != reflect.Interface { 141 xerrorf(t, "inconsistent interface type <%v>: expecting interface reflect.Type, found <%v>", t, rtype) 142 } else if ast.IsExported(name) { 143 // rtype is an interface type, and reflect only returns exported methods 144 // rtype.MethodByName returns a Method with the following caveats 145 // 1) Type == method signature, without a receiver 146 // 2) Func == nil. 147 rmethod, _ := rtype.MethodByName(name) 148 if rmethod.Type == nil { 149 xerrorf(t, "interface type <%v>: reflect method %q not found", t, name) 150 } else if rmethod.Index != i { 151 xerrorf(t, "inconsistent interface type <%v>: method %q has go/types.Func index=%d but reflect.Method index=%d", 152 t, name, i, rmethod.Index) 153 } 154 rfunctype = rAddReceiver(rtype, rmethod.Type) 155 } 156 } else { 157 rmethod, _ := rtype.MethodByName(gfunc.Name()) 158 rfunc = rmethod.Func 159 if rfunc.Kind() != reflect.Func { 160 if rtype.Kind() != reflect.Ptr { 161 // also search in the method set of pointer-to-t 162 rmethod, _ = reflect.PtrTo(rtype).MethodByName(gfunc.Name()) 163 rfunc = rmethod.Func 164 } 165 } 166 if rfunc.Kind() != reflect.Func { 167 if ast.IsExported(name) { 168 xerrorf(t, "type <%v>: reflect method %q not found", t, gfunc.Name()) 169 } 170 } else { 171 rfunctype = rmethod.Type 172 } 173 t.methodvalues[i] = rfunc 174 } 175 return t.makemethod(i, gfunc, &t.methodvalues, rfunctype) // lock already held 176} 177 178// insert recv as the the first parameter of rtype function type 179func rAddReceiver(recv reflect.Type, rtype reflect.Type) reflect.Type { 180 nin := rtype.NumIn() 181 rin := make([]reflect.Type, nin+1) 182 rin[0] = recv 183 for i := 0; i < nin; i++ { 184 rin[i+1] = rtype.In(i) 185 } 186 nout := rtype.NumOut() 187 rout := make([]reflect.Type, nout) 188 for i := 0; i < nout; i++ { 189 rout[i] = rtype.Out(i) 190 } 191 return reflect.FuncOf(rin, rout, rtype.IsVariadic()) 192} 193 194// remove the first parameter of rtype function type 195func rRemoveReceiver(rtype reflect.Type) reflect.Type { 196 nin := rtype.NumIn() 197 if nin == 0 { 198 return rtype 199 } 200 rin := make([]reflect.Type, nin-1) 201 for i := 1; i < nin; i++ { 202 rin[i-1] = rtype.In(i) 203 } 204 nout := rtype.NumOut() 205 rout := make([]reflect.Type, nout) 206 for i := 0; i < nout; i++ { 207 rout[i] = rtype.Out(i) 208 } 209 return reflect.FuncOf(rin, rout, nin > 1 && rtype.IsVariadic()) 210} 211 212// remove the first parameter of t function type 213func removeReceiver(t Type) Type { 214 nin := t.NumIn() 215 if nin == 0 { 216 return t 217 } 218 tin := make([]Type, nin-1) 219 for i := 1; i < nin; i++ { 220 tin[i-1] = t.In(i) 221 } 222 nout := t.NumOut() 223 tout := make([]Type, nout) 224 for i := 0; i < nout; i++ { 225 tout[i] = t.Out(i) 226 } 227 return t.Universe().FuncOf(tin, tout, nin > 1 && t.IsVariadic()) 228} 229 230func (t *xtype) gmethod(i int) *types.Func { 231 var gfun *types.Func 232 if gtype, ok := t.gtype.Underlying().(*types.Interface); ok { 233 gfun = gtype.Method(i) 234 } else if gtype, ok := t.gtype.(*types.Named); ok { 235 gfun = gtype.Method(i) 236 } else { 237 xerrorf(t, "Method on invalid type %v", t) 238 } 239 return gfun 240} 241 242func (t *xtype) makemethod(index int, gfun *types.Func, rfuns *[]reflect.Value, rfunctype reflect.Type) Method { 243 // sanity checks 244 name := gfun.Name() 245 gsig := gfun.Type().Underlying().(*types.Signature) 246 if rfunctype != nil { 247 nparams := 0 248 if gsig.Params() != nil { 249 nparams = gsig.Params().Len() 250 } 251 if gsig.Recv() != nil { 252 if nparams+1 != rfunctype.NumIn() { 253 xerrorf(t, `type <%v>: inconsistent %d-th method signature: 254 go/types.Type has receiver <%v> and %d parameters: %v 255 reflect.Type has %d parameters: %v`, t, index, gsig.Recv(), nparams, gsig, rfunctype.NumIn(), rfunctype) 256 } 257 } else if nparams != rfunctype.NumIn() { 258 xerrorf(t, `type <%v>: inconsistent %d-th method signature: 259 go/types.Type has no receiver and %d parameters: %v 260 reflect.Type has %d parameters: %v`, t, index, nparams, gsig, rfunctype.NumIn(), rfunctype) 261 } 262 } 263 var tmethod Type 264 if rfunctype != nil { 265 rsig := ReflectUnderlying(rfunctype) 266 tmethod = t.universe.maketype(gsig, rsig) // lock already held 267 } 268 return Method{ 269 Name: name, 270 Pkg: (*Package)(gfun.Pkg()), 271 Type: tmethod, 272 Funs: rfuns, 273 Index: index, 274 GoFun: gfun, 275 } 276} 277 278func resizemethodvalues(t *xtype) { 279 n := t.NumMethod() 280 if cap(t.methodvalues) < n { 281 slice := make([]reflect.Value, n, n+n/2+4) 282 copy(slice, t.methodvalues) 283 t.methodvalues = slice 284 } else if len(t.methodvalues) < n { 285 t.methodvalues = t.methodvalues[0:n] 286 } 287} 288 289// return one of the methods defined by interface tinterf but missing from t 290func MissingMethod(t, tinterf Type) *Method { 291 n := tinterf.NumMethod() 292 var mtdinterf Method 293 if t == nil && n > 0 { 294 mtdinterf = tinterf.Method(0) 295 return &mtdinterf 296 } 297 for i := 0; i < n; i++ { 298 mtdinterf = tinterf.Method(i) 299 mtd, count := t.MethodByName(mtdinterf.Name, mtdinterf.Pkg.Name()) 300 if count == 1 { 301 tfunc := mtd.Type 302 if t.Kind() != reflect.Interface { 303 tfunc = removeReceiver(tfunc) 304 } 305 if !mtdinterf.Type.IdenticalTo(tfunc) { 306 continue 307 } 308 } 309 return &mtdinterf 310 } 311 return nil 312} 313