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 }