github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 "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 }