github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  }