1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-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 * lookup.go 12 * 13 * Created on May 07, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package xreflect 18 19import ( 20 "go/types" 21 "reflect" 22 23 "github.com/cosmos72/gomacro/typeutil" 24) 25 26type depthMap struct { 27 gmap typeutil.Map 28} 29 30func (m *depthMap) visited(gtype types.Type, depth int) bool { 31 if at := m.gmap.At(gtype); at != nil && at.(int) < depth { 32 // already visited at shallower depth. 33 // avoids infinite loop for self-referencing types 34 // as type X struct { *X } 35 return true 36 } 37 m.gmap.Set(gtype, depth) 38 return false 39} 40 41// FieldByName returns the (possibly embedded) struct field with given name, 42// and the number of fields found at the same (shallowest) depth: 0 if not found. 43// Private fields are returned only if they were declared in pkgpath. 44func (t *xtype) FieldByName(name, pkgpath string) (field StructField, count int) { 45 if name == "_" || t.kind != reflect.Struct { 46 return 47 } 48 // debugf("field cache for %v <%v> = %v", unsafe.Pointer(t), t, t.fieldcache) 49 qname := QName2(name, pkgpath) 50 51 v := t.universe 52 if v.ThreadSafe { 53 defer un(lock(v)) 54 } 55 field, found := t.fieldcache[qname] 56 if found { 57 if field.Index == nil { // marker for ambiguous field names 58 count = int(field.Offset) // reuse Offset as "number of ambiguous fields" 59 } else { 60 count = 1 61 } 62 return field, count 63 } 64 var tovisit []StructField 65 var visited depthMap 66 field, count, tovisit = fieldByName(t, qname, 0, nil, &visited) 67 68 // breadth-first recursion 69 for count == 0 && len(tovisit) != 0 { 70 var next []StructField 71 for _, f := range tovisit { 72 efield, ecount, etovisit := fieldByName(unwrap(f.Type), qname, f.Offset, f.Index, &visited) 73 if count == 0 { 74 if ecount > 0 { 75 field = efield 76 } else { 77 // no recursion if we found something 78 next = append(next, etovisit...) 79 } 80 } 81 count += ecount 82 } 83 tovisit = next 84 } 85 if count > 0 { 86 cacheFieldByName(t, qname, &field, count) 87 } 88 return field, count 89} 90 91func fieldByName(t *xtype, qname QName, offset uintptr, index []int, m *depthMap) (field StructField, count int, tovisit []StructField) { 92 // also support embedded fields: they can be named types or pointers to named types 93 t, gtype := derefStruct(t) 94 if gtype == nil || m.visited(gtype, len(index)) { 95 return 96 } 97 // debugf("fieldByName: visiting %v <%v> <%v> at depth %d", t.kind, t.gtype, t.rtype, len(index)) 98 99 n := t.NumField() 100 for i := 0; i < n; i++ { 101 102 gfield := gtype.Field(i) 103 if matchFieldByName(qname, gfield) { 104 if count == 0 { 105 field = t.field(i) // lock already held. makes a copy 106 field.Offset += offset 107 field.Index = concat(index, field.Index) // make a copy of index 108 // debugf("fieldByName: %d-th field of <%v> matches: %#v", i, t.rtype, field) 109 } 110 count++ 111 } else if count == 0 && gfield.Anonymous() { 112 efield := t.field(i) // lock already held 113 efield.Offset += offset 114 efield.Index = concat(index, efield.Index) // make a copy of index 115 // debugf("fieldByName: %d-th field of <%v> is anonymous: %#v", i, t.rtype, efield) 116 tovisit = append(tovisit, efield) 117 } 118 } 119 return field, count, tovisit 120} 121 122func derefStruct(t *xtype) (*xtype, *types.Struct) { 123 if t != nil { 124 switch gtype := t.gtype.Underlying().(type) { 125 case *types.Struct: 126 return t, gtype 127 case *types.Pointer: 128 gelem, ok := gtype.Elem().Underlying().(*types.Struct) 129 if ok { 130 // not t.Elem(), it would acquire Universe lock 131 return unwrap(t.elem()), gelem 132 } 133 } 134 } 135 return nil, nil 136} 137 138// return true if gfield name matches given name, or if it's anonymous and its *type* name matches given name 139func matchFieldByName(qname QName, gfield *types.Var) bool { 140 // always check the field's package, not the type's package 141 if qname == QNameGo(gfield) { 142 return true 143 } 144 if gfield.Anonymous() { 145 gtype := gfield.Type() 146 if gptr, ok := gtype.(*types.Pointer); ok { 147 // unnamed field has unnamed pointer type, as for example *Foo 148 // check the element type 149 gtype = gptr.Elem() 150 } 151 switch gtype := gtype.(type) { 152 case *types.Basic: 153 // is it possible to embed basic types? 154 // yes, and they work as unexported embedded fields, 155 // i.e. in the same package as the struct that includes them 156 return qname == QNameGo2(gtype.Name(), gfield.Pkg()) 157 case *types.Named: 158 // gtype.Obj().Pkg() and gfield.Pkg() should be identical for *unexported* fields 159 // (they are ignored for exported fields) 160 return qname == QNameGo2(gtype.Obj().Name(), gfield.Pkg()) 161 } 162 } 163 return false 164} 165 166// add field to type's fieldcache. used by Type.FieldByName after a successful lookup 167func cacheFieldByName(t *xtype, qname QName, field *StructField, count int) { 168 if t.fieldcache == nil { 169 t.fieldcache = make(map[QName]StructField) 170 } 171 if count > 1 { 172 field.Index = nil // marker for ambiguous field names 173 field.Offset = uintptr(count) // reuse Offset as "number of ambiguous fields" 174 } 175 t.fieldcache[qname] = *field 176 t.universe.fieldcache = true 177} 178 179// anonymousFields returns the anonymous fields of a struct type (either named or unnamed) 180// also accepts a pointer to a struct type 181func anonymousFields(t *xtype, offset uintptr, index []int, m *depthMap) []StructField { 182 t, gtype := derefStruct(t) 183 if gtype == nil || m.visited(gtype, len(index)) { 184 return nil 185 } 186 n := gtype.NumFields() 187 var tovisit []StructField 188 for i := 0; i < n; i++ { 189 gfield := gtype.Field(i) 190 if gfield.Anonymous() { 191 field := t.field(i) // not t.Field(), it would acquire Universe lock 192 field.Offset += offset 193 field.Index = concat(index, field.Index) // make a copy of index 194 tovisit = append(tovisit, field) 195 } 196 } 197 return tovisit 198} 199 200// MethodByName returns the method with given name (including wrapper methods for embedded fields) 201// and the number of methods found at the same (shallowest) depth: 0 if not found. 202// Private methods are returned only if they were declared in pkgpath. 203func (t *xtype) MethodByName(name, pkgpath string) (method Method, count int) { 204 // debugf("method cache for %v <%v> = %v", unsafe.Pointer(t), t, t.methodcache) 205 206 // only named types and interfaces can have methods 207 if name == "_" || (!t.Named() && t.kind != reflect.Interface) { 208 return 209 } 210 v := t.universe 211 if v.ThreadSafe { 212 defer un(lock(v)) 213 } 214 return t.methodByName(name, pkgpath) 215} 216 217func (t *xtype) methodByName(name, pkgpath string) (method Method, count int) { 218 if name == "_" || (!t.Named() && t.kind != reflect.Interface) { 219 return 220 } 221 qname := QName2(name, pkgpath) 222 method, found := t.methodcache[qname] 223 if found { 224 index := method.Index 225 if index < 0 { // marker for ambiguous method names 226 count = -index 227 } else { 228 count = 1 229 } 230 return method, count 231 } 232 var visited depthMap 233 method, count = methodByName(t, qname, nil) 234 if count == 0 { 235 tovisit := anonymousFields(t, 0, nil, &visited) 236 // breadth-first recursion on struct's anonymous fields 237 for count == 0 && len(tovisit) != 0 { 238 var next []StructField 239 for _, f := range tovisit { 240 et := unwrap(f.Type) 241 emethod, ecount := methodByName(et, qname, f.Index) 242 if count == 0 { 243 if ecount > 0 { 244 method = emethod 245 } else { 246 // no recursion if we found something 247 next = append(next, anonymousFields(et, f.Offset, f.Index, &visited)...) 248 } 249 } 250 count += ecount 251 } 252 tovisit = next 253 } 254 } 255 if count > 0 { 256 cacheMethodByName(t, qname, &method, count) 257 } 258 return method, count 259} 260 261// For interfaces, search in *all* methods including wrapper methods for embedded interfaces 262// For all other named types, only search in explicitly declared methods, ignoring wrapper methods for embedded fields. 263func methodByName(t *xtype, qname QName, index []int) (method Method, count int) { 264 265 // debugf("methodByName: visiting %v <%v> <%v> at depth %d", t.kind, t.gtype, t.rtype, len(index)) 266 267 // also support embedded fields: they can be interfaces, named types, pointers to named types 268 if t.kind == reflect.Ptr { 269 te := unwrap(t.elem()) 270 if te.kind == reflect.Interface || te.kind == reflect.Ptr { 271 return 272 } 273 t = te 274 } 275 n := t.NumMethod() 276 for i := 0; i < n; i++ { 277 gmethod := t.gmethod(i) 278 if matchMethodByName(qname, gmethod) { 279 if count == 0 { 280 method = t.method(i) // lock already held 281 method.FieldIndex = concat(index, method.FieldIndex) // make a copy of index 282 // debugf("methodByName: %d-th explicit method of <%v> matches: %#v", i, t.rtype, method) 283 } 284 count++ 285 } 286 } 287 return 288} 289 290// return true if gmethod name matches given name 291func matchMethodByName(qname QName, gmethod *types.Func) bool { 292 // always check the methods's package, not the type's package 293 return qname == QNameGo(gmethod) 294} 295 296// add method to type's methodcache. used by Type.MethodByName after a successful lookup 297func cacheMethodByName(t *xtype, qname QName, method *Method, count int) { 298 if t.methodcache == nil { 299 t.methodcache = make(map[QName]Method) 300 } 301 if count > 1 { 302 method.Index = -count // marker for ambiguous method names 303 } 304 t.methodcache[qname] = *method 305 t.universe.methodcache = true 306} 307 308// visit type's direct and embedded fields in breadth-first order 309func (v *Universe) VisitFields(t Type, visitor func(StructField)) { 310 xt := unwrap(t) 311 if xt == nil { 312 return 313 } 314 var curr, tovisit []*xtype 315 curr = []*xtype{xt} 316 var seen typeutil.Map 317 318 for len(curr) != 0 { 319 for _, xt := range curr { 320 // embedded fields can be named types or pointers to named types 321 xt, _ = derefStruct(xt) 322 if xt == nil || xt.kind != reflect.Struct || seen.At(xt.gtype) != nil { 323 continue 324 } 325 seen.Set(xt.gtype, xt.gtype) 326 327 for i, n := 0, xt.NumField(); i < n; i++ { 328 field := xt.field(i) 329 visitor(field) 330 if field.Anonymous { 331 tovisit = append(tovisit, unwrap(field.Type)) 332 } 333 } 334 } 335 curr = tovisit 336 tovisit = nil 337 } 338} 339 340func invalidateCache(gtype types.Type, t interface{}) { 341 if t, ok := t.(Type); ok { 342 t := unwrap(t) 343 t.fieldcache = nil 344 t.methodcache = nil 345 } 346} 347 348func invalidateMethodCache(gtype types.Type, t interface{}) { 349 if t, ok := t.(Type); ok { 350 t := unwrap(t) 351 t.methodcache = nil 352 } 353} 354 355// clears all xtype.fieldcache and xtype.methodcache. 356// invoked by NamedOf() when a type is redefined. 357func (v *Universe) InvalidateCache() { 358 if v.fieldcache || v.methodcache { 359 v.gmap.Iterate(invalidateCache) 360 v.fieldcache = false 361 v.methodcache = false 362 } 363} 364 365// clears all xtype.methodcache. 366// invoked by AddMethod() when a method is redefined. 367func (v *Universe) InvalidateMethodCache() { 368 if v.methodcache { 369 v.gmap.Iterate(invalidateMethodCache) 370 v.methodcache = false 371 } 372} 373