github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/builtin_fn.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"unsafe"
     8  
     9  	"github.com/u-root/u-root/cmds/elvish/eval/vals"
    10  	"github.com/xiaq/persistent/hash"
    11  )
    12  
    13  var ErrArgs = errors.New("args error")
    14  
    15  // BuiltinFn uses reflection to wrap arbitrary Go functions into Elvish
    16  // functions.
    17  //
    18  // Parameters are passed following these rules:
    19  //
    20  // 1. If the first parameter of function has type *Frame, it gets the current
    21  // call frame.
    22  //
    23  // 2. If (possibly after a *Frame parameter) the first parameter has type
    24  // RawOptions, it gets a map of options. If the function has not declared an
    25  // RawOptions parameter but is passed options, an error is thrown.
    26  //
    27  // 3. If the last parameter is non-variadic and has type Inputs, it represents
    28  // an optional parameter that contains the input to this function. If the
    29  // argument is not supplied, the input channel of the Frame will be used to
    30  // supply the inputs.
    31  //
    32  // 4. Other parameters are converted using elvToGo.
    33  //
    34  // Return values go to the channel part of the stdout port, after being
    35  // converted using goToElv. If the last return value has type error and is not
    36  // nil, it is turned into an exception and no ouputting happens. If the last
    37  // return value is a nil error, it is ignored.
    38  type BuiltinFn struct {
    39  	name string
    40  	impl interface{}
    41  
    42  	// Type information of impl.
    43  
    44  	frame   bool
    45  	options bool
    46  	inputs  bool
    47  	// Type of "normal" (non-Frame, non-Options, non-variadic) arguments.
    48  	normalArgs []reflect.Type
    49  	// Type of variadic arguments, nil if function is non-variadic
    50  	variadicArg reflect.Type
    51  }
    52  
    53  var _ Callable = &BuiltinFn{}
    54  
    55  type (
    56  	Inputs func(func(interface{}))
    57  )
    58  
    59  var (
    60  	frameType      = reflect.TypeOf((*Frame)(nil))
    61  	rawOptionsType = reflect.TypeOf(RawOptions(nil))
    62  	inputsType     = reflect.TypeOf(Inputs(nil))
    63  )
    64  
    65  // NewBuiltinFn creates a new ReflectBuiltinFn instance.
    66  func NewBuiltinFn(name string, impl interface{}) *BuiltinFn {
    67  	implType := reflect.TypeOf(impl)
    68  	b := &BuiltinFn{name: name, impl: impl}
    69  
    70  	i := 0
    71  	if i < implType.NumIn() && implType.In(i) == frameType {
    72  		b.frame = true
    73  		i++
    74  	}
    75  	if i < implType.NumIn() && implType.In(i) == rawOptionsType {
    76  		b.options = true
    77  		i++
    78  	}
    79  	for ; i < implType.NumIn(); i++ {
    80  		paramType := implType.In(i)
    81  		if i == implType.NumIn()-1 {
    82  			if implType.IsVariadic() {
    83  				b.variadicArg = paramType.Elem()
    84  				break
    85  			} else if paramType == inputsType {
    86  				b.inputs = true
    87  				break
    88  			}
    89  		}
    90  		b.normalArgs = append(b.normalArgs, paramType)
    91  	}
    92  	return b
    93  }
    94  
    95  // Kind returns "fn".
    96  func (*BuiltinFn) Kind() string {
    97  	return "fn"
    98  }
    99  
   100  // Equal compares identity.
   101  func (b *BuiltinFn) Equal(rhs interface{}) bool {
   102  	return b == rhs
   103  }
   104  
   105  // Hash hashes the address.
   106  func (b *BuiltinFn) Hash() uint32 {
   107  	return hash.Pointer(unsafe.Pointer(b))
   108  }
   109  
   110  // Repr returns an opaque representation "<builtin $name>".
   111  func (b *BuiltinFn) Repr(int) string {
   112  	return "<builtin " + b.name + ">"
   113  }
   114  
   115  // error(nil) is treated as nil by reflect.TypeOf, so we first get the type of
   116  // *error and use Elem to obtain type of error.
   117  var errorType = reflect.TypeOf((*error)(nil)).Elem()
   118  
   119  var errNoOptions = errors.New("function does not accept any options")
   120  
   121  // Call calls the implementation using reflection.
   122  func (b *BuiltinFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error {
   123  	if b.variadicArg != nil {
   124  		if len(args) < len(b.normalArgs) {
   125  			return fmt.Errorf("want %d or more arguments, got %d",
   126  				len(b.normalArgs), len(args))
   127  		}
   128  	} else if b.inputs {
   129  		if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 {
   130  			return fmt.Errorf("want %d or %d arguments, got %d",
   131  				len(b.normalArgs), len(b.normalArgs)+1, len(args))
   132  		}
   133  	} else if len(args) != len(b.normalArgs) {
   134  		return fmt.Errorf("want %d arguments, got %d", len(b.normalArgs), len(args))
   135  	}
   136  	if !b.options && len(opts) > 0 {
   137  		return errNoOptions
   138  	}
   139  
   140  	var in []reflect.Value
   141  	if b.frame {
   142  		in = append(in, reflect.ValueOf(f))
   143  	}
   144  	if b.options {
   145  		in = append(in, reflect.ValueOf(opts))
   146  	}
   147  	for i, arg := range args {
   148  		var typ reflect.Type
   149  		if i < len(b.normalArgs) {
   150  			typ = b.normalArgs[i]
   151  		} else if b.variadicArg != nil {
   152  			typ = b.variadicArg
   153  		} else if b.inputs {
   154  			break // Handled after the loop
   155  		} else {
   156  			panic("impossible")
   157  		}
   158  		ptr := reflect.New(typ)
   159  		err := vals.ScanToGo(arg, ptr.Interface())
   160  		if err != nil {
   161  			return fmt.Errorf("wrong type of %d'th argument: %v", i+1, err)
   162  		}
   163  		in = append(in, ptr.Elem())
   164  	}
   165  
   166  	if b.inputs {
   167  		var inputs Inputs
   168  		if len(args) == len(b.normalArgs) {
   169  			inputs = Inputs(f.IterateInputs)
   170  		} else {
   171  			// Wrap an iterable argument in Inputs.
   172  			iterable := args[len(args)-1]
   173  			inputs = Inputs(func(f func(interface{})) {
   174  				err := vals.Iterate(iterable, func(v interface{}) bool {
   175  					f(v)
   176  					return true
   177  				})
   178  				maybeThrow(err)
   179  			})
   180  		}
   181  		in = append(in, reflect.ValueOf(inputs))
   182  	}
   183  
   184  	outs := reflect.ValueOf(b.impl).Call(in)
   185  
   186  	if len(outs) > 0 && outs[len(outs)-1].Type() == errorType {
   187  		err := outs[len(outs)-1].Interface()
   188  		if err != nil {
   189  			return err.(error)
   190  		}
   191  		outs = outs[:len(outs)-1]
   192  	}
   193  
   194  	for _, out := range outs {
   195  		f.OutputChan() <- vals.FromGo(out.Interface())
   196  	}
   197  	return nil
   198  }