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
5package ir
6
7// This file defines utilities for population of method sets.
8
9import (
10	"fmt"
11	"go/types"
12)
13
14// MethodValue returns the Function implementing method sel, building
15// wrapper methods on demand.  It returns nil if sel denotes an
16// abstract (interface) method.
17//
18// Precondition: sel.Kind() == MethodVal.
19//
20// Thread-safe.
21//
22// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
23//
24func (prog *Program) MethodValue(sel *types.Selection) *Function {
25	if sel.Kind() != types.MethodVal {
26		panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
27	}
28	T := sel.Recv()
29	if isInterface(T) {
30		return nil // abstract method
31	}
32	if prog.mode&LogSource != 0 {
33		defer logStack("MethodValue %s %v", T, sel)()
34	}
35
36	prog.methodsMu.Lock()
37	defer prog.methodsMu.Unlock()
38
39	return prog.addMethod(prog.createMethodSet(T), sel)
40}
41
42// LookupMethod returns the implementation of the method of type T
43// identified by (pkg, name).  It returns nil if the method exists but
44// is abstract, and panics if T has no such method.
45//
46func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
47	sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
48	if sel == nil {
49		panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
50	}
51	return prog.MethodValue(sel)
52}
53
54// methodSet contains the (concrete) methods of a non-interface type.
55type methodSet struct {
56	mapping  map[string]*Function // populated lazily
57	complete bool                 // mapping contains all methods
58}
59
60// Precondition: !isInterface(T).
61// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
62func (prog *Program) createMethodSet(T types.Type) *methodSet {
63	mset, ok := prog.methodSets.At(T).(*methodSet)
64	if !ok {
65		mset = &methodSet{mapping: make(map[string]*Function)}
66		prog.methodSets.Set(T, mset)
67	}
68	return mset
69}
70
71// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
72func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
73	if sel.Kind() == types.MethodExpr {
74		panic(sel)
75	}
76	id := sel.Obj().Id()
77	fn := mset.mapping[id]
78	if fn == nil {
79		obj := sel.Obj().(*types.Func)
80
81		needsPromotion := len(sel.Index()) > 1
82		needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
83		if needsPromotion || needsIndirection {
84			fn = makeWrapper(prog, sel)
85		} else {
86			fn = prog.declaredFunc(obj)
87		}
88		if fn.Signature.Recv() == nil {
89			panic(fn) // missing receiver
90		}
91		mset.mapping[id] = fn
92	}
93	return fn
94}
95
96// RuntimeTypes returns a new unordered slice containing all
97// concrete types in the program for which a complete (non-empty)
98// method set is required at run-time.
99//
100// Thread-safe.
101//
102// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
103//
104func (prog *Program) RuntimeTypes() []types.Type {
105	prog.methodsMu.Lock()
106	defer prog.methodsMu.Unlock()
107
108	var res []types.Type
109	prog.methodSets.Iterate(func(T types.Type, v interface{}) {
110		if v.(*methodSet).complete {
111			res = append(res, T)
112		}
113	})
114	return res
115}
116
117// declaredFunc returns the concrete function/method denoted by obj.
118// Panic ensues if there is none.
119//
120func (prog *Program) declaredFunc(obj *types.Func) *Function {
121	if v := prog.packageLevelValue(obj); v != nil {
122		return v.(*Function)
123	}
124	panic("no concrete method: " + obj.String())
125}
126
127// needMethodsOf ensures that runtime type information (including the
128// complete method set) is available for the specified type T and all
129// its subcomponents.
130//
131// needMethodsOf must be called for at least every type that is an
132// operand of some MakeInterface instruction, and for the type of
133// every exported package member.
134//
135// Precondition: T is not a method signature (*Signature with Recv()!=nil).
136//
137// Thread-safe.  (Called via emitConv from multiple builder goroutines.)
138//
139// TODO(adonovan): make this faster.  It accounts for 20% of SSA build time.
140//
141// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
142//
143func (prog *Program) needMethodsOf(T types.Type) {
144	prog.methodsMu.Lock()
145	prog.needMethods(T, false)
146	prog.methodsMu.Unlock()
147}
148
149// Precondition: T is not a method signature (*Signature with Recv()!=nil).
150// Recursive case: skip => don't create methods for T.
151//
152// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
153//
154func (prog *Program) needMethods(T types.Type, skip bool) {
155	// Each package maintains its own set of types it has visited.
156	if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
157		// needMethods(T) was previously called
158		if !prevSkip || skip {
159			return // already seen, with same or false 'skip' value
160		}
161	}
162	prog.runtimeTypes.Set(T, skip)
163
164	tmset := prog.MethodSets.MethodSet(T)
165
166	if !skip && !isInterface(T) && tmset.Len() > 0 {
167		// Create methods of T.
168		mset := prog.createMethodSet(T)
169		if !mset.complete {
170			mset.complete = true
171			n := tmset.Len()
172			for i := 0; i < n; i++ {
173				prog.addMethod(mset, tmset.At(i))
174			}
175		}
176	}
177
178	// Recursion over signatures of each method.
179	for i := 0; i < tmset.Len(); i++ {
180		sig := tmset.At(i).Type().(*types.Signature)
181		prog.needMethods(sig.Params(), false)
182		prog.needMethods(sig.Results(), false)
183	}
184
185	switch t := T.(type) {
186	case *types.Basic:
187		// nop
188
189	case *types.Interface:
190		// nop---handled by recursion over method set.
191
192	case *types.Pointer:
193		prog.needMethods(t.Elem(), false)
194
195	case *types.Slice:
196		prog.needMethods(t.Elem(), false)
197
198	case *types.Chan:
199		prog.needMethods(t.Elem(), false)
200
201	case *types.Map:
202		prog.needMethods(t.Key(), false)
203		prog.needMethods(t.Elem(), false)
204
205	case *types.Signature:
206		if t.Recv() != nil {
207			panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
208		}
209		prog.needMethods(t.Params(), false)
210		prog.needMethods(t.Results(), false)
211
212	case *types.Named:
213		// A pointer-to-named type can be derived from a named
214		// type via reflection.  It may have methods too.
215		prog.needMethods(types.NewPointer(T), false)
216
217		// Consider 'type T struct{S}' where S has methods.
218		// Reflection provides no way to get from T to struct{S},
219		// only to S, so the method set of struct{S} is unwanted,
220		// so set 'skip' flag during recursion.
221		prog.needMethods(t.Underlying(), true)
222
223	case *types.Array:
224		prog.needMethods(t.Elem(), false)
225
226	case *types.Struct:
227		for i, n := 0, t.NumFields(); i < n; i++ {
228			prog.needMethods(t.Field(i).Type(), false)
229		}
230
231	case *types.Tuple:
232		for i, n := 0, t.Len(); i < n; i++ {
233			prog.needMethods(t.At(i).Type(), false)
234		}
235
236	default:
237		panic(T)
238	}
239}
240