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  }