gitlab.com/apertussolutions/u-root@v7.0.0+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  }