github.com/traefik/yaegi@v0.15.1/interp/interp.go (about)

     1  package interp
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"go/build"
     9  	"go/scanner"
    10  	"go/token"
    11  	"io"
    12  	"io/fs"
    13  	"os"
    14  	"os/signal"
    15  	"path"
    16  	"path/filepath"
    17  	"reflect"
    18  	"runtime"
    19  	"runtime/debug"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"sync/atomic"
    24  )
    25  
    26  // Interpreter node structure for AST and CFG.
    27  type node struct {
    28  	debug      *nodeDebugData // debug info
    29  	child      []*node        // child subtrees (AST)
    30  	anc        *node          // ancestor (AST)
    31  	param      []*itype       // generic parameter nodes (AST)
    32  	start      *node          // entry point in subtree (CFG)
    33  	tnext      *node          // true branch successor (CFG)
    34  	fnext      *node          // false branch successor (CFG)
    35  	interp     *Interpreter   // interpreter context
    36  	frame      *frame         // frame pointer used for closures only (TODO: suppress this)
    37  	index      int64          // node index (dot display)
    38  	findex     int            // index of value in frame or frame size (func def, type def)
    39  	level      int            // number of frame indirections to access value
    40  	nleft      int            // number of children in left part (assign) or indicates preceding type (compositeLit)
    41  	nright     int            // number of children in right part (assign)
    42  	kind       nkind          // kind of node
    43  	pos        token.Pos      // position in source code, relative to fset
    44  	sym        *symbol        // associated symbol
    45  	typ        *itype         // type of value in frame, or nil
    46  	recv       *receiver      // method receiver node for call, or nil
    47  	types      []reflect.Type // frame types, used by function literals only
    48  	scope      *scope         // frame scope
    49  	action     action         // action
    50  	exec       bltn           // generated function to execute
    51  	gen        bltnGenerator  // generator function to produce above bltn
    52  	val        interface{}    // static generic value (CFG execution)
    53  	rval       reflect.Value  // reflection value to let runtime access interpreter (CFG)
    54  	ident      string         // set if node is a var or func
    55  	redeclared bool           // set if node is a redeclared variable (CFG)
    56  	meta       interface{}    // meta stores meta information between gta runs, like errors
    57  }
    58  
    59  func (n *node) shouldBreak() bool {
    60  	if n == nil || n.debug == nil {
    61  		return false
    62  	}
    63  
    64  	if n.debug.breakOnLine || n.debug.breakOnCall {
    65  		return true
    66  	}
    67  
    68  	return false
    69  }
    70  
    71  func (n *node) setProgram(p *Program) {
    72  	if n.debug == nil {
    73  		n.debug = new(nodeDebugData)
    74  	}
    75  	n.debug.program = p
    76  }
    77  
    78  func (n *node) setBreakOnCall(v bool) {
    79  	if n.debug == nil {
    80  		if !v {
    81  			return
    82  		}
    83  		n.debug = new(nodeDebugData)
    84  	}
    85  	n.debug.breakOnCall = v
    86  }
    87  
    88  func (n *node) setBreakOnLine(v bool) {
    89  	if n.debug == nil {
    90  		if !v {
    91  			return
    92  		}
    93  		n.debug = new(nodeDebugData)
    94  	}
    95  	n.debug.breakOnLine = v
    96  }
    97  
    98  // receiver stores method receiver object access path.
    99  type receiver struct {
   100  	node  *node         // receiver value for alias and struct types
   101  	val   reflect.Value // receiver value for interface type and value type
   102  	index []int         // path in receiver value for interface or value type
   103  }
   104  
   105  // frame contains values for the current execution level (a function context).
   106  type frame struct {
   107  	// id is an atomic counter used for cancellation, only accessed
   108  	// via newFrame/runid/setrunid/clone.
   109  	// Located at start of struct to ensure proper alignment.
   110  	id uint64
   111  
   112  	debug *frameDebugData
   113  
   114  	root *frame          // global space
   115  	anc  *frame          // ancestor frame (caller space)
   116  	data []reflect.Value // values
   117  
   118  	mutex     sync.RWMutex
   119  	deferred  [][]reflect.Value  // defer stack
   120  	recovered interface{}        // to handle panic recover
   121  	done      reflect.SelectCase // for cancellation of channel operations
   122  }
   123  
   124  func newFrame(anc *frame, length int, id uint64) *frame {
   125  	f := &frame{
   126  		anc:  anc,
   127  		data: make([]reflect.Value, length),
   128  		id:   id,
   129  	}
   130  	if anc == nil {
   131  		f.root = f
   132  	} else {
   133  		f.done = anc.done
   134  		f.root = anc.root
   135  	}
   136  	return f
   137  }
   138  
   139  func (f *frame) runid() uint64      { return atomic.LoadUint64(&f.id) }
   140  func (f *frame) setrunid(id uint64) { atomic.StoreUint64(&f.id, id) }
   141  func (f *frame) clone(fork bool) *frame {
   142  	f.mutex.RLock()
   143  	defer f.mutex.RUnlock()
   144  	nf := &frame{
   145  		anc:       f.anc,
   146  		root:      f.root,
   147  		deferred:  f.deferred,
   148  		recovered: f.recovered,
   149  		id:        f.runid(),
   150  		done:      f.done,
   151  		debug:     f.debug,
   152  	}
   153  	if fork {
   154  		nf.data = make([]reflect.Value, len(f.data))
   155  		copy(nf.data, f.data)
   156  	} else {
   157  		nf.data = f.data
   158  	}
   159  	return nf
   160  }
   161  
   162  // Exports stores the map of binary packages per package path.
   163  // The package path is the path joined from the import path and the package name
   164  // as specified in source files by the "package" statement.
   165  type Exports map[string]map[string]reflect.Value
   166  
   167  // imports stores the map of source packages per package path.
   168  type imports map[string]map[string]*symbol
   169  
   170  // opt stores interpreter options.
   171  type opt struct {
   172  	// dotCmd is the command to process the dot graph produced when astDot and/or
   173  	// cfgDot is enabled. It defaults to 'dot -Tdot -o <filename>.dot'.
   174  	dotCmd       string
   175  	context      build.Context     // build context: GOPATH, build constraints
   176  	stdin        io.Reader         // standard input
   177  	stdout       io.Writer         // standard output
   178  	stderr       io.Writer         // standard error
   179  	args         []string          // cmdline args
   180  	env          map[string]string // environment of interpreter, entries in form of "key=value"
   181  	filesystem   fs.FS             // filesystem containing sources
   182  	astDot       bool              // display AST graph (debug)
   183  	cfgDot       bool              // display CFG graph (debug)
   184  	noRun        bool              // compile, but do not run
   185  	fastChan     bool              // disable cancellable chan operations
   186  	specialStdio bool              // allows os.Stdin, os.Stdout, os.Stderr to not be file descriptors
   187  	unrestricted bool              // allow use of non sandboxed symbols
   188  }
   189  
   190  // Interpreter contains global resources and state.
   191  type Interpreter struct {
   192  	// id is an atomic counter counter used for run cancellation,
   193  	// only accessed via runid/stop
   194  	// Located at start of struct to ensure proper alignment on 32 bit
   195  	// architectures.
   196  	id uint64
   197  
   198  	// nindex is a node number incremented for each new node.
   199  	// It is used for debug (AST and CFG graphs). As it is atomically
   200  	// incremented, keep it aligned on 64 bits boundary.
   201  	nindex int64
   202  
   203  	name string // name of the input source file (or main)
   204  
   205  	opt                                         // user settable options
   206  	cancelChan bool                             // enables cancellable chan operations
   207  	fset       *token.FileSet                   // fileset to locate node in source code
   208  	binPkg     Exports                          // binary packages used in interpreter, indexed by path
   209  	rdir       map[string]bool                  // for src import cycle detection
   210  	mapTypes   map[reflect.Value][]reflect.Type // special interfaces mapping for wrappers
   211  
   212  	mutex    sync.RWMutex
   213  	frame    *frame            // program data storage during execution
   214  	universe *scope            // interpreter global level scope
   215  	scopes   map[string]*scope // package level scopes, indexed by import path
   216  	srcPkg   imports           // source packages used in interpreter, indexed by path
   217  	pkgNames map[string]string // package names, indexed by import path
   218  	done     chan struct{}     // for cancellation of channel operations
   219  	roots    []*node
   220  	generic  map[string]*node
   221  
   222  	hooks *hooks // symbol hooks
   223  
   224  	debugger *Debugger
   225  }
   226  
   227  const (
   228  	mainID     = "main"
   229  	selfPrefix = "github.com/traefik/yaegi"
   230  	selfPath   = selfPrefix + "/interp/interp"
   231  	// DefaultSourceName is the name used by default when the name of the input
   232  	// source file has not been specified for an Eval.
   233  	// TODO(mpl): something even more special as a name?
   234  	DefaultSourceName = "_.go"
   235  
   236  	// Test is the value to pass to EvalPath to activate evaluation of test functions.
   237  	Test = false
   238  	// NoTest is the value to pass to EvalPath to skip evaluation of test functions.
   239  	NoTest = true
   240  )
   241  
   242  // Self points to the current interpreter if accessed from within itself, or is nil.
   243  var Self *Interpreter
   244  
   245  // Symbols exposes interpreter values.
   246  var Symbols = Exports{
   247  	selfPath: map[string]reflect.Value{
   248  		"New": reflect.ValueOf(New),
   249  
   250  		"Interpreter": reflect.ValueOf((*Interpreter)(nil)),
   251  		"Options":     reflect.ValueOf((*Options)(nil)),
   252  		"Panic":       reflect.ValueOf((*Panic)(nil)),
   253  	},
   254  }
   255  
   256  func init() { Symbols[selfPath]["Symbols"] = reflect.ValueOf(Symbols) }
   257  
   258  // _error is a wrapper of error interface type.
   259  type _error struct {
   260  	IValue interface{}
   261  	WError func() string
   262  }
   263  
   264  func (w _error) Error() string { return w.WError() }
   265  
   266  // Panic is an error recovered from a panic call in interpreted code.
   267  type Panic struct {
   268  	// Value is the recovered value of a call to panic.
   269  	Value interface{}
   270  
   271  	// Callers is the call stack obtained from the recover call.
   272  	// It may be used as the parameter to runtime.CallersFrames.
   273  	Callers []uintptr
   274  
   275  	// Stack is the call stack buffer for debug.
   276  	Stack []byte
   277  }
   278  
   279  // TODO: Capture interpreter stack frames also and remove
   280  // fmt.Fprintln(n.interp.stderr, oNode.cfgErrorf("panic")) in runCfg.
   281  
   282  func (e Panic) Error() string { return fmt.Sprint(e.Value) }
   283  
   284  // Walk traverses AST n in depth first order, call cbin function
   285  // at node entry and cbout function at node exit.
   286  func (n *node) Walk(in func(n *node) bool, out func(n *node)) {
   287  	if in != nil && !in(n) {
   288  		return
   289  	}
   290  	for _, child := range n.child {
   291  		child.Walk(in, out)
   292  	}
   293  	if out != nil {
   294  		out(n)
   295  	}
   296  }
   297  
   298  // Options are the interpreter options.
   299  type Options struct {
   300  	// GoPath sets GOPATH for the interpreter.
   301  	GoPath string
   302  
   303  	// BuildTags sets build constraints for the interpreter.
   304  	BuildTags []string
   305  
   306  	// Standard input, output and error streams.
   307  	// They default to os.Stdin, os.Stdout and os.Stderr respectively.
   308  	Stdin          io.Reader
   309  	Stdout, Stderr io.Writer
   310  
   311  	// Cmdline args, defaults to os.Args.
   312  	Args []string
   313  
   314  	// Environment of interpreter. Entries are in the form "key=values".
   315  	Env []string
   316  
   317  	// SourcecodeFilesystem is where the _sourcecode_ is loaded from and does
   318  	// NOT affect the filesystem of scripts when they run.
   319  	// It can be any fs.FS compliant filesystem (e.g. embed.FS, or fstest.MapFS for testing)
   320  	// See example/fs/fs_test.go for an example.
   321  	SourcecodeFilesystem fs.FS
   322  
   323  	// Unrestricted allows to run non sandboxed stdlib symbols such as os/exec and environment
   324  	Unrestricted bool
   325  }
   326  
   327  // New returns a new interpreter.
   328  func New(options Options) *Interpreter {
   329  	i := Interpreter{
   330  		opt:      opt{context: build.Default, filesystem: &realFS{}, env: map[string]string{}},
   331  		frame:    newFrame(nil, 0, 0),
   332  		fset:     token.NewFileSet(),
   333  		universe: initUniverse(),
   334  		scopes:   map[string]*scope{},
   335  		binPkg:   Exports{"": map[string]reflect.Value{"_error": reflect.ValueOf((*_error)(nil))}},
   336  		mapTypes: map[reflect.Value][]reflect.Type{},
   337  		srcPkg:   imports{},
   338  		pkgNames: map[string]string{},
   339  		rdir:     map[string]bool{},
   340  		hooks:    &hooks{},
   341  		generic:  map[string]*node{},
   342  	}
   343  
   344  	if i.opt.stdin = options.Stdin; i.opt.stdin == nil {
   345  		i.opt.stdin = os.Stdin
   346  	}
   347  
   348  	if i.opt.stdout = options.Stdout; i.opt.stdout == nil {
   349  		i.opt.stdout = os.Stdout
   350  	}
   351  
   352  	if i.opt.stderr = options.Stderr; i.opt.stderr == nil {
   353  		i.opt.stderr = os.Stderr
   354  	}
   355  
   356  	if i.opt.args = options.Args; i.opt.args == nil {
   357  		i.opt.args = os.Args
   358  	}
   359  
   360  	// unrestricted allows to use non sandboxed stdlib symbols and env.
   361  	if options.Unrestricted {
   362  		i.opt.unrestricted = true
   363  	} else {
   364  		for _, e := range options.Env {
   365  			a := strings.SplitN(e, "=", 2)
   366  			if len(a) == 2 {
   367  				i.opt.env[a[0]] = a[1]
   368  			} else {
   369  				i.opt.env[a[0]] = ""
   370  			}
   371  		}
   372  	}
   373  
   374  	if options.SourcecodeFilesystem != nil {
   375  		i.opt.filesystem = options.SourcecodeFilesystem
   376  	}
   377  
   378  	i.opt.context.GOPATH = options.GoPath
   379  	if len(options.BuildTags) > 0 {
   380  		i.opt.context.BuildTags = options.BuildTags
   381  	}
   382  
   383  	// astDot activates AST graph display for the interpreter
   384  	i.opt.astDot, _ = strconv.ParseBool(os.Getenv("YAEGI_AST_DOT"))
   385  
   386  	// cfgDot activates CFG graph display for the interpreter
   387  	i.opt.cfgDot, _ = strconv.ParseBool(os.Getenv("YAEGI_CFG_DOT"))
   388  
   389  	// dotCmd defines how to process the dot code generated whenever astDot and/or
   390  	// cfgDot is enabled. It defaults to 'dot -Tdot -o<filename>.dot' where filename
   391  	// is context dependent.
   392  	i.opt.dotCmd = os.Getenv("YAEGI_DOT_CMD")
   393  
   394  	// noRun disables the execution (but not the compilation) in the interpreter
   395  	i.opt.noRun, _ = strconv.ParseBool(os.Getenv("YAEGI_NO_RUN"))
   396  
   397  	// fastChan disables the cancellable version of channel operations in evalWithContext
   398  	i.opt.fastChan, _ = strconv.ParseBool(os.Getenv("YAEGI_FAST_CHAN"))
   399  
   400  	// specialStdio allows to assign directly io.Writer and io.Reader to os.Stdxxx,
   401  	// even if they are not file descriptors.
   402  	i.opt.specialStdio, _ = strconv.ParseBool(os.Getenv("YAEGI_SPECIAL_STDIO"))
   403  
   404  	return &i
   405  }
   406  
   407  const (
   408  	bltnAppend  = "append"
   409  	bltnCap     = "cap"
   410  	bltnClose   = "close"
   411  	bltnComplex = "complex"
   412  	bltnImag    = "imag"
   413  	bltnCopy    = "copy"
   414  	bltnDelete  = "delete"
   415  	bltnLen     = "len"
   416  	bltnMake    = "make"
   417  	bltnNew     = "new"
   418  	bltnPanic   = "panic"
   419  	bltnPrint   = "print"
   420  	bltnPrintln = "println"
   421  	bltnReal    = "real"
   422  	bltnRecover = "recover"
   423  )
   424  
   425  func initUniverse() *scope {
   426  	sc := &scope{global: true, sym: map[string]*symbol{
   427  		// predefined Go types
   428  		"any":         {kind: typeSym, typ: &itype{cat: interfaceT, str: "any"}},
   429  		"bool":        {kind: typeSym, typ: &itype{cat: boolT, name: "bool", str: "bool"}},
   430  		"byte":        {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
   431  		"comparable":  {kind: typeSym, typ: &itype{cat: comparableT, name: "comparable", str: "comparable"}},
   432  		"complex64":   {kind: typeSym, typ: &itype{cat: complex64T, name: "complex64", str: "complex64"}},
   433  		"complex128":  {kind: typeSym, typ: &itype{cat: complex128T, name: "complex128", str: "complex128"}},
   434  		"error":       {kind: typeSym, typ: &itype{cat: errorT, name: "error", str: "error"}},
   435  		"float32":     {kind: typeSym, typ: &itype{cat: float32T, name: "float32", str: "float32"}},
   436  		"float64":     {kind: typeSym, typ: &itype{cat: float64T, name: "float64", str: "float64"}},
   437  		"int":         {kind: typeSym, typ: &itype{cat: intT, name: "int", str: "int"}},
   438  		"int8":        {kind: typeSym, typ: &itype{cat: int8T, name: "int8", str: "int8"}},
   439  		"int16":       {kind: typeSym, typ: &itype{cat: int16T, name: "int16", str: "int16"}},
   440  		"int32":       {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
   441  		"int64":       {kind: typeSym, typ: &itype{cat: int64T, name: "int64", str: "int64"}},
   442  		"interface{}": {kind: typeSym, typ: &itype{cat: interfaceT, str: "interface{}"}},
   443  		"rune":        {kind: typeSym, typ: &itype{cat: int32T, name: "int32", str: "int32"}},
   444  		"string":      {kind: typeSym, typ: &itype{cat: stringT, name: "string", str: "string"}},
   445  		"uint":        {kind: typeSym, typ: &itype{cat: uintT, name: "uint", str: "uint"}},
   446  		"uint8":       {kind: typeSym, typ: &itype{cat: uint8T, name: "uint8", str: "uint8"}},
   447  		"uint16":      {kind: typeSym, typ: &itype{cat: uint16T, name: "uint16", str: "uint16"}},
   448  		"uint32":      {kind: typeSym, typ: &itype{cat: uint32T, name: "uint32", str: "uint32"}},
   449  		"uint64":      {kind: typeSym, typ: &itype{cat: uint64T, name: "uint64", str: "uint64"}},
   450  		"uintptr":     {kind: typeSym, typ: &itype{cat: uintptrT, name: "uintptr", str: "uintptr"}},
   451  
   452  		// predefined Go constants
   453  		"false": {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(false)},
   454  		"true":  {kind: constSym, typ: untypedBool(nil), rval: reflect.ValueOf(true)},
   455  		"iota":  {kind: constSym, typ: untypedInt(nil)},
   456  
   457  		// predefined Go zero value
   458  		"nil": {typ: &itype{cat: nilT, untyped: true, str: "nil"}},
   459  
   460  		// predefined Go builtins
   461  		bltnAppend:  {kind: bltnSym, builtin: _append},
   462  		bltnCap:     {kind: bltnSym, builtin: _cap},
   463  		bltnClose:   {kind: bltnSym, builtin: _close},
   464  		bltnComplex: {kind: bltnSym, builtin: _complex},
   465  		bltnImag:    {kind: bltnSym, builtin: _imag},
   466  		bltnCopy:    {kind: bltnSym, builtin: _copy},
   467  		bltnDelete:  {kind: bltnSym, builtin: _delete},
   468  		bltnLen:     {kind: bltnSym, builtin: _len},
   469  		bltnMake:    {kind: bltnSym, builtin: _make},
   470  		bltnNew:     {kind: bltnSym, builtin: _new},
   471  		bltnPanic:   {kind: bltnSym, builtin: _panic},
   472  		bltnPrint:   {kind: bltnSym, builtin: _print},
   473  		bltnPrintln: {kind: bltnSym, builtin: _println},
   474  		bltnReal:    {kind: bltnSym, builtin: _real},
   475  		bltnRecover: {kind: bltnSym, builtin: _recover},
   476  	}}
   477  	return sc
   478  }
   479  
   480  // resizeFrame resizes the global frame of interpreter.
   481  func (interp *Interpreter) resizeFrame() {
   482  	l := len(interp.universe.types)
   483  	b := len(interp.frame.data)
   484  	if l-b <= 0 {
   485  		return
   486  	}
   487  	data := make([]reflect.Value, l)
   488  	copy(data, interp.frame.data)
   489  	for j, t := range interp.universe.types[b:] {
   490  		data[b+j] = reflect.New(t).Elem()
   491  	}
   492  	interp.frame.data = data
   493  }
   494  
   495  // Eval evaluates Go code represented as a string. Eval returns the last result
   496  // computed by the interpreter, and a non nil error in case of failure.
   497  func (interp *Interpreter) Eval(src string) (res reflect.Value, err error) {
   498  	return interp.eval(src, "", true)
   499  }
   500  
   501  // EvalPath evaluates Go code located at path and returns the last result computed
   502  // by the interpreter, and a non nil error in case of failure.
   503  // The main function of the main package is executed if present.
   504  func (interp *Interpreter) EvalPath(path string) (res reflect.Value, err error) {
   505  	if !isFile(interp.opt.filesystem, path) {
   506  		_, err := interp.importSrc(mainID, path, NoTest)
   507  		return res, err
   508  	}
   509  
   510  	b, err := fs.ReadFile(interp.filesystem, path)
   511  	if err != nil {
   512  		return res, err
   513  	}
   514  	return interp.eval(string(b), path, false)
   515  }
   516  
   517  // EvalPathWithContext evaluates Go code located at path and returns the last
   518  // result computed by the interpreter, and a non nil error in case of failure.
   519  // The main function of the main package is executed if present.
   520  func (interp *Interpreter) EvalPathWithContext(ctx context.Context, path string) (res reflect.Value, err error) {
   521  	interp.mutex.Lock()
   522  	interp.done = make(chan struct{})
   523  	interp.cancelChan = !interp.opt.fastChan
   524  	interp.mutex.Unlock()
   525  
   526  	done := make(chan struct{})
   527  	go func() {
   528  		defer close(done)
   529  		res, err = interp.EvalPath(path)
   530  	}()
   531  
   532  	select {
   533  	case <-ctx.Done():
   534  		interp.stop()
   535  		return reflect.Value{}, ctx.Err()
   536  	case <-done:
   537  	}
   538  	return res, err
   539  }
   540  
   541  // EvalTest evaluates Go code located at path, including test files with "_test.go" suffix.
   542  // A non nil error is returned in case of failure.
   543  // The main function, test functions and benchmark functions are internally compiled but not
   544  // executed. Test functions can be retrieved using the Symbol() method.
   545  func (interp *Interpreter) EvalTest(path string) error {
   546  	_, err := interp.importSrc(mainID, path, Test)
   547  	return err
   548  }
   549  
   550  func isFile(filesystem fs.FS, path string) bool {
   551  	fi, err := fs.Stat(filesystem, path)
   552  	return err == nil && fi.Mode().IsRegular()
   553  }
   554  
   555  func (interp *Interpreter) eval(src, name string, inc bool) (res reflect.Value, err error) {
   556  	prog, err := interp.compileSrc(src, name, inc)
   557  	if err != nil {
   558  		return res, err
   559  	}
   560  
   561  	if interp.noRun {
   562  		return res, err
   563  	}
   564  
   565  	return interp.Execute(prog)
   566  }
   567  
   568  // EvalWithContext evaluates Go code represented as a string. It returns
   569  // a map on current interpreted package exported symbols.
   570  func (interp *Interpreter) EvalWithContext(ctx context.Context, src string) (reflect.Value, error) {
   571  	var v reflect.Value
   572  	var err error
   573  
   574  	interp.mutex.Lock()
   575  	interp.done = make(chan struct{})
   576  	interp.cancelChan = !interp.opt.fastChan
   577  	interp.mutex.Unlock()
   578  
   579  	done := make(chan struct{})
   580  	go func() {
   581  		defer func() {
   582  			if r := recover(); r != nil {
   583  				var pc [64]uintptr
   584  				n := runtime.Callers(1, pc[:])
   585  				err = Panic{Value: r, Callers: pc[:n], Stack: debug.Stack()}
   586  			}
   587  			close(done)
   588  		}()
   589  		v, err = interp.Eval(src)
   590  	}()
   591  
   592  	select {
   593  	case <-ctx.Done():
   594  		interp.stop()
   595  		return reflect.Value{}, ctx.Err()
   596  	case <-done:
   597  	}
   598  	return v, err
   599  }
   600  
   601  // stop sends a semaphore to all running frames and closes the chan
   602  // operation short circuit channel. stop may only be called once per
   603  // invocation of EvalWithContext.
   604  func (interp *Interpreter) stop() {
   605  	atomic.AddUint64(&interp.id, 1)
   606  	close(interp.done)
   607  }
   608  
   609  func (interp *Interpreter) runid() uint64 { return atomic.LoadUint64(&interp.id) }
   610  
   611  // ignoreScannerError returns true if the error from Go scanner can be safely ignored
   612  // to let the caller grab one more line before retrying to parse its input.
   613  func ignoreScannerError(e *scanner.Error, s string) bool {
   614  	msg := e.Msg
   615  	if strings.HasSuffix(msg, "found 'EOF'") {
   616  		return true
   617  	}
   618  	if msg == "raw string literal not terminated" {
   619  		return true
   620  	}
   621  	if strings.HasPrefix(msg, "expected operand, found '}'") && !strings.HasSuffix(s, "}") {
   622  		return true
   623  	}
   624  	return false
   625  }
   626  
   627  // ImportUsed automatically imports pre-compiled packages included by Use().
   628  // This is mainly useful for REPLs, or single command lines. In case of an ambiguous default
   629  // package name, for example "rand" for crypto/rand and math/rand, the package name is
   630  // constructed by replacing the last "/" by a "_", producing crypto_rand and math_rand.
   631  // ImportUsed should not be called more than once, and not after a first Eval, as it may
   632  // rename packages.
   633  func (interp *Interpreter) ImportUsed() {
   634  	sc := interp.universe
   635  	for k := range interp.binPkg {
   636  		// By construction, the package name is the last path element of the key.
   637  		name := path.Base(k)
   638  		if sym, ok := sc.sym[name]; ok {
   639  			// Handle collision by renaming old and new entries.
   640  			name2 := key2name(fixKey(sym.typ.path))
   641  			sc.sym[name2] = sym
   642  			if name2 != name {
   643  				delete(sc.sym, name)
   644  			}
   645  			name = key2name(fixKey(k))
   646  		}
   647  		sc.sym[name] = &symbol{kind: pkgSym, typ: &itype{cat: binPkgT, path: k, scope: sc}}
   648  	}
   649  }
   650  
   651  func key2name(name string) string {
   652  	return filepath.Join(name, DefaultSourceName)
   653  }
   654  
   655  func fixKey(k string) string {
   656  	i := strings.LastIndex(k, "/")
   657  	if i >= 0 {
   658  		k = k[:i] + "_" + k[i+1:]
   659  	}
   660  	return k
   661  }
   662  
   663  // REPL performs a Read-Eval-Print-Loop on input reader.
   664  // Results are printed to the output writer of the Interpreter, provided as option
   665  // at creation time. Errors are printed to the similarly defined errors writer.
   666  // The last interpreter result value and error are returned.
   667  func (interp *Interpreter) REPL() (reflect.Value, error) {
   668  	in, out, errs := interp.stdin, interp.stdout, interp.stderr
   669  	ctx, cancel := context.WithCancel(context.Background())
   670  	end := make(chan struct{})     // channel to terminate the REPL
   671  	sig := make(chan os.Signal, 1) // channel to trap interrupt signal (Ctrl-C)
   672  	lines := make(chan string)     // channel to read REPL input lines
   673  	prompt := getPrompt(in, out)   // prompt activated on tty like IO stream
   674  	s := bufio.NewScanner(in)      // read input stream line by line
   675  	var v reflect.Value            // result value from eval
   676  	var err error                  // error from eval
   677  	src := ""                      // source string to evaluate
   678  
   679  	signal.Notify(sig, os.Interrupt)
   680  	defer signal.Stop(sig)
   681  	prompt(v)
   682  
   683  	go func() {
   684  		defer close(end)
   685  		for s.Scan() {
   686  			lines <- s.Text()
   687  		}
   688  		if e := s.Err(); e != nil {
   689  			fmt.Fprintln(errs, e)
   690  		}
   691  	}()
   692  
   693  	go func() {
   694  		for {
   695  			select {
   696  			case <-sig:
   697  				cancel()
   698  				lines <- ""
   699  			case <-end:
   700  				return
   701  			}
   702  		}
   703  	}()
   704  
   705  	for {
   706  		var line string
   707  
   708  		select {
   709  		case <-end:
   710  			cancel()
   711  			return v, err
   712  		case line = <-lines:
   713  			src += line + "\n"
   714  		}
   715  
   716  		v, err = interp.EvalWithContext(ctx, src)
   717  		if err != nil {
   718  			switch e := err.(type) {
   719  			case scanner.ErrorList:
   720  				if len(e) > 0 && ignoreScannerError(e[0], line) {
   721  					continue
   722  				}
   723  				fmt.Fprintln(errs, strings.TrimPrefix(e[0].Error(), DefaultSourceName+":"))
   724  			case Panic:
   725  				fmt.Fprintln(errs, e.Value)
   726  				fmt.Fprintln(errs, string(e.Stack))
   727  			default:
   728  				fmt.Fprintln(errs, err)
   729  			}
   730  		}
   731  		if errors.Is(err, context.Canceled) {
   732  			ctx, cancel = context.WithCancel(context.Background())
   733  		}
   734  		src = ""
   735  		prompt(v)
   736  	}
   737  }
   738  
   739  func doPrompt(out io.Writer) func(v reflect.Value) {
   740  	return func(v reflect.Value) {
   741  		if v.IsValid() {
   742  			fmt.Fprintln(out, ":", v)
   743  		}
   744  		fmt.Fprint(out, "> ")
   745  	}
   746  }
   747  
   748  // getPrompt returns a function which prints a prompt only if input is a terminal.
   749  func getPrompt(in io.Reader, out io.Writer) func(reflect.Value) {
   750  	forcePrompt, _ := strconv.ParseBool(os.Getenv("YAEGI_PROMPT"))
   751  	if forcePrompt {
   752  		return doPrompt(out)
   753  	}
   754  	s, ok := in.(interface{ Stat() (os.FileInfo, error) })
   755  	if !ok {
   756  		return func(reflect.Value) {}
   757  	}
   758  	stat, err := s.Stat()
   759  	if err == nil && stat.Mode()&os.ModeCharDevice != 0 {
   760  		return doPrompt(out)
   761  	}
   762  	return func(reflect.Value) {}
   763  }