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