gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/cmds/core/elvish/eval/builtin_fn.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  
     9  	"github.com/u-root/u-root/cmds/core/elvish/eval/vals"
    10  	"github.com/u-root/u-root/cmds/core/elvish/hash"
    11  )
    12  
    13  var ErrArgs = errors.New("args error")
    14  
    15  // BuiltinFn uses reflection to wrap Go functions into Elvish functions.
    16  // Functions with simple signatures are handled by commonCallable,
    17  // more elaborate cases are handled by CustomCallable interface, e.g.
    18  // when importing the necessary types would have created a dependency loop.
    19  //
    20  // Parameters are converted using ScanToGo.
    21  //
    22  // Return values go to the channel part of the stdout port, after being
    23  // converted using goToElv. If the last return value has type error and is not
    24  // nil, it is turned into an exception and no ouputting happens. If the last
    25  // return value is a nil error, it is ignored.
    26  //
    27  // Note: reflect.Call is deliberately not used as it disables DCE
    28  // (see https://github.com/u-root/u-root/issues/1477).
    29  type BuiltinFn struct {
    30  	name string
    31  	impl CustomCallable
    32  
    33  	// Type information of impl.
    34  
    35  	frame   bool
    36  	options bool
    37  	inputs  bool
    38  	// Type of "normal" (non-Frame, non-Options, non-variadic) arguments.
    39  	normalArgs []reflect.Type
    40  	// Type of variadic arguments, nil if function is non-variadic
    41  	variadicArg reflect.Type
    42  }
    43  
    44  var _ Callable = &BuiltinFn{}
    45  
    46  // CustomCallable is an interface for functions that have complex signatures
    47  // not covered by commonCallable.
    48  type CustomCallable interface {
    49  	// Target returns the callable's target function, needed to examine arguments.
    50  	Target() interface{}
    51  	// Call invokes the target function.
    52  	Call(f *Frame, args []interface{}, opts RawOptions, inputs Inputs) ([]interface{}, error)
    53  }
    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  // NewBuiltinFnCustom creates a new ReflectBuiltinFn instance.
    66  func NewBuiltinFnCustom(name string, impl CustomCallable) *BuiltinFn {
    67  	implType := reflect.TypeOf(impl.Target())
    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  // NewBuiltinFn creates a new ReflectBuiltinFn instance.
    96  func NewBuiltinFn(name string, impl interface{}) *BuiltinFn {
    97  	if _, err := callFunc(impl, nil, nil, nil, nil, true); err != nil {
    98  		panic(fmt.Sprintf("%s: %v", name, err))
    99  	}
   100  	return NewBuiltinFnCustom(name, &commonCallable{target: impl})
   101  }
   102  
   103  // Kind returns "fn".
   104  func (*BuiltinFn) Kind() string {
   105  	return "fn"
   106  }
   107  
   108  // Equal compares identity.
   109  func (b *BuiltinFn) Equal(rhs interface{}) bool {
   110  	return b == rhs
   111  }
   112  
   113  // Hash hashes the address.
   114  func (b *BuiltinFn) Hash() uint32 {
   115  	return hash.Hash(b)
   116  }
   117  
   118  // Repr returns an opaque representation "<builtin $name>".
   119  func (b *BuiltinFn) Repr(int) string {
   120  	return "<builtin " + b.name + ">"
   121  }
   122  
   123  // error(nil) is treated as nil by reflect.TypeOf, so we first get the type of
   124  // *error and use Elem to obtain type of error.
   125  var errorType = reflect.TypeOf((*error)(nil)).Elem()
   126  
   127  var errNoOptions = errors.New("function does not accept any options")
   128  
   129  // Call calls the implementation using reflection.
   130  func (b *BuiltinFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error {
   131  	if b.variadicArg != nil {
   132  		if len(args) < len(b.normalArgs) {
   133  			return fmt.Errorf("%s: want %d or more arguments, got %d",
   134  				b.name, len(b.normalArgs), len(args))
   135  		}
   136  	} else if b.inputs {
   137  		if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 {
   138  			return fmt.Errorf("%s: want %d or %d arguments, got %d",
   139  				b.name, len(b.normalArgs), len(b.normalArgs)+1, len(args))
   140  		}
   141  	} else if len(args) != len(b.normalArgs) {
   142  		return fmt.Errorf("%s: want %d arguments, got %d", b.name, len(b.normalArgs), len(args))
   143  	}
   144  	if !b.options && len(opts) > 0 {
   145  		return errNoOptions
   146  	}
   147  
   148  	var goArgs []interface{}
   149  	for i, arg := range args {
   150  		var typ reflect.Type
   151  		if i < len(b.normalArgs) {
   152  			typ = b.normalArgs[i]
   153  		} else if b.variadicArg != nil {
   154  			typ = b.variadicArg
   155  		} else if b.inputs {
   156  			break // Handled after the loop
   157  		} else {
   158  			panic("impossible")
   159  		}
   160  		ptr := reflect.New(typ)
   161  		err := vals.ScanToGo(arg, ptr.Interface())
   162  		if err != nil {
   163  			return fmt.Errorf("%s: wrong type of %d'th argument: %v", b.name, i+1, err)
   164  		}
   165  		goArgs = append(goArgs, ptr.Elem().Interface())
   166  	}
   167  
   168  	var inputs Inputs
   169  	if b.inputs {
   170  		if len(args) == len(b.normalArgs) {
   171  			inputs = Inputs(f.IterateInputs)
   172  		} else {
   173  			// Wrap an iterable argument in Inputs.
   174  			iterable := args[len(args)-1]
   175  			inputs = Inputs(func(f func(interface{})) {
   176  				err := vals.Iterate(iterable, func(v interface{}) bool {
   177  					f(v)
   178  					return true
   179  				})
   180  				maybeThrow(err)
   181  			})
   182  		}
   183  	}
   184  
   185  	outs, err := b.impl.Call(f, goArgs, opts, inputs)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	for _, out := range outs {
   190  		f.OutputChan() <- vals.FromGo(out)
   191  	}
   192  	return nil
   193  }
   194  
   195  type commonCallable struct {
   196  	target interface{}
   197  }
   198  
   199  func (c *commonCallable) Target() interface{} {
   200  	return c.target
   201  }
   202  
   203  func (c *commonCallable) Call(
   204  	f *Frame, args []interface{}, opts RawOptions, inputs Inputs) ([]interface{}, error) {
   205  	return callFunc(c.target, f, args, opts, inputs, false)
   206  }
   207  
   208  func callFunc(fnp interface{}, f *Frame, args []interface{}, opts RawOptions, inputs Inputs, checkOnly bool) ([]interface{}, error) {
   209  	switch fn := fnp.(type) {
   210  	case func():
   211  		if checkOnly {
   212  			return nil, nil
   213  		}
   214  		fn()
   215  		return nil, nil
   216  
   217  	case func() error:
   218  		if checkOnly {
   219  			return nil, nil
   220  		}
   221  		err := fn()
   222  		return nil, err
   223  
   224  	case func() int:
   225  		if checkOnly {
   226  			return nil, nil
   227  		}
   228  		out := fn()
   229  		return []interface{}{out}, nil
   230  
   231  	case func(*Frame):
   232  		if checkOnly {
   233  			return nil, nil
   234  		}
   235  		fn(f)
   236  		return nil, nil
   237  
   238  	case func(*Frame, RawOptions):
   239  		if checkOnly {
   240  			return nil, nil
   241  		}
   242  		fn(f, opts)
   243  		return nil, nil
   244  
   245  	case func(*Frame, RawOptions, Callable, Callable):
   246  		if checkOnly {
   247  			return nil, nil
   248  		}
   249  		fn(f, opts, args[0].(Callable), args[1].(Callable))
   250  		return nil, nil
   251  
   252  	case func(*Frame, RawOptions, string, Inputs):
   253  		if checkOnly {
   254  			return nil, nil
   255  		}
   256  		fn(f, opts, args[0].(string), inputs)
   257  		return nil, nil
   258  
   259  	case func(*Frame, ...int):
   260  		if checkOnly {
   261  			return nil, nil
   262  		}
   263  		var vargs []int
   264  		for _, arg := range args {
   265  			vargs = append(vargs, arg.(int))
   266  		}
   267  		fn(f, vargs...)
   268  		return nil, nil
   269  
   270  	case func(*Frame, ...interface{}) error:
   271  		if checkOnly {
   272  			return nil, nil
   273  		}
   274  		err := fn(f, args...)
   275  		return nil, err
   276  
   277  	case func(*Frame, interface{}, interface{}, interface{}):
   278  		if checkOnly {
   279  			return nil, nil
   280  		}
   281  		fn(f, args[0], args[1], args[2])
   282  		return nil, nil
   283  
   284  	case func(*Frame, ...int) error:
   285  		if checkOnly {
   286  			return nil, nil
   287  		}
   288  		var vargs []int
   289  		for _, arg := range args {
   290  			vargs = append(vargs, arg.(int))
   291  		}
   292  		err := fn(f, vargs...)
   293  		return nil, err
   294  
   295  	case func(*Frame, ...string) error:
   296  		if checkOnly {
   297  			return nil, nil
   298  		}
   299  		var vargs []string
   300  		for _, arg := range args {
   301  			vargs = append(vargs, arg.(string))
   302  		}
   303  		err := fn(f, vargs...)
   304  		return nil, err
   305  
   306  	case func(*Frame, string):
   307  		if checkOnly {
   308  			return nil, nil
   309  		}
   310  		fn(f, args[0].(string))
   311  		return nil, nil
   312  
   313  	case func(*Frame, string) error:
   314  		if checkOnly {
   315  			return nil, nil
   316  		}
   317  		err := fn(f, args[0].(string))
   318  		return nil, err
   319  
   320  	case func(Inputs):
   321  		if checkOnly {
   322  			return nil, nil
   323  		}
   324  		fn(inputs)
   325  		return nil, nil
   326  
   327  	case func(RawOptions):
   328  		if checkOnly {
   329  			return nil, nil
   330  		}
   331  		fn(opts)
   332  		return nil, nil
   333  
   334  	case func(RawOptions, ...interface{}):
   335  		if checkOnly {
   336  			return nil, nil
   337  		}
   338  		fn(opts, args...)
   339  		return nil, nil
   340  
   341  	case func(int, float64):
   342  		if checkOnly {
   343  			return nil, nil
   344  		}
   345  		fn(args[0].(int), args[1].(float64))
   346  		return nil, nil
   347  
   348  	case func(...int) error:
   349  		if checkOnly {
   350  			return nil, nil
   351  		}
   352  		var vargs []int
   353  		for _, arg := range args {
   354  			vargs = append(vargs, arg.(int))
   355  		}
   356  		err := fn(vargs...)
   357  		return nil, err
   358  
   359  	case func(float64):
   360  		if checkOnly {
   361  			return nil, nil
   362  		}
   363  		fn(args[0].(float64))
   364  		return nil, nil
   365  
   366  	case func(int):
   367  		if checkOnly {
   368  			return nil, nil
   369  		}
   370  		fn(args[0].(int))
   371  		return nil, nil
   372  
   373  	case func(string):
   374  		if checkOnly {
   375  			return nil, nil
   376  		}
   377  		fn(args[0].(string))
   378  		return nil, nil
   379  
   380  	case func(string) string:
   381  		if checkOnly {
   382  			return nil, nil
   383  		}
   384  		out := fn(args[0].(string))
   385  		return []interface{}{out}, nil
   386  
   387  	case func(string, ...string):
   388  		if checkOnly {
   389  			return nil, nil
   390  		}
   391  		var vargs []string
   392  		for _, arg := range args[1:] {
   393  			vargs = append(vargs, arg.(string))
   394  		}
   395  		fn(args[0].(string), vargs...)
   396  		return nil, nil
   397  	}
   398  
   399  	sig := fmt.Sprintf("%#v", fnp)[1:]
   400  	sig = sig[:strings.LastIndex(sig, "(")-1]
   401  	return nil, fmt.Errorf("unsupported function signature: %s", sig)
   402  }