~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/rpc/rpcreflect/type.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

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