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