-
Notifications
You must be signed in to change notification settings - Fork 0
/
type.go
341 lines (311 loc) · 8.68 KB
/
type.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package rpcreflect
import (
"errors"
"reflect"
"sort"
"sync"
)
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
stringType = reflect.TypeOf("")
)
var (
typeMutex sync.RWMutex
typesByGoType = make(map[reflect.Type]*Type)
objTypeMutex sync.RWMutex
objTypesByGoType = make(map[reflect.Type]*ObjType)
)
var ErrMethodNotFound = errors.New("no such method")
// Type holds information about a type that implements RPC server methods,
// a root-level RPC type.
type Type struct {
// root holds the root type.
root reflect.Type
// method maps from root-object method name to
// information about that method. The term "obtain"
// is because these methods obtain an object to
// call an action on.
method map[string]*RootMethod
// discarded holds names of all discarded methods.
discarded []string
}
// MethodNames returns the names of all the root object
// methods on the receiving object.
func (r *Type) MethodNames() []string {
names := make([]string, 0, len(r.method))
for name := range r.method {
names = append(names, name)
}
sort.Strings(names)
return names
}
// Method returns information on the method with the given name,
// or the zero Method and ErrMethodNotFound if there is no such method
// (the only possible error).
func (r *Type) Method(name string) (RootMethod, error) {
m, ok := r.method[name]
if !ok {
return RootMethod{}, ErrMethodNotFound
}
return *m, nil
}
func (r *Type) DiscardedMethods() []string {
return append([]string(nil), r.discarded...)
}
// RootMethod holds information on a root-level method.
type RootMethod struct {
// Call invokes the method. The rcvr parameter must be
// the same type as the root object. The given id is passed
// as an argument to the method.
Call func(rcvr reflect.Value, id string) (reflect.Value, error)
// Methods holds RPC object-method information about
// objects returned from the above call.
ObjType *ObjType
}
// TypeOf returns information on all root-level RPC methods
// implemented by an object of the given Go type.
// If goType is nil, it returns nil.
func TypeOf(goType reflect.Type) *Type {
if goType == nil {
return nil
}
typeMutex.RLock()
t := typesByGoType[goType]
typeMutex.RUnlock()
if t != nil {
return t
}
typeMutex.Lock()
defer typeMutex.Unlock()
t = typesByGoType[goType]
if t != nil {
return t
}
t = typeOf(goType)
typesByGoType[goType] = t
return t
}
// typeOf is like TypeOf but without the cache - it
// always allocates. Called with rootTypeMutex locked.
func typeOf(goType reflect.Type) *Type {
rm := &Type{
method: make(map[string]*RootMethod),
}
for i := 0; i < goType.NumMethod(); i++ {
m := goType.Method(i)
if m.PkgPath != "" || isKillMethod(m) {
// The Kill method gets a special exception because
// it fulfils the Killer interface which we're expecting,
// so it's not really discarded as such.
continue
}
if o := newRootMethod(m); o != nil {
rm.method[m.Name] = o
} else {
rm.discarded = append(rm.discarded, m.Name)
}
}
return rm
}
func isKillMethod(m reflect.Method) bool {
return m.Name == "Kill" && m.Type.NumIn() == 1 && m.Type.NumOut() == 0
}
func newRootMethod(m reflect.Method) *RootMethod {
if m.PkgPath != "" {
return nil
}
t := m.Type
if t.NumIn() != 2 ||
t.NumOut() != 2 ||
t.In(1) != stringType ||
t.Out(1) != errorType {
return nil
}
f := func(rcvr reflect.Value, id string) (r reflect.Value, err error) {
out := rcvr.Method(m.Index).Call([]reflect.Value{reflect.ValueOf(id)})
if !out[1].IsNil() {
// Workaround LP 1251076.
// gccgo appears to get confused and thinks that out[1] is not nil when
// in fact it is an interface value of type error and value nil.
// This workaround solves the problem by leaving error as nil if in fact
// it was nil and causes no harm for gc because the predicates above
// assert that if out[1] is not nil, then it is an error type.
err, _ = out[1].Interface().(error)
}
r = out[0]
return
}
return &RootMethod{
Call: f,
ObjType: ObjTypeOf(t.Out(0)),
}
}
// ObjType holds information on RPC methods implemented on
// an RPC object.
type ObjType struct {
goType reflect.Type
method map[string]*ObjMethod
discarded []string
}
func (t *ObjType) GoType() reflect.Type {
return t.goType
}
// Method returns information on the method with the given name,
// or the zero Method and ErrMethodNotFound if there is no such method
// (the only possible error).
func (t *ObjType) Method(name string) (ObjMethod, error) {
m, ok := t.method[name]
if !ok {
return ObjMethod{}, ErrMethodNotFound
}
return *m, nil
}
// DiscardedMethods returns the names of all methods that cannot
// implement RPC calls because their type signature is inappropriate.
func (t *ObjType) DiscardedMethods() []string {
return append([]string(nil), t.discarded...)
}
// MethodNames returns the names of all the RPC methods
// defined on the type.
func (t *ObjType) MethodNames() []string {
names := make([]string, 0, len(t.method))
for name := range t.method {
names = append(names, name)
}
sort.Strings(names)
return names
}
// ObjMethod holds information about an RPC method on an
// object returned by a root-level method.
type ObjMethod struct {
// Params holds the argument type of the method, or nil
// if there is no argument.
Params reflect.Type
// Result holds the return type of the method, or nil
// if the method returns no value.
Result reflect.Type
// Call calls the method with the given argument
// on the given receiver value. If the method does
// not return a value, the returned value will not be valid.
Call func(rcvr, arg reflect.Value) (reflect.Value, error)
}
// ObjTypeOf returns information on all RPC methods
// implemented by an object of the given Go type,
// as returned from a root-level method.
// If objType is nil, it returns nil.
func ObjTypeOf(objType reflect.Type) *ObjType {
if objType == nil {
return nil
}
objTypeMutex.RLock()
methods := objTypesByGoType[objType]
objTypeMutex.RUnlock()
if methods != nil {
return methods
}
objTypeMutex.Lock()
defer objTypeMutex.Unlock()
methods = objTypesByGoType[objType]
if methods != nil {
return methods
}
methods = objTypeOf(objType)
objTypesByGoType[objType] = methods
return methods
}
// objTypeOf is like ObjTypeOf but without the cache.
// Called with objTypeMutex locked.
func objTypeOf(goType reflect.Type) *ObjType {
objType := &ObjType{
method: make(map[string]*ObjMethod),
goType: goType,
}
for i := 0; i < goType.NumMethod(); i++ {
m := goType.Method(i)
if m.PkgPath != "" {
continue
}
if objm := newMethod(m, goType.Kind()); objm != nil {
objType.method[m.Name] = objm
} else {
objType.discarded = append(objType.discarded, m.Name)
}
}
return objType
}
func newMethod(m reflect.Method, receiverKind reflect.Kind) *ObjMethod {
if m.PkgPath != "" {
return nil
}
var p ObjMethod
var assemble func(arg reflect.Value) []reflect.Value
// N.B. The method type has the receiver as its first argument
// unless the receiver is an interface.
receiverArgCount := 1
if receiverKind == reflect.Interface {
receiverArgCount = 0
}
t := m.Type
switch {
case t.NumIn() == 0+receiverArgCount:
// Method() ...
assemble = func(arg reflect.Value) []reflect.Value {
return nil
}
case t.NumIn() == 1+receiverArgCount:
// Method(T) ...
p.Params = t.In(receiverArgCount)
assemble = func(arg reflect.Value) []reflect.Value {
return []reflect.Value{arg}
}
default:
return nil
}
switch {
case t.NumOut() == 0:
// Method(...)
p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) {
rcvr.Method(m.Index).Call(assemble(arg))
return
}
case t.NumOut() == 1 && t.Out(0) == errorType:
// Method(...) error
p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) {
out := rcvr.Method(m.Index).Call(assemble(arg))
if !out[0].IsNil() {
err = out[0].Interface().(error)
}
return
}
case t.NumOut() == 1:
// Method(...) R
p.Result = t.Out(0)
p.Call = func(rcvr, arg reflect.Value) (reflect.Value, error) {
out := rcvr.Method(m.Index).Call(assemble(arg))
return out[0], nil
}
case t.NumOut() == 2 && t.Out(1) == errorType:
// Method(...) (R, error)
p.Result = t.Out(0)
p.Call = func(rcvr, arg reflect.Value) (r reflect.Value, err error) {
out := rcvr.Method(m.Index).Call(assemble(arg))
r = out[0]
if !out[1].IsNil() {
err = out[1].Interface().(error)
}
return
}
default:
return nil
}
// The parameters and return value must be of struct type.
if p.Params != nil && p.Params.Kind() != reflect.Struct {
return nil
}
if p.Result != nil && p.Result.Kind() != reflect.Struct {
return nil
}
return &p
}