github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/eval/frame.go (about) 1 package eval 2 3 import ( 4 "bufio" 5 "io" 6 "os" 7 "strings" 8 "sync" 9 10 "github.com/u-root/u-root/cmds/core/elvish/util" 11 ) 12 13 // Frame contains information of the current running function, aknin to a call 14 // frame in native CPU execution. A Frame is only modified during and very 15 // shortly after creation; new Frame's are "forked" when needed. 16 type Frame struct { 17 *Evaler 18 srcMeta *Source 19 20 local, up Ns 21 ports []*Port 22 23 begin, end int 24 traceback *stackTrace 25 26 background bool 27 } 28 29 // NewTopFrame creates a top-level Frame. 30 func NewTopFrame(ev *Evaler, src *Source, ports []*Port) *Frame { 31 return &Frame{ 32 ev, src, 33 ev.Global, make(Ns), 34 ports, 35 0, len(src.code), nil, false, 36 } 37 } 38 39 // Close releases resources allocated for this frame. It always returns a nil 40 // error. It may be called only once. 41 func (fm *Frame) Close() error { 42 for _, port := range fm.ports { 43 port.Close() 44 } 45 return nil 46 } 47 48 // InputChan returns a channel from which input can be read. 49 func (fm *Frame) InputChan() chan interface{} { 50 return fm.ports[0].Chan 51 } 52 53 // InputFile returns a file from which input can be read. 54 func (fm *Frame) InputFile() *os.File { 55 return fm.ports[0].File 56 } 57 58 // OutputChan returns a channel onto which output can be written. 59 func (fm *Frame) OutputChan() chan<- interface{} { 60 return fm.ports[1].Chan 61 } 62 63 // OutputFile returns a file onto which output can be written. 64 func (fm *Frame) OutputFile() *os.File { 65 return fm.ports[1].File 66 } 67 68 // IterateInputs calls the passed function for each input element. 69 func (fm *Frame) IterateInputs(f func(interface{})) { 70 var w sync.WaitGroup 71 inputs := make(chan interface{}) 72 73 w.Add(2) 74 go func() { 75 linesToChan(fm.ports[0].File, inputs) 76 w.Done() 77 }() 78 go func() { 79 for v := range fm.ports[0].Chan { 80 inputs <- v 81 } 82 w.Done() 83 }() 84 go func() { 85 w.Wait() 86 close(inputs) 87 }() 88 89 for v := range inputs { 90 f(v) 91 } 92 } 93 94 func linesToChan(r io.Reader, ch chan<- interface{}) { 95 filein := bufio.NewReader(r) 96 for { 97 line, err := filein.ReadString('\n') 98 if line != "" { 99 ch <- strings.TrimSuffix(line, "\n") 100 } 101 if err != nil { 102 if err != io.EOF { 103 logger.Println("error on reading:", err) 104 } 105 break 106 } 107 } 108 } 109 110 // fork returns a modified copy of ec. The ports are forked, and the name is 111 // changed to the given value. Other fields are copied shallowly. 112 func (fm *Frame) fork(name string) *Frame { 113 newPorts := make([]*Port, len(fm.ports)) 114 for i, p := range fm.ports { 115 newPorts[i] = p.Fork() 116 } 117 return &Frame{ 118 fm.Evaler, fm.srcMeta, 119 fm.local, fm.up, 120 newPorts, 121 fm.begin, fm.end, fm.traceback, fm.background, 122 } 123 } 124 125 // Eval evaluates an op. It does so in a protected environment so that 126 // exceptions thrown are wrapped in an Error. 127 func (fm *Frame) Eval(op Op) (err error) { 128 defer catch(&err, fm) 129 e := op.Exec(fm) 130 if e != nil { 131 if exc, ok := e.(*Exception); ok { 132 return exc 133 } 134 return fm.makeException(e) 135 } 136 return nil 137 } 138 139 // Call calls a function with the given arguments and options. It does so in a 140 // protected environment so that exceptions thrown are wrapped in an Error. 141 func (fm *Frame) Call(f Callable, args []interface{}, opts map[string]interface{}) (err error) { 142 defer catch(&err, fm) 143 e := f.Call(fm, args, opts) 144 if e != nil { 145 if exc, ok := e.(*Exception); ok { 146 return exc 147 } 148 return fm.makeException(e) 149 } 150 return nil 151 } 152 153 // CaptureOutput calls a function with the given arguments and options, 154 // capturing and returning the output. It does so in a protected environment so 155 // that exceptions thrown are wrapped in an Error. 156 func (fm *Frame) CaptureOutput(fn Callable, args []interface{}, opts map[string]interface{}) (vs []interface{}, err error) { 157 // XXX There is no source. 158 opFunc := func(f *Frame) error { 159 return fn.Call(f, args, opts) 160 } 161 return pcaptureOutput(fm, Op{funcOp(opFunc), -1, -1}) 162 } 163 164 // CallWithOutputCallback calls a function with the given arguments and options, 165 // feeding the outputs to the given callbacks. It does so in a protected 166 // environment so that exceptions thrown are wrapped in an Error. 167 func (fm *Frame) CallWithOutputCallback(fn Callable, args []interface{}, opts map[string]interface{}, valuesCb func(<-chan interface{}), bytesCb func(*os.File)) error { 168 // XXX There is no source. 169 opFunc := func(f *Frame) error { 170 return fn.Call(f, args, opts) 171 } 172 return pcaptureOutputInner(fm, Op{funcOp(opFunc), -1, -1}, valuesCb, bytesCb) 173 } 174 175 func catch(perr *error, fm *Frame) { 176 // NOTE: We have to duplicate instead of calling util.Catch here, since 177 // recover can only catch a panic when called directly from a deferred 178 // function. 179 r := recover() 180 if r == nil { 181 return 182 } 183 if exc, ok := r.(util.Thrown); ok { 184 err := exc.Wrapped 185 if _, ok := err.(*Exception); !ok { 186 err = fm.makeException(err) 187 } 188 *perr = err 189 } else if r != nil { 190 panic(r) 191 } 192 } 193 194 // makeException turns an error into an Exception by adding traceback. 195 func (fm *Frame) makeException(e error) *Exception { 196 return &Exception{e, fm.addTraceback()} 197 } 198 199 func (fm *Frame) addTraceback() *stackTrace { 200 return &stackTrace{ 201 entry: &util.SourceRange{ 202 Name: fm.srcMeta.describePath(), Source: fm.srcMeta.code, 203 Begin: fm.begin, End: fm.end, 204 }, 205 next: fm.traceback, 206 } 207 } 208 209 // errorpf stops the ec.eval immediately by panicking with a diagnostic message. 210 // The panic is supposed to be caught by ec.eval. 211 func (fm *Frame) errorpf(begin, end int, format string, args ...interface{}) { 212 fm.begin, fm.end = begin, end 213 throwf(format, args...) 214 }