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